Skip to content

Conversation

@cmyers-mieweb
Copy link
Collaborator

Introduces ScheduledJob model, migrations, and seeder for cron-based job scheduling. Updates job-runner to process scheduled jobs and create pending jobs when schedule conditions are met. Adds support for defaultStorage on nodes, including migration, model, and form update. Implements oci-build-job utility to pull OCI container images to Proxmox nodes using API credentials and storage configuration.

Introduces ScheduledJob model, migrations, and seeder for cron-based job scheduling. Updates job-runner to process scheduled jobs and create pending jobs when schedule conditions are met. Adds support for defaultStorage on nodes, including migration, model, and form update. Implements oci-build-job utility to pull OCI container images to Proxmox nodes using API credentials and storage configuration.
@cmyers-mieweb
Copy link
Collaborator Author

cmyers-mieweb commented Dec 10, 2025


Scheduled Jobs / OCI Image Pull Integration

Summary

  • Add a new ScheduledJobs Sequelize model/migration
    Columns: id, schedule (STRING, cron expression), command (STRING), lastRunAt (DATETIME).
    Purpose: DB-driven cron-style scheduled tasks.

  • Add defaultStorage column to Node model
    Allows OCI pulls to target the appropriate Proxmox storage.

  • Seed a ScheduledJob
    Runs the OCI image pull job (create-a-container/utils/oci-build-job.js) for Debian 13 and Rocky 9.

  • Extend the job-runner
    Evaluates ScheduledJobs before claiming pending jobs and creates new pending Job records when schedules fire.

  • oci-build-job implemented
    Pulls OCI LXC images directly to Proxmox nodes (no Packer).


Why

  • Enables recurring automated OCI LXC image pulls for configured Proxmox nodes.
  • Centralizes scheduling in the database — configurable via API/UI.
  • Keeps job-runner behavior unchanged by generating standard "pending" Job entries.

Files Changed (High-Level)

Migrations

  • create-a-container/migrations/XXXXXX-create-scheduled-jobs.js
  • create-a-container/migrations/XXXXXX-add-default-storage-to-nodes.js

Models

  • create-a-container/models/scheduledjob.js
  • node.js (added defaultStorage)

Seeders

  • create-a-container/seeders/XXXXXX-seed-oci-scheduledjob.js

Job Runner

  • job-runner.js (added processScheduledJobs, uses cron-parser)

Utils

  • oci-build-job.js (invoked by ScheduledJob command)

UI

  • form.ejs (added defaultStorage field)

package.json

  • Added cron-parser dependency

Architecture Diagram (Mermaid)

graph LR
  A[ScheduledJobs] -->|"evaluated by"| B[Job Runner]
  B -->|"on match"| C[Jobs (pending)]
  C -->|"claimed by"| D[Runner Execution]
  D -->|"executes"| E[OCI Build Job Script]
  E -->|"uses"| F[Node Model (apiUrl, token, defaultStorage)]

Loading

Implementation Notes (Concise)

ScheduledJobs Table Columns

  • id INTEGER PK AUTOINCREMENT
  • schedule STRING (cron expression, e.g., "0 2 * * *")
  • command TEXT/STRING
  • lastRunAt DATETIME NULL
  • createdAt / updatedAt

Job Runner: processScheduledJobs()

  • Loads all ScheduledJobs
  • Uses cron-parser to check if now matches
  • Compares with lastRunAt to avoid duplicate runs
  • Inserts a record in Jobs table with status "pending"
  • Updates lastRunAt

Seeded Command

Typically runs:

node oci-build-job.js

Node Model

  • Added defaultStorage (STRING, NULL)

Step-by-Step Setup (Windows Dev)

Install dependencies

cd c:\Users\cmyers\Documents\GitHub\opensource-server\create-a-container
npm install

Add cron-parser

npm install cron-parser --save

Run migrations

npm run db:migrate
# or:
npx sequelize db:migrate

Seed ScheduledJob

npm run db:seed:all
# or:
npx sequelize db:seed:all

Start job runner

node create-a-container/job-runner.js

Watch logs for scheduled job creation and OCI pull runs.


Testing Instructions

Quick Functional Test

  1. Create a ScheduledJob with:

    * * * * *
    
  2. Command:

    node oci-build-job.js
    
  3. Start job-runner → expect new pending job every minute.

Manual run of OCI job

node create-a-container/utils/oci-build-job.js

Verify

  • DB Jobs table inserts
  • Per-node pull attempts logged
  • Pull success/failure messages appear

Suggested Unit / Integration Tests

  • Model tests
    Validate cron string correctness, lastRunAt updating logic.

  • processScheduledJobs tests
    Mock DB + cron-parser, assert inserts.

  • End-to-end
    Disposable DB → runner → assert job gets created.

  • OCI pull smoke test
    Mock Proxmox API endpoint for /pull-image.


DB / Migration Verification Checklist

  • ScheduledJobs table exists
  • Nodes.defaultStorage column exists
  • Seeded OCI ScheduledJob created

Admin UI

  • Node create/edit form updated
  • defaultStorage field now selectable/editable

Operational Notes

  • Job-runner should run at least every 30–60 seconds.
  • Cron expressions evaluated in server local time unless otherwise configured.
  • lastRunAt is minute-granular to avoid duplicate runs.

Rollback

  • Migration down scripts remove both new changes.
  • Seeder undo removes the OCI ScheduledJob.

Security Notes

  • Scheduled job commands run with full job-runner privileges.
  • Restrict ScheduledJob management to trusted admins.
  • Protect DB tokens & secrets.

Files to Review

  • create-a-container/migrations/XXXX-create-scheduled-jobs.js
  • create-a-container/models/scheduledjob.js
  • create-a-container/migrations/XXXX-add-default-storage-to-nodes.js
  • node.js
  • create-a-container/seeders/XXXX-seed-oci-scheduledjob.js
  • job-runner.js
  • oci-build-job.js

Acceptance Criteria

  • ScheduledJobs and defaultStorage exist in schema
  • Job-runner enqueues Jobs based on schedule
  • Seeded OCI job runs successfully
  • Admin UI supports defaultStorage
  • Automated tests cover scheduling + validation

Notes / TODOs

  • Define timezone behavior (UTC vs local)
  • Consider adding timezone field per schedule
  • Consider adding enabled BOOLEAN
  • Add audit logs for schedule → job creation


<div class="mb-3">
<label for="defaultStorage" class="form-label">Default Storage</label>
<input
Copy link
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Has the router been updated to handle this input?

Carter Myers added 3 commits December 11, 2025 15:30
Introduces a script to build and push site-specific OCI images using Docker, a seeder to schedule this job, and a Debian Dockerfile template. Updates oci-build-job.js to prefer LOCAL_REGISTRY for image registry. The README is updated to reference the local registry in example commands.
Replaces separate oci-build-job and build-push-oci scripts with a single oci-build-push-pull job that builds, pushes, and pulls OCI images for all sites and nodes. Updates scheduled job seeders to use the new script and introduces shared Proxmox utilities for task polling and image pulling. Old scripts and their references are removed or deprecated for migration rollback support.
Moved and consolidated the OCI build/push/pull logic into the bin/oci-build-push-pull.js file, removing the old utils/oci-build-push-pull.js and proxmox-utils.js modules. Updated the Dockerfile template to use Proxmox's minimal LXC rootfs for more accurate OCI images. Improved CLI argument parsing and Proxmox API utilities for image pulling and storage selection. Updated README to use ghcr.io for container pulls. Removed obsolete seeder for build-push-oci job.
* Reads environment variables to determine registry and image tags.
* @returns {Array<{name: string, registry: string, image: string, tag: string}>} List of pre-built image specifications
*/
// No pre-built images are used; we only operate on site-built images.
Copy link
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

This comment + the JSDoc comment above refer to a now non-existent function. Please remove them

* @throws {Error} If docker build or push fails
*/
async function buildAndPushImageForSite(site, registry, repoBase, buildContext, dockerfilePath, tagSuffix = 'latest') {
const domain = site.internalDomain || site.domain || site.name || `site-${site.id}`;
Copy link
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Not sure why my previous comment on this line was marked outdated, but it stands. site.internalDomain is guaranteed defined and not null due to the database schema so these fallbacks are unnessecary.

&& /usr/local/bin/pown.sh "$DOMAIN"
# Optional: allow customizations at build-time (example: run site installer)
ARG DOMAIN
RUN true
Copy link
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

We don't need this part (the ARG and the RUN) since pown.sh is running above. We can stop this Dockerfile after the COPY

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

3 participants