diff --git a/.github/workflows/auto-gen-docs.yaml b/.github/workflows/auto-gen-docs.yaml index e56ec635..5e246276 100644 --- a/.github/workflows/auto-gen-docs.yaml +++ b/.github/workflows/auto-gen-docs.yaml @@ -5,6 +5,11 @@ on: push: branches: - master + - main + paths: + - 'docs/**' + - 'website/**' + - 'assets/**' jobs: # Set the job key. The key is displayed as the job name @@ -78,4 +83,4 @@ jobs: branch: docs-generator title: Auto-generated docs update body: | - Auto generated docs from master commit ${{ github.sha }} \ No newline at end of file + Auto generated docs from master commit ${{ github.sha }} diff --git a/.github/workflows/auto-tests-bats.yaml b/.github/workflows/auto-tests-bats.yaml new file mode 100644 index 00000000..b5115458 --- /dev/null +++ b/.github/workflows/auto-tests-bats.yaml @@ -0,0 +1,61 @@ +name: Run bats tests +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: Run automated bats tests + runs-on: ubuntu-latest + steps: + - name: Check out code + uses: actions/checkout@v3 + + - name: Set up env vars + run: | + echo "GO111MODULE=on" >> $GITHUB_ENV + echo "GO_VERSION=v$(sed -n 's/GO_VERSION=//p' config.base.env)" >> $GITHUB_ENV + echo "HELM_VERSION=v$(sed -n 's/HELM_VERSION=//p' config.base.env)" >> $GITHUB_ENV + echo "KIND_CLUSTER_NAME=$(sed -n 's/KIND_CLUSTER_NAME=//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: Setup BATS + uses: mig4/setup-bats@v1 + with: + bats-version: 1.9.0 + + - name: Setup Bats libs + uses: brokenpip3/setup-bats-libs@0.1.0 + + - name: Kind setup + uses: helm/kind-action@v1.5.0 + with: + cluster_name: ${{env.KIND_CLUSTER_NAME}} + + - name: Jenkins Operator - bats tests + run: make bats-tests diff --git a/.github/workflows/auto-tests-e2e.yaml b/.github/workflows/auto-tests-e2e.yaml new file mode 100644 index 00000000..a51e198e --- /dev/null +++ b/.github/workflows/auto-tests-e2e.yaml @@ -0,0 +1,68 @@ +name: Run e2e tests +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' + +env: + MINIKUBE_CPUS_NUMBER: 2 + MINIKUBE_MEMORY_AMOUNT: 6144 + +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 "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 + sudo mkdir -p $HOME/.kube $HOME/.minikube + sudo chown -R $USER $HOME/.kube $HOME/.minikube + make minikube-start \ + MINIKUBE_DRIVER='docker' \ + MEMORY_AMOUNT=${{ env.MINIKUBE_MEMORY_AMOUNT }} \ + CPUS_NUMBER=${{ env.MINIKUBE_CPUS_NUMBER }} + + - name: Jenkins Operator - e2e + run: make e2e E2E_TEST_ARGS='-ginkgo.v' diff --git a/.github/workflows/auto-tests.yaml b/.github/workflows/auto-tests-helm.yaml similarity index 87% rename from .github/workflows/auto-tests.yaml rename to .github/workflows/auto-tests-helm.yaml index ea24938b..2a827d18 100644 --- a/.github/workflows/auto-tests.yaml +++ b/.github/workflows/auto-tests-helm.yaml @@ -1,12 +1,23 @@ -name: Run tests +name: Run Helm e2e tests on: push: branches: - master + - main + paths-ignore: + - 'docs/**' + - 'website/**' + - 'assets/**' + - 'backup/**' + - '*.md' pull_request: types: [opened, synchronize, ready_for_review, reopened] - branches: - - master + paths-ignore: + - 'docs/**' + - 'website/**' + - 'assets/**' + - 'backup/**' + - '*.md' env: MINIKUBE_CPUS_NUMBER: 2 @@ -53,9 +64,6 @@ jobs: MEMORY_AMOUNT=${{ env.MINIKUBE_MEMORY_AMOUNT }} \ CPUS_NUMBER=${{ env.MINIKUBE_CPUS_NUMBER }} - - name: Jenkins Operator - e2e - run: make e2e E2E_TEST_ARGS='-ginkgo.v' - - name: Jenkins Operator - Helm Chart tests run: | git reset --hard diff --git a/.github/workflows/release-backup.yaml b/.github/workflows/release-backup.yaml index 7bab0051..f62bf4e6 100644 --- a/.github/workflows/release-backup.yaml +++ b/.github/workflows/release-backup.yaml @@ -7,7 +7,9 @@ on: - 'backup/pvc/**' push: - branches: ["master"] + branches: + - master + - main tags: ["*"] paths: - 'backup/pvc/**' diff --git a/.github/workflows/deploy-nightly.yaml b/.github/workflows/release-nightly.yaml similarity index 64% rename from .github/workflows/deploy-nightly.yaml rename to .github/workflows/release-nightly.yaml index 85e4f7a4..f4f3a709 100644 --- a/.github/workflows/deploy-nightly.yaml +++ b/.github/workflows/release-nightly.yaml @@ -17,10 +17,10 @@ jobs: name: Publish nightly snapshot runs-on: ubuntu-latest steps: - - name: Check out code + - name: Prep - check out code uses: actions/checkout@v2 - - name: Set up env vars + - name: Prep - Set up env vars run: | echo "GO111MODULE=on" >> $GITHUB_ENV echo "CHANGE_MINIKUBE_NONE_USER=true" >> $GITHUB_ENV @@ -28,20 +28,31 @@ jobs: echo "MINIKUBE_WANTREPORTERRORPROMPT=false" >> $GITHUB_ENV echo "GO_VERSION=v$(sed -n 's/GO_VERSION=//p' config.base.env)" >> $GITHUB_ENV echo "HELM_VERSION=v$(sed -n 's/HELM_VERSION=//p' config.base.env)" >> $GITHUB_ENV + echo "KIND_CLUSTER_NAME=$(sed -n 's/KIND_CLUSTER_NAME=//p' config.base.env)" >> $GITHUB_ENV echo "GOPATH=/home/runner/go" >> $GITHUB_ENV - - name: Prepare go environment + - name: Prep - setup BATS + if: ${{ github.event.inputs.skipTests != 'true' }} + uses: mig4/setup-bats@v1 + with: + bats-version: 1.9.0 + + - name: Prep - setup Bats libs + if: ${{ github.event.inputs.skipTests != 'true' }} + uses: brokenpip3/setup-bats-libs@0.1.0 + + - name: Prep - go environment uses: actions/setup-go@v2 with: go-version: ${{ env.GO_VERSION }} - - name: Ensure Golang runtime dependencies + - name: Prep - Ensure Golang runtime dependencies run: make go-dependencies - - name: Verify code formatting + - name: Test - verify code formatting run: make verify - - name: Prepare environment for e2e + - name: Prep - Minikube setup if: ${{ github.event.inputs.skipTests != 'true' }} run: | sudo apt-get update @@ -53,11 +64,11 @@ jobs: MEMORY_AMOUNT=${{ env.MINIKUBE_MEMORY_AMOUNT }} \ CPUS_NUMBER=${{ env.MINIKUBE_CPUS_NUMBER }} - - name: Jenkins Operator - e2e + - name: Test - e2e if: ${{ github.event.inputs.skipTests != 'true' }} run: make e2e E2E_TEST_ARGS='-ginkgo.v' - - name: Jenkins Operator - Helm Chart tests + - name: Test - Helm Chart if: ${{ github.event.inputs.skipTests != 'true' }} run: | git reset --hard @@ -65,14 +76,29 @@ jobs: eval $(bin/minikube docker-env) make helm-e2e E2E_TEST_ARGS='-ginkgo.v' - - name: Login to Quay.io + - name: Prep - Destroy minikube + if: ${{ github.event.inputs.skipTests != 'true' }} + run: | + make minikube-destroy + + - name: Prep - Kind setup + if: ${{ github.event.inputs.skipTests != 'true' }} + uses: helm/kind-action@v1.5.0 + with: + cluster_name: ${{env.KIND_CLUSTER_NAME}} + + - name: Test - bats + if: ${{ github.event.inputs.skipTests != 'true' }} + run: make bats-tests + + - name: Post - Login to Quay.io uses: docker/login-action@v1 with: registry: quay.io username: ${{ secrets.QUAYIO_USERNAME }} password: ${{ secrets.QUAYIO_TOKEN }} - - name: Release Container Runtime + - name: Post - Push image run: | git reset --hard make container-runtime-snapshot-push diff --git a/.gitignore b/.gitignore index 4fe56ae6..85ea5a70 100644 --- a/.gitignore +++ b/.gitignore @@ -90,4 +90,7 @@ tags *.iml /bin -testbin/* \ No newline at end of file +testbin/* + +### Bats +chart/jenkins-operator/deploy.tmp diff --git a/.pre-commit-config.yaml b/.pre-commit-config.yaml index 54e2311e..333b17e1 100644 --- a/.pre-commit-config.yaml +++ b/.pre-commit-config.yaml @@ -1,16 +1,21 @@ repos: - - repo: https://github.com/golangci/golangci-lint - rev: v1.51.2 - hooks: - - id: golangci-lint - repo: https://github.com/sirosen/check-jsonschema - rev: 0.21.0 + rev: 0.22.0 hooks: - id: check-github-workflows - repo: https://github.com/pre-commit/pre-commit-hooks rev: v4.4.0 hooks: - id: detect-private-key - - id: check-yaml - id: trailing-whitespace - id: end-of-file-fixer + - repo: https://github.com/gruntwork-io/pre-commit + rev: "v0.1.19" + hooks: + - id: helmlint + - repo: https://github.com/norwoodj/helm-docs + rev: "v1.11.0" + hooks: + - id: helm-docs + args: + - --chart-search-root=chart/jenkins-operator diff --git a/Dockerfile b/Dockerfile index 236f85b5..d810ed9b 100644 --- a/Dockerfile +++ b/Dockerfile @@ -28,6 +28,13 @@ RUN CGO_ENABLED=0 GOOS=$TARGETOS GOARCH=$TARGETARCH GO111MODULE=on go build -ldf # 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 diff --git a/Makefile b/Makefile index b356b131..a3e8f279 100644 --- a/Makefile +++ b/Makefile @@ -95,7 +95,7 @@ e2e: deepcopy-gen manifests ## Runs e2e tests, you can use EXTRA_ARGS -jenkins-api-hostname=$(JENKINS_API_HOSTNAME) -jenkins-api-port=$(JENKINS_API_PORT) -jenkins-api-use-nodeport=$(JENKINS_API_USE_NODEPORT) $(E2E_TEST_ARGS) .PHONY: helm-e2e -IMAGE_NAME := $(QUAY_REGISTRY):$(GITCOMMIT)-amd64 +IMAGE_NAME := quay.io/$(QUAY_ORGANIZATION)/$(QUAY_REGISTRY):$(GITCOMMIT)-amd64 helm-e2e: helm container-runtime-build-amd64 ## Runs helm e2e tests, you can use EXTRA_ARGS @echo "+ $@" @@ -140,6 +140,13 @@ install: ## Installs the executable @echo "+ $@" go install -tags "$(BUILDTAGS)" ${GO_LDFLAGS} $(BUILD_PATH) +.PHONY: update-lts-version +update-lts-version: ## Update the latest lts version + @echo "+ $@" + 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 + .PHONY: run run: export WATCH_NAMESPACE = $(NAMESPACE) run: export OPERATOR_NAME = $(NAME) @@ -214,7 +221,7 @@ container-runtime-build-%: ## Build the container --output=type=docker --platform linux/$* \ --build-arg GO_VERSION=$(GO_VERSION) \ --build-arg CTIMEVAR="$(CTIMEVAR)" \ - --tag $(QUAY_REGISTRY):$(GITCOMMIT)-$* . \ + --tag quay.io/$(QUAY_ORGANIZATION)/$(QUAY_REGISTRY):$(GITCOMMIT)-$* . \ --file Dockerfile $(CONTAINER_RUNTIME_EXTRA_ARGS) .PHONY: container-runtime-build @@ -282,7 +289,7 @@ 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_REGISTRY):$(GITCOMMIT) /usr/bin/jenkins-operator $(OPERATOR_ARGS) + quay.io/${QUAY_ORGANIZATION}/$(QUAY_REGISTRY):$(GITCOMMIT) /usr/bin/jenkins-operator $(OPERATOR_ARGS) .PHONY: minikube-run minikube-run: export WATCH_NAMESPACE = $(NAMESPACE) @@ -356,6 +363,36 @@ minikube-start: minikube check-minikube ## Start minikube bin/minikube status && exit 0 || \ bin/minikube start --kubernetes-version $(MINIKUBE_KUBERNETES_VERSION) --dns-domain=$(CLUSTER_DOMAIN) --extra-config=kubelet.cluster-domain=$(CLUSTER_DOMAIN) --driver=$(MINIKUBE_DRIVER) --memory $(MEMORY_AMOUNT) --cpus $(CPUS_NUMBER) +.PHONY: minikube-destroy +minikube-destroy: ## Stop and destroy minikube + @echo "+ $@" + bin/minikube stop + bin/minikube delete + +.PHONY: kind-setup +kind-setup: ## Setup kind cluster + @echo "+ $@" + kind create cluster --name $(KIND_CLUSTER_NAME) + +.PHONY: kind-clean +kind-clean: ## Delete kind cluster + @echo "+ $@" + kind delete cluster --name $(KIND_CLUSTER_NAME) + +.PHONY: 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: 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 -x test/bats +else +bats-tests: ## Run bats tests + @echo "+ $@" + OPERATOR_IMAGE="${IMAGE_NAME}" TERM=xterm bats -T -p -x test/bats +endif + .PHONY: crc-start crc-start: check-crc ## Start CodeReady Containers Kubernetes cluster @echo "+ $@" @@ -417,7 +454,7 @@ ifneq ($(GITUNTRACKEDCHANGES),) endif ifneq ($(GITIGNOREDBUTTRACKEDCHANGES),) @echo "Ignored but tracked files:" - @git ls-files -i --exclude-standard + @git ls-files -i -c --exclude-standard @echo endif @echo "Dependencies:" @@ -474,7 +511,7 @@ uninstall-crds: manifests kustomize # Deploy controller in the configured Kubernetes cluster in ~/.kube/config deploy: manifests kustomize - cd config/manager && $(KUSTOMIZE) edit set image controller=$(QUAY_REGISTRY):$(GITCOMMIT) + 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 diff --git a/api/v1alpha2/jenkins_types.go b/api/v1alpha2/jenkins_types.go index 85f3b229..b806902e 100644 --- a/api/v1alpha2/jenkins_types.go +++ b/api/v1alpha2/jenkins_types.go @@ -370,6 +370,11 @@ type JenkinsMaster struct { // +optional Plugins []Plugin `json:"plugins,omitempty"` + // 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 + LatestPlugins bool `json:"latestPlugins"` + // DisableCSRFProtection allows you to toggle CSRF Protection on Jenkins DisableCSRFProtection bool `json:"disableCSRFProtection"` diff --git a/backup/pvc/Dockerfile b/backup/pvc/Dockerfile index c562aab4..834c8c92 100644 --- a/backup/pvc/Dockerfile +++ b/backup/pvc/Dockerfile @@ -1,4 +1,12 @@ -FROM debian:buster-slim +FROM debian:bullseye-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:bullseye-slim" ARG UID ARG GID diff --git a/backup/pvc/VERSION.txt b/backup/pvc/VERSION.txt index 22c08f72..f0cfd3bb 100644 --- a/backup/pvc/VERSION.txt +++ b/backup/pvc/VERSION.txt @@ -1 +1 @@ -v0.2.1 +v0.2.2 diff --git a/backup/pvc/bin/restore.sh b/backup/pvc/bin/restore.sh index 497f2a23..5b6f504b 100644 --- a/backup/pvc/bin/restore.sh +++ b/backup/pvc/bin/restore.sh @@ -7,9 +7,9 @@ set -eo pipefail [[ -z "${JENKINS_HOME}" ]] && echo "Required 'JENKINS_HOME' env not set" && exit 1; backup_number=$1 -echo "Running restore backup" +echo "Running restore backup with backup number #${backup_number}" tar -C ${JENKINS_HOME} -zxf "${BACKUP_DIR}/${backup_number}.tar.gz" echo Done -exit 0 \ No newline at end of file +exit 0 diff --git a/chart/jenkins-operator/README.md b/chart/jenkins-operator/README.md new file mode 100644 index 00000000..a964e0ff --- /dev/null +++ b/chart/jenkins-operator/README.md @@ -0,0 +1,112 @@ +# jenkins-operator + +![Version: 0.6.2](https://img.shields.io/badge/Version-0.6.2-informational?style=flat-square) ![AppVersion: 0.7.1](https://img.shields.io/badge/AppVersion-0.7.1-informational?style=flat-square) + +Kubernetes native operator which fully manages Jenkins on Kubernetes + +## Requirements + +| Repository | Name | Version | +|------------|------|---------| +| https://charts.jetstack.io | cert-manager | 1.5.1 | + +## Values + +| Key | Type | Default | Description | +|-----|------|---------|-------------| +| 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.2.1"` | | +| 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 | object | `{}` | | +| jenkins.configuration.groovyScripts | object | `{}` | | +| 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.387.1-lts"` | | +| jenkins.imagePullPolicy | string | `"Always"` | | +| jenkins.imagePullSecrets | list | `[]` | | +| jenkins.labels | object | `{}` | | +| jenkins.latestPlugins | bool | `true` | | +| 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.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:d91a729"` | | +| 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.11.0](https://github.com/norwoodj/helm-docs/releases/v1.11.0) diff --git a/chart/jenkins-operator/crds/jenkins-crd.yaml b/chart/jenkins-operator/crds/jenkins-crd.yaml index 8823ac22..b00bb4f4 100644 --- a/chart/jenkins-operator/crds/jenkins-crd.yaml +++ b/chart/jenkins-operator/crds/jenkins-crd.yaml @@ -1100,6 +1100,11 @@ spec: - resources type: object type: array + latestPlugins: + description: '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' + type: boolean disableCSRFProtection: description: DisableCSRFProtection allows you to toggle CSRF Protection on Jenkins @@ -3119,8 +3124,8 @@ spec: type: object type: array seedJobAgentImage: - type: string - description: 'SeedJobAgentImage defines the image that will be used by the seed job agent. If not defined jenkins/inbound-agent:4.10-3 will be used.' + type: string + description: 'SeedJobAgentImage defines the image that will be used by the seed job agent. If not defined jenkins/inbound-agent:4.10-3 will be used.' seedJobs: description: 'SeedJobs defines list of Jenkins Seed Job configurations More info: https://jenkinsci.github.io/kubernetes-operator/docs/getting-started/latest/configuration#configure-seed-jobs-and-pipelines' diff --git a/chart/jenkins-operator/templates/jenkins.yaml b/chart/jenkins-operator/templates/jenkins.yaml index 9eee535f..1ce3b0cc 100644 --- a/chart/jenkins-operator/templates/jenkins.yaml +++ b/chart/jenkins-operator/templates/jenkins.yaml @@ -95,9 +95,6 @@ spec: {{- 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,6 +104,7 @@ spec: {{- with .Values.jenkins.plugins }} plugins: {{ toYaml . | nindent 4 }} {{- end }} + latestPlugins: {{ .Values.jenkins.latestPlugins }} {{- if .Values.jenkins.priorityClassName }} priorityClassName: {{ .Values.jenkins.priorityClassName }} {{- end }} diff --git a/chart/jenkins-operator/values.yaml b/chart/jenkins-operator/values.yaml index 7e7e5397..a818f09a 100644 --- a/chart/jenkins-operator/values.yaml +++ b/chart/jenkins-operator/values.yaml @@ -27,16 +27,13 @@ jenkins: # 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.375.3-lts + image: jenkins/jenkins:2.387.1-lts # env contains jenkins container environment variables env: [] @@ -90,15 +87,15 @@ jenkins: # # basePlugins: # - name: kubernetes - # version: 3883.v4d70a_a_a_df034 + # version: 3896.v19b_160fd9589 # - name: workflow-job - # version: 1282.ve6d865025906 + # version: 1284.v2fe8ed4573d4 # - name: workflow-aggregator - # version: 590.v6a_d052e5a_a_b_5 + # version: 596.v8c21c963d92d # - name: git # version: 5.0.0 # - name: job-dsl - # version: "1.81" + # version: "1.82" # - name: configuration-as-code # version: 1569.vb_72405b_80249 # - name: kubernetes-credentials-provider @@ -117,6 +114,12 @@ 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/ # Example: diff --git a/config.base.env b/config.base.env index 0c1a1f08..b78b3937 100644 --- a/config.base.env +++ b/config.base.env @@ -13,3 +13,5 @@ GEN_CRD_API=gen-crd-api-reference-docs IMAGE_PULL_MODE=local HELM_VERSION=3.1.2 CLUSTER_DOMAIN=cluster.local +LATEST_LTS_VERSION=2.387.1 +KIND_CLUSTER_NAME=jenkins diff --git a/config/crd/bases/jenkins.io_jenkins.yaml b/config/crd/bases/jenkins.io_jenkins.yaml index e94f6036..b00bb4f4 100644 --- a/config/crd/bases/jenkins.io_jenkins.yaml +++ b/config/crd/bases/jenkins.io_jenkins.yaml @@ -1100,6 +1100,11 @@ spec: - resources type: object type: array + latestPlugins: + description: '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' + type: boolean disableCSRFProtection: description: DisableCSRFProtection allows you to toggle CSRF Protection on Jenkins @@ -3120,7 +3125,7 @@ spec: type: array seedJobAgentImage: type: string - description: SeedJobAgentImage defines the image that will be used by the seed job agent. If not defined jenkins/inbound-agent:4.10-3 will be used. + description: 'SeedJobAgentImage defines the image that will be used by the seed job agent. If not defined jenkins/inbound-agent:4.10-3 will be used.' seedJobs: description: 'SeedJobs defines list of Jenkins Seed Job configurations More info: https://jenkinsci.github.io/kubernetes-operator/docs/getting-started/latest/configuration#configure-seed-jobs-and-pipelines' diff --git a/pkg/configuration/base/plugin.go b/pkg/configuration/base/plugin.go index b396c3f2..41614ac9 100644 --- a/pkg/configuration/base/plugin.go +++ b/pkg/configuration/base/plugin.go @@ -36,8 +36,7 @@ func (r *JenkinsBaseConfigurationReconciler) verifyPlugins(jenkinsClient jenkins continue } if found, ok := isPluginVersionCompatible(allPluginsInJenkins, plugin); !ok { - r.logger.V(log.VWarn).Info(fmt.Sprintf("Incompatible plugin '%s' version, actual '%+v'", plugin, found.Version)) - status = false + r.logger.V(log.VWarn).Info(fmt.Sprintf("The plugin you specified as code is incompatible with this jenkins version: plugin '%s' version, actual '%+v'", plugin, found.Version)) } } } diff --git a/pkg/configuration/base/reconcile_test.go b/pkg/configuration/base/reconcile_test.go index 246d3529..c36e1ae9 100644 --- a/pkg/configuration/base/reconcile_test.go +++ b/pkg/configuration/base/reconcile_test.go @@ -340,7 +340,7 @@ func TestJenkinsBaseConfigurationReconciler_verifyPlugins(t *testing.T) { got, err := r.verifyPlugins(jenkinsClient) assert.NoError(t, err) - assert.False(t, got) + assert.True(t, got) }) t.Run("plugin version matter for user plugins", func(t *testing.T) { jenkins := &v1alpha2.Jenkins{ @@ -377,7 +377,7 @@ func TestJenkinsBaseConfigurationReconciler_verifyPlugins(t *testing.T) { got, err := r.verifyPlugins(jenkinsClient) assert.NoError(t, err) - assert.False(t, got) + assert.True(t, got) }) t.Run("missing base plugin", func(t *testing.T) { jenkins := &v1alpha2.Jenkins{ diff --git a/pkg/configuration/base/resources/scripts_configmap.go b/pkg/configuration/base/resources/scripts_configmap.go index e22bb08c..732d656e 100644 --- a/pkg/configuration/base/resources/scripts_configmap.go +++ b/pkg/configuration/base/resources/scripts_configmap.go @@ -14,307 +14,6 @@ import ( const installPluginsCommand = "jenkins-plugin-cli" -// bash scripts installs single jenkins plugin with specific version -const installPluginsBashScript = `#!/bin/bash -eu - -# Resolve dependencies and download plugins given on the command line -# -# FROM jenkins -# RUN install-plugins.sh docker-slaves github-branch-source -# -# Environment variables: -# REF: directory with preinstalled plugins. Default: /usr/share/jenkins/ref/plugins -# JENKINS_WAR: full path to the jenkins.war. Default: /usr/share/jenkins/jenkins.war -# JENKINS_UC: url of the Update Center. Default: "" -# JENKINS_UC_EXPERIMENTAL: url of the Experimental Update Center for experimental versions of plugins. Default: "" -# JENKINS_INCREMENTALS_REPO_MIRROR: url of the incrementals repo mirror. Default: "" -# JENKINS_UC_DOWNLOAD: download url of the Update Center. Default: JENKINS_UC/download -# CURL_OPTIONS When downloading the plugins with curl. Curl options. Default: -sSfL -# CURL_CONNECTION_TIMEOUT When downloading the plugins with curl. Maximum time allowed for connection. Default: 20 -# CURL_RETRY When downloading the plugins with curl. Retry request if transient problems occur. Default: 3 -# CURL_RETRY_DELAY When downloading the plugins with curl. Wait time between retries. Default: 0 -# CURL_RETRY_MAX_TIME When downloading the plugins with curl. Retry only within this period. Default: 60 - -set -o pipefail - -echo "WARN: install-plugins.sh is deprecated, please switch to jenkins-plugin-cli" - -JENKINS_WAR=${JENKINS_WAR:-/usr/share/jenkins/jenkins.war} - -. /usr/local/bin/jenkins-support - -REF_DIR="${REF}/plugins" -FAILED="$REF_DIR/failed-plugins.txt" - -getLockFile() { - printf '%s' "$REF_DIR/${1}.lock" -} - -getArchiveFilename() { - printf '%s' "$REF_DIR/${1}.jpi" -} - -download() { - local plugin originalPlugin version lock ignoreLockFile url - plugin="$1" - version="${2:-latest}" - ignoreLockFile="${3:-}" - url="${4:-}" - lock="$(getLockFile "$plugin")" - - if [[ $ignoreLockFile ]] || mkdir "$lock" &>/dev/null; then - if ! doDownload "$plugin" "$version" "$url"; then - # some plugin don't follow the rules about artifact ID - # typically: docker-plugin - originalPlugin="$plugin" - plugin="${plugin}-plugin" - if ! doDownload "$plugin" "$version" "$url"; then - echo "Failed to download plugin: $originalPlugin or $plugin" >&2 - echo "Not downloaded: ${originalPlugin}" >> "$FAILED" - return 1 - fi - fi - - if ! checkIntegrity "$plugin"; then - echo "Downloaded file is not a valid ZIP: $(getArchiveFilename "$plugin")" >&2 - echo "Download integrity: ${plugin}" >> "$FAILED" - rm $(getArchiveFilename "$plugin") - return 1 - fi - - resolveDependencies "$plugin" - fi -} - -doDownload() { - local plugin version url jpi - plugin="$1" - version="$2" - url="$3" - jpi="$(getArchiveFilename "$plugin")" - - # If plugin already exists and is the same version do not download - if test -f "$jpi" && unzip -p "$jpi" META-INF/MANIFEST.MF | tr -d '\r' | grep "^Plugin-Version: ${version}$" > /dev/null; then - echo "Using provided plugin: $plugin" - return 0 - fi - - if [[ -n $url ]] ; then - echo "Will use url=$url" - elif [[ "$version" == "latest" && -n "$JENKINS_UC_LATEST" ]]; then - # If version-specific Update Center is available, which is the case for LTS versions, - # use it to resolve latest versions. - url="$JENKINS_UC_LATEST/latest/${plugin}.hpi" - elif [[ "$version" == "experimental" && -n "$JENKINS_UC_EXPERIMENTAL" ]]; then - # Download from the experimental update center - url="$JENKINS_UC_EXPERIMENTAL/latest/${plugin}.hpi" - elif [[ "$version" == incrementals* ]] ; then - # Download from Incrementals repo: https://jenkins.io/blog/2018/05/15/incremental-deployment/ - # Example URL: https://repo.jenkins-ci.org/incrementals/org/jenkins-ci/plugins/workflow/workflow-support/2.19-rc289.d09828a05a74/workflow-support-2.19-rc289.d09828a05a74.hpi - local groupId incrementalsVersion - # add a trailing ; so the \n gets added to the end - readarray -t "-d;" arrIN <<<"${version};"; - unset 'arrIN[-1]'; - groupId=${arrIN[1]} - incrementalsVersion=${arrIN[2]} - url="${JENKINS_INCREMENTALS_REPO_MIRROR}/$(echo "${groupId}" | tr '.' '/')/${plugin}/${incrementalsVersion}/${plugin}-${incrementalsVersion}.hpi" - else - JENKINS_UC_DOWNLOAD=${JENKINS_UC_DOWNLOAD:-"$JENKINS_UC/download"} - url="$JENKINS_UC_DOWNLOAD/plugins/$plugin/$version/${plugin}.hpi" - fi - - echo "Downloading plugin: $plugin from $url" - # We actually want to allow variable value to be split into multiple options passed to curl. - # This is needed to allow long options and any options that take value. - # shellcheck disable=SC2086 - retry_command curl ${CURL_OPTIONS:--sSfL} --connect-timeout "${CURL_CONNECTION_TIMEOUT:-20}" --retry "${CURL_RETRY:-3}" --retry-delay "${CURL_RETRY_DELAY:-0}" --retry-max-time "${CURL_RETRY_MAX_TIME:-60}" "$url" -o "$jpi" - return $? -} - -checkIntegrity() { - local plugin jpi - plugin="$1" - jpi="$(getArchiveFilename "$plugin")" - - unzip -t -qq "$jpi" >/dev/null - return $? -} - -resolveDependencies() { - local plugin jpi dependencies - plugin="$1" - jpi="$(getArchiveFilename "$plugin")" - - dependencies="$(unzip -p "$jpi" META-INF/MANIFEST.MF | tr -d '\r' | tr '\n' '|' | sed -e 's#| ##g' | tr '|' '\n' | grep "^Plugin-Dependencies: " | sed -e 's#^Plugin-Dependencies: ##')" - - if [[ ! $dependencies ]]; then - echo " > $plugin has no dependencies" - return - fi - - echo " > $plugin depends on $dependencies" - - IFS=',' read -r -a array <<< "$dependencies" - - for d in "${array[@]}" - do - plugin="$(cut -d':' -f1 - <<< "$d")" - if [[ $d == *"resolution:=optional"* ]]; then - echo "Skipping optional dependency $plugin" - else - local pluginInstalled - if pluginInstalled="$(echo -e "${bundledPlugins}\n${installedPlugins}" | grep "^${plugin}:")"; then - pluginInstalled="${pluginInstalled//[$'\r']}" - local versionInstalled; versionInstalled=$(versionFromPlugin "${pluginInstalled}") - local minVersion; minVersion=$(versionFromPlugin "${d}") - if versionLT "${versionInstalled}" "${minVersion}"; then - echo "Upgrading bundled dependency $d ($minVersion > $versionInstalled)" - download "$plugin" & - else - echo "Skipping already installed dependency $d ($minVersion <= $versionInstalled)" - fi - else - download "$plugin" & - fi - fi - done - wait -} - -bundledPlugins() { - if [ -f "$JENKINS_WAR" ] - then - TEMP_PLUGIN_DIR=/tmp/plugintemp.$$ - for i in $(jar tf "$JENKINS_WAR" | grep -E '[^detached-]plugins.*\..pi' | sort) - do - rm -fr $TEMP_PLUGIN_DIR - mkdir -p $TEMP_PLUGIN_DIR - PLUGIN=$(basename "$i"|cut -f1 -d'.') - (cd $TEMP_PLUGIN_DIR;jar xf "$JENKINS_WAR" "$i";jar xvf "$TEMP_PLUGIN_DIR/$i" META-INF/MANIFEST.MF >/dev/null 2>&1) - VER=$(grep -E -i Plugin-Version "$TEMP_PLUGIN_DIR/META-INF/MANIFEST.MF"|cut -d: -f2|sed 's/ //') - echo "$PLUGIN:$VER" - done - rm -fr $TEMP_PLUGIN_DIR - else - echo "war not found, installing all plugins: $JENKINS_WAR" - fi -} - -versionFromPlugin() { - local plugin=$1 - if [[ $plugin =~ .*:.* ]]; then - echo "${plugin##*:}" - else - echo "latest" - fi - -} - -installedPlugins() { - for f in "$REF_DIR"/*.jpi; do - echo "$(basename "$f" | sed -e 's/\.jpi//'):$(get_plugin_version "$f")" - done -} - -jenkinsMajorMinorVersion() { - if [[ -f "$JENKINS_WAR" ]]; then - local version major minor - version="$(java -jar "$JENKINS_WAR" --version)" - major="$(echo "$version" | cut -d '.' -f 1)" - minor="$(echo "$version" | cut -d '.' -f 2)" - echo "$major.$minor" - else - echo "" - fi -} - -main() { - local plugin jenkinsVersion - local plugins=() - - mkdir -p "$REF_DIR" || exit 1 - rm -f "$FAILED" - - echo "Cleaning up locks" - find "$REF_DIR" -regex ".*.lock" | while read -r filepath; do - rm -r "$filepath" - done - - # Read plugins from stdin or from the command line arguments - if [[ ($# -eq 0) ]]; then - while read -r line || [ "$line" != "" ]; do - # Remove leading/trailing spaces, comments, and empty lines - plugin=$(echo "${line}" | tr -d '\r' | sed -e 's/^[ \t]*//g' -e 's/[ \t]*$//g' -e 's/[ \t]*#.*$//g' -e '/^[ \t]*$/d') - - # Avoid adding empty plugin into array - if [ ${#plugin} -ne 0 ]; then - plugins+=("${plugin}") - fi - done - else - plugins=("$@") - fi - - # Create lockfile manually before first run to make sure any explicit version set is used. - echo "Creating initial locks..." - for plugin in "${plugins[@]}"; do - mkdir "$(getLockFile "${plugin%%:*}")" - done - - echo "Analyzing war $JENKINS_WAR..." - bundledPlugins="$(bundledPlugins)" - - echo "Registering preinstalled plugins..." - installedPlugins="$(installedPlugins)" - - # Get the update center URL based on the jenkins version - jenkinsVersion="$(jenkinsMajorMinorVersion)" - # shellcheck disable=SC2086 - jenkinsUcJson=$(curl ${CURL_OPTIONS:--sSfL} -o /dev/null -w "%{url_effective}" "${JENKINS_UC}/update-center.json?version=${jenkinsVersion}") - if [ -n "${jenkinsUcJson}" ]; then - JENKINS_UC_LATEST=${jenkinsUcJson//update-center.json/} - echo "Using version-specific update center: $JENKINS_UC_LATEST..." - else - JENKINS_UC_LATEST= - fi - - echo "Downloading plugins..." - for plugin in "${plugins[@]}"; do - local reg='^([^:]+):?([^:]+)?:?([^:]+)?:?(http.+)?' - if [[ $plugin =~ $reg ]]; then - local pluginId="${BASH_REMATCH[1]}" - local version="${BASH_REMATCH[2]}" - local lock="${BASH_REMATCH[3]}" - local url="${BASH_REMATCH[4]}" - download "$pluginId" "$version" "${lock:-true}" "${url}" & - else - echo "Skipping the line '${plugin}' as it does not look like a reference to a plugin" - fi - done - wait - - echo - echo "WAR bundled plugins:" - echo "${bundledPlugins}" - echo - echo "Installed plugins:" - installedPlugins - - if [[ -f $FAILED ]]; then - echo "Some plugins failed to download!" "$(<"$FAILED")" >&2 - exit 1 - fi - - echo "Cleaning up locks" - find "$REF_DIR" -regex ".*.lock" | while read -r filepath; do - rm -r "$filepath" - done - -} - -main "$@" -` - var initBashTemplate = template.Must(template.New(InitScriptName).Parse(`#!/usr/bin/env bash set -e set -x @@ -347,7 +46,7 @@ cat > {{ .JenkinsHomePath }}/base-plugins.txt << EOF {{ end }} EOF -{{ $installPluginsCommand }} --verbose -f {{ .JenkinsHomePath }}/base-plugins.txt +{{ $installPluginsCommand }} --verbose --latest {{ .LatestPlugins }} -f {{ .JenkinsHomePath }}/base-plugins.txt echo "Installing plugins required by Operator - end" echo "Installing plugins required by user - begin" @@ -357,7 +56,7 @@ cat > {{ .JenkinsHomePath }}/user-plugins.txt << EOF {{ end }} EOF -{{ $installPluginsCommand }} --verbose -f {{ .JenkinsHomePath }}/user-plugins.txt +{{ $installPluginsCommand }} --verbose --latest {{ .LatestPlugins }} -f {{ .JenkinsHomePath }}/user-plugins.txt echo "Installing plugins required by user - end" `)) @@ -369,6 +68,13 @@ func buildConfigMapTypeMeta() metav1.TypeMeta { } func buildInitBashScript(jenkins *v1alpha2.Jenkins) (*string, error) { + defaultlatestPlugin := true + + latestP := jenkins.Spec.Master.LatestPlugins + if !latestP { + latestP = defaultlatestPlugin + } + data := struct { JenkinsHomePath string InitConfigurationPath string @@ -376,6 +82,7 @@ func buildInitBashScript(jenkins *v1alpha2.Jenkins) (*string, error) { JenkinsScriptsVolumePath string BasePlugins []v1alpha2.Plugin UserPlugins []v1alpha2.Plugin + LatestPlugins bool }{ JenkinsHomePath: getJenkinsHomePath(jenkins), InitConfigurationPath: jenkinsInitConfigurationVolumePath, @@ -383,6 +90,7 @@ func buildInitBashScript(jenkins *v1alpha2.Jenkins) (*string, error) { UserPlugins: jenkins.Spec.Master.Plugins, InstallPluginsCommand: installPluginsCommand, JenkinsScriptsVolumePath: JenkinsScriptsVolumePath, + LatestPlugins: latestP, } output, err := render.Render(initBashTemplate, data) @@ -410,8 +118,7 @@ func NewScriptsConfigMap(meta metav1.ObjectMeta, jenkins *v1alpha2.Jenkins) (*co TypeMeta: buildConfigMapTypeMeta(), ObjectMeta: meta, Data: map[string]string{ - InitScriptName: *initBashScript, - installPluginsCommand: installPluginsBashScript, + InitScriptName: *initBashScript, }, }, nil } diff --git a/pkg/plugins/base_plugins.go b/pkg/plugins/base_plugins.go index e88dc665..65eaceb8 100644 --- a/pkg/plugins/base_plugins.go +++ b/pkg/plugins/base_plugins.go @@ -3,11 +3,11 @@ package plugins const ( configurationAsCodePlugin = "configuration-as-code:1569.vb_72405b_80249" gitPlugin = "git:5.0.0" - jobDslPlugin = "job-dsl:1.81" - kubernetesPlugin = "kubernetes:3883.v4d70a_a_a_df034" + jobDslPlugin = "job-dsl:1.82" + kubernetesPlugin = "kubernetes:3896.v19b_160fd9589" kubernetesCredentialsProviderPlugin = "kubernetes-credentials-provider:1.209.v862c6e5fb_1ef" - workflowAggregatorPlugin = "workflow-aggregator:590.v6a_d052e5a_a_b_5" - workflowJobPlugin = "workflow-job:1282.ve6d865025906" + workflowAggregatorPlugin = "workflow-aggregator:596.v8c21c963d92d" + workflowJobPlugin = "workflow-job:1284.v2fe8ed4573d4" ) // basePluginsList contains plugins to install by operator. diff --git a/test/bats/1-deploy.bats b/test/bats/1-deploy.bats new file mode 100644 index 00000000..911d323e --- /dev/null +++ b/test/bats/1-deploy.bats @@ -0,0 +1,103 @@ +setup() { + load 'test_helper' + _common_setup +} + +diag() { + echo "# DEBUG $@" >&3 +} + +#bats test_tags=phase:setup +@test "1.0 Create namespace" { + ${KUBECTL} get ns ${DETIK_CLIENT_NAMESPACE} && skip "Namespace ${DETIK_CLIENT_NAMESPACE} already exists" + run ${KUBECTL} create ns ${DETIK_CLIENT_NAMESPACE} + assert_success +} + +#bats test_tags=phase:helm +@test "1.1 Vanilla install helm chart" { + run echo ${DETIK_CLIENT_NAMESPACE} + run echo ${OPERATOR_IMAGE} + ${HELM} status default && skip "Helm release 'default' already exists" + run ${HELM} install default \ + --set jenkins.namespace=${DETIK_CLIENT_NAMESPACE} \ + --set namespace=${DETIK_CLIENT_NAMESPACE} \ + --set operator.image=${OPERATOR_IMAGE} \ + chart/jenkins-operator + assert_success + assert ${HELM} status default + touch "chart/jenkins-operator/deploy.tmp" +} + +#bats test_tags=phase:helm +@test "1.2 Helm: check Jenkins operator pods status" { + [[ ! -f "chart/jenkins-operator/deploy.tmp" ]] && skip "Jenkins helm chart have not been deployed correctly" + run verify "there is 1 deployment named 'default-jenkins-operator'" + assert_success + + run verify "there is 1 pod named 'default-jenkins-operator-'" + assert_success + + run try "at most 20 times every 10s to get pods named 'default-jenkins-operator-' and verify that '.status.containerStatuses[?(@.name==\"jenkins-operator\")].ready' is 'true'" + assert_success +} + +#bats test_tags=phase:helm +@test "1.3 Helm: check Jenkins Pod status" { + [[ ! -f "chart/jenkins-operator/deploy.tmp" ]] && skip "Jenkins helm chart have not been deployed correctly" + run try "at most 20 times every 10s to get pods named 'jenkins-jenkins' and verify that '.status.containerStatuses[?(@.name==\"jenkins-master\")].ready' is 'true'" + assert_success + + run try "at most 20 times every 5s to get pods named 'jenkins-jenkins' and verify that '.status.containerStatuses[?(@.name==\"jenkins-master\")].ready' is 'true'" + assert_success +} + +#bats test_tags=phase:helm +@test "1.4 Helm: check Jenkins service status" { + [[ ! -f "chart/jenkins-operator/deploy.tmp" ]] && skip "Jenkins helm chart have not been deployed correctly" + run verify "there is 1 service named 'jenkins-operator-http-jenkins'" + assert_success + + run verify "there is 1 service named 'jenkins-operator-slave-jenkins'" + assert_success +} + +#bats test_tags=phase:helm +@test "1.5 Helm: check Jenkins configmaps created" { + [[ ! -f "chart/jenkins-operator/deploy.tmp" ]] && skip "Jenkins helm chart have not been deployed correctly" + run verify "there is 1 configmap named 'jenkins-operator-base-configuration-jenkins'" + assert_success + run verify "there is 1 configmap named 'jenkins-operator-init-configuration-jenkins'" + assert_success + run verify "there is 1 configmap named 'jenkins-operator-scripts-jenkins'" + assert_success +} + +#bats test_tags=phase:helm +@test "1.6 Helm: check Jenkins operator role status" { + [[ ! -f "chart/jenkins-operator/deploy.tmp" ]] && skip "Jenkins helm chart have not been deployed correctly" + run verify "there are 2 role named 'jenkins-operator*'" + assert_success + run verify "there is 1 role named 'leader-election-role'" + assert_success +} + +#bats test_tags=phase:helm +@test "1.7 Helm: check Jenkins operator role binding status" { + [[ ! -f "chart/jenkins-operator/deploy.tmp" ]] && skip "Jenkins helm chart have not been deployed correctly" + run verify "there is 1 rolebinding named 'jenkins-operator-jenkins'" + assert_success + run verify "there is 1 rolebinding named 'leader-election-rolebinding'" + assert_success +} + +#bats test_tags=phase:helm +@test "1.8 Helm: check Jenkins operator service account status" { + [[ ! -f "chart/jenkins-operator/deploy.tmp" ]] && skip "Jenkins helm chart have not been deployed correctly" + run verify "there are 2 serviceaccount named 'jenkins-operator*'" + assert_success +} + +@test "1.9 Helm: Clean" { + rm "chart/jenkins-operator/deploy.tmp" +} diff --git a/test/bats/test_helper.bash b/test/bats/test_helper.bash new file mode 100644 index 00000000..0fde73fa --- /dev/null +++ b/test/bats/test_helper.bash @@ -0,0 +1,13 @@ +_common_setup() { + export BATS_LIB_PATH="/usr/lib/" + bats_load_library bats-support + bats_load_library bats-assert + bats_load_library bats-file + bats_load_library bats-detik/detik.bash + + CONTEXT="kind-jenkins" + export DETIK_CLIENT_NAME="kubectl" + export DETIK_CLIENT_NAMESPACE="ns-$(git rev-parse --verify HEAD --short)" + export KUBECTL="kubectl --context=${CONTEXT} -n ${DETIK_CLIENT_NAMESPACE}" + export HELM="helm --kube-context=${CONTEXT} -n ${DETIK_CLIENT_NAMESPACE}" +} diff --git a/test/e2e/configuration_test.go b/test/e2e/configuration_test.go index 08671a5c..85453650 100644 --- a/test/e2e/configuration_test.go +++ b/test/e2e/configuration_test.go @@ -26,11 +26,11 @@ const e2e = "e2e" var expectedBasePluginsList = []plugins.Plugin{ plugins.Must(plugins.New("configuration-as-code:1569.vb_72405b_80249")), plugins.Must(plugins.New("git:5.0.0")), - plugins.Must(plugins.New("kubernetes:3883.v4d70a_a_a_df034")), + plugins.Must(plugins.New("kubernetes:3896.v19b_160fd9589")), plugins.Must(plugins.New("kubernetes-credentials-provider:1.209.v862c6e5fb_1ef")), - plugins.Must(plugins.New("job-dsl:1.81")), - plugins.Must(plugins.New("workflow-aggregator:590.v6a_d052e5a_a_b_5")), - plugins.Must(plugins.New("workflow-job:1282.ve6d865025906")), + plugins.Must(plugins.New("job-dsl:1.82")), + plugins.Must(plugins.New("workflow-aggregator:596.v8c21c963d92d")), + plugins.Must(plugins.New("workflow-job:1284.v2fe8ed4573d4")), } func createUserConfigurationSecret(namespace string, stringData map[string]string) { diff --git a/test/e2e/jenkins_pod_restart_test.go b/test/e2e/jenkins_pod_restart_test.go index 0c2fcf2d..70846c94 100644 --- a/test/e2e/jenkins_pod_restart_test.go +++ b/test/e2e/jenkins_pod_restart_test.go @@ -97,7 +97,7 @@ var _ = Describe("Jenkins controller", func() { Context("when running Jenkins safe restart", func() { It("authorization strategy is not overwritten", func() { - // TODO: @brokenpip3 temporary disable this flaky test + // TODO: @brokenpip3 temporary disable this flaky test Skip("Temporary skipping this test") WaitForJenkinsBaseConfigurationToComplete(jenkins) WaitForJenkinsUserConfigurationToComplete(jenkins) diff --git a/test/e2e/test_utility.go b/test/e2e/test_utility.go index 9f19c399..b5452564 100644 --- a/test/e2e/test_utility.go +++ b/test/e2e/test_utility.go @@ -21,7 +21,7 @@ import ( "sigs.k8s.io/controller-runtime/pkg/envtest" ) -const JenkinsTestImage = "jenkins/jenkins:2.375.3-lts" +const JenkinsTestImage = "jenkins/jenkins:2.387.1-lts" var ( Cfg *rest.Config diff --git a/test/helm/helm_test.go b/test/helm/helm_test.go index fa4dc5bf..a8206b30 100644 --- a/test/helm/helm_test.go +++ b/test/helm/helm_test.go @@ -47,7 +47,7 @@ var _ = Describe("Jenkins Controller", func() { cmd := exec.Command("../../bin/helm", "upgrade", "jenkins", "../../chart/jenkins-operator", "--namespace", namespace.Name, "--debug", "--set-string", fmt.Sprintf("jenkins.namespace=%s", namespace.Name), - "--set-string", fmt.Sprintf("jenkins.image=%s", "jenkins/jenkins:2.375.3-lts"), + "--set-string", fmt.Sprintf("jenkins.image=%s", "jenkins/jenkins:2.387.1-lts"), "--set-string", fmt.Sprintf("operator.image=%s", *imageName), "--install") output, err := cmd.CombinedOutput() Expect(err).NotTo(HaveOccurred(), string(output))