Compare commits

..

No commits in common. "master" and "v0.5.0" have entirely different histories.

500 changed files with 97971 additions and 40111 deletions

View File

@ -1,63 +0,0 @@
// For format details, see https://aka.ms/devcontainer.json
{
"name": "Jenkins kubernetes operator devcontainer",
"image": "mcr.microsoft.com/devcontainers/base:bookworm",
"features": {
"ghcr.io/devcontainers/features/docker-in-docker:2": {
"enableNonRootDocker": "true",
"moby": "true"
},
"ghcr.io/devcontainers/features/kubectl-helm-minikube:1": {
"version": "latest",
"helm": "latest",
"minikube": "none"
},
"ghcr.io/devcontainers/features/go:1": {
"version": "1.22",
"golangciLintVersion": "1.58.2"
},
"ghcr.io/mpriscella/features/kind:1": {
"version": "latest"
},
"ghcr.io/edouard-lopez/devcontainer-features/bats:0": {
"version": "latest"
},
"ghcr.io/brokenpip3/devcontainers-bats/bats-libs:0": {
},
"ghcr.io/devcontainers/features/nix:1": {
"multiUser": "false",
"extraNixConfig": "experimental-features = nix-command flakes"
},
"ghcr.io/devcontainers/features/hugo:1": {
"version": "v0.99.1"
}
},
// "forwardPorts": [],
"postCreateCommand": "go version",
// "postStartCommand": "nohup bash -c 'minikube start &' > minikube.log 2>&1",
// Configure tool-specific properties.
"customizations": {
"codespaces": {
"openFiles": [
"Makefile"
]
},
// install some vscode extensions
"vscode": {
"extensions": [
"golang.Go",
"jetmartin.bats",
"ms-kubernetes-tools.vscode-kubernetes-tools",
"budparr.language-hugo-vscode",
"GitHub.copilot",
"GitHub.copilot-chat"
]
}
},
// "remoteUser": "root"
}

View File

@ -1,5 +0,0 @@
# More info: https://docs.docker.com/engine/reference/builder/#dockerignore-file
# Ignore all files which are not go type
!**/*.go
!**/*.mod
!**/*.sum

1
.envrc
View File

@ -1 +0,0 @@
has nix && use flake

View File

@ -1,22 +0,0 @@
---
name: "🐛 Bug report"
about: Create a report to help us improve
title: ''
labels: 'bug'
projects: ''
assignees: ''
---
**Describe the bug**
A clear and concise description of what the bug is.
**To Reproduce**
Steps to reproduce the behavior.
**Additional information**
Kubernetes version:
Jenkins Operator version:
Add error logs about the problem here (operator logs and Kubernetes events).

View File

@ -1,14 +0,0 @@
---
name: "📚 Documentation issue"
about: Suggest changes to project's documentation
title: ''
labels: 'documentation'
projects: ''
assignees: ''
---
**Relevant links**
Link(s) to the section(s) of documentation that are outdated or otherwise wrong
**Description**
Description of the changes you would like to see

View File

@ -1,17 +0,0 @@
---
name: "🚀 Feature request"
about: Suggest an idea for this project
title: ''
labels: 'enhancement'
projects: ''
assignees: ''
---
**Describe the solution you'd like**
A clear and concise description of what you want to happen.
**Describe alternatives you've considered**
A clear and concise description of any alternative solutions or features you've considered.
**Additional context**
Add any other context about the feature request here e.g. external links or diagrams, etc.

View File

@ -1,28 +0,0 @@
version: 2
updates:
- package-ecosystem: "gomod"
directory: "/"
schedule:
interval: "daily"
groups:
golang:
patterns:
- "*"
- package-ecosystem: "npm"
directory: "/website"
schedule:
interval: "daily"
groups:
npm:
patterns:
- "*"
- package-ecosystem: "github-actions"
directory: "/"
schedule:
interval: "daily"
groups:
actions:
patterns:
- "*"

27
.github/issue_template.md vendored Normal file
View File

@ -0,0 +1,27 @@
# Expected Behavior
# Actual Behavior
# Steps to Reproduce the Problem
1.
2.
3.
# Additional Info
- Kubernetes version:
**Output of `kubectl version`:**
```
(paste your output here)
```
- Jenkins Operator version:
```
(paste your output here)
```
<!-- Any other additional information -->

View File

@ -15,3 +15,20 @@ review them:
- [ ] Commit messages follow [commit message best practices](https://github.com/jenkinsci/kubernetes-operator/blob/master/CONTRIBUTING.md#commit-messages) - [ ] Commit messages follow [commit message best practices](https://github.com/jenkinsci/kubernetes-operator/blob/master/CONTRIBUTING.md#commit-messages)
_See [the contribution guide](https://github.com/jenkinsci/kubernetes-operator/blob/master/CONTRIBUTING.md) for more details._ _See [the contribution guide](https://github.com/jenkinsci/kubernetes-operator/blob/master/CONTRIBUTING.md) for more details._
## Reviewer Notes
If API changes are included, additive changes must be approved by at least two [OWNERS](https://github.com/jenkinsci/kubernetes-operator/blob/master/OWNERS) and backwards incompatible changes must be approved by [more than 50% of the OWNERS](https://github.com/jenkinsci/kubernetes-operator/blob/master/OWNERS).
# Release Notes
```
Describe any user facing changes here, or delete this block.
Examples of user facing changes:
- API changes
- Bug fixes
- Any changes in behavior
```

View File

@ -1,99 +0,0 @@
name: Website
on:
push:
branches:
- master
- main
paths:
- 'website/**'
- 'assets/**'
pull_request:
types: [opened, synchronize, ready_for_review, reopened]
paths:
- 'website/**'
- 'assets/**'
permissions:
contents: read
pages: write
id-token: write
concurrency:
group: "website"
cancel-in-progress: false
defaults:
run:
shell: bash
jobs:
update-date:
name: Auto update dates
runs-on: ubuntu-latest
if: github.event_name == 'pull_request' || (github.ref == 'refs/heads/main' || github.ref == 'refs/heads/master')
steps:
- uses: DeterminateSystems/nix-installer-action@e50d5f73bfe71c2dd0aa4218de8f4afa59f8f81d # v16
with:
diagnostic-endpoint: ""
- name: Checkout code
uses: actions/checkout@11bd71901bbe5b1630ceea73d27597364c9af683 # v4
- name: Check for changes
run: |
IS_CHANGED=$(git diff --name-only ${{ github.event.before }} ${{ github.sha }} | grep -Ec "^website*" || :)
[[ $IS_CHANGED -gt 0 ]] && echo "IS_CHANGED=true" >> $GITHUB_ENV || echo "IS_CHANGED=false" >> $GITHUB_ENV
- name: Update last modified date in modified docs
if: env.IS_CHANGED == 'true'
run: |
git diff --name-only --diff-filter=d ${{ github.event.before }} ${{ github.sha }} | grep -E "^website*" \
| sed -e 's/\(.*\)/"\1"/' | xargs sed -i "/date:/c\date: $(date +'%Y-%m-%d')"
- name: Create Pull Request
uses: peter-evans/create-pull-request@271a8d0340265f705b14b6d32b9829c1cb33d45e # v6
if: env.IS_CHANGED == 'true' && (github.ref == 'refs/heads/main' || github.ref == 'refs/heads/master')
with:
commit-message: Auto-updated docs
branch: docs-generator
title: Auto-generated docs update
body: |
Auto generated docs from master commit ${{ github.sha }}
website-generate:
name: Auto generate website
runs-on: ubuntu-latest
if: github.event_name == 'pull_request' || (github.ref == 'refs/heads/main' || github.ref == 'refs/heads/master')
steps:
- uses: DeterminateSystems/nix-installer-action@e50d5f73bfe71c2dd0aa4218de8f4afa59f8f81d # v16
with:
diagnostic-endpoint: ""
- name: Checkout code
uses: actions/checkout@11bd71901bbe5b1630ceea73d27597364c9af683 # v4
- name: nix checks
run: nix flake check
- name: nix build
env:
HUGO_ENVIRONMENT: production
HUGO_ENV: production
run: nix build .#website
- name: Setup Pages
id: pages
uses: actions/configure-pages@983d7736d9b0ae728b81ab479565c72886d7745b # v5
- name: Upload artifact
uses: actions/upload-pages-artifact@56afc609e74202658d3ffba0e8f6dda462b719fa # v3
with:
path: ./result
website-deploy:
name: Deploy website
if: github.ref == 'refs/heads/main' || github.ref == 'refs/heads/master'
environment:
name: github-pages
url: ${{ steps.deployment.outputs.page_url }}
runs-on: ubuntu-latest
needs: website-generate
steps:
- name: Deploy to GitHub Pages
id: deployment
uses: actions/deploy-pages@d6db90164ac5ed86f2b6aed7e0febac5b3c0c03e # v4

View File

@ -1,60 +0,0 @@
name: Update k8s manifests
on:
push:
branches:
- master
- main
paths-ignore:
- 'docs/**'
- 'website/**'
- 'assets/**'
- 'backup/**'
- '*.md'
workflow_dispatch:
release:
types: [published]
jobs:
update-manifest:
name: Update k8s manifests
runs-on: ubuntu-latest
steps:
- name: Check out code
uses: actions/checkout@11bd71901bbe5b1630ceea73d27597364c9af683 # v4
- name: Set up env vars
run: |
echo "HELM_VERSION=v$(sed -n 's/HELM_VERSION=//p' config.base.env)" >> $GITHUB_ENV
- name: Helm lint
run: make helm-lint
#TODO: add also the webhook part and understand if is necessary
- name: Helm update plain manifests
run: |
helm template --set fullnameOverride=jenkins-operator \
--set jenkins.enabled=false \
--set jenkins.backup.enabled=false \
--set jenkins.backup.pvc.enabled=false \
--set operator.resources.limits.cpu=100m \
--set operator.resources.limits.memory=120Mi \
--set operator.resources.requests.cpu=100m \
--set operator.resources.requests.memory=120Mi \
chart/jenkins-operator/ > deploy/all-in-one-v1alpha2.yaml
sed -i '/namespace: default/d' deploy/all-in-one-v1alpha2.yaml
sed -i 's/# Source: .*//g' deploy/all-in-one-v1alpha2.yaml
sed -i 's/app\.kubernetes\.io\/instance: release-name//g' deploy/all-in-one-v1alpha2.yaml
sed -i 's/app\.kubernetes\.io\/managed-by: Helm//g' deploy/all-in-one-v1alpha2.yaml
sed -i 's/helm\.sh\/chart: [a-zA-Z0-9]+//g' deploy/all-in-one-v1alpha2.yaml
sed -i '/^[[:space:]]*$/d' deploy/all-in-one-v1alpha2.yaml
cp chart/jenkins-operator/crds/jenkins-crd.yaml deploy/crds/jenkins.io_jenkins_crd.yaml
- name: Create Pull Request
uses: peter-evans/create-pull-request@271a8d0340265f705b14b6d32b9829c1cb33d45e # v6
with:
commit-message: Auto-updated Kubernetes Manifests
branch: manifest-deploy-update
title: Auto-updated Kubernetes Manifests
body: |
Auto-updated Kubernetes Manifests from master commit ${{ github.sha }}

View File

@ -1,22 +0,0 @@
name: "Stale issue automation"
on:
workflow_dispatch:
schedule:
- cron: "0 9 * * *"
permissions:
issues: write
pull-requests: write
jobs:
stale:
runs-on: ubuntu-latest
steps:
- uses: actions/stale@5bef64f19d7facfb25b37b414482c7164d639639 # v9
with:
repo-token: ${{ secrets.GITHUB_TOKEN }}
operations-per-run: 200
days-before-issue-stale: 60
days-before-issue-close: 10
exempt-pr-labels: "not-stale"
exempt-issue-labels: "not-stale"

View File

@ -1,67 +0,0 @@
name: Tests BATS
on:
push:
branches:
- master
- main
paths-ignore:
- 'docs/**'
- 'website/**'
- 'assets/**'
- 'backup/**'
- '*.md'
pull_request:
types: [opened, synchronize, ready_for_review, reopened]
paths-ignore:
- 'docs/**'
- 'website/**'
- 'assets/**'
- 'backup/**'
- '*.md'
jobs:
run-tests:
if: github.event.pull_request.draft == false
name: BATS test ${{ matrix.test-file }}
runs-on: ubuntu-latest
strategy:
fail-fast: false
matrix:
test-file: ["1-deploy", "2-deploy-with-more-options", "3-deploy-with-webhook"]
steps:
- name: Check out code
uses: actions/checkout@11bd71901bbe5b1630ceea73d27597364c9af683 # v4
- name: Set up env vars
run: |
echo "GO111MODULE=on" >> $GITHUB_ENV
echo "GO_VERSION=v$(sed -n 's/GO_VERSION=//p' config.base.env | tr -d '\n' | tr -d '"')" >> $GITHUB_ENV
echo "HELM_VERSION=v$(sed -n 's/HELM_VERSION=//p' config.base.env | tr -d '\n' | tr -d '"')" >> $GITHUB_ENV
echo "KIND_CLUSTER_NAME=$(sed -n 's/KIND_CLUSTER_NAME=//p' config.base.env | tr -d '\n' | tr -d '"')" >> $GITHUB_ENV
echo "GOPATH=/home/runner/go" >> $GITHUB_ENV
- name: Prepare go environment
uses: actions/setup-go@0aaccfd150d50ccaeb58ebd88d36e91967a5f35b # v5
with:
go-version: ${{ env.GO_VERSION }}
- name: Ensure Golang runtime dependencies
run: make go-dependencies
- name: Setup Bats and libs
uses: bats-core/bats-action@e412797c46257a2dbf3775f6f6010b33ee6cb99f # 3.0.1
with:
support-path: "${{ github.workspace }}/.bats/bats-support"
assert-path: "${{ github.workspace }}/.bats/bats-assert"
detik-path: "${{ github.workspace }}/.bats/bats-detik"
file-path: "${{ github.workspace }}/.bats/bats-file"
- name: Kind setup
uses: helm/kind-action@a1b0e391336a6ee6713a0583f8c6240d70863de3 # v1.12.0
with:
cluster_name: ${{env.KIND_CLUSTER_NAME}}
- name: Jenkins Operator - bats tests
env:
BATS_LIB_PATH: "${{ github.workspace }}/.bats"
run: BATS_TEST_PATH=${{matrix.test-file}}.bats make bats-tests

View File

@ -1,115 +0,0 @@
name: Tests E2E
on:
push:
branches:
- master
- main
paths-ignore:
- 'docs/**'
- 'website/**'
- 'assets/**'
- 'backup/**'
- '*.md'
pull_request:
types: [opened, synchronize, ready_for_review, reopened]
paths-ignore:
- 'docs/**'
- 'website/**'
- 'assets/**'
- 'backup/**'
- '*.md'
jobs:
create-e2e-list:
name: E2E Create tests list
runs-on: ubuntu-latest
outputs:
matrix: ${{ steps.matrix.outputs.matrix }}
steps:
- uses: actions/checkout@11bd71901bbe5b1630ceea73d27597364c9af683 # v4
- id: matrix
run: |
script=$(./test/make_matrix_ginkgo.sh e2e)
echo "matrix=${script}" >> $GITHUB_OUTPUT
verify-code:
name: E2E Verify code before tests
runs-on: ubuntu-latest
needs: [create-e2e-list]
steps:
- uses: actions/checkout@11bd71901bbe5b1630ceea73d27597364c9af683 # v4
- name: Set up env vars
run: |
echo "GO111MODULE=on" >> $GITHUB_ENV
echo "GO_VERSION=v$(sed -n 's/GO_VERSION=//p' config.base.env | tr -d '\n' | tr -d '"')" >> $GITHUB_ENV
echo "GOPATH=/home/runner/go" >> $GITHUB_ENV
- name: Prepare go environment
uses: actions/setup-go@0aaccfd150d50ccaeb58ebd88d36e91967a5f35b # v5
with:
go-version: ${{ env.GO_VERSION }}
- name: Ensure Golang runtime dependencies
run: make go-dependencies
- name: Verify code formatting
run: make verify
run-e2e-tests:
runs-on: ubuntu-latest
needs: [create-e2e-list, verify-code]
if: github.event.pull_request.draft == false
name: E2E ${{ matrix.test }}
strategy:
fail-fast: false
matrix: ${{ fromJSON(needs.create-e2e-list.outputs.matrix) }}
steps:
- name: Check out code
uses: actions/checkout@11bd71901bbe5b1630ceea73d27597364c9af683 # v4
- name: Set up env vars
run: |
echo "GO111MODULE=on" >> $GITHUB_ENV
echo "GO_VERSION=v$(sed -n 's/GO_VERSION=//p' config.base.env | tr -d '\n' | tr -d '"')" >> $GITHUB_ENV
echo "KIND_CLUSTER_NAME=$(sed -n 's/KIND_CLUSTER_NAME=//p' config.base.env | tr -d '\n' | tr -d '"')" >> $GITHUB_ENV
echo "GOPATH=/home/runner/go" >> $GITHUB_ENV
- name: Prepare go environment
uses: actions/setup-go@0aaccfd150d50ccaeb58ebd88d36e91967a5f35b # v5
with:
go-version: ${{ env.GO_VERSION }}
- name: Ensure Golang runtime dependencies
run: make go-dependencies
- name: Kind setup
uses: helm/kind-action@a1b0e391336a6ee6713a0583f8c6240d70863de3 # v1.12.0
with:
cluster_name: ${{env.KIND_CLUSTER_NAME}}
config: kind-cluster.yaml
- name: Prepare environment for e2e
run: |
sudo apt-get update && sudo apt-get install -y socat
sudo mkdir -p $HOME/.kube
sudo chown -R $USER $HOME/.kube
- name: Jenkins Operator - e2e Chart tests
env:
TNAME: ${{ matrix.test }}
TFILE: ${{ matrix.file }}
TLINE: ${{ matrix.line }}
run: |
git reset --hard
printf "\n \n > Running test: %s from file: $s line: %s\n" "${TNAME}" "${TFILE}" "${TLINE}"
make e2e E2E_TEST_ARGS='-ginkgo.v -ginkgo.focus="${TNAME}"'
- name: Debug
if: failure()
shell: bash
continue-on-error: true
run: |
randomns=$(kubectl get ns| grep -i 'ns[0-9]\+' |cut -d ' ' -f 1)
kubectl get pods -n ${randomns}
kubectl get events -n ${randomns}

View File

@ -1,117 +0,0 @@
name: Tests HELM
on:
push:
branches:
- master
- main
paths-ignore:
- 'docs/**'
- 'website/**'
- 'assets/**'
- 'backup/**'
- '*.md'
pull_request:
types: [opened, synchronize, ready_for_review, reopened]
paths-ignore:
- 'docs/**'
- 'website/**'
- 'assets/**'
- 'backup/**'
- '*.md'
jobs:
create-helm-list:
name: HELM Create tests list
runs-on: ubuntu-latest
outputs:
matrix: ${{ steps.matrix.outputs.matrix }}
steps:
- uses: actions/checkout@11bd71901bbe5b1630ceea73d27597364c9af683 # v4
- id: matrix
run: |
script=$(./test/make_matrix_ginkgo.sh helm)
echo "matrix=${script}" >> $GITHUB_OUTPUT
verify-code:
name: HELM Verify code before tests
runs-on: ubuntu-latest
needs: [create-helm-list]
steps:
- uses: actions/checkout@11bd71901bbe5b1630ceea73d27597364c9af683 # v4
- name: Set up env vars
run: |
echo "GO111MODULE=on" >> $GITHUB_ENV
echo "GO_VERSION=v$(sed -n 's/GO_VERSION=//p' config.base.env | tr -d '\n' | tr -d '"')" >> $GITHUB_ENV
echo "GOPATH=/home/runner/go" >> $GITHUB_ENV
- name: Prepare go environment
uses: actions/setup-go@0aaccfd150d50ccaeb58ebd88d36e91967a5f35b # v5
with:
go-version: ${{ env.GO_VERSION }}
- name: Ensure Golang runtime dependencies
run: make go-dependencies
- name: Verify code formatting
run: make verify
run-helm-tests:
runs-on: ubuntu-latest
needs: [create-helm-list, verify-code]
if: github.event.pull_request.draft == false
name: HELM ${{ matrix.test }}
strategy:
fail-fast: false
matrix: ${{ fromJSON(needs.create-helm-list.outputs.matrix) }}
steps:
- name: Check out code
uses: actions/checkout@11bd71901bbe5b1630ceea73d27597364c9af683 # v4
- name: Set up env vars
run: |
echo "GO111MODULE=on" >> $GITHUB_ENV
echo "GO_VERSION=v$(sed -n 's/GO_VERSION=//p' config.base.env | tr -d '\n' | tr -d '"')" >> $GITHUB_ENV
echo "HELM_VERSION=v$(sed -n 's/HELM_VERSION=//p' config.base.env | tr -d '\n' | tr -d '"')" >> $GITHUB_ENV
echo "KIND_CLUSTER_NAME=$(sed -n 's/KIND_CLUSTER_NAME=//p' config.base.env | tr -d '\n' | tr -d '"')" >> $GITHUB_ENV
echo "GOPATH=/home/runner/go" >> $GITHUB_ENV
- name: Prepare go environment
uses: actions/setup-go@0aaccfd150d50ccaeb58ebd88d36e91967a5f35b # v5
with:
go-version: ${{ env.GO_VERSION }}
- name: Ensure Golang runtime dependencies
run: make go-dependencies
- name: Kind setup
uses: helm/kind-action@a1b0e391336a6ee6713a0583f8c6240d70863de3 # v1.12.0
with:
cluster_name: ${{env.KIND_CLUSTER_NAME}}
config: kind-cluster.yaml
- name: Prepare environment for e2e
run: |
sudo apt-get update && sudo apt-get install -y socat
sudo mkdir -p $HOME/.kube
sudo chown -R $USER $HOME/.kube
- name: Jenkins Operator - Helm Chart tests
env:
TNAME: ${{ matrix.test }}
TFILE: ${{ matrix.file }}
TLINE: ${{ matrix.line }}
run: |
git reset --hard
make helm-lint
printf "\n \n > Running test: %s from file: $s line: %s\n" "${TNAME}" "${TFILE}" "${TLINE}"
make helm-e2e E2E_TEST_ARGS='-ginkgo.v -ginkgo.focus="${TNAME}"'
- name: Debug
if: failure()
shell: bash
continue-on-error: true
run: |
randomns=$(kubectl get ns| grep -i 'ns[0-9]\+' |cut -d ' ' -f 1)
kubectl get pods -n ${randomns}
kubectl get events -n ${randomns}

60
.github/workflows/auto-tests.yaml vendored Normal file
View File

@ -0,0 +1,60 @@
name: Run tests
on:
push:
branches:
- master
pull_request:
types: [opened, synchronize, ready_for_review, reopened]
branches:
- master
jobs:
run-tests:
if: github.event.pull_request.draft == false
name: Run automated tests
runs-on: ubuntu-latest
steps:
- name: Check out code
uses: actions/checkout@v2
- name: Set up env vars
run: |
echo "GO111MODULE=on" >> $GITHUB_ENV
echo "CHANGE_MINIKUBE_NONE_USER=true" >> $GITHUB_ENV
echo "MINIKUBE_WANTUPDATENOTIFICATION=false" >> $GITHUB_ENV
echo "MINIKUBE_WANTREPORTERRORPROMPT=false" >> $GITHUB_ENV
echo "GO_VERSION=v$(sed -n 's/GO_VERSION=//p' config.base.env)" >> $GITHUB_ENV
echo "MINIKUBE_VERSION=v$(sed -n 's/MINIKUBE_VERSION=//p' config.minikube.env)" >> $GITHUB_ENV
echo "OPERATOR_SDK_VERSION=v$(sed -n 's/OPERATOR_SDK_VERSION=//p' config.base.env)" >> $GITHUB_ENV
echo "MINIKUBE_KUBERNETES_VERSION=$(sed -n 's/MINIKUBE_KUBERNETES_VERSION=//p' config.minikube.env)" >> $GITHUB_ENV
echo "HELM_VERSION=v$(sed -n 's/HELM_VERSION=//p' config.base.env)" >> $GITHUB_ENV
echo "GOPATH=/home/runner/go" >> $GITHUB_ENV
- name: Prepare go environment
uses: actions/setup-go@v2
with:
go-version: ${{ env.GO_VERSION }}
- name: Ensure Golang runtime dependencies
run: make go-dependencies
- name: Verify code formatting
run: make verify
- name: Prepare environment for e2e
run: |
sudo apt-get update
sudo apt-get install socat
curl -Lo kubectl https://storage.googleapis.com/kubernetes-release/release/$MINIKUBE_KUBERNETES_VERSION/bin/linux/amd64/kubectl && chmod +x kubectl && sudo mv kubectl /usr/local/bin/
curl -Lo minikube https://storage.googleapis.com/minikube/releases/$MINIKUBE_VERSION/minikube-linux-amd64 && chmod +x minikube && sudo mv minikube /usr/local/bin/
curl -Lo operator-sdk https://github.com/operator-framework/operator-sdk/releases/download/$OPERATOR_SDK_VERSION/operator-sdk-$OPERATOR_SDK_VERSION-x86_64-linux-gnu && chmod +x operator-sdk && sudo mv operator-sdk /usr/local/bin/
curl -Lo helm.tar.gz https://get.helm.sh/helm-$HELM_VERSION-linux-amd64.tar.gz && tar xzfv helm.tar.gz && sudo mv linux-amd64/helm /usr/local/bin/
sudo mkdir -p $HOME/.kube $HOME/.minikube
touch KUBECONFIG
sudo minikube start --vm-driver=none --kubernetes-version=$MINIKUBE_KUBERNETES_VERSION
sudo chown -R $USER $HOME/.kube $HOME/.minikube
- name: Jenkins Operator - e2e
run: make build e2e
- name: Jenkins Operator Helm Chart - e2e
run: make e2e BUILDTAGS=Helm E2E_TEST_SELECTOR='^.*Helm.*$'

View File

@ -1,72 +0,0 @@
name: auto-update-jenkins-lts
on:
schedule:
- cron: '0 7 * * 0'
workflow_dispatch:
permissions:
contents: write
pull-requests: write
jobs:
check-and-update-jenkins-lts:
runs-on: ubuntu-latest
steps:
- name: Check out code
uses: actions/checkout@08c6903cd8c0fde910a37f88322edcfb5dd907a8 # v5
- name: Set up env vars
run: |
echo "GO111MODULE=on" >> $GITHUB_ENV
echo "GO_VERSION=v$(sed -n 's/GO_VERSION=//p' config.base.env | tr -d '\n' | tr -d '"')" >> $GITHUB_ENV
echo "KIND_CLUSTER_NAME=$(sed -n 's/KIND_CLUSTER_NAME=//p' config.base.env | tr -d '\n' | tr -d '"')" >> $GITHUB_ENV
echo "GOPATH=/home/runner/go" >> $GITHUB_ENV
- name: Check if update needed
id: check
run: |
CURRENT=$(sed -n 's/LATEST_LTS_VERSION=//p' config.base.env | tr -d '\n' | tr -d '"')
LATEST=$(curl -s https://www.jenkins.io/changelog-stable/ | grep -oP 'changelog/\K\d+\.\d+\.\d+' | head -1)
echo "current=$CURRENT" >> $GITHUB_OUTPUT
echo "latest=$LATEST" >> $GITHUB_OUTPUT
if [ "$CURRENT" != "$LATEST" ]; then
echo "needs_update=true" >> $GITHUB_OUTPUT
else
echo "needs_update=false" >> $GITHUB_OUTPUT
fi
- name: Prepare go environment
if: steps.check.outputs.needs_update == 'true'
uses: actions/setup-go@44694675825211faa026b3c33043df3e48a5fa00 # v6
with:
go-version: ${{ env.GO_VERSION }}
- name: Ensure Golang runtime dependencies
if: steps.check.outputs.needs_update == 'true'
run: make go-dependencies
- name: Update Jenkins lts version
if: steps.check.outputs.needs_update == 'true'
run: make update-jenkins-lts
- name: Update Jenkins base plugins
if: steps.check.outputs.needs_update == 'true'
run: make update-plugins
- name: Run verify
if: steps.check.outputs.needs_update == 'true'
run: make verify
- name: Create Pull Request
if: steps.check.outputs.needs_update == 'true'
uses: peter-evans/create-pull-request@271a8d0340265f705b14b6d32b9829c1cb33d45e # v7.0.8
with:
token: ${{ secrets.GITHUB_TOKEN }}
commit-message: "chore: update Jenkins lts version to ${{ steps.check.outputs.latest }}"
title: "chore: update Jenkins lts version to ${{ steps.check.outputs.latest }}"
body: "auto-update Jenkins lts version from ${{ steps.check.outputs.current }} to ${{ steps.check.outputs.latest }}"
branch: auto-update-jenkins-lts-${{ steps.check.outputs.latest }}
delete-branch: true

View File

@ -1,60 +0,0 @@
name: Release Backup Pvc
on:
workflow_dispatch:
pull_request:
types: [edited, opened, reopened, synchronize]
paths:
- 'backup/pvc/**'
push:
branches:
- master
- main
tags: ["*"]
paths:
- 'backup/pvc/**'
jobs:
build-and-release-backup:
name: Release Backup Pvc, build, bump and push new image
runs-on: ubuntu-latest
steps:
- name: Checkout code
uses: actions/checkout@11bd71901bbe5b1630ceea73d27597364c9af683 # v4
- name: Check envs
run: make -C backup/pvc check-env
- name: Build the e2e image
run: make -C backup/pvc docker-build-e2e
- name: Run the e2e tests
run: make -C backup/pvc docker-e2e
- name: Configure Git
if: github.ref == 'refs/heads/master' && github.event_name != 'pull_request'
run: |
git config user.name "$GITHUB_ACTOR"
git config user.email "$GITHUB_ACTOR@users.noreply.github.com"
- name: Bump the version
if: github.ref == 'refs/heads/master' && github.event_name != 'pull_request'
shell: bash
run: |
make -C backup/pvc sembump
make -C backup/pvc bump-version
- name: Login to Quay.io
if: github.ref == 'refs/heads/master' && github.event_name != 'pull_request'
uses: docker/login-action@74a5d142397b4f367a81961eba4e8cd7edddf772 # v3
with:
registry: quay.io
username: ${{ secrets.QUAYIO_USERNAME }}
password: ${{ secrets.QUAYIO_TOKEN }}
- name: Build and push the image to Quay.io
if: github.ref == 'refs/heads/master' && github.event_name != 'pull_request'
run: |
git reset --hard
make -C backup/pvc docker-build
make -C backup/pvc docker-release

View File

@ -1,40 +0,0 @@
name: Release Helm chart
# Run this workflow manually
on:
workflow_dispatch:
inputs:
chartVersion:
description: "Helm chart version to release. eg. 0.4.1"
required: true
appVersion:
description: "Operator app version without quotes, eg. 0.5.1 . If not provided, the one in Chart.yaml won't be overwritten."
required: false
jobs:
release-helm-chart:
runs-on: ubuntu-latest
steps:
- name: Checkout code
uses: actions/checkout@11bd71901bbe5b1630ceea73d27597364c9af683 # v4
- name: Deploy Helm chart
run: |
sed -i '0,/version:.*/s//version: ${{ github.event.inputs.chartVersion }}/' chart/jenkins-operator/Chart.yaml
if [ ${{ github.event.inputs.appVersion }} ] ; then
sed -i '/appVersion:.*/s//appVersion: "${{ github.event.inputs.appVersion }}"/' chart/jenkins-operator/Chart.yaml
fi
make helm-release-latest
# Creates pull request with new chart version
- name: Create Pull Request
uses: peter-evans/create-pull-request@271a8d0340265f705b14b6d32b9829c1cb33d45e # v6
with:
commit-message: Release Helm chart ${{ github.event.inputs.chartVersion }}
branch: helm-chart-release-${{ github.event.inputs.chartVersion }}
title: Release ${{ github.event.inputs.chartVersion }} Helm Chart
body: |
Release ${{ github.event.inputs.chartVersion }} Helm Chart

View File

@ -1,89 +0,0 @@
name: Publish nightly snapshot
on:
schedule:
- cron: '0 2 * * *'
workflow_dispatch:
inputs:
skipTests:
description: "Flag for skipping the tests. If set to true (without quotation marks), the workflow will skip tests and go straight to releasing the nightly build. Use with caution!"
required: false
jobs:
publish-image:
name: Publish nightly snapshot
runs-on: ubuntu-latest
steps:
- name: Prep - check out code
uses: actions/checkout@11bd71901bbe5b1630ceea73d27597364c9af683 # v4
- name: Prep - Set up env vars
run: |
echo "GO111MODULE=on" >> $GITHUB_ENV
echo "GO_VERSION=v$(sed -n 's/GO_VERSION=//p' config.base.env | tr -d '\n' | tr -d '"')" >> $GITHUB_ENV
echo "HELM_VERSION=v$(sed -n 's/HELM_VERSION=//p' config.base.env | tr -d '\n' | tr -d '"')" >> $GITHUB_ENV
echo "KIND_CLUSTER_NAME=$(sed -n 's/KIND_CLUSTER_NAME=//p' config.base.env | tr -d '\n' | tr -d '"')" >> $GITHUB_ENV
echo "GOPATH=/home/runner/go" >> $GITHUB_ENV
- name: Prep - setup Bats and bats libs
if: ${{ github.event.inputs.skipTests != 'true' }}
uses: bats-core/bats-action@472edde1138d59aca53ff162fb8d996666d21e4a # 2.0.0
with:
support-path: "${{ github.workspace }}/.bats/bats-support"
assert-path: "${{ github.workspace }}/.bats/bats-assert"
detik-path: "${{ github.workspace }}/.bats/bats-detik"
file-path: "${{ github.workspace }}/.bats/bats-file"
- name: Prep - go environment
uses: actions/setup-go@0aaccfd150d50ccaeb58ebd88d36e91967a5f35b # v5
with:
go-version: ${{ env.GO_VERSION }}
- name: Prep - Ensure Golang runtime dependencies
run: make go-dependencies
- name: Test - verify code formatting
run: make verify
- name: Prep - Minikube setup
if: ${{ github.event.inputs.skipTests != 'true' }}
run: |
sudo apt-get update
sudo apt-get install socat
sudo mkdir -p $HOME/.kube
sudo chown -R $USER $HOME/.kube
- name: Prep - Kind setup
if: ${{ github.event.inputs.skipTests != 'true' }}
uses: helm/kind-action@a1b0e391336a6ee6713a0583f8c6240d70863de3 # v1.12.0
with:
cluster_name: ${{env.KIND_CLUSTER_NAME}}
config: kind-cluster.yaml
- name: Test - e2e
if: ${{ github.event.inputs.skipTests != 'true' }}
run: make e2e E2E_TEST_ARGS='-ginkgo.v'
- name: Test - Helm Chart
if: ${{ github.event.inputs.skipTests != 'true' }}
run: |
git reset --hard
make helm-lint
make helm-e2e E2E_TEST_ARGS='-ginkgo.v'
- name: Test - bats
env:
BATS_LIB_PATH: "${{ github.workspace }}/.bats"
if: ${{ github.event.inputs.skipTests != 'true' }}
run: make bats-tests
- name: Post - Login to Quay.io
uses: docker/login-action@74a5d142397b4f367a81961eba4e8cd7edddf772 # v3
with:
registry: quay.io
username: ${{ secrets.QUAYIO_USERNAME }}
password: ${{ secrets.QUAYIO_TOKEN }}
- name: Post - Push image
run: |
git reset --hard
make container-runtime-snapshot-push

View File

@ -1,57 +0,0 @@
name: Release Operator
on:
workflow_dispatch:
jobs:
build-and-release:
name: Release Operator, build and push newest image
runs-on: ubuntu-latest
steps:
- name: Checkout code
uses: actions/checkout@11bd71901bbe5b1630ceea73d27597364c9af683 # v4
with:
fetch-depth: 0
- name: Set up env vars
run: |
echo "GO111MODULE=on" >> $GITHUB_ENV
echo "GO_VERSION=v$(sed -n 's/GO_VERSION=//p' config.base.env | tr -d '\n' | tr -d '"')" >> $GITHUB_ENV
echo "GOPATH=/home/runner/go" >> $GITHUB_ENV
- name: Prepare go environment
uses: actions/setup-go@0aaccfd150d50ccaeb58ebd88d36e91967a5f35b # v5
with:
go-version: ${{ env.GO_VERSION }}
- name: Ensure Golang runtime dependencies
run: make go-dependencies
- name: Configure Git
run: |
git config user.name "$GITHUB_ACTOR"
git config user.email "$GITHUB_ACTOR@users.noreply.github.com"
- name: Tag the release
run: make tag
- name: Set version for release tag name
run: |
echo "VERSION=$(cat VERSION.txt)" >> $GITHUB_ENV
- name: Release
uses: softprops/action-gh-release@c95fe1489396fe8a9eb87c0abf8aa5b2ef267fda # v2
with:
tag_name: ${{ env.VERSION }}
- name: Login to Quay.io
uses: docker/login-action@74a5d142397b4f367a81961eba4e8cd7edddf772 # v3
with:
registry: quay.io
username: ${{ secrets.QUAYIO_USERNAME }}
password: ${{ secrets.QUAYIO_TOKEN }}
- name: Build and push the image to Quay.io
run: |
git reset --hard
make container-runtime-release

View File

@ -1,126 +0,0 @@
#!/usr/bin/env bash
set -euo pipefail
SCRIPT_DIR="$(cd "$(dirname "${BASH_SOURCE[0]}")" && pwd)"
PROJECT_ROOT="$(cd "$SCRIPT_DIR/../../" && pwd)"
PLUGINS=(
"configuration-as-code"
"git"
"job-dsl"
"kubernetes"
"kubernetes-credentials-provider"
"workflow-aggregator"
)
version_compare() {
local ver1="$1"
local ver2="$2"
ver1=$(echo "$ver1" | sed 's/\([0-9.]*\).*/\1/')
ver2=$(echo "$ver2" | sed 's/\([0-9.]*\).*/\1/')
if [ "$ver1" = "$ver2" ]; then
return 0
fi
local sorted=$(printf '%s\n%s' "$ver1" "$ver2" | sort -V)
local first=$(echo "$sorted" | head -n1)
if [ "$first" = "$ver1" ]; then
return 1
else
return 2
fi
}
fetch_update_center_data() {
local jenkins_version="$1"
local url="https://updates.jenkins.io/update-center.actual.json?version=${jenkins_version}"
local data=$(curl -s -L -f "$url")
if ! echo "$data" | jq . > /dev/null 2>&1; then
echo "error something happened" >&2
return 1
fi
echo "$data"
}
find_compatible_version() {
local plugin_id="$1"
local jenkins_version="$2"
local update_data="$3"
local plugin_data=$(echo "$update_data" | jq ".plugins[\"$plugin_id\"]")
local plugin_version=$(echo "$plugin_data" | jq -r '.version')
local compatible_since=$(echo "$plugin_data" | jq -r '.compatibleSinceVersion // empty')
if [ -n "$compatible_since" ]; then
version_compare "$jenkins_version" "$compatible_since"
local result=$?
if [ $result -eq 1 ]; then
echo "Error: Plugin '$plugin_id' requires Jenkins $compatible_since or newer" >&2
return 1
fi
fi
echo "$plugin_version"
}
update_go_file() {
local file_path="$1"
local plugin_id="$2"
local new_version="$3"
local dry_run="$4"
local var_name=""
case "$plugin_id" in
"configuration-as-code") var_name="configurationAsCodePlugin" ;;
"git") var_name="gitPlugin" ;;
"job-dsl") var_name="jobDslPlugin" ;;
"kubernetes") var_name="kubernetesPlugin" ;;
"kubernetes-credentials-provider") var_name="kubernetesCredentialsProviderPlugin" ;;
"workflow-aggregator") var_name="workflowAggregatorPlugin" ;;
esac
local relative_path=$(realpath --relative-to="$PROJECT_ROOT" "$file_path")
local new_line=" ${var_name} = \"${plugin_id}:${new_version}\""
if [ "$dry_run" = "true" ]; then
echo "$relative_path: $plugin_id:$new_version"
else
sed -i "s|^[[:space:]]*${var_name}[[:space:]]*=.*|${new_line}|" "$file_path"
echo "$relative_path: $var_name -> $plugin_id:$new_version"
fi
}
main() {
if [ $# -lt 1 ]; then
echo "usage: $0 <jenkins-version> [--dry-run]" >&2
exit 1
fi
local jenkins_version="$1"
local dry_run="false"
if [ $# -gt 1 ] && [ "$2" = "--dry-run" ]; then
dry_run="true"
fi
local update_data
if ! update_data=$(fetch_update_center_data "$jenkins_version"); then
exit 1
fi
for plugin_id in "${PLUGINS[@]}"; do
local compatible_version
if compatible_version=$(find_compatible_version "$plugin_id" "$jenkins_version" "$update_data"); then
#printf "%-35s %-30s\n" "$plugin_id" "$compatible_version"
update_go_file "$PROJECT_ROOT/pkg/plugins/base_plugins.go" "$plugin_id" "$compatible_version" "$dry_run"
update_go_file "$PROJECT_ROOT/test/e2e/configuration_test.go" "$plugin_id" "$compatible_version" "$dry_run"
fi
done
}
main "$@"

14
.gitignore vendored
View File

@ -88,17 +88,3 @@ tags
### IntelliJ IDEA ### ### IntelliJ IDEA ###
*.iml *.iml
/bin
testbin/*
### Bats
chart/jenkins-operator/deploy.tmp
### Nix
result
### website
website/node_modules
website/public
website/.hugo_build.lock

View File

@ -1,41 +1,29 @@
run: run:
deadline: 5m deadline: 10m
allow-parallel-runners: true linters-settings:
skip-files: errcheck:
- api/v1alpha2/zz_generated.deepcopy.go check-blank: false
issues: ignore: fmt:.*,io/ioutil:^Read.*,Write
exclude-use-default: false
exclude-rules:
- path: "internal/*"
linters:
- dupl
- path: (.+)_test.go
linters:
- dupl
linters: linters:
disable-all: true enable-all: true
enable: disable:
- dupl - funlen
- errcheck - gocognit
- exportloopref - godox
- goconst - gomnd
- gochecknoglobals
- gochecknoinits
- lll
- prealloc
- wsl
- gocyclo - gocyclo
- gofmt - scopelint
- goimports - dupl
- gosimple - gosec
- govet - maligned
- ineffassign - testpackage
- loggercheck - goerr113
- misspell
- nakedret - nakedret
- staticcheck - nestif
- typecheck - godot
- unconvert
- unparam
- unused
output:
sort-results: true
sort-order:
- file
- severity
- linter

View File

@ -1,26 +0,0 @@
repos:
- repo: https://github.com/sirosen/check-jsonschema
rev: a167de9d5f4e87e1cdb16cb560aa704b79b6f655 # frozen: 0.32.1
hooks:
- id: check-github-workflows
- repo: https://github.com/pre-commit/pre-commit-hooks
rev: cef0300fd0fc4d2a87a85fa2093c6b283ea36f4b # frozen: v5.0.0
hooks:
- id: detect-private-key
- id: trailing-whitespace
- id: end-of-file-fixer
- repo: https://github.com/gruntwork-io/pre-commit
rev: "d9196b3a0a6fbc530f2bacea36c11a3b0214ff13" # frozen: v0.1.28
hooks:
- id: helmlint
- repo: https://github.com/norwoodj/helm-docs
rev: "37d3055fece566105cf8cff7c17b7b2355a01677" # frozen: v1.14.2
hooks:
- id: helm-docs
args:
- --chart-search-root=chart/jenkins-operator
- repo: https://github.com/brokenpip3/pre-commit-hooks
rev: dd7b3821637ba3c3a8628ad487fd84edec8006f2 # frozen: 0.0.1
hooks:
- id: github-actions-hash
files: ^.github/workflows/.*\.(yml|yaml)$ # limit only to github workflows

View File

@ -1 +0,0 @@
* @brokenpip3

View File

@ -2,4 +2,5 @@
You can find the Jenkins Code of Conduct [on jenkins.io](https://jenkins.io/project/conduct/). You can find the Jenkins Code of Conduct [on jenkins.io](https://jenkins.io/project/conduct/).
It applies to the entire `jenkinsci` GitHub organization, among other community spaces. It applies to the entire `jenkinsci` GitHub organization, among other community spaces.
[version]: http://contributor-covenant.org/version/1/4/

View File

@ -1,50 +1,46 @@
# Contribution Model # Contributing
Thanks for taking the time to contribute! :+1::tada: First off, thanks for taking the time to contribute! :tada::+1:
The following is a set of guidelines for contributing to the Jenkins Operator. These are mostly guidelines, not rules.
Use your best judgment, and feel free to propose changes to this document in a pull request.
In this project we appreciate any kind of contributions: code, documentation, design, etc.
Any contribution counts, and the size does not matter!
## Newcomers
If you are a newcomer contributor and have any questions, please do not hesitate to ask in the `#jenkins-operator` [Slack](https://virtuslab-oss.slack.com) Channel.
#### Table Of Contents
[Code of Conduct](#code-of-conduct)
## Code of Conduct ## Code of Conduct
This project and everyone participating in it is governed by the [Jenkins Code of Conduct](CODE_OF_CONDUCT.md). By participating, you are expected to uphold this code. This project and everyone participating in it is governed by the [Atom Code of Conduct](CODE_OF_CONDUCT.md). By participating, you are expected to uphold this code.
## We Develop with GitHub ## Commit Messages
We use GitHub to host code, to track issues and feature requests, as well as accept pull requests.
## We Use GitHub Flow, So All Code Changes Happen Through Pull Requests All commit messages should follow
Pull requests are the best way to propose changes to the codebase (we use GitHub Flow). We actively welcome your pull requests: [these best practices](https://chris.beams.io/posts/git-commit/), specifically:
- Create feature requests and discuss the scope. - Start with a subject line
- Fork the repo and create your branch from master. - Contain a body that explains _why_ you're making the change you're making
- If youve added code that should be tested, add tests. - Reference an issue number one exists, closing it if applicable (with text such
- If youve changed APIs or design, update the documentation. as
- Create a draft pull request (not yet ready for review, triggers CI build). ["Fixes #245" or "Closes #111"](https://help.github.com/articles/closing-issues-using-keywords/))
- Ensure the e2e tests pass (wait for GitHub status checks).
- Mark that pull request as ready for review.
## Quality Standards Not sure what to put? Try to Include:
It is important to keep the quality bar high and ensure all of us follow best practices and security, so the code is solid and can be reused by other people. Below you can find some quality standards.
### General Contribution - What is the problem being solved?
- Why is this the best approach?
- What other approaches did you consider?
- What side effects will this approach have?
- What future work remains to be done?
- Break down your pull request into smaller pieces, less code is easier to review. Most of PRs should fall under 200 lines of code changes unless specifically justified otherwise. ## Coding standards
- Add descriptive comments and labels to pull requests and issues.
- Ensure end to end tests are passing.
- commit message should follow [these best practices](https://chris.beams.io/posts/git-commit/), specifically: start with a subject line and reference an issue number e.g. ["Fixes #245" or "Closes #111"](https://help.github.com/articles/closing-issues-using-keywords/)
### Large Architectural Changes ### Go
In case of large change which requires significant engineering effort and introduces side effects, we suggest writing a design proposal document first.
Design proposal is simply a document that states what you propose to do including: - [Go code review comments](https://github.com/golang/go/wiki/CodeReviewComments)
- Problem statement
- Description of potential solution
- Side effects
- Breaking changes
Keep in mind that a proposal is not a pitch, it needs to be technical.
### Proposing changes to Helm Chart
When issuing a PR that modifies the project's Helm Chart, please do not include in your PR changes that would release a new package version when merged.
Specifically, please do not update `chart/index.yaml` and `chart/jenkins-operator/Chart.yaml` files and do not build chart archive package.
For the sake of PR's brevity and security, Project's maintainers will issue a separate PR that releases new version of the Chart after your PR has been merged.

View File

@ -1,42 +0,0 @@
ARG GO_VERSION
# Build the manager binary
FROM golang:$GO_VERSION as builder
ARG CTIMEVAR
ARG TARGETOS
ARG TARGETARCH
WORKDIR /workspace
# Copy the Go Modules manifests
COPY go.mod go.mod
COPY go.sum go.sum
# cache deps before building and copying source so that we don't need to re-download as much
# and so that source changes don't invalidate our downloaded layer
RUN go mod download
# Copy the go source
COPY api/ api/
COPY internal/controller/ internal/controller/
COPY internal/ internal/
COPY pkg/ pkg/
COPY version/ version/
COPY cmd/main.go cmd/main.go
# Build
RUN CGO_ENABLED=0 GOOS=${TARGETOS:-linux} GOARCH=${TARGETARCH} go build -a -o manager cmd/main.go
# Use distroless as minimal base image to package the manager binary
# Refer to https://github.com/GoogleContainerTools/distroless for more details
FROM gcr.io/distroless/static:nonroot
LABEL maintainer="Jenkins Kubernetes Operator Community" \
org.opencontainers.image.authors="Jenkins Kubernetes Operator Community" \
org.opencontainers.image.title="jenkins-kubernetes-operator" \
org.opencontainers.image.description="Kubernetes native Jenkins Operator" \
org.opencontainers.image.url="quay.io/jenkins-kubernetes-operator/operator" \
org.opencontainers.image.source="https://github.com/jenkinsci/kubernetes-operator/tree/master" \
org.opencontainers.image.base.name="gcr.io/distroless/static:nonroot"
WORKDIR /
COPY --from=builder /workspace/manager .
USER 65532:65532
ENTRYPOINT ["/manager"]

603
Makefile
View File

@ -1,4 +1,74 @@
include variables.mk # Set POSIX sh for maximum interoperability
SHELL := /bin/sh
PATH := $(GOPATH)/bin:$(PATH)
OSFLAG :=
ifeq ($(OS),Windows_NT)
OSFLAG = WIN32
else
UNAME_S := $(shell uname -s)
ifeq ($(UNAME_S),Linux)
OSFLAG = LINUX
endif
ifeq ($(UNAME_S),Darwin)
OSFLAG = OSX
endif
endif
include config.base.env
# Import config
# You can change the default config with `make config="config_special.env" build`
config ?= config.minikube.env
include $(config)
# Set an output prefix, which is the local directory if not specified
PREFIX?=$(shell pwd)
VERSION := $(shell cat VERSION.txt)
GITCOMMIT := $(shell git rev-parse --short HEAD)
GITBRANCH := $(shell git rev-parse --abbrev-ref HEAD)
GITUNTRACKEDCHANGES := $(shell git status --porcelain --untracked-files=no)
GITIGNOREDBUTTRACKEDCHANGES := $(shell git ls-files -i --exclude-standard)
ifneq ($(GITUNTRACKEDCHANGES),)
GITCOMMIT := $(GITCOMMIT)-dirty
endif
ifneq ($(GITIGNOREDBUTTRACKEDCHANGES),)
GITCOMMIT := $(GITCOMMIT)-dirty
endif
VERSION_TAG := $(VERSION)
LATEST_TAG := latest
BUILD_TAG := $(GITBRANCH)-$(GITCOMMIT)
BUILD_PATH := ./cmd/manager
# CONTAINER_RUNTIME_COMMAND is Container Runtime - it could be docker or podman
CONTAINER_RUNTIME_COMMAND := docker
# Set any default go build tags
BUILDTAGS :=
# Set the build dir, where built cross-compiled binaries will be output
BUILDDIR := ${PREFIX}/cross
CTIMEVAR=-X $(PKG)/version.GitCommit=$(GITCOMMIT) -X $(PKG)/version.Version=$(VERSION)
GO_LDFLAGS=-ldflags "-w $(CTIMEVAR)"
GO_LDFLAGS_STATIC=-ldflags "-w $(CTIMEVAR) -extldflags -static"
# List the GOOS and GOARCH to build
GOOSARCHES = linux/amd64
PACKAGES = $(shell go list -f '{{.ImportPath}}/' ./... | grep -v vendor)
PACKAGES_FOR_UNIT_TESTS = $(shell go list -f '{{.ImportPath}}/' ./... | grep -v vendor | grep -v e2e)
# Run all the e2e tests by default
E2E_TEST_SELECTOR ?= .*
JENKINS_API_HOSTNAME := $(shell $(JENKINS_API_HOSTNAME_COMMAND) 2> /dev/null || echo "" )
OPERATOR_ARGS ?= --jenkins-api-hostname=$(JENKINS_API_HOSTNAME) --jenkins-api-port=$(JENKINS_API_PORT) --jenkins-api-use-nodeport=$(JENKINS_API_USE_NODEPORT) --cluster-domain=$(CLUSTER_DOMAIN) $(OPERATOR_EXTRA_ARGS)
.DEFAULT_GOAL := help
.PHONY: all .PHONY: all
all: status checkmake clean build verify install container-runtime-build container-runtime-images ## Build the image all: status checkmake clean build verify install container-runtime-build container-runtime-images ## Build the image
@ -51,7 +121,7 @@ build: deepcopy-gen $(NAME) ## Builds a dynamic executable or package
.PHONY: $(NAME) .PHONY: $(NAME)
$(NAME): $(wildcard *.go) $(wildcard */*.go) VERSION.txt $(NAME): $(wildcard *.go) $(wildcard */*.go) VERSION.txt
@echo "+ $@" @echo "+ $@"
CGO_ENABLED=0 go build -tags "$(BUILDTAGS)" ${GO_LDFLAGS} -o bin/manager $(BUILD_PATH) CGO_ENABLED=0 go build -tags "$(BUILDTAGS)" ${GO_LDFLAGS} -o build/_output/bin/jenkins-operator $(BUILD_PATH)
.PHONY: static .PHONY: static
static: ## Builds a static executable static: ## Builds a static executable
@ -66,26 +136,22 @@ fmt: ## Verifies all files have been `gofmt`ed
@go fmt $(PACKAGES) @go fmt $(PACKAGES)
.PHONY: lint .PHONY: lint
HAS_GOLINT := $(shell which $(PROJECT_DIR)/bin/golangci-lint) HAS_GOLINT := $(shell which golangci-lint)
lint: ## Verifies `golint` passes lint: ## Verifies `golint` passes
@echo "+ $@" @echo "+ $@"
ifndef HAS_GOLINT ifndef HAS_GOLINT
GOBIN=$(PROJECT_DIR)/bin go install github.com/golangci/golangci-lint/cmd/golangci-lint@v1.55.0 go get github.com/golangci/golangci-lint/cmd/golangci-lint@v1.26.0
endif endif
@bin/golangci-lint run @golangci-lint run
.PHONY: lint-fix
lint-fix: golangci-lint ## Run golangci-lint linter and perform fixes
@bin/golangci-lint run --fix
.PHONY: goimports .PHONY: goimports
HAS_GOIMPORTS := $(shell which $(PROJECT_DIR)/bin/goimports) HAS_GOIMPORTS := $(shell which goimports)
goimports: ## Verifies `goimports` passes goimports: ## Verifies `goimports` passes
@echo "+ $@" @echo "+ $@"
ifndef HAS_GOIMPORTS ifndef HAS_GOIMPORTS
$(call GOBIN=$(PROJECT_DIR)/bin go install golang.org/x/tools/cmd/goimports@v0.1.0) go get -u golang.org/x/tools/cmd/goimports
endif endif
@bin/goimports -l -e $(shell find . -type f -name '*.go' -not -path "./vendor/*") @goimports -l -e $(shell find . -type f -name '*.go' -not -path "./vendor/*")
.PHONY: test .PHONY: test
test: ## Runs the go tests test: ## Runs the go tests
@ -93,63 +159,47 @@ test: ## Runs the go tests
@RUNNING_TESTS=1 go test -tags "$(BUILDTAGS) cgo" $(PACKAGES_FOR_UNIT_TESTS) @RUNNING_TESTS=1 go test -tags "$(BUILDTAGS) cgo" $(PACKAGES_FOR_UNIT_TESTS)
.PHONY: e2e .PHONY: e2e
e2e: deepcopy-gen manifests backup-kind-load jenkins-kind-load ## Runs e2e tests, you can use EXTRA_ARGS CURRENT_DIRECTORY := $(shell pwd)
e2e: container-runtime-build ## Runs e2e tests, you can use EXTRA_ARGS
@echo "+ $@" @echo "+ $@"
RUNNING_TESTS=1 go test -parallel=1 "./test/e2e/" -ginkgo.v -tags "$(BUILDTAGS) cgo" -v -timeout 60m -run "$(E2E_TEST_SELECTOR)" \ @echo "Docker image: $(DOCKER_REGISTRY):$(GITCOMMIT)"
-jenkins-api-hostname=$(JENKINS_API_HOSTNAME) -jenkins-api-port=$(JENKINS_API_PORT) -jenkins-api-use-nodeport=$(JENKINS_API_USE_NODEPORT) $(E2E_TEST_ARGS) ifeq ($(KUBERNETES_PROVIDER),minikube)
kubectl config use-context $(KUBECTL_CONTEXT)
.PHONY: jenkins-kind-load endif
jenkins-kind-load: ## Load the jenkins lts version in kind to speed up tests ifeq ($(KUBERNETES_PROVIDER),crc)
@echo "+ $@" oc project $(CRC_OC_PROJECT)
docker pull jenkins/jenkins:$(LATEST_LTS_VERSION) endif
kind load docker-image jenkins/jenkins:$(LATEST_LTS_VERSION) --name $(KIND_CLUSTER_NAME) cp deploy/service_account.yaml deploy/namespace-init.yaml
cat deploy/role.yaml >> deploy/namespace-init.yaml
## Backup Section cat deploy/role_binding.yaml >> deploy/namespace-init.yaml
cat deploy/operator.yaml >> deploy/namespace-init.yaml
.PHONY: backup-kind-load ifeq ($(OSFLAG), LINUX)
backup-kind-load: ## Load latest backup image in the cluster ifeq ($(IMAGE_PULL_MODE), remote)
@echo "+ $@" sed -i 's|\(image:\).*|\1 $(DOCKER_ORGANIZATION)/$(DOCKER_REGISTRY):$(GITCOMMIT)|g' deploy/namespace-init.yaml
make -C backup/pvc backup-kind-load sed -i 's|\(imagePullPolicy\): IfNotPresent|\1: Always|g' deploy/namespace-init.yaml
## HELM Section
.PHONY: helm
HAS_HELM := $(shell command -v helm 2> /dev/null)
helm: ## Download helm if it's not present, otherwise symlink
@echo "+ $@"
ifeq ($(strip $(HAS_HELM)),)
mkdir -p $(PROJECT_DIR)/bin
curl -Lo $(PROJECT_DIR)/bin/helm.tar.gz https://get.helm.sh/helm-v$(HELM_VERSION)-$(PLATFORM)-amd64.tar.gz && tar xzfv $(PROJECT_DIR)/bin/helm.tar.gz -C $(PROJECT_DIR)/bin
mv $(PROJECT_DIR)/bin/$(PLATFORM)-amd64/helm $(PROJECT_DIR)/bin/helm
rm -rf $(PROJECT_DIR)/bin/$(PLATFORM)-amd64
rm -rf $(PROJECT_DIR)/bin/helm.tar.gz
else else
mkdir -p $(PROJECT_DIR)/bin sed -i 's|\(image:\).*|\1 $(DOCKER_REGISTRY):$(GITCOMMIT)|g' deploy/namespace-init.yaml
test -L $(PROJECT_DIR)/bin/helm || ln -sf $(shell command -v helm) $(PROJECT_DIR)/bin/helm endif
ifeq ($(KUBERNETES_PROVIDER),minikube)
sed -i 's|\(imagePullPolicy\): IfNotPresent|\1: Never|g' deploy/namespace-init.yaml
endif
endif endif
.PHONY: helm-lint ifeq ($(OSFLAG), OSX)
helm-lint: helm ifeq ($(IMAGE_PULL_MODE), remote)
bin/helm lint chart/jenkins-operator sed -i '' 's|\(image:\).*|\1 $(DOCKER_ORGANIZATION)/$(DOCKER_REGISTRY):$(GITCOMMIT)|g' deploy/namespace-init.yaml
sed -i '' 's|\(imagePullPolicy\): IfNotPresent|\1: Always|g' deploy/namespace-init.yaml
else
sed -i '' 's|\(image:\).*|\1 $(DOCKER_REGISTRY):$(GITCOMMIT)|g' deploy/namespace-init.yaml
endif
ifeq ($(KUBERNETES_PROVIDER),minikube)
sed -i '' 's|\(imagePullPolicy\): IfNotPresent|\1: Never|g' deploy/namespace-init.yaml
endif
endif
.PHONY: helm-release-latest RUNNING_TESTS=1 go test -parallel=1 "./test/e2e/" -tags "$(BUILDTAGS) cgo" -v -timeout 60m -run "$(E2E_TEST_SELECTOR)" \
helm-release-latest: helm -root=$(CURRENT_DIRECTORY) -kubeconfig=$(HOME)/.kube/config -globalMan deploy/crds/jenkins_$(API_VERSION)_jenkins_crd.yaml \
mkdir -p /tmp/jenkins-operator-charts -namespacedMan deploy/namespace-init.yaml $(TEST_ARGS)
mv chart/jenkins-operator/*.tgz /tmp/jenkins-operator-charts
cd chart && ../bin/helm package jenkins-operator
mv chart/jenkins-operator-*.tgz chart/jenkins-operator/
bin/helm repo index chart/ --url https://raw.githubusercontent.com/jenkinsci/kubernetes-operator/master/chart/ --merge chart/index.yaml
mv /tmp/jenkins-operator-charts/*.tgz chart/jenkins-operator/
.PHONY: helm-e2e
IMAGE_NAME := quay.io/$(QUAY_ORGANIZATION)/$(QUAY_REGISTRY):$(GITCOMMIT)-amd64
helm-e2e: helm container-runtime-build-amd64 backup-kind-load ## Runs helm e2e tests, you can use EXTRA_ARGS
kind load docker-image ${IMAGE_NAME} --name $(KIND_CLUSTER_NAME)
@echo "+ $@"
RUNNING_TESTS=1 go test -parallel=1 "./test/helm/" -ginkgo.v -tags "$(BUILDTAGS) cgo" -v -timeout 60m -run "$(E2E_TEST_SELECTOR)" -image-name=$(IMAGE_NAME) $(E2E_TEST_ARGS)
## CODE CHECKS section
.PHONY: vet .PHONY: vet
vet: ## Verifies `go vet` passes vet: ## Verifies `go vet` passes
@ -157,25 +207,23 @@ vet: ## Verifies `go vet` passes
@go vet $(PACKAGES) @go vet $(PACKAGES)
.PHONY: staticcheck .PHONY: staticcheck
HAS_STATICCHECK := $(shell which $(PROJECT_DIR)/bin/staticcheck) HAS_STATICCHECK := $(shell which staticcheck)
PLATFORM = $(shell echo $(UNAME_S) | tr A-Z a-z)
staticcheck: ## Verifies `staticcheck` passes staticcheck: ## Verifies `staticcheck` passes
@echo "+ $@" @echo "+ $@"
ifndef HAS_STATICCHECK ifndef HAS_STATICCHECK
$(eval TMP_DIR := $(shell mktemp -d)) wget -O staticcheck_$(PLATFORM)_amd64.tar.gz https://github.com/dominikh/go-tools/releases/download/2020.1.3/staticcheck_$(PLATFORM)_amd64.tar.gz
wget -O $(TMP_DIR)/staticcheck_$(PLATFORM)_amd64.tar.gz https://github.com/dominikh/go-tools/releases/download/2023.1.7/staticcheck_$(PLATFORM)_amd64.tar.gz tar zxvf staticcheck_$(PLATFORM)_amd64.tar.gz
tar zxvf $(TMP_DIR)/staticcheck_$(PLATFORM)_amd64.tar.gz -C $(TMP_DIR) mkdir -p $(GOPATH)/bin
mkdir -p $(PROJECT_DIR)/bin mv staticcheck/staticcheck $(GOPATH)/bin
mv $(TMP_DIR)/staticcheck/staticcheck $(PROJECT_DIR)/bin
rm -rf $(TMP_DIR)
endif endif
@$(PROJECT_DIR)/bin/staticcheck $(PACKAGES) @staticcheck $(PACKAGES)
.PHONY: cover .PHONY: cover
cover: ## Runs go test with coverage cover: ## Runs go test with coverage
@echo "" > coverage.txt @echo "" > coverage.txt
@for d in $(PACKAGES); do \ @for d in $(PACKAGES); do \
ENVTEST_K8S_VERSION = 1.26 IMG_RUNNING_TESTS=1 go test -race -coverprofile=profile.out -covermode=atomic "$$d"; \
KUBEBUILDER_ASSETS="$(shell $(ENVTEST) use $(ENVTEST_K8S_VERSION) --bin-dir $(LOCALBIN) -p path)" IMG_RUNNING_TESTS=1 go test -race -coverprofile=profile.out -covermode=atomic "$$d"; \
if [ -f profile.out ]; then \ if [ -f profile.out ]; then \
cat profile.out >> coverage.txt; \ cat profile.out >> coverage.txt; \
rm profile.out; \ rm profile.out; \
@ -191,62 +239,21 @@ install: ## Installs the executable
@echo "+ $@" @echo "+ $@"
go install -tags "$(BUILDTAGS)" ${GO_LDFLAGS} $(BUILD_PATH) go install -tags "$(BUILDTAGS)" ${GO_LDFLAGS} $(BUILD_PATH)
.PHONY: update-plugins
update-plugins: ## Update jenkins base plugins
@echo "+ $@"
@JENKINS_VERSION=$$(sed -n 's/LATEST_LTS_VERSION=//p' config.base.env | tr -d '\n' | tr -d '"'); \
echo "updating plugins for Jenkins $$JENKINS_VERSION"; \
./.github/workflows/update-plugins.sh "$$JENKINS_VERSION"
.PHONY: update-plugins-dry-run
update-plugins-dry-run: ## Update jenkins base plugin -- dry run
@echo "+ $@"
@JENKINS_VERSION=$$(sed -n 's/LATEST_LTS_VERSION=//p' config.base.env | tr -d '\n' | tr -d '"'); \
echo "checking plugins for Jenkins $$JENKINS_VERSION -- dry run"; \
./.github/workflows/update-plugins.sh "$$JENKINS_VERSION" --dry-run
.PHONY: update-jenkins-lts
update-jenkins-lts: ## Fetch latest Jenkins lts version and update if necessary
@echo "+ $@"
@LATEST_VERSION=$$(curl -s https://www.jenkins.io/changelog-stable/ | grep -oP 'changelog/\K\d+\.\d+\.\d+' | head -1); \
CURRENT_VERSION=$$(sed -n 's/LATEST_LTS_VERSION=//p' config.base.env | tr -d '\n' | tr -d '"'); \
echo "current version: $$CURRENT_VERSION"; \
echo "latest version: $$LATEST_VERSION"; \
if [ "$$CURRENT_VERSION" != "$$LATEST_VERSION" ]; then \
echo "updating Jenkins lts version from $$CURRENT_VERSION to $$LATEST_VERSION"; \
sed -i "s/LATEST_LTS_VERSION=\".*\"/LATEST_LTS_VERSION=\"$$LATEST_VERSION\"/" config.base.env; \
$(MAKE) update-lts-version LATEST_LTS_VERSION=$$LATEST_VERSION; \
echo "updated Jenkins LTS version to $$LATEST_VERSION"; \
else \
echo "up to date"; \
fi
.PHONY: update-lts-version
update-lts-version: ## Update the latest lts version
@echo "+ $@"
echo $(LATEST_LTS_VERSION)
sed -i 's|jenkins/jenkins:[0-9]\+.[0-9]\+.[0-9]\+|jenkins/jenkins:$(LATEST_LTS_VERSION)|g' chart/jenkins-operator/values.yaml
sed -i 's|jenkins/jenkins:[0-9]\+.[0-9]\+.[0-9]\+|jenkins/jenkins:$(LATEST_LTS_VERSION)|g' test/e2e/test_utility.go
sed -i 's|jenkins/jenkins:[0-9]\+.[0-9]\+.[0-9]\+|jenkins/jenkins:$(LATEST_LTS_VERSION)|g' test/helm/helm_test.go
sed -i 's|jenkins/jenkins:[0-9]\+.[0-9]\+.[0-9]\+|jenkins/jenkins:$(LATEST_LTS_VERSION)|g' pkg/constants/constants.go
#TODO: source the version from config.base.env for bats test, no need of hardcoded version
sed -i 's|jenkins/jenkins:[0-9]\+.[0-9]\+.[0-9]\+|jenkins/jenkins:$(LATEST_LTS_VERSION)|g' test/bats/1-deploy.bats
sed -i 's|jenkins/jenkins:[0-9]\+.[0-9]\+.[0-9]\+|jenkins/jenkins:$(LATEST_LTS_VERSION)|g' test/bats/2-deploy-with-more-options.bats
sed -i 's|jenkins/jenkins:[0-9]\+.[0-9]\+.[0-9]\+|jenkins/jenkins:$(LATEST_LTS_VERSION)|g' test/bats/3-deploy-with-webhook.bats
.PHONY: run .PHONY: run
run: export WATCH_NAMESPACE = $(NAMESPACE) run: export WATCH_NAMESPACE = $(NAMESPACE)
run: export OPERATOR_NAME = $(NAME) run: export OPERATOR_NAME = $(NAME)
run: fmt vet install-crds build ## Run the executable, you can use EXTRA_ARGS run: build ## Run the executable, you can use EXTRA_ARGS
@echo "+ $@" @echo "+ $@"
ifeq ($(KUBERNETES_PROVIDER),kind) ifeq ($(KUBERNETES_PROVIDER),minikube)
kubectl config use-context $(KUBECTL_CONTEXT) kubectl config use-context $(KUBECTL_CONTEXT)
endif endif
ifeq ($(KUBERNETES_PROVIDER),crc) ifeq ($(KUBERNETES_PROVIDER),crc)
oc project $(CRC_OC_PROJECT) oc project $(CRC_OC_PROJECT)
endif endif
kubectl apply -f deploy/crds/jenkins_$(API_VERSION)_jenkins_crd.yaml
kubectl apply -f deploy/crds/jenkins_$(API_VERSION)_jenkinsimage_crd.yaml
@echo "Watching '$(WATCH_NAMESPACE)' namespace" @echo "Watching '$(WATCH_NAMESPACE)' namespace"
bin/manager $(OPERATOR_ARGS) build/_output/bin/jenkins-operator $(OPERATOR_ARGS)
.PHONY: clean .PHONY: clean
clean: ## Cleanup any build binaries or packages clean: ## Cleanup any build binaries or packages
@ -301,66 +308,46 @@ endif
container-runtime-login: ## Log in into the Docker repository container-runtime-login: ## Log in into the Docker repository
@echo "+ $@" @echo "+ $@"
.PHONY: container-runtime-build-%
container-runtime-build-%: ## Build the container
@echo "+ $@"
$(CONTAINER_RUNTIME_COMMAND) buildx build \
--output=type=docker --platform linux/$* \
--build-arg GO_VERSION=$(GO_VERSION) \
--build-arg CTIMEVAR="$(CTIMEVAR)" \
--tag quay.io/$(QUAY_ORGANIZATION)/$(QUAY_REGISTRY):$(GITCOMMIT)-$* . \
--file Dockerfile $(CONTAINER_RUNTIME_EXTRA_ARGS)
.PHONY: container-runtime-build .PHONY: container-runtime-build
container-runtime-build: check-env deepcopy-gen container-runtime-build-amd64 container-runtime-build-arm64 container-runtime-build: check-env ## Build the container
@echo "+ $@"
$(CONTAINER_RUNTIME_COMMAND) build \
--build-arg GO_VERSION=$(GO_VERSION) \
--build-arg OPERATOR_SDK_VERSION=$(OPERATOR_SDK_VERSION) \
-t $(DOCKER_REGISTRY):$(GITCOMMIT) . \
--file build/Dockerfile $(CONTAINER_RUNTIME_EXTRA_ARGS)
.PHONY: container-runtime-images .PHONY: container-runtime-images
container-runtime-images: ## List all local containers container-runtime-images: ## List all local containers
@echo "+ $@" @echo "+ $@"
$(CONTAINER_RUNTIME_COMMAND) images $(CONTAINER_RUNTIME_EXTRA_ARGS) $(CONTAINER_RUNTIME_COMMAND) images $(CONTAINER_RUNTIME_EXTRA_ARGS)
define buildx-create-command
$(CONTAINER_RUNTIME_COMMAND) buildx create \
--driver=docker-container \
--use
endef
## Parameter is version
define container-runtime-push-command
$(CONTAINER_RUNTIME_COMMAND) buildx build \
--output=type=registry --platform linux/amd64,linux/arm64 \
--build-arg GO_VERSION=$(GO_VERSION) \
--build-arg CTIMEVAR="$(CTIMEVAR)" \
--tag quay.io/$(QUAY_ORGANIZATION)/$(QUAY_REGISTRY):$(1) . \
--file Dockerfile $(CONTAINER_RUNTIME_EXTRA_ARGS)
endef
.PHONY: container-runtime-push .PHONY: container-runtime-push
container-runtime-push: check-env deepcopy-gen ## Push the container container-runtime-push: ## Push the container
@echo "+ $@" @echo "+ $@"
$(call buildx-create-command) $(CONTAINER_RUNTIME_COMMAND) tag $(DOCKER_REGISTRY):$(GITCOMMIT) $(DOCKER_ORGANIZATION)/$(DOCKER_REGISTRY):$(BUILD_TAG) $(CONTAINER_RUNTIME_EXTRA_ARGS)
$(call container-runtime-push-command,$(BUILD_TAG)) $(CONTAINER_RUNTIME_COMMAND) push $(DOCKER_ORGANIZATION)/$(DOCKER_REGISTRY):$(BUILD_TAG) $(CONTAINER_RUNTIME_EXTRA_ARGS)
.PHONY: container-runtime-snapshot-push .PHONY: container-runtime-snapshot-push
container-runtime-snapshot-push: check-env deepcopy-gen container-runtime-snapshot-push:
@echo "+ $@" @echo "+ $@"
$(call buildx-create-command) $(CONTAINER_RUNTIME_COMMAND) tag $(DOCKER_REGISTRY):$(GITCOMMIT) $(DOCKER_ORGANIZATION)/$(DOCKER_REGISTRY):$(GITCOMMIT) $(CONTAINER_RUNTIME_EXTRA_ARGS)
$(call container-runtime-push-command,$(GITCOMMIT)) $(CONTAINER_RUNTIME_COMMAND) push $(DOCKER_ORGANIZATION)/$(DOCKER_REGISTRY):$(GITCOMMIT) $(CONTAINER_RUNTIME_EXTRA_ARGS)
.PHONY: container-runtime-release-version .PHONY: container-runtime-release-version
container-runtime-release-version: check-env deepcopy-gen ## Release image with version tag (in addition to build tag) container-runtime-release-version: ## Release image with version tag (in addition to build tag)
@echo "+ $@" @echo "+ $@"
$(call buildx-create-command) $(CONTAINER_RUNTIME_COMMAND) tag $(DOCKER_REGISTRY):$(GITCOMMIT) $(DOCKER_ORGANIZATION)/$(DOCKER_REGISTRY):$(VERSION_TAG) $(CONTAINER_RUNTIME_EXTRA_ARGS)
$(call container-runtime-push-command,$(VERSION_TAG)) $(CONTAINER_RUNTIME_COMMAND) push $(DOCKER_ORGANIZATION)/$(DOCKER_REGISTRY):$(VERSION_TAG) $(CONTAINER_RUNTIME_EXTRA_ARGS)
.PHONY: container-runtime-release-latest .PHONY: container-runtime-release-latest
container-runtime-release-latest: check-env deepcopy-gen ## Release image with latest tags (in addition to build tag) container-runtime-release-latest: ## Release image with latest tags (in addition to build tag)
@echo "+ $@" @echo "+ $@"
$(call buildx-create-command) $(CONTAINER_RUNTIME_COMMAND) tag $(DOCKER_REGISTRY):$(GITCOMMIT) $(DOCKER_ORGANIZATION)/$(DOCKER_REGISTRY):$(LATEST_TAG) $(CONTAINER_RUNTIME_EXTRA_ARGS)
$(call container-runtime-push-command,$(LATEST_TAG)) $(CONTAINER_RUNTIME_COMMAND) push $(DOCKER_ORGANIZATION)/$(DOCKER_REGISTRY):$(LATEST_TAG) $(CONTAINER_RUNTIME_EXTRA_ARGS)
.PHONY: container-runtime-release .PHONY: container-runtime-release
container-runtime-release: container-runtime-release-version container-runtime-release-latest ## Release image with version and latest tags (in addition to build tag) container-runtime-release: container-runtime-build container-runtime-release-version container-runtime-release-latest ## Release image with version and latest tags (in addition to build tag)
@echo "+ $@" @echo "+ $@"
# if this session isn't interactive, then we don't want to allocate a # if this session isn't interactive, then we don't want to allocate a
@ -368,7 +355,7 @@ container-runtime-release: container-runtime-release-version container-runtime-r
# so that the user can send e.g. ^C through. # so that the user can send e.g. ^C through.
INTERACTIVE := $(shell [ -t 0 ] && echo 1 || echo 0) INTERACTIVE := $(shell [ -t 0 ] && echo 1 || echo 0)
ifeq ($(INTERACTIVE), 1) ifeq ($(INTERACTIVE), 1)
DOCKER_FLAGS += -t DOCKER_FLAGS += -t
endif endif
.PHONY: container-runtime-run .PHONY: container-runtime-run
@ -376,29 +363,52 @@ container-runtime-run: ## Run the container in docker, you can use EXTRA_ARGS
@echo "+ $@" @echo "+ $@"
$(CONTAINER_RUNTIME_COMMAND) run $(CONTAINER_RUNTIME_EXTRA_ARGS) --rm -i $(DOCKER_FLAGS) \ $(CONTAINER_RUNTIME_COMMAND) run $(CONTAINER_RUNTIME_EXTRA_ARGS) --rm -i $(DOCKER_FLAGS) \
--volume $(HOME)/.kube/config:/home/jenkins-operator/.kube/config \ --volume $(HOME)/.kube/config:/home/jenkins-operator/.kube/config \
quay.io/${QUAY_ORGANIZATION}/$(QUAY_REGISTRY):$(GITCOMMIT) /usr/bin/jenkins-operator $(OPERATOR_ARGS) $(DOCKER_REGISTRY):$(GITCOMMIT) /usr/bin/jenkins-operator $(OPERATOR_ARGS)
.PHONY: minikube-run
minikube-run: export WATCH_NAMESPACE = $(NAMESPACE)
minikube-run: export OPERATOR_NAME = $(NAME)
minikube-run: minikube-start ## Run the operator locally and use minikube as Kubernetes cluster, you can use OPERATOR_ARGS
@echo "+ $@"
kubectl config use-context minikube
kubectl apply -f deploy/crds/jenkins_$(API_VERSION)_jenkins_crd.yaml
@echo "Watching '$(WATCH_NAMESPACE)' namespace"
build/_output/bin/jenkins-operator $(OPERATOR_ARGS)
.PHONY: crc-run .PHONY: crc-run
crc-run: export WATCH_NAMESPACE = $(NAMESPACE) crc-run: export WATCH_NAMESPACE = $(NAMESPACE)
crc-run: export OPERATOR_NAME = $(NAME) crc-run: export OPERATOR_NAME = $(NAME)
crc-run: crc-start run ## Run the operator locally and use CodeReady Containers as Kubernetes cluster, you can use OPERATOR_ARGS crc-run: crc-start ## Run the operator locally and use CodeReady Containers as Kubernetes cluster, you can use OPERATOR_ARGS
@echo "+ $@" @echo "+ $@"
oc project $(CRC_OC_PROJECT)
kubectl apply -f deploy/crds/jenkins_$(API_VERSION)_jenkins_crd.yaml
@echo "Watching '$(WATCH_NAMESPACE)' namespace"
build/_output/bin/jenkins-operator $(OPERATOR_ARGS)
.PHONY: deepcopy-gen .PHONY: deepcopy-gen
deepcopy-gen: generate ## Generate deepcopy golang code deepcopy-gen: ## Generate deepcopy golang code
@echo "+ $@" @echo "+ $@"
operator-sdk generate k8s
.PHONY: scheme-doc-gen .PHONY: scheme-doc-gen
HAS_GEN_CRD_API_REFERENCE_DOCS := $(shell ls gen-crd-api-reference-docs 2> /dev/null) HAS_GEN_CRD_API_REFERENCE_DOCS := $(shell ls gen-crd-api-reference-docs 2> /dev/null)
scheme-doc-gen: ## Generate Jenkins CRD scheme doc scheme-doc-gen: ## Generate Jenkins CRD scheme doc
@echo "+ $@" @echo "+ $@"
ifndef HAS_GEN_CRD_API_REFERENCE_DOCS ifndef HAS_GEN_CRD_API_REFERENCE_DOCS
@wget https://github.com/ahmetb/$(GEN_CRD_API)/releases/download/v0.1.2/$(GEN_CRD_API)_$(PLATFORM)_amd64.tar.gz @wget https://github.com/ahmetb/$(GEN_CRD_API)/releases/download/v0.1.2/$(GEN_CRD_API)_linux_amd64.tar.gz
@mkdir -p $(GEN_CRD_API) @mkdir -p $(GEN_CRD_API)
@tar -C $(GEN_CRD_API) -zxf $(GEN_CRD_API)_$(PLATFORM)_amd64.tar.gz @tar -C $(GEN_CRD_API) -zxf $(GEN_CRD_API)_linux_amd64.tar.gz
@rm $(GEN_CRD_API)_$(PLATFORM)_amd64.tar.gz @rm $(GEN_CRD_API)_linux_amd64.tar.gz
endif
$(GEN_CRD_API)/$(GEN_CRD_API) -config gen-crd-api-config.json -api-dir github.com/jenkinsci/kubernetes-operator/pkg/apis/jenkins/$(API_VERSION) -template-dir $(GEN_CRD_API)/template -out-file documentation/$(VERSION)/jenkins-$(API_VERSION)-scheme.md
.PHONY: check-minikube
check-minikube: ## Checks if KUBERNETES_PROVIDER is set to minikube
@echo "+ $@"
@echo "KUBERNETES_PROVIDER '$(KUBERNETES_PROVIDER)'"
ifneq ($(KUBERNETES_PROVIDER),minikube)
$(error KUBERNETES_PROVIDER not set to 'minikube')
endif endif
$(GEN_CRD_API)/$(GEN_CRD_API) -config gen-crd-api-config.json -api-dir $(PKG)/api/$(API_VERSION) -template-dir $(GEN_CRD_API)/template -out-file documentation/$(VERSION)/jenkins-$(API_VERSION)-scheme.md
.PHONY: check-crc .PHONY: check-crc
check-crc: ## Checks if KUBERNETES_PROVIDER is set to crc check-crc: ## Checks if KUBERNETES_PROVIDER is set to crc
@ -408,77 +418,41 @@ ifneq ($(KUBERNETES_PROVIDER),crc)
$(error KUBERNETES_PROVIDER not set to 'crc') $(error KUBERNETES_PROVIDER not set to 'crc')
endif endif
.PHONY: kind-setup .PHONY: minikube-start
kind-setup: ## Setup kind cluster minikube-start: check-minikube ## Start minikube
@echo "+ $@" @echo "+ $@"
kind create cluster --config kind-cluster.yaml --name $(KIND_CLUSTER_NAME) @minikube status && exit 0 || \
minikube start --kubernetes-version $(MINIKUBE_KUBERNETES_VERSION) --dns-domain=$(CLUSTER_DOMAIN) --extra-config=kubelet.cluster-domain=$(CLUSTER_DOMAIN) --vm-driver=$(MINIKUBE_DRIVER) --memory 4096 --cpus 3
.PHONY: kind-clean
kind-clean: ## Delete kind cluster
@echo "+ $@"
kind delete cluster --name $(KIND_CLUSTER_NAME)
.PHONY: kind-revamp
kind-revamp: kind-clean kind-setup ## Delete and recreate kind cluster
@echo "+ $@"
.PHONY: bats-tests ## Run bats tests
IMAGE_NAME := quay.io/$(QUAY_ORGANIZATION)/$(QUAY_REGISTRY):$(GITCOMMIT)-amd64
BUILD_PRESENT := $(shell docker images |grep -q ${IMAGE_NAME})
ifndef BUILD_PRESENT
bats-tests: backup-kind-load container-runtime-build-amd64 ## Run bats tests
@echo "+ $@"
kind load docker-image ${IMAGE_NAME} --name $(KIND_CLUSTER_NAME)
OPERATOR_IMAGE="${IMAGE_NAME}" TERM=xterm bats -T -p test/bats$(if $(BATS_TEST_PATH),/${BATS_TEST_PATH})
else
bats-tests: backup-kind-load
@echo "+ $@"
OPERATOR_IMAGE="${IMAGE_NAME}" TERM=xterm bats -T -p test/bats$(if $(BATS_TEST_PATH),/${BATS_TEST_PATH})
endif
.PHONY: crc-start .PHONY: crc-start
crc-start: check-crc ## Start CodeReady Containers Kubernetes cluster crc-start: check-crc ## Start CodeReady Containers Kubernetes cluster
@echo "+ $@" @echo "+ $@"
crc start crc start
.PHONY: sembump
HAS_SEMBUMP := $(shell which $(PROJECT_DIR)/bin/sembump)
sembump: # Download sembump locally if necessary
@echo "+ $@"
ifndef HAS_SEMBUMP
mkdir -p $(PROJECT_DIR)/bin
wget -O $(PROJECT_DIR)/bin/sembump https://github.com/justintout/sembump/releases/download/v0.1.0/sembump-$(PLATFORM)-amd64
chmod +x $(PROJECT_DIR)/bin/sembump
endif
.PHONY: bump-version .PHONY: bump-version
BUMP := patch BUMP := patch
bump-version: sembump ## Bump the version in the version file. Set BUMP to [ patch | major | minor ] bump-version: ## Bump the version in the version file. Set BUMP to [ patch | major | minor ]
@echo "+ $@" @echo "+ $@"
$(eval NEW_VERSION=$(shell bin/sembump --kind $(BUMP) $(VERSION))) #@go get -u github.com/jessfraz/junk/sembump # update sembump tool FIXME
$(eval NEW_VERSION=$(shell sembump --kind $(BUMP) $(VERSION)))
@echo "Bumping VERSION.txt from $(VERSION) to $(NEW_VERSION)" @echo "Bumping VERSION.txt from $(VERSION) to $(NEW_VERSION)"
echo $(NEW_VERSION) > VERSION.txt echo $(NEW_VERSION) > VERSION.txt
@echo "Updating version from $(VERSION) to $(NEW_VERSION) in README.md" @echo "Updating version from $(VERSION) to $(NEW_VERSION) in README.md"
sed -i.bak 's/$(VERSION)/$(NEW_VERSION)/g' README.md sed -i s/$(VERSION)/$(NEW_VERSION)/g README.md
sed -i.bak 's/$(VERSION)/$(NEW_VERSION)/g' config/manager/manager.yaml sed -i s/$(VERSION)/$(NEW_VERSION)/g deploy/operator.yaml
sed -i.bak 's/$(VERSION)/$(NEW_VERSION)/g' deploy/$(ALL_IN_ONE_DEPLOY_FILE_PREFIX)-$(API_VERSION).yaml sed -i s/$(VERSION)/$(NEW_VERSION)/g deploy/$(ALL_IN_ONE_DEPLOY_FILE_PREFIX)-$(API_VERSION).yaml
rm */*/**.bak cp deploy/service_account.yaml deploy/$(ALL_IN_ONE_DEPLOY_FILE_PREFIX)-$(API_VERSION).yaml
rm */**.bak cat deploy/role.yaml >> deploy/$(ALL_IN_ONE_DEPLOY_FILE_PREFIX)-$(API_VERSION).yaml
rm *.bak cat deploy/role_binding.yaml >> deploy/$(ALL_IN_ONE_DEPLOY_FILE_PREFIX)-$(API_VERSION).yaml
cp config/service_account.yaml deploy/$(ALL_IN_ONE_DEPLOY_FILE_PREFIX)-$(API_VERSION).yaml cat deploy/operator.yaml >> deploy/$(ALL_IN_ONE_DEPLOY_FILE_PREFIX)-$(API_VERSION).yaml
cat config/rbac/leader_election_role.yaml >> deploy/$(ALL_IN_ONE_DEPLOY_FILE_PREFIX)-$(API_VERSION).yaml git add VERSION.txt README.md deploy/operator.yaml deploy/$(ALL_IN_ONE_DEPLOY_FILE_PREFIX)-$(API_VERSION).yaml
cat config/rbac/leader_election_role_binding.yaml >> deploy/$(ALL_IN_ONE_DEPLOY_FILE_PREFIX)-$(API_VERSION).yaml
cat config/rbac/role.yaml >> deploy/$(ALL_IN_ONE_DEPLOY_FILE_PREFIX)-$(API_VERSION).yaml
cat config/rbac/role_binding.yaml >> deploy/$(ALL_IN_ONE_DEPLOY_FILE_PREFIX)-$(API_VERSION).yaml
cat config/manager/manager.yaml >> deploy/$(ALL_IN_ONE_DEPLOY_FILE_PREFIX)-$(API_VERSION).yaml
git add VERSION.txt README.md config/manager/manager.yaml deploy/$(ALL_IN_ONE_DEPLOY_FILE_PREFIX)-$(API_VERSION).yaml
git commit -vaem "Bump version to $(NEW_VERSION)" git commit -vaem "Bump version to $(NEW_VERSION)"
@echo "Run make tag to create and push the tag for new version $(NEW_VERSION)" @echo "Run make tag to create and push the tag for new version $(NEW_VERSION)"
.PHONY: tag .PHONY: tag
tag: ## Create a new git tag to prepare to build a release tag: ## Create a new git tag to prepare to build a release
@echo "+ $@" @echo "+ $@"
git tag -a $(VERSION) -m "$(VERSION)" git tag -s -a $(VERSION) -m "$(VERSION)"
git push origin $(VERSION) git push origin $(VERSION)
.PHONY: help .PHONY: help
@ -497,187 +471,30 @@ ifneq ($(GITUNTRACKEDCHANGES),)
endif endif
ifneq ($(GITIGNOREDBUTTRACKEDCHANGES),) ifneq ($(GITIGNOREDBUTTRACKEDCHANGES),)
@echo "Ignored but tracked files:" @echo "Ignored but tracked files:"
@git ls-files -i -o --exclude-standard @git ls-files -i --exclude-standard
@echo @echo
endif endif
@echo "Dependencies:" @echo "Dependencies:"
go mod vendor -v go mod vendor -v
@echo @echo
.PHONY: helm-package
helm-package:
@echo "+ $@"
mkdir -p /tmp/jenkins-operator-charts
mv chart/jenkins-operator/*.tgz /tmp/jenkins-operator-charts
cd chart && helm package jenkins-operator
mv /tmp/jenkins-operator-charts/*.tgz chart/jenkins-operator/
rm -rf /tmp/jenkins-operator-charts/
# Download and build hugo extended locally if necessary .PHONY: helm-deploy
HUGO_PATH = $(shell pwd)/bin/hugo helm-deploy: helm-package
HUGO_VERSION = v0.99.1 @echo "+ $@"
HAS_HUGO := $(shell $(HUGO_PATH)/hugo version 2>&- | grep $(HUGO_VERSION)) helm repo index chart/ --url https://raw.githubusercontent.com/jenkinsci/kubernetes-operator/master/chart/jenkins-operator/
.PHONY: hugo cd chart/ && mv jenkins-operator-*.tgz jenkins-operator
hugo:
ifeq ($(HAS_HUGO), )
@echo "Installing Hugo $(HUGO_VERSION)"
rm -rf $(HUGO_PATH)
git clone https://github.com/gohugoio/hugo.git --depth=1 --branch $(HUGO_VERSION) $(HUGO_PATH)
cd $(HUGO_PATH) && go build --tags extended -o hugo main.go
endif
.PHONY: generate-docs .PHONY: generate-docs
generate-docs: hugo ## Re-generate docs directory from the website directory generate-docs: ## Re-generate docs directory from the website directory
@echo "+ $@" @echo "+ $@"
rm -rf docs || echo "Cannot remove docs dir, ignoring" rm -rf docs || echo "Cannot remove docs dir, ignoring"
cd website && npm install hugo -s website -d ../docs
$(HUGO_PATH)/hugo -s website -d ../docs
.PHONY: run-docs
run-docs: hugo
@echo "+ $@"
cd website && $(HUGO_PATH)/hugo server -D
##################### FROM OPERATOR SDK ########################
# Install CRDs into a cluster
.PHONY: install-crds
install-crds: manifests kustomize
$(KUSTOMIZE) build config/crd | kubectl apply -f -
# Uninstall CRDs from a cluster
ifndef ignore-not-found
ignore-not-found = false
endif
.PHONY: uninstall-crds
uninstall-crds: manifests kustomize
$(KUSTOMIZE) build config/crd | kubectl delete --ignore-not-found=$(ignore-not-found) -f -
# Deploy controller in the configured Kubernetes cluster in ~/.kube/config
.PHONY: deploy
deploy: manifests kustomize
cd config/manager && $(KUSTOMIZE) edit set image controller=quay.io/$(QUAY_ORGANIZATION)/$(QUAY_REGISTRY):$(GITCOMMIT)
$(KUSTOMIZE) build config/default | kubectl apply -f -
# UnDeploy controller from the configured Kubernetes cluster in ~/.kube/config
.PHONY: undeploy
undeploy:
$(KUSTOMIZE) build config/default | kubectl delete --ignore-not-found=$(ignore-not-found) -f -
# Generate manifests e.g. CRD, RBAC etc.
.PHONY: manifests
manifests: controller-gen
$(CONTROLLER_GEN) rbac:roleName=manager-role crd webhook paths="./..." output:crd:artifacts:config=config/crd/bases
# Generate code
.PHONY: generate
generate: controller-gen
$(CONTROLLER_GEN) object:headerFile="hack/boilerplate.go.txt" paths="./..."
##@ Build Dependencies
## Location to install dependencies to
LOCALBIN ?= $(shell pwd)/bin
$(LOCALBIN):
mkdir -p $(LOCALBIN)
## Tool Binaries
KUSTOMIZE ?= $(LOCALBIN)/kustomize
CONTROLLER_GEN ?= $(LOCALBIN)/controller-gen
ENVTEST ?= $(LOCALBIN)/setup-envtest
## Tool Versions
KUSTOMIZE_VERSION ?= v5.3.0
CONTROLLER_TOOLS_VERSION ?= v0.14.0
KUSTOMIZE_INSTALL_SCRIPT ?= "https://raw.githubusercontent.com/kubernetes-sigs/kustomize/master/hack/install_kustomize.sh"
.PHONY: kustomize
kustomize: $(KUSTOMIZE) ## Download kustomize locally if necessary.
$(KUSTOMIZE): $(LOCALBIN)
test -s $(LOCALBIN)/kustomize || { curl -s $(KUSTOMIZE_INSTALL_SCRIPT) | bash -s -- $(subst v,,$(KUSTOMIZE_VERSION)) $(LOCALBIN); }
.PHONY: controller-gen
controller-gen: $(CONTROLLER_GEN) ## Download controller-gen locally if necessary.
$(CONTROLLER_GEN): $(LOCALBIN)
test -s $(LOCALBIN)/controller-gen || GOBIN=$(LOCALBIN) go install sigs.k8s.io/controller-tools/cmd/controller-gen@$(CONTROLLER_TOOLS_VERSION)
.PHONY: envtest
envtest: $(ENVTEST) ## Download envtest-setup locally if necessary.
$(ENVTEST): $(LOCALBIN)
test -s $(LOCALBIN)/setup-envtest || GOBIN=$(LOCALBIN) go install sigs.k8s.io/controller-runtime/tools/setup-envtest@release-0.17
.PHONY: operator-sdk
HAS_OPERATOR_SDK := $(shell which $(PROJECT_DIR)/bin/operator-sdk)
operator-sdk: # Download operator-sdk locally if necessary
@echo "+ $@"
ifndef HAS_OPERATOR_SDK
wget -O $(PROJECT_DIR)/bin/operator-sdk https://github.com/operator-framework/operator-sdk/releases/download/v${OPERATOR_SDK_VERSION}/operator-sdk_$(PLATFORM)_amd64
chmod +x $(PROJECT_DIR)/bin/operator-sdk
endif
# Generate bundle manifests and metadata, then validate generated files.
.PHONY: bundle
bundle: manifests operator-sdk kustomize
bin/operator-sdk generate kustomize manifests -q
cd config/manager && $(KUSTOMIZE) edit set image controller=quay.io/$(QUAY_ORGANIZATION)/$(QUAY_REGISTRY):$(VERSION_TAG)
$(KUSTOMIZE) build config/manifests | bin/operator-sdk generate bundle $(BUNDLE_GEN_FLAGS)
bin/operator-sdk bundle validate ./bundle
# Build the bundle image.
.PHONY: bundle-build
bundle-build:
docker build -f bundle.Dockerfile -t $(BUNDLE_IMG) .
# Download kubebuilder
.PHONY: kubebuilder
kubebuilder:
mkdir -p ${ENVTEST_ASSETS_DIR}
test -f ${ENVTEST_ASSETS_DIR}/setup-envtest.sh || curl -sSLo ${ENVTEST_ASSETS_DIR}/setup-envtest.sh https://raw.githubusercontent.com/kubernetes-sigs/controller-runtime/v0.7.0/hack/setup-envtest.sh
source ${ENVTEST_ASSETS_DIR}/setup-envtest.sh; fetch_envtest_tools $(ENVTEST_ASSETS_DIR); setup_envtest_env $(ENVTEST_ASSETS_DIR);
# install cert-manager v1.5.1
install-cert-manager: kind-setup
kubectl apply -f https://github.com/jetstack/cert-manager/releases/download/v1.5.1/cert-manager.yaml
uninstall-cert-manager: kind-setup
kubectl delete -f https://github.com/jetstack/cert-manager/releases/download/v1.5.1/cert-manager.yaml
# Deploy the operator locally along with webhook using helm charts
.PHONY: deploy-webhook
deploy-webhook: container-runtime-build-amd64
@echo "+ $@"
bin/helm upgrade jenkins chart/jenkins-operator --install --set-string operator.image=${IMAGE_NAME} --set webhook.enabled=true --set jenkins.enabled=false
# https://sdk.operatorframework.io/docs/upgrading-sdk-version/v1.6.1/#gov2-gov3-ansiblev1-helmv1-add-opm-and-catalog-build-makefile-targets
.PHONY: opm
OPM = ./bin/opm
opm:
ifeq (,$(wildcard $(OPM)))
ifeq (,$(shell which opm 2>/dev/null))
@{ \
set -e ;\
mkdir -p $(dir $(OPM)) ;\
curl -sSLo $(OPM) https://github.com/operator-framework/operator-registry/releases/download/v1.23.0/$${OS}-$${ARCH}-opm ;\
chmod +x $(OPM) ;\
}
else
OPM = $(shell which opm)
endif
endif
BUNDLE_IMGS ?= $(BUNDLE_IMG)
CATALOG_IMG ?= $(IMAGE_TAG_BASE)-catalog:v$(VERSION) ifneq ($(origin CATALOG_BASE_IMG), undefined) FROM_INDEX_OPT := --from-index $(CATALOG_BASE_IMG) endif
.PHONY: catalog-build
catalog-build: opm
$(OPM) index add --container-tool docker --mode semver --tag $(CATALOG_IMG) --bundles $(BUNDLE_IMGS) $(FROM_INDEX_OPT)
.PHONY: catalog-push
catalog-push: ## Push the catalog image.
$(MAKE) docker-push IMG=$(CATALOG_IMG)
# PLATFORMS defines the target platforms for the manager image be build to provide support to multiple
# architectures. (i.e. make docker-buildx IMG=myregistry/mypoperator:0.0.1). To use this option you need to:
# - able to use docker buildx . More info: https://docs.docker.com/build/buildx/
# - have enable BuildKit, More info: https://docs.docker.com/develop/develop-images/build_enhancements/
# - be able to push the image for your registry (i.e. if you do not inform a valid value via IMG=<myregistry/image:<tag>> than the export will fail)
# To properly provided solutions that supports more than one platform you should use this option.
PLATFORMS ?= linux/arm64,linux/amd64,linux/s390x,linux/ppc64le
.PHONY: docker-buildx
docker-buildx: test ## Build and push docker image for the manager for cross-platform support
# copy existing Dockerfile and insert --platform=${BUILDPLATFORM} into Dockerfile.cross, and preserve the original Dockerfile
sed -e '1 s/\(^FROM\)/FROM --platform=\$$\{BUILDPLATFORM\}/; t' -e ' 1,// s//FROM --platform=\$$\{BUILDPLATFORM\}/' Dockerfile > Dockerfile.cross
- docker buildx create --name project-v3-builder
docker buildx use project-v3-builder
- docker buildx build --push --platform=$(PLATFORMS) --tag ${IMG} -f Dockerfile.cross
- docker buildx rm project-v3-builder
rm Dockerfile.cross

21
PROJECT
View File

@ -1,21 +0,0 @@
domain: jenkins.io
layout: go.kubebuilder.io/v4
projectName: jenkins-operator
repo: github.com/jenkinsci/kubernetes-operator
resources:
- api:
crdVersion: v1
namespaced: true
# TODO(user): Uncomment the below line if this resource implements a controller, else delete it.
# controller: true
domain: jenkins.io
group: jenkins.io
kind: Jenkins
path: github.com/jenkinsci/kubernetes-operator/api/v1alpha2
version: v1alpha2
webhooks:
webhookVersion: v1
version: "3"
plugins:
manifests.sdk.operatorframework.io/v2: {}
scorecard.sdk.operatorframework.io/v2: {}

View File

@ -1,16 +1,11 @@
# Jenkins Operator # Jenkins Operator
[![Version](https://img.shields.io/badge/version-v0.8.0-brightgreen.svg)](https://github.com/jenkinsci/kubernetes-operator/releases/tag/v0.8.0) [![Version](https://img.shields.io/badge/version-v0.5.0-brightgreen.svg)](https://github.com/jenkinsci/kubernetes-operator/releases/tag/v0.5.0)
[![Build status](https://github.com/jenkinsci/kubernetes-operator/actions/workflows/auto-tests-e2e.yaml/badge.svg)](https://github.com/jenkinsci/kubernetes-operator/actions/workflows/auto-tests-e2e.yaml) [![Build Status](https://travis-ci.org/jenkinsci/kubernetes-operator.svg?branch=master)](https://travis-ci.org/jenkinsci/kubernetes-operator)
[![Go Report Card](https://goreportcard.com/badge/github.com/jenkinsci/kubernetes-operator "Go Report Card")](https://goreportcard.com/report/github.com/jenkinsci/kubernetes-operator) [![Go Report Card](https://goreportcard.com/badge/github.com/jenkinsci/kubernetes-operator "Go Report Card")](https://goreportcard.com/report/github.com/jenkinsci/kubernetes-operator)
[![Gitter chat](https://badges.gitter.im/jenkinsci/kubernetes-operator.png)](https://gitter.im/jenkinsci/kubernetes-operator) [![Docker Pulls](https://img.shields.io/docker/pulls/virtuslab/jenkins-operator.svg)](https://hub.docker.com/r/virtuslab/jenkins-operator/tags)
<a href=""> ![logo](/assets/jenkins_gopher_wide.png)
<picture>
<source media="(prefers-color-scheme: dark)" srcset="https://raw.githubusercontent.com/jenkinsci/kubernetes-operator/master/assets/jenkins_gopher_wide_exp_dark.png">
<img src="https://raw.githubusercontent.com/jenkinsci/kubernetes-operator/master/assets/jenkins_gopher_wide_exp.png">
</picture>
</a>
## What's the Jenkins Operator? ## What's the Jenkins Operator?
@ -20,9 +15,9 @@ It has been built with Immutability and declarative Configuration as Code in min
## Preliminaries ## Preliminaries
Considering that this Operator is created for managing instances for Jenkins, Considering that this Operator is created for managing instances for Jenkins,
it is important to understand what it is important to understand what
- [Jenkins Pipelines](https://jenkins.io/doc/book/pipeline/) and - [Jenkins Pipelines](https://jenkins.io/doc/book/pipeline/) and
- CasC ([Configuration as Code](https://github.com/jenkinsci/configuration-as-code-plugin)) are. - CasC ([Configuration as Code](https://github.com/jenkinsci/configuration-as-code-plugin)) are.
Jenkins Pipelines use Scripts written in [Groovy](https://groovy-lang.org/) which aid in the CasC aspect of Jenkins. Jenkins Pipelines use Scripts written in [Groovy](https://groovy-lang.org/) which aid in the CasC aspect of Jenkins.
@ -38,13 +33,14 @@ Jenkins uses [plugins](https://plugins.jenkins.io/) like CasC to extend it's sol
## Problem statement and goals ## Problem statement and goals
The main reason why we decided to implement the **Jenkins Operator** is the fact that we faced a lot of problems with standard Jenkins deployment. The main reason why we decided to implement the **Jenkins Operator** is the fact that we faced a lot of problems with standard Jenkins deployment.
We want to make Jenkins more robust, suitable for dynamic and multi-tenant environments. We want to make Jenkins more robust, suitable for dynamic and multi-tenant environments.
Some of the problems we want to solve: Some of the problems we want to solve:
- [installing plugins with incompatible versions or security vulnerabilities](https://jenkinsci.github.io/kubernetes-operator/docs/getting-started/latest/customizing-jenkins/#install-plugins/) - [installing plugins with incompatible versions or security vulnerabilities](https://jenkinsci.github.io/kubernetes-operator/docs/getting-started/latest/customization/#install-plugins)
- [better configuration as code](https://jenkinsci.github.io/kubernetes-operator/docs/getting-started/latest/customizing-jenkins/) - [better configuration as code](https://jenkinsci.github.io/kubernetes-operator/docs/getting-started/latest/customization/)
- [security and hardening out of the box](https://jenkinsci.github.io/kubernetes-operator/docs/security/) - [security and hardening out of the box](https://jenkinsci.github.io/kubernetes-operator/docs/security/)
- [make errors more visible for end users](https://jenkinsci.github.io/kubernetes-operator/docs/troubleshooting/) - [make errors more visible for end users](https://jenkinsci.github.io/kubernetes-operator/docs/getting-started/latest/diagnostics/)
- [backup and restore for jobs history](https://jenkinsci.github.io/kubernetes-operator/docs/getting-started/latest/configure-backup-and-restore/)
- orphaned jobs with no JNLP connection - orphaned jobs with no JNLP connection
- handle graceful shutdown properly - handle graceful shutdown properly
- proper end to end tests for Jenkins lifecycle - proper end to end tests for Jenkins lifecycle
@ -54,38 +50,27 @@ Some of the problems we want to solve:
Go to [**our documentation website**](https://jenkinsci.github.io/kubernetes-operator/) for more information. Go to [**our documentation website**](https://jenkinsci.github.io/kubernetes-operator/) for more information.
Selected content: Selected content:
1. [How it works](https://jenkinsci.github.io/kubernetes-operator/docs/how-it-works/) 1. [Installation](https://jenkinsci.github.io/kubernetes-operator/docs/installation/)
2. [Getting Started](https://jenkinsci.github.io/kubernetes-operator/docs/getting-started/) 2. [Getting Started](https://jenkinsci.github.io/kubernetes-operator/docs/getting-started/)
3. [Security](https://jenkinsci.github.io/kubernetes-operator/docs/security/) 3. [How it works](https://jenkinsci.github.io/kubernetes-operator/docs/how-it-works/)
4. [Troubleshooting](https://jenkinsci.github.io/kubernetes-operator/docs/troubleshooting/) 4. [Security](https://jenkinsci.github.io/kubernetes-operator/docs/security/)
5. [Developer Guide](https://jenkinsci.github.io/kubernetes-operator/docs/developer-guide/) 5. [Developer Guide](https://jenkinsci.github.io/kubernetes-operator/docs/developer-guide/)
6. [FAQ](https://jenkinsci.github.io/kubernetes-operator/docs/faq/) 5. [Jenkins Custom Resource Definition scheme](https://jenkinsci.github.io/kubernetes-operator/docs/getting-started/latest/scheme/)
7. [Jenkins Custom Resource Definition Schema](https://jenkinsci.github.io/kubernetes-operator/docs/getting-started/latest/schema/)
## Common Issues and Workarounds ## Common Issues and Workarounds
- Multibranch Pipelines and Backup Issues: https://github.com/jenkinsci/kubernetes-operator/issues/104#issuecomment-554289768 - Multibranch Pipelines and Backup Issues: https://github.com/jenkinsci/kubernetes-operator/issues/104#issuecomment-554289768
## Community ## Community
Main channel of communication on topics related to Jenkins Operator is [Jenkins Operator Category](https://community.jenkins.io/c/contributing/jenkins-operator/20) on [Jenkins Community Discourse](https://community.jenkins.io/).
Here you can ask questions about the project, discuss best practices on using it, and talk to other users of the Operator, contributors and project's maintainers. We have a dedicated channel called `#jenkins-operator` on [virtuslab-oss.slack.com](https://virtuslab-oss.slack.com)
Fill out ([Invite form](https://forms.gle/X3X8qA1XMirdBuEH7)) and come say hi !
We also have a [gitter](https://gitter.im/jenkinsci/kubernetes-operator)/[matrix](https://matrix.to/#/#jenkinsci_kubernetes-operator:gitter.im) channel, come to say hi! Every Thursday we have a community call at 16:30 CET on [Google Meet](https://meet.google.com/rsf-nhte-gnq). Feel free to join, ask questions and have fun :)
## Snapshots between releases
We are trying our best to resolve issues quickly, but they have to wait to be released. If you can't wait for an official
docker image release and acknowledge the risk, you can use our unofficial images, which are built nightly.
You can find the project's Quay.io repository [here](https://quay.io/organization/jenkins-kubernetes-operator).
Look for the images with tag "{git-hash}", where {git-hash} is the hash of the master commit that interests you.
## Contribution ## Contribution
Feel free to file [issues](https://github.com/jenkinsci/kubernetes-operator/issues) or [pull requests](https://github.com/jenkinsci/kubernetes-operator/pulls), Feel free to file [issues](https://github.com/jenkinsci/kubernetes-operator/issues) or [pull requests](https://github.com/jenkinsci/kubernetes-operator/pulls).
but please consult [CONTRIBUTING](https://github.com/jenkinsci/kubernetes-operator/blob/master/CONTRIBUTING.md) document beforehand.
Before any big pull request please consult the maintainers to ensure a common direction. Before any big pull request please consult the maintainers to ensure a common direction.
@ -93,7 +78,6 @@ Before any big pull request please consult the maintainers to ensure a common di
- [Jenkins World 2019 Lisbon](assets/Jenkins_World_Lisbon_2019%20-Jenkins_Kubernetes_Operator.pdf) - [Jenkins World 2019 Lisbon](assets/Jenkins_World_Lisbon_2019%20-Jenkins_Kubernetes_Operator.pdf)
- [Jenkins Online Meetup 2020](assets/Jenkins_Online_Meetup-Jenkins_Kubernetes_Operator.pdf) - [Jenkins Online Meetup 2020](assets/Jenkins_Online_Meetup-Jenkins_Kubernetes_Operator.pdf)
- [Jenkins Online Meetup 2021](https://www.youtube.com/watch?v=BsYYVkophsk)
## About the authors ## About the authors

View File

@ -1,31 +0,0 @@
# Jenkins Operator Roadmap
This document outlines the vision and technical roadmap for [jenkinsci/kubernetes-operator](https://github.com/jenkinsci/kubernetes-operator) project.
## Project Vision
With Jenkins Operator project we want to enable our community to run Jenkins in cloud-native environments. Also, support most of the public cloud providers (AWS, Azure, GCP) in terms of additional capabilities like backups, observability and cloud security.
With declarative configuration and full lifecycle management based on [Operator Framework](https://operatorframework.io/) this can become the de facto standard for running Jenkins on top of Kubernetes.
## Technical Roadmap
- Break down Jenkins Custom Resource into smaller parts, support multiple Custom Resource Definitions [#495](https://github.com/jenkinsci/kubernetes-operator/issues/495)
- Introduce more granular schema for configuring Jenkins
- Introduce independent reconciliation controllers
- Refactor e2e tests to support testing individual reconciliation controllers
- Migrate Jenkins instance from Pod to Deployment [#497](https://github.com/jenkinsci/kubernetes-operator/issues/497)
- Unblock easier integration with 3rd party systems e.g. sidecars injection
- Improve contribution process and establish governance model [#496](https://github.com/jenkinsci/kubernetes-operator/issues/496)
- Improve CONTRIBUTING.md
- Introduce architecture decision proposals process
- Introduce governance model
- Engage with community more
- After releasing Operator version with new API, gather feedback from users on where the Operator should go next
- Reference Architecture - Jenkins on Kubernetes
- Bridge the gap between Jenkins and Kubernetes
- https://www.jenkins.io/blog/2020/12/04/gsod-project-report/
## Have a Question?
In case of questions, feel free to create a thread on [Jenkins Operator Category](https://community.jenkins.io/c/contributing/jenkins-operator/20) of Jenkins Community Discourse or contact us directly.

19
SECURITY.md Normal file
View File

@ -0,0 +1,19 @@
# Security Policy
The Jenkins project takes security seriously.
We make every possible effort to ensure users can adequately secure their automation infrastructure.
To that end, we work with Jenkins core and plugin developers, as well as security researchers, to fix security vulnerabilities in Jenkins in a timely manner, and to improve the security of Jenkins in general.
## Reporting Security Vulnerabilities
Please report security vulnerabilities in the Jenkins issue tracker under the [SECURITY project](https://issues.jenkins-ci.org/browse/SECURITY).
This project is configured in such a way that only the reporter and the security team can see the details.
By restricting access to this potentially sensitive information, we can work on a fix and deliver it before the method of attack becomes well-known.
If you are unable to report using our issue tracker, you can also send your report to the private Jenkins security team mailing list: `jenkinsci-cert@googlegroups.com`
The Jenkins security team will then file an issue on your behalf, and will work with the maintainers of the affected component(s) to get the issue resolved.
## Learn More
For further details about our scope, issue handling process, or disclosure process, see [Reporting Security Vulnerabilities on jenkins.io](https://jenkins.io/security/reporting/).

View File

@ -1 +1 @@
v0.9.0-beta1 v0.5.0

View File

@ -1,380 +0,0 @@
/*
Copyright 2021.
Licensed under the Apache License, Version 2.0 (the "License");
you may not use this file except in compliance with the License.
You may obtain a copy of the License at
http://www.apache.org/licenses/LICENSE-2.0
Unless required by applicable law or agreed to in writing, software
distributed under the License is distributed on an "AS IS" BASIS,
WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
See the License for the specific language governing permissions and
limitations under the License.
*/
package v1alpha2
import (
"compress/gzip"
"encoding/json"
"errors"
"io"
"net/http"
"os"
"time"
"github.com/jenkinsci/kubernetes-operator/pkg/log"
"github.com/jenkinsci/kubernetes-operator/pkg/plugins"
"golang.org/x/mod/semver"
"k8s.io/apimachinery/pkg/runtime"
ctrl "sigs.k8s.io/controller-runtime"
logf "sigs.k8s.io/controller-runtime/pkg/log"
"sigs.k8s.io/controller-runtime/pkg/webhook"
"sigs.k8s.io/controller-runtime/pkg/webhook/admission"
)
var (
jenkinslog = logf.Log.WithName("jenkins-resource") // log is for logging in this package.
SecValidator = *NewSecurityValidator()
_ webhook.Validator = &Jenkins{}
initialSecurityWarningsDownloadSucceded = false
)
const (
Hosturl = "https://ci.jenkins.io/job/Infra/job/plugin-site-api/job/generate-data/lastSuccessfulBuild/artifact/plugins.json.gzip"
PluginDataFileCompressedPath = "/tmp/plugins.json.gzip"
PluginDataFile = "/tmp/plugins.json"
shortenedCheckingPeriod = 1 * time.Hour
defaultCheckingPeriod = 12 * time.Minute
)
func (in *Jenkins) SetupWebhookWithManager(mgr ctrl.Manager) error {
return ctrl.NewWebhookManagedBy(mgr).
For(in).
Complete()
}
// TODO(user): change verbs to "verbs=create;update;delete" if you want to enable deletion validation.
// +kubebuilder:webhook:path=/validate-jenkins-io-jenkins-io-v1alpha2-jenkins,mutating=false,failurePolicy=fail,sideEffects=None,groups=jenkins.io.jenkins.io,resources=jenkins,verbs=create;update,versions=v1alpha2,name=vjenkins.kb.io,admissionReviewVersions={v1}
// ValidateCreate implements webhook.Validator so a webhook will be registered for the type
func (in *Jenkins) ValidateCreate() (admission.Warnings, error) {
if in.Spec.ValidateSecurityWarnings {
jenkinslog.Info("validate create", "name", in.Name)
err := Validate(*in)
return nil, err
}
return nil, nil
}
// ValidateUpdate implements webhook.Validator so a webhook will be registered for the type
func (in *Jenkins) ValidateUpdate(old runtime.Object) (admission.Warnings, error) {
if in.Spec.ValidateSecurityWarnings {
jenkinslog.Info("validate update", "name", in.Name)
return nil, Validate(*in)
}
return nil, nil
}
func (in *Jenkins) ValidateDelete() (admission.Warnings, error) {
return nil, nil
}
type SecurityValidator struct {
PluginDataCache PluginsInfo
isCached bool
Attempts int
checkingPeriod time.Duration
}
type PluginsInfo struct {
Plugins []PluginInfo `json:"plugins"`
}
type PluginInfo struct {
Name string `json:"name"`
SecurityWarnings []Warning `json:"securityWarnings"`
}
type Warning struct {
Versions []Version `json:"versions"`
ID string `json:"id"`
Message string `json:"message"`
URL string `json:"url"`
Active bool `json:"active"`
}
type Version struct {
FirstVersion string `json:"firstVersion"`
LastVersion string `json:"lastVersion"`
}
type PluginData struct {
Version string
Kind string
}
// Validates security warnings for both updating and creating a Jenkins CR
func Validate(r Jenkins) error {
if !SecValidator.isCached {
return errors.New("plugins data has not been fetched")
}
pluginSet := make(map[string]PluginData)
var faultyBasePlugins string
var faultyUserPlugins string
basePlugins := plugins.BasePlugins()
for _, plugin := range basePlugins {
// Only Update the map if the plugin is not present or a lower version is being used
if pluginData, ispresent := pluginSet[plugin.Name]; !ispresent || semver.Compare(makeSemanticVersion(plugin.Version), pluginData.Version) == 1 {
pluginSet[plugin.Name] = PluginData{Version: plugin.Version, Kind: "base"}
}
}
for _, plugin := range r.Spec.Master.Plugins {
if pluginData, ispresent := pluginSet[plugin.Name]; !ispresent || semver.Compare(makeSemanticVersion(plugin.Version), pluginData.Version) == 1 {
pluginSet[plugin.Name] = PluginData{Version: plugin.Version, Kind: "user-defined"}
}
}
for _, plugin := range SecValidator.PluginDataCache.Plugins {
if pluginData, ispresent := pluginSet[plugin.Name]; ispresent {
var hasVulnerabilities bool
for _, warning := range plugin.SecurityWarnings {
for _, version := range warning.Versions {
firstVersion := version.FirstVersion
lastVersion := version.LastVersion
if len(firstVersion) == 0 {
firstVersion = "0" // setting default value in case of empty string
}
if len(lastVersion) == 0 {
lastVersion = pluginData.Version // setting default value in case of empty string
}
// checking if this warning applies to our version as well
if compareVersions(firstVersion, lastVersion, pluginData.Version) {
jenkinslog.Info("Security Vulnerability detected in "+pluginData.Kind+" "+plugin.Name+":"+pluginData.Version, "Warning message", warning.Message, "For more details,check security advisory", warning.URL)
hasVulnerabilities = true
}
}
}
if hasVulnerabilities {
if pluginData.Kind == "base" {
faultyBasePlugins += "\n" + plugin.Name + ":" + pluginData.Version
} else {
faultyUserPlugins += "\n" + plugin.Name + ":" + pluginData.Version
}
}
}
}
if len(faultyBasePlugins) > 0 || len(faultyUserPlugins) > 0 {
var errormsg string
if len(faultyBasePlugins) > 0 {
errormsg += "security vulnerabilities detected in the following base plugins: " + faultyBasePlugins
}
if len(faultyUserPlugins) > 0 {
errormsg += "security vulnerabilities detected in the following user-defined plugins: " + faultyUserPlugins
}
return errors.New(errormsg)
}
return nil
}
// NewMonitor creates a new worker and instantiates all the data structures required
func NewSecurityValidator() *SecurityValidator {
return &SecurityValidator{
isCached: false,
Attempts: 0,
checkingPeriod: shortenedCheckingPeriod,
}
}
func (in *SecurityValidator) MonitorSecurityWarnings(securityWarningsFetched chan bool) {
jenkinslog.Info("Security warnings check: enabled\n")
for {
in.checkForSecurityVulnerabilities(securityWarningsFetched)
<-time.After(in.checkingPeriod)
}
}
func (in *SecurityValidator) checkForSecurityVulnerabilities(securityWarningsFetched chan bool) {
err := in.fetchPluginData()
if err != nil {
jenkinslog.Info("Cache plugin data", "failed to fetch plugin data", err)
in.checkingPeriod = shortenedCheckingPeriod
return
}
in.isCached = true
in.checkingPeriod = defaultCheckingPeriod
// should only be executed once when the operator starts
if !initialSecurityWarningsDownloadSucceded {
securityWarningsFetched <- in.isCached
initialSecurityWarningsDownloadSucceded = true
}
}
// Downloads extracts and reads the JSON data in every 12 hours
func (in *SecurityValidator) fetchPluginData() error {
jenkinslog.Info("Initializing/Updating the plugin data cache")
var err error
for in.Attempts = 0; in.Attempts < 5; in.Attempts++ {
err = in.download()
if err != nil {
jenkinslog.V(log.VDebug).Info("Cache Plugin Data", "failed to download file", err)
continue
}
break
}
if err != nil {
return err
}
for in.Attempts = 0; in.Attempts < 5; in.Attempts++ {
err = in.extract()
if err != nil {
jenkinslog.V(log.VDebug).Info("Cache Plugin Data", "failed to extract file", err)
continue
}
break
}
if err != nil {
return err
}
for in.Attempts = 0; in.Attempts < 5; in.Attempts++ {
err = in.cache()
if err != nil {
jenkinslog.V(log.VDebug).Info("Cache Plugin Data", "failed to read plugin data file", err)
continue
}
break
}
return err
}
func (in *SecurityValidator) download() error {
pluginDataFileCompressed, err := os.Create(PluginDataFileCompressedPath)
if err != nil {
return err
}
// ensure pluginDataFileCompressed is closed
defer func() {
if err := pluginDataFileCompressed.Close(); err != nil {
jenkinslog.V(log.VDebug).Info("Failed to close SecurityValidator.download io", "error", err)
}
}()
req, err := http.NewRequest(http.MethodGet, Hosturl, nil)
if err != nil {
return err
}
req.Header.Set("Content-Type", "application/json")
Client := http.Client{
Timeout: 1 * time.Minute,
}
response, err := Client.Do(req)
if err != nil {
return err
}
defer httpResponseCloser(response)
_, err = io.Copy(pluginDataFileCompressed, response.Body)
return err
}
func (in *SecurityValidator) extract() error {
reader, err := os.Open(PluginDataFileCompressedPath)
if err != nil {
return err
}
defer func() {
if err := reader.Close(); err != nil {
log.Log.Error(err, "failed to close SecurityValidator.extract.reader ")
}
}()
archive, err := gzip.NewReader(reader)
if err != nil {
return err
}
defer func() {
if err := archive.Close(); err != nil {
log.Log.Error(err, "failed to close SecurityValidator.extract.archive ")
}
}()
writer, err := os.Create(PluginDataFile)
if err != nil {
return err
}
defer func() {
if err := writer.Close(); err != nil {
log.Log.Error(err, "failed to close SecurityValidator.extract.writer")
}
}()
_, err = io.Copy(writer, archive)
return err
}
// Loads the JSON data into memory and stores it
func (in *SecurityValidator) cache() error {
jsonFile, err := os.Open(PluginDataFile)
if err != nil {
return err
}
defer func() {
if err := jsonFile.Close(); err != nil {
log.Log.Error(err, "failed to close SecurityValidator.cache.jsonFile")
}
}()
byteValue, err := io.ReadAll(jsonFile)
if err != nil {
return err
}
err = json.Unmarshal(byteValue, &in.PluginDataCache)
return err
}
// returns a semantic version that can be used for comparison, allowed versioning format vMAJOR.MINOR.PATCH or MAJOR.MINOR.PATCH
func makeSemanticVersion(version string) string {
if version[0] != 'v' {
version = "v" + version
}
return semver.Canonical(version)
}
// Compare if the current version lies between first version and last version
func compareVersions(firstVersion string, lastVersion string, pluginVersion string) bool {
firstSemVer := makeSemanticVersion(firstVersion)
lastSemVer := makeSemanticVersion(lastVersion)
pluginSemVer := makeSemanticVersion(pluginVersion)
if semver.Compare(pluginSemVer, firstSemVer) == -1 || semver.Compare(pluginSemVer, lastSemVer) == 1 {
return false
}
return true
}
func httpResponseCloser(response *http.Response) {
if err := response.Body.Close(); err != nil {
log.Log.Error(err, "failed to close http response body")
}
}

View File

@ -1,177 +0,0 @@
package v1alpha2
import (
"errors"
"testing"
"github.com/stretchr/testify/assert"
metav1 "k8s.io/apimachinery/pkg/apis/meta/v1"
)
func TestMakeSemanticVersion(t *testing.T) {
t.Run("only major version specified", func(t *testing.T) {
got := makeSemanticVersion("1")
assert.Equal(t, got, "v1.0.0")
})
t.Run("major and minor version specified", func(t *testing.T) {
got := makeSemanticVersion("1.2")
assert.Equal(t, got, "v1.2.0")
})
t.Run("major,minor and patch version specified", func(t *testing.T) {
got := makeSemanticVersion("1.2.3")
assert.Equal(t, got, "v1.2.3")
})
t.Run("semantic versions begin with a leading v and no patch version", func(t *testing.T) {
got := makeSemanticVersion("v2.5")
assert.Equal(t, got, "v2.5.0")
})
t.Run("semantic versions with prerelease versions", func(t *testing.T) {
got := makeSemanticVersion("2.1.2-alpha.1")
assert.Equal(t, got, "v2.1.2-alpha.1")
})
t.Run("semantic versions with prerelease versions", func(t *testing.T) {
got := makeSemanticVersion("0.11.2-9.c8b45b8bb173")
assert.Equal(t, got, "v0.11.2-9.c8b45b8bb173")
})
t.Run("semantic versions with build suffix", func(t *testing.T) {
got := makeSemanticVersion("1.7.9+meta")
assert.Equal(t, got, "v1.7.9")
})
t.Run("invalid semantic version", func(t *testing.T) {
got := makeSemanticVersion("google-login-1.2")
assert.Equal(t, got, "")
})
}
func TestCompareVersions(t *testing.T) {
t.Run("Plugin Version lies between first and last version", func(t *testing.T) {
got := compareVersions("1.2", "1.6", "1.4")
assert.Equal(t, got, true)
})
t.Run("Plugin Version is greater than the last version", func(t *testing.T) {
got := compareVersions("1", "2", "3")
assert.Equal(t, got, false)
})
t.Run("Plugin Version is less than the first version", func(t *testing.T) {
got := compareVersions("1.4", "2.5", "1.1")
assert.Equal(t, got, false)
})
t.Run("Plugins Versions have prerelease version and it lies between first and last version", func(t *testing.T) {
got := compareVersions("1.2.1-alpha", "1.2.1", "1.2.1-beta")
assert.Equal(t, got, true)
})
t.Run("Plugins Versions have prerelease version and it is greater than the last version", func(t *testing.T) {
got := compareVersions("v2.2.1-alpha", "v2.5.1-beta.1", "v2.5.1-beta.2")
assert.Equal(t, got, false)
})
}
func TestValidate(t *testing.T) {
t.Run("Validating when plugins data file is not fetched", func(t *testing.T) {
userplugins := []Plugin{{Name: "script-security", Version: "1.77"}, {Name: "git-client", Version: "3.9"}, {Name: "git", Version: "4.8.1"}, {Name: "plain-credentials", Version: "1.7"}}
jenkinscr := *createJenkinsCR(userplugins, true)
_, got := jenkinscr.ValidateCreate()
assert.Equal(t, got, errors.New("plugins data has not been fetched"))
})
SecValidator.isCached = true
t.Run("Validating a Jenkins CR with plugins not having security warnings and validation is turned on", func(t *testing.T) {
SecValidator.PluginDataCache = PluginsInfo{Plugins: []PluginInfo{
{Name: "security-script"},
{Name: "git-client"},
{Name: "git"},
{Name: "google-login", SecurityWarnings: createSecurityWarnings("", "1.2")},
{Name: "sample-plugin", SecurityWarnings: createSecurityWarnings("", "0.8")},
{Name: "mailer"},
{Name: "plain-credentials"}}}
userplugins := []Plugin{{Name: "script-security", Version: "1.77"}, {Name: "git-client", Version: "3.9"}, {Name: "git", Version: "4.8.1"}, {Name: "plain-credentials", Version: "1.7"}}
jenkinscr := *createJenkinsCR(userplugins, true)
_, got := jenkinscr.ValidateCreate()
assert.Nil(t, got)
})
t.Run("Validating a Jenkins CR with some of the plugins having security warnings and validation is turned on", func(t *testing.T) {
SecValidator.PluginDataCache = PluginsInfo{Plugins: []PluginInfo{
{Name: "security-script", SecurityWarnings: createSecurityWarnings("1.2", "2.2")},
{Name: "workflow-cps", SecurityWarnings: createSecurityWarnings("2.59", "")},
{Name: "git-client"},
{Name: "git"},
{Name: "sample-plugin", SecurityWarnings: createSecurityWarnings("0.8", "")},
{Name: "command-launcher", SecurityWarnings: createSecurityWarnings("1.2", "1.4")},
{Name: "plain-credentials"},
{Name: "google-login", SecurityWarnings: createSecurityWarnings("1.1", "1.3")},
{Name: "mailer", SecurityWarnings: createSecurityWarnings("1.0.3", "1.1.4")},
}}
userplugins := []Plugin{{Name: "google-login", Version: "1.2"}, {Name: "mailer", Version: "1.1"}, {Name: "git", Version: "4.8.1"}, {Name: "command-launcher", Version: "1.6"}, {Name: "workflow-cps", Version: "2.59"}}
jenkinscr := *createJenkinsCR(userplugins, true)
_, got := jenkinscr.ValidateCreate()
assert.Equal(t, got, errors.New("security vulnerabilities detected in the following user-defined plugins: \nworkflow-cps:2.59\ngoogle-login:1.2\nmailer:1.1"))
})
t.Run("Updating a Jenkins CR with some of the plugins having security warnings and validation is turned on", func(t *testing.T) {
SecValidator.PluginDataCache = PluginsInfo{Plugins: []PluginInfo{
{Name: "handy-uri-templates-2-api", SecurityWarnings: createSecurityWarnings("2.1.8-1.0", "2.2.8-1.0")},
{Name: "workflow-cps", SecurityWarnings: createSecurityWarnings("2.59", "")},
{Name: "resource-disposer", SecurityWarnings: createSecurityWarnings("0.7", "1.2")},
{Name: "git"},
{Name: "jjwt-api"},
{Name: "blueocean-github-pipeline", SecurityWarnings: createSecurityWarnings("1.2.0-alpha-2", "1.2.0-beta-5")},
{Name: "command-launcher", SecurityWarnings: createSecurityWarnings("1.2", "1.4")},
{Name: "plain-credentials"},
{Name: "ghprb", SecurityWarnings: createSecurityWarnings("1.1", "1.43")},
{Name: "mailer", SecurityWarnings: createSecurityWarnings("1.0.3", "1.1.4")},
}}
userplugins := []Plugin{{Name: "google-login", Version: "1.2"}, {Name: "mailer", Version: "1.1"}, {Name: "git", Version: "4.8.1"}, {Name: "command-launcher", Version: "1.6"}, {Name: "workflow-cps", Version: "2.59"}}
oldjenkinscr := *createJenkinsCR(userplugins, true)
userplugins = []Plugin{{Name: "handy-uri-templates-2-api", Version: "2.1.8-1.0"}, {Name: "resource-disposer", Version: "0.8"}, {Name: "jjwt-api", Version: "0.11.2-9.c8b45b8bb173"}, {Name: "blueocean-github-pipeline", Version: "1.2.0-beta-3"}, {Name: "ghprb", Version: "1.39"}}
newjenkinscr := *createJenkinsCR(userplugins, true)
_, got := newjenkinscr.ValidateUpdate(&oldjenkinscr)
assert.Equal(t, got, errors.New("security vulnerabilities detected in the following user-defined plugins: \nhandy-uri-templates-2-api:2.1.8-1.0\nresource-disposer:0.8\nblueocean-github-pipeline:1.2.0-beta-3\nghprb:1.39"))
})
t.Run("Validation is turned off", func(t *testing.T) {
userplugins := []Plugin{{Name: "google-login", Version: "1.2"}, {Name: "mailer", Version: "1.1"}, {Name: "git", Version: "4.8.1"}, {Name: "command-launcher", Version: "1.6"}, {Name: "workflow-cps", Version: "2.59"}}
jenkinscr := *createJenkinsCR(userplugins, false)
_, got := jenkinscr.ValidateCreate()
assert.Nil(t, got)
userplugins = []Plugin{{Name: "google-login", Version: "1.2"}, {Name: "mailer", Version: "1.1"}, {Name: "git", Version: "4.8.1"}, {Name: "command-launcher", Version: "1.6"}, {Name: "workflow-cps", Version: "2.59"}}
newjenkinscr := *createJenkinsCR(userplugins, false)
_, got = newjenkinscr.ValidateUpdate(&jenkinscr)
assert.Nil(t, got)
})
}
func createJenkinsCR(userPlugins []Plugin, validateSecurityWarnings bool) *Jenkins {
jenkins := &Jenkins{
TypeMeta: JenkinsTypeMeta(),
ObjectMeta: metav1.ObjectMeta{
Name: "jenkins",
Namespace: "test",
},
Spec: JenkinsSpec{
Master: JenkinsMaster{
Plugins: userPlugins,
DisableCSRFProtection: false,
},
ValidateSecurityWarnings: validateSecurityWarnings,
},
}
return jenkins
}
func createSecurityWarnings(firstVersion string, lastVersion string) []Warning {
return []Warning{{Versions: []Version{{FirstVersion: firstVersion, LastVersion: lastVersion}}, ID: "null", Message: "unit testing", URL: "null", Active: false}}
}

Binary file not shown.

Before

Width:  |  Height:  |  Size: 242 KiB

View File

@ -1,22 +1,11 @@
FROM debian:bookworm-slim FROM debian:buster-slim
LABEL maintainer="Jenkins Kubernetes Operator Community" \
org.opencontainers.image.authors="Jenkins Kubernetes Operator Community" \
org.opencontainers.image.title="backup-pvc" \
org.opencontainers.image.description="Jenkins Operator Backup img via pvc volume" \
org.opencontainers.image.url="quay.io/jenkins-kubernetes-operator/backup-pvc" \
org.opencontainers.image.source="https://github.com/jenkinsci/kubernetes-operator/tree/master/backup/pvc" \
org.opencontainers.image.base.name="debian:bookworm-slim"
ARG UID ARG UID
ARG GID ARG GID
ENV USER=user ENV USER=user
RUN apt update \ RUN addgroup --gid "$GID" "$USER" && \
&& apt install -y procps zstd \
&& rm -rf /var/lib/apt/lists/* \
&& addgroup --gid "$GID" "$USER" && \
adduser \ adduser \
--disabled-password \ --disabled-password \
--gecos "" \ --gecos "" \
@ -24,9 +13,9 @@ RUN apt update \
--uid "$UID" \ --uid "$UID" \
"$USER" "$USER"
COPY bin/*.sh /home/user/bin/
RUN chmod -R a+rx /home/user
WORKDIR /home/user/bin WORKDIR /home/user/bin
COPY bin .
RUN chmod +x *.sh
USER user USER user
CMD ./run.sh CMD ./run.sh

View File

@ -11,11 +11,10 @@ include $(config)
PREFIX?=$(shell pwd) PREFIX?=$(shell pwd)
VERSION := $(shell cat VERSION.txt) VERSION := $(shell cat VERSION.txt)
PROJECT_DIR := $(shell dirname $(abspath $(lastword $(MAKEFILE_LIST))))
GITCOMMIT := $(shell git rev-parse --short HEAD) GITCOMMIT := $(shell git rev-parse --short HEAD)
GITBRANCH := $(shell git rev-parse --abbrev-ref HEAD) GITBRANCH := $(shell git rev-parse --abbrev-ref HEAD)
GITUNTRACKEDCHANGES := $(shell git status --porcelain --untracked-files=no) GITUNTRACKEDCHANGES := $(shell git status --porcelain --untracked-files=no)
GITIGNOREDBUTTRACKEDCHANGES := $(shell git ls-files -i -o --exclude-standard) GITIGNOREDBUTTRACKEDCHANGES := $(shell git ls-files -i --exclude-standard)
ifneq ($(GITUNTRACKEDCHANGES),) ifneq ($(GITUNTRACKEDCHANGES),)
GITCOMMIT := $(GITCOMMIT)-dirty GITCOMMIT := $(GITCOMMIT)-dirty
endif endif
@ -58,7 +57,7 @@ spring-clean: ## Cleanup git ignored files (interactive)
git clean -Xdi git clean -Xdi
.PHONY: checkmake .PHONY: checkmake
HAS_CHECKMAKE := $(shell command -v checkmake) HAS_CHECKMAKE := $(shell which checkmake)
checkmake: ## Check this Makefile checkmake: ## Check this Makefile
@echo "+ $@" @echo "+ $@"
ifndef HAS_CHECKMAKE ifndef HAS_CHECKMAKE
@ -68,12 +67,12 @@ endif
define e2e define e2e
echo "\nRunning $(1) e2e test"; echo "\nRunning $(1) e2e test";
@e2e/$(1)/test.sh quay.io/$(QUAY_ORGANIZATION)/$(QUAY_REGISTRY)-$(NAME):$(GITCOMMIT) @e2e/$(1)/test.sh $(DOCKER_REGISTRY)-$(NAME):$(GITCOMMIT)
endef endef
.PHONY: docker-e2e .PHONY: docker-e2e
E2E_TESTS := $(shell ls e2e) E2E_TESTS := $(shell ls e2e)
docker-e2e: docker-build-e2e ## Make e2e tests for docker image docker-e2e: docker-build ## Make e2e tests for docker image
@echo "+ $@" @echo "+ $@"
$(foreach TEST_NAME,$(E2E_TESTS), $(call e2e,$(TEST_NAME))) $(foreach TEST_NAME,$(E2E_TESTS), $(call e2e,$(TEST_NAME)))
@ -81,15 +80,10 @@ docker-e2e: docker-build-e2e ## Make e2e tests for docker image
docker-login: ## Log in into the Docker repository docker-login: ## Log in into the Docker repository
@echo "+ $@" @echo "+ $@"
.PHONY: docker-build-e2e
docker-build-e2e: UID=1001
docker-build-e2e: GID=1001
docker-build-e2e: docker-build
.PHONY: docker-build .PHONY: docker-build
docker-build: check-env ## Build the container docker-build: check-env ## Build the container
@echo "+ $@" @echo "+ $@"
docker build . --build-arg UID=$(UID) --build-arg GID=$(GID) -t quay.io/$(QUAY_ORGANIZATION)/$(QUAY_REGISTRY)-$(NAME):$(GITCOMMIT) --file Dockerfile docker build . --build-arg UID=$(UID) --build-arg GID=$(GID) -t $(DOCKER_REGISTRY)-$(NAME):$(GITCOMMIT) --file Dockerfile
.PHONY: docker-images .PHONY: docker-images
docker-images: ## List all local containers docker-images: ## List all local containers
@ -99,31 +93,25 @@ docker-images: ## List all local containers
.PHONY: docker-push .PHONY: docker-push
docker-push: docker-build ## Push the container docker-push: docker-build ## Push the container
@echo "+ $@" @echo "+ $@"
docker tag quay.io/$(QUAY_ORGANIZATION)/$(QUAY_REGISTRY)-$(NAME):$(GITCOMMIT) quay.io/$(QUAY_ORGANIZATION)/$(QUAY_REGISTRY)-$(NAME):$(BUILD_TAG) docker tag $(DOCKER_REGISTRY)-$(NAME):$(GITCOMMIT) $(DOCKER_ORGANIZATION)/$(DOCKER_REGISTRY)-$(NAME):$(BUILD_TAG)
docker push quay.io/$(QUAY_ORGANIZATION)/$(QUAY_REGISTRY)-$(NAME):$(BUILD_TAG) docker push $(DOCKER_ORGANIZATION)/$(DOCKER_REGISTRY)-$(NAME):$(BUILD_TAG)
.PHONY: docker-release-version .PHONY: docker-release-version
docker-release-version: docker-build ## Release image with version tag (in addition to build tag) docker-release-version: docker-build ## Release image with version tag (in addition to build tag)
@echo "+ $@" @echo "+ $@"
docker tag quay.io/$(QUAY_ORGANIZATION)/$(QUAY_REGISTRY)-$(NAME):$(GITCOMMIT) quay.io/$(QUAY_ORGANIZATION)/$(QUAY_REGISTRY)-$(NAME):$(VERSION_TAG) docker tag $(DOCKER_REGISTRY)-$(NAME):$(GITCOMMIT) $(DOCKER_ORGANIZATION)/$(DOCKER_REGISTRY)-$(NAME):$(VERSION_TAG)
docker push quay.io/$(QUAY_ORGANIZATION)/$(QUAY_REGISTRY)-$(NAME):$(VERSION_TAG) docker push $(DOCKER_ORGANIZATION)/$(DOCKER_REGISTRY)-$(NAME):$(VERSION_TAG)
.PHONY: docker-release-latest .PHONY: docker-release-latest
docker-release-latest: docker-build ## Release image with latest tags (in addition to build tag) docker-release-latest: docker-build ## Release image with latest tags (in addition to build tag)
@echo "+ $@" @echo "+ $@"
docker tag quay.io/$(QUAY_ORGANIZATION)/$(QUAY_REGISTRY)-$(NAME):$(GITCOMMIT) quay.io/$(QUAY_ORGANIZATION)/$(QUAY_REGISTRY)-$(NAME):$(LATEST_TAG) docker tag $(DOCKER_REGISTRY)-$(NAME):$(GITCOMMIT) $(DOCKER_ORGANIZATION)/$(DOCKER_REGISTRY)-$(NAME):$(LATEST_TAG)
docker push quay.io/$(QUAY_ORGANIZATION)/$(QUAY_REGISTRY)-$(NAME):$(LATEST_TAG) docker push $(DOCKER_ORGANIZATION)/$(DOCKER_REGISTRY)-$(NAME):$(LATEST_TAG)
.PHONY: docker-release .PHONY: docker-release
docker-release: docker-release-version docker-release-latest ## Release image with version and latest tags (in addition to build tag) docker-release: docker-release-version docker-release-latest ## Release image with version and latest tags (in addition to build tag)
@echo "+ $@" @echo "+ $@"
.PHONY: backup-kind-load
backup-kind-load: docker-build ## Build and load backup img in kind with e2e-test tag
@echo "+ $@"
docker tag quay.io/$(QUAY_ORGANIZATION)/$(QUAY_REGISTRY)-$(NAME):$(GITCOMMIT) quay.io/$(QUAY_ORGANIZATION)/$(QUAY_REGISTRY)-$(NAME):e2e-test
kind load docker-image quay.io/$(QUAY_ORGANIZATION)/$(QUAY_REGISTRY)-$(NAME):e2e-test --name $(KIND_CLUSTER_NAME)
# if this session isn't interactive, then we don't want to allocate a # if this session isn't interactive, then we don't want to allocate a
# TTY, which would fail, but if it is interactive, we do want to attach # TTY, which would fail, but if it is interactive, we do want to attach
# so that the user can send e.g. ^C through. # so that the user can send e.g. ^C through.
@ -133,30 +121,20 @@ ifeq ($(INTERACTIVE), 1)
endif endif
.PHONY: docker-run .PHONY: docker-run
docker-run: docker-build docker-run: docker-build ## Run the container in docker, you can use EXTRA_ARGS
@echo "+ $@" @echo "+ $@"
docker run --rm -i $(DOCKER_FLAGS) \ docker run --rm -i $(DOCKER_FLAGS) \
quay.io/$(QUAY_ORGANIZATION)/$(QUAY_REGISTRY)-$(NAME):$(GITCOMMIT) $(ARGS) $(DOCKER_REGISTRY)-$(NAME):$(GITCOMMIT) $(ARGS)
.PHONY: sembump
HAS_SEMBUMP := $(shell which $(PROJECT_DIR)/bin/sembump)
sembump: # Download sembump locally if necessary
@echo "+ $@"
ifndef HAS_SEMBUMP
mkdir -p $(PROJECT_DIR)/bin
wget -O $(PROJECT_DIR)/bin/sembump https://github.com/justintout/sembump/releases/download/v0.1.0/sembump-linux-amd64
chmod +x $(PROJECT_DIR)/bin/sembump
endif
.PHONY: bump-version .PHONY: bump-version
BUMP := patch BUMP := patch
bump-version: sembump ## Bump the version in the version file. Set BUMP to [ patch | major | minor ] bump-version: ## Bump the version in the version file. Set BUMP to [ patch | major | minor ]
@echo "+ $@" @echo "+ $@"
$(eval NEW_VERSION=$(shell $(PROJECT_DIR)/bin/sembump --kind $(BUMP) $(VERSION))) $(eval NEW_VERSION=$(shell sembump --kind $(BUMP) $(VERSION)))
@echo "Bumping VERSION.txt from $(VERSION) to $(NEW_VERSION)" @echo "Bumping VERSION.txt from $(VERSION) to $(NEW_VERSION)"
echo $(NEW_VERSION) > VERSION.txt echo $(NEW_VERSION) > VERSION.txt
git add VERSION.txt git add VERSION.txt
git commit -avm "Bump backup PVC version to $(NEW_VERSION)" git commit -vaem "Bump backup PVC version to $(NEW_VERSION)"
.PHONY: tag .PHONY: tag
tag: ## Create a new git tag to prepare to build a release tag: ## Create a new git tag to prepare to build a release
@ -180,7 +158,7 @@ ifneq ($(GITUNTRACKEDCHANGES),)
endif endif
ifneq ($(GITIGNOREDBUTTRACKEDCHANGES),) ifneq ($(GITIGNOREDBUTTRACKEDCHANGES),)
@echo "Ignored but tracked files:" @echo "Ignored but tracked files:"
@git ls-files -i -c --exclude-standard @git ls-files -i --exclude-standard
@echo @echo
endif endif
@echo "Dependencies:" @echo "Dependencies:"

View File

@ -1 +1 @@
v0.4.3 v0.1.0

68
backup/pvc/bin/backup.sh Executable file → Normal file
View File

@ -1,67 +1,23 @@
#!/usr/bin/env bash #!/usr/bin/env bash
set -eo pipefail set -eo pipefail
source "$(dirname "$0")/utils.sh"
[[ ! $# -eq 1 ]] && _log "ERROR" "Usage: $0 BACKUP_NUMBER" && exit 1 [[ ! $# -eq 1 ]] && echo "Usage: $0 backup_number" && exit 1;
[[ -z "${BACKUP_DIR}" ]] && _log "ERROR" "Required 'BACKUP_DIR' env not set" && exit 1 [[ -z "${BACKUP_DIR}" ]] && echo "Required 'BACKUP_DIR' env not set" && exit 1;
[[ -z "${JENKINS_HOME}" ]] && _log "ERROR" "Required 'JENKINS_HOME' env not set" && exit 1 [[ -z "${JENKINS_HOME}" ]] && echo "Required 'JENKINS_HOME' env not set" && exit 1;
BACKUP_RETRY_COUNT=${BACKUP_RETRY_COUNT:-3} BACKUP_TMP_DIR=$(mktemp -d)
BACKUP_RETRY_INTERVAL=${BACKUP_RETRY_INTERVAL:-60}
BACKUP_NUMBER=$1
TRAP_FILE="/tmp/_backup_${BACKUP_NUMBER}_is_running"
# --> Check if another backup process is running (operator restart/crash) backup_number=$1
for ((i=0; i<BACKUP_RETRY_COUNT; i++)); do echo "Running backup"
[[ ! -f "${TRAP_FILE}" ]] && _log "INFO" "[backup] no other backup process are running" && break
_log "INFO" "[backup] backup is already running. Waiting for ${BACKUP_RETRY_INTERVAL} seconds..."
sleep "${BACKUP_RETRY_INTERVAL}"
done
[[ -f "${TRAP_FILE}" ]] && { _log "ERROR" "[backup] backup is still running after waiting ${BACKUP_RETRY_COUNT} time ${BACKUP_RETRY_INTERVAL}s. Exiting."; exit 1; }
# --< Done
_log "INFO" "[backup] running backup ${BACKUP_NUMBER}" # config.xml in a job directory is a config file that shouldnt be backed up
touch "${TRAP_FILE}" # config.xml in child directores is state that should. For example-
# create temp dir on the same filesystem with a BACKUP_DIR to be able use atomic mv enstead of copy
BACKUP_TMP_DIR=$(mktemp -d --tmpdir="${BACKUP_DIR}")
_clean(){
test -d "${BACKUP_TMP_DIR}" && rm -fr "${BACKUP_TMP_DIR}"
test -f "${TRAP_FILE}" && rm -f "${TRAP_FILE}"
}
_trap(){
_clean
_log "ERROR" "[backup] something wrong happened, check the logs"
}
trap '_trap' SIGQUIT SIGINT SIGTERM
# config.xml in a job directory is a config file that shouldn't be backed up
# config.xml in child directories is state that should. For example-
# branches/myorg/branches/myrepo/branches/master/config.xml should be retained while # branches/myorg/branches/myrepo/branches/master/config.xml should be retained while
# branches/myorg/config.xml should not # branches/myorg/config.xml should not
tar --zstd -C "${JENKINS_HOME}" -cf "${BACKUP_TMP_DIR}/${BACKUP_NUMBER}.tar.zstd" \ tar -C ${JENKINS_HOME} -czf "${BACKUP_TMP_DIR}/${backup_number}.tar.gz" --exclude jobs/*/workspace* --no-wildcards-match-slash --anchored --exclude jobs/*/config.xml -c jobs && \
--exclude jobs/*/workspace* \ mv ${BACKUP_TMP_DIR}/${backup_number}.tar.gz ${BACKUP_DIR}/${backup_number}.tar.gz
--no-wildcards-match-slash --anchored \
--ignore-failed-read \
--exclude jobs/*/config.xml -c jobs || ret=$?
if [[ "$ret" -eq 0 ]]; then [[ ! -s ${BACKUP_DIR}/${backup_number}.tar.gz ]] && echo "backup file '${BACKUP_DIR}/${backup_number}.tar.gz' is empty" && exit 1;
_log "INFO" "[backup] backup ${BACKUP_NUMBER} was completed without warnings"
elif [[ "$ret" -eq 1 ]]; then
_log "INFO" "[backup] backup ${BACKUP_NUMBER} was completed with some warnings"
else
_log "ERROR" "[backup] backup ${BACKUP_NUMBER} failed with error code: $ret"
_clean
exit "$ret"
fi
mv "${BACKUP_TMP_DIR}/${BACKUP_NUMBER}.tar.zstd" "${BACKUP_DIR}/${BACKUP_NUMBER}.tar.zstd" echo Done
_log "INFO" "[backup] cleaning ${BACKUP_TMP_DIR} and trap file ${TRAP_FILE}"
_clean
[[ ! -s ${BACKUP_DIR}/${BACKUP_NUMBER}.tar.zstd ]] && _log "ERROR" "[backup] file '${BACKUP_DIR}/${BACKUP_NUMBER}.tar.zstd' is empty" && exit 1
_log "INFO" "[backup] ${BACKUP_NUMBER} done"
exit 0 exit 0

31
backup/pvc/bin/get-latest.sh Executable file → Normal file
View File

@ -1,39 +1,12 @@
#!/usr/bin/env bash #!/usr/bin/env bash
set -eo pipefail set -eo pipefail
source "$(dirname "$0")/utils.sh"
is_backup_not_exist() { [[ -z "${BACKUP_DIR}" ]] && echo "Required 'BACKUP_DIR' env not set" && exit 1
local backup_dir="$1"
# Save the current value of 'set -e'
local previous_e
previous_e=$(set +e; :; echo $?)
# Temporarily turn off 'set -e'
set +e
# Run ls command to check if any files matching the pattern exist
ls "${backup_dir}"/*.tar.* 1> /dev/null 2>&1
# Store the exit status of the ls command
local ls_exit_status=$?
# Restore the previous value of 'set -e'
[ "$previous_e" = "0" ] && set -e
# Return true if ls command succeeded (no files found), otherwise return false
[ $ls_exit_status -ne 0 ]
}
[[ -z "${BACKUP_DIR}" ]] && { _log "ERROR" "Required 'BACKUP_DIR' env not set"; exit 1; } latest=$(find ${BACKUP_DIR} -name '*.tar.gz' -exec basename {} \; | sort -g | tail -n 1)
# Check if we have any backup
if is_backup_not_exist "${BACKUP_DIR}"; then
_log "No backups exist in ${BACKUP_DIR}"
echo "-1"
exit 0
fi
# Search for all the tar.* inside the backup dir to support the migration between gzip vs zstd
latest=$(find "${BACKUP_DIR}"/*.tar.* -maxdepth 0 -exec basename {} \; | sort -g | tail -n 1)
if [[ "${latest}" == "" ]]; then if [[ "${latest}" == "" ]]; then
_log "Could not get the latest backup."
echo "-1" echo "-1"
else else
echo "${latest%%.*}" echo "${latest%%.*}"

58
backup/pvc/bin/restore.sh Executable file → Normal file
View File

@ -1,57 +1,15 @@
#!/usr/bin/env bash #!/usr/bin/env bash
set -eo pipefail set -eo pipefail
source "$(dirname "$0")/utils.sh"
[[ ! $# -eq 1 ]] && _log "ERROR" "Usage: $0 <backup number>" && exit 1 [[ ! $# -eq 1 ]] && echo "Usage: $0 backup_number" && exit 1
[[ -z "${BACKUP_DIR}" ]] && _log "ERROR" "Required 'BACKUP_DIR' env not set" && exit 1 [[ -z "${BACKUP_DIR}" ]] && echo "Required 'BACKUP_DIR' env not set" && exit 1;
[[ -z "${JENKINS_HOME}" ]] && _log "ERROR" "Required 'JENKINS_HOME' env not set" && exit 1 [[ -z "${JENKINS_HOME}" ]] && echo "Required 'JENKINS_HOME' env not set" && exit 1;
BACKUP_NUMBER=$1
RESTORE_RETRY_COUNT=${RESTORE_RETRY_COUNT:-10}
RESTORE_RETRY_INTERVAL=${RESTORE_RETRY_INTERVAL:-10}
# --> Check if another restore process is running (operator restart/crash) backup_number=$1
TRAP_FILE="/tmp/_restore_${BACKUP_NUMBER}_is_running" echo "Running restore backup"
trap "rm -f ${TRAP_FILE}" SIGINT SIGTERM
for ((i=0; i<RESTORE_RETRY_COUNT; i++)); do tar -C ${JENKINS_HOME} -zxf "${BACKUP_DIR}/${backup_number}.tar.gz"
[[ ! -f "${TRAP_FILE}" ]] && _log "INFO" "[restore] no other process are running, restoring" && break
_log "INFO" "[restore] is already running. Waiting for ${RESTORE_RETRY_INTERVAL} seconds..."
sleep "${RESTORE_RETRY_INTERVAL}"
done
[[ -f "${TRAP_FILE}" ]] && { _log "ERROR" "[restore] is still running after waiting ${RESTORE_RETRY_COUNT} time ${RESTORE_RETRY_INTERVAL}s. Exiting."; exit 1; }
# --< Done
_log "INFO" "[restore] restore backup with backup number #${BACKUP_NUMBER}" echo Done
touch "${TRAP_FILE}" exit 0
BACKUP_FILE="${BACKUP_DIR}/${BACKUP_NUMBER}"
if [[ -f "$BACKUP_FILE.tar.gz" ]]; then
_log "INFO" "[restore] old format tar.gz found, restoring it"
OPTS=""
EXT="tar.gz"
elif [[ -f "$BACKUP_FILE.tar.zstd" ]]; then
_log "INFO" "[restore] Backup file found, proceeding"
OPTS="--zstd"
EXT="tar.zstd"
else
_log "ERROR" "[restore] backup file not found: $BACKUP_FILE"
exit 1
fi
tar $OPTS -C "${JENKINS_HOME}" -xf "${BACKUP_DIR}/${BACKUP_NUMBER}.${EXT}" || ret=$?
if [[ "$ret" -eq 0 ]]; then
_log "INFO" "[restore] restore ${BACKUP_NUMBER} was completed without warnings"
elif [[ "$ret" -eq 1 ]]; then
_log "INFO" "[restore] restore ${BACKUP_NUMBER} was completed with some warnings"
else
_log "ERROR" "[restore] restore ${BACKUP_NUMBER} failed with error code: $ret"
exit "$ret"
fi
_log "INFO" "[restore] deleting lock file ${TRAP_FILE}"
test -f "${TRAP_FILE}" && rm -f "${TRAP_FILE}"
_log "INFO" "[restore] restoring ${BACKUP_NUMBER} Done"
exit 0

69
backup/pvc/bin/run.sh Executable file → Normal file
View File

@ -1,72 +1,15 @@
#!/usr/bin/env bash #!/usr/bin/env bash
set -eo pipefail set -eo pipefail
source "$(dirname "$0")/utils.sh"
# Use 60 as default in case BACKUP_CLEANUP_INTERVAL did not set [[ -z "${BACKUP_DIR}" ]] && echo "Required 'BACKUP_DIR' env not set" && exit 1;
BACKUP_CLEANUP_INTERVAL=${BACKUP_CLEANUP_INTERVAL:=60} [[ -z "${JENKINS_HOME}" ]] && echo "Required 'JENKINS_HOME' env not set" && exit 1;
# Ensure required environment variables are set
check_env_var() {
if [[ -z "${!1}" ]]; then
_log "ERROR" "Required '$1' environment variable is not set"
exit 1
fi
}
is_backup_not_exist() {
local backup_dir="$1"
# Save the current value of 'set -e'
local previous_e
previous_e=$(set +e; :; echo $?)
# Temporarily turn off 'set -e'
set +e
# Run ls command to check if any files matching the pattern exist
ls "${backup_dir}"/*.tar.* 1> /dev/null 2>&1
# Store the exit status of the ls command
local ls_exit_status=$?
# Restore the previous value of 'set -e'
[ "$previous_e" = "0" ] && set -e
# Return true if ls command succeeded (no files found), otherwise return false
[ $ls_exit_status -ne 0 ]
}
# Function to find exceeding backups
find_exceeding_backups() {
local backup_dir="$1"
local backup_count="$2"
# Check if we have any backup
if is_backup_not_exist "${backup_dir}"; then
_log "ERROR" "[run] backups not found in ${backup_dir}"
return
fi
find "${backup_dir}"/*.tar.zstd -maxdepth 0 -exec basename {} \; | sort -gr | tail -n +$((backup_count +1))
}
check_env_var "BACKUP_DIR"
check_env_var "JENKINS_HOME"
if [[ -z "${BACKUP_COUNT}" ]]; then
_log "WARNING" "[run] no BACKUP_COUNT set, it means you MUST delete old backups manually or by custom script"
else
_log "INFO" "[run] retaining only the ${BACKUP_COUNT} most recent backups, cleanup occurs every ${BACKUP_CLEANUP_INTERVAL} seconds"
fi
while true; while true;
do do
sleep "$BACKUP_CLEANUP_INTERVAL" sleep 10
if [[ -n "${BACKUP_COUNT}" ]]; then if [[ ! -z "${BACKUP_COUNT}" ]]; then
exceeding_backups=$(find_exceeding_backups "${BACKUP_DIR}" "${BACKUP_COUNT}") echo "Trimming to only ${BACKUP_COUNT} recent backups in preparation for new backup"
if [[ -n "$exceeding_backups" ]]; then find ${BACKUP_DIR} -name '*.tar.gz' -exec basename {} \; | sort -gr | tail -n +$((BACKUP_COUNT +1)) | xargs -I '{}' rm ${BACKUP_DIR}/'{}'
_log "INFO" "[run] removing backups: $(echo "$exceeding_backups" | tr '\n' ', ' | sed 's/,$//')"
echo "$exceeding_backups" | while read -r file; do
rm "${BACKUP_DIR}/${file}"
done
fi
fi fi
done done

View File

@ -1,14 +0,0 @@
#!/usr/bin/env bash
# Common utils
_log() {
local level="$1"
local message="$2"
local timestamp=$(date '+%Y-%m-%d %H:%M:%S')
if [[ "$level" =~ ^(ERROR|ERR|error|err)$ ]]; then
echo "${timestamp} - ${level} - ${message}" > /proc/1/fd/2
else
echo "${timestamp} - ${level} - ${message}" > /proc/1/fd/1
echo "${timestamp} - ${level} - ${message}" >&2
fi
}

View File

@ -1,7 +1,6 @@
# Setup variables for the Makefile # Setup variables for the Makefile
NAME=pvc NAME=pvc
QUAY_ORGANIZATION=jenkins-kubernetes-operator DOCKER_ORGANIZATION=virtuslab
QUAY_REGISTRY=backup DOCKER_REGISTRY=jenkins-operator-backup
UID=1000 UID=1000
GID=1000 GID=1000
KIND_CLUSTER_NAME=jenkins

View File

@ -30,7 +30,7 @@ trap "docker rm -vf $cid > /dev/null;rm -rf ${BACKUP_DIR};rm -rf ${RESTORE_FOLDE
backup_number=1 backup_number=1
docker exec ${cid} /home/user/bin/backup.sh ${backup_number} docker exec ${cid} /home/user/bin/backup.sh ${backup_number}
backup_file="${BACKUP_DIR}/${backup_number}.tar.zstd" backup_file="${BACKUP_DIR}/${backup_number}.tar.gz"
[[ ! -f ${backup_file} ]] && echo "Backup file ${backup_file} not found" && exit 1; [[ ! -f ${backup_file} ]] && echo "Backup file ${backup_file} not found" && exit 1;
docker exec ${cid} /bin/bash -c "JENKINS_HOME=${RESTORE_FOLDER};/home/user/bin/restore.sh ${backup_number}" docker exec ${cid} /bin/bash -c "JENKINS_HOME=${RESTORE_FOLDER};/home/user/bin/restore.sh ${backup_number}"
@ -38,4 +38,4 @@ docker exec ${cid} /bin/bash -c "JENKINS_HOME=${RESTORE_FOLDER};/home/user/bin/r
echo "Compare directories" echo "Compare directories"
diff --brief --recursive "${RESTORE_FOLDER}" "${JENKINS_HOME_AFTER_RESTORE}" diff --brief --recursive "${RESTORE_FOLDER}" "${JENKINS_HOME_AFTER_RESTORE}"
echo "Directories are the same" echo "Directories are the same"
echo PASS echo PASS

View File

@ -19,20 +19,17 @@ mkdir -p ${BACKUP_DIR}
mkdir -p ${JENKINS_HOME} mkdir -p ${JENKINS_HOME}
mkdir -p ${BACKUP_DIR}/lost+found mkdir -p ${BACKUP_DIR}/lost+found
touch ${BACKUP_DIR}/1.tar.zstd touch ${BACKUP_DIR}/1.tar.gz
touch ${BACKUP_DIR}/2.tar.zstd touch ${BACKUP_DIR}/2.tar.gz
touch ${BACKUP_DIR}/3.tar.zstd touch ${BACKUP_DIR}/3.tar.gz
touch ${BACKUP_DIR}/4.tar.zstd touch ${BACKUP_DIR}/4.tar.gz
touch ${BACKUP_DIR}/5.tar.zstd touch ${BACKUP_DIR}/5.tar.gz
touch ${BACKUP_DIR}/6.tar.zstd touch ${BACKUP_DIR}/6.tar.gz
touch ${BACKUP_DIR}/7.tar.zstd touch ${BACKUP_DIR}/7.tar.gz
touch ${BACKUP_DIR}/8.tar.zstd touch ${BACKUP_DIR}/8.tar.gz
touch ${BACKUP_DIR}/9.tar.zstd touch ${BACKUP_DIR}/9.tar.gz
touch ${BACKUP_DIR}/10.tar.zstd touch ${BACKUP_DIR}/10.tar.gz
touch ${BACKUP_DIR}/11.tar.zstd touch ${BACKUP_DIR}/11.tar.gz
# Emulate backup creation
BACKUP_TMP_DIR=$(mktemp -d --tmpdir="${BACKUP_DIR}")
touch ${BACKUP_TMP_DIR}/12.tar.zstd
# Create an instance of the container under testing # Create an instance of the container under testing
cid="$(docker run -e JENKINS_HOME=${JENKINS_HOME} -v ${JENKINS_HOME}:${JENKINS_HOME}:ro -e BACKUP_DIR=${BACKUP_DIR} -v ${BACKUP_DIR}:${BACKUP_DIR}:rw -d ${docker_image})" cid="$(docker run -e JENKINS_HOME=${JENKINS_HOME} -v ${JENKINS_HOME}:${JENKINS_HOME}:ro -e BACKUP_DIR=${BACKUP_DIR} -v ${BACKUP_DIR}:${BACKUP_DIR}:rw -d ${docker_image})"
@ -41,20 +38,10 @@ echo "Docker container ID '${cid}'"
# Remove test directory and container afterwards # Remove test directory and container afterwards
trap "docker rm -vf $cid > /dev/null;rm -rf ${BACKUP_DIR};rm -rf ${JENKINS_HOME}" EXIT trap "docker rm -vf $cid > /dev/null;rm -rf ${BACKUP_DIR};rm -rf ${JENKINS_HOME}" EXIT
echo "Try to get latest against 11 backups and one in progress"
latest=$(docker exec ${cid} /bin/bash -c "JENKINS_HOME=${RESTORE_FOLDER};/home/user/bin/get-latest.sh") latest=$(docker exec ${cid} /bin/bash -c "JENKINS_HOME=${RESTORE_FOLDER};/home/user/bin/get-latest.sh")
rm ${BACKUP_DIR}/*.tar.gz
rm ${BACKUP_DIR}/*.tar.zstd
echo "Try to get latest against one in progress"
empty_latest=$(docker exec ${cid} /bin/bash -c "JENKINS_HOME=${RESTORE_FOLDER};/home/user/bin/get-latest.sh") empty_latest=$(docker exec ${cid} /bin/bash -c "JENKINS_HOME=${RESTORE_FOLDER};/home/user/bin/get-latest.sh")
rmdir ${BACKUP_DIR}/lost+found
rm ${BACKUP_TMP_DIR}/*.tar.zstd
rmdir ${BACKUP_TMP_DIR}
echo "Try to get latest against empty dir"
empty_dir_latest=$(docker exec ${cid} /bin/bash -c "JENKINS_HOME=${RESTORE_FOLDER};/home/user/bin/get-latest.sh")
if [[ "${DEBUG}" ]]; then if [[ "${DEBUG}" ]]; then
docker logs ${cid} docker logs ${cid}
ls -la ${BACKUP_DIR} ls -la ${BACKUP_DIR}
@ -68,9 +55,5 @@ if [[ ! "${empty_latest}" == "-1" ]]; then
echo "Latest backup number should be '-1' but is '${empty_latest}'" echo "Latest backup number should be '-1' but is '${empty_latest}'"
exit 1 exit 1
fi fi
if [[ ! "${empty_dir_latest}" == "-1" ]]; then
echo "Latest backup number should be '-1' but is '${empty_dir_latest}'"
exit 1
fi
echo PASS echo PASS

View File

@ -1,8 +1,6 @@
#!/bin/bash #!/bin/bash
set -eo pipefail set -eo pipefail
echo "Running limit_backup_count e2e test..."
[[ "${DEBUG}" ]] && set -x [[ "${DEBUG}" ]] && set -x
# set current working directory to the directory of the script # set current working directory to the directory of the script
@ -21,31 +19,28 @@ mkdir -p ${BACKUP_DIR}
mkdir -p ${JENKINS_HOME} mkdir -p ${JENKINS_HOME}
mkdir -p ${BACKUP_DIR}/lost+found mkdir -p ${BACKUP_DIR}/lost+found
touch ${BACKUP_DIR}/1.tar.zstd touch ${BACKUP_DIR}/1.tar.gz
touch ${BACKUP_DIR}/2.tar.zstd touch ${BACKUP_DIR}/2.tar.gz
touch ${BACKUP_DIR}/3.tar.zstd touch ${BACKUP_DIR}/3.tar.gz
touch ${BACKUP_DIR}/4.tar.zstd touch ${BACKUP_DIR}/4.tar.gz
touch ${BACKUP_DIR}/5.tar.zstd touch ${BACKUP_DIR}/5.tar.gz
touch ${BACKUP_DIR}/6.tar.zstd touch ${BACKUP_DIR}/6.tar.gz
touch ${BACKUP_DIR}/7.tar.zstd touch ${BACKUP_DIR}/7.tar.gz
touch ${BACKUP_DIR}/8.tar.zstd touch ${BACKUP_DIR}/8.tar.gz
touch ${BACKUP_DIR}/9.tar.zstd touch ${BACKUP_DIR}/9.tar.gz
touch ${BACKUP_DIR}/10.tar.zstd touch ${BACKUP_DIR}/10.tar.gz
touch ${BACKUP_DIR}/11.tar.zstd touch ${BACKUP_DIR}/11.tar.gz
# Emulate backup creation
BACKUP_TMP_DIR=$(mktemp -d --tmpdir="${BACKUP_DIR}")
touch ${BACKUP_TMP_DIR}/12.tar.zstd
# Create an instance of the container under testing # Create an instance of the container under testing
cid="$(docker run -e BACKUP_CLEANUP_INTERVAL=1 -e BACKUP_COUNT=2 -e JENKINS_HOME=${JENKINS_HOME} -v ${JENKINS_HOME}:${JENKINS_HOME}:ro -e BACKUP_DIR=${BACKUP_DIR} -v ${BACKUP_DIR}:${BACKUP_DIR}:rw -d ${docker_image})" cid="$(docker run -e BACKUP_COUNT=2 -e JENKINS_HOME=${JENKINS_HOME} -v ${JENKINS_HOME}:${JENKINS_HOME}:ro -e BACKUP_DIR=${BACKUP_DIR} -v ${BACKUP_DIR}:${BACKUP_DIR}:rw -d ${docker_image})"
echo "Docker container ID '${cid}'" echo "Docker container ID '${cid}'"
# Remove test directory and container afterwards # Remove test directory and container afterwards
trap "docker rm -vf $cid > /dev/null;rm -rf ${BACKUP_DIR};rm -rf ${JENKINS_HOME}" EXIT trap "docker rm -vf $cid > /dev/null;rm -rf ${BACKUP_DIR};rm -rf ${JENKINS_HOME}" EXIT
sleep 2 sleep 11
mv ${BACKUP_TMP_DIR}/12.tar.zstd ${BACKUP_DIR}/ touch ${BACKUP_DIR}/12.tar.gz
sleep 2 sleep 11
if [[ "${DEBUG}" ]]; then if [[ "${DEBUG}" ]]; then
docker logs ${cid} docker logs ${cid}
@ -53,7 +48,7 @@ if [[ "${DEBUG}" ]]; then
fi fi
# only two latest backup should exists # only two latest backup should exists
[[ $(ls -1 ${BACKUP_DIR} | grep 'tar.zstd' | wc -l) -eq 2 ]] || exit 1 [[ $(ls -1 ${BACKUP_DIR} | grep 'tar.gz' | wc -l) -eq 2 ]] || exit 1
[[ -f ${BACKUP_DIR}/11.tar.zstd ]] || exit 2 [[ -f ${BACKUP_DIR}/11.tar.gz ]] || exit 2
[[ -f ${BACKUP_DIR}/12.tar.zstd ]] || exit 3 [[ -f ${BACKUP_DIR}/12.tar.gz ]] || exit 3
echo PASS echo PASS

View File

@ -1,8 +1,6 @@
#!/bin/bash #!/bin/bash
set -eo pipefail set -eo pipefail
echo "Running limit_backup_count_no_backups e2e test..."
[[ "${DEBUG}" ]] && set -x [[ "${DEBUG}" ]] && set -x
# set current working directory to the directory of the script # set current working directory to the directory of the script
@ -21,7 +19,7 @@ mkdir -p ${BACKUP_DIR}
mkdir -p ${JENKINS_HOME} mkdir -p ${JENKINS_HOME}
# Create an instance of the container under testing # Create an instance of the container under testing
cid="$(docker run -e BACKUP_CLEANUP_INTERVAL=1 -e BACKUP_COUNT=2 -e JENKINS_HOME=${JENKINS_HOME} -v ${JENKINS_HOME}:${JENKINS_HOME}:ro -e BACKUP_DIR=${BACKUP_DIR} -v ${BACKUP_DIR}:${BACKUP_DIR}:rw -d ${docker_image})" cid="$(docker run -e BACKUP_COUNT=2 -e JENKINS_HOME=${JENKINS_HOME} -v ${JENKINS_HOME}:${JENKINS_HOME}:ro -e BACKUP_DIR=${BACKUP_DIR} -v ${BACKUP_DIR}:${BACKUP_DIR}:rw -d ${docker_image})"
echo "Docker container ID '${cid}'" echo "Docker container ID '${cid}'"
# Remove test directory and container afterwards # Remove test directory and container afterwards
@ -29,16 +27,8 @@ trap "docker rm -vf $cid > /dev/null;rm -rf ${BACKUP_DIR};rm -rf ${JENKINS_HOME}
# container should be running # container should be running
echo 'Checking if container is running' echo 'Checking if container is running'
sleep 3 sleep 11
set +e
docker exec ${cid} echo docker exec ${cid} echo
exit_code=$?
set -e
if [ $exit_code -ne 0 ]; then
echo "container terminated with following logs:"
docker logs "${cid}"
exit 1
fi
echo 'Container is running' echo 'Container is running'
echo PASS echo PASS

View File

@ -1,40 +0,0 @@
#!/bin/bash
set -eo pipefail
echo "Running tmp_dir_clean_after_backup_creation e2e test..."
[[ "${DEBUG}" ]] && set -x
# set current working directory to the directory of the script
cd "$(dirname "$0")"
docker_image=$1
if ! docker inspect ${docker_image} &> /dev/null; then
echo "Image '${docker_image}' does not exists"
false
fi
JENKINS_HOME="$(pwd)/jenkins_home"
BACKUP_DIR="$(pwd)/backup"
mkdir -p ${BACKUP_DIR}
# Create an instance of the container under testing
cid="$(docker run -e BACKUP_CLEANUP_INTERVAL=1 -e JENKINS_HOME=${JENKINS_HOME} -v ${JENKINS_HOME}:${JENKINS_HOME}:ro -e BACKUP_DIR=${BACKUP_DIR} -v ${BACKUP_DIR}:${BACKUP_DIR}:rw -d ${docker_image})"
echo "Docker container ID '${cid}'"
# Remove test directory and container afterwards
trap "docker rm -vf $cid > /dev/null;rm -rf ${BACKUP_DIR}" EXIT
backup_number=1
docker exec ${cid} /home/user/bin/backup.sh ${backup_number}
[ "$(docker exec ${cid} ls /tmp | grep 'tmp')" ] && echo "tmp directory not empty" && exit 1;
# We should also check backup directory, since after #1000 we create temp directory at backup filesystem
[ "$(docker exec ${cid} ls ${BACKUP_DIR} | grep 'tmp')" ] && echo "backup dir consists temp directory" && exit 1;
backup_file="${BACKUP_DIR}/${backup_number}.tar.zstd"
[[ ! -f ${backup_file} ]] && echo "Backup file ${backup_file} not found" && exit 1;
echo "tmp directory empty, backup in backup directory present"
echo PASS

21
build/Dockerfile Normal file
View File

@ -0,0 +1,21 @@
ARG GO_VERSION
# build stage
FROM golang:$GO_VERSION-alpine3.11 AS build-stage
ARG OPERATOR_SDK_VERSION
ENV GO111MODULE=on
RUN apk --no-cache add git curl make \
&& curl -L https://github.com/operator-framework/operator-sdk/releases/download/v$OPERATOR_SDK_VERSION/operator-sdk-v$OPERATOR_SDK_VERSION-x86_64-linux-gnu -o /usr/local/bin/operator-sdk \
&& chmod +x /usr/local/bin/operator-sdk
ADD . /kubernetes-operator
RUN cd /kubernetes-operator && make build
# run stage
FROM alpine:3.10
USER nobody
COPY --from=build-stage /kubernetes-operator/build/_output/bin/jenkins-operator /usr/local/bin/jenkins-operator
CMD [ "/usr/local/bin/jenkins-operator" ]

View File

@ -1,214 +1,6 @@
apiVersion: v1 apiVersion: v1
entries: entries:
jenkins-operator: jenkins-operator:
- apiVersion: v2
appVersion: 0.9.0-beta1
created: "2025-04-06T21:25:18.36969916Z"
dependencies:
- condition: cert-manager.enabled
name: cert-manager
repository: https://charts.jetstack.io
version: 1.14.2
- condition: cert-manager.enabled
name: cert-manager-crds
repository: ""
version: 1.14.2
description: Kubernetes native operator which fully manages Jenkins on Kubernetes
digest: 78b0756202efbf6a05d5016a4358053a07c89c0f4a3f3f1fb447eaf7323df078
icon: https://raw.githubusercontent.com/jenkinsci/kubernetes-operator/master/assets/jenkins-operator-icon.png
name: jenkins-operator
urls:
- https://raw.githubusercontent.com/jenkinsci/kubernetes-operator/master/chart/jenkins-operator/jenkins-operator-0.9.0-beta1.tgz
version: 0.9.0-beta1
- apiVersion: v2
appVersion: 0.8.1
created: "2024-07-05T15:26:01.708923805Z"
dependencies:
- condition: cert-manager.enabled
name: cert-manager
repository: https://charts.jetstack.io
version: 1.14.2
- condition: cert-manager.enabled
name: cert-manager-crds
repository: ""
version: 1.14.2
description: Kubernetes native operator which fully manages Jenkins on Kubernetes
digest: 82c061ff42bbf2dbcf942939ffbd5b63508c9d8490fc06672a474af1cad14a5d
icon: https://raw.githubusercontent.com/jenkinsci/kubernetes-operator/master/assets/jenkins-operator-icon.png
name: jenkins-operator
urls:
- https://raw.githubusercontent.com/jenkinsci/kubernetes-operator/master/chart/jenkins-operator/jenkins-operator-0.8.1.tgz
version: 0.8.1
- apiVersion: v2
appVersion: 0.8.0
created: "2023-09-13T06:54:41.369295961Z"
dependencies:
- condition: webhook.enabled
name: cert-manager
repository: https://charts.jetstack.io
version: 1.5.1
description: Kubernetes native operator which fully manages Jenkins on Kubernetes
digest: da8ae04166cb1b64a9dd3d741c6a50d63846ebe8e2e92f09313ad3c6a0dd9ca4
icon: https://raw.githubusercontent.com/jenkinsci/kubernetes-operator/master/assets/jenkins-operator-icon.png
name: jenkins-operator
urls:
- https://raw.githubusercontent.com/jenkinsci/kubernetes-operator/master/chart/jenkins-operator/jenkins-operator-0.8.0.tgz
version: 0.8.0
- apiVersion: v2
appVersion: 0.8.0-beta.2
created: "2023-06-30T21:22:53.308590035Z"
dependencies:
- condition: webhook.enabled
name: cert-manager
repository: https://charts.jetstack.io
version: 1.5.1
description: Kubernetes native operator which fully manages Jenkins on Kubernetes
digest: b2502f91dffa1136190a8a98d73ac997c70387e100d79200b7403039ca98411e
icon: https://raw.githubusercontent.com/jenkinsci/kubernetes-operator/master/assets/jenkins-operator-icon.png
name: jenkins-operator
urls:
- https://raw.githubusercontent.com/jenkinsci/kubernetes-operator/master/chart/jenkins-operator/jenkins-operator-0.8.0-beta.2.tgz
version: 0.8.0-beta.2
- apiVersion: v2
appVersion: v0.8.0-beta
created: "2023-04-17T22:11:04.706959723Z"
dependencies:
- condition: webhook.enabled
name: cert-manager
repository: https://charts.jetstack.io
version: 1.5.1
description: Kubernetes native operator which fully manages Jenkins on Kubernetes
digest: 4855dc0c673a1b3b4cd7ee502029c3d995f243a9a7051ad03d29def7c48a7c11
icon: https://raw.githubusercontent.com/jenkinsci/kubernetes-operator/master/assets/jenkins-operator-icon.png
name: jenkins-operator
urls:
- https://raw.githubusercontent.com/jenkinsci/kubernetes-operator/master/chart/jenkins-operator/jenkins-operator-v0.8.0-beta.tgz
version: v0.8.0-beta
- apiVersion: v2
appVersion: 0.7.1
created: "2022-06-22T15:47:45.014723169Z"
dependencies:
- condition: webhook.enabled
name: cert-manager
repository: https://charts.jetstack.io
version: 1.5.1
description: Kubernetes native operator which fully manages Jenkins on Kubernetes
digest: e4f72051328b0feae90f5445c58a8776941ffb900a5849de14780ec0e2ce6081
icon: https://raw.githubusercontent.com/jenkinsci/kubernetes-operator/master/assets/jenkins-operator-icon.png
name: jenkins-operator
urls:
- https://raw.githubusercontent.com/jenkinsci/kubernetes-operator/master/chart/jenkins-operator/jenkins-operator-0.6.2.tgz
version: 0.6.2
- apiVersion: v2
appVersion: 0.7.1
created: "2022-06-22T11:26:06.518430865Z"
dependencies:
- condition: webhook.enabled
name: cert-manager
repository: https://charts.jetstack.io
version: 1.5.1
description: Kubernetes native operator which fully manages Jenkins on Kubernetes
digest: a52262394e66a1274cc9508f5013c4da13182317f12bf949a7c28e8916be3fb8
icon: https://raw.githubusercontent.com/jenkinsci/kubernetes-operator/master/assets/jenkins-operator-icon.png
name: jenkins-operator
urls:
- https://raw.githubusercontent.com/jenkinsci/kubernetes-operator/master/chart/jenkins-operator/jenkins-operator-0.6.1.tgz
version: 0.6.1
- apiVersion: v2
appVersion: 0.7.0
created: "2021-12-08T14:21:18.243261+01:00"
dependencies:
- condition: webhook.enabled
name: cert-manager
repository: https://charts.jetstack.io
version: 1.5.1
description: Kubernetes native operator which fully manages Jenkins on Kubernetes
digest: 6b36f47d6647231bd21581bea6b23c3660acb11073ebde2cb47f9a700101c462
icon: https://raw.githubusercontent.com/jenkinsci/kubernetes-operator/master/assets/jenkins-operator-icon.png
name: jenkins-operator
urls:
- https://raw.githubusercontent.com/jenkinsci/kubernetes-operator/master/chart/jenkins-operator/jenkins-operator-0.6.0.tgz
version: 0.6.0
- apiVersion: v2
appVersion: 0.6.0
created: "2021-08-11T15:40:10.659538+02:00"
description: Kubernetes native operator which fully manages Jenkins on Kubernetes
digest: e79615d988cc0c0bb64996394268ff87d232797b72ab9ad9a7891e9999daee9f
icon: https://raw.githubusercontent.com/jenkinsci/kubernetes-operator/master/assets/jenkins-operator-icon.png
name: jenkins-operator
urls:
- https://raw.githubusercontent.com/jenkinsci/kubernetes-operator/master/chart/jenkins-operator/jenkins-operator-0.5.3.tgz
version: 0.5.3
- apiVersion: v2
appVersion: 0.6.0
created: "2021-06-11T13:50:32.677639006+02:00"
description: Kubernetes native operator which fully manages Jenkins on Kubernetes
digest: 48fbf15c3ffff7003623edcde0bec39dc37d0a62303f08066960d5fac799af90
icon: https://raw.githubusercontent.com/jenkinsci/kubernetes-operator/master/assets/jenkins-operator-icon.png
name: jenkins-operator
urls:
- https://raw.githubusercontent.com/jenkinsci/kubernetes-operator/master/chart/jenkins-operator/jenkins-operator-0.5.2.tgz
version: 0.5.2
- apiVersion: v2
appVersion: 0.6.0
created: "2021-06-11T13:50:32.677639006+02:00"
description: Kubernetes native operator which fully manages Jenkins on Kubernetes
digest: 6f92759aaa7baafe5fe3a07f237b2c31b6921ca20cba145ee168c8fdb331d294
icon: https://raw.githubusercontent.com/jenkinsci/kubernetes-operator/master/assets/jenkins-operator-icon.png
name: jenkins-operator
urls:
- https://raw.githubusercontent.com/jenkinsci/kubernetes-operator/master/chart/jenkins-operator/jenkins-operator-0.5.1.tgz
version: 0.5.1
- apiVersion: v2
appVersion: 0.6.0
created: "2021-06-11T13:50:32.697552742+02:00"
description: Kubernetes native operator which fully manages Jenkins on Kubernetes
digest: aae33e13b8fcab6957b8ded2a2ae08025db38e1d86bbec8f8d9595b9eb904df1
icon: https://raw.githubusercontent.com/jenkinsci/kubernetes-operator/master/assets/jenkins-operator-icon.png
name: jenkins-operator
urls:
- https://raw.githubusercontent.com/jenkinsci/kubernetes-operator/master/chart/jenkins-operator/jenkins-operator-0.5.0.tgz
version: 0.5.0
- apiVersion: v2
appVersion: 0.5.0
created: "2021-02-19T12:36:08.931516+01:00"
description: Kubernetes native operator which fully manages Jenkins on Kubernetes
digest: 7a63ffab918dc012e0231cb362f16fa33d61b6db37a70b00b8c08ceb8cc90768
icon: https://raw.githubusercontent.com/jenkinsci/kubernetes-operator/master/assets/jenkins-operator-icon.png
name: jenkins-operator
urls:
- https://raw.githubusercontent.com/jenkinsci/kubernetes-operator/master/chart/jenkins-operator/jenkins-operator-0.4.3.tgz
version: 0.4.3
- apiVersion: v2
appVersion: 0.5.0
created: "2021-02-04T08:33:21.964999+01:00"
description: Kubernetes native operator which fully manages Jenkins on Kubernetes
digest: f045e699339da7345a1b848c5a068ec932ee3da7dfccf8c1f62c4df67aca488e
icon: https://raw.githubusercontent.com/jenkinsci/kubernetes-operator/master/assets/jenkins-operator-icon.png
name: jenkins-operator
urls:
- https://raw.githubusercontent.com/jenkinsci/kubernetes-operator/master/chart/jenkins-operator/jenkins-operator-0.4.2.tgz
version: 0.4.2
- apiVersion: v2
appVersion: 0.5.0
created: "2021-02-04T08:33:21.989979+01:00"
description: Kubernetes native operator which fully manages Jenkins on Kubernetes
digest: ac45a506be78fe1b68547e9ab6423491740b57a59a3ef631313c94bd67db164d
icon: https://raw.githubusercontent.com/jenkinsci/kubernetes-operator/master/assets/jenkins-operator-icon.png
name: jenkins-operator
urls:
- https://raw.githubusercontent.com/jenkinsci/kubernetes-operator/master/chart/jenkins-operator/jenkins-operator-0.4.1.tgz
version: 0.4.1
- apiVersion: v2
appVersion: 0.5.0
created: "2021-01-23T14:09:57.322319864+01:00"
description: Kubernetes native operator which fully manages Jenkins on Kubernetes
digest: 88149b9fd3a53e0850ddf0628ba07d7637e95e3a616e780497c2b3a3bed8b88a
icon: https://raw.githubusercontent.com/jenkinsci/kubernetes-operator/master/assets/jenkins-operator-icon.png
name: jenkins-operator
urls:
- https://raw.githubusercontent.com/jenkinsci/kubernetes-operator/master/chart/jenkins-operator/jenkins-operator-0.4.0.tgz
version: 0.4.0
- apiVersion: v2 - apiVersion: v2
appVersion: 0.4.0 appVersion: 0.4.0
created: "2020-07-19T17:26:48.811339047+02:00" created: "2020-07-19T17:26:48.811339047+02:00"
@ -416,4 +208,4 @@ entries:
urls: urls:
- https://raw.githubusercontent.com/jenkinsci/kubernetes-operator/master/chart/jenkins-operator/jenkins-operator-0.0.1.tgz - https://raw.githubusercontent.com/jenkinsci/kubernetes-operator/master/chart/jenkins-operator/jenkins-operator-0.0.1.tgz
version: 0.0.1 version: 0.0.1
generated: "2025-04-06T21:25:18.363088324Z" generated: "2020-06-18T11:48:46.181495+02:00"

View File

@ -20,7 +20,3 @@
.idea/ .idea/
*.tmproj *.tmproj
.vscode/ .vscode/
# Ignore packaged charts
jenkins-operator-*.tgz

View File

@ -1,6 +0,0 @@
dependencies:
- name: cert-manager
repository: https://charts.jetstack.io
version: v1.14.2
digest: sha256:5f6f7c115d7b96e8c8e85515e087a9379473fd3d5262198a9e25c1a84d4ff9bd
generated: "2024-02-15T23:08:28.352007672+01:00"

View File

@ -1,14 +1,6 @@
apiVersion: v2 apiVersion: v2
appVersion: "0.9.0-beta1" appVersion: "0.4.0"
description: Kubernetes native operator which fully manages Jenkins on Kubernetes description: Kubernetes native operator which fully manages Jenkins on Kubernetes
name: jenkins-operator name: jenkins-operator
version: 0.9.0-beta1 version: 0.3.4
icon: https://raw.githubusercontent.com/jenkinsci/kubernetes-operator/master/assets/jenkins-operator-icon.png icon: https://raw.githubusercontent.com/jenkinsci/kubernetes-operator/master/assets/jenkins-operator-icon.png
dependencies:
- name: cert-manager
version: "1.14.2"
condition: cert-manager.enabled
repository: https://charts.jetstack.io
- name: cert-manager-crds
version: "1.14.2"
condition: cert-manager.enabled

View File

@ -1,117 +0,0 @@
# jenkins-operator
![Version: 0.8.1](https://img.shields.io/badge/Version-0.8.1-informational?style=flat-square) ![AppVersion: 0.8.1](https://img.shields.io/badge/AppVersion-0.8.1-informational?style=flat-square)
Kubernetes native operator which fully manages Jenkins on Kubernetes
## Requirements
| Repository | Name | Version |
|------------|------|---------|
| | cert-manager-crds | 1.14.2 |
| https://charts.jetstack.io | cert-manager | 1.14.2 |
## Values
| Key | Type | Default | Description |
|-----|------|---------|-------------|
| cert-manager.enabled | bool | `false` | |
| cert-manager.startupapicheck.enabled | bool | `false` | |
| jenkins.annotations | object | `{}` | |
| jenkins.apiVersion | string | `"jenkins.io/v1alpha2"` | |
| jenkins.authorizationStrategy | string | `"createUser"` | |
| jenkins.backup.backupCommand[0] | string | `"/home/user/bin/backup.sh"` | |
| jenkins.backup.containerName | string | `"backup"` | |
| jenkins.backup.enabled | bool | `true` | |
| jenkins.backup.env[0].name | string | `"BACKUP_DIR"` | |
| jenkins.backup.env[0].value | string | `"/backup"` | |
| jenkins.backup.env[1].name | string | `"JENKINS_HOME"` | |
| jenkins.backup.env[1].value | string | `"/jenkins-home"` | |
| jenkins.backup.env[2].name | string | `"BACKUP_COUNT"` | |
| jenkins.backup.env[2].value | string | `"3"` | |
| jenkins.backup.getLatestAction[0] | string | `"/home/user/bin/get-latest.sh"` | |
| jenkins.backup.image | string | `"quay.io/jenkins-kubernetes-operator/backup-pvc:v0.4.3"` | |
| jenkins.backup.interval | int | `30` | |
| jenkins.backup.makeBackupBeforePodDeletion | bool | `true` | |
| jenkins.backup.pvc.className | string | `""` | |
| jenkins.backup.pvc.enabled | bool | `true` | |
| jenkins.backup.pvc.size | string | `"5Gi"` | |
| jenkins.backup.resources.limits.cpu | string | `"1000m"` | |
| jenkins.backup.resources.limits.memory | string | `"2Gi"` | |
| jenkins.backup.resources.requests.cpu | string | `"100m"` | |
| jenkins.backup.resources.requests.memory | string | `"500Mi"` | |
| jenkins.backup.restoreCommand[0] | string | `"/home/user/bin/restore.sh"` | |
| jenkins.backup.volumeMounts[0].mountPath | string | `"/jenkins-home"` | |
| jenkins.backup.volumeMounts[0].name | string | `"jenkins-home"` | |
| jenkins.backup.volumeMounts[1].mountPath | string | `"/backup"` | |
| jenkins.backup.volumeMounts[1].name | string | `"backup"` | |
| jenkins.basePlugins | list | `[]` | |
| jenkins.configuration.configurationAsCode | list | `[]` | |
| jenkins.configuration.groovyScripts | list | `[]` | |
| jenkins.configuration.secretData | object | `{}` | |
| jenkins.configuration.secretRefName | string | `""` | |
| jenkins.disableCSRFProtection | bool | `false` | |
| jenkins.enabled | bool | `true` | |
| jenkins.env | list | `[]` | |
| jenkins.hostAliases | object | `{}` | |
| jenkins.image | string | `"jenkins/jenkins:2.492.3-lts"` | |
| jenkins.imagePullPolicy | string | `"Always"` | |
| jenkins.imagePullSecrets | list | `[]` | |
| jenkins.labels | object | `{}` | |
| jenkins.latestPlugins | bool | `true` | |
| jenkins.lifecycle | object | `{}` | |
| jenkins.livenessProbe.failureThreshold | int | `20` | |
| jenkins.livenessProbe.httpGet.path | string | `"/login"` | |
| jenkins.livenessProbe.httpGet.port | string | `"http"` | |
| jenkins.livenessProbe.httpGet.scheme | string | `"HTTP"` | |
| jenkins.livenessProbe.initialDelaySeconds | int | `100` | |
| jenkins.livenessProbe.periodSeconds | int | `10` | |
| jenkins.livenessProbe.successThreshold | int | `1` | |
| jenkins.livenessProbe.timeoutSeconds | int | `8` | |
| jenkins.name | string | `"jenkins"` | |
| jenkins.namespace | string | `"default"` | |
| jenkins.nodeSelector | object | `{}` | |
| jenkins.notifications | list | `[]` | |
| jenkins.plugins | list | `[]` | |
| jenkins.priorityClassName | string | `""` | |
| jenkins.readinessProbe.failureThreshold | int | `60` | |
| jenkins.readinessProbe.httpGet.path | string | `"/login"` | |
| jenkins.readinessProbe.httpGet.port | string | `"http"` | |
| jenkins.readinessProbe.httpGet.scheme | string | `"HTTP"` | |
| jenkins.readinessProbe.initialDelaySeconds | int | `120` | |
| jenkins.readinessProbe.periodSeconds | int | `10` | |
| jenkins.readinessProbe.successThreshold | int | `1` | |
| jenkins.readinessProbe.timeoutSeconds | int | `8` | |
| jenkins.resources.limits.cpu | string | `"1000m"` | |
| jenkins.resources.limits.memory | string | `"3Gi"` | |
| jenkins.resources.requests.cpu | string | `"250m"` | |
| jenkins.resources.requests.memory | string | `"500Mi"` | |
| jenkins.securityContext.fsGroup | int | `1000` | |
| jenkins.securityContext.runAsUser | int | `1000` | |
| jenkins.seedJobAgentImage | string | `""` | |
| jenkins.seedJobs | list | `[]` | |
| jenkins.serviceAccount.annotations | object | `{}` | |
| jenkins.skipPlugins | bool | `false` | |
| jenkins.terminationGracePeriodSeconds | int | `30` | |
| jenkins.tolerations | list | `[]` | |
| jenkins.validateSecurityWarnings | bool | `false` | |
| jenkins.volumeMounts | list | `[]` | |
| jenkins.volumes[0].name | string | `"backup"` | |
| jenkins.volumes[0].persistentVolumeClaim.claimName | string | `"jenkins-backup"` | |
| operator.affinity | object | `{}` | |
| operator.fullnameOverride | string | `""` | |
| operator.image | string | `"quay.io/jenkins-kubernetes-operator/operator:v0.9.0-beta1"` | |
| operator.imagePullPolicy | string | `"IfNotPresent"` | |
| operator.imagePullSecrets | list | `[]` | |
| operator.nameOverride | string | `""` | |
| operator.nodeSelector | object | `{}` | |
| operator.replicaCount | int | `1` | |
| operator.resources | object | `{}` | |
| operator.tolerations | list | `[]` | |
| webhook.certificate.duration | string | `"2160h"` | |
| webhook.certificate.name | string | `"webhook-certificate"` | |
| webhook.certificate.renewbefore | string | `"360h"` | |
| webhook.enabled | bool | `false` | |
----------------------------------------------
Autogenerated from chart metadata using [helm-docs v1.14.2](https://github.com/norwoodj/helm-docs/releases/v1.14.2)

View File

@ -1,3 +0,0 @@
apiVersion: v2
name: cert-manager-crds
version: "1.14.2"

File diff suppressed because it is too large Load Diff

View File

@ -9,45 +9,31 @@ metadata:
{{- end }} {{- end }}
spec: spec:
configurationAsCode: configurationAsCode:
{{- if .Values.jenkins.configuration.configurationAsCode }}
configurations: configurations:
{{ range .Values.jenkins.configuration.configurationAsCode }} {{- range .Values.jenkins.configuration.configurationAsCode }}
- name: {{ .configMapName }} - name: {{ .configMapName }}
{{- end }} {{- end }}
{{- if .Values.jenkins.configuration.configurationAsCode }}
secret: secret:
{{- if .Values.jenkins.configuration.secretRefName }} {{- if .Values.jenkins.configuration.secretRefName }}
name: {{ .Values.jenkins.configuration.secretRefName }} name: {{ .Values.jenkins.configuration.secretRefName }}
{{- else if .Values.jenkins.configuration.secretData }} {{- else if .Values.jenkins.configuration.secretData }}
name: jenkins-{{ .Values.jenkins.name }} name: jenkins-{{ .Values.jenkins.name }}
{{- else }}
name: ""
{{- end }} {{- end }}
{{- else }}
configurations: []
secret:
name: ""
{{- end }} {{- end }}
groovyScripts: groovyScripts:
{{- if .Values.jenkins.configuration.groovyScripts }}
configurations: configurations:
{{- range .Values.jenkins.configuration.groovyScripts }} {{- range .Values.jenkins.configuration.groovyScripts }}
- name: {{ .configMapName }} - name: {{ .configMapName }}
{{- end }} {{- end }}
{{- if .Values.jenkins.configuration.groovyScripts }}
secret: secret:
{{- if .Values.jenkins.configuration.secretRefName }} {{- if .Values.jenkins.configuration.secretRefName }}
name: {{ .Values.jenkins.configuration.secretRefName }} name: {{ .Values.jenkins.configuration.secretRefName }}
{{- else if .Values.jenkins.configuration.secretData }} {{- else if .Values.jenkins.configuration.secretData }}
name: jenkins-{{ .Values.jenkins.name }} name: jenkins-{{ .Values.jenkins.name }}
{{- else }}
name: ""
{{- end }} {{- end }}
{{- else }}
configurations: []
secret:
name: ""
{{- end }} {{- end }}
jenkinsAPISettings:
authorizationStrategy: {{ .Values.jenkins.authorizationStrategy }}
{{- if .Values.jenkins.backup.enabled }} {{- if .Values.jenkins.backup.enabled }}
backup: backup:
containerName: {{ .Values.jenkins.backup.containerName }} containerName: {{ .Values.jenkins.backup.containerName }}
@ -65,13 +51,6 @@ spec:
{{- with .Values.jenkins.backup.restoreCommand }} {{- with .Values.jenkins.backup.restoreCommand }}
command: {{ toYaml . | nindent 8 }} command: {{ toYaml . | nindent 8 }}
{{- end }} {{- end }}
{{- if .Values.jenkins.backup.getLatestAction }}
getLatestAction:
exec:
{{- with .Values.jenkins.backup.getLatestAction }}
command: {{ toYaml . | nindent 8 }}
{{- end }}
{{- end }}
{{- if .Values.jenkins.backup.recoveryOnce }} {{- if .Values.jenkins.backup.recoveryOnce }}
recoveryOnce: {{ .Values.jenkins.backup.recoveryOnce }} recoveryOnce: {{ .Values.jenkins.backup.recoveryOnce }}
{{- end }} {{- end }}
@ -85,19 +64,10 @@ spec:
{{- with .Values.jenkins.notifications }} {{- with .Values.jenkins.notifications }}
notifications: {{ toYaml . | nindent 4 }} notifications: {{ toYaml . | nindent 4 }}
{{- end }} {{- end }}
{{- with .Values.jenkins.serviceAccount }}
serviceAccount: {{ toYaml . | nindent 4 }}
{{- end }}
master: master:
{{- with .Values.jenkins.labels }} {{- with .Values.jenkins.labels }}
labels: {{ toYaml . | nindent 6 }} labels: {{ toYaml . | nindent 6 }}
{{- end }} {{- end }}
{{- with .Values.jenkins.nodeSelector }}
nodeSelector: {{ toYaml . | nindent 6 }}
{{- end }}
{{- with .Values.jenkins.tolerations }}
tolerations: {{ toYaml . | nindent 6 }}
{{- end }}
{{- with .Values.jenkins.annotations }} {{- with .Values.jenkins.annotations }}
annotations: {{ toYaml . | nindent 6 }} annotations: {{ toYaml . | nindent 6 }}
{{- end }} {{- end }}
@ -107,47 +77,48 @@ spec:
{{- with .Values.jenkins.plugins }} {{- with .Values.jenkins.plugins }}
plugins: {{ toYaml . | nindent 4 }} plugins: {{ toYaml . | nindent 4 }}
{{- end }} {{- end }}
latestPlugins: {{ .Values.jenkins.latestPlugins }}
{{- if .Values.jenkins.priorityClassName }}
priorityClassName: {{ .Values.jenkins.priorityClassName }} priorityClassName: {{ .Values.jenkins.priorityClassName }}
{{- end }}
disableCSRFProtection: {{ .Values.jenkins.disableCSRFProtection }} disableCSRFProtection: {{ .Values.jenkins.disableCSRFProtection }}
{{- with .Values.jenkins.hostAliases }}
hostAliases: {{ toYaml . | nindent 4 }}
{{- end }}
skipPlugins: {{ .Values.jenkins.skipPlugins }}
{{- if .Values.jenkins.terminationGracePeriodSeconds }}
terminationGracePeriodSeconds: {{ .Values.jenkins.terminationGracePeriodSeconds }}
{{- end }}
containers: containers:
- name: jenkins-master - name: jenkins-master
image: {{ .Values.jenkins.image }} image: {{ .Values.jenkins.image }}
imagePullPolicy: {{ .Values.jenkins.imagePullPolicy }} imagePullPolicy: {{ .Values.jenkins.imagePullPolicy }}
{{- with .Values.jenkins.livenessProbe }} {{- with .Values.jenkins.imagePullSecrets }}
livenessProbe: {{ toYaml . | nindent 10 }} imagePullSecrets: {{ toYaml . | nindent 10 }}
{{- end }}
{{- with .Values.jenkins.readinessProbe }}
readinessProbe: {{ toYaml . | nindent 10 }}
{{- end }} {{- end }}
livenessProbe:
failureThreshold: 12
httpGet:
path: /login
port: http
scheme: HTTP
initialDelaySeconds: 80
periodSeconds: 10
successThreshold: 1
timeoutSeconds: 5
readinessProbe:
failureThreshold: 3
httpGet:
path: /login
port: http
scheme: HTTP
initialDelaySeconds: 30
periodSeconds: 10
successThreshold: 1
timeoutSeconds: 1
{{- with .Values.jenkins.resources }} {{- with .Values.jenkins.resources }}
resources: {{ toYaml . | nindent 10 }} resources: {{ toYaml . | nindent 10 }}
{{- end }} {{- end }}
{{- with .Values.jenkins.env }} {{- with .Values.jenkins.env }}
env: {{- toYaml . | nindent 8 }} env: {{- toYaml . | nindent 8 }}
{{- end }} {{- end }}
{{- with .Values.jenkins.lifecycle}}
lifecycle: {{ toYaml . | nindent 10 }}
{{- end }}
{{- with .Values.jenkins.volumeMounts }} {{- with .Values.jenkins.volumeMounts }}
volumeMounts: {{- toYaml . | nindent 8 }} volumeMounts: {{- toYaml . | nindent 8 }}
{{- end }} {{- end }}
{{- if .Values.jenkins.backup.enabled }} {{- if .Values.jenkins.backup.enabled }}
- name: {{ .Values.jenkins.backup.containerName }} - name: {{ .Values.jenkins.backup.containerName }}
image: {{ .Values.jenkins.backup.image }} image: {{ .Values.jenkins.backup.image }}
imagePullPolicy: {{ .Values.jenkins.imagePullPolicy }} imagePullPolicy: IfNotPresent
{{- with .Values.jenkins.backup.resources }}
resources: {{ toYaml . | nindent 10 }}
{{- end }}
{{- with .Values.jenkins.backup.env }} {{- with .Values.jenkins.backup.env }}
env: {{- toYaml . | nindent 8 }} env: {{- toYaml . | nindent 8 }}
{{- end }} {{- end }}
@ -158,18 +129,11 @@ spec:
{{- with .Values.jenkins.volumes }} {{- with .Values.jenkins.volumes }}
volumes: {{- toYaml . | nindent 4 }} volumes: {{- toYaml . | nindent 4 }}
{{- end }} {{- end }}
{{- with .Values.jenkins.imagePullSecrets }}
imagePullSecrets: {{ toYaml . | nindent 4 }}
{{- end }}
{{- with .Values.jenkins.securityContext}} {{- with .Values.jenkins.securityContext}}
securityContext: securityContext:
{{- toYaml . | nindent 6 }} {{- toYaml . | nindent 6 }}
{{- end }} {{- end }}
validateSecurityWarnings: {{ .Values.jenkins.validateSecurityWarnings }}
{{- with .Values.jenkins.seedJobs }} {{- with .Values.jenkins.seedJobs }}
seedJobs: {{- toYaml . | nindent 4 }} seedJobs: {{- toYaml . | nindent 4 }}
{{- end }} {{- end }}
{{- if .Values.jenkins.seedJobAgentImage }}
seedJobAgentImage: {{ .Values.jenkins.seedJobAgentImage }}
{{- end }}
{{- end }} {{- end }}

View File

@ -1,28 +0,0 @@
---
# permissions to do leader election.
apiVersion: rbac.authorization.k8s.io/v1
kind: Role
metadata:
name: leader-election-role
rules:
- apiGroups:
- ""
- coordination.k8s.io
resources:
- configmaps
- leases
verbs:
- get
- list
- watch
- create
- update
- patch
- delete
- apiGroups:
- ""
resources:
- events
verbs:
- create
- patch

View File

@ -1,12 +0,0 @@
---
apiVersion: rbac.authorization.k8s.io/v1
kind: RoleBinding
metadata:
name: leader-election-rolebinding
roleRef:
apiGroup: rbac.authorization.k8s.io
kind: Role
name: leader-election-role
subjects:
- kind: ServiceAccount
name: jenkins-operator

View File

@ -30,40 +30,13 @@ spec:
containerPort: 80 containerPort: 80
protocol: TCP protocol: TCP
command: command:
- /manager - jenkins-operator
args: args: []
{{- if .Values.webhook.enabled }}
- --validate-security-warnings
{{- end }}
{{- if .Values.webhook.enabled }}
volumeMounts:
- mountPath: /tmp/k8s-webhook-server/serving-certs
name: webhook-certs
readOnly: true
{{- end }}
livenessProbe:
httpGet:
path: /healthz
port: 8081
initialDelaySeconds: 15
periodSeconds: 20
readinessProbe:
httpGet:
path: /readyz
port: 8081
initialDelaySeconds: 5
periodSeconds: 10
env: env:
- name: WATCH_NAMESPACE - name: WATCH_NAMESPACE
{{- if .Values.jenkins.enabled }}
value: {{ .Values.jenkins.namespace }}
{{- else if .Values.operator.watchNamespace }}
value: {{ .Values.operator.watchNamespace }}
{{- else }}
valueFrom: valueFrom:
fieldRef: fieldRef:
fieldPath: metadata.namespace fieldPath: metadata.namespace
{{- end }}
- name: POD_NAME - name: POD_NAME
valueFrom: valueFrom:
fieldRef: fieldRef:
@ -84,11 +57,3 @@ spec:
tolerations: tolerations:
{{- toYaml . | nindent 8 }} {{- toYaml . | nindent 8 }}
{{- end }} {{- end }}
{{- if .Values.webhook.enabled }}
volumes:
- name: webhook-certs
secret:
defaultMode: 420
secretName: jenkins-{{ .Values.webhook.certificate.name }}
terminationGracePeriodSeconds: 10
{{- end }}

View File

@ -1,15 +1,127 @@
{{ if eq .Values.jenkins.namespace "" }} ---
{{- /* kind: Role
# This is a special case when .Values.jenkins.namespace is equal to empty apiVersion: rbac.authorization.k8s.io/v1
# string which leads to WATCH_NAMESPACE env of jenkins-operator to be set to metadata:
# empty string and leads to operator actually watching all namespaces. In this name: jenkins-operator
# case we need to create clusterrole and clusterrolebinding instead of role and rules:
# rolebinding - apiGroups:
*/ -}} - ""
{{- template "jenkins-operator.role" .Values.jenkins.namespace }} resources:
{{ else }} - services
{{- template "jenkins-operator.role" .Release.Namespace }} - configmaps
{{- if ne .Release.Namespace .Values.jenkins.namespace -}} - secrets
{{- template "jenkins-operator.role" .Values.jenkins.namespace }} verbs:
{{- end }} - get
{{ end }} - create
- update
- list
- watch
- apiGroups:
- apps
resources:
- deployments
- daemonsets
- replicasets
- statefulsets
verbs:
- '*'
- apiGroups:
- ""
resources:
- serviceaccounts
verbs:
- get
- create
- update
- list
- watch
- apiGroups:
- rbac.authorization.k8s.io
resources:
- roles
- rolebindings
verbs:
- get
- create
- update
- list
- watch
- apiGroups:
- ""
resources:
- pods/portforward
verbs:
- create
- apiGroups:
- ""
resources:
- pods/log
verbs:
- get
- list
- watch
- apiGroups:
- ""
resources:
- pods
- pods/exec
verbs:
- "*"
- apiGroups:
- ""
resources:
- events
verbs:
- watch
- list
- create
- patch
- apiGroups:
- apps
resourceNames:
- jenkins-operator
resources:
- deployments/finalizers
verbs:
- update
- apiGroups:
- jenkins.io
resources:
- '*'
verbs:
- '*'
- apiGroups:
- ""
resources:
- persistentvolumeclaims
verbs:
- get
- list
- watch
- apiGroups:
- "route.openshift.io"
resources:
- routes
verbs:
- get
- list
- watch
- create
- update
- apiGroups:
- "image.openshift.io"
resources:
- imagestreams
verbs:
- get
- list
- watch
- apiGroups:
- "build.openshift.io"
resources:
- builds
- buildconfigs
verbs:
- get
- list
- watch

View File

@ -1,53 +1,12 @@
{{ if eq .Values.jenkins.namespace "" }}
{{- /*
# This is a special case when .Values.jenkins.namespace is equal to empty
# string which leads to WATCH_NAMESPACE env of jenkins-operator to be set to
# empty string and leads to operator actually watching all namespaces. In this
# case we need to create clusterrole and clusterrolebinding instead of role and
# rolebinding
*/ -}}
---
kind: ClusterRoleBinding
apiVersion: rbac.authorization.k8s.io/v1
metadata:
name: jenkins-operator
subjects:
- kind: ServiceAccount
name: jenkins-operator
namespace: {{ .Release.Namespace }}
roleRef:
kind: ClusterRole
name: jenkins-operator
apiGroup: rbac.authorization.k8s.io
{{ else }}
--- ---
kind: RoleBinding kind: RoleBinding
apiVersion: rbac.authorization.k8s.io/v1 apiVersion: rbac.authorization.k8s.io/v1
metadata: metadata:
name: jenkins-operator name: jenkins-operator
namespace: {{ .Release.Namespace }}
subjects: subjects:
- kind: ServiceAccount - kind: ServiceAccount
name: jenkins-operator name: jenkins-operator
namespace: {{ .Release.Namespace }}
roleRef: roleRef:
kind: Role kind: Role
name: jenkins-operator name: jenkins-operator
apiGroup: rbac.authorization.k8s.io apiGroup: rbac.authorization.k8s.io
{{ if ne .Release.Namespace .Values.jenkins.namespace }}
---
kind: RoleBinding
apiVersion: rbac.authorization.k8s.io/v1
metadata:
name: jenkins-operator
namespace: {{ .Values.jenkins.namespace }}
subjects:
- kind: ServiceAccount
name: jenkins-operator
namespace: {{ .Release.Namespace }}
roleRef:
kind: Role
name: jenkins-operator
apiGroup: rbac.authorization.k8s.io
{{ end }}
{{ end }}

View File

@ -1,34 +0,0 @@
{{- if .Values.webhook.enabled }}
apiVersion: cert-manager.io/v1
kind: Certificate
metadata:
name: jenkins-{{ .Values.webhook.certificate.name }}
namespace: {{ .Release.Namespace }}
spec:
duration: {{ .Values.webhook.certificate.duration }}
renewBefore: {{ .Values.webhook.certificate.renewbefore }}
secretName: jenkins-{{ .Values.webhook.certificate.name }}
dnsNames:
- jenkins-webhook-service.{{ .Release.Namespace }}.svc
- jenkins-webhook-service.{{ .Release.Namespace }}.svc.cluster.local
issuerRef:
kind: Issuer
name: selfsigned
---
apiVersion: cert-manager.io/v1
kind: Issuer
metadata:
name: selfsigned
namespace: {{ .Release.Namespace }}
spec:
selfSigned: {}
---
apiVersion: v1
kind: Secret
metadata:
name: jenkins-{{ .Values.webhook.certificate.name }}
type: opaque
{{- end }}

View File

@ -1,47 +0,0 @@
{{- if .Values.webhook.enabled }}
apiVersion: admissionregistration.k8s.io/v1
kind: ValidatingWebhookConfiguration
metadata:
name: {{ .Release.Name }}-webhook
annotations:
cert-manager.io/inject-ca-from: {{ .Release.Namespace }}/jenkins-{{ .Values.webhook.certificate.name }}
webhooks:
- admissionReviewVersions:
- v1
- v1beta1
clientConfig:
service:
name: jenkins-webhook-service
namespace: {{ .Release.Namespace }}
path: /validate-jenkins-io-v1alpha2-jenkins
failurePolicy: Fail
name: vjenkins.kb.io
timeoutSeconds: 30
rules:
- apiGroups:
- jenkins.io
apiVersions:
- v1alpha2
operations:
- CREATE
- UPDATE
resources:
- jenkins
scope: "Namespaced"
sideEffects: None
---
apiVersion: v1
kind: Service
metadata:
name: jenkins-webhook-service
namespace: {{ .Release.Namespace }}
spec:
ports:
- port: 443
targetPort: 9443
selector:
app.kubernetes.io/name: {{ include "jenkins-operator.name" . }}
app.kubernetes.io/instance: {{ .Release.Name }}
---
{{- end }}

View File

@ -18,25 +18,18 @@ jenkins:
# namespace is the namespace where the resources will be deployed # namespace is the namespace where the resources will be deployed
# It's not recommended to use default namespace # It's not recommended to use default namespace
# Create new namespace for jenkins (called e.g. jenkins) # Create new namespace for jenkins (called e.g. jenkins)
# Note: this affects roles and rolebindings for jenkins operator itself
namespace: default namespace: default
# labels are injected into metadata labels field # labels are injected into metadata labels field
labels: {} labels: {}
# nodeSelector are injected into metadata nodeSelector field
nodeSelector: {}
# tolerations are injected into metadata tolerations field
tolerations: []
# annotations are injected into metadata annotations field # annotations are injected into metadata annotations field
annotations: {} annotations: {}
# image is the name (and tag) of the Jenkins instance # image is the name (and tag) of the Jenkins instance
# Default: jenkins/jenkins:lts # Default: jenkins/jenkins:lts
# It's recommended to use LTS (tag: "lts") version # It's recommended to use LTS (tag: "lts") version
image: jenkins/jenkins:2.528.1-lts image: jenkins/jenkins:2.263.2-lts-alpine
# env contains jenkins container environment variables # env contains jenkins container environment variables
env: [] env: []
@ -44,9 +37,6 @@ jenkins:
# imagePullPolicy defines policy for pulling images # imagePullPolicy defines policy for pulling images
imagePullPolicy: Always imagePullPolicy: Always
# lifecycle is used if you want to specify lifecycle hooks for the master container
lifecycle: {}
# priorityClassName indicates the importance of a Pod relative to other Pods # priorityClassName indicates the importance of a Pod relative to other Pods
# See: https://kubernetes.io/docs/concepts/configuration/pod-priority-preemption/ # See: https://kubernetes.io/docs/concepts/configuration/pod-priority-preemption/
priorityClassName: "" priorityClassName: ""
@ -56,38 +46,14 @@ jenkins:
# See https://github.com/jenkinsci/kubernetes-operator/pull/193 for more info # See https://github.com/jenkinsci/kubernetes-operator/pull/193 for more info
disableCSRFProtection: false disableCSRFProtection: false
# adding entries to a pod's /etc/hosts file provides pod-level override of hostname
# resolution when DNS and other options are not applicable.
hostAliases: {}
# - ip: "127.0.0.1"
# hostnames:
# - "foo.local"
# - "bar.local"
# - ip: "10.1.2.3"
# hostnames:
# - "foo.remote"
# - "bar.remote"
# Optional duration in seconds the pod needs to terminate gracefully.
# Default 30sec
terminationGracePeriodSeconds: 30
# validateSecurityWarnings enables or disables validating potential security warnings in Jenkins plugins via admission webhooks.
validateSecurityWarnings: false
# imagePullSecrets is used if you want to pull images from private repository # imagePullSecrets is used if you want to pull images from private repository
# See https://jenkinsci.github.io/kubernetes-operator/docs/getting-started/latest/configuration/#pulling-docker-images-from-private-repositories for more info # See https://jenkinsci.github.io/kubernetes-operator/docs/getting-started/latest/configuration/#pulling-docker-images-from-private-repositories for more info
imagePullSecrets: [] imagePullSecrets: []
# notifications is feature that notify user about Jenkins reconciliation status # notifications is feature that notify user about Jenkins reconcilation status
# See https://jenkinsci.github.io/kubernetes-operator/docs/getting-started/latest/notifications/ for more info # See https://jenkinsci.github.io/kubernetes-operator/docs/getting-started/latest/notifications/ for more info
notifications: [] notifications: []
# Enables customization of the Service Account attached to the master Jenkins instance via annotations
# https://jenkinsci.github.io/kubernetes-operator/docs/getting-started/latest/schema/#github.com/jenkinsci/kubernetes-operator/api/v1alpha2.ServiceAccount
serviceAccount:
annotations: {}
# basePlugins are plugins installed and required by the operator # basePlugins are plugins installed and required by the operator
# Shouldn't contain plugins defined by user # Shouldn't contain plugins defined by user
# You can change their versions here # You can change their versions here
@ -97,21 +63,34 @@ jenkins:
# #
# basePlugins: # basePlugins:
# - name: kubernetes # - name: kubernetes
# version: 4246.v5a_12b_1fe120e # version: 1.28.6
# - name: workflow-job # - name: workflow-job
# version: 1400.v7fd111b_ec82f # version: "2.40"
# - name: workflow-aggregator # - name: workflow-aggregator
# version: 596.v8c21c963d92d # version: "2.6"
# - name: git # - name: git
# version: 5.2.2 # version: 4.5.0
# - name: job-dsl # - name: job-dsl
# version: "1.87" # version: "1.77"
# - name: configuration-as-code # - name: configuration-as-code
# version: 1810.v9b_c30a_249a_4c # version: "1.46"
# - name: kubernetes-credentials-provider # - name: kubernetes-credentials-provider
# version: 1.262.v2670ef7ea_0c5 # version: 0.15
basePlugins:
basePlugins: [] - name: kubernetes
version: "1.28.6"
- name: workflow-job
version: "2.40"
- name: workflow-aggregator
version: "2.6"
- name: git
version: "4.5.0"
- name: job-dsl
version: "1.77"
- name: configuration-as-code
version: "1.46"
- name: kubernetes-credentials-provider
version: "0.15"
# plugins are plugins required by the user # plugins are plugins required by the user
# You can define plugins here # You can define plugins here
@ -124,14 +103,9 @@ jenkins:
# version: "0.6" # version: "0.6"
plugins: [] plugins: []
# latestPlugins: Allow to override jenkins-plugin-cli default behavior
# while downloading the plugin and dependencies
# see: https://github.com/jenkinsci/plugin-installation-manager-tool#cli-options
# default to true
latestPlugins: true
# seedJobs is placeholder for jenkins seed jobs # seedJobs is placeholder for jenkins seed jobs
# For seed job creation tutorial, check https://jenkinsci.github.io/kubernetes-operator/docs/getting-started/latest/configuring-seed-jobs-and-pipelines/ # For seed job creation tutorial, check https://jenkinsci.github.io/kubernetes-operator/docs/getting-started/latest/configuration/#prepare-job-definitions-and-pipelines
# See https://jenkinsci.github.io/kubernetes-operator/docs/getting-started/latest/configuration/#configure-seed-jobs for additional info
# Example: # Example:
# #
# seedJobs: # seedJobs:
@ -142,22 +116,14 @@ jenkins:
# repositoryUrl: https://github.com/jenkinsci/kubernetes-operator.git # repositoryUrl: https://github.com/jenkinsci/kubernetes-operator.git
seedJobs: [] seedJobs: []
# SeedJobAgentImage defines the image that will be used by the seed job agent. If not defined jenkins/inbound-agent:3248.v65ecb_254c298-6 will be used.
seedJobAgentImage: ""
# skipPlugins allows to skip installation of both BasePlugins and Plugins.
# Requires using a custom image which includes the BasePlugins.
# Defaults to false.
skipPlugins: false
# Resource limit/request for Jenkins # Resource limit/request for Jenkins
# See https://kubernetes.io/docs/concepts/configuration/manage-compute-resources-container/ for details # See https://kubernetes.io/docs/concepts/configuration/manage-compute-resources-container/ for details
resources: resources:
limits: limits:
cpu: 1000m cpu: 1500m
memory: 3Gi memory: 3Gi
requests: requests:
cpu: 250m cpu: 1
memory: 500Mi memory: 500Mi
# volumes used by Jenkins # volumes used by Jenkins
@ -168,14 +134,8 @@ jenkins:
claimName: jenkins-backup claimName: jenkins-backup
# volumeMounts are mounts for Jenkins pod # volumeMounts are mounts for Jenkins pod
# Note that attempting to overwrite default mount settings for restricted,
# non-configurable volumeMounts will result in Operator error
# See https://jenkinsci.github.io/kubernetes-operator/docs/installation/#note-on-restricted-jenkins-controller-pod-volumemounts for details
volumeMounts: [] volumeMounts: []
# defines authorization strategy of the operator for the Jenkins API
authorizationStrategy: createUser
# securityContext for pod # securityContext for pod
securityContext: securityContext:
runAsUser: 1000 runAsUser: 1000
@ -188,29 +148,6 @@ jenkins:
# See https://jenkinsci.github.io/kubernetes-operator/docs/getting-started/latest/schema/#github.com/jenkinsci/kubernetes-operator/pkg/apis/jenkins/v1alpha2.Service for details # See https://jenkinsci.github.io/kubernetes-operator/docs/getting-started/latest/schema/#github.com/jenkinsci/kubernetes-operator/pkg/apis/jenkins/v1alpha2.Service for details
#slaveService: #slaveService:
# LivenessProbe for Jenkins Master pod
livenessProbe:
failureThreshold: 20
httpGet:
path: /login
port: http
scheme: HTTP
initialDelaySeconds: 100
periodSeconds: 10
successThreshold: 1
timeoutSeconds: 8
# ReadinessProbe for Jenkins Master pod
readinessProbe:
failureThreshold: 60
httpGet:
path: /login
port: http
scheme: HTTP
initialDelaySeconds: 120
periodSeconds: 10
successThreshold: 1
timeoutSeconds: 8
# backup is section for configuring operator's backup feature # backup is section for configuring operator's backup feature
# By default backup feature is enabled and pre-configured # By default backup feature is enabled and pre-configured
# This section simplifies the configuration described here: https://jenkinsci.github.io/kubernetes-operator/docs/getting-started/latest/configure-backup-and-restore/ # This section simplifies the configuration described here: https://jenkinsci.github.io/kubernetes-operator/docs/getting-started/latest/configure-backup-and-restore/
@ -221,8 +158,8 @@ jenkins:
enabled: true enabled: true
# image used by backup feature # image used by backup feature
# By default using prebuilt backup PVC image # By default using prebuilt backup PVC image by VirtusLab
image: quay.io/jenkins-kubernetes-operator/backup-pvc:v0.4.3 image: virtuslab/jenkins-operator-backup-pvc:v0.1.0
# containerName is backup container name # containerName is backup container name
containerName: backup containerName: backup
@ -256,25 +193,11 @@ jenkins:
# See https://kubernetes.io/docs/concepts/storage/persistent-volumes/#class-1 for more details # See https://kubernetes.io/docs/concepts/storage/persistent-volumes/#class-1 for more details
className: "" className: ""
# resources used by backup container
resources:
limits:
cpu: 1000m
memory: 2Gi
requests:
cpu: 100m
memory: 500Mi
# env contains container environment variables # env contains container environment variables
# PVC backup provider handles these variables: # PVC backup provider handles these variables:
# BACKUP_DIR - path for storing backup files (default: "/backup") # BACKUP_DIR - path for storing backup files (default: "/backup")
# JENKINS_HOME - path to jenkins home (default: "/jenkins-home") # JENKINS_HOME - path to jenkins home (default: "/jenkins-home")
# BACKUP_COUNT - define how much recent backups will be kept # BACKUP_COUNT - define how much recent backups will be kept
# Optional in case you want to modify the backup and restore retry logic
# BACKUP_RETRY_COUNT
# BACKUP_RETRY_INTERVAL
# RESTORE_RETRY_COUNT
# RESTORE_RETRY_INTERVAL
env: env:
- name: BACKUP_DIR - name: BACKUP_DIR
value: /backup value: /backup
@ -282,15 +205,6 @@ jenkins:
value: /jenkins-home value: /jenkins-home
- name: BACKUP_COUNT - name: BACKUP_COUNT
value: "3" # keep only the 3 most recent backups value: "3" # keep only the 3 most recent backups
#- name: BACKUP_RETRY_COUNT
# value: "3"
#- name: BACKUP_RETRY_INTERVAL
# value: "60"
#- name: RESTORE_RETRY_COUNT
# value: "10"
#- name: RESTORE_RETRY_INTERVAL
# value: "10"
# volumeMounts holds the mount points for volumes # volumeMounts holds the mount points for volumes
volumeMounts: volumeMounts:
@ -302,10 +216,10 @@ jenkins:
# configuration is section where we can configure Jenkins instance # configuration is section where we can configure Jenkins instance
# See https://jenkinsci.github.io/kubernetes-operator/docs/getting-started/latest/customization/ for details # See https://jenkinsci.github.io/kubernetes-operator/docs/getting-started/latest/customization/ for details
configuration: configuration:
configurationAsCode: [] configurationAsCode: {}
# - configMapName: jenkins-casc # - configMapName: jenkins-casc
# content: {} # content: {}
groovyScripts: [] groovyScripts: {}
# - configMapName: jenkins-gs # - configMapName: jenkins-gs
# content: {} # content: {}
@ -320,7 +234,7 @@ operator:
replicaCount: 1 replicaCount: 1
# image is the name (and tag) of the Jenkins Operator image # image is the name (and tag) of the Jenkins Operator image
image: quay.io/jenkins-kubernetes-operator/operator:v0.9.0-beta1 image: virtuslab/jenkins-operator:v0.4.0
# imagePullPolicy defines policy for pulling images # imagePullPolicy defines policy for pulling images
imagePullPolicy: IfNotPresent imagePullPolicy: IfNotPresent
@ -334,33 +248,7 @@ operator:
# fullnameOverride overrides the deployment name # fullnameOverride overrides the deployment name
fullnameOverride: "" fullnameOverride: ""
# Select a different namespace to look for the Jenkins CR and deploy Jenkins in. Defaults to the same namespace as
# the operator.
# watchNamespace: "jenkins-namespace"
resources: {} resources: {}
nodeSelector: {} nodeSelector: {}
tolerations: [] tolerations: []
affinity: {} affinity: {}
webhook:
# TLS certificates for webhook
certificate:
name: webhook-certificate
# validity of the certificate
duration: 2160h
# time after which the certificate will be automatically renewed
renewbefore: 360h
# enable or disable the validation webhook
enabled: false
cert-manager:
# cert-manager is required to generate certificates for webhook. If you don't have cert-manager installed in your cluster,
# you can install it as a subordinate chart
enabled: false
# This startupapicheck is a Helm post-install hook that waits for the webhook
# endpoints to become available.
startupapicheck:
enabled: false

31
cicd/jobs/build.jenkins Normal file
View File

@ -0,0 +1,31 @@
#!/usr/bin/env groovy
pipelineJob('build-jenkins-operator') {
displayName('Build jenkins-operator')
logRotator {
numToKeep(10)
daysToKeep(30)
}
configure { project ->
project / 'properties' / 'org.jenkinsci.plugins.workflow.job.properties.DurabilityHintJobProperty' {
hint('PERFORMANCE_OPTIMIZED')
}
}
definition {
cpsScm {
scm {
git {
remote {
url('https://github.com/jenkinsci/kubernetes-operator.git')
credentials('jenkins-operator')
}
branches('*/master')
}
}
scriptPath('cicd/pipelines/build.jenkins')
}
}
}

View File

@ -0,0 +1,48 @@
#!/usr/bin/env groovy
def label = "build-jenkins-operator-${UUID.randomUUID().toString()}"
def home = "/home/jenkins"
def workspace = "${home}/workspace/build-jenkins-operator"
def workdir = "${workspace}/src/github.com/jenkinsci/kubernetes-operator/"
podTemplate(label: label,
containers: [
containerTemplate(name: 'jnlp', image: 'jenkins/inbound-agent:alpine'),
containerTemplate(name: 'go', image: 'golang:1-alpine', command: 'cat', ttyEnabled: true),
],
envVars: [
envVar(key: 'GOPATH', value: workspace),
],
) {
node(label) {
dir(workdir) {
stage('Init') {
timeout(time: 3, unit: 'MINUTES') {
checkout scm
}
container('go') {
sh 'apk --no-cache --update add make git gcc libc-dev'
}
}
stage('Dep') {
container('go') {
sh 'make dep'
}
}
stage('Test') {
container('go') {
sh 'make test'
}
}
stage('Build') {
container('go') {
sh 'make build'
}
}
}
}
}

View File

@ -1,222 +0,0 @@
/*
Copyright 2021.
Licensed under the Apache License, Version 2.0 (the "License");
you may not use this file except in compliance with the License.
You may obtain a copy of the License at
http://www.apache.org/licenses/LICENSE-2.0
Unless required by applicable law or agreed to in writing, software
distributed under the License is distributed on an "AS IS" BASIS,
WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
See the License for the specific language governing permissions and
limitations under the License.
*/
package main
import (
"flag"
"fmt"
"os"
r "runtime"
"github.com/jenkinsci/kubernetes-operator/api/v1alpha2"
controllers "github.com/jenkinsci/kubernetes-operator/internal/controller"
"github.com/jenkinsci/kubernetes-operator/pkg/client"
"github.com/jenkinsci/kubernetes-operator/pkg/configuration/base/resources"
"github.com/jenkinsci/kubernetes-operator/pkg/constants"
"github.com/jenkinsci/kubernetes-operator/pkg/event"
"github.com/jenkinsci/kubernetes-operator/pkg/log"
"github.com/jenkinsci/kubernetes-operator/pkg/notifications"
e "github.com/jenkinsci/kubernetes-operator/pkg/notifications/event"
"github.com/jenkinsci/kubernetes-operator/version"
routev1 "github.com/openshift/api/route/v1"
"github.com/pkg/errors"
corev1 "k8s.io/api/core/v1"
"k8s.io/apimachinery/pkg/runtime"
utilruntime "k8s.io/apimachinery/pkg/util/runtime"
"k8s.io/client-go/kubernetes"
clientgoscheme "k8s.io/client-go/kubernetes/scheme"
"sigs.k8s.io/controller-runtime/pkg/cache"
"sigs.k8s.io/controller-runtime/pkg/client/config"
"sigs.k8s.io/controller-runtime/pkg/metrics/server"
"sigs.k8s.io/controller-runtime/pkg/webhook"
// Import all Kubernetes client auth plugins (e.g. Azure, GCP, OIDC, etc.)
// to ensure that exec-entrypoint and run can make use of them.
_ "k8s.io/client-go/plugin/pkg/client/auth"
ctrl "sigs.k8s.io/controller-runtime"
"sigs.k8s.io/controller-runtime/pkg/healthz"
logf "sigs.k8s.io/controller-runtime/pkg/log"
"sigs.k8s.io/controller-runtime/pkg/log/zap"
// +kubebuilder:scaffold:imports
)
var (
metricsHost = "0.0.0.0"
metricsPort int32 = 8383
scheme = runtime.NewScheme()
logger = logf.Log.WithName("cmd")
)
func printInfo() {
logger.Info(fmt.Sprintf("Version: %s", version.Version))
logger.Info(fmt.Sprintf("Git commit: %s", version.GitCommit))
logger.Info(fmt.Sprintf("Go Version: %s", r.Version()))
logger.Info(fmt.Sprintf("Go OS/Arch: %s/%s", r.GOOS, r.GOARCH))
}
func init() {
utilruntime.Must(clientgoscheme.AddToScheme(scheme))
utilruntime.Must(v1alpha2.AddToScheme(scheme))
utilruntime.Must(routev1.AddToScheme(scheme))
utilruntime.Must(corev1.AddToScheme(scheme))
// +kubebuilder:scaffold:scheme
}
func main() {
var metricsAddr string
var enableLeaderElection bool
var probeAddr string
var validateSecurityWarnings bool
isRunningInCluster, err := resources.IsRunningInCluster()
if err != nil {
fatal(errors.Wrap(err, "failed to determine if operator is running in cluster"), true)
}
flag.StringVar(&metricsAddr, "metrics-bind-address", ":8080", "The address the metric endpoint binds to.")
flag.StringVar(&probeAddr, "health-probe-bind-address", ":8081", "The address the probe endpoint binds to.")
flag.BoolVar(&enableLeaderElection, "leader-elect", isRunningInCluster, "Enable leader election for controller manager. "+
"Enabling this will ensure there is only one active controller manager.")
flag.BoolVar(&validateSecurityWarnings, "validate-security-warnings", false, "Enable validation for potential security warnings in jenkins custom resource plugins")
hostname := flag.String("jenkins-api-hostname", "", "Hostname or IP of Jenkins API. It can be service name, node IP or localhost.")
port := flag.Int("jenkins-api-port", 0, "The port on which Jenkins API is running. Note: If you want to use nodePort don't set this setting and --jenkins-api-use-nodeport must be true.")
useNodePort := flag.Bool("jenkins-api-use-nodeport", false, "Connect to Jenkins API using the service nodePort instead of service port. If you want to set this as true - don't set --jenkins-api-port.")
kubernetesClusterDomain := flag.String("cluster-domain", "cluster.local", "Use custom domain name instead of 'cluster.local'.")
opts := zap.Options{
Development: true,
}
opts.BindFlags(flag.CommandLine)
flag.Parse()
debug := &opts.Development
log.Debug = *debug
ctrl.SetLogger(zap.New(zap.UseFlagOptions(&opts)))
printInfo()
namespace, found := os.LookupEnv("WATCH_NAMESPACE")
if !found {
fatal(errors.New("failed to get watch namespace, please set up WATCH_NAMESPACE environment variable"), *debug)
}
logger.Info(fmt.Sprintf("Watch namespace: %v", namespace))
if validateSecurityWarnings {
securityWarningsFetched := make(chan bool)
go v1alpha2.SecValidator.MonitorSecurityWarnings(securityWarningsFetched)
if !<-securityWarningsFetched {
logger.Info("Unable to get the plugins data")
}
}
// get a config to talk to the API server
cfg, err := config.GetConfig()
if err != nil {
fatal(errors.Wrap(err, "failed to get config"), *debug)
}
cacheNamespace := map[string]cache.Config{}
cacheNamespace[namespace] = cache.Config{}
mgr, err := ctrl.NewManager(ctrl.GetConfigOrDie(), ctrl.Options{
// MetricsBindAddress: fmt.Sprintf("%s:%d", metricsHost, metricsPort),
Metrics: server.Options{
BindAddress: fmt.Sprintf("%s:%d", metricsHost, metricsPort),
},
Scheme: scheme,
// Port: 9443,
WebhookServer: webhook.NewServer(webhook.Options{
Port: 9443,
}),
HealthProbeBindAddress: probeAddr,
LeaderElection: enableLeaderElection,
LeaderElectionID: "c674355f.jenkins.io",
// Namespace: namespace,
Cache: cache.Options{DefaultNamespaces: cacheNamespace},
})
if err != nil {
fatal(errors.Wrap(err, "unable to start manager"), *debug)
}
// setup events
events, err := event.New(cfg, constants.OperatorName)
if err != nil {
fatal(errors.Wrap(err, "failed to setup events"), *debug)
}
// setup controller
clientSet, err := kubernetes.NewForConfig(cfg)
if err != nil {
fatal(errors.Wrap(err, "failed to create Kubernetes client set"), *debug)
}
if resources.IsRouteAPIAvailable(clientSet) {
logger.Info("Route API found: Route creation will be performed")
}
notificationEvents := make(chan e.Event)
go notifications.Listen(notificationEvents, events, mgr.GetClient())
// validate jenkins API connection
jenkinsAPIConnectionSettings := client.JenkinsAPIConnectionSettings{Hostname: *hostname, Port: *port, UseNodePort: *useNodePort}
if err := jenkinsAPIConnectionSettings.Validate(); err != nil {
fatal(errors.Wrap(err, "invalid command line parameters"), *debug)
}
// validate kubernetes cluster domain
if *kubernetesClusterDomain == "" {
fatal(errors.Wrap(err, "Kubernetes cluster domain can't be empty"), *debug)
}
if err = (&controllers.JenkinsReconciler{
Client: mgr.GetClient(),
Scheme: mgr.GetScheme(),
JenkinsAPIConnectionSettings: jenkinsAPIConnectionSettings,
ClientSet: *clientSet,
Config: *cfg,
NotificationEvents: &notificationEvents,
KubernetesClusterDomain: *kubernetesClusterDomain,
}).SetupWithManager(mgr); err != nil {
fatal(errors.Wrap(err, "unable to create Jenkins controller"), *debug)
}
if validateSecurityWarnings {
if err = (&v1alpha2.Jenkins{}).SetupWebhookWithManager(mgr); err != nil {
fatal(errors.Wrap(err, "unable to create Webhook"), *debug)
}
}
// +kubebuilder:scaffold:builder
if err := mgr.AddHealthzCheck("health", healthz.Ping); err != nil {
fatal(errors.Wrap(err, "unable to set up health check"), *debug)
}
if err := mgr.AddReadyzCheck("check", healthz.Ping); err != nil {
fatal(errors.Wrap(err, "unable to set up ready check"), *debug)
}
logger.Info("starting manager")
if err := mgr.Start(ctrl.SetupSignalHandler()); err != nil {
fatal(errors.Wrap(err, "problem running manager"), *debug)
}
}
func fatal(err error, debug bool) {
if debug {
logger.Error(nil, fmt.Sprintf("%+v", err))
} else {
logger.Error(nil, fmt.Sprintf("%s", err))
}
os.Exit(1)
}

277
cmd/manager/main.go Normal file
View File

@ -0,0 +1,277 @@
package main
import (
"context"
"flag"
"fmt"
"os"
"runtime"
"github.com/jenkinsci/kubernetes-operator/pkg/controller/jenkinsimage"
// Import all Kubernetes client auth plugins (e.g. Azure, GCP, OIDC, etc.)
_ "k8s.io/client-go/plugin/pkg/client/auth"
"github.com/jenkinsci/kubernetes-operator/pkg/apis"
"github.com/jenkinsci/kubernetes-operator/pkg/client"
"github.com/jenkinsci/kubernetes-operator/pkg/configuration/base/resources"
"github.com/jenkinsci/kubernetes-operator/pkg/constants"
"github.com/jenkinsci/kubernetes-operator/pkg/controller/jenkins"
"github.com/jenkinsci/kubernetes-operator/pkg/event"
"github.com/jenkinsci/kubernetes-operator/pkg/log"
"github.com/jenkinsci/kubernetes-operator/pkg/notifications"
e "github.com/jenkinsci/kubernetes-operator/pkg/notifications/event"
"github.com/jenkinsci/kubernetes-operator/version"
"github.com/operator-framework/operator-sdk/pkg/k8sutil"
kubemetrics "github.com/operator-framework/operator-sdk/pkg/kube-metrics"
"github.com/operator-framework/operator-sdk/pkg/leader"
"github.com/operator-framework/operator-sdk/pkg/log/zap"
"github.com/operator-framework/operator-sdk/pkg/metrics"
sdkVersion "github.com/operator-framework/operator-sdk/version"
"github.com/pkg/errors"
"github.com/spf13/pflag"
v1 "k8s.io/api/core/v1"
"k8s.io/apimachinery/pkg/runtime/schema"
"k8s.io/apimachinery/pkg/util/intstr"
"k8s.io/client-go/kubernetes"
_ "k8s.io/client-go/plugin/pkg/client/auth/gcp"
"k8s.io/client-go/rest"
"sigs.k8s.io/controller-runtime/pkg/client/config"
"sigs.k8s.io/controller-runtime/pkg/manager"
"sigs.k8s.io/controller-runtime/pkg/manager/signals"
)
// Change below variables to serve metrics on different host or port.
var (
metricsHost = "0.0.0.0"
metricsPort int32 = 8383
operatorMetricsPort int32 = 8686
)
var logger = log.Log.WithName("cmd")
func printInfo() {
logger.Info(fmt.Sprintf("Version: %s", version.Version))
logger.Info(fmt.Sprintf("Git commit: %s", version.GitCommit))
logger.Info(fmt.Sprintf("Go Version: %s", runtime.Version()))
logger.Info(fmt.Sprintf("Go OS/Arch: %s/%s", runtime.GOOS, runtime.GOARCH))
logger.Info(fmt.Sprintf("operator-sdk Version: %v", sdkVersion.Version))
}
func main() {
// Add the zap logger flag set to the CLI. The flag set must
// be added before calling pflag.Parse().
pflag.CommandLine.AddFlagSet(zap.FlagSet())
// Add flags registered by imported packages (e.g. glog and
// controller-runtime)
pflag.CommandLine.AddGoFlagSet(flag.CommandLine)
hostname := pflag.String("jenkins-api-hostname", "", "Hostname or IP of Jenkins API. It can be service name, node IP or localhost.")
port := pflag.Int("jenkins-api-port", 0, "The port on which Jenkins API is running. Note: If you want to use nodePort don't set this setting and --jenkins-api-use-nodeport must be true.")
useNodePort := pflag.Bool("jenkins-api-use-nodeport", false, "Connect to Jenkins API using the service nodePort instead of service port. If you want to set this as true - don't set --jenkins-api-port.")
debug := pflag.Bool("debug", false, "Set log level to debug")
kubernetesClusterDomain := pflag.String("cluster-domain", "cluster.local", "Use custom domain name instead of 'cluster.local'.")
pflag.Parse()
log.SetupLogger(*debug)
printInfo()
namespace, err := k8sutil.GetWatchNamespace()
if err != nil {
fatal(errors.Wrap(err, "failed to get watch namespace"), *debug)
}
logger.Info(fmt.Sprintf("Watch namespace: %v", namespace))
// get a config to talk to the apiserver
cfg, err := config.GetConfig()
if err != nil {
fatal(errors.Wrap(err, "failed to get config"), *debug)
}
ctx := context.TODO()
// Become the leader before proceeding
err = leader.Become(ctx, "jenkins-operator-lock")
if err != nil {
fatal(errors.Wrap(err, "failed to become leader"), *debug)
}
// Create a new Cmd to provide shared dependencies and start components
mgr, err := manager.New(cfg, manager.Options{
Namespace: namespace,
MetricsBindAddress: fmt.Sprintf("%s:%d", metricsHost, metricsPort),
})
if err != nil {
fatal(errors.Wrap(err, "failed to create manager"), *debug)
}
logger.Info("Registering Components.")
// setup Scheme for all resources
if err := apis.AddToScheme(mgr.GetScheme()); err != nil {
fatal(errors.Wrap(err, "failed to setup scheme"), *debug)
}
// setup events
events, err := event.New(cfg, constants.OperatorName)
if err != nil {
fatal(errors.Wrap(err, "failed to create manager"), *debug)
}
clientSet, err := kubernetes.NewForConfig(cfg)
if err != nil {
fatal(errors.Wrap(err, "failed to create Kubernetes client set"), *debug)
}
if resources.IsRouteAPIAvailable(clientSet) {
logger.Info("Route API found: Route creation will be performed")
}
c := make(chan e.Event)
go notifications.Listen(c, events, mgr.GetClient())
// validate jenkins API connection
jenkinsAPIConnectionSettings := client.JenkinsAPIConnectionSettings{Hostname: *hostname, Port: *port, UseNodePort: *useNodePort}
if err := jenkinsAPIConnectionSettings.Validate(); err != nil {
fatal(errors.Wrap(err, "invalid command line parameters"), *debug)
}
// validate kubernetes cluster domain
if *kubernetesClusterDomain == "" {
fatal(errors.Wrap(err, "Kubernetes cluster domain can't be empty"), *debug)
}
// setup Jenkins controller
if err := jenkins.Add(mgr, jenkinsAPIConnectionSettings, *kubernetesClusterDomain, *clientSet, *cfg, &c); err != nil {
fatal(errors.Wrap(err, "failed to setup controllers"), *debug)
}
// setup JenkinsImage controller
if err = jenkinsimage.Add(mgr); err != nil {
fatal(errors.Wrap(err, "failed to setup controllers"), *debug)
}
if err = serveCRMetrics(cfg); err != nil {
logger.V(log.VWarn).Info("Could not generate and serve custom resource metrics", "error", err.Error())
}
// Add to the below struct any other metrics ports you want to expose.
servicePorts := []v1.ServicePort{
{Port: metricsPort, Name: metrics.OperatorPortName, Protocol: v1.ProtocolTCP, TargetPort: intstr.IntOrString{Type: intstr.Int, IntVal: metricsPort}},
{Port: operatorMetricsPort, Name: metrics.CRPortName, Protocol: v1.ProtocolTCP, TargetPort: intstr.IntOrString{Type: intstr.Int, IntVal: operatorMetricsPort}},
}
// Create Service object to expose the metrics port(s).
service, err := metrics.CreateMetricsService(ctx, cfg, servicePorts)
if err != nil {
logger.V(log.VWarn).Info("Could not create metrics Service", "error", err.Error())
}
// CreateServiceMonitors will automatically create the prometheus-operator ServiceMonitor resources
// necessary to configure Prometheus to scrape metrics from this operator.
services := []*v1.Service{service}
_, err = metrics.CreateServiceMonitors(cfg, namespace, services)
if err != nil {
logger.V(log.VWarn).Info("Could not create ServiceMonitor object", "error", err.Error())
// If this operator is deployed to a cluster without the prometheus-operator running, it will return
// ErrServiceMonitorNotPresent, which can be used to safely skip ServiceMonitor creation.
if err == metrics.ErrServiceMonitorNotPresent {
logger.V(log.VWarn).Info("Install prometheus-operator in your cluster to create ServiceMonitor objects", "error", err.Error())
}
}
logger.Info("Starting the Cmd.")
// start the Cmd
if err := mgr.Start(signals.SetupSignalHandler()); err != nil {
fatal(errors.Wrap(err, "failed to start cmd"), *debug)
}
}
// serveCRMetrics gets the Operator/CustomResource GVKs and generates metrics based on those types.
// It serves those metrics on "http://metricsHost:operatorMetricsPort".
func serveCRMetrics(cfg *rest.Config) error {
// Below function returns filtered operator/CustomResource specific GVKs.
// For more control override the below GVK list with your own custom logic.
gvks, err := k8sutil.GetGVKsFromAddToScheme(apis.AddToScheme)
if err != nil {
return err
}
// We perform our custom GKV filtering on top of the one performed
// by operator-sdk code
filteredGVK := filterGKVsFromAddToScheme(gvks)
if err != nil {
return err
}
// Get the namespace the operator is currently deployed in.
operatorNs, err := k8sutil.GetOperatorNamespace()
if err != nil {
return err
}
// To generate metrics in other namespaces, add the values below.
ns := []string{operatorNs}
// Generate and serve custom resource specific metrics.
return kubemetrics.GenerateAndServeCRMetrics(cfg, ns, filteredGVK, metricsHost, operatorMetricsPort)
}
func filterGKVsFromAddToScheme(gvks []schema.GroupVersionKind) []schema.GroupVersionKind {
// We use gkvFilters to filter from the existing GKVs defined in the used
// runtime.Schema for the operator. The reason for that is that
// kube-metrics tries to list all of the defined Kinds in the schemas
// that are passed, including Kinds that the operator doesn't use and
// thus the role used the operator doesn't have them set and we don't want
// to set as they are not used by the operator.
// For the fields that the filters have we have defined the value '*' to
// specify any will be a match (accepted)
matchAnyValue := "*"
gvkFilters := []schema.GroupVersionKind{
// Kubernetes Resources
{Kind: "PersistentVolumeClaim", Version: matchAnyValue},
{Kind: "ServiceAccount", Version: matchAnyValue},
{Kind: "Secret", Version: matchAnyValue},
{Kind: "Pod", Version: matchAnyValue},
{Kind: "ConfigMap", Version: matchAnyValue},
{Kind: "Service", Version: matchAnyValue},
{Group: "apps", Kind: "Deployment", Version: matchAnyValue},
// Openshift Resources
{Group: "route.openshift.io", Kind: "Route", Version: matchAnyValue},
{Group: "image.openshift.io", Kind: "ImageStream", Version: matchAnyValue},
// Custom Resources
{Group: "jenkins.io", Kind: "Jenkins", Version: matchAnyValue},
{Group: "jenkins.io", Kind: "JenkinsImage", Version: matchAnyValue},
}
ownGVKs := []schema.GroupVersionKind{}
for _, gvk := range gvks {
for _, gvkFilter := range gvkFilters {
match := true
if gvkFilter.Kind == matchAnyValue && gvkFilter.Group == matchAnyValue && gvkFilter.Version == matchAnyValue {
logger.V(1).Info("gvkFilter should at least have one of its fields defined. Skipping...")
match = false
} else {
if gvkFilter.Kind != matchAnyValue && gvkFilter.Kind != gvk.Kind {
match = false
}
if gvkFilter.Group != matchAnyValue && gvkFilter.Group != gvk.Group {
match = false
}
if gvkFilter.Version != matchAnyValue && gvkFilter.Version != gvk.Version {
match = false
}
}
if match {
ownGVKs = append(ownGVKs, gvk)
}
}
}
return ownGVKs
}
func fatal(err error, debug bool) {
if debug {
logger.Error(nil, fmt.Sprintf("%+v", err))
} else {
logger.Error(nil, fmt.Sprintf("%s", err))
}
os.Exit(-1)
}

View File

@ -1,16 +1,15 @@
ALL_IN_ONE_DEPLOY_FILE_PREFIX="all-in-one" # Setup variables for the Makefile
API_VERSION_NEXT="v1alpha3" NAME=kubernetes-operator
API_VERSION="v1alpha2" OPERATOR_SDK_VERSION=0.17.0
CLUSTER_DOMAIN="cluster.local" GO_VERSION=1.14.2
GEN_CRD_API="gen-crd-api-reference-docs" PKG=github.com/jenkinsci/kubernetes-operator
GO_VERSION="1.22" DOCKER_ORGANIZATION=virtuslab
HELM_VERSION="3.12.3" DOCKER_REGISTRY=jenkins-operator
IMAGE_PULL_MODE="local" NAMESPACE=default
KIND_CLUSTER_NAME="jenkins" API_VERSION=v1alpha2
LATEST_LTS_VERSION="2.528.1" API_VERSION_NEXT=v1alpha3
NAME="kubernetes-operator" ALL_IN_ONE_DEPLOY_FILE_PREFIX=all-in-one
NAMESPACE="default" GEN_CRD_API=gen-crd-api-reference-docs
OPERATOR_SDK_VERSION="1.35.0" IMAGE_PULL_MODE=local
PKG="github.com/jenkinsci/kubernetes-operator" HELM_VERSION=3.1.2
QUAY_ORGANIZATION="jenkins-kubernetes-operator" CLUSTER_DOMAIN=cluster.local
QUAY_REGISTRY="operator"

Some files were not shown because too many files have changed in this diff Show More