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

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

@ -3,3 +3,4 @@
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.
[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
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
We use GitHub to host code, to track issues and feature requests, as well as accept pull requests.
## Commit Messages
## We Use GitHub Flow, So All Code Changes Happen Through Pull Requests
Pull requests are the best way to propose changes to the codebase (we use GitHub Flow). We actively welcome your pull requests:
All commit messages should follow
[these best practices](https://chris.beams.io/posts/git-commit/), specifically:
- Create feature requests and discuss the scope.
- Fork the repo and create your branch from master.
- If youve added code that should be tested, add tests.
- If youve changed APIs or design, update the documentation.
- Create a draft pull request (not yet ready for review, triggers CI build).
- Ensure the e2e tests pass (wait for GitHub status checks).
- Mark that pull request as ready for review.
- Start with a subject line
- Contain a body that explains _why_ you're making the change you're making
- Reference an issue number one exists, closing it if applicable (with text such
as
["Fixes #245" or "Closes #111"](https://help.github.com/articles/closing-issues-using-keywords/))
## Quality Standards
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.
Not sure what to put? Try to Include:
### 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.
- 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/)
## Coding standards
### Large Architectural Changes
In case of large change which requires significant engineering effort and introduces side effects, we suggest writing a design proposal document first.
### Go
Design proposal is simply a document that states what you propose to do including:
- 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.
- [Go code review comments](https://github.com/golang/go/wiki/CodeReviewComments)

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"]

601
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
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)
$(NAME): $(wildcard *.go) $(wildcard */*.go) VERSION.txt
@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
static: ## Builds a static executable
@ -66,26 +136,22 @@ fmt: ## Verifies all files have been `gofmt`ed
@go fmt $(PACKAGES)
.PHONY: lint
HAS_GOLINT := $(shell which $(PROJECT_DIR)/bin/golangci-lint)
HAS_GOLINT := $(shell which golangci-lint)
lint: ## Verifies `golint` passes
@echo "+ $@"
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
@bin/golangci-lint run
.PHONY: lint-fix
lint-fix: golangci-lint ## Run golangci-lint linter and perform fixes
@bin/golangci-lint run --fix
@golangci-lint run
.PHONY: goimports
HAS_GOIMPORTS := $(shell which $(PROJECT_DIR)/bin/goimports)
HAS_GOIMPORTS := $(shell which goimports)
goimports: ## Verifies `goimports` passes
@echo "+ $@"
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
@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
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)
.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 "+ $@"
RUNNING_TESTS=1 go test -parallel=1 "./test/e2e/" -ginkgo.v -tags "$(BUILDTAGS) cgo" -v -timeout 60m -run "$(E2E_TEST_SELECTOR)" \
-jenkins-api-hostname=$(JENKINS_API_HOSTNAME) -jenkins-api-port=$(JENKINS_API_PORT) -jenkins-api-use-nodeport=$(JENKINS_API_USE_NODEPORT) $(E2E_TEST_ARGS)
.PHONY: jenkins-kind-load
jenkins-kind-load: ## Load the jenkins lts version in kind to speed up tests
@echo "+ $@"
docker pull jenkins/jenkins:$(LATEST_LTS_VERSION)
kind load docker-image jenkins/jenkins:$(LATEST_LTS_VERSION) --name $(KIND_CLUSTER_NAME)
## Backup Section
.PHONY: backup-kind-load
backup-kind-load: ## Load latest backup image in the cluster
@echo "+ $@"
make -C backup/pvc backup-kind-load
## 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
@echo "Docker image: $(DOCKER_REGISTRY):$(GITCOMMIT)"
ifeq ($(KUBERNETES_PROVIDER),minikube)
kubectl config use-context $(KUBECTL_CONTEXT)
endif
ifeq ($(KUBERNETES_PROVIDER),crc)
oc project $(CRC_OC_PROJECT)
endif
cp deploy/service_account.yaml deploy/namespace-init.yaml
cat deploy/role.yaml >> deploy/namespace-init.yaml
cat deploy/role_binding.yaml >> deploy/namespace-init.yaml
cat deploy/operator.yaml >> deploy/namespace-init.yaml
ifeq ($(OSFLAG), LINUX)
ifeq ($(IMAGE_PULL_MODE), remote)
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
mkdir -p $(PROJECT_DIR)/bin
test -L $(PROJECT_DIR)/bin/helm || ln -sf $(shell command -v helm) $(PROJECT_DIR)/bin/helm
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-lint
helm-lint: helm
bin/helm lint chart/jenkins-operator
ifeq ($(OSFLAG), OSX)
ifeq ($(IMAGE_PULL_MODE), remote)
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
helm-release-latest: helm
mkdir -p /tmp/jenkins-operator-charts
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
RUNNING_TESTS=1 go test -parallel=1 "./test/e2e/" -tags "$(BUILDTAGS) cgo" -v -timeout 60m -run "$(E2E_TEST_SELECTOR)" \
-root=$(CURRENT_DIRECTORY) -kubeconfig=$(HOME)/.kube/config -globalMan deploy/crds/jenkins_$(API_VERSION)_jenkins_crd.yaml \
-namespacedMan deploy/namespace-init.yaml $(TEST_ARGS)
.PHONY: vet
vet: ## Verifies `go vet` passes
@ -157,25 +207,23 @@ vet: ## Verifies `go vet` passes
@go vet $(PACKAGES)
.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
@echo "+ $@"
ifndef HAS_STATICCHECK
$(eval TMP_DIR := $(shell mktemp -d))
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 $(TMP_DIR)/staticcheck_$(PLATFORM)_amd64.tar.gz -C $(TMP_DIR)
mkdir -p $(PROJECT_DIR)/bin
mv $(TMP_DIR)/staticcheck/staticcheck $(PROJECT_DIR)/bin
rm -rf $(TMP_DIR)
wget -O staticcheck_$(PLATFORM)_amd64.tar.gz https://github.com/dominikh/go-tools/releases/download/2020.1.3/staticcheck_$(PLATFORM)_amd64.tar.gz
tar zxvf staticcheck_$(PLATFORM)_amd64.tar.gz
mkdir -p $(GOPATH)/bin
mv staticcheck/staticcheck $(GOPATH)/bin
endif
@$(PROJECT_DIR)/bin/staticcheck $(PACKAGES)
@staticcheck $(PACKAGES)
.PHONY: cover
cover: ## Runs go test with coverage
@echo "" > coverage.txt
@for d in $(PACKAGES); do \
ENVTEST_K8S_VERSION = 1.26
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"; \
IMG_RUNNING_TESTS=1 go test -race -coverprofile=profile.out -covermode=atomic "$$d"; \
if [ -f profile.out ]; then \
cat profile.out >> coverage.txt; \
rm profile.out; \
@ -191,62 +239,21 @@ install: ## Installs the executable
@echo "+ $@"
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
run: export WATCH_NAMESPACE = $(NAMESPACE)
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 "+ $@"
ifeq ($(KUBERNETES_PROVIDER),kind)
ifeq ($(KUBERNETES_PROVIDER),minikube)
kubectl config use-context $(KUBECTL_CONTEXT)
endif
ifeq ($(KUBERNETES_PROVIDER),crc)
oc project $(CRC_OC_PROJECT)
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"
bin/manager $(OPERATOR_ARGS)
build/_output/bin/jenkins-operator $(OPERATOR_ARGS)
.PHONY: clean
clean: ## Cleanup any build binaries or packages
@ -301,66 +308,46 @@ endif
container-runtime-login: ## Log in into the Docker repository
@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
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
container-runtime-images: ## List all local containers
@echo "+ $@"
$(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
container-runtime-push: check-env deepcopy-gen ## Push the container
container-runtime-push: ## Push the container
@echo "+ $@"
$(call buildx-create-command)
$(call container-runtime-push-command,$(BUILD_TAG))
$(CONTAINER_RUNTIME_COMMAND) tag $(DOCKER_REGISTRY):$(GITCOMMIT) $(DOCKER_ORGANIZATION)/$(DOCKER_REGISTRY):$(BUILD_TAG) $(CONTAINER_RUNTIME_EXTRA_ARGS)
$(CONTAINER_RUNTIME_COMMAND) push $(DOCKER_ORGANIZATION)/$(DOCKER_REGISTRY):$(BUILD_TAG) $(CONTAINER_RUNTIME_EXTRA_ARGS)
.PHONY: container-runtime-snapshot-push
container-runtime-snapshot-push: check-env deepcopy-gen
container-runtime-snapshot-push:
@echo "+ $@"
$(call buildx-create-command)
$(call container-runtime-push-command,$(GITCOMMIT))
$(CONTAINER_RUNTIME_COMMAND) tag $(DOCKER_REGISTRY):$(GITCOMMIT) $(DOCKER_ORGANIZATION)/$(DOCKER_REGISTRY):$(GITCOMMIT) $(CONTAINER_RUNTIME_EXTRA_ARGS)
$(CONTAINER_RUNTIME_COMMAND) push $(DOCKER_ORGANIZATION)/$(DOCKER_REGISTRY):$(GITCOMMIT) $(CONTAINER_RUNTIME_EXTRA_ARGS)
.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 "+ $@"
$(call buildx-create-command)
$(call container-runtime-push-command,$(VERSION_TAG))
$(CONTAINER_RUNTIME_COMMAND) tag $(DOCKER_REGISTRY):$(GITCOMMIT) $(DOCKER_ORGANIZATION)/$(DOCKER_REGISTRY):$(VERSION_TAG) $(CONTAINER_RUNTIME_EXTRA_ARGS)
$(CONTAINER_RUNTIME_COMMAND) push $(DOCKER_ORGANIZATION)/$(DOCKER_REGISTRY):$(VERSION_TAG) $(CONTAINER_RUNTIME_EXTRA_ARGS)
.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 "+ $@"
$(call buildx-create-command)
$(call container-runtime-push-command,$(LATEST_TAG))
$(CONTAINER_RUNTIME_COMMAND) tag $(DOCKER_REGISTRY):$(GITCOMMIT) $(DOCKER_ORGANIZATION)/$(DOCKER_REGISTRY):$(LATEST_TAG) $(CONTAINER_RUNTIME_EXTRA_ARGS)
$(CONTAINER_RUNTIME_COMMAND) push $(DOCKER_ORGANIZATION)/$(DOCKER_REGISTRY):$(LATEST_TAG) $(CONTAINER_RUNTIME_EXTRA_ARGS)
.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 "+ $@"
# if this session isn't interactive, then we don't want to allocate a
@ -376,29 +363,52 @@ container-runtime-run: ## Run the container in docker, you can use EXTRA_ARGS
@echo "+ $@"
$(CONTAINER_RUNTIME_COMMAND) run $(CONTAINER_RUNTIME_EXTRA_ARGS) --rm -i $(DOCKER_FLAGS) \
--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
crc-run: export WATCH_NAMESPACE = $(NAMESPACE)
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 "+ $@"
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
deepcopy-gen: generate ## Generate deepcopy golang code
deepcopy-gen: ## Generate deepcopy golang code
@echo "+ $@"
operator-sdk generate k8s
.PHONY: scheme-doc-gen
HAS_GEN_CRD_API_REFERENCE_DOCS := $(shell ls gen-crd-api-reference-docs 2> /dev/null)
scheme-doc-gen: ## Generate Jenkins CRD scheme doc
@echo "+ $@"
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)
@tar -C $(GEN_CRD_API) -zxf $(GEN_CRD_API)_$(PLATFORM)_amd64.tar.gz
@rm $(GEN_CRD_API)_$(PLATFORM)_amd64.tar.gz
@tar -C $(GEN_CRD_API) -zxf $(GEN_CRD_API)_linux_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
$(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
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')
endif
.PHONY: kind-setup
kind-setup: ## Setup kind cluster
.PHONY: minikube-start
minikube-start: check-minikube ## Start minikube
@echo "+ $@"
kind create cluster --config kind-cluster.yaml --name $(KIND_CLUSTER_NAME)
.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
@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: crc-start
crc-start: check-crc ## Start CodeReady Containers Kubernetes cluster
@echo "+ $@"
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
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 "+ $@"
$(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 $(NEW_VERSION) > VERSION.txt
@echo "Updating version from $(VERSION) to $(NEW_VERSION) in README.md"
sed -i.bak 's/$(VERSION)/$(NEW_VERSION)/g' README.md
sed -i.bak 's/$(VERSION)/$(NEW_VERSION)/g' config/manager/manager.yaml
sed -i.bak 's/$(VERSION)/$(NEW_VERSION)/g' deploy/$(ALL_IN_ONE_DEPLOY_FILE_PREFIX)-$(API_VERSION).yaml
rm */*/**.bak
rm */**.bak
rm *.bak
cp config/service_account.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
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
sed -i s/$(VERSION)/$(NEW_VERSION)/g README.md
sed -i s/$(VERSION)/$(NEW_VERSION)/g deploy/operator.yaml
sed -i s/$(VERSION)/$(NEW_VERSION)/g deploy/$(ALL_IN_ONE_DEPLOY_FILE_PREFIX)-$(API_VERSION).yaml
cp deploy/service_account.yaml deploy/$(ALL_IN_ONE_DEPLOY_FILE_PREFIX)-$(API_VERSION).yaml
cat deploy/role.yaml >> deploy/$(ALL_IN_ONE_DEPLOY_FILE_PREFIX)-$(API_VERSION).yaml
cat deploy/role_binding.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
git add VERSION.txt README.md deploy/operator.yaml deploy/$(ALL_IN_ONE_DEPLOY_FILE_PREFIX)-$(API_VERSION).yaml
git commit -vaem "Bump version to $(NEW_VERSION)"
@echo "Run make tag to create and push the tag for new version $(NEW_VERSION)"
.PHONY: tag
tag: ## Create a new git tag to prepare to build a release
@echo "+ $@"
git tag -a $(VERSION) -m "$(VERSION)"
git tag -s -a $(VERSION) -m "$(VERSION)"
git push origin $(VERSION)
.PHONY: help
@ -497,187 +471,30 @@ ifneq ($(GITUNTRACKEDCHANGES),)
endif
ifneq ($(GITIGNOREDBUTTRACKEDCHANGES),)
@echo "Ignored but tracked files:"
@git ls-files -i -o --exclude-standard
@git ls-files -i --exclude-standard
@echo
endif
@echo "Dependencies:"
go mod vendor -v
@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
HUGO_PATH = $(shell pwd)/bin/hugo
HUGO_VERSION = v0.99.1
HAS_HUGO := $(shell $(HUGO_PATH)/hugo version 2>&- | grep $(HUGO_VERSION))
.PHONY: hugo
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: helm-deploy
helm-deploy: helm-package
@echo "+ $@"
helm repo index chart/ --url https://raw.githubusercontent.com/jenkinsci/kubernetes-operator/master/chart/jenkins-operator/
cd chart/ && mv jenkins-operator-*.tgz jenkins-operator
.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 "+ $@"
rm -rf docs || echo "Cannot remove docs dir, ignoring"
cd website && npm install
$(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
hugo -s website -d ../docs

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
[![Version](https://img.shields.io/badge/version-v0.8.0-brightgreen.svg)](https://github.com/jenkinsci/kubernetes-operator/releases/tag/v0.8.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)
[![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://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)
[![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="">
<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>
![logo](/assets/jenkins_gopher_wide.png)
## What's the Jenkins Operator?
@ -41,10 +36,11 @@ The main reason why we decided to implement the **Jenkins Operator** is the fact
We want to make Jenkins more robust, suitable for dynamic and multi-tenant environments.
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/)
- [better configuration as code](https://jenkinsci.github.io/kubernetes-operator/docs/getting-started/latest/customizing-jenkins/)
- [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/customization/)
- [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
- handle graceful shutdown properly
- 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.
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/)
3. [Security](https://jenkinsci.github.io/kubernetes-operator/docs/security/)
4. [Troubleshooting](https://jenkinsci.github.io/kubernetes-operator/docs/troubleshooting/)
3. [How it works](https://jenkinsci.github.io/kubernetes-operator/docs/how-it-works/)
4. [Security](https://jenkinsci.github.io/kubernetes-operator/docs/security/)
5. [Developer Guide](https://jenkinsci.github.io/kubernetes-operator/docs/developer-guide/)
6. [FAQ](https://jenkinsci.github.io/kubernetes-operator/docs/faq/)
7. [Jenkins Custom Resource Definition Schema](https://jenkinsci.github.io/kubernetes-operator/docs/getting-started/latest/schema/)
5. [Jenkins Custom Resource Definition scheme](https://jenkinsci.github.io/kubernetes-operator/docs/getting-started/latest/scheme/)
## Common Issues and Workarounds
- Multibranch Pipelines and Backup Issues: https://github.com/jenkinsci/kubernetes-operator/issues/104#issuecomment-554289768
## 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!
## 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.
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 :)
## Contribution
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.
Feel free to file [issues](https://github.com/jenkinsci/kubernetes-operator/issues) or [pull requests](https://github.com/jenkinsci/kubernetes-operator/pulls).
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 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

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
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"
FROM debian:buster-slim
ARG UID
ARG GID
ENV USER=user
RUN apt update \
&& apt install -y procps zstd \
&& rm -rf /var/lib/apt/lists/* \
&& addgroup --gid "$GID" "$USER" && \
RUN addgroup --gid "$GID" "$USER" && \
adduser \
--disabled-password \
--gecos "" \
@ -24,9 +13,9 @@ RUN apt update \
--uid "$UID" \
"$USER"
COPY bin/*.sh /home/user/bin/
RUN chmod -R a+rx /home/user
WORKDIR /home/user/bin
COPY bin .
RUN chmod +x *.sh
USER user
CMD ./run.sh

View File

@ -11,11 +11,10 @@ include $(config)
PREFIX?=$(shell pwd)
VERSION := $(shell cat VERSION.txt)
PROJECT_DIR := $(shell dirname $(abspath $(lastword $(MAKEFILE_LIST))))
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 -o --exclude-standard)
GITIGNOREDBUTTRACKEDCHANGES := $(shell git ls-files -i --exclude-standard)
ifneq ($(GITUNTRACKEDCHANGES),)
GITCOMMIT := $(GITCOMMIT)-dirty
endif
@ -58,7 +57,7 @@ spring-clean: ## Cleanup git ignored files (interactive)
git clean -Xdi
.PHONY: checkmake
HAS_CHECKMAKE := $(shell command -v checkmake)
HAS_CHECKMAKE := $(shell which checkmake)
checkmake: ## Check this Makefile
@echo "+ $@"
ifndef HAS_CHECKMAKE
@ -68,12 +67,12 @@ endif
define e2e
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
.PHONY: docker-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 "+ $@"
$(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
@echo "+ $@"
.PHONY: docker-build-e2e
docker-build-e2e: UID=1001
docker-build-e2e: GID=1001
docker-build-e2e: docker-build
.PHONY: docker-build
docker-build: check-env ## Build the container
@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
docker-images: ## List all local containers
@ -99,31 +93,25 @@ docker-images: ## List all local containers
.PHONY: docker-push
docker-push: docker-build ## Push the container
@echo "+ $@"
docker tag quay.io/$(QUAY_ORGANIZATION)/$(QUAY_REGISTRY)-$(NAME):$(GITCOMMIT) quay.io/$(QUAY_ORGANIZATION)/$(QUAY_REGISTRY)-$(NAME):$(BUILD_TAG)
docker push quay.io/$(QUAY_ORGANIZATION)/$(QUAY_REGISTRY)-$(NAME):$(BUILD_TAG)
docker tag $(DOCKER_REGISTRY)-$(NAME):$(GITCOMMIT) $(DOCKER_ORGANIZATION)/$(DOCKER_REGISTRY)-$(NAME):$(BUILD_TAG)
docker push $(DOCKER_ORGANIZATION)/$(DOCKER_REGISTRY)-$(NAME):$(BUILD_TAG)
.PHONY: docker-release-version
docker-release-version: docker-build ## Release image with version tag (in addition to build tag)
@echo "+ $@"
docker tag quay.io/$(QUAY_ORGANIZATION)/$(QUAY_REGISTRY)-$(NAME):$(GITCOMMIT) quay.io/$(QUAY_ORGANIZATION)/$(QUAY_REGISTRY)-$(NAME):$(VERSION_TAG)
docker push quay.io/$(QUAY_ORGANIZATION)/$(QUAY_REGISTRY)-$(NAME):$(VERSION_TAG)
docker tag $(DOCKER_REGISTRY)-$(NAME):$(GITCOMMIT) $(DOCKER_ORGANIZATION)/$(DOCKER_REGISTRY)-$(NAME):$(VERSION_TAG)
docker push $(DOCKER_ORGANIZATION)/$(DOCKER_REGISTRY)-$(NAME):$(VERSION_TAG)
.PHONY: docker-release-latest
docker-release-latest: docker-build ## Release image with latest tags (in addition to build tag)
@echo "+ $@"
docker tag quay.io/$(QUAY_ORGANIZATION)/$(QUAY_REGISTRY)-$(NAME):$(GITCOMMIT) quay.io/$(QUAY_ORGANIZATION)/$(QUAY_REGISTRY)-$(NAME):$(LATEST_TAG)
docker push quay.io/$(QUAY_ORGANIZATION)/$(QUAY_REGISTRY)-$(NAME):$(LATEST_TAG)
docker tag $(DOCKER_REGISTRY)-$(NAME):$(GITCOMMIT) $(DOCKER_ORGANIZATION)/$(DOCKER_REGISTRY)-$(NAME):$(LATEST_TAG)
docker push $(DOCKER_ORGANIZATION)/$(DOCKER_REGISTRY)-$(NAME):$(LATEST_TAG)
.PHONY: docker-release
docker-release: docker-release-version docker-release-latest ## Release image with version and latest tags (in addition to build tag)
@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
# TTY, which would fail, but if it is interactive, we do want to attach
# so that the user can send e.g. ^C through.
@ -133,30 +121,20 @@ ifeq ($(INTERACTIVE), 1)
endif
.PHONY: docker-run
docker-run: docker-build
docker-run: docker-build ## Run the container in docker, you can use EXTRA_ARGS
@echo "+ $@"
docker run --rm -i $(DOCKER_FLAGS) \
quay.io/$(QUAY_ORGANIZATION)/$(QUAY_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
$(DOCKER_REGISTRY)-$(NAME):$(GITCOMMIT) $(ARGS)
.PHONY: bump-version
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 "+ $@"
$(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 $(NEW_VERSION) > 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
tag: ## Create a new git tag to prepare to build a release
@ -180,7 +158,7 @@ ifneq ($(GITUNTRACKEDCHANGES),)
endif
ifneq ($(GITIGNOREDBUTTRACKEDCHANGES),)
@echo "Ignored but tracked files:"
@git ls-files -i -c --exclude-standard
@git ls-files -i --exclude-standard
@echo
endif
@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
set -eo pipefail
source "$(dirname "$0")/utils.sh"
[[ ! $# -eq 1 ]] && _log "ERROR" "Usage: $0 BACKUP_NUMBER" && exit 1
[[ -z "${BACKUP_DIR}" ]] && _log "ERROR" "Required 'BACKUP_DIR' env not set" && exit 1
[[ -z "${JENKINS_HOME}" ]] && _log "ERROR" "Required 'JENKINS_HOME' env not set" && exit 1
BACKUP_RETRY_COUNT=${BACKUP_RETRY_COUNT:-3}
BACKUP_RETRY_INTERVAL=${BACKUP_RETRY_INTERVAL:-60}
BACKUP_NUMBER=$1
TRAP_FILE="/tmp/_backup_${BACKUP_NUMBER}_is_running"
[[ ! $# -eq 1 ]] && echo "Usage: $0 backup_number" && exit 1;
[[ -z "${BACKUP_DIR}" ]] && echo "Required 'BACKUP_DIR' env not set" && exit 1;
[[ -z "${JENKINS_HOME}" ]] && echo "Required 'JENKINS_HOME' env not set" && exit 1;
BACKUP_TMP_DIR=$(mktemp -d)
# --> Check if another backup process is running (operator restart/crash)
for ((i=0; i<BACKUP_RETRY_COUNT; i++)); do
[[ ! -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
backup_number=$1
echo "Running backup"
_log "INFO" "[backup] running backup ${BACKUP_NUMBER}"
touch "${TRAP_FILE}"
# 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-
# config.xml in a job directory is a config file that shouldnt be backed up
# config.xml in child directores is state that should. For example-
# branches/myorg/branches/myrepo/branches/master/config.xml should be retained while
# branches/myorg/config.xml should not
tar --zstd -C "${JENKINS_HOME}" -cf "${BACKUP_TMP_DIR}/${BACKUP_NUMBER}.tar.zstd" \
--exclude jobs/*/workspace* \
--no-wildcards-match-slash --anchored \
--ignore-failed-read \
--exclude jobs/*/config.xml -c jobs || ret=$?
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 && \
mv ${BACKUP_TMP_DIR}/${backup_number}.tar.gz ${BACKUP_DIR}/${backup_number}.tar.gz
if [[ "$ret" -eq 0 ]]; then
_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
[[ ! -s ${BACKUP_DIR}/${backup_number}.tar.gz ]] && echo "backup file '${BACKUP_DIR}/${backup_number}.tar.gz' is empty" && exit 1;
mv "${BACKUP_TMP_DIR}/${BACKUP_NUMBER}.tar.zstd" "${BACKUP_DIR}/${BACKUP_NUMBER}.tar.zstd"
_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"
echo Done
exit 0

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

@ -1,39 +1,12 @@
#!/usr/bin/env bash
set -eo pipefail
source "$(dirname "$0")/utils.sh"
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 ]
}
[[ -z "${BACKUP_DIR}" ]] && echo "Required 'BACKUP_DIR' env not set" && exit 1
[[ -z "${BACKUP_DIR}" ]] && { _log "ERROR" "Required 'BACKUP_DIR' env not set"; exit 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)
latest=$(find ${BACKUP_DIR} -name '*.tar.gz' -exec basename {} \; | sort -g | tail -n 1)
if [[ "${latest}" == "" ]]; then
_log "Could not get the latest backup."
echo "-1"
else
echo "${latest%%.*}"

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

@ -1,57 +1,15 @@
#!/usr/bin/env bash
set -eo pipefail
source "$(dirname "$0")/utils.sh"
[[ ! $# -eq 1 ]] && _log "ERROR" "Usage: $0 <backup number>" && exit 1
[[ -z "${BACKUP_DIR}" ]] && _log "ERROR" "Required 'BACKUP_DIR' env not set" && exit 1
[[ -z "${JENKINS_HOME}" ]] && _log "ERROR" "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}
[[ ! $# -eq 1 ]] && echo "Usage: $0 backup_number" && exit 1
[[ -z "${BACKUP_DIR}" ]] && echo "Required 'BACKUP_DIR' env not set" && exit 1;
[[ -z "${JENKINS_HOME}" ]] && echo "Required 'JENKINS_HOME' env not set" && exit 1;
# --> Check if another restore process is running (operator restart/crash)
TRAP_FILE="/tmp/_restore_${BACKUP_NUMBER}_is_running"
trap "rm -f ${TRAP_FILE}" SIGINT SIGTERM
backup_number=$1
echo "Running restore backup"
for ((i=0; i<RESTORE_RETRY_COUNT; i++)); do
[[ ! -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
tar -C ${JENKINS_HOME} -zxf "${BACKUP_DIR}/${backup_number}.tar.gz"
_log "INFO" "[restore] restore backup with backup number #${BACKUP_NUMBER}"
touch "${TRAP_FILE}"
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"
echo Done
exit 0

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

@ -1,72 +1,15 @@
#!/usr/bin/env bash
set -eo pipefail
source "$(dirname "$0")/utils.sh"
# Use 60 as default in case BACKUP_CLEANUP_INTERVAL did not set
BACKUP_CLEANUP_INTERVAL=${BACKUP_CLEANUP_INTERVAL:=60}
# 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
[[ -z "${BACKUP_DIR}" ]] && echo "Required 'BACKUP_DIR' env not set" && exit 1;
[[ -z "${JENKINS_HOME}" ]] && echo "Required 'JENKINS_HOME' env not set" && exit 1;
while true;
do
sleep "$BACKUP_CLEANUP_INTERVAL"
if [[ -n "${BACKUP_COUNT}" ]]; then
exceeding_backups=$(find_exceeding_backups "${BACKUP_DIR}" "${BACKUP_COUNT}")
if [[ -n "$exceeding_backups" ]]; then
_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
sleep 10
if [[ ! -z "${BACKUP_COUNT}" ]]; then
echo "Trimming to only ${BACKUP_COUNT} recent backups in preparation for new backup"
find ${BACKUP_DIR} -name '*.tar.gz' -exec basename {} \; | sort -gr | tail -n +$((BACKUP_COUNT +1)) | xargs -I '{}' rm ${BACKUP_DIR}/'{}'
fi
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
NAME=pvc
QUAY_ORGANIZATION=jenkins-kubernetes-operator
QUAY_REGISTRY=backup
DOCKER_ORGANIZATION=virtuslab
DOCKER_REGISTRY=jenkins-operator-backup
UID=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
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;
docker exec ${cid} /bin/bash -c "JENKINS_HOME=${RESTORE_FOLDER};/home/user/bin/restore.sh ${backup_number}"

View File

@ -19,20 +19,17 @@ mkdir -p ${BACKUP_DIR}
mkdir -p ${JENKINS_HOME}
mkdir -p ${BACKUP_DIR}/lost+found
touch ${BACKUP_DIR}/1.tar.zstd
touch ${BACKUP_DIR}/2.tar.zstd
touch ${BACKUP_DIR}/3.tar.zstd
touch ${BACKUP_DIR}/4.tar.zstd
touch ${BACKUP_DIR}/5.tar.zstd
touch ${BACKUP_DIR}/6.tar.zstd
touch ${BACKUP_DIR}/7.tar.zstd
touch ${BACKUP_DIR}/8.tar.zstd
touch ${BACKUP_DIR}/9.tar.zstd
touch ${BACKUP_DIR}/10.tar.zstd
touch ${BACKUP_DIR}/11.tar.zstd
# Emulate backup creation
BACKUP_TMP_DIR=$(mktemp -d --tmpdir="${BACKUP_DIR}")
touch ${BACKUP_TMP_DIR}/12.tar.zstd
touch ${BACKUP_DIR}/1.tar.gz
touch ${BACKUP_DIR}/2.tar.gz
touch ${BACKUP_DIR}/3.tar.gz
touch ${BACKUP_DIR}/4.tar.gz
touch ${BACKUP_DIR}/5.tar.gz
touch ${BACKUP_DIR}/6.tar.gz
touch ${BACKUP_DIR}/7.tar.gz
touch ${BACKUP_DIR}/8.tar.gz
touch ${BACKUP_DIR}/9.tar.gz
touch ${BACKUP_DIR}/10.tar.gz
touch ${BACKUP_DIR}/11.tar.gz
# 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})"
@ -41,20 +38,10 @@ echo "Docker container ID '${cid}'"
# Remove test directory and container afterwards
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")
rm ${BACKUP_DIR}/*.tar.zstd
echo "Try to get latest against one in progress"
rm ${BACKUP_DIR}/*.tar.gz
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
docker logs ${cid}
ls -la ${BACKUP_DIR}
@ -68,9 +55,5 @@ if [[ ! "${empty_latest}" == "-1" ]]; then
echo "Latest backup number should be '-1' but is '${empty_latest}'"
exit 1
fi
if [[ ! "${empty_dir_latest}" == "-1" ]]; then
echo "Latest backup number should be '-1' but is '${empty_dir_latest}'"
exit 1
fi
echo PASS

View File

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

View File

@ -1,8 +1,6 @@
#!/bin/bash
set -eo pipefail
echo "Running limit_backup_count_no_backups e2e test..."
[[ "${DEBUG}" ]] && set -x
# set current working directory to the directory of the script
@ -21,7 +19,7 @@ mkdir -p ${BACKUP_DIR}
mkdir -p ${JENKINS_HOME}
# 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}'"
# 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
echo 'Checking if container is running'
sleep 3
set +e
sleep 11
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 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
entries:
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
appVersion: 0.4.0
created: "2020-07-19T17:26:48.811339047+02:00"
@ -416,4 +208,4 @@ entries:
urls:
- https://raw.githubusercontent.com/jenkinsci/kubernetes-operator/master/chart/jenkins-operator/jenkins-operator-0.0.1.tgz
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/
*.tmproj
.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
appVersion: "0.9.0-beta1"
appVersion: "0.4.0"
description: Kubernetes native operator which fully manages Jenkins on Kubernetes
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
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 }}
spec:
configurationAsCode:
{{- if .Values.jenkins.configuration.configurationAsCode }}
configurations:
{{ range .Values.jenkins.configuration.configurationAsCode }}
{{- range .Values.jenkins.configuration.configurationAsCode }}
- name: {{ .configMapName }}
{{- end }}
{{- if .Values.jenkins.configuration.configurationAsCode }}
secret:
{{- if .Values.jenkins.configuration.secretRefName }}
name: {{ .Values.jenkins.configuration.secretRefName }}
{{- else if .Values.jenkins.configuration.secretData }}
name: jenkins-{{ .Values.jenkins.name }}
{{- else }}
name: ""
{{- end }}
{{- else }}
configurations: []
secret:
name: ""
{{- end }}
groovyScripts:
{{- if .Values.jenkins.configuration.groovyScripts }}
configurations:
{{- range .Values.jenkins.configuration.groovyScripts }}
- name: {{ .configMapName }}
{{- end }}
{{- if .Values.jenkins.configuration.groovyScripts }}
secret:
{{- if .Values.jenkins.configuration.secretRefName }}
name: {{ .Values.jenkins.configuration.secretRefName }}
{{- else if .Values.jenkins.configuration.secretData }}
name: jenkins-{{ .Values.jenkins.name }}
{{- else }}
name: ""
{{- end }}
{{- else }}
configurations: []
secret:
name: ""
{{- end }}
jenkinsAPISettings:
authorizationStrategy: {{ .Values.jenkins.authorizationStrategy }}
{{- if .Values.jenkins.backup.enabled }}
backup:
containerName: {{ .Values.jenkins.backup.containerName }}
@ -65,13 +51,6 @@ spec:
{{- with .Values.jenkins.backup.restoreCommand }}
command: {{ toYaml . | nindent 8 }}
{{- end }}
{{- if .Values.jenkins.backup.getLatestAction }}
getLatestAction:
exec:
{{- with .Values.jenkins.backup.getLatestAction }}
command: {{ toYaml . | nindent 8 }}
{{- end }}
{{- end }}
{{- if .Values.jenkins.backup.recoveryOnce }}
recoveryOnce: {{ .Values.jenkins.backup.recoveryOnce }}
{{- end }}
@ -85,19 +64,10 @@ spec:
{{- with .Values.jenkins.notifications }}
notifications: {{ toYaml . | nindent 4 }}
{{- end }}
{{- with .Values.jenkins.serviceAccount }}
serviceAccount: {{ toYaml . | nindent 4 }}
{{- end }}
master:
{{- with .Values.jenkins.labels }}
labels: {{ toYaml . | nindent 6 }}
{{- end }}
{{- with .Values.jenkins.nodeSelector }}
nodeSelector: {{ toYaml . | nindent 6 }}
{{- end }}
{{- with .Values.jenkins.tolerations }}
tolerations: {{ toYaml . | nindent 6 }}
{{- end }}
{{- with .Values.jenkins.annotations }}
annotations: {{ toYaml . | nindent 6 }}
{{- end }}
@ -107,47 +77,48 @@ spec:
{{- with .Values.jenkins.plugins }}
plugins: {{ toYaml . | nindent 4 }}
{{- end }}
latestPlugins: {{ .Values.jenkins.latestPlugins }}
{{- if .Values.jenkins.priorityClassName }}
priorityClassName: {{ .Values.jenkins.priorityClassName }}
{{- end }}
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:
- name: jenkins-master
image: {{ .Values.jenkins.image }}
imagePullPolicy: {{ .Values.jenkins.imagePullPolicy }}
{{- with .Values.jenkins.livenessProbe }}
livenessProbe: {{ toYaml . | nindent 10 }}
{{- end }}
{{- with .Values.jenkins.readinessProbe }}
readinessProbe: {{ toYaml . | nindent 10 }}
{{- with .Values.jenkins.imagePullSecrets }}
imagePullSecrets: {{ toYaml . | nindent 10 }}
{{- 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 }}
resources: {{ toYaml . | nindent 10 }}
{{- end }}
{{- with .Values.jenkins.env }}
env: {{- toYaml . | nindent 8 }}
{{- end }}
{{- with .Values.jenkins.lifecycle}}
lifecycle: {{ toYaml . | nindent 10 }}
{{- end }}
{{- with .Values.jenkins.volumeMounts }}
volumeMounts: {{- toYaml . | nindent 8 }}
{{- end }}
{{- if .Values.jenkins.backup.enabled }}
- name: {{ .Values.jenkins.backup.containerName }}
image: {{ .Values.jenkins.backup.image }}
imagePullPolicy: {{ .Values.jenkins.imagePullPolicy }}
{{- with .Values.jenkins.backup.resources }}
resources: {{ toYaml . | nindent 10 }}
{{- end }}
imagePullPolicy: IfNotPresent
{{- with .Values.jenkins.backup.env }}
env: {{- toYaml . | nindent 8 }}
{{- end }}
@ -158,18 +129,11 @@ spec:
{{- with .Values.jenkins.volumes }}
volumes: {{- toYaml . | nindent 4 }}
{{- end }}
{{- with .Values.jenkins.imagePullSecrets }}
imagePullSecrets: {{ toYaml . | nindent 4 }}
{{- end }}
{{- with .Values.jenkins.securityContext}}
securityContext:
{{- toYaml . | nindent 6 }}
{{- end }}
validateSecurityWarnings: {{ .Values.jenkins.validateSecurityWarnings }}
{{- with .Values.jenkins.seedJobs }}
seedJobs: {{- toYaml . | nindent 4 }}
{{- end }}
{{- if .Values.jenkins.seedJobAgentImage }}
seedJobAgentImage: {{ .Values.jenkins.seedJobAgentImage }}
{{- 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
protocol: TCP
command:
- /manager
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
- jenkins-operator
args: []
env:
- name: WATCH_NAMESPACE
{{- if .Values.jenkins.enabled }}
value: {{ .Values.jenkins.namespace }}
{{- else if .Values.operator.watchNamespace }}
value: {{ .Values.operator.watchNamespace }}
{{- else }}
valueFrom:
fieldRef:
fieldPath: metadata.namespace
{{- end }}
- name: POD_NAME
valueFrom:
fieldRef:
@ -84,11 +57,3 @@ spec:
tolerations:
{{- toYaml . | nindent 8 }}
{{- 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 "" }}
{{- /*
# 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
*/ -}}
{{- template "jenkins-operator.role" .Values.jenkins.namespace }}
{{ else }}
{{- template "jenkins-operator.role" .Release.Namespace }}
{{- if ne .Release.Namespace .Values.jenkins.namespace -}}
{{- template "jenkins-operator.role" .Values.jenkins.namespace }}
{{- end }}
{{ end }}
---
kind: Role
apiVersion: rbac.authorization.k8s.io/v1
metadata:
name: jenkins-operator
rules:
- apiGroups:
- ""
resources:
- services
- configmaps
- secrets
verbs:
- get
- 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
apiVersion: rbac.authorization.k8s.io/v1
metadata:
name: jenkins-operator
namespace: {{ .Release.Namespace }}
subjects:
- kind: ServiceAccount
name: jenkins-operator
namespace: {{ .Release.Namespace }}
roleRef:
kind: Role
name: jenkins-operator
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
# It's not recommended to use default namespace
# Create new namespace for jenkins (called e.g. jenkins)
# Note: this affects roles and rolebindings for jenkins operator itself
namespace: default
# labels are injected into metadata labels field
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: {}
# image is the name (and tag) of the Jenkins instance
# Default: jenkins/jenkins:lts
# 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: []
@ -44,9 +37,6 @@ jenkins:
# imagePullPolicy defines policy for pulling images
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
# See: https://kubernetes.io/docs/concepts/configuration/pod-priority-preemption/
priorityClassName: ""
@ -56,38 +46,14 @@ jenkins:
# See https://github.com/jenkinsci/kubernetes-operator/pull/193 for more info
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
# See https://jenkinsci.github.io/kubernetes-operator/docs/getting-started/latest/configuration/#pulling-docker-images-from-private-repositories for more info
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
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
# Shouldn't contain plugins defined by user
# You can change their versions here
@ -97,21 +63,34 @@ jenkins:
#
# basePlugins:
# - name: kubernetes
# version: 4246.v5a_12b_1fe120e
# version: 1.28.6
# - name: workflow-job
# version: 1400.v7fd111b_ec82f
# version: "2.40"
# - name: workflow-aggregator
# version: 596.v8c21c963d92d
# version: "2.6"
# - name: git
# version: 5.2.2
# version: 4.5.0
# - name: job-dsl
# version: "1.87"
# version: "1.77"
# - name: configuration-as-code
# version: 1810.v9b_c30a_249a_4c
# version: "1.46"
# - name: kubernetes-credentials-provider
# version: 1.262.v2670ef7ea_0c5
basePlugins: []
# version: 0.15
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
# You can define plugins here
@ -124,14 +103,9 @@ jenkins:
# version: "0.6"
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
# 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:
#
# seedJobs:
@ -142,22 +116,14 @@ jenkins:
# repositoryUrl: https://github.com/jenkinsci/kubernetes-operator.git
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
# See https://kubernetes.io/docs/concepts/configuration/manage-compute-resources-container/ for details
resources:
limits:
cpu: 1000m
cpu: 1500m
memory: 3Gi
requests:
cpu: 250m
cpu: 1
memory: 500Mi
# volumes used by Jenkins
@ -168,14 +134,8 @@ jenkins:
claimName: jenkins-backup
# 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: []
# defines authorization strategy of the operator for the Jenkins API
authorizationStrategy: createUser
# securityContext for pod
securityContext:
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
#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
# 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/
@ -221,8 +158,8 @@ jenkins:
enabled: true
# image used by backup feature
# By default using prebuilt backup PVC image
image: quay.io/jenkins-kubernetes-operator/backup-pvc:v0.4.3
# By default using prebuilt backup PVC image by VirtusLab
image: virtuslab/jenkins-operator-backup-pvc:v0.1.0
# containerName is backup container name
containerName: backup
@ -256,25 +193,11 @@ jenkins:
# See https://kubernetes.io/docs/concepts/storage/persistent-volumes/#class-1 for more details
className: ""
# resources used by backup container
resources:
limits:
cpu: 1000m
memory: 2Gi
requests:
cpu: 100m
memory: 500Mi
# env contains container environment variables
# PVC backup provider handles these variables:
# BACKUP_DIR - path for storing backup files (default: "/backup")
# JENKINS_HOME - path to jenkins home (default: "/jenkins-home")
# 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:
- name: BACKUP_DIR
value: /backup
@ -282,15 +205,6 @@ jenkins:
value: /jenkins-home
- name: BACKUP_COUNT
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:
@ -302,10 +216,10 @@ jenkins:
# configuration is section where we can configure Jenkins instance
# See https://jenkinsci.github.io/kubernetes-operator/docs/getting-started/latest/customization/ for details
configuration:
configurationAsCode: []
configurationAsCode: {}
# - configMapName: jenkins-casc
# content: {}
groovyScripts: []
groovyScripts: {}
# - configMapName: jenkins-gs
# content: {}
@ -320,7 +234,7 @@ operator:
replicaCount: 1
# 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: IfNotPresent
@ -334,33 +248,7 @@ operator:
# fullnameOverride overrides the deployment name
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: {}
nodeSelector: {}
tolerations: []
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"
API_VERSION_NEXT="v1alpha3"
API_VERSION="v1alpha2"
CLUSTER_DOMAIN="cluster.local"
GEN_CRD_API="gen-crd-api-reference-docs"
GO_VERSION="1.22"
HELM_VERSION="3.12.3"
IMAGE_PULL_MODE="local"
KIND_CLUSTER_NAME="jenkins"
LATEST_LTS_VERSION="2.528.1"
NAME="kubernetes-operator"
NAMESPACE="default"
OPERATOR_SDK_VERSION="1.35.0"
PKG="github.com/jenkinsci/kubernetes-operator"
QUAY_ORGANIZATION="jenkins-kubernetes-operator"
QUAY_REGISTRY="operator"
# Setup variables for the Makefile
NAME=kubernetes-operator
OPERATOR_SDK_VERSION=0.17.0
GO_VERSION=1.14.2
PKG=github.com/jenkinsci/kubernetes-operator
DOCKER_ORGANIZATION=virtuslab
DOCKER_REGISTRY=jenkins-operator
NAMESPACE=default
API_VERSION=v1alpha2
API_VERSION_NEXT=v1alpha3
ALL_IN_ONE_DEPLOY_FILE_PREFIX=all-in-one
GEN_CRD_API=gen-crd-api-reference-docs
IMAGE_PULL_MODE=local
HELM_VERSION=3.1.2
CLUSTER_DOMAIN=cluster.local

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