Skip to main content
Last updated: 8 Sep 2026

JobRequest Architecture

System Components

  • govuk-job-request-operator: This includes:
  • OPA Gatekeeper policies responsible for validating API requests to create/update JobRequest and JobRequestReview resources, and providing useful error messages to the end user if the request is denied.
  • govuk-cli: A command line interface with a jobrequest subcommand which is the primary interface developers are expected to use to create and review JobRequests. This also does validation prior to making create requests to the Kubernetes API to give the most friendly error messages possible.

Expected usage

The following describes the intended happy path from creation of a JobRequest to Job completion.

  1. User A uses govuk-cli to create a JobRequest, it prints out the CLI command which can be used to review the JobRequest, and waits to show the logs from the Job that will be created later.
  2. User A sends User B the printed out CLI command to run in order to review the request.
  3. User B runs the CLI command, reads and reviews the command intended to run, chooses to approve or reject it, then govuk-cli creates a JobRequestReview.
  4. Review outcome: a. If user B rejects the JobRequest, assuming user A is still following the logs, user A sees a message telling them the request was rejected. This flow ends here. b. If user B approves the JobRequest then continue this process
  5. The JobRequest operator changes the Status of the JobRequest to approved.
  6. The JobRequest operator reads the pod spec it needs to create from the target resource specified in the JobRequest
  7. The JobRequest operator creates a Kubernetes Job from the pod spec retrieved in the previous step, overriding the command to be the one approved in the JobRequest
  8. The JobRequest operator updates the Status of the JobRequest to include the name of the Job that was created
  9. The govuk-cli command that was run in step 1. sees the Job has been created, informs user A, and starts printing out the logs of the Job as they are produced.
  10. The JobRequest operator watches the status of the Job and updates the JobRequest status to include the current state of the Job.
  11. When the status of the Job reaches a terminal state, the govuk-cli stops following the logs and informs User A the Job has completed.

Sequence Diagram for an Approved JobRequest



sequenceDiagram
    actor Requester
    actor Reviewer
    participant k8sAPI as Kubernetes API
    participant Operator

    Requester->>k8sAPI: `govuk-cli create jobrequest... --follow`
    k8sAPI->>Operator: Job Request Created
    Requester->>Reviewer: Please review my request
    Reviewer->>k8sAPI: `govuk-cli jobrequest review...` Approved
    k8sAPI->>Operator: JobRequestReview Created
    Operator->>k8sAPI: Create Job
    k8sAPI->>Requester: Logs
    k8sAPI->>Requester: More Logs
    k8sAPI->>Requester: Job Complete

Sequence Diagram for a Rejected JobRequest



sequenceDiagram
    actor Requester
    actor Reviewer
    participant k8sAPI as Kubernetes API
    participant Operator

    Requester->>k8sAPI: `govuk-cli create jobrequest... --follow`
    k8sAPI->>Operator: JobRequest Created
    Requester->>Reviewer: Please review my request
    Reviewer->>k8sAPI: `govuk-cli jobrequest review...` Rejected
    k8sAPI->>Operator: Rejected JobRequestReview Created
    Operator->>k8sAPI: Update JobRequest to Rejected
    k8sAPI->>Requester: JobRequest Rejected

Garbage Collection

Any time a JobRequest or JobRequestReview resource is presented for reconcilliation, if it has lived longer than the TTL duration (which is set for 720 hours (30 days)), it will be deleted.

The Kubernetes Controller Runtime configuration includes a SyncPeriod, any resources managed by the controller runtime will be presented to the operator every time the SyncPeriod has elapsed.

The SyncPeriod in the is configurable in the govuk-job-request-operator by setting the --resource-ttl flag.

Architecture Diagrams

JobRequest state diagram



stateDiagram
    state "''" as NoState

    [*] --> NoState
    NoState --> Malformed
    NoState --> Pending
    Pending --> Rejected
    Pending --> Approved
    Approved --> Started
    Approved --> Malformed
    Started --> Complete
    Started --> Failed
    Started --> Malformed
    Malformed --> [*]
    Complete --> [*]
    Failed --> [*]

JobRequestReview state diagram



stateDiagram
    [*] --> Approved
    [*] --> Rejected

    Approved --> JobRequestNotFound
    Approved --> JobRequestMalformed
    Approved --> Conflict

    Rejected --> JobRequestNotFound
    Rejected --> JobRequestMalformed
    Rejected --> Conflict

    Approved --> [*]
    Rejected --> [*]
    JobRequestNotFound --> [*]
    JobRequestMalformed --> [*]
    Conflict --> [*]

Creating a JobRequest Flowchart



flowchart TD
    RequesterA@{ shape: person }
    RequesterB@{ shape: person }
    govuk-cli

    RequesterA--govuk-cli jobrequest create ...-->govuk-cli
    govuk-cli--Create JobRequest-->k8sAPI

    RequesterB--Create JobRequest-->k8sAPI

    subgraph k8s[Kubernetes]
        direction TD

        k8sAPI[Kubernetes API]
        MutatingAdmissionPolicy
        gatekeeper[OPA Gatekeeper]
        govuk-job-request-operator[Operator JobRequest controller]
        etcd[(Etcd)]

        k8sAPI-- Create JobRequest -->MutatingAdmissionPolicy
        MutatingAdmissionPolicy--Create JobRequest-->gatekeeper
        gatekeeper--Create JobRequest-->etcd

        etcd--Created JobRequest-->govuk-job-request-operator

        govuk-job-request-operator-->validateJobRequest{Validate}
        validateJobRequest--valid\n\nSet JobRequest Pending-->k8sAPI
        validateJobRequest--invalid\n\nSet JobRequest Malformed-->k8sAPI
    end

Reviewing a JobRequest Flowchart



flowchart TD
    RequesterA@{ shape: person }
    RequesterB@{ shape: person }
    govuk-cli

    RequesterA--govuk-cli jobrequest review ...-->govuk-cli
    govuk-cli--Create JobRequestReview-->k8sAPI

    RequesterB--Create JobRequestReview-->k8sAPI

    subgraph k8s[Kubernetes]
        direction TD

        k8sAPI[Kubernetes API]
        MutatingAdmissionPolicy
        gatekeeper[OPA Gatekeeper]
        govuk-job-request-operator[Operator JobRequestReview controller]
        etcd[(Etcd)]

        k8sAPI-- Create JobRequestReview -->MutatingAdmissionPolicy
        MutatingAdmissionPolicy-- Create JobRequestReview -->gatekeeper
        gatekeeper-- Create JobRequestReview -->etcd

        etcd-- Created JobRequestReview -->govuk-job-request-operator

        govuk-job-request-operator-->validateJobRequestReview{Validate}
        validateJobRequestReview-- invalid\n\nSet JobRequestReview Malformed -->k8sAPI

        validateJobRequestReview-- valid -->jobRequestFound{JobRequest Exists?}
        jobRequestFound-- found\n\nSet state of JobRequestReview to Approved/Rejected\n\nSet state of JobRequest to Approved/Rejected -->k8sAPI
        jobRequestFound-- not-found\n\nSet state of JobRequestReview toJobRequestNotFound -->k8sAPI
    end

After a JobRequest has been Approved Flowchart



flowchart TD
    subgraph k8s[Kubernetes]
        direction TD

        k8sAPI[Kubernetes API]
        MutatingAdmissionPolicy
        gatekeeper[OPA Gatekeeper]
        govuk-job-request-operator[Operator JobRequest controller]
        etcd[(Etcd)]

        etcd-- 1. JobRequest Updated -->govuk-job-request-operator
        govuk-job-request-operator<-- 2. Get Pod/Deployment -->k8sAPI

        govuk-job-request-operator-- 3. Create Job -->k8sAPI
        k8sAPI-- 3. Create Job -->etcd

        govuk-job-request-operator-- 4. Watch Job -->k8sAPI

        etcd-- 5. Job State Change --> govuk-job-request-operator
        govuk-job-request-operator-- 5. Update JobRequest with Job State -->k8sAPI
        k8sAPI-- 5. Update JobRequest -->MutatingAdmissionPolicy
        MutatingAdmissionPolicy-- 5. Update JobRequest -->gatekeeper
        gatekeeper-- 5. Update JobRequest -->etcd

    end