-
Notifications
You must be signed in to change notification settings - Fork 0
DOCSP-55611: Add Kanopy cronjob deployment for GitHub metrics collection #7
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
Merged
Merged
Changes from all commits
Commits
Show all changes
4 commits
Select commit
Hold shift + click to select a range
70c4b21
Add Kanopy cronjob deployment for GitHub metrics collection
cbullinger d8c121c
Simplify Drone pipeline to single pipeline with sequential steps
cbullinger 2c6bd43
apply dachary feedback
cbullinger a90dcae
Apply Dachary and Kyle feedback
cbullinger File filter
Filter by extension
Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
There are no files selected for viewing
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
| Original file line number | Diff line number | Diff line change |
|---|---|---|
| @@ -0,0 +1,77 @@ | ||
| --- | ||
| kind: pipeline | ||
| type: kubernetes | ||
| name: github-metrics | ||
|
|
||
| trigger: | ||
| branch: | ||
| - main | ||
| event: | ||
| - push | ||
|
|
||
| steps: | ||
| - name: check-changes | ||
| image: alpine/git | ||
| commands: | ||
| - | | ||
| # Check if any files in github-metrics/ directory changed | ||
| git diff --name-only $DRONE_COMMIT_BEFORE $DRONE_COMMIT_AFTER | grep -q "^github-metrics/" && echo "Changes detected" || (echo "No changes in github-metrics/, skipping" && exit 78) | ||
|
|
||
| - name: test | ||
| image: node:20-alpine | ||
| commands: | ||
| - cd github-metrics | ||
| - npm ci | ||
| - node --version | ||
| - npm --version | ||
| - echo "Validating package.json and dependencies..." | ||
|
|
||
| - name: publish | ||
| image: plugins/kaniko-ecr | ||
| settings: | ||
| create_repository: true | ||
| registry: 795250896452.dkr.ecr.us-east-1.amazonaws.com | ||
| repo: docs/github-metrics | ||
| tags: | ||
| - git-${DRONE_COMMIT_SHA:0:7} | ||
| - latest | ||
| access_key: | ||
| from_secret: ecr_access_key | ||
| secret_key: | ||
| from_secret: ecr_secret_key | ||
| context: github-metrics | ||
| dockerfile: github-metrics/Dockerfile | ||
|
|
||
| - name: deploy | ||
| image: quay.io/mongodb/drone-helm:v3 | ||
| settings: | ||
| chart: mongodb/cronjobs | ||
| chart_version: 1.21.2 | ||
| add_repos: [ mongodb=https://10gen.github.io/helm-charts ] | ||
| namespace: docs | ||
| release: github-metrics | ||
| values: image.tag=git-${DRONE_COMMIT_SHA:0:7},image.repository=795250896452.dkr.ecr.us-east-1.amazonaws.com/docs/github-metrics | ||
| values_files: [ 'github-metrics/cronjobs.yml' ] | ||
| api_server: https://api.prod.corp.mongodb.com | ||
| kubernetes_token: | ||
| from_secret: kubernetes_token | ||
|
|
||
| - name: notify-slack | ||
| image: alpine/curl | ||
| environment: | ||
| SLACK_WEBHOOK: | ||
| from_secret: slack_webhook_url | ||
| commands: | ||
| - | | ||
| if [ "$DRONE_BUILD_STATUS" = "success" ]; then | ||
| STATUS_MSG="✅ *GitHub Metrics CronJob Deploy Succeeded*" | ||
| else | ||
| STATUS_MSG="❌ *GitHub Metrics CronJob Deploy Failed*" | ||
| fi | ||
| curl -X POST -H 'Content-type: application/json' \ | ||
| --data "{\"text\": \"$STATUS_MSG\n*Repo:* $DRONE_REPO_NAME\n*Branch:* $DRONE_BRANCH\n*Commit:* ${DRONE_COMMIT_SHA:0:7}\n*Author:* $DRONE_COMMIT_AUTHOR\n*Build:* <$DRONE_BUILD_LINK|#$DRONE_BUILD_NUMBER>\"}" \ | ||
| "$SLACK_WEBHOOK" | ||
| when: | ||
| status: | ||
| - success | ||
| - failure |
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
| Original file line number | Diff line number | Diff line change |
|---|---|---|
| @@ -0,0 +1,29 @@ | ||
| FROM node:20-alpine | ||
|
|
||
| # Set working directory | ||
| WORKDIR /app | ||
|
|
||
| # Copy package files first (for better Docker layer caching) | ||
| COPY package.json package-lock.json ./ | ||
|
|
||
| # Install dependencies (use ci for reproducible builds) | ||
| RUN npm ci --only=production | ||
|
|
||
| # Copy the rest of the application files | ||
| COPY . . | ||
|
|
||
| # Create a non-root user for security best practices | ||
| RUN addgroup -g 1001 -S nodejs && \ | ||
| adduser -S nodejs -u 1001 && \ | ||
| chown -R nodejs:nodejs /app | ||
|
|
||
| # Switch to non-root user | ||
| USER nodejs | ||
|
|
||
| # Set NODE_ENV to production | ||
| ENV NODE_ENV=production | ||
|
|
||
| # Command to run the application | ||
| # This will be executed by the Kubernetes CronJob | ||
| CMD ["node", "index.js"] | ||
|
|
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
| Original file line number | Diff line number | Diff line change |
|---|---|---|
| @@ -0,0 +1,80 @@ | ||
| import fs from 'fs'; | ||
| import { writeFile, mkdir } from 'fs/promises'; | ||
| import path from 'path'; | ||
|
|
||
| // Path to the state file (mounted from persistent volume) | ||
| // Can be overridden via STATE_FILE_PATH environment variable | ||
| const STATE_FILE_PATH = process.env.STATE_FILE_PATH || '/data/last-run.json'; | ||
|
|
||
| // Minimum days between runs (13 days to account for timing variations with weekly Monday runs) | ||
| // Can be overridden via MIN_DAYS_BETWEEN_RUNS environment variable | ||
| const MIN_DAYS_BETWEEN_RUNS = parseInt(process.env.MIN_DAYS_BETWEEN_RUNS || '13', 10); | ||
|
|
||
| /** | ||
| * Check if enough time has passed since the last run | ||
| * @returns {boolean} true if should run, false if should skip | ||
| */ | ||
| export function shouldRun() { | ||
| try { | ||
| // Check if state file exists | ||
| if (!fs.existsSync(STATE_FILE_PATH)) { | ||
| console.log('No previous run found. Running for the first time.'); | ||
| return true; | ||
| } | ||
|
|
||
| // Read the last run timestamp | ||
| const stateData = JSON.parse(fs.readFileSync(STATE_FILE_PATH, 'utf8')); | ||
| const lastRunTime = new Date(stateData.lastRun); | ||
| const now = new Date(); | ||
|
|
||
| // Calculate days since last run | ||
| const daysSinceLastRun = (now - lastRunTime) / (1000 * 60 * 60 * 24); | ||
|
|
||
| console.log(`Last run: ${lastRunTime.toISOString()}`); | ||
| console.log(`Days since last run: ${daysSinceLastRun.toFixed(2)}`); | ||
| console.log(`Minimum days required: ${MIN_DAYS_BETWEEN_RUNS}`); | ||
|
|
||
| if (daysSinceLastRun < MIN_DAYS_BETWEEN_RUNS) { | ||
| console.log(`⏭️ Skipping run - only ${daysSinceLastRun.toFixed(2)} days since last run (need ${MIN_DAYS_BETWEEN_RUNS})`); | ||
| return false; | ||
| } | ||
|
|
||
| console.log(`✅ Proceeding with run - ${daysSinceLastRun.toFixed(2)} days since last run`); | ||
| return true; | ||
|
|
||
| } catch (error) { | ||
| console.error('Error checking last run time:', error.message); | ||
| console.log('Proceeding with run due to error reading state file'); | ||
| return true; // Run if we can't read the state file | ||
| } | ||
| } | ||
|
|
||
| /** | ||
| * Update the state file with the current timestamp | ||
| */ | ||
| export async function updateLastRun() { | ||
| try { | ||
| const now = new Date(); | ||
| const stateData = { | ||
| lastRun: now.toISOString(), | ||
| timestamp: now.getTime() | ||
| }; | ||
|
|
||
| // Ensure the directory exists | ||
| const dir = path.dirname(STATE_FILE_PATH); | ||
| if (!fs.existsSync(dir)) { | ||
| await mkdir(dir, { recursive: true }); | ||
| } | ||
|
|
||
| // Write the state file | ||
| await writeFile(STATE_FILE_PATH, JSON.stringify(stateData, null, 2), 'utf8'); | ||
| console.log(`✅ Updated last run timestamp: ${now.toISOString()}`); | ||
|
|
||
| } catch (error) { | ||
| console.error('Error updating last run time:', error.message); | ||
| // Don't throw - we don't want to fail the job just because we can't write the state file | ||
| } | ||
| } | ||
|
|
||
| export { MIN_DAYS_BETWEEN_RUNS }; | ||
|
|
||
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
| Original file line number | Diff line number | Diff line change |
|---|---|---|
| @@ -0,0 +1,35 @@ | ||
| --- | ||
| # `image` can be skipped if the values are being set in your .drone.yml file | ||
| image: | ||
| repository: 795250896452.dkr.ecr.us-east-1.amazonaws.com/docs/github-metrics | ||
| tag: latest | ||
|
|
||
| # global secrets are references to k8s Secrets | ||
| globalEnvSecrets: | ||
| GITHUB_TOKEN: github-token | ||
| ATLAS_CONNECTION_STRING: atlas-connection-string | ||
|
|
||
| cronJobs: | ||
| - name: github-metrics-collection | ||
| # Run weekly on Mondays at 8am UTC | ||
| # The application checks if it ran in the last 14 days and skips if so | ||
| # Cron format: minute hour day-of-month month day-of-week | ||
| # 0 = Sunday, 1 = Monday, etc. | ||
| schedule: "0 8 * * 1" | ||
| command: | ||
| - node | ||
| - index.js | ||
| resources: | ||
| requests: | ||
| cpu: 100m | ||
| memory: 256Mi | ||
| limits: | ||
| cpu: 500m | ||
| memory: 512Mi | ||
| # Persistent volume to store last run timestamp | ||
| persistence: | ||
| enabled: true | ||
| storageClass: "standard" | ||
| accessMode: ReadWriteOnce | ||
| size: 1Gi | ||
| mountPath: /data |
Oops, something went wrong.
Add this suggestion to a batch that can be applied as a single commit.
This suggestion is invalid because no changes were made to the code.
Suggestions cannot be applied while the pull request is closed.
Suggestions cannot be applied while viewing a subset of changes.
Only one suggestion per line can be applied in a batch.
Add this suggestion to a batch that can be applied as a single commit.
Applying suggestions on deleted lines is not supported.
You must change the existing code in this line in order to create a valid suggestion.
Outdated suggestions cannot be applied.
This suggestion has been applied or marked resolved.
Suggestions cannot be applied from pending reviews.
Suggestions cannot be applied on multi-line comments.
Suggestions cannot be applied while the pull request is queued to merge.
Suggestion cannot be applied right now. Please check back later.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Hmm, it looks like we're trying to read from
process.envhere but I don't see where we're setting these vars in the env setup?