-
Notifications
You must be signed in to change notification settings - Fork 122
Commit
This commit does not belong to any branch on this repository, and may belong to a fork outside of the repository.
Add CI README and CI architecture diagram
- Loading branch information
Showing
5 changed files
with
1,043 additions
and
0 deletions.
There are no files selected for viewing
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,126 @@ | ||
# Jenkins CI Architecture | ||
|
||
- [Jenkins CI Architecture](#jenkins-ci-architecture) | ||
- [Overview](#overview) | ||
- [Jenkins Best Practice](#jenkins-best-practice) | ||
- [Jenkins Immediate Dependencies](#jenkins-immediate-dependencies) | ||
- [Bash Scripts](#bash-scripts) | ||
- [`ci/test`](#citest) | ||
- [GKE Tests](#gke-tests) | ||
- [`ci/submit_coverage`](#cisubmit_coverage) | ||
- [`ci/parse-changelog`](#ciparse-changelog) | ||
- [Displaying Test Results](#displaying-test-results) | ||
- [`junit`](#junit) | ||
- [`publishHTML`](#publishhtml) | ||
|
||
## Overview | ||
|
||
<i>Note: The diagram's categories (system, container, component) follow the | ||
[C4 diagramming model](https://c4model.com/). In particular, the word | ||
"container" in this context is unrelated to Docker containers.</i> | ||
|
||
<!-- Note: This diagram can be re-regenerated by editing | ||
public/img/README_CI.puml. The VS Code PlantUML extension provides a good | ||
live-preview experience. --> | ||
![CI Architecture](./public/img/ci_architecture.svg) | ||
|
||
|
||
Our CI pipeline runs on Jenkins, and the main entrypoint is the `Jenkinsfile` | ||
in the root directory. | ||
|
||
The diagram above shows the relationships between the systems (external | ||
services) and software components (bash scripts, ruby gems, etc) used by | ||
Jenkins | ||
|
||
Jenkins has many dependencies, relies on multiple external systems, and | ||
delegates to many scripts to achieve its testing and reporting goals. | ||
|
||
## Jenkins Best Practice | ||
|
||
We try to keep the workload on the main Jenkins executor | ||
as light as possible, with individual tests | ||
or other CPU or memory intensive work running on separate agents. The main executor should be a coordinator, kicking off other processes and collecting | ||
their results. | ||
|
||
## Jenkins Immediate Dependencies | ||
|
||
The `Jenkinsfile` itself has 3 main dependencies: | ||
|
||
1. **Our many bash scripts.** These are located in the root directory root | ||
and under `ci`, and perform, or delegate, the real work of the CI -- running | ||
tests in cucumber or rspec, processing test results, and spinning up | ||
infrastructure in cases where Jenkins agents aren't available (see the GKE | ||
section). | ||
|
||
2. **Jenkins functions.** In addition to the [built-in Jenkins | ||
functionality](https://www.jenkins.io/doc/book/pipeline/syntax/), this | ||
includes a number of [custom Jenkins | ||
functions](https://github.com/conjurinc/jenkins-pipeline-library/tree/master/vars) | ||
that we maintain ourselves. | ||
|
||
One notable example is `scanAndReport`, which, with a single function call | ||
from the `Jenkinsfile`, runs vulnerability scans and reports their results. | ||
|
||
3. **Environment variables.** These are set automatically by Jenkins, | ||
depending on the context that triggers the CI run. It includes variables like | ||
`env.BRANCH_NAME` and `env.STAGE_NAME`, for example. [Here is a full | ||
list](https://devopsqa.wordpress.com/2019/11/19/list-of-available-jenkins-environment-variables/). | ||
|
||
More on these below. | ||
|
||
|
||
## Bash Scripts | ||
|
||
### `ci/test` | ||
|
||
The is the standard and recommended way to run a test suite. It should always | ||
be used when possible. Specific instructions for creating a new test suite, | ||
and for calling cucumber from within it, are in the top comments of the | ||
`ci/test` file. | ||
|
||
### GKE Tests | ||
|
||
Our Jenkins infrastructure does not provide GKE agents because: | ||
|
||
> GKE is kubernetes, and docker on kubernetes is a massive nightmare, and | ||
> Jenkins executors on kubernetes is a nightmare, and we wouldn’t really gain | ||
> anything beyond it running within the google cloud environment. | ||
> | ||
> -- Matthew Brace | ||
This means that to test on GKE we must handle spinning up GKE hosts manually. | ||
The code that does this lives under `ci/authn-k8s`. | ||
|
||
This GKE tests' entrypoint -- `ci/authn-k8s/test.sh` -- is an exception to | ||
the normal pattern. It hasn't yet been refactored to fit the `ci/test` | ||
pattern, but should be. | ||
|
||
### `ci/submit_coverage` | ||
|
||
This is the script that submits our coverage reports to CodeClimate. | ||
|
||
It's worth calling out, because it's more complex than you might expect. Its | ||
complexity stems from the constraint that the entire report must be sent to | ||
CodeClimate in one go. Because of this, we have to wait till all tests are | ||
complete and merge their json before sending them. | ||
|
||
### `ci/parse-changelog` | ||
|
||
This validates that the `CHANGELOG.md` is correctly formatted. It runs | ||
quickly and is invoked on every CI run. | ||
|
||
## Displaying Test Results | ||
|
||
### `junit` | ||
|
||
The Jenkins `junit` command is a plugin that processes test results and can | ||
then render them in different ways -- like this display in the Blue Ocean | ||
"Test" tab: | ||
|
||
![juint screenshot](./public/img/junit.png) | ||
|
||
### `publishHTML` | ||
|
||
Similarly, publishHTML makes test coverage reports available: | ||
|
||
![juint screenshot](./public/img/publishHTML.png) |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,71 @@ | ||
@startuml ci_architecture | ||
|
||
!include https://raw.githubusercontent.com/plantuml-stdlib/C4-PlantUML/master/C4_Context.puml | ||
!include https://raw.githubusercontent.com/plantuml-stdlib/C4-PlantUML/master/C4_Container.puml | ||
!include https://raw.githubusercontent.com/plantuml-stdlib/C4-PlantUML/master/C4_Component.puml | ||
|
||
title CI Architecture Overview | ||
|
||
' Person(personAlias, "Label", "Optional Description") | ||
' Boundary(bashScripts, "Bash Scripts") { | ||
' Container(bashTests, "Bash CI Scripts", "Wraps Rspec/Cucumber/Etc") | ||
' } | ||
|
||
' TODO: Preamble stuff, scans, build, code coverage, etc | ||
|
||
Component(jenkins, "Jenkins", "CI Test Runner") | ||
|
||
Container_Boundary(bashTests, "Bash CI Scripts") | ||
Container(validateChangelog, "ci/parse-changelog", "Validates Changelog") | ||
Container(build, "build.sh", "Docker Build Script") | ||
Container(runStandardTest, "ci/test", "Runs Named Test Suite") | ||
Container(shared, "ci/shared.sh", "Shared Test Suite Functions") | ||
Container(cucumber, "cucumber", "Cucumber Feature Tests") | ||
Container(ldap, "LDAP", "LDAP Test Server") | ||
Container(rspec, "Rspec", "Conjur Rspec Tests") | ||
Container(authnK8s, "GKE Tests", "Authn K8s GKE Tests") | ||
System(gke, "GKE Host", "Test Machine") | ||
Container(submitCoverage, "ci/submit_coverage", "Send coverage to CodeClimate") | ||
System(codeClimate, "Code Climate", "Code Quality SaaS") | ||
Container(pushImage, "push-image.sh", "Push images to registry") | ||
System(dockerRegistry, "Docker Regisry", "Registry hosting images") | ||
Container(publishImage, "publish-image.sh", "Publish Debian/RPM packages") | ||
System(packageRegistry, "Package Regisry", "Registry hosting Deb/RPM packages") | ||
|
||
Component(pipelineLib, "Pipeline Library", "Common Jenkins Functions") | ||
Component(scanAndReport, "Vulnerability Scan", "Uses scanAndReport") | ||
Component(envVars, "Env Vars", "Set by cron/context") | ||
Container_Boundary(agents, "Available Agents") | ||
System(baseAWS, "Base AWS", "AWS Host") | ||
System(awsFips, "AWS RHEL EE FIPS", "RHEL FIPS Host") | ||
System(gcp, "GCP", "GCP Host") | ||
System(azure, "Azure", "Azure Host") | ||
|
||
Rel(jenkins, bashTests, "Calls") | ||
Rel(bashTests, validateChangelog, "Calls") | ||
Rel(bashTests, build, "Calls") | ||
Rel(bashTests, runStandardTest, "Calls") | ||
Rel(runStandardTest, rspec, "Runs") | ||
Rel(runStandardTest, shared, "Uses") | ||
Rel(shared, cucumber, "Calls") | ||
Rel(shared, ldap, "Uses") | ||
Rel(runStandardTest, authnK8s, "Calls") | ||
Rel(authnK8s, gke, "Runs On") | ||
Component(authnK8s, "GKE Tests", "Authn K8s GKE Tests") | ||
System(gke, "GKE Host", "Test Machine") | ||
Rel(bashTests, submitCoverage, "Calls") | ||
Rel(submitCoverage, codeClimate, "Sends") | ||
Rel(bashTests, pushImage, "Calls") | ||
Rel(pushImage, dockerRegistry, "Sends") | ||
Rel(bashTests, publishImage, "Calls") | ||
Rel(publishImage, packageRegistry, "Sends") | ||
Rel(jenkins, pipelineLib, "Uses") | ||
Rel(pipelineLib, scanAndReport, "Provides") | ||
Rel(jenkins, envVars, "Uses") | ||
Rel(jenkins, agents, "Has") | ||
Rel(agents, baseAWS, "Has") | ||
Rel(agents, awsFips, "Has") | ||
Rel(agents, gcp, "Has") | ||
Rel(agents, azure, "Has") | ||
|
||
@enduml |
Oops, something went wrong.