Skip to content

Add public RenderJob() function returning Job specs #734

Description

@xdu31

Overview

Add a public Go package export that returns Kubernetes Job specs for validators without deploying them. This enables external controllers to use AICR as a validation planning engine.

Go Package Design

Primary consumer function:

// RenderJob returns a ready-to-deploy Kubernetes Job spec for a validator.
// This is the primary contract for external consumers.
func RenderJob(ctx context.Context, config ValidationConfig, catalog ValidatorCatalog,
    snapshotCMRef, recipeCMRef string, opts ...Option) (*batchv1.Job, error)

Internal/advanced function:

// Plan returns a JobPlan data structure (internal representation).
// This is exposed for:
// 1. AICR internal testability (pure function testing)
// 2. Advanced consumers who need to inspect the plan before rendering
//
// Most consumers should use RenderJob() instead.
func Plan(ctx context.Context, config ValidationConfig, catalog ValidatorCatalog,
    snapshotCMRef, recipeCMRef string, opts ...Option) (*JobPlan, error)

Rationale

Why RenderJob() as primary contract:

  • Prevents consumer divergence from AICR's renderer (addressing review feedback)
  • Simple one-call function for consumers
  • Version pinning in go.mod handles breaking changes
  • Terminology note: This is Go package consumption, not an API (per architectural guidance)

Why expose Plan() at all:

  • Internal testability (pure function without K8s types)
  • Advanced consumers can inspect before rendering
  • Not required for basic usage

JobPlan Structure

JobPlan is an internal data structure not intended for consumer manipulation:

type JobPlan struct {
    ValidatorName   string
    Image          string
    Command        []string
    Args           []string
    Env            []corev1.EnvVar
    Volumes        []corev1.Volume
    VolumeMounts   []corev1.VolumeMount
    Resources      corev1.ResourceRequirements
    // ... other fields
}

Contract: JobPlan structure may evolve in minor versions. Consumers should not build custom renderers from JobPlan - use RenderJob() instead.

Example Usage

Basic Consumer (Controller)

// Simple: just call RenderJob
job, err := validator.RenderJob(ctx, config, catalog, snapCM, recipeCM)
if err != nil {
    return err
}

// Deploy
_, err = clientset.BatchV1().Jobs(ns).Create(ctx, job, metav1.CreateOptions{})

Advanced Consumer (Custom Inspection)

// Advanced: inspect plan before rendering
plan, err := validator.Plan(ctx, config, catalog, snapCM, recipeCM)
if err != nil {
    return err
}

// Inspect plan (e.g., for dry-run reporting)
fmt.Printf("Will run validator: %s with image: %s\n", plan.ValidatorName, plan.Image)

// Still use AICR's renderer (don't build custom)
job := validator.RenderJobFromPlan(plan)

Acceptance Criteria

  • RenderJob() function exported from pkg/validator/job/ package
  • Returns *batchv1.Job ready for deployment
  • Plan() function exported but documented as internal/advanced use
  • JobPlan struct exported with godoc warning about stability
  • Unit tests for RenderJob() (integration)
  • Unit tests for Plan() (pure function)
  • Godoc clearly states RenderJob() is primary contract
  • Godoc clarifies this is Go package consumption (not an API)
  • Example code in package documentation

Related

Metadata

Metadata

Assignees

No one assigned

    Labels

    Type

    No type

    Fields

    No fields configured for issues without a type.

    Projects

    No projects

    Milestone

    No milestone

    Relationships

    None yet

    Development

    No branches or pull requests

    Issue actions