diff --git a/package.json b/package.json index 23aa363f4f4..9768df67e65 100644 --- a/package.json +++ b/package.json @@ -5,6 +5,6 @@ "author": "Government Digital Service", "license": "MIT", "dependencies": { - "mermaid": "^11.16.1" + "mermaid": "^11.17.2" } } diff --git a/source/kubernetes/job-requests/architecture/index.html.md b/source/kubernetes/job-requests/architecture/index.html.md index 11013a669a0..0425cd1b782 100644 --- a/source/kubernetes/job-requests/architecture/index.html.md +++ b/source/kubernetes/job-requests/architecture/index.html.md @@ -1,9 +1,278 @@ --- -title: Job Request Architecture +title: JobRequest Architecture weight: 10 layout: multipage_layout --- -# Job Request Architecture +# JobRequest Architecture -Coming soon... +## System Components + +* [govuk-job-request-operator](/repos/govuk-job-request-operator.html): + This includes: + * [Custom Resource Defitinitions + (CRDs)](https://kubernetes.io/docs/concepts/extend-kubernetes/api-extension/custom-resources/) + for JobRequest (short name `jr`) and JobRequestReview (short name `jrr`) + resources + * [Kubernetes + Operator](https://kubernetes.io/docs/concepts/extend-kubernetes/operator/) + which is responsible for reconcilling the JobRequest and JobRequestReview + resources + * [Kubernetes Mutating Admission + Policy](https://kubernetes.io/docs/reference/access-authn-authz/mutating-admission-policy/) + which is responsible for validating, and adding the AWS ARN of the + requesting user into annotations on the JobRequest and JobRequestReview + during admission + * [Garbage + collection](https://kubernetes.io/docs/concepts/architecture/garbage-collection/) + to clean up JobRequest and JobRequestReview resoureces more than 30 days old +* [OPA + Gatekeeper](https://kubernetes.io/blog/2019/08/06/opa-gatekeeper-policy-and-governance-for-kubernetes/) + [policies](https://github.com/alphagov/govuk-infrastructure/tree/main/terraform/deployments/cluster-services/modules/gatekeeper) + 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](/repos/govuk-job-request-operator.html): 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. +9. The JobRequest operator watches the status of the Job and updates the JobRequest status to include the current state of the Job. +10. 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](https://pkg.go.dev/sigs.k8s.io/controller-runtime/pkg/cache#Config)
+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](https://github.com/alphagov/govuk-job-request-operator#configuration)
+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
+
+
+
diff --git a/source/kubernetes/job-requests/index.html.md b/source/kubernetes/job-requests/index.html.md
index a38d913752e..48a15693546 100644
--- a/source/kubernetes/job-requests/index.html.md
+++ b/source/kubernetes/job-requests/index.html.md
@@ -9,10 +9,33 @@ layout: multipage_layout
If you are looking for how to create or review JobRequests, see the
[JobRequests User Guide](/kubernetes/job-requests/user-guide/)
+If you are looking to understand how the JobRequest system works, see [the
+JobRequest architecture guide](/kubernetes/job-requests/architecture).
+
## What is the JobRequest system
-Coming soon...
+The JobRequest system allows for a user to run commands within containers in
+Kubernetes in a way which is:
+
+* Peer reviewed
+* Audited
+* Has isolated logs not mixed with regular application logs
+* Easy to use
+* Easy to see the logs for an executed command
+* Does not require the user to create and understand kubernetes manifests
+* Is garbage collected after a month
+
+## What is the process for executing a Job using JobRequests
+
+1. A user creates a JobRequest to run a command in Kubernetes, which will be
+ run in a Pod that is a copy of an existing Pod or Deployment.
+2. A different user reviews that request (either approving or rejecting).
+3. If approved a Kubernetes Job will be created which:
+ * Has its Pod spec copied from the requested Pod/Deployment
+ * Has the command of the Pod overridden with the command the user requested
## Why might you use it
-Coming soon...
+* To execute a Rake task in a Kubernetes cluster
+* To receive a peer review for a command you are going to run in a Kubernetes cluster
+* To have logs produced so you can see what your command did
diff --git a/yarn.lock b/yarn.lock
index 614346eef49..b02b930ff34 100644
--- a/yarn.lock
+++ b/yarn.lock
@@ -34,10 +34,10 @@
"@iconify/types" "^2.0.0"
mlly "^1.8.0"
-"@mermaid-js/parser@^1.2.0":
- version "1.2.0"
- resolved "https://registry.yarnpkg.com/@mermaid-js/parser/-/parser-1.2.0.tgz#266d728c54d2d4034d270f8b31d790e26296a5fa"
- integrity sha512-oYPyv8A4As1yH5Bx+04iQEQxXuIQDe0GKCNSRgao6z8AM9jixXIfP0vsppRLvGf+nKIOb9/LdpWA4YuJiVvESA==
+"@mermaid-js/parser@^1.2.1":
+ version "1.2.1"
+ resolved "https://registry.yarnpkg.com/@mermaid-js/parser/-/parser-1.2.1.tgz#94cc40416137bb10d2bc1b0a49c623d716e76233"
+ integrity sha512-n12NohV3mrUyUL2o93IgG/ifeW9FTyeJn3zDxkhwa8MJ9Fxg3HQMlA3RiGmD/3UnJvheztkjjQAjA2T4LmUcpw==
dependencies:
"@chevrotain/types" "~11.1.2"
@@ -322,10 +322,10 @@ cytoscape-fcose@^2.2.0:
dependencies:
cose-base "^2.2.0"
-cytoscape@^3.33.3:
- version "3.34.0"
- resolved "https://registry.yarnpkg.com/cytoscape/-/cytoscape-3.34.0.tgz#5fbe2eb1cf76b070a8ecd5647c35f65aa097c9c6"
- integrity sha512-62rNSrioXw93uliKFBwjukeQyeWwH2PqDrTac31r2P6464u3AUvTk0xS4LVvT251g7IgkFunrI48ZEZGjywSOg==
+cytoscape@^3.34.0:
+ version "3.34.2"
+ resolved "https://registry.yarnpkg.com/cytoscape/-/cytoscape-3.34.2.tgz#295edfc876b87bd971de3ac7a9d65c6be18f4ad6"
+ integrity sha512-Cm2jaj1X/PBNlzV9yH8zcfGOxO7U+CJ/+mxSBVPSchLaugdp4jtlGx5qaHtPRZ6tgiZ5P+o1XoRfJA+ba6KM3g==
"d3-array@1 - 2":
version "2.12.1"
@@ -606,10 +606,10 @@ dagre-d3-es@7.0.14:
d3 "^7.9.0"
lodash-es "^4.17.21"
-dayjs@^1.11.20:
- version "1.11.21"
- resolved "https://registry.yarnpkg.com/dayjs/-/dayjs-1.11.21.tgz#57f87562e62de76f3c704bd2b8d522fc33068eb2"
- integrity sha512-98IT+HOahAisibz/yjKbzuOBwYcjJ7BCLPzARyHiyEBmRz4fatF+KPJszEHXsGYjUG234aH/cOjW1wwTbKUZlA==
+dayjs@^1.11.21:
+ version "1.11.23"
+ resolved "https://registry.yarnpkg.com/dayjs/-/dayjs-1.11.23.tgz#b0a363506dde5f36cf5075e42ebe8115165a8c79"
+ integrity sha512-QDTCU0M0MxR3hQfnlDJfwekQiaanm1ubOD231u73WBckQ/fsamwRLiE2GBz6D3a/xF1NgfiDLJjXBa1hYOYTtQ==
delaunator@5:
version "5.0.1"
@@ -630,6 +630,13 @@ es-toolkit@^1.45.1:
resolved "https://registry.yarnpkg.com/es-toolkit/-/es-toolkit-1.46.1.tgz#38ca27191a98a867fc544b81cf1477a68947fb06"
integrity sha512-5eNtXOs3tbfxXOj04tjjseeWkRWaoCjdEI+96DgwzZoe6c9juL49pXlzAFTI72aWC9Y8p7168g6XIKjh7k6pyQ==
+fastdom@1.0.12:
+ version "1.0.12"
+ resolved "https://registry.yarnpkg.com/fastdom/-/fastdom-1.0.12.tgz#ae43d55af017252ae499b2e186511ab97412de39"
+ integrity sha512-LB+xjSTEbjHE1cWsxu+tN2Xqr1kpi+V9aADI7sVM5ZMaXyYGPHULQMzpJMYqOTULK/73pUkWVzzObFRBkPr+hg==
+ dependencies:
+ strictdom "^1.0.1"
+
hachure-fill@^0.5.2:
version "0.5.2"
resolved "https://registry.yarnpkg.com/hachure-fill/-/hachure-fill-0.5.2.tgz#d19bc4cc8750a5962b47fb1300557a85fcf934cc"
@@ -652,7 +659,7 @@ internmap@^1.0.0:
resolved "https://registry.yarnpkg.com/internmap/-/internmap-1.0.1.tgz#0017cc8a3b99605f0302f2b198d272e015e5df95"
integrity sha512-lDB5YccMydFBtasVtxnZ3MRBHuaoE8GKsppq+EchKL2U4nK/DmEpPHNH8MZe5HkMtpSiTSOZwfN0tzYjO/lJEw==
-katex@^0.16.45:
+katex@^0.16.47:
version "0.16.47"
resolved "https://registry.yarnpkg.com/katex/-/katex-0.16.47.tgz#0a13a42c2deb4f74e61f162d440b9165a548030f"
integrity sha512-Eeo8Ys1doU1z+x8AZsPpQu+p/QcZBI5PeOo7QGQdy2x2m0MU/hYagBbGOmXwr5KVbEfVuWv9LpnQWeehogurjg==
@@ -684,26 +691,27 @@ marked@^16.3.0:
resolved "https://registry.yarnpkg.com/marked/-/marked-16.4.2.tgz#4959a64be6c486f0db7467ead7ce288de54290a3"
integrity sha512-TI3V8YYWvkVf3KJe1dRkpnjs68JUPyEa5vjKrp1XEEJUAOaQc+Qj+L1qWbPd0SJuAdQkFU0h73sXXqwDYxsiDA==
-mermaid@^11.16.1:
- version "11.16.1"
- resolved "https://registry.yarnpkg.com/mermaid/-/mermaid-11.16.1.tgz#57ae2342f6c45b967113b04c9258430bdd057ee8"
- integrity sha512-TQsq6u22fAn3rek5VOubrhKPo1g5hwC3FXUN9hiyupTckcYiGuuKGkNQrKYwGJkXUxZdojwRG46gsSCFZMDp4g==
+mermaid@^11.17.2:
+ version "11.17.2"
+ resolved "https://registry.yarnpkg.com/mermaid/-/mermaid-11.17.2.tgz#e3caf3717582c0e44e5d04cff043e025caec7cba"
+ integrity sha512-V6K3C8EBdEsPFZXSKMJe6ppQOENxuHARr9GvHX4hh47lAbhMRD9qf4oEK7LoaRQxULMa80/qt5gHO73aCleBBg==
dependencies:
"@braintree/sanitize-url" "^7.1.2"
"@iconify/utils" "^3.0.2"
- "@mermaid-js/parser" "^1.2.0"
+ "@mermaid-js/parser" "^1.2.1"
"@types/d3" "^7.4.3"
"@upsetjs/venn.js" "^2.0.0"
- cytoscape "^3.33.3"
+ cytoscape "^3.34.0"
cytoscape-cose-bilkent "^4.1.0"
cytoscape-fcose "^2.2.0"
d3 "^7.9.0"
d3-sankey "^0.12.3"
dagre-d3-es "7.0.14"
- dayjs "^1.11.20"
+ dayjs "^1.11.21"
dompurify "^3.3.3"
es-toolkit "^1.45.1"
- katex "^0.16.45"
+ fastdom "1.0.12"
+ katex "^0.16.47"
khroma "^2.1.0"
marked "^16.3.0"
roughjs "^4.6.6"
@@ -793,6 +801,11 @@ rw@1:
resolved "https://registry.yarnpkg.com/safer-buffer/-/safer-buffer-2.1.2.tgz#44fa161b0187b9549dd84bb91802f9bd8385cd6a"
integrity sha512-YZo3K82SD7Riyi0E1EQPojLz7kpepnSQI9IyPbHHg1XXXevb5dJI7tpyN2ADxGcQbHG7vcyRHk0cbwqcQriUtg==
+strictdom@^1.0.1:
+ version "1.0.1"
+ resolved "https://registry.yarnpkg.com/strictdom/-/strictdom-1.0.1.tgz#189de91649f73d44d59b8432efa68ef9d2659460"
+ integrity sha512-cEmp9QeXXRmjj/rVp9oyiqcvyocWab/HaoN4+bwFeZ7QzykJD6L3yD4v12K1x0tHpqRqVpJevN3gW7kyM39Bqg==
+
stylis@^4.3.6:
version "4.3.6"
resolved "https://registry.yarnpkg.com/stylis/-/stylis-4.3.6.tgz#7c7b97191cb4f195f03ecab7d52f7902ed378320"