Skip to content

Commit

Permalink
refactor job policy
Browse files Browse the repository at this point in the history
  • Loading branch information
jsbroks committed Oct 20, 2024
1 parent 91e73a5 commit ea14164
Show file tree
Hide file tree
Showing 10 changed files with 118 additions and 36 deletions.
Original file line number Diff line number Diff line change
@@ -1,21 +1,21 @@
name: Apps / Job Policy Checker
name: Apps / Jobs

on:
pull_request:
branches: ["*"]
paths:
- apps/job-policy-checker/**
- apps/jobs/**
- packages/job-dispatch/**
- packages/db/**
- .github/workflows/apps-job-policy-checker.yaml
- .github/workflows/apps-jobs.yaml
- pnpm-lock.yaml
push:
branches: ["main"]
paths:
- apps/job-policy-checker/**
- apps/jobs/**
- packages/job-dispatch/**
- packages/db/**
- .github/workflows/apps-job-policy-checker.yaml
- .github/workflows/apps-jobs.yaml
- pnpm-lock.yaml

jobs:
Expand Down Expand Up @@ -58,7 +58,7 @@ jobs:
id: meta
uses: docker/metadata-action@v4
with:
images: ctrlplane/job-policy-checker
images: ctrlplane/jobs
tags: |
type=sha,format=short,prefix=
Expand All @@ -67,7 +67,7 @@ jobs:
if: github.ref != 'refs/heads/main'
with:
push: false
file: apps/job-policy-checker/Dockerfile
file: apps/jobs/Dockerfile
platforms: ${{ matrix.platform }}
tags: ${{ steps.meta.outputs.tags }}

Expand All @@ -76,6 +76,6 @@ jobs:
if: github.ref == 'refs/heads/main' && env.DOCKERHUB_LOGIN == 'true'
with:
push: true
file: apps/job-policy-checker/Dockerfile
file: apps/jobs/Dockerfile
platforms: ${{ matrix.platform }}
tags: ${{ steps.meta.outputs.tags }}
13 changes: 0 additions & 13 deletions apps/job-policy-checker/src/config.ts

This file was deleted.

File renamed without changes.
File renamed without changes.
File renamed without changes.
Original file line number Diff line number Diff line change
@@ -1,5 +1,5 @@
{
"name": "@ctrlplane/job-policy-checker",
"name": "@ctrlplane/jobs",
"version": "0.1.0",
"private": true,
"type": "module",
Expand All @@ -15,8 +15,8 @@
"dependencies": {
"@ctrlplane/db": "workspace:*",
"@ctrlplane/job-dispatch": "workspace:*",
"@ctrlplane/logger": "workspace:*",
"@ctrlplane/validators": "workspace:*",
"@t3-oss/env-core": "catalog:",
"cron": "^3.1.7",
"zod": "catalog:"
},
Expand Down
104 changes: 104 additions & 0 deletions apps/jobs/src/index.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,104 @@
// Examples:
//
// Run specific jobs with default schedules:
// node index.js -j policy-checker
//
// Run jobs with policy-checker running every 5 minutes:
// node index.js -j "policy-checker=*/5 * * * *""
//
// Run all jobs once:
// node index.js -r

import { parseArgs } from "node:util";
import { CronJob } from "cron";
import { z } from "zod";

import { logger } from "@ctrlplane/logger";

import { run as jobPolicyChecker } from "./policy-checker/index.js";

const jobs: Record<string, { run: () => Promise<void>; schedule: string }> = {
"policy-checker": {
run: jobPolicyChecker,
schedule: "* * * * *", // Default: Every minute
},
};

const jobSchema = z.object({
job: z.array(z.string()),
runOnce: z.boolean().optional(),
});

const parseJobArgs = () => {
const { values } = parseArgs({
options: {
job: {
type: "string",
short: "j",
multiple: true,
},
runOnce: {
type: "boolean",
short: "r",
},
},
});
return jobSchema.parse(values);
};

const getJobConfig = (jobName: string) => {
const [job, schedule] = jobName.split("=");
const jobConfig = jobs[job ?? ""];
if (jobConfig == null)
throw new Error(`Job ${job} not found in configuration`);
return { job, schedule: schedule ?? jobConfig.schedule };
};

const parseJobSchedulePairs = (parsedValues: z.infer<typeof jobSchema>) => {
if (parsedValues.job.length > 0) return parsedValues.job.map(getJobConfig);
return Object.entries(jobs).map(([job, config]) => ({
job,
schedule: config.schedule,
}));
};

const runJob = async (job: string) => {
logger.info(`Running job: ${job}`);
try {
await jobs[job]?.run();
logger.info(`Job ${job} completed successfully`);
} catch (error) {
logger.error(`Error running job ${job}:`, error);
}
};

const main = () => {
const parsedValues = parseJobArgs();
const jobSchedulePairs = parseJobSchedulePairs(parsedValues);

if (jobSchedulePairs.length === 0) {
logger.info("No jobs specified to run.");
return;
}

logger.info(
`Starting jobs: ${jobSchedulePairs.map((pair) => pair.job).join(", ")}`,
);

for (const { job, schedule } of jobSchedulePairs) {
if (job == null) continue;

if (parsedValues.runOnce) {
logger.info(`Running job ${job} once`);
runJob(job);
continue;
}
const cronJob = new CronJob(schedule, () => runJob(job));

cronJob.start();
logger.info(`Scheduled job ${job} with cron: ${schedule}`);
runJob(job);
}
};

main();
Original file line number Diff line number Diff line change
@@ -1,5 +1,3 @@
import { CronJob } from "cron";

import { eq } from "@ctrlplane/db";
import { db } from "@ctrlplane/db/client";
import * as schema from "@ctrlplane/db/schema";
Expand All @@ -10,9 +8,7 @@ import {
} from "@ctrlplane/job-dispatch";
import { JobStatus } from "@ctrlplane/validators/jobs";

import { env } from "./config.js";

const run = async () => {
export const run = async () => {
const releaseJobTriggers = await db
.select()
.from(schema.releaseJobTrigger)
Expand All @@ -31,11 +27,3 @@ const run = async () => {
.then(cancelOldReleaseJobTriggersOnJobDispatch)
.dispatch();
};

const releaseJobTriggerPolicyChecker = new CronJob(env.CRON_TIME, run);

console.log("Starting job config policy checker cronjob");

run().catch(console.error);

if (env.CRON_ENABLED) releaseJobTriggerPolicyChecker.start();
File renamed without changes.
3 changes: 3 additions & 0 deletions pnpm-lock.yaml

Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.

0 comments on commit ea14164

Please sign in to comment.