Merge pull request #510 from jenkinsci/operator-sdk-1.3.0

#494 Upgrade to Operator sdk 1.3.0
This commit is contained in:
Tomasz Sęk 2021-02-12 12:19:45 +01:00 committed by GitHub
commit 3dab502234
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
147 changed files with 12270 additions and 3484 deletions

5
.dockerignore Normal file
View File

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

View File

@ -23,9 +23,6 @@ jobs:
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
@ -44,17 +41,12 @@ jobs:
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
make minikube-start MINIKUBE_DRIVER='docker' CPUS_NUMBER=2
- name: Jenkins Operator - e2e
run: make build e2e
run: |
make e2e E2E_TEST_ARGS='-ginkgo.v'
- name: Jenkins Operator Helm Chart - e2e
run: make e2e BUILDTAGS=Helm E2E_TEST_SELECTOR='^.*Helm.*$'
#TODO Helm e2e test

3
.gitignore vendored
View File

@ -88,3 +88,6 @@ tags
### IntelliJ IDEA ###
*.iml
bin
testbin/*

33
Dockerfile Normal file
View File

@ -0,0 +1,33 @@
ARG GO_VERSION
# Build the manager binary
FROM golang:$GO_VERSION as builder
ARG CTIMEVAR
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 controllers/ controllers/
COPY internal/ internal/
COPY pkg/ pkg/
COPY version/ version/
COPY main.go main.go
# Build
RUN CGO_ENABLED=0 GOOS=linux GOARCH=amd64 GO111MODULE=on go build -ldflags "-w $CTIMEVAR" -o manager 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
WORKDIR /
COPY --from=builder /workspace/manager .
USER 65532:65532
ENTRYPOINT ["/manager"]

300
Makefile
View File

@ -1,74 +1,4 @@
# 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
include variables.mk
.PHONY: all
all: status checkmake clean build verify install container-runtime-build container-runtime-images ## Build the image
@ -121,7 +51,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 build/_output/bin/jenkins-operator $(BUILD_PATH)
CGO_ENABLED=0 go build -tags "$(BUILDTAGS)" ${GO_LDFLAGS} -o bin/manager $(BUILD_PATH)
.PHONY: static
static: ## Builds a static executable
@ -136,22 +66,22 @@ fmt: ## Verifies all files have been `gofmt`ed
@go fmt $(PACKAGES)
.PHONY: lint
HAS_GOLINT := $(shell which golangci-lint)
HAS_GOLINT := $(shell which $(PROJECT_DIR)/bin/golangci-lint)
lint: ## Verifies `golint` passes
@echo "+ $@"
ifndef HAS_GOLINT
go get github.com/golangci/golangci-lint/cmd/golangci-lint@v1.26.0
$(call go-get-tool,$(PROJECT_DIR)/bin/golangci-lint,github.com/golangci/golangci-lint/cmd/golangci-lint@v1.26.0)
endif
@golangci-lint run
@bin/golangci-lint run
.PHONY: goimports
HAS_GOIMPORTS := $(shell which goimports)
HAS_GOIMPORTS := $(shell which $(PROJECT_DIR)/bin/goimports)
goimports: ## Verifies `goimports` passes
@echo "+ $@"
ifndef HAS_GOIMPORTS
go get -u golang.org/x/tools/cmd/goimports
$(call go-get-tool,$(PROJECT_DIR)/bin/goimports,golang.org/x/tools/cmd/goimports@v0.1.0)
endif
@goimports -l -e $(shell find . -type f -name '*.go' -not -path "./vendor/*")
@bin/goimports -l -e $(shell find . -type f -name '*.go' -not -path "./vendor/*")
.PHONY: test
test: ## Runs the go tests
@ -159,47 +89,10 @@ test: ## Runs the go tests
@RUNNING_TESTS=1 go test -tags "$(BUILDTAGS) cgo" $(PACKAGES_FOR_UNIT_TESTS)
.PHONY: e2e
CURRENT_DIRECTORY := $(shell pwd)
e2e: container-runtime-build ## Runs e2e tests, you can use EXTRA_ARGS
e2e: deepcopy-gen ## Runs e2e tests, you can use EXTRA_ARGS
@echo "+ $@"
@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
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
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
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)
-jenkins-api-hostname=$(JENKINS_API_HOSTNAME) -jenkins-api-port=$(JENKINS_API_PORT) -jenkins-api-use-nodeport=$(JENKINS_API_USE_NODEPORT) $(E2E_TEST_ARGS)
.PHONY: vet
vet: ## Verifies `go vet` passes
@ -207,17 +100,18 @@ vet: ## Verifies `go vet` passes
@go vet $(PACKAGES)
.PHONY: staticcheck
HAS_STATICCHECK := $(shell which staticcheck)
PLATFORM = $(shell echo $(UNAME_S) | tr A-Z a-z)
HAS_STATICCHECK := $(shell which $(PROJECT_DIR)/bin/staticcheck)
staticcheck: ## Verifies `staticcheck` passes
@echo "+ $@"
ifndef HAS_STATICCHECK
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
$(eval TMP_DIR := $(shell mktemp -d))
wget -O $(TMP_DIR)/staticcheck_$(PLATFORM)_amd64.tar.gz https://github.com/dominikh/go-tools/releases/download/2020.1.3/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)
endif
@staticcheck $(PACKAGES)
@$(PROJECT_DIR)/bin/staticcheck $(PACKAGES)
.PHONY: cover
cover: ## Runs go test with coverage
@ -242,7 +136,7 @@ install: ## Installs the executable
.PHONY: run
run: export WATCH_NAMESPACE = $(NAMESPACE)
run: export OPERATOR_NAME = $(NAME)
run: build ## Run the executable, you can use EXTRA_ARGS
run: fmt vet manifests install-crds build ## Run the executable, you can use EXTRA_ARGS
@echo "+ $@"
ifeq ($(KUBERNETES_PROVIDER),minikube)
kubectl config use-context $(KUBECTL_CONTEXT)
@ -250,10 +144,8 @@ 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"
build/_output/bin/jenkins-operator $(OPERATOR_ARGS)
bin/manager $(OPERATOR_ARGS)
.PHONY: clean
clean: ## Cleanup any build binaries or packages
@ -309,13 +201,13 @@ container-runtime-login: ## Log in into the Docker repository
@echo "+ $@"
.PHONY: container-runtime-build
container-runtime-build: check-env ## Build the container
container-runtime-build: check-env deepcopy-gen ## Build the container
@echo "+ $@"
$(CONTAINER_RUNTIME_COMMAND) build \
--build-arg GO_VERSION=$(GO_VERSION) \
--build-arg OPERATOR_SDK_VERSION=$(OPERATOR_SDK_VERSION) \
--build-arg CTIMEVAR="$(CTIMEVAR)" \
-t $(DOCKER_REGISTRY):$(GITCOMMIT) . \
--file build/Dockerfile $(CONTAINER_RUNTIME_EXTRA_ARGS)
--file Dockerfile $(CONTAINER_RUNTIME_EXTRA_ARGS)
.PHONY: container-runtime-images
container-runtime-images: ## List all local containers
@ -368,27 +260,18 @@ container-runtime-run: ## Run the container in docker, you can use EXTRA_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
minikube-run: minikube-start run ## 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 the operator locally and use CodeReady Containers as Kubernetes cluster, you can use OPERATOR_ARGS
crc-run: crc-start run ## 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 deepcopy golang code
deepcopy-gen: generate ## 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)
@ -400,7 +283,7 @@ ifndef HAS_GEN_CRD_API_REFERENCE_DOCS
@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
$(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-minikube
check-minikube: ## Checks if KUBERNETES_PROVIDER is set to minikube
@ -418,23 +301,41 @@ ifneq ($(KUBERNETES_PROVIDER),crc)
$(error KUBERNETES_PROVIDER not set to 'crc')
endif
.PHONY: minikube-start
minikube-start: check-minikube ## Start minikube
.PHONY: minikube
HAS_MINIKUBE := $(shell which $(PROJECT_DIR)/bin/minikube)
minikube: ## Download minikube if it's not present
@echo "+ $@"
@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
ifndef HAS_MINIKUBE
mkdir -p $(PROJECT_DIR)/bin
wget -O $(PROJECT_DIR)/bin/minikube https://github.com/kubernetes/minikube/releases/download/v$(MINIKUBE_VERSION)/minikube-$(PLATFORM)-amd64
chmod +x $(PROJECT_DIR)/bin/minikube
endif
.PHONY: minikube-start
minikube-start: minikube check-minikube ## Start minikube
@echo "+ $@"
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 4096 --cpus $(CPUS_NUMBER)
.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
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: ## Bump the version in the version file. Set BUMP to [ patch | major | minor ]
bump-version: sembump ## Bump the version in the version file. Set BUMP to [ patch | major | minor ]
@echo "+ $@"
#@go get -u github.com/jessfraz/junk/sembump # update sembump tool FIXME
$(eval NEW_VERSION=$(shell sembump --kind $(BUMP) $(VERSION)))
$(eval NEW_VERSION=$(shell bin/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"
@ -493,8 +394,101 @@ helm-deploy: helm-package
helm repo index chart/ --url https://raw.githubusercontent.com/jenkinsci/kubernetes-operator/master/chart/jenkins-operator/
cd chart/ && mv jenkins-operator-*.tgz jenkins-operator
# Download hugo locally if necessary
HUGO = $(shell pwd)/bin/hugo
hugo:
$(call go-get-tool,$(HUGO),github.com/gohugoio/hugo@v0.62.2)
.PHONY: generate-docs
generate-docs: ## Re-generate docs directory from the website directory
generate-docs: hugo ## Re-generate docs directory from the website directory
@echo "+ $@"
rm -rf docs || echo "Cannot remove docs dir, ignoring"
hugo -s website -d ../docs
bin/hugo -s website -d ../docs
.PHONY: all-in-one-build
FILENAME := config/all_in_one_$(API_VERSION).yaml
all-in-one-build: ## Re-generate all-in-one yaml
@echo "+ $@"
> $(FILENAME)
cat config/rbac/leader_election_role.yaml >> $(FILENAME)
cat config/rbac/leader_election_role_binding.yaml >> $(FILENAME)
cat config/rbac/role.yaml >> $(FILENAME)
cat config/rbac/role_binding.yaml >> $(FILENAME)
cat config/manager/manager.yaml >> $(FILENAME)
##################### FROM OPERATOR SDK ########################
# Install CRDs into a cluster
install-crds: manifests kustomize
$(KUSTOMIZE) build config/crd | kubectl apply -f -
# Uninstall CRDs from a cluster
uninstall-crds: manifests kustomize
$(KUSTOMIZE) build config/crd | kubectl delete -f -
# Deploy controller in the configured Kubernetes cluster in ~/.kube/config
deploy: manifests kustomize
cd config/manager && $(KUSTOMIZE) edit set image controller=$(DOCKER_REGISTRY):$(GITCOMMIT)
$(KUSTOMIZE) build config/default | kubectl apply -f -
# UnDeploy controller from the configured Kubernetes cluster in ~/.kube/config
undeploy:
$(KUSTOMIZE) build config/default | kubectl delete -f -
# Generate manifests e.g. CRD, RBAC etc.
manifests: controller-gen all-in-one-build
$(CONTROLLER_GEN) $(CRD_OPTIONS) rbac:roleName=manager-role webhook paths="./..." output:crd:artifacts:config=config/crd/bases
# Generate code
generate: controller-gen
$(CONTROLLER_GEN) object:headerFile="hack/boilerplate.go.txt" paths="./..."
# Download controller-gen locally if necessary
CONTROLLER_GEN = $(shell pwd)/bin/controller-gen
controller-gen:
$(call go-get-tool,$(CONTROLLER_GEN),sigs.k8s.io/controller-tools/cmd/controller-gen@v0.4.1)
# Download kustomize locally if necessary
KUSTOMIZE = $(shell pwd)/bin/kustomize
kustomize:
$(call go-get-tool,$(KUSTOMIZE),sigs.k8s.io/kustomize/kustomize/v3@v3.8.7)
# go-get-tool will 'go get' any package $2 and install it to $1.
define go-get-tool
@[ -f $(1) ] || { \
set -e ;\
TMP_DIR=$$(mktemp -d) ;\
cd $$TMP_DIR ;\
go mod init tmp ;\
echo "Downloading $(2)" ;\
GOBIN=$(PROJECT_DIR)/bin go get $(2) ;\
rm -rf $$TMP_DIR ;\
}
endef
.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/v1.3.0/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=$(DOCKER_ORGANIZATION)/$(DOCKER_REGISTRY):$(VERSION_TAG)
$(KUSTOMIZE) build config/manifests | bin/operator-sdk generate bundle -q --overwrite --version $(VERSION) $(BUNDLE_METADATA_OPTS)
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
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);

13
PROJECT Normal file
View File

@ -0,0 +1,13 @@
domain: jenkins.io
layout: go.kubebuilder.io/v3
projectName: jenkins-operator
repo: github.com/jenkinsci/kubernetes-operator
resources:
- crdVersion: v1
group: jenkins.io
kind: Jenkins
version: v1alpha2
version: 3-alpha
plugins:
manifests.sdk.operatorframework.io/v2: {}
scorecard.sdk.operatorframework.io/v2: {}

View File

@ -1,4 +1,5 @@
// Package v1alpha2 contains API Schema definitions for the jenkins.io v1alpha2 API group
// +k8s:deepcopy-gen=package,register
// +kubebuilder:object:generate=true
// +groupName=jenkins.io
package v1alpha2

View File

@ -6,7 +6,7 @@ import (
metav1 "k8s.io/apimachinery/pkg/apis/meta/v1"
)
// JenkinsSpec defines the desired state of the Jenkins.
// JenkinsSpec defines the desired state of Jenkins
// +k8s:openapi-gen=true
type JenkinsSpec struct {
// Master represents Jenkins master pod properties and Jenkins plugins.
@ -264,14 +264,6 @@ type JenkinsMaster struct {
// +optional
Annotations map[string]string `json:"annotations,omitempty"`
// Annotations is an unstructured key value map stored with a resource that may be
// set by external tools to store and retrieve arbitrary metadata. They are not
// queryable and should be preserved when modifying objects.
// More info: http://kubernetes.io/docs/user-guide/annotations
// Deprecated: will be removed in the future, please use Annotations(annotations)
// +optional
AnnotationsDeprecated map[string]string `json:"masterAnnotations,omitempty"`
// Map of string keys and values that can be used to organize and categorize
// (scope and select) objects. May match selectors of replication controllers
// and services.
@ -489,12 +481,13 @@ type JenkinsStatus struct {
AppliedGroovyScripts []AppliedGroovyScript `json:"appliedGroovyScripts,omitempty"`
}
// +kubebuilder:object:root=true
// +kubebuilder:subresource:status
// +genclient
// +k8s:deepcopy-gen:interfaces=k8s.io/apimachinery/pkg/runtime.Object
// Jenkins is the Schema for the jenkins API
// +k8s:openapi-gen=true
// +kubebuilder:subresource:status
type Jenkins struct {
metav1.TypeMeta `json:",inline"`
metav1.ObjectMeta `json:"metadata,omitempty"`
@ -506,9 +499,10 @@ type Jenkins struct {
Status JenkinsStatus `json:"status,omitempty"`
}
// +kubebuilder:object:root=true
// +k8s:deepcopy-gen:interfaces=k8s.io/apimachinery/pkg/runtime.Object
// JenkinsList contains a list of Jenkins.
// JenkinsList contains a list of Jenkins
type JenkinsList struct {
metav1.TypeMeta `json:",inline"`
metav1.ListMeta `json:"metadata,omitempty"`

View File

@ -15,11 +15,17 @@ const (
)
var (
// GroupVersion is group version used to register these objects
GroupVersion = schema.GroupVersion{Group: "jenkins.io", Version: "v1alpha2"}
// SchemeGroupVersion is group version used to register these objects
SchemeGroupVersion = schema.GroupVersion{Group: "jenkins.io", Version: "v1alpha2"}
// SchemeBuilder is used to add go types to the GroupVersionKind scheme
SchemeBuilder = &scheme.Builder{GroupVersion: SchemeGroupVersion}
SchemeBuilder = &scheme.Builder{GroupVersion: GroupVersion}
// AddToScheme adds the types in this group-version to the given scheme.
AddToScheme = SchemeBuilder.AddToScheme
)
// GetObjectKind returns Jenkins object kind
@ -47,5 +53,4 @@ func JenkinsTypeMeta() metav1.TypeMeta {
func init() {
SchemeBuilder.Register(&Jenkins{}, &JenkinsList{})
SchemeBuilder.Register(&JenkinsImage{}, &JenkinsImageList{})
}

View File

@ -1,11 +1,27 @@
// +build !ignore_autogenerated
// Code generated by operator-sdk. DO NOT EDIT.
/*
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.
*/
// Code generated by controller-gen. DO NOT EDIT.
package v1alpha2
import (
v1 "k8s.io/api/core/v1"
corev1 "k8s.io/api/core/v1"
rbacv1 "k8s.io/api/rbac/v1"
runtime "k8s.io/apimachinery/pkg/runtime"
)
@ -13,7 +29,6 @@ import (
// DeepCopyInto is an autogenerated deepcopy function, copying the receiver, writing into out. in must be non-nil.
func (in *AppliedGroovyScript) DeepCopyInto(out *AppliedGroovyScript) {
*out = *in
return
}
// DeepCopy is an autogenerated deepcopy function, copying the receiver, creating a new AppliedGroovyScript.
@ -30,7 +45,6 @@ func (in *AppliedGroovyScript) DeepCopy() *AppliedGroovyScript {
func (in *Backup) DeepCopyInto(out *Backup) {
*out = *in
in.Action.DeepCopyInto(&out.Action)
return
}
// DeepCopy is an autogenerated deepcopy function, copying the receiver, creating a new Backup.
@ -46,7 +60,6 @@ func (in *Backup) DeepCopy() *Backup {
// DeepCopyInto is an autogenerated deepcopy function, copying the receiver, writing into out. in must be non-nil.
func (in *ConfigMapRef) DeepCopyInto(out *ConfigMapRef) {
*out = *in
return
}
// DeepCopy is an autogenerated deepcopy function, copying the receiver, creating a new ConfigMapRef.
@ -63,7 +76,6 @@ func (in *ConfigMapRef) DeepCopy() *ConfigMapRef {
func (in *ConfigurationAsCode) DeepCopyInto(out *ConfigurationAsCode) {
*out = *in
in.Customization.DeepCopyInto(&out.Customization)
return
}
// DeepCopy is an autogenerated deepcopy function, copying the receiver, creating a new ConfigurationAsCode.
@ -92,51 +104,50 @@ func (in *Container) DeepCopyInto(out *Container) {
}
if in.Ports != nil {
in, out := &in.Ports, &out.Ports
*out = make([]v1.ContainerPort, len(*in))
*out = make([]corev1.ContainerPort, len(*in))
copy(*out, *in)
}
if in.EnvFrom != nil {
in, out := &in.EnvFrom, &out.EnvFrom
*out = make([]v1.EnvFromSource, len(*in))
*out = make([]corev1.EnvFromSource, len(*in))
for i := range *in {
(*in)[i].DeepCopyInto(&(*out)[i])
}
}
if in.Env != nil {
in, out := &in.Env, &out.Env
*out = make([]v1.EnvVar, len(*in))
*out = make([]corev1.EnvVar, len(*in))
for i := range *in {
(*in)[i].DeepCopyInto(&(*out)[i])
}
}
if in.VolumeMounts != nil {
in, out := &in.VolumeMounts, &out.VolumeMounts
*out = make([]v1.VolumeMount, len(*in))
*out = make([]corev1.VolumeMount, len(*in))
for i := range *in {
(*in)[i].DeepCopyInto(&(*out)[i])
}
}
if in.LivenessProbe != nil {
in, out := &in.LivenessProbe, &out.LivenessProbe
*out = new(v1.Probe)
*out = new(corev1.Probe)
(*in).DeepCopyInto(*out)
}
if in.ReadinessProbe != nil {
in, out := &in.ReadinessProbe, &out.ReadinessProbe
*out = new(v1.Probe)
*out = new(corev1.Probe)
(*in).DeepCopyInto(*out)
}
if in.Lifecycle != nil {
in, out := &in.Lifecycle, &out.Lifecycle
*out = new(v1.Lifecycle)
*out = new(corev1.Lifecycle)
(*in).DeepCopyInto(*out)
}
if in.SecurityContext != nil {
in, out := &in.SecurityContext, &out.SecurityContext
*out = new(v1.SecurityContext)
*out = new(corev1.SecurityContext)
(*in).DeepCopyInto(*out)
}
return
}
// DeepCopy is an autogenerated deepcopy function, copying the receiver, creating a new Container.
@ -158,7 +169,6 @@ func (in *Customization) DeepCopyInto(out *Customization) {
*out = make([]ConfigMapRef, len(*in))
copy(*out, *in)
}
return
}
// DeepCopy is an autogenerated deepcopy function, copying the receiver, creating a new Customization.
@ -175,7 +185,6 @@ func (in *Customization) DeepCopy() *Customization {
func (in *GroovyScripts) DeepCopyInto(out *GroovyScripts) {
*out = *in
in.Customization.DeepCopyInto(&out.Customization)
return
}
// DeepCopy is an autogenerated deepcopy function, copying the receiver, creating a new GroovyScripts.
@ -193,10 +202,9 @@ func (in *Handler) DeepCopyInto(out *Handler) {
*out = *in
if in.Exec != nil {
in, out := &in.Exec, &out.Exec
*out = new(v1.ExecAction)
*out = new(corev1.ExecAction)
(*in).DeepCopyInto(*out)
}
return
}
// DeepCopy is an autogenerated deepcopy function, copying the receiver, creating a new Handler.
@ -209,22 +217,6 @@ func (in *Handler) DeepCopy() *Handler {
return out
}
// DeepCopyInto is an autogenerated deepcopy function, copying the receiver, writing into out. in must be non-nil.
func (in *Image) DeepCopyInto(out *Image) {
*out = *in
return
}
// DeepCopy is an autogenerated deepcopy function, copying the receiver, creating a new Image.
func (in *Image) DeepCopy() *Image {
if in == nil {
return nil
}
out := new(Image)
in.DeepCopyInto(out)
return out
}
// DeepCopyInto is an autogenerated deepcopy function, copying the receiver, writing into out. in must be non-nil.
func (in *Jenkins) DeepCopyInto(out *Jenkins) {
*out = *in
@ -232,7 +224,6 @@ func (in *Jenkins) DeepCopyInto(out *Jenkins) {
in.ObjectMeta.DeepCopyInto(&out.ObjectMeta)
in.Spec.DeepCopyInto(&out.Spec)
in.Status.DeepCopyInto(&out.Status)
return
}
// DeepCopy is an autogenerated deepcopy function, copying the receiver, creating a new Jenkins.
@ -256,7 +247,6 @@ func (in *Jenkins) DeepCopyObject() runtime.Object {
// DeepCopyInto is an autogenerated deepcopy function, copying the receiver, writing into out. in must be non-nil.
func (in *JenkinsAPISettings) DeepCopyInto(out *JenkinsAPISettings) {
*out = *in
return
}
// DeepCopy is an autogenerated deepcopy function, copying the receiver, creating a new JenkinsAPISettings.
@ -269,110 +259,6 @@ func (in *JenkinsAPISettings) DeepCopy() *JenkinsAPISettings {
return out
}
// DeepCopyInto is an autogenerated deepcopy function, copying the receiver, writing into out. in must be non-nil.
func (in *JenkinsImage) DeepCopyInto(out *JenkinsImage) {
*out = *in
out.TypeMeta = in.TypeMeta
in.ObjectMeta.DeepCopyInto(&out.ObjectMeta)
in.Spec.DeepCopyInto(&out.Spec)
in.Status.DeepCopyInto(&out.Status)
return
}
// DeepCopy is an autogenerated deepcopy function, copying the receiver, creating a new JenkinsImage.
func (in *JenkinsImage) DeepCopy() *JenkinsImage {
if in == nil {
return nil
}
out := new(JenkinsImage)
in.DeepCopyInto(out)
return out
}
// DeepCopyObject is an autogenerated deepcopy function, copying the receiver, creating a new runtime.Object.
func (in *JenkinsImage) DeepCopyObject() runtime.Object {
if c := in.DeepCopy(); c != nil {
return c
}
return nil
}
// DeepCopyInto is an autogenerated deepcopy function, copying the receiver, writing into out. in must be non-nil.
func (in *JenkinsImageList) DeepCopyInto(out *JenkinsImageList) {
*out = *in
out.TypeMeta = in.TypeMeta
in.ListMeta.DeepCopyInto(&out.ListMeta)
if in.Items != nil {
in, out := &in.Items, &out.Items
*out = make([]JenkinsImage, len(*in))
for i := range *in {
(*in)[i].DeepCopyInto(&(*out)[i])
}
}
return
}
// DeepCopy is an autogenerated deepcopy function, copying the receiver, creating a new JenkinsImageList.
func (in *JenkinsImageList) DeepCopy() *JenkinsImageList {
if in == nil {
return nil
}
out := new(JenkinsImageList)
in.DeepCopyInto(out)
return out
}
// DeepCopyObject is an autogenerated deepcopy function, copying the receiver, creating a new runtime.Object.
func (in *JenkinsImageList) DeepCopyObject() runtime.Object {
if c := in.DeepCopy(); c != nil {
return c
}
return nil
}
// DeepCopyInto is an autogenerated deepcopy function, copying the receiver, writing into out. in must be non-nil.
func (in *JenkinsImageSpec) DeepCopyInto(out *JenkinsImageSpec) {
*out = *in
out.BaseImage = in.BaseImage
if in.Plugins != nil {
in, out := &in.Plugins, &out.Plugins
*out = make([]JenkinsPlugin, len(*in))
copy(*out, *in)
}
return
}
// DeepCopy is an autogenerated deepcopy function, copying the receiver, creating a new JenkinsImageSpec.
func (in *JenkinsImageSpec) DeepCopy() *JenkinsImageSpec {
if in == nil {
return nil
}
out := new(JenkinsImageSpec)
in.DeepCopyInto(out)
return out
}
// DeepCopyInto is an autogenerated deepcopy function, copying the receiver, writing into out. in must be non-nil.
func (in *JenkinsImageStatus) DeepCopyInto(out *JenkinsImageStatus) {
*out = *in
if in.InstalledPlugins != nil {
in, out := &in.InstalledPlugins, &out.InstalledPlugins
*out = make([]JenkinsPlugin, len(*in))
copy(*out, *in)
}
return
}
// DeepCopy is an autogenerated deepcopy function, copying the receiver, creating a new JenkinsImageStatus.
func (in *JenkinsImageStatus) DeepCopy() *JenkinsImageStatus {
if in == nil {
return nil
}
out := new(JenkinsImageStatus)
in.DeepCopyInto(out)
return out
}
// DeepCopyInto is an autogenerated deepcopy function, copying the receiver, writing into out. in must be non-nil.
func (in *JenkinsList) DeepCopyInto(out *JenkinsList) {
*out = *in
@ -385,7 +271,6 @@ func (in *JenkinsList) DeepCopyInto(out *JenkinsList) {
(*in)[i].DeepCopyInto(&(*out)[i])
}
}
return
}
// DeepCopy is an autogenerated deepcopy function, copying the receiver, creating a new JenkinsList.
@ -416,13 +301,6 @@ func (in *JenkinsMaster) DeepCopyInto(out *JenkinsMaster) {
(*out)[key] = val
}
}
if in.AnnotationsDeprecated != nil {
in, out := &in.AnnotationsDeprecated, &out.AnnotationsDeprecated
*out = make(map[string]string, len(*in))
for key, val := range *in {
(*out)[key] = val
}
}
if in.Labels != nil {
in, out := &in.Labels, &out.Labels
*out = make(map[string]string, len(*in))
@ -439,7 +317,7 @@ func (in *JenkinsMaster) DeepCopyInto(out *JenkinsMaster) {
}
if in.SecurityContext != nil {
in, out := &in.SecurityContext, &out.SecurityContext
*out = new(v1.PodSecurityContext)
*out = new(corev1.PodSecurityContext)
(*in).DeepCopyInto(*out)
}
if in.Containers != nil {
@ -451,19 +329,19 @@ func (in *JenkinsMaster) DeepCopyInto(out *JenkinsMaster) {
}
if in.ImagePullSecrets != nil {
in, out := &in.ImagePullSecrets, &out.ImagePullSecrets
*out = make([]v1.LocalObjectReference, len(*in))
*out = make([]corev1.LocalObjectReference, len(*in))
copy(*out, *in)
}
if in.Volumes != nil {
in, out := &in.Volumes, &out.Volumes
*out = make([]v1.Volume, len(*in))
*out = make([]corev1.Volume, len(*in))
for i := range *in {
(*in)[i].DeepCopyInto(&(*out)[i])
}
}
if in.Tolerations != nil {
in, out := &in.Tolerations, &out.Tolerations
*out = make([]v1.Toleration, len(*in))
*out = make([]corev1.Toleration, len(*in))
for i := range *in {
(*in)[i].DeepCopyInto(&(*out)[i])
}
@ -478,7 +356,6 @@ func (in *JenkinsMaster) DeepCopyInto(out *JenkinsMaster) {
*out = make([]Plugin, len(*in))
copy(*out, *in)
}
return
}
// DeepCopy is an autogenerated deepcopy function, copying the receiver, creating a new JenkinsMaster.
@ -491,22 +368,6 @@ func (in *JenkinsMaster) DeepCopy() *JenkinsMaster {
return out
}
// DeepCopyInto is an autogenerated deepcopy function, copying the receiver, writing into out. in must be non-nil.
func (in *JenkinsPlugin) DeepCopyInto(out *JenkinsPlugin) {
*out = *in
return
}
// DeepCopy is an autogenerated deepcopy function, copying the receiver, creating a new JenkinsPlugin.
func (in *JenkinsPlugin) DeepCopy() *JenkinsPlugin {
if in == nil {
return nil
}
out := new(JenkinsPlugin)
in.DeepCopyInto(out)
return out
}
// DeepCopyInto is an autogenerated deepcopy function, copying the receiver, writing into out. in must be non-nil.
func (in *JenkinsSpec) DeepCopyInto(out *JenkinsSpec) {
*out = *in
@ -536,7 +397,6 @@ func (in *JenkinsSpec) DeepCopyInto(out *JenkinsSpec) {
}
in.ServiceAccount.DeepCopyInto(&out.ServiceAccount)
out.JenkinsAPISettings = in.JenkinsAPISettings
return
}
// DeepCopy is an autogenerated deepcopy function, copying the receiver, creating a new JenkinsSpec.
@ -574,7 +434,6 @@ func (in *JenkinsStatus) DeepCopyInto(out *JenkinsStatus) {
*out = make([]AppliedGroovyScript, len(*in))
copy(*out, *in)
}
return
}
// DeepCopy is an autogenerated deepcopy function, copying the receiver, creating a new JenkinsStatus.
@ -591,7 +450,6 @@ func (in *JenkinsStatus) DeepCopy() *JenkinsStatus {
func (in *Mailgun) DeepCopyInto(out *Mailgun) {
*out = *in
out.APIKeySecretKeySelector = in.APIKeySecretKeySelector
return
}
// DeepCopy is an autogenerated deepcopy function, copying the receiver, creating a new Mailgun.
@ -608,7 +466,6 @@ func (in *Mailgun) DeepCopy() *Mailgun {
func (in *MicrosoftTeams) DeepCopyInto(out *MicrosoftTeams) {
*out = *in
out.WebHookURLSecretKeySelector = in.WebHookURLSecretKeySelector
return
}
// DeepCopy is an autogenerated deepcopy function, copying the receiver, creating a new MicrosoftTeams.
@ -644,7 +501,6 @@ func (in *Notification) DeepCopyInto(out *Notification) {
*out = new(SMTP)
**out = **in
}
return
}
// DeepCopy is an autogenerated deepcopy function, copying the receiver, creating a new Notification.
@ -660,7 +516,6 @@ func (in *Notification) DeepCopy() *Notification {
// DeepCopyInto is an autogenerated deepcopy function, copying the receiver, writing into out. in must be non-nil.
func (in *Plugin) DeepCopyInto(out *Plugin) {
*out = *in
return
}
// DeepCopy is an autogenerated deepcopy function, copying the receiver, creating a new Plugin.
@ -678,7 +533,6 @@ func (in *Restore) DeepCopyInto(out *Restore) {
*out = *in
in.Action.DeepCopyInto(&out.Action)
in.GetLatestAction.DeepCopyInto(&out.GetLatestAction)
return
}
// DeepCopy is an autogenerated deepcopy function, copying the receiver, creating a new Restore.
@ -696,7 +550,6 @@ func (in *SMTP) DeepCopyInto(out *SMTP) {
*out = *in
out.UsernameSecretKeySelector = in.UsernameSecretKeySelector
out.PasswordSecretKeySelector = in.PasswordSecretKeySelector
return
}
// DeepCopy is an autogenerated deepcopy function, copying the receiver, creating a new SMTP.
@ -713,7 +566,6 @@ func (in *SMTP) DeepCopy() *SMTP {
func (in *SecretKeySelector) DeepCopyInto(out *SecretKeySelector) {
*out = *in
out.LocalObjectReference = in.LocalObjectReference
return
}
// DeepCopy is an autogenerated deepcopy function, copying the receiver, creating a new SecretKeySelector.
@ -729,7 +581,6 @@ func (in *SecretKeySelector) DeepCopy() *SecretKeySelector {
// DeepCopyInto is an autogenerated deepcopy function, copying the receiver, writing into out. in must be non-nil.
func (in *SecretRef) DeepCopyInto(out *SecretRef) {
*out = *in
return
}
// DeepCopy is an autogenerated deepcopy function, copying the receiver, creating a new SecretRef.
@ -745,7 +596,6 @@ func (in *SecretRef) DeepCopy() *SecretRef {
// DeepCopyInto is an autogenerated deepcopy function, copying the receiver, writing into out. in must be non-nil.
func (in *SeedJob) DeepCopyInto(out *SeedJob) {
*out = *in
return
}
// DeepCopy is an autogenerated deepcopy function, copying the receiver, creating a new SeedJob.
@ -780,7 +630,6 @@ func (in *Service) DeepCopyInto(out *Service) {
*out = make([]string, len(*in))
copy(*out, *in)
}
return
}
// DeepCopy is an autogenerated deepcopy function, copying the receiver, creating a new Service.
@ -803,7 +652,6 @@ func (in *ServiceAccount) DeepCopyInto(out *ServiceAccount) {
(*out)[key] = val
}
}
return
}
// DeepCopy is an autogenerated deepcopy function, copying the receiver, creating a new ServiceAccount.
@ -820,7 +668,6 @@ func (in *ServiceAccount) DeepCopy() *ServiceAccount {
func (in *Slack) DeepCopyInto(out *Slack) {
*out = *in
out.WebHookURLSecretKeySelector = in.WebHookURLSecretKeySelector
return
}
// DeepCopy is an autogenerated deepcopy function, copying the receiver, creating a new Slack.

View File

@ -1,21 +0,0 @@
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,277 +0,0 @@
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,7 +1,7 @@
# Setup variables for the Makefile
NAME=kubernetes-operator
OPERATOR_SDK_VERSION=0.17.0
GO_VERSION=1.14.2
OPERATOR_SDK_VERSION=1.3.0
GO_VERSION=1.15.6
PKG=github.com/jenkinsci/kubernetes-operator
DOCKER_ORGANIZATION=virtuslab
DOCKER_REGISTRY=jenkins-operator

View File

@ -2,9 +2,9 @@ KUBERNETES_PROVIDER=minikube
MINIKUBE_KUBERNETES_VERSION=v1.17.4
MINIKUBE_DRIVER=virtualbox
MINIKUBE_VERSION=1.4.0
MINIKUBE_VERSION=1.17.1
KUBECTL_CONTEXT=minikube
JENKINS_API_HOSTNAME_COMMAND=minikube ip
JENKINS_API_HOSTNAME_COMMAND=bin/minikube ip
JENKINS_API_PORT=0
JENKINS_API_USE_NODEPORT=true

View File

@ -0,0 +1,227 @@
---
# 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
---
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: default
namespace: default
---
apiVersion: rbac.authorization.k8s.io/v1
kind: ClusterRole
metadata:
creationTimestamp: null
name: manager-role
rules:
- apiGroups:
- ""
resources:
- services
- configmaps
- secrets
- serviceaccounts
verbs:
- get
- create
- update
- list
- watch
- apiGroups:
- apps
resources:
- deployments
- daemonsets
- replicasets
- statefulsets
verbs:
- '*'
- apiGroups:
- rbac.authorization.k8s.io
resources:
- roles
- rolebindings
verbs:
- 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
---
apiVersion: rbac.authorization.k8s.io/v1
kind: ClusterRoleBinding
metadata:
name: manager-rolebinding
roleRef:
apiGroup: rbac.authorization.k8s.io
kind: ClusterRole
name: manager-role
subjects:
- kind: ServiceAccount
name: default
namespace: default
---
apiVersion: apps/v1
kind: Deployment
metadata:
name: jenkins-operator
namespace: default
labels:
control-plane: controller-manager
spec:
selector:
matchLabels:
control-plane: controller-manager
replicas: 1
template:
metadata:
labels:
control-plane: controller-manager
spec:
securityContext:
runAsUser: 65532
containers:
- command:
- /manager
args:
- --leader-elect
image: virtuslab/jenkins-operator:v0.5.0
name: jenkins-operator
imagePullPolicy: IfNotPresent
securityContext:
allowPrivilegeEscalation: false
livenessProbe:
httpGet:
path: /healthz
port: 8081
initialDelaySeconds: 15
periodSeconds: 20
readinessProbe:
httpGet:
path: /readyz
port: 8081
initialDelaySeconds: 5
periodSeconds: 10
resources:
limits:
cpu: 100m
memory: 30Mi
requests:
cpu: 100m
memory: 20Mi
env:
- name: WATCH_NAMESPACE
valueFrom:
fieldRef:
fieldPath: metadata.namespace
terminationGracePeriodSeconds: 10

View File

@ -0,0 +1,25 @@
# The following manifests contain a self-signed issuer CR and a certificate CR.
# More document can be found at https://docs.cert-manager.io
# WARNING: Targets CertManager v1.0. Check https://cert-manager.io/docs/installation/upgrading/ for breaking changes.
apiVersion: cert-manager.io/v1
kind: Issuer
metadata:
name: selfsigned-issuer
namespace: default
spec:
selfSigned: {}
---
apiVersion: cert-manager.io/v1
kind: Certificate
metadata:
name: serving-cert # this name should match the one appeared in kustomizeconfig.yaml
namespace: default
spec:
# $(SERVICE_NAME) and $(SERVICE_NAMESPACE) will be substituted by kustomize
dnsNames:
- $(SERVICE_NAME).$(SERVICE_NAMESPACE).svc
- $(SERVICE_NAME).$(SERVICE_NAMESPACE).svc.cluster.local
issuerRef:
kind: Issuer
name: selfsigned-issuer
secretName: webhook-server-cert # this secret will not be prefixed, since it's not managed by kustomize

View File

@ -0,0 +1,5 @@
resources:
- certificate.yaml
configurations:
- kustomizeconfig.yaml

View File

@ -0,0 +1,16 @@
# This configuration is for teaching kustomize how to update name ref and var substitution
nameReference:
- kind: Issuer
group: cert-manager.io
fieldSpecs:
- kind: Certificate
group: cert-manager.io
path: spec/issuerRef/name
varReference:
- kind: Certificate
group: cert-manager.io
path: spec/commonName
- kind: Certificate
group: cert-manager.io
path: spec/dnsNames

File diff suppressed because it is too large Load Diff

View File

@ -0,0 +1,21 @@
# This kustomization.yaml is not intended to be run by itself,
# since it depends on service name and namespace that are out of this kustomize package.
# It should be run by config/default
resources:
- bases/jenkins.io_jenkins.yaml
# +kubebuilder:scaffold:crdkustomizeresource
patchesStrategicMerge:
# [WEBHOOK] To enable webhook, uncomment all the sections with [WEBHOOK] prefix.
# patches here are for enabling the conversion webhook for each CRD
#- patches/webhook_in_jenkins.yaml
# +kubebuilder:scaffold:crdkustomizewebhookpatch
# [CERTMANAGER] To enable webhook, uncomment all the sections with [CERTMANAGER] prefix.
# patches here are for enabling the CA injection for each CRD
#- patches/cainjection_in_jenkins.yaml
# +kubebuilder:scaffold:crdkustomizecainjectionpatch
# the following config is for teaching kustomize how to do kustomization for CRDs.
configurations:
- kustomizeconfig.yaml

View File

@ -0,0 +1,19 @@
# This file is for teaching kustomize how to substitute name and namespace reference in CRD
nameReference:
- kind: Service
version: v1
fieldSpecs:
- kind: CustomResourceDefinition
version: v1
group: apiextensions.k8s.io
path: spec/conversion/webhook/clientConfig/service/name
namespace:
- kind: CustomResourceDefinition
version: v1
group: apiextensions.k8s.io
path: spec/conversion/webhook/clientConfig/service/namespace
create: false
varReference:
- path: metadata/annotations

View File

@ -0,0 +1,7 @@
# The following patch adds a directive for certmanager to inject CA into the CRD
apiVersion: apiextensions.k8s.io/v1
kind: CustomResourceDefinition
metadata:
annotations:
cert-manager.io/inject-ca-from: $(CERTIFICATE_NAMESPACE)/$(CERTIFICATE_NAME)
name: jenkins.jenkins.io

View File

@ -0,0 +1,14 @@
# The following patch enables a conversion webhook for the CRD
apiVersion: apiextensions.k8s.io/v1
kind: CustomResourceDefinition
metadata:
name: jenkins.jenkins.io
spec:
conversion:
strategy: Webhook
webhook:
clientConfig:
service:
namespace: default
name: webhook-service
path: /convert

View File

@ -0,0 +1,74 @@
# Adds namespace to all resources.
namespace: jenkins-operator
# Value of this field is prepended to the
# names of all resources, e.g. a deployment named
# "wordpress" becomes "alices-wordpress".
# Note that it should also match with the prefix (text before '-') of the namespace
# field above.
namePrefix: jenkins-operator-
# Labels to add to all resources and selectors.
#commonLabels:
# someName: someValue
bases:
- ../crd
- ../rbac
- ../manager
# [WEBHOOK] To enable webhook, uncomment all the sections with [WEBHOOK] prefix including the one in
# crd/kustomization.yaml
#- ../webhook
# [CERTMANAGER] To enable cert-manager, uncomment all sections with 'CERTMANAGER'. 'WEBHOOK' components are required.
#- ../certmanager
# [PROMETHEUS] To enable prometheus monitor, uncomment all sections with 'PROMETHEUS'.
#- ../prometheus
patchesStrategicMerge:
# Protect the /metrics endpoint by putting it behind auth.
# If you want your controller-manager to expose the /metrics
# endpoint w/o any authn/z, please comment the following line.
- manager_auth_proxy_patch.yaml
# Mount the controller config file for loading manager configurations
# through a ComponentConfig type
#- manager_config_patch.yaml
# [WEBHOOK] To enable webhook, uncomment all the sections with [WEBHOOK] prefix including the one in
# crd/kustomization.yaml
#- manager_webhook_patch.yaml
# [CERTMANAGER] To enable cert-manager, uncomment all sections with 'CERTMANAGER'.
# Uncomment 'CERTMANAGER' sections in crd/kustomization.yaml to enable the CA injection in the admission webhooks.
# 'CERTMANAGER' needs to be enabled to use ca injection
#- webhookcainjection_patch.yaml
# the following config is for teaching kustomize how to do var substitution
vars:
# [CERTMANAGER] To enable cert-manager, uncomment all sections with 'CERTMANAGER' prefix.
#- name: CERTIFICATE_NAMESPACE # namespace of the certificate CR
# objref:
# kind: Certificate
# group: cert-manager.io
# version: v1
# name: serving-cert # this name should match the one in certificate.yaml
# fieldref:
# fieldpath: metadata.namespace
#- name: CERTIFICATE_NAME
# objref:
# kind: Certificate
# group: cert-manager.io
# version: v1
# name: serving-cert # this name should match the one in certificate.yaml
#- name: SERVICE_NAMESPACE # namespace of the service
# objref:
# kind: Service
# version: v1
# name: webhook-service
# fieldref:
# fieldpath: metadata.namespace
#- name: SERVICE_NAME
# objref:
# kind: Service
# version: v1
# name: webhook-service

View File

@ -0,0 +1,26 @@
# This patch inject a sidecar container which is a HTTP proxy for the
# controller manager, it performs RBAC authorization against the Kubernetes API using SubjectAccessReviews.
apiVersion: apps/v1
kind: Deployment
metadata:
name: controller-manager
namespace: default
spec:
template:
spec:
containers:
- name: kube-rbac-proxy
image: gcr.io/kubebuilder/kube-rbac-proxy:v0.5.0
args:
- "--secure-listen-address=0.0.0.0:8443"
- "--upstream=http://127.0.0.1:8080/"
- "--logtostderr=true"
- "--v=10"
ports:
- containerPort: 8443
name: https
- name: manager
args:
- "--health-probe-bind-address=:8081"
- "--metrics-bind-address=127.0.0.1:8080"
- "--leader-elect"

View File

@ -0,0 +1,20 @@
apiVersion: apps/v1
kind: Deployment
metadata:
name: controller-manager
namespace: default
spec:
template:
spec:
containers:
- name: manager
args:
- "--config=controller_manager_config.yaml"
volumeMounts:
- name: manager-config
mountPath: /controller_manager_config.yaml
subPath: controller_manager_config.yaml
volumes:
- name: manager-config
configMap:
name: manager-config

View File

@ -0,0 +1,11 @@
apiVersion: controller-runtime.sigs.k8s.io/v1alpha1
kind: ControllerManagerConfig
health:
healthProbeBindAddress: :8081
metrics:
bindAddress: 127.0.0.1:8080
webhook:
port: 9443
leaderElection:
leaderElect: true
resourceName: 9cf053ac.jenkins.io

View File

@ -0,0 +1,16 @@
resources:
- manager.yaml
generatorOptions:
disableNameSuffixHash: true
configMapGenerator:
- files:
- controller_manager_config.yaml
name: manager-config
apiVersion: kustomize.config.k8s.io/v1beta1
kind: Kustomization
images:
- name: controller
newName: controller
newTag: latest

View File

@ -0,0 +1,55 @@
---
apiVersion: apps/v1
kind: Deployment
metadata:
name: jenkins-operator
namespace: default
labels:
control-plane: controller-manager
spec:
selector:
matchLabels:
control-plane: controller-manager
replicas: 1
template:
metadata:
labels:
control-plane: controller-manager
spec:
securityContext:
runAsUser: 65532
containers:
- command:
- /manager
args:
- --leader-elect
image: virtuslab/jenkins-operator:v0.5.0
name: jenkins-operator
imagePullPolicy: IfNotPresent
securityContext:
allowPrivilegeEscalation: false
livenessProbe:
httpGet:
path: /healthz
port: 8081
initialDelaySeconds: 15
periodSeconds: 20
readinessProbe:
httpGet:
path: /readyz
port: 8081
initialDelaySeconds: 5
periodSeconds: 10
resources:
limits:
cpu: 100m
memory: 30Mi
requests:
cpu: 100m
memory: 20Mi
env:
- name: WATCH_NAMESPACE
valueFrom:
fieldRef:
fieldPath: metadata.namespace
terminationGracePeriodSeconds: 10

View File

@ -0,0 +1,2 @@
resources:
- monitor.yaml

View File

@ -0,0 +1,16 @@
# Prometheus Monitor Service (Metrics)
apiVersion: monitoring.coreos.com/v1
kind: ServiceMonitor
metadata:
labels:
control-plane: controller-manager
name: controller-manager-metrics-monitor
namespace: default
spec:
endpoints:
- path: /metrics
port: https
selector:
matchLabels:
control-plane: controller-manager

View File

@ -0,0 +1,7 @@
apiVersion: rbac.authorization.k8s.io/v1
kind: ClusterRole
metadata:
name: metrics-reader
rules:
- nonResourceURLs: ["/metrics"]
verbs: ["get"]

View File

@ -0,0 +1,13 @@
apiVersion: rbac.authorization.k8s.io/v1
kind: ClusterRole
metadata:
name: proxy-role
rules:
- apiGroups: ["authentication.k8s.io"]
resources:
- tokenreviews
verbs: ["create"]
- apiGroups: ["authorization.k8s.io"]
resources:
- subjectaccessreviews
verbs: ["create"]

View File

@ -0,0 +1,12 @@
apiVersion: rbac.authorization.k8s.io/v1
kind: ClusterRoleBinding
metadata:
name: proxy-rolebinding
roleRef:
apiGroup: rbac.authorization.k8s.io
kind: ClusterRole
name: proxy-role
subjects:
- kind: ServiceAccount
name: default
namespace: default

View File

@ -0,0 +1,14 @@
apiVersion: v1
kind: Service
metadata:
labels:
control-plane: controller-manager
name: controller-manager-metrics-service
namespace: default
spec:
ports:
- name: https
port: 8443
targetPort: https
selector:
control-plane: controller-manager

View File

@ -0,0 +1,24 @@
# permissions for end users to edit jenkins.
apiVersion: rbac.authorization.k8s.io/v1
kind: ClusterRole
metadata:
name: jenkins-editor-role
rules:
- apiGroups:
- jenkins.io
resources:
- jenkins
verbs:
- create
- delete
- get
- list
- patch
- update
- watch
- apiGroups:
- jenkins.io
resources:
- jenkins/status
verbs:
- get

View File

@ -0,0 +1,20 @@
# permissions for end users to view jenkins.
apiVersion: rbac.authorization.k8s.io/v1
kind: ClusterRole
metadata:
name: jenkins-viewer-role
rules:
- apiGroups:
- jenkins.io
resources:
- jenkins
verbs:
- get
- list
- watch
- apiGroups:
- jenkins.io
resources:
- jenkins/status
verbs:
- get

View File

@ -0,0 +1,12 @@
resources:
- role.yaml
- role_binding.yaml
- leader_election_role.yaml
- leader_election_role_binding.yaml
# Comment the following 4 lines if you want to disable
# the auth proxy (https://github.com/brancz/kube-rbac-proxy)
# which protects your /metrics endpoint.
- auth_proxy_service.yaml
- auth_proxy_role.yaml
- auth_proxy_role_binding.yaml
- auth_proxy_client_clusterrole.yaml

View File

@ -0,0 +1,28 @@
---
# 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

@ -0,0 +1,13 @@
---
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: default
namespace: default

118
config/rbac/role.yaml Normal file
View File

@ -0,0 +1,118 @@
---
apiVersion: rbac.authorization.k8s.io/v1
kind: ClusterRole
metadata:
creationTimestamp: null
name: manager-role
rules:
- apiGroups:
- ""
resources:
- services
- configmaps
- secrets
- serviceaccounts
verbs:
- get
- create
- update
- list
- watch
- apiGroups:
- apps
resources:
- deployments
- daemonsets
- replicasets
- statefulsets
verbs:
- '*'
- apiGroups:
- rbac.authorization.k8s.io
resources:
- roles
- rolebindings
verbs:
- 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

@ -0,0 +1,13 @@
---
apiVersion: rbac.authorization.k8s.io/v1
kind: ClusterRoleBinding
metadata:
name: manager-rolebinding
roleRef:
apiGroup: rbac.authorization.k8s.io
kind: ClusterRole
name: manager-role
subjects:
- kind: ServiceAccount
name: default
namespace: default

View File

@ -0,0 +1,55 @@
apiVersion: jenkins.io/v1alpha2
kind: Jenkins
metadata:
name: example
namespace: default
spec:
configurationAsCode:
configurations: []
secret:
name: ""
groovyScripts:
configurations: []
secret:
name: ""
jenkinsAPISettings:
authorizationStrategy: createUser
master:
disableCSRFProtection: false
containers:
- name: jenkins-master
image: jenkins/jenkins:2.263.3-lts-alpine
imagePullPolicy: Always
livenessProbe:
failureThreshold: 12
httpGet:
path: /login
port: http
scheme: HTTP
initialDelaySeconds: 100
periodSeconds: 10
successThreshold: 1
timeoutSeconds: 5
readinessProbe:
failureThreshold: 10
httpGet:
path: /login
port: http
scheme: HTTP
initialDelaySeconds: 80
periodSeconds: 10
successThreshold: 1
timeoutSeconds: 1
resources:
limits:
cpu: 1500m
memory: 3Gi
requests:
cpu: "1"
memory: 500Mi
seedJobs:
- id: jenkins-operator
targets: "cicd/jobs/*.jenkins"
description: "Jenkins Operator repository"
repositoryBranch: master
repositoryUrl: https://github.com/jenkinsci/kubernetes-operator.git

View File

@ -0,0 +1,4 @@
## Append samples you want in your CSV to this file as resources ##
resources:
- jenkins.io_v1alpha2_jenkins.yaml
# +kubebuilder:scaffold:manifestskustomizesamples

View File

@ -0,0 +1,7 @@
apiVersion: scorecard.operatorframework.io/v1alpha3
kind: Configuration
metadata:
name: config
stages:
- parallel: true
tests: []

View File

@ -0,0 +1,16 @@
resources:
- bases/config.yaml
patchesJson6902:
- path: patches/basic.config.yaml
target:
group: scorecard.operatorframework.io
version: v1alpha3
kind: Configuration
name: config
- path: patches/olm.config.yaml
target:
group: scorecard.operatorframework.io
version: v1alpha3
kind: Configuration
name: config
# +kubebuilder:scaffold:patchesJson6902

View File

@ -0,0 +1,10 @@
- op: add
path: /stages/0/tests/-
value:
entrypoint:
- scorecard-test
- basic-check-spec
image: quay.io/operator-framework/scorecard-test:v1.3.0
labels:
suite: basic
test: basic-check-spec-test

View File

@ -0,0 +1,50 @@
- op: add
path: /stages/0/tests/-
value:
entrypoint:
- scorecard-test
- olm-bundle-validation
image: quay.io/operator-framework/scorecard-test:v1.3.0
labels:
suite: olm
test: olm-bundle-validation-test
- op: add
path: /stages/0/tests/-
value:
entrypoint:
- scorecard-test
- olm-crds-have-validation
image: quay.io/operator-framework/scorecard-test:v1.3.0
labels:
suite: olm
test: olm-crds-have-validation-test
- op: add
path: /stages/0/tests/-
value:
entrypoint:
- scorecard-test
- olm-crds-have-resources
image: quay.io/operator-framework/scorecard-test:v1.3.0
labels:
suite: olm
test: olm-crds-have-resources-test
- op: add
path: /stages/0/tests/-
value:
entrypoint:
- scorecard-test
- olm-spec-descriptors
image: quay.io/operator-framework/scorecard-test:v1.3.0
labels:
suite: olm
test: olm-spec-descriptors-test
- op: add
path: /stages/0/tests/-
value:
entrypoint:
- scorecard-test
- olm-status-descriptors
image: quay.io/operator-framework/scorecard-test:v1.3.0
labels:
suite: olm
test: olm-status-descriptors-test

View File

@ -1,10 +1,10 @@
package jenkins
package controllers
import (
"fmt"
"reflect"
"github.com/jenkinsci/kubernetes-operator/pkg/apis/jenkins/v1alpha2"
"github.com/jenkinsci/kubernetes-operator/api/v1alpha2"
"github.com/jenkinsci/kubernetes-operator/pkg/constants"
"github.com/jenkinsci/kubernetes-operator/pkg/log"
@ -20,14 +20,14 @@ import (
type enqueueRequestForJenkins struct{}
func (e *enqueueRequestForJenkins) Create(evt event.CreateEvent, q workqueue.RateLimitingInterface) {
if req := e.getOwnerReconcileRequests(evt.Meta); req != nil {
if req := e.getOwnerReconcileRequests(evt.Object); req != nil {
q.Add(*req)
}
}
func (e *enqueueRequestForJenkins) Update(evt event.UpdateEvent, q workqueue.RateLimitingInterface) {
req1 := e.getOwnerReconcileRequests(evt.MetaOld)
req2 := e.getOwnerReconcileRequests(evt.MetaNew)
req1 := e.getOwnerReconcileRequests(evt.ObjectOld)
req2 := e.getOwnerReconcileRequests(evt.ObjectNew)
if req1 != nil || req2 != nil {
jenkinsName := "unknown"
@ -39,7 +39,7 @@ func (e *enqueueRequestForJenkins) Update(evt event.UpdateEvent, q workqueue.Rat
}
log.Log.WithValues("cr", jenkinsName).Info(
fmt.Sprintf("%T/%s has been updated", evt.ObjectNew, evt.MetaNew.GetName()))
fmt.Sprintf("%T/%s has been updated", evt.ObjectNew, evt.ObjectNew.GetName()))
}
if req1 != nil {
@ -52,13 +52,13 @@ func (e *enqueueRequestForJenkins) Update(evt event.UpdateEvent, q workqueue.Rat
}
func (e *enqueueRequestForJenkins) Delete(evt event.DeleteEvent, q workqueue.RateLimitingInterface) {
if req := e.getOwnerReconcileRequests(evt.Meta); req != nil {
if req := e.getOwnerReconcileRequests(evt.Object); req != nil {
q.Add(*req)
}
}
func (e *enqueueRequestForJenkins) Generic(evt event.GenericEvent, q workqueue.RateLimitingInterface) {
if req := e.getOwnerReconcileRequests(evt.Meta); req != nil {
if req := e.getOwnerReconcileRequests(evt.Object); req != nil {
q.Add(*req)
}
}
@ -85,8 +85,8 @@ func (e *jenkinsDecorator) Create(evt event.CreateEvent, q workqueue.RateLimitin
func (e *jenkinsDecorator) Update(evt event.UpdateEvent, q workqueue.RateLimitingInterface) {
if !reflect.DeepEqual(evt.ObjectOld.(*v1alpha2.Jenkins).Spec, evt.ObjectNew.(*v1alpha2.Jenkins).Spec) {
log.Log.WithValues("cr", evt.MetaNew.GetName()).Info(
fmt.Sprintf("%T/%s has been updated", evt.ObjectNew, evt.MetaNew.GetName()))
log.Log.WithValues("cr", evt.ObjectNew.GetName()).Info(
fmt.Sprintf("%T/%s has been updated", evt.ObjectNew, evt.ObjectNew.GetName()))
}
e.handler.Update(evt, q)
}

View File

@ -1,4 +1,20 @@
package jenkins
/*
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 controllers
import (
"context"
@ -7,8 +23,9 @@ import (
"reflect"
"time"
"github.com/jenkinsci/kubernetes-operator/pkg/apis/jenkins/v1alpha2"
"github.com/jenkinsci/kubernetes-operator/api/v1alpha2"
jenkinsclient "github.com/jenkinsci/kubernetes-operator/pkg/client"
"github.com/jenkinsci/kubernetes-operator/pkg/configuration"
"github.com/jenkinsci/kubernetes-operator/pkg/configuration/base"
"github.com/jenkinsci/kubernetes-operator/pkg/configuration/base/resources"
"github.com/jenkinsci/kubernetes-operator/pkg/configuration/user"
@ -17,15 +34,17 @@ import (
"github.com/jenkinsci/kubernetes-operator/pkg/notifications/event"
"github.com/jenkinsci/kubernetes-operator/pkg/notifications/reason"
"github.com/jenkinsci/kubernetes-operator/pkg/plugins"
"github.com/pkg/errors"
corev1 "k8s.io/api/core/v1"
apierrors "k8s.io/apimachinery/pkg/api/errors"
metav1 "k8s.io/apimachinery/pkg/apis/meta/v1"
"k8s.io/apimachinery/pkg/runtime"
"k8s.io/client-go/kubernetes"
"k8s.io/client-go/rest"
"sigs.k8s.io/controller-runtime/pkg/controller"
ctrl "sigs.k8s.io/controller-runtime"
"sigs.k8s.io/controller-runtime/pkg/client"
"sigs.k8s.io/controller-runtime/pkg/handler"
"sigs.k8s.io/controller-runtime/pkg/manager"
"sigs.k8s.io/controller-runtime/pkg/reconcile"
"sigs.k8s.io/controller-runtime/pkg/source"
)
@ -37,7 +56,6 @@ type reconcileError struct {
const (
APIVersion = "core/v1"
PodKind = "Pod"
SecretKind = "Secret"
ConfigMapKind = "ConfigMap"
containerProbeURI = "login"
@ -46,66 +64,59 @@ const (
var reconcileErrors = map[string]reconcileError{}
var logx = log.Log
var _ reconcile.Reconciler = &ReconcileJenkins{}
// Add creates a newReconcilierConfiguration Jenkins Controller and adds it to the Manager. The Manager will set fields on the Controller
// and Start it when the Manager is Started.
func Add(mgr manager.Manager, jenkinsAPIConnectionSettings jenkinsclient.JenkinsAPIConnectionSettings, kubernetesClusterDomain string, clientSet kubernetes.Clientset, config rest.Config, notificationEvents *chan event.Event) error {
reconciler := newReconciler(mgr, jenkinsAPIConnectionSettings, kubernetesClusterDomain, clientSet, config, notificationEvents)
return add(mgr, reconciler)
// JenkinsReconciler reconciles a Jenkins object
type JenkinsReconciler struct {
Client client.Client
Scheme *runtime.Scheme
JenkinsAPIConnectionSettings jenkinsclient.JenkinsAPIConnectionSettings
ClientSet kubernetes.Clientset
Config rest.Config
NotificationEvents *chan event.Event
KubernetesClusterDomain string
}
// add adds a newReconcilierConfiguration Controller to mgr with r as the reconcile.Reconciler.
func add(mgr manager.Manager, r reconcile.Reconciler) error {
// Create a newReconcilierConfiguration controller
c, err := controller.New("jenkins-controller", mgr, controller.Options{Reconciler: r})
if err != nil {
return errors.WithStack(err)
}
// Watch for changes to primary resource Jenkins
decorator := jenkinsDecorator{handler: &handler.EnqueueRequestForObject{}}
err = c.Watch(&source.Kind{Type: &v1alpha2.Jenkins{}}, &decorator)
if err != nil {
return errors.WithStack(err)
}
// Watch for changes to secondary resource Pods and requeue the owner Jenkins
podResource := &source.Kind{Type: &corev1.Pod{TypeMeta: metav1.TypeMeta{APIVersion: APIVersion, Kind: PodKind}}}
err = c.Watch(podResource, &handler.EnqueueRequestForOwner{
IsController: true,
OwnerType: &v1alpha2.Jenkins{},
})
if err != nil {
return errors.WithStack(err)
}
secretResource := &source.Kind{Type: &corev1.Secret{TypeMeta: metav1.TypeMeta{APIVersion: APIVersion, Kind: SecretKind}}}
err = c.Watch(secretResource, &handler.EnqueueRequestForOwner{
IsController: true,
OwnerType: &v1alpha2.Jenkins{},
})
if err != nil {
return errors.WithStack(err)
}
// SetupWithManager sets up the controller with the Manager.
func (r *JenkinsReconciler) SetupWithManager(mgr ctrl.Manager) error {
jenkinsHandler := &enqueueRequestForJenkins{}
err = c.Watch(secretResource, jenkinsHandler)
if err != nil {
return errors.WithStack(err)
}
configMapResource := &source.Kind{Type: &corev1.ConfigMap{TypeMeta: metav1.TypeMeta{APIVersion: APIVersion, Kind: ConfigMapKind}}}
err = c.Watch(configMapResource, jenkinsHandler)
if err != nil {
return errors.WithStack(err)
}
return nil
secretResource := &source.Kind{Type: &corev1.Secret{TypeMeta: metav1.TypeMeta{APIVersion: APIVersion, Kind: SecretKind}}}
decorator := jenkinsDecorator{handler: &handler.EnqueueRequestForObject{}}
return ctrl.NewControllerManagedBy(mgr).
For(&v1alpha2.Jenkins{}).
Owns(&corev1.Pod{}).
Owns(&corev1.Secret{}).
Owns(&corev1.ConfigMap{}).
Watches(secretResource, jenkinsHandler).
Watches(configMapResource, jenkinsHandler).
Watches(&source.Kind{Type: &v1alpha2.Jenkins{}}, &decorator).
Complete(r)
}
// Reconcile it's a main reconciliation loop which maintain desired state based on Jenkins.Spec.
func (r *ReconcileJenkins) Reconcile(request reconcile.Request) (reconcile.Result, error) {
func (r *JenkinsReconciler) newJenkinsReconcilier(jenkins *v1alpha2.Jenkins) configuration.Configuration {
config := configuration.Configuration{
Client: r.Client,
ClientSet: r.ClientSet,
Notifications: r.NotificationEvents,
Jenkins: jenkins,
Scheme: r.Scheme,
Config: &r.Config,
JenkinsAPIConnectionSettings: r.JenkinsAPIConnectionSettings,
KubernetesClusterDomain: r.KubernetesClusterDomain,
}
return config
}
// +kubebuilder:rbac:groups=jenkins.io,resources=jenkins,verbs=get;list;watch;create;update;patch;delete
// +kubebuilder:rbac:groups=jenkins.io,resources=jenkins/status,verbs=get;update;patch
// +kubebuilder:rbac:groups=jenkins.io,resources=jenkins/finalizers,verbs=update
// +kubebuilder:rbac:groups=core,resources=pods,verbs=get;list;watch;create;update;patch;delete
// +kubebuilder:rbac:groups=core,resources=configmaps,verbs=get;list;watch
// +kubebuilder:rbac:groups=v1,resources=secrets,verbs=get;list;watch
// For more details, check Reconcile and its Result here:
// - https://pkg.go.dev/sigs.k8s.io/controller-runtime@v0.7.0/pkg/reconcile
func (r *JenkinsReconciler) Reconcile(_ context.Context, request ctrl.Request) (ctrl.Result, error) {
reconcileFailLimit := uint64(10)
logger := logx.WithValues("cr", request.Name)
logger.V(log.VDebug).Info("Reconciling Jenkins")
@ -131,18 +142,18 @@ func (r *ReconcileJenkins) Reconcile(request reconcile.Request) (reconcile.Resul
reconcileErrors[request.Name] = lastErrors
if lastErrors.counter >= reconcileFailLimit {
if log.Debug {
logger.V(log.VWarn).Info(fmt.Sprintf("Reconcile loop failed %d times with the same error, giving up: %+v", reconcileFailLimit, err))
logger.V(log.VWarn).Info(fmt.Sprintf("Reconcile loop failed %d times with the same errors, giving up: %+v", reconcileFailLimit, err))
} else {
logger.V(log.VWarn).Info(fmt.Sprintf("Reconcile loop failed %d times with the same error, giving up: %s", reconcileFailLimit, err))
logger.V(log.VWarn).Info(fmt.Sprintf("Reconcile loop failed %d times with the same errors, giving up: %s", reconcileFailLimit, err))
}
*r.notificationEvents <- event.Event{
*r.NotificationEvents <- event.Event{
Jenkins: *jenkins,
Phase: event.PhaseBase,
Level: v1alpha2.NotificationLevelWarning,
Reason: reason.NewReconcileLoopFailed(
reason.OperatorSource,
[]string{fmt.Sprintf("Reconcile loop failed %d times with the same error, giving up: %s", reconcileFailLimit, err)},
[]string{fmt.Sprintf("Reconcile loop failed %d times with the same errors, giving up: %s", reconcileFailLimit, err)},
),
}
return reconcile.Result{Requeue: false}, nil
@ -155,7 +166,7 @@ func (r *ReconcileJenkins) Reconcile(request reconcile.Request) (reconcile.Resul
}
if groovyErr, ok := err.(*jenkinsclient.GroovyScriptExecutionFailed); ok {
*r.notificationEvents <- event.Event{
*r.NotificationEvents <- event.Event{
Jenkins: *jenkins,
Phase: event.PhaseBase,
Level: v1alpha2.NotificationLevelWarning,
@ -175,12 +186,12 @@ func (r *ReconcileJenkins) Reconcile(request reconcile.Request) (reconcile.Resul
return result, nil
}
func (r *ReconcileJenkins) reconcile(request reconcile.Request) (reconcile.Result, *v1alpha2.Jenkins, error) {
func (r *JenkinsReconciler) reconcile(request reconcile.Request) (reconcile.Result, *v1alpha2.Jenkins, error) {
logger := logx.WithValues("cr", request.Name)
// Fetch the Jenkins instance
jenkins := &v1alpha2.Jenkins{}
var err error
err = r.client.Get(context.TODO(), request.NamespacedName, jenkins)
err = r.Client.Get(context.TODO(), request.NamespacedName, jenkins)
if err != nil {
if apierrors.IsNotFound(err) {
// Request object not found, could have been deleted after reconcile request.
@ -200,17 +211,9 @@ func (r *ReconcileJenkins) reconcile(request reconcile.Request) (reconcile.Resul
return reconcile.Result{Requeue: true}, jenkins, nil
}
requeue, err = r.handleDeprecatedData(jenkins)
if err != nil {
return reconcile.Result{}, jenkins, err
}
if requeue {
return reconcile.Result{Requeue: true}, jenkins, nil
}
config := r.newReconcilierConfiguration(jenkins)
config := r.newJenkinsReconcilier(jenkins)
// Reconcile base configuration
baseConfiguration := base.New(config, r.jenkinsAPIConnectionSettings)
baseConfiguration := base.New(config, r.JenkinsAPIConnectionSettings)
var baseMessages []string
baseMessages, err = baseConfiguration.Validate(jenkins)
@ -219,7 +222,7 @@ func (r *ReconcileJenkins) reconcile(request reconcile.Request) (reconcile.Resul
}
if len(baseMessages) > 0 {
message := "Validation of base configuration failed, please correct Jenkins CR."
*r.notificationEvents <- event.Event{
*r.NotificationEvents <- event.Event{
Jenkins: *jenkins,
Phase: event.PhaseBase,
Level: v1alpha2.NotificationLevelWarning,
@ -248,14 +251,14 @@ func (r *ReconcileJenkins) reconcile(request reconcile.Request) (reconcile.Resul
if jenkins.Status.BaseConfigurationCompletedTime == nil {
now := metav1.Now()
jenkins.Status.BaseConfigurationCompletedTime = &now
err = r.client.Update(context.TODO(), jenkins)
err = r.Client.Status().Update(context.TODO(), jenkins)
if err != nil {
return reconcile.Result{}, jenkins, errors.WithStack(err)
}
message := fmt.Sprintf("Base configuration phase is complete, took %s",
jenkins.Status.BaseConfigurationCompletedTime.Sub(jenkins.Status.ProvisionStartTime.Time))
*r.notificationEvents <- event.Event{
*r.NotificationEvents <- event.Event{
Jenkins: *jenkins,
Phase: event.PhaseBase,
Level: v1alpha2.NotificationLevelInfo,
@ -274,7 +277,7 @@ func (r *ReconcileJenkins) reconcile(request reconcile.Request) (reconcile.Resul
}
if len(messages) > 0 {
message := "Validation of user configuration failed, please correct Jenkins CR"
*r.notificationEvents <- event.Event{
*r.NotificationEvents <- event.Event{
Jenkins: *jenkins,
Phase: event.PhaseUser,
Level: v1alpha2.NotificationLevelWarning,
@ -309,13 +312,13 @@ func (r *ReconcileJenkins) reconcile(request reconcile.Request) (reconcile.Resul
if jenkins.Status.UserConfigurationCompletedTime == nil {
now := metav1.Now()
jenkins.Status.UserConfigurationCompletedTime = &now
err = r.client.Update(context.TODO(), jenkins)
err = r.Client.Status().Update(context.TODO(), jenkins)
if err != nil {
return reconcile.Result{}, jenkins, errors.WithStack(err)
}
message := fmt.Sprintf("User configuration phase is complete, took %s",
jenkins.Status.UserConfigurationCompletedTime.Sub(jenkins.Status.ProvisionStartTime.Time))
*r.notificationEvents <- event.Event{
*r.NotificationEvents <- event.Event{
Jenkins: *jenkins,
Phase: event.PhaseUser,
Level: v1alpha2.NotificationLevelInfo,
@ -326,7 +329,7 @@ func (r *ReconcileJenkins) reconcile(request reconcile.Request) (reconcile.Resul
return reconcile.Result{}, jenkins, nil
}
func (r *ReconcileJenkins) setDefaults(jenkins *v1alpha2.Jenkins) (requeue bool, err error) {
func (r *JenkinsReconciler) setDefaults(jenkins *v1alpha2.Jenkins) (requeue bool, err error) {
changed := false
logger := logx.WithValues("cr", jenkins.Name)
@ -356,7 +359,7 @@ func (r *ReconcileJenkins) setDefaults(jenkins *v1alpha2.Jenkins) (requeue bool,
if jenkinsContainer.ReadinessProbe == nil {
logger.Info("Setting default Jenkins readinessProbe")
changed = true
jenkinsContainer.ReadinessProbe = resources.NewSimpleProbe(containerProbeURI, containerProbePortName, corev1.URISchemeHTTP, 30)
jenkinsContainer.ReadinessProbe = resources.NewProbe(containerProbeURI, containerProbePortName, corev1.URISchemeHTTP, 60, 1, 10)
}
if jenkinsContainer.LivenessProbe == nil {
logger.Info("Setting default Jenkins livenessProbe")
@ -390,7 +393,7 @@ func (r *ReconcileJenkins) setDefaults(jenkins *v1alpha2.Jenkins) (requeue bool,
logger.Info("Setting default Jenkins master service")
changed = true
var serviceType = corev1.ServiceTypeClusterIP
if r.jenkinsAPIConnectionSettings.UseNodePort {
if r.JenkinsAPIConnectionSettings.UseNodePort {
serviceType = corev1.ServiceTypeNodePort
}
jenkins.Spec.Service = v1alpha2.Service{
@ -441,7 +444,7 @@ func (r *ReconcileJenkins) setDefaults(jenkins *v1alpha2.Jenkins) (requeue bool,
}
if changed {
return changed, errors.WithStack(r.client.Update(context.TODO(), jenkins))
return changed, errors.WithStack(r.Client.Update(context.TODO(), jenkins))
}
return changed, nil
}
@ -455,7 +458,7 @@ func isJavaOpsVariableNotSet(container v1alpha2.Container) bool {
return true
}
func (r *ReconcileJenkins) setDefaultsForContainer(jenkins *v1alpha2.Jenkins, containerName string, containerIndex int) bool {
func (r *JenkinsReconciler) setDefaultsForContainer(jenkins *v1alpha2.Jenkins, containerName string, containerIndex int) bool {
changed := false
logger := logx.WithValues("cr", jenkins.Name, "container", containerName)
@ -482,18 +485,3 @@ func basePlugins() (result []v1alpha2.Plugin) {
}
return
}
func (r *ReconcileJenkins) handleDeprecatedData(jenkins *v1alpha2.Jenkins) (requeue bool, err error) {
changed := false
logger := logx.WithValues("cr", jenkins.Name)
if len(jenkins.Spec.Master.AnnotationsDeprecated) > 0 {
changed = true
jenkins.Spec.Master.Annotations = jenkins.Spec.Master.AnnotationsDeprecated
jenkins.Spec.Master.AnnotationsDeprecated = map[string]string{}
logger.V(log.VWarn).Info("spec.master.masterAnnotations is deprecated, the annotations have been moved to spec.master.annotations")
}
if changed {
return changed, errors.WithStack(r.client.Update(context.TODO(), jenkins))
}
return changed, nil
}

82
go.mod
View File

@ -1,75 +1,29 @@
module github.com/jenkinsci/kubernetes-operator
go 1.13
go 1.15
require (
github.com/bndr/gojenkins v0.0.0-20181125150310-de43c03cf849
github.com/bndr/gojenkins v1.0.1
github.com/docker/distribution v2.7.1+incompatible
github.com/elazarl/goproxy v0.0.0-20190711103511-473e67f1d7d2 // indirect
github.com/elazarl/goproxy/ext v0.0.0-20190711103511-473e67f1d7d2 // indirect
github.com/emersion/go-sasl v0.0.0-20190817083125-240c8404624e // indirect
github.com/emersion/go-smtp v0.11.2
github.com/go-logr/logr v0.1.0
github.com/go-logr/zapr v0.1.1
github.com/go-openapi/spec v0.19.4
github.com/golang/mock v1.3.1
github.com/golangci/golangci-lint v1.26.0 // indirect
github.com/mailgun/mailgun-go/v3 v3.6.0
github.com/openshift/api v3.9.1-0.20190924102528-32369d4db2ad+incompatible
github.com/operator-framework/operator-sdk v0.17.0
github.com/go-logr/logr v0.3.0
github.com/go-logr/zapr v0.2.0
github.com/golang/mock v1.4.1
github.com/mailgun/mailgun-go/v3 v3.6.4
github.com/onsi/ginkgo v1.14.1
github.com/onsi/gomega v1.10.2
github.com/opencontainers/go-digest v1.0.0 // indirect
github.com/openshift/api v3.9.0+incompatible
github.com/pkg/errors v0.9.1
github.com/robfig/cron v1.2.0
github.com/spf13/pflag v1.0.5
github.com/stretchr/testify v1.5.1
go.uber.org/zap v1.14.1
golang.org/x/lint v0.0.0-20200302205851-738671d3881b // indirect
golang.org/x/net v0.0.0-20200226121028-0de0cce0169b
golang.org/x/tools v0.0.0-20200505023115-26f46d2f7ef8 // indirect
github.com/stretchr/testify v1.6.1
go.uber.org/zap v1.15.0
gopkg.in/alexcesaro/quotedprintable.v3 v3.0.0-20150716171945-2caba252f4dc // indirect
gopkg.in/gomail.v2 v2.0.0-20160411212932-81ebce5c23df
k8s.io/api v0.17.4
k8s.io/apimachinery v0.17.4
k8s.io/cli-runtime v0.17.4
k8s.io/client-go v12.0.0+incompatible
k8s.io/code-generator v0.17.4
k8s.io/gengo v0.0.0-20191010091904-7fa3014cb28f
k8s.io/kube-openapi v0.0.0-20191107075043-30be4d16710a
k8s.io/utils v0.0.0-20190801114015-581e00157fb1
sigs.k8s.io/controller-runtime v0.5.2
sigs.k8s.io/controller-tools v0.2.8
)
// Pinned to kubernetes-1.16.2
replace (
github.com/Azure/go-autorest => github.com/Azure/go-autorest v12.2.0+incompatible
k8s.io/api => k8s.io/api v0.0.0-20191016110408-35e52d86657a
k8s.io/apiextensions-apiserver => k8s.io/apiextensions-apiserver v0.0.0-20191016113550-5357c4baaf65
k8s.io/apimachinery => k8s.io/apimachinery v0.0.0-20191004115801-a2eda9f80ab8
k8s.io/apiserver => k8s.io/apiserver v0.0.0-20191016112112-5190913f932d
k8s.io/cli-runtime => k8s.io/cli-runtime v0.0.0-20191016114015-74ad18325ed5
k8s.io/client-go => k8s.io/client-go v0.0.0-20191016111102-bec269661e48
k8s.io/cloud-provider => k8s.io/cloud-provider v0.0.0-20191016115326-20453efc2458
k8s.io/cluster-bootstrap => k8s.io/cluster-bootstrap v0.0.0-20191016115129-c07a134afb42
k8s.io/component-base => k8s.io/component-base v0.0.0-20191016111319-039242c015a9
k8s.io/cri-api => k8s.io/cri-api v0.0.0-20190828162817-608eb1dad4ac
k8s.io/csi-translation-lib => k8s.io/csi-translation-lib v0.0.0-20191016115521-756ffa5af0bd
k8s.io/kube-aggregator => k8s.io/kube-aggregator v0.0.0-20191016112429-9587704a8ad4
k8s.io/kube-controller-manager => k8s.io/kube-controller-manager v0.0.0-20191016114939-2b2b218dc1df
k8s.io/kube-proxy => k8s.io/kube-proxy v0.0.0-20191016114407-2e83b6f20229
k8s.io/kube-scheduler => k8s.io/kube-scheduler v0.0.0-20191016114748-65049c67a58b
k8s.io/kubectl => k8s.io/kubectl v0.0.0-20191016120415-2ed914427d51
k8s.io/kubelet => k8s.io/kubelet v0.0.0-20191016114556-7841ed97f1b2
k8s.io/legacy-cloud-providers => k8s.io/legacy-cloud-providers v0.0.0-20191016115753-cf0698c3a16b
k8s.io/metrics => k8s.io/metrics v0.0.0-20191016113814-3b1a734dba6e
k8s.io/sample-apiserver => k8s.io/sample-apiserver v0.0.0-20191016112829-06bb3c9d77c9
)
replace (
github.com/coreos/prometheus-operator => github.com/coreos/prometheus-operator v0.35.1
github.com/docker/docker => github.com/moby/moby v0.7.3-0.20190826074503-38ab9da00309
github.com/operator-framework/operator-sdk => github.com/operator-framework/operator-sdk v0.17.0
k8s.io/code-generator => k8s.io/code-generator v0.0.0-20181117043124-c2090bec4d9b
k8s.io/kube-openapi => k8s.io/kube-openapi v0.0.0-20180711000925-0cf8f7e6ed1d
sigs.k8s.io/controller-runtime => sigs.k8s.io/controller-runtime v0.4.0
sigs.k8s.io/controller-tools => sigs.k8s.io/controller-tools v0.1.11-0.20190411181648-9d55346c2bde
k8s.io/api v0.20.2
k8s.io/apimachinery v0.20.2
k8s.io/cli-runtime v0.20.2
k8s.io/client-go v0.20.2
k8s.io/utils v0.0.0-20201110183641-67b214c5f920
sigs.k8s.io/controller-runtime v0.7.0
)

1283
go.sum

File diff suppressed because it is too large Load Diff

15
hack/boilerplate.go.txt Normal file
View File

@ -0,0 +1,15 @@
/*
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.
*/

193
main.go Normal file
View File

@ -0,0 +1,193 @@
/*
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"
"github.com/jenkinsci/kubernetes-operator/controllers"
"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/client/config"
// 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
isRunningInCluster, err := resources.IsRunningInCluster()
if err != nil {
fatal(errors.Wrap(err, "failed to get watch namespace"), 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.")
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))
// 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)
}
mgr, err := ctrl.NewManager(ctrl.GetConfigOrDie(), ctrl.Options{
Scheme: scheme,
MetricsBindAddress: fmt.Sprintf("%s:%d", metricsHost, metricsPort),
Port: 9443,
HealthProbeBindAddress: probeAddr,
LeaderElection: enableLeaderElection,
LeaderElectionID: "c674355f.jenkins.io",
})
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)
}
// +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)
}

View File

@ -1,24 +0,0 @@
package apis
import (
"github.com/jenkinsci/kubernetes-operator/pkg/apis/jenkins/v1alpha2"
routev1 "github.com/openshift/api/route/v1"
appsv1 "k8s.io/api/apps/v1"
"k8s.io/apimachinery/pkg/runtime"
)
// AddToSchemes may be used to add all resources defined in the project to a Scheme.
var AddToSchemes runtime.SchemeBuilder
// AddToScheme adds all Resources to the Scheme.
func AddToScheme(s *runtime.Scheme) error {
return AddToSchemes.AddToScheme(s)
}
func init() {
// Register the types with the Scheme so the components can map objects to GroupVersionKinds and back
AddToSchemes = append(AddToSchemes, v1alpha2.SchemeBuilder.AddToScheme)
AddToSchemes = append(AddToSchemes, routev1.Install)
AddToSchemes = append(AddToSchemes, appsv1.AddToScheme)
}

View File

@ -1,58 +0,0 @@
package v1alpha2
import (
metav1 "k8s.io/apimachinery/pkg/apis/meta/v1"
)
// EDIT THIS FILE! THIS IS SCAFFOLDING FOR YOU TO OWN!
// NOTE: json tags are required. Any new fields you add must have json tags for the fields to be serialized.
// JenkinsImageSpec defines the desired state of JenkinsImage
type JenkinsImageSpec struct {
BaseImage Image `json:"image"`
Plugins []JenkinsPlugin `json:"plugins"` // Plugins list
}
// Defines Jenkins Plugin structure
type JenkinsPlugin struct {
Name string `json:"name"`
Version string `json:"version,omitempty"`
}
// Defines Jenkins Plugin structure
type Image struct {
Name string `json:"name"`
Tag string `json:"version,omitempty"`
}
// JenkinsImageStatus defines the observed state of JenkinsImage
type JenkinsImageStatus struct {
Image string `json:"image,omitempty"`
MD5Sum string `json:"md5sum,omitempty"`
InstalledPlugins []JenkinsPlugin `json:"installedPlugins,omitempty"`
}
// +k8s:deepcopy-gen:interfaces=k8s.io/apimachinery/pkg/runtime.Object
// JenkinsImage is the Schema for the jenkinsimages API
// +kubebuilder:subresource:status
// +kubebuilder:resource:path=jenkinsimages,scope=Namespaced
type JenkinsImage struct {
metav1.TypeMeta `json:",inline"`
metav1.ObjectMeta `json:"metadata,omitempty"`
Spec JenkinsImageSpec `json:"spec,omitempty"`
Status JenkinsImageStatus `json:"status,omitempty"`
}
// +k8s:deepcopy-gen:interfaces=k8s.io/apimachinery/pkg/runtime.Object
// JenkinsImageList contains a list of JenkinsImage
type JenkinsImageList struct {
metav1.TypeMeta `json:",inline"`
metav1.ListMeta `json:"metadata,omitempty"`
Items []JenkinsImage `json:"items"`
}
func init() {
SchemeBuilder.Register(&JenkinsImage{}, &JenkinsImageList{})
}

View File

@ -1,275 +0,0 @@
// +build !ignore_autogenerated
// This file was autogenerated by openapi-gen. Do not edit it manually!
package v1alpha2
import (
spec "github.com/go-openapi/spec"
common "k8s.io/kube-openapi/pkg/common"
)
func GetOpenAPIDefinitions(ref common.ReferenceCallback) map[string]common.OpenAPIDefinition {
return map[string]common.OpenAPIDefinition{
"github.com/kubernetes-operator/pkg/apis/jenkins/v1alpha2.Jenkins": schema_pkg_apis_jenkins_v1alpha2_Jenkins(ref),
"github.com/kubernetes-operator/pkg/apis/jenkins/v1alpha2.JenkinsSpec": schema_pkg_apis_jenkins_v1alpha2_JenkinsSpec(ref),
"github.com/kubernetes-operator/pkg/apis/jenkins/v1alpha2.JenkinsStatus": schema_pkg_apis_jenkins_v1alpha2_JenkinsStatus(ref),
}
}
func schema_pkg_apis_jenkins_v1alpha2_Jenkins(ref common.ReferenceCallback) common.OpenAPIDefinition {
return common.OpenAPIDefinition{
Schema: spec.Schema{
SchemaProps: spec.SchemaProps{
Description: "Jenkins is the Schema for the jenkins API",
Type: []string{"object"},
Properties: map[string]spec.Schema{
"kind": {
SchemaProps: spec.SchemaProps{
Description: "Kind is a string value representing the REST resource this object represents. Servers may infer this from the endpoint the client submits requests to. Cannot be updated. In CamelCase. More info: https://git.k8s.io/community/contributors/devel/sig-architecture/api-conventions.md#types-kinds",
Type: []string{"string"},
Format: "",
},
},
"apiVersion": {
SchemaProps: spec.SchemaProps{
Description: "APIVersion defines the versioned schema of this representation of an object. Servers should convert recognized schemas to the latest internal value, and may reject unrecognized values. More info: https://git.k8s.io/community/contributors/devel/sig-architecture/api-conventions.md#resources",
Type: []string{"string"},
Format: "",
},
},
"metadata": {
SchemaProps: spec.SchemaProps{
Ref: ref("k8s.io/apimachinery/pkg/apis/meta/v1.ObjectMeta"),
},
},
"spec": {
SchemaProps: spec.SchemaProps{
Description: "Spec defines the desired state of the Jenkins",
Ref: ref("github.com/kubernetes-operator/pkg/apis/jenkins/v1alpha2.JenkinsSpec"),
},
},
"status": {
SchemaProps: spec.SchemaProps{
Description: "Status defines the observed state of Jenkins",
Ref: ref("github.com/kubernetes-operator/pkg/apis/jenkins/v1alpha2.JenkinsStatus"),
},
},
},
},
},
Dependencies: []string{
"github.com/kubernetes-operator/pkg/apis/jenkins/v1alpha2.JenkinsSpec", "github.com/kubernetes-operator/pkg/apis/jenkins/v1alpha2.JenkinsStatus", "k8s.io/apimachinery/pkg/apis/meta/v1.ObjectMeta"},
}
}
func schema_pkg_apis_jenkins_v1alpha2_JenkinsSpec(ref common.ReferenceCallback) common.OpenAPIDefinition {
return common.OpenAPIDefinition{
Schema: spec.Schema{
SchemaProps: spec.SchemaProps{
Description: "JenkinsSpec defines the desired state of the Jenkins",
Type: []string{"object"},
Properties: map[string]spec.Schema{
"master": {
SchemaProps: spec.SchemaProps{
Description: "Master represents Jenkins master pod properties and Jenkins plugins. Every single change here requires a pod restart.",
Ref: ref("github.com/kubernetes-operator/pkg/apis/jenkins/v1alpha2.JenkinsMaster"),
},
},
"seedJobs": {
SchemaProps: spec.SchemaProps{
Description: "SeedJobs defines list of Jenkins Seed Job configurations More info: https://github.com/jenkinsci/kubernetes-operator/blob/master/docs/getting-started.md#configure-seed-jobs-and-pipelines",
Type: []string{"array"},
Items: &spec.SchemaOrArray{
Schema: &spec.Schema{
SchemaProps: spec.SchemaProps{
Ref: ref("github.com/kubernetes-operator/pkg/apis/jenkins/v1alpha2.SeedJob"),
},
},
},
},
},
"notifications": {
SchemaProps: spec.SchemaProps{
Description: "Notifications defines list of a services which are used to inform about Jenkins status Can be used to integrate chat services like Slack, Microsoft Teams or Mailgun",
Type: []string{"array"},
Items: &spec.SchemaOrArray{
Schema: &spec.Schema{
SchemaProps: spec.SchemaProps{
Ref: ref("github.com/kubernetes-operator/pkg/apis/jenkins/v1alpha2.Notification"),
},
},
},
},
},
"service": {
SchemaProps: spec.SchemaProps{
Description: "Service is Kubernetes service of Jenkins master HTTP pod Defaults to : port: 8080 type: ClusterIP",
Ref: ref("github.com/kubernetes-operator/pkg/apis/jenkins/v1alpha2.Service"),
},
},
"slaveService": {
SchemaProps: spec.SchemaProps{
Description: "Service is Kubernetes service of Jenkins slave pods Defaults to : port: 50000 type: ClusterIP",
Ref: ref("github.com/kubernetes-operator/pkg/apis/jenkins/v1alpha2.Service"),
},
},
"backup": {
SchemaProps: spec.SchemaProps{
Description: "Backup defines configuration of Jenkins backup More info: https://github.com/jenkinsci/kubernetes-operator/blob/master/docs/getting-started.md#configure-backup-and-restore",
Ref: ref("github.com/kubernetes-operator/pkg/apis/jenkins/v1alpha2.Backup"),
},
},
"restore": {
SchemaProps: spec.SchemaProps{
Description: "Backup defines configuration of Jenkins backup restore More info: https://github.com/jenkinsci/kubernetes-operator/blob/master/docs/getting-started.md#configure-backup-and-restore",
Ref: ref("github.com/kubernetes-operator/pkg/apis/jenkins/v1alpha2.Restore"),
},
},
"groovyScripts": {
SchemaProps: spec.SchemaProps{
Description: "GroovyScripts defines configuration of Jenkins customization via groovy scripts",
Ref: ref("github.com/kubernetes-operator/pkg/apis/jenkins/v1alpha2.GroovyScripts"),
},
},
"configurationAsCode": {
SchemaProps: spec.SchemaProps{
Description: "ConfigurationAsCode defines configuration of Jenkins customization via Configuration as Code Jenkins plugin",
Ref: ref("github.com/kubernetes-operator/pkg/apis/jenkins/v1alpha2.ConfigurationAsCode"),
},
},
"roles": {
SchemaProps: spec.SchemaProps{
Description: "Roles defines list of extra RBAC roles for the Jenkins Master pod service account",
Type: []string{"array"},
Items: &spec.SchemaOrArray{
Schema: &spec.Schema{
SchemaProps: spec.SchemaProps{
Ref: ref("k8s.io/api/rbac/v1.RoleRef"),
},
},
},
},
},
"serviceAccount": {
SchemaProps: spec.SchemaProps{
Description: "ServiceAccount defines Jenkins master service account attributes",
Ref: ref("github.com/kubernetes-operator/pkg/apis/jenkins/v1alpha2.ServiceAccount"),
},
},
"jenkinsAPISettings": {
SchemaProps: spec.SchemaProps{
Description: "JenkinsAPISettings defines configuration used by the operator to gain admin access to the Jenkins API",
Ref: ref("github.com/kubernetes-operator/pkg/apis/jenkins/v1alpha2.JenkinsAPISettings"),
},
},
},
Required: []string{"master", "jenkinsAPISettings"},
},
},
Dependencies: []string{
"github.com/kubernetes-operator/pkg/apis/jenkins/v1alpha2.Backup", "github.com/kubernetes-operator/pkg/apis/jenkins/v1alpha2.ConfigurationAsCode", "github.com/kubernetes-operator/pkg/apis/jenkins/v1alpha2.GroovyScripts", "github.com/kubernetes-operator/pkg/apis/jenkins/v1alpha2.JenkinsAPISettings", "github.com/kubernetes-operator/pkg/apis/jenkins/v1alpha2.JenkinsMaster", "github.com/kubernetes-operator/pkg/apis/jenkins/v1alpha2.Notification", "github.com/kubernetes-operator/pkg/apis/jenkins/v1alpha2.Restore", "github.com/kubernetes-operator/pkg/apis/jenkins/v1alpha2.SeedJob", "github.com/kubernetes-operator/pkg/apis/jenkins/v1alpha2.Service", "github.com/kubernetes-operator/pkg/apis/jenkins/v1alpha2.ServiceAccount", "k8s.io/api/rbac/v1.RoleRef"},
}
}
func schema_pkg_apis_jenkins_v1alpha2_JenkinsStatus(ref common.ReferenceCallback) common.OpenAPIDefinition {
return common.OpenAPIDefinition{
Schema: spec.Schema{
SchemaProps: spec.SchemaProps{
Description: "JenkinsStatus defines the observed state of Jenkins",
Type: []string{"object"},
Properties: map[string]spec.Schema{
"operatorVersion": {
SchemaProps: spec.SchemaProps{
Description: "OperatorVersion is the operator version which manages this CR",
Type: []string{"string"},
Format: "",
},
},
"provisionStartTime": {
SchemaProps: spec.SchemaProps{
Description: "ProvisionStartTime is a time when Jenkins master pod has been created",
Ref: ref("k8s.io/apimachinery/pkg/apis/meta/v1.Time"),
},
},
"baseConfigurationCompletedTime": {
SchemaProps: spec.SchemaProps{
Description: "BaseConfigurationCompletedTime is a time when Jenkins base configuration phase has been completed",
Ref: ref("k8s.io/apimachinery/pkg/apis/meta/v1.Time"),
},
},
"userConfigurationCompletedTime": {
SchemaProps: spec.SchemaProps{
Description: "UserConfigurationCompletedTime is a time when Jenkins user configuration phase has been completed",
Ref: ref("k8s.io/apimachinery/pkg/apis/meta/v1.Time"),
},
},
"restoredBackup": {
SchemaProps: spec.SchemaProps{
Description: "RestoredBackup is the restored backup number after Jenkins master pod restart",
Type: []string{"integer"},
Format: "int64",
},
},
"lastBackup": {
SchemaProps: spec.SchemaProps{
Description: "LastBackup is the latest backup number",
Type: []string{"integer"},
Format: "int64",
},
},
"pendingBackup": {
SchemaProps: spec.SchemaProps{
Description: "PendingBackup is the pending backup number",
Type: []string{"integer"},
Format: "int64",
},
},
"backupDoneBeforePodDeletion": {
SchemaProps: spec.SchemaProps{
Description: "BackupDoneBeforePodDeletion tells if backup before pod deletion has been made",
Type: []string{"boolean"},
Format: "",
},
},
"userAndPasswordHash": {
SchemaProps: spec.SchemaProps{
Description: "UserAndPasswordHash is a SHA256 hash made from user and password",
Type: []string{"string"},
Format: "",
},
},
"createdSeedJobs": {
SchemaProps: spec.SchemaProps{
Description: "CreatedSeedJobs contains list of seed job id already created in Jenkins",
Type: []string{"array"},
Items: &spec.SchemaOrArray{
Schema: &spec.Schema{
SchemaProps: spec.SchemaProps{
Type: []string{"string"},
Format: "",
},
},
},
},
},
"appliedGroovyScripts": {
SchemaProps: spec.SchemaProps{
Description: "AppliedGroovyScripts is a list with all applied groovy scripts in Jenkins by the operator",
Type: []string{"array"},
Items: &spec.SchemaOrArray{
Schema: &spec.Schema{
SchemaProps: spec.SchemaProps{
Ref: ref("github.com/kubernetes-operator/pkg/apis/jenkins/v1alpha2.AppliedGroovyScript"),
},
},
},
},
},
},
},
},
Dependencies: []string{
"github.com/kubernetes-operator/pkg/apis/jenkins/v1alpha2.AppliedGroovyScript", "k8s.io/apimachinery/pkg/apis/meta/v1.Time"},
}
}

View File

@ -7,7 +7,7 @@ import (
"strings"
"time"
"github.com/jenkinsci/kubernetes-operator/pkg/apis/jenkins/v1alpha2"
"github.com/jenkinsci/kubernetes-operator/api/v1alpha2"
jenkinsclient "github.com/jenkinsci/kubernetes-operator/pkg/client"
"github.com/jenkinsci/kubernetes-operator/pkg/configuration"
"github.com/jenkinsci/kubernetes-operator/pkg/configuration/base/resources"
@ -132,7 +132,7 @@ func (bar *BackupAndRestore) Restore(jenkinsClient jenkinsclient.Jenkins) error
bar.logger.V(log.VDebug).Info("Skipping restore backup")
if jenkins.Status.PendingBackup == 0 {
jenkins.Status.PendingBackup = 1
return bar.Client.Update(context.TODO(), jenkins)
return bar.Client.Status().Update(context.TODO(), jenkins)
}
return nil
}
@ -152,7 +152,7 @@ func (bar *BackupAndRestore) Restore(jenkinsClient jenkinsclient.Jenkins) error
bar.logger.V(log.VDebug).Info("Skipping restore backup, get latest action returned -1")
jenkins.Status.LastBackup = 0
jenkins.Status.PendingBackup = 1
return bar.Client.Update(context.TODO(), jenkins)
return bar.Client.Status().Update(context.TODO(), jenkins)
}
backupNumber, err = strconv.ParseUint(backupNumberString, 10, 64)
@ -180,11 +180,25 @@ func (bar *BackupAndRestore) Restore(jenkinsClient jenkinsclient.Jenkins) error
if err != nil {
return err
}
//TODO fix me because we're doing two saves unatomically
jenkins.Spec.Restore.RecoveryOnce = 0
err = bar.Client.Update(context.TODO(), jenkins)
if err != nil {
return err
}
key := types.NamespacedName{
Namespace: jenkins.Namespace,
Name: jenkins.Name,
}
err = bar.Client.Get(context.TODO(), key, jenkins)
if err != nil {
return err
}
bar.Configuration.Jenkins = jenkins
jenkins.Status.RestoredBackup = backupNumber
jenkins.Status.PendingBackup = backupNumber + 1
return bar.Client.Update(context.TODO(), jenkins)
return bar.Client.Status().Update(context.TODO(), jenkins)
}
return err
@ -216,7 +230,7 @@ func (bar *BackupAndRestore) Backup(setBackupDoneBeforePodDeletion bool) error {
jenkins.Status.LastBackup = backupNumber
jenkins.Status.PendingBackup = backupNumber
jenkins.Status.BackupDoneBeforePodDeletion = setBackupDoneBeforePodDeletion
return bar.Client.Update(context.TODO(), jenkins)
return bar.Client.Status().Update(context.TODO(), jenkins)
}
return err
@ -234,7 +248,7 @@ func triggerBackup(ticker *time.Ticker, k8sClient k8s.Client, logger logr.Logger
}
if jenkins.Status.LastBackup == jenkins.Status.PendingBackup {
jenkins.Status.PendingBackup++
err = k8sClient.Update(context.TODO(), jenkins)
err = k8sClient.Status().Update(context.TODO(), jenkins)
if err != nil {
logger.V(log.VWarn).Info(fmt.Sprintf("backup trigger, error when updating CR: %s", err))
}

View File

@ -7,7 +7,7 @@ import (
metav1 "k8s.io/apimachinery/pkg/apis/meta/v1"
)
func (r *ReconcileJenkinsBaseConfiguration) createScriptsConfigMap(meta metav1.ObjectMeta) error {
func (r *JenkinsBaseConfigurationReconciler) createScriptsConfigMap(meta metav1.ObjectMeta) error {
configMap, err := resources.NewScriptsConfigMap(meta, r.Configuration.Jenkins)
if err != nil {
return err
@ -15,7 +15,7 @@ func (r *ReconcileJenkinsBaseConfiguration) createScriptsConfigMap(meta metav1.O
return stackerr.WithStack(r.CreateOrUpdateResource(configMap))
}
func (r *ReconcileJenkinsBaseConfiguration) createInitConfigurationConfigMap(meta metav1.ObjectMeta) error {
func (r *JenkinsBaseConfigurationReconciler) createInitConfigurationConfigMap(meta metav1.ObjectMeta) error {
configMap, err := resources.NewInitConfigurationConfigMap(meta, r.Configuration.Jenkins)
if err != nil {
return err
@ -23,7 +23,7 @@ func (r *ReconcileJenkinsBaseConfiguration) createInitConfigurationConfigMap(met
return stackerr.WithStack(r.CreateOrUpdateResource(configMap))
}
func (r *ReconcileJenkinsBaseConfiguration) createBaseConfigurationConfigMap(meta metav1.ObjectMeta) error {
func (r *JenkinsBaseConfigurationReconciler) createBaseConfigurationConfigMap(meta metav1.ObjectMeta) error {
configMap, err := resources.NewBaseConfigurationConfigMap(meta, r.Configuration.Jenkins, r.KubernetesClusterDomain)
if err != nil {
return err

View File

@ -7,7 +7,7 @@ import (
corev1 "k8s.io/api/core/v1"
)
func (r *ReconcileJenkinsBaseConfiguration) compareContainers(expected corev1.Container, actual corev1.Container) (messages []string, verbose []string) {
func (r *JenkinsBaseConfigurationReconciler) compareContainers(expected corev1.Container, actual corev1.Container) (messages []string, verbose []string) {
if !reflect.DeepEqual(expected.Args, actual.Args) {
messages = append(messages, "Arguments have changed")
verbose = append(verbose, fmt.Sprintf("Arguments have changed to '%+v' in container '%s'", expected.Args, expected.Name))

View File

@ -4,7 +4,7 @@ import (
"context"
"fmt"
"github.com/jenkinsci/kubernetes-operator/pkg/apis/jenkins/v1alpha2"
"github.com/jenkinsci/kubernetes-operator/api/v1alpha2"
"github.com/jenkinsci/kubernetes-operator/pkg/configuration/base/resources"
"github.com/jenkinsci/kubernetes-operator/pkg/notifications/event"
"github.com/jenkinsci/kubernetes-operator/pkg/notifications/reason"
@ -16,7 +16,7 @@ import (
"sigs.k8s.io/controller-runtime/pkg/reconcile"
)
func (r *ReconcileJenkinsBaseConfiguration) ensureJenkinsDeployment(meta metav1.ObjectMeta) (reconcile.Result, error) {
func (r *JenkinsBaseConfigurationReconciler) ensureJenkinsDeployment(meta metav1.ObjectMeta) (reconcile.Result, error) {
userAndPasswordHash, err := r.calculateUserAndPasswordHash()
if err != nil {
return reconcile.Result{}, err

View File

@ -3,7 +3,7 @@ package base
import (
"context"
"github.com/jenkinsci/kubernetes-operator/pkg/apis/jenkins/v1alpha2"
"github.com/jenkinsci/kubernetes-operator/api/v1alpha2"
"github.com/jenkinsci/kubernetes-operator/pkg/configuration/base/resources"
stackerr "github.com/pkg/errors"
@ -11,7 +11,7 @@ import (
"k8s.io/apimachinery/pkg/types"
)
func (r *ReconcileJenkinsBaseConfiguration) addLabelForWatchesResources(customization v1alpha2.Customization) error {
func (r *JenkinsBaseConfigurationReconciler) addLabelForWatchesResources(customization v1alpha2.Customization) error {
labelsForWatchedResources := resources.BuildLabelsForWatchedResources(*r.Configuration.Jenkins)
if len(customization.Secret.Name) > 0 {

View File

@ -3,16 +3,16 @@ package base
import (
"fmt"
"github.com/jenkinsci/kubernetes-operator/pkg/apis/jenkins/v1alpha2"
"github.com/jenkinsci/kubernetes-operator/api/v1alpha2"
jenkinsclient "github.com/jenkinsci/kubernetes-operator/pkg/client"
"github.com/jenkinsci/kubernetes-operator/pkg/log"
"github.com/jenkinsci/kubernetes-operator/pkg/plugins"
"github.com/bndr/gojenkins"
"github.com/jenkinsci/kubernetes-operator/pkg/log"
"github.com/jenkinsci/kubernetes-operator/pkg/plugins"
stackerr "github.com/pkg/errors"
)
func (r *ReconcileJenkinsBaseConfiguration) verifyPlugins(jenkinsClient jenkinsclient.Jenkins) (bool, error) {
func (r *JenkinsBaseConfigurationReconciler) verifyPlugins(jenkinsClient jenkinsclient.Jenkins) (bool, error) {
allPluginsInJenkins, err := jenkinsClient.GetPlugins(fetchAllPlugins)
if err != nil {
return false, stackerr.WithStack(err)

View File

@ -5,10 +5,9 @@ import (
"fmt"
"reflect"
"github.com/jenkinsci/kubernetes-operator/pkg/apis/jenkins/v1alpha2"
"github.com/jenkinsci/kubernetes-operator/api/v1alpha2"
"github.com/jenkinsci/kubernetes-operator/pkg/configuration/backuprestore"
"github.com/jenkinsci/kubernetes-operator/pkg/configuration/base/resources"
"github.com/jenkinsci/kubernetes-operator/pkg/log"
"github.com/jenkinsci/kubernetes-operator/pkg/notifications/event"
"github.com/jenkinsci/kubernetes-operator/pkg/notifications/reason"
"github.com/jenkinsci/kubernetes-operator/version"
@ -20,7 +19,7 @@ import (
"sigs.k8s.io/controller-runtime/pkg/reconcile"
)
func (r *ReconcileJenkinsBaseConfiguration) checkForPodRecreation(currentJenkinsMasterPod corev1.Pod, userAndPasswordHash string) reason.Reason {
func (r *JenkinsBaseConfigurationReconciler) checkForPodRecreation(currentJenkinsMasterPod corev1.Pod, userAndPasswordHash string) reason.Reason {
var messages []string
var verbose []string
@ -52,7 +51,14 @@ func (r *ReconcileJenkinsBaseConfiguration) checkForPodRecreation(currentJenkins
r.Configuration.Jenkins.Status.OperatorVersion, version.Version))
}
if !reflect.DeepEqual(r.Configuration.Jenkins.Spec.Master.SecurityContext, currentJenkinsMasterPod.Spec.SecurityContext) {
//FIXME too hacky
var jenkinsSecurityContext *corev1.PodSecurityContext
if r.Configuration.Jenkins.Spec.Master.SecurityContext == nil {
jenkinsSecurityContext = &corev1.PodSecurityContext{}
} else {
jenkinsSecurityContext = r.Configuration.Jenkins.Spec.Master.SecurityContext
}
if !reflect.DeepEqual(jenkinsSecurityContext, currentJenkinsMasterPod.Spec.SecurityContext) {
messages = append(messages, "Jenkins pod security context has changed")
verbose = append(verbose, fmt.Sprintf("Jenkins pod security context has changed, actual '%+v' required '%+v'",
currentJenkinsMasterPod.Spec.SecurityContext, r.Configuration.Jenkins.Spec.Master.SecurityContext))
@ -140,7 +146,7 @@ func (r *ReconcileJenkinsBaseConfiguration) checkForPodRecreation(currentJenkins
return reason.NewPodRestart(reason.OperatorSource, messages, verbose...)
}
func (r *ReconcileJenkinsBaseConfiguration) ensureJenkinsMasterPod(meta metav1.ObjectMeta) (reconcile.Result, error) {
func (r *JenkinsBaseConfigurationReconciler) ensureJenkinsMasterPod(meta metav1.ObjectMeta) (reconcile.Result, error) {
userAndPasswordHash, err := r.calculateUserAndPasswordHash()
if err != nil {
return reconcile.Result{}, err
@ -162,13 +168,6 @@ func (r *ReconcileJenkinsBaseConfiguration) ensureJenkinsMasterPod(meta metav1.O
return reconcile.Result{}, stackerr.WithStack(err)
}
currentJenkinsMasterPod, err := r.waitUntilCreateJenkinsMasterPod()
if err == nil {
r.handleAdmissionControllerChanges(currentJenkinsMasterPod)
} else {
r.logger.V(log.VWarn).Info(fmt.Sprintf("waitUntilCreateJenkinsMasterPod has failed: %s", err))
}
now := metav1.Now()
r.Configuration.Jenkins.Status = v1alpha2.JenkinsStatus{
OperatorVersion: version.Version,
@ -177,7 +176,7 @@ func (r *ReconcileJenkinsBaseConfiguration) ensureJenkinsMasterPod(meta metav1.O
PendingBackup: r.Configuration.Jenkins.Status.LastBackup,
UserAndPasswordHash: userAndPasswordHash,
}
return reconcile.Result{Requeue: true}, r.Client.Update(context.TODO(), r.Configuration.Jenkins)
return reconcile.Result{Requeue: true}, r.Client.Status().Update(context.TODO(), r.Configuration.Jenkins)
} else if err != nil && !apierrors.IsNotFound(err) {
return reconcile.Result{}, stackerr.WithStack(err)
}

View File

@ -9,11 +9,12 @@ import (
stackerr "github.com/pkg/errors"
rbacv1 "k8s.io/api/rbac/v1"
"k8s.io/apimachinery/pkg/api/errors"
metav1 "k8s.io/apimachinery/pkg/apis/meta/v1"
"sigs.k8s.io/controller-runtime/pkg/client"
)
func (r *ReconcileJenkinsBaseConfiguration) createRBAC(meta metav1.ObjectMeta) error {
func (r *JenkinsBaseConfigurationReconciler) createRBAC(meta metav1.ObjectMeta) error {
err := r.createServiceAccount(meta)
if err != nil {
return err
@ -38,13 +39,16 @@ func (r *ReconcileJenkinsBaseConfiguration) createRBAC(meta metav1.ObjectMeta) e
return nil
}
func (r *ReconcileJenkinsBaseConfiguration) ensureExtraRBAC(meta metav1.ObjectMeta) error {
func (r *JenkinsBaseConfigurationReconciler) ensureExtraRBAC(meta metav1.ObjectMeta) error {
var err error
var name string
for _, roleRef := range r.Configuration.Jenkins.Spec.Roles {
name = getExtraRoleBindingName(meta.Name, roleRef)
roleBinding := resources.NewRoleBinding(name, meta.Namespace, meta.Name, roleRef)
err = r.CreateOrUpdateResource(roleBinding)
err := r.Client.Create(context.TODO(), roleBinding)
if err != nil && errors.IsAlreadyExists(err) {
continue
}
if err != nil {
return stackerr.WithStack(err)
}

View File

@ -4,13 +4,14 @@ import (
"context"
"testing"
"github.com/bndr/gojenkins"
"github.com/golang/mock/gomock"
"github.com/jenkinsci/kubernetes-operator/pkg/apis/jenkins/v1alpha2"
"github.com/jenkinsci/kubernetes-operator/api/v1alpha2"
"github.com/jenkinsci/kubernetes-operator/pkg/client"
"github.com/jenkinsci/kubernetes-operator/pkg/configuration"
"github.com/jenkinsci/kubernetes-operator/pkg/configuration/base/resources"
"github.com/jenkinsci/kubernetes-operator/pkg/log"
"github.com/bndr/gojenkins"
"github.com/golang/mock/gomock"
"github.com/stretchr/testify/assert"
corev1 "k8s.io/api/core/v1"
rbacv1 "k8s.io/api/rbac/v1"
@ -161,12 +162,12 @@ func TestCompareVolumes(t *testing.T) {
})
}
func TestReconcileJenkinsBaseConfiguration_verifyPlugins(t *testing.T) {
func TestJenkinsBaseConfigurationReconciler_verifyPlugins(t *testing.T) {
log.SetupLogger(true)
t.Run("happy, empty base and user plugins", func(t *testing.T) {
jenkins := &v1alpha2.Jenkins{}
r := ReconcileJenkinsBaseConfiguration{
r := JenkinsBaseConfigurationReconciler{
logger: log.Log,
Configuration: configuration.Configuration{
Jenkins: jenkins,
@ -194,7 +195,7 @@ func TestReconcileJenkinsBaseConfiguration_verifyPlugins(t *testing.T) {
},
},
}
r := ReconcileJenkinsBaseConfiguration{
r := JenkinsBaseConfigurationReconciler{
logger: log.Log,
Configuration: configuration.Configuration{
Jenkins: jenkins,
@ -238,7 +239,7 @@ func TestReconcileJenkinsBaseConfiguration_verifyPlugins(t *testing.T) {
},
},
}
r := ReconcileJenkinsBaseConfiguration{
r := JenkinsBaseConfigurationReconciler{
logger: log.Log,
Configuration: configuration.Configuration{
Jenkins: jenkins,
@ -275,7 +276,7 @@ func TestReconcileJenkinsBaseConfiguration_verifyPlugins(t *testing.T) {
},
},
}
r := ReconcileJenkinsBaseConfiguration{
r := JenkinsBaseConfigurationReconciler{
logger: log.Log,
Configuration: configuration.Configuration{
Jenkins: jenkins,
@ -312,7 +313,7 @@ func TestReconcileJenkinsBaseConfiguration_verifyPlugins(t *testing.T) {
},
},
}
r := ReconcileJenkinsBaseConfiguration{
r := JenkinsBaseConfigurationReconciler{
logger: log.Log,
Configuration: configuration.Configuration{
Jenkins: jenkins,
@ -349,7 +350,7 @@ func TestReconcileJenkinsBaseConfiguration_verifyPlugins(t *testing.T) {
},
},
}
r := ReconcileJenkinsBaseConfiguration{
r := JenkinsBaseConfigurationReconciler{
logger: log.Log,
Configuration: configuration.Configuration{
Jenkins: jenkins,
@ -386,7 +387,7 @@ func TestReconcileJenkinsBaseConfiguration_verifyPlugins(t *testing.T) {
},
},
}
r := ReconcileJenkinsBaseConfiguration{
r := JenkinsBaseConfigurationReconciler{
logger: log.Log,
Configuration: configuration.Configuration{
Jenkins: jenkins,
@ -415,7 +416,7 @@ func TestReconcileJenkinsBaseConfiguration_verifyPlugins(t *testing.T) {
},
},
}
r := ReconcileJenkinsBaseConfiguration{
r := JenkinsBaseConfigurationReconciler{
logger: log.Log,
Configuration: configuration.Configuration{
Jenkins: jenkins,
@ -618,7 +619,7 @@ func TestEnsureExtraRBAC(t *testing.T) {
t.Run("empty", func(t *testing.T) {
// given
fakeClient := fake.NewFakeClient()
fakeClient := fake.NewClientBuilder().Build()
err := v1alpha2.SchemeBuilder.AddToScheme(scheme.Scheme)
assert.NoError(t, err)
@ -654,7 +655,7 @@ func TestEnsureExtraRBAC(t *testing.T) {
clusterRoleKind := "ClusterRole"
t.Run("one extra", func(t *testing.T) {
// given
fakeClient := fake.NewFakeClient()
fakeClient := fake.NewClientBuilder().Build()
err := v1alpha2.SchemeBuilder.AddToScheme(scheme.Scheme)
assert.NoError(t, err)
@ -695,7 +696,7 @@ func TestEnsureExtraRBAC(t *testing.T) {
})
t.Run("two extra", func(t *testing.T) {
// given
fakeClient := fake.NewFakeClient()
fakeClient := fake.NewClientBuilder().Build()
err := v1alpha2.SchemeBuilder.AddToScheme(scheme.Scheme)
assert.NoError(t, err)
@ -743,7 +744,7 @@ func TestEnsureExtraRBAC(t *testing.T) {
})
t.Run("delete one extra", func(t *testing.T) {
// given
fakeClient := fake.NewFakeClient()
fakeClient := fake.NewClientBuilder().Build()
err := v1alpha2.SchemeBuilder.AddToScheme(scheme.Scheme)
assert.NoError(t, err)
@ -801,8 +802,8 @@ func TestEnsureExtraRBAC(t *testing.T) {
roleBindings, err := fetchAllRoleBindings(fakeClient)
assert.NoError(t, err)
assert.Equal(t, 3, len(roleBindings.Items))
assert.Equal(t, metaObject.Name, roleBindings.Items[1].Name)
assert.Equal(t, jenkins.Spec.Roles[0], roleBindings.Items[2].RoleRef)
assert.Equal(t, metaObject.Name, roleBindings.Items[0].Name)
assert.Equal(t, jenkins.Spec.Roles[0], roleBindings.Items[1].RoleRef)
})
}

View File

@ -9,7 +9,7 @@ import (
"strings"
"time"
"github.com/jenkinsci/kubernetes-operator/pkg/apis/jenkins/v1alpha2"
"github.com/jenkinsci/kubernetes-operator/api/v1alpha2"
jenkinsclient "github.com/jenkinsci/kubernetes-operator/pkg/client"
"github.com/jenkinsci/kubernetes-operator/pkg/configuration"
"github.com/jenkinsci/kubernetes-operator/pkg/configuration/base/resources"
@ -33,15 +33,15 @@ const (
)
// ReconcileJenkinsBaseConfiguration defines values required for Jenkins base configuration.
type ReconcileJenkinsBaseConfiguration struct {
type JenkinsBaseConfigurationReconciler struct {
configuration.Configuration
logger logr.Logger
jenkinsAPIConnectionSettings jenkinsclient.JenkinsAPIConnectionSettings
}
// New create structure which takes care of base configuration
func New(config configuration.Configuration, jenkinsAPIConnectionSettings jenkinsclient.JenkinsAPIConnectionSettings) *ReconcileJenkinsBaseConfiguration {
return &ReconcileJenkinsBaseConfiguration{
func New(config configuration.Configuration, jenkinsAPIConnectionSettings jenkinsclient.JenkinsAPIConnectionSettings) *JenkinsBaseConfigurationReconciler {
return &JenkinsBaseConfigurationReconciler{
Configuration: config,
logger: log.Log.WithValues("cr", config.Jenkins.Name),
jenkinsAPIConnectionSettings: jenkinsAPIConnectionSettings,
@ -49,7 +49,7 @@ func New(config configuration.Configuration, jenkinsAPIConnectionSettings jenkin
}
// Reconcile takes care of base configuration.
func (r *ReconcileJenkinsBaseConfiguration) Reconcile() (reconcile.Result, jenkinsclient.Jenkins, error) {
func (r *JenkinsBaseConfigurationReconciler) Reconcile() (reconcile.Result, jenkinsclient.Jenkins, error) {
metaObject := resources.NewResourceObjectMeta(r.Configuration.Jenkins)
// Create Necessary Resources
@ -134,7 +134,7 @@ func useDeploymentForJenkinsMaster(jenkins *v1alpha2.Jenkins) bool {
return false
}
func (r *ReconcileJenkinsBaseConfiguration) ensureResourcesRequiredForJenkinsPod(metaObject metav1.ObjectMeta) error {
func (r *JenkinsBaseConfigurationReconciler) ensureResourcesRequiredForJenkinsPod(metaObject metav1.ObjectMeta) error {
if err := r.createOperatorCredentialsSecret(metaObject); err != nil {
return err
}
@ -197,7 +197,7 @@ func (r *ReconcileJenkinsBaseConfiguration) ensureResourcesRequiredForJenkinsPod
return nil
}
func (r *ReconcileJenkinsBaseConfiguration) createOperatorCredentialsSecret(meta metav1.ObjectMeta) error {
func (r *JenkinsBaseConfigurationReconciler) createOperatorCredentialsSecret(meta metav1.ObjectMeta) error {
found := &corev1.Secret{}
err := r.Configuration.Client.Get(context.TODO(), types.NamespacedName{Name: resources.GetOperatorCredentialsSecretName(r.Configuration.Jenkins), Namespace: r.Configuration.Jenkins.ObjectMeta.Namespace}, found)
@ -214,7 +214,7 @@ func (r *ReconcileJenkinsBaseConfiguration) createOperatorCredentialsSecret(meta
return stackerr.WithStack(r.UpdateResource(resources.NewOperatorCredentialsSecret(meta, r.Configuration.Jenkins)))
}
func (r *ReconcileJenkinsBaseConfiguration) calculateUserAndPasswordHash() (string, error) {
func (r *JenkinsBaseConfigurationReconciler) calculateUserAndPasswordHash() (string, error) {
credentialsSecret := &corev1.Secret{}
err := r.Client.Get(context.TODO(), types.NamespacedName{Name: resources.GetOperatorCredentialsSecretName(r.Configuration.Jenkins), Namespace: r.Configuration.Jenkins.ObjectMeta.Namespace}, credentialsSecret)
if err != nil {
@ -222,8 +222,14 @@ func (r *ReconcileJenkinsBaseConfiguration) calculateUserAndPasswordHash() (stri
}
hash := sha256.New()
hash.Write(credentialsSecret.Data[resources.OperatorCredentialsSecretUserNameKey])
hash.Write(credentialsSecret.Data[resources.OperatorCredentialsSecretPasswordKey])
_, err = hash.Write(credentialsSecret.Data[resources.OperatorCredentialsSecretUserNameKey])
if err != nil {
return "", stackerr.WithStack(err)
}
_, err = hash.Write(credentialsSecret.Data[resources.OperatorCredentialsSecretPasswordKey])
if err != nil {
return "", stackerr.WithStack(err)
}
return base64.StdEncoding.EncodeToString(hash.Sum(nil)), nil
}
@ -282,7 +288,7 @@ func CompareContainerVolumeMounts(expected corev1.Container, actual corev1.Conta
}
// compareVolumes returns true if Jenkins pod and Jenkins CR volumes are the same
func (r *ReconcileJenkinsBaseConfiguration) compareVolumes(actualPod corev1.Pod) bool {
func (r *JenkinsBaseConfigurationReconciler) compareVolumes(actualPod corev1.Pod) bool {
var withoutServiceAccount []corev1.Volume
for _, volume := range actualPod.Spec.Volumes {
if !strings.HasPrefix(volume.Name, actualPod.Spec.ServiceAccountName) {
@ -296,7 +302,7 @@ func (r *ReconcileJenkinsBaseConfiguration) compareVolumes(actualPod corev1.Pod)
)
}
func (r *ReconcileJenkinsBaseConfiguration) detectJenkinsMasterPodStartingIssues() (stopReconcileLoop bool, err error) {
func (r *JenkinsBaseConfigurationReconciler) detectJenkinsMasterPodStartingIssues() (stopReconcileLoop bool, err error) {
jenkinsMasterPod, err := r.Configuration.GetJenkinsMasterPod()
if err != nil {
return false, err
@ -330,7 +336,7 @@ func (r *ReconcileJenkinsBaseConfiguration) detectJenkinsMasterPodStartingIssues
return false, nil
}
func (r *ReconcileJenkinsBaseConfiguration) filterEvents(source corev1.EventList, jenkinsMasterPod corev1.Pod) []string {
func (r *JenkinsBaseConfigurationReconciler) filterEvents(source corev1.EventList, jenkinsMasterPod corev1.Pod) []string {
events := []string{}
for _, eventItem := range source.Items {
if r.Configuration.Jenkins.Status.ProvisionStartTime.UTC().After(eventItem.LastTimestamp.UTC()) {
@ -347,7 +353,7 @@ func (r *ReconcileJenkinsBaseConfiguration) filterEvents(source corev1.EventList
return events
}
func (r *ReconcileJenkinsBaseConfiguration) waitForJenkins() (reconcile.Result, error) {
func (r *JenkinsBaseConfigurationReconciler) waitForJenkins() (reconcile.Result, error) {
jenkinsMasterPod, err := r.Configuration.GetJenkinsMasterPod()
if err != nil {
return reconcile.Result{}, err
@ -388,7 +394,7 @@ func (r *ReconcileJenkinsBaseConfiguration) waitForJenkins() (reconcile.Result,
return reconcile.Result{}, nil
}
func (r *ReconcileJenkinsBaseConfiguration) ensureBaseConfiguration(jenkinsClient jenkinsclient.Jenkins) (reconcile.Result, error) {
func (r *JenkinsBaseConfigurationReconciler) ensureBaseConfiguration(jenkinsClient jenkinsclient.Jenkins) (reconcile.Result, error) {
customization := v1alpha2.GroovyScripts{
Customization: v1alpha2.Customization{
Secret: v1alpha2.SecretRef{Name: ""},
@ -403,30 +409,3 @@ func (r *ReconcileJenkinsBaseConfiguration) ensureBaseConfiguration(jenkinsClien
})
return reconcile.Result{Requeue: requeue}, err
}
func (r *ReconcileJenkinsBaseConfiguration) waitUntilCreateJenkinsMasterPod() (currentJenkinsMasterPod *corev1.Pod, err error) {
currentJenkinsMasterPod, err = r.Configuration.GetJenkinsMasterPod()
for {
if err != nil && !apierrors.IsNotFound(err) {
return nil, stackerr.WithStack(err)
} else if err == nil {
break
}
currentJenkinsMasterPod, err = r.Configuration.GetJenkinsMasterPod()
time.Sleep(time.Millisecond * 10)
}
return
}
func (r *ReconcileJenkinsBaseConfiguration) handleAdmissionControllerChanges(currentJenkinsMasterPod *corev1.Pod) {
if !reflect.DeepEqual(r.Configuration.Jenkins.Spec.Master.SecurityContext, currentJenkinsMasterPod.Spec.SecurityContext) {
r.Configuration.Jenkins.Spec.Master.SecurityContext = currentJenkinsMasterPod.Spec.SecurityContext
r.logger.Info(fmt.Sprintf("The Admission controller has changed the Jenkins master pod spec.securityContext, changing the Jenkinc CR spec.master.securityContext to '%+v'", currentJenkinsMasterPod.Spec.SecurityContext))
}
for i, container := range r.Configuration.Jenkins.Spec.Master.Containers {
if !reflect.DeepEqual(container.SecurityContext, currentJenkinsMasterPod.Spec.Containers[i].SecurityContext) {
r.Configuration.Jenkins.Spec.Master.Containers[i].SecurityContext = currentJenkinsMasterPod.Spec.Containers[i].SecurityContext
r.logger.Info(fmt.Sprintf("The Admission controller has changed the securityContext, changing the Jenkins CR spec.master.containers[%s].securityContext to '+%v'", container.Name, currentJenkinsMasterPod.Spec.Containers[i].SecurityContext))
}
}
}

View File

@ -3,7 +3,7 @@ package resources
import (
"fmt"
"github.com/jenkinsci/kubernetes-operator/pkg/apis/jenkins/v1alpha2"
"github.com/jenkinsci/kubernetes-operator/api/v1alpha2"
"github.com/jenkinsci/kubernetes-operator/pkg/constants"
corev1 "k8s.io/api/core/v1"

View File

@ -1,128 +0,0 @@
package resources
import (
"fmt"
jenkinsv1alpha2 "github.com/jenkinsci/kubernetes-operator/pkg/apis/jenkins/v1alpha2"
corev1 "k8s.io/api/core/v1"
metav1 "k8s.io/apimachinery/pkg/apis/meta/v1"
logf "sigs.k8s.io/controller-runtime/pkg/log"
)
const (
NameWithSuffixFormat = "%s-%s"
PluginDefinitionFormat = "%s:%s"
BuilderDockerfileArg = "--dockerfile=/workspace/dockerfile/Dockerfile"
BuilderContextDirArg = "--context=dir://workspace/"
BuilderPushArg = "--no-push"
BuilderDigestFileArg = "--digest-file=/dev/termination-log"
BuilderSuffix = "builder"
DockerfileStorageSuffix = "dockerfile-storage"
DockerfileNameSuffix = "dockerfile"
JenkinsImageBuilderImage = "gcr.io/kaniko-project/executor:latest"
JenkinsImageBuilderName = "jenkins-image-builder"
JenkinsImageDefaultBaseImage = "jenkins/jenkins:lts"
DockerfileName = "Dockerfile"
DockerfileTemplate = `FROM %s
RUN curl -o /tmp/install-plugins.sh https://raw.githubusercontent.com/jenkinsci/docker/master/install-plugins.sh
RUN chmod +x /tmp/install-plugins.sh
RUN install-plugins.sh %s `
)
var log = logf.Log.WithName("controller_jenkinsimage")
// NewBuilderPod returns a busybox pod with the same name/namespace as the cr.
func NewBuilderPod(cr *jenkinsv1alpha2.JenkinsImage) *corev1.Pod {
name := fmt.Sprintf(NameWithSuffixFormat, cr.Name, BuilderSuffix)
args := []string{BuilderDockerfileArg, BuilderContextDirArg, BuilderPushArg, BuilderDigestFileArg}
volumes := getVolumes(cr)
volumeMounts := getVolumesMounts(cr)
p := &corev1.Pod{
ObjectMeta: metav1.ObjectMeta{
Name: name,
Namespace: cr.Namespace,
},
Spec: corev1.PodSpec{
RestartPolicy: corev1.RestartPolicyNever,
Containers: []corev1.Container{
{
Name: JenkinsImageBuilderName,
Image: JenkinsImageBuilderImage,
Args: args,
VolumeMounts: volumeMounts,
},
},
Volumes: volumes,
},
}
return p
}
// NewDockerfileConfigMap returns a busybox pod with the same name/namespace as the cr.
func NewDockerfileConfigMap(cr *jenkinsv1alpha2.JenkinsImage) *corev1.ConfigMap {
dockerfileContent := fmt.Sprintf(DockerfileTemplate, getDefaultedBaseImage(cr), getPluginsList(cr))
name := fmt.Sprintf(NameWithSuffixFormat, cr.Name, DockerfileNameSuffix)
data := map[string]string{DockerfileName: dockerfileContent}
dockerfile := &corev1.ConfigMap{
ObjectMeta: metav1.ObjectMeta{
Name: name,
Namespace: cr.Namespace,
},
Data: data,
}
return dockerfile
}
func getPluginsList(cr *jenkinsv1alpha2.JenkinsImage) string {
logger := log.WithName("jenkinsimage_getPluginsList")
plugins := ""
for _, v := range cr.Spec.Plugins {
plugins += fmt.Sprintf(PluginDefinitionFormat, v.Name, v.Version) + " "
logger.Info(fmt.Sprintf("Adding plugin %s:%s ", v.Name, v.Version))
}
return plugins
}
func getDefaultedBaseImage(cr *jenkinsv1alpha2.JenkinsImage) string {
if len(cr.Spec.BaseImage.Name) != 0 {
return cr.Spec.BaseImage.Name
}
return JenkinsImageDefaultBaseImage
}
func getVolumes(cr *jenkinsv1alpha2.JenkinsImage) []corev1.Volume {
name := fmt.Sprintf(NameWithSuffixFormat, cr.Name, DockerfileStorageSuffix)
storage := corev1.Volume{
Name: name,
VolumeSource: corev1.VolumeSource{
EmptyDir: &corev1.EmptyDirVolumeSource{},
},
}
name = fmt.Sprintf(NameWithSuffixFormat, cr.Name, DockerfileNameSuffix)
config := corev1.Volume{
Name: name,
VolumeSource: corev1.VolumeSource{
ConfigMap: &corev1.ConfigMapVolumeSource{
LocalObjectReference: corev1.LocalObjectReference{Name: name},
},
},
}
volumes := []corev1.Volume{storage, config}
return volumes
}
func getVolumesMounts(cr *jenkinsv1alpha2.JenkinsImage) []corev1.VolumeMount {
name := fmt.Sprintf(NameWithSuffixFormat, cr.Name, DockerfileStorageSuffix)
storage := corev1.VolumeMount{
Name: name,
MountPath: "/workspace",
}
name = fmt.Sprintf(NameWithSuffixFormat, cr.Name, DockerfileNameSuffix)
config := corev1.VolumeMount{
Name: name,
MountPath: "/workspace/dockerfile",
}
volumeMounts := []corev1.VolumeMount{storage, config}
return volumeMounts
}

View File

@ -3,7 +3,7 @@ package resources
import (
"fmt"
"github.com/jenkinsci/kubernetes-operator/pkg/apis/jenkins/v1alpha2"
"github.com/jenkinsci/kubernetes-operator/api/v1alpha2"
appsv1 "k8s.io/api/apps/v1"
corev1 "k8s.io/api/core/v1"

View File

@ -4,8 +4,8 @@ import (
"fmt"
"text/template"
"github.com/jenkinsci/kubernetes-operator/api/v1alpha2"
"github.com/jenkinsci/kubernetes-operator/internal/render"
"github.com/jenkinsci/kubernetes-operator/pkg/apis/jenkins/v1alpha2"
"github.com/jenkinsci/kubernetes-operator/pkg/constants"
corev1 "k8s.io/api/core/v1"

View File

@ -3,7 +3,7 @@ package resources
import (
"fmt"
"github.com/jenkinsci/kubernetes-operator/pkg/apis/jenkins/v1alpha2"
"github.com/jenkinsci/kubernetes-operator/api/v1alpha2"
"github.com/jenkinsci/kubernetes-operator/pkg/constants"
metav1 "k8s.io/apimachinery/pkg/apis/meta/v1"

View File

@ -3,7 +3,7 @@ package resources
import (
"fmt"
"github.com/jenkinsci/kubernetes-operator/pkg/apis/jenkins/v1alpha2"
"github.com/jenkinsci/kubernetes-operator/api/v1alpha2"
"github.com/jenkinsci/kubernetes-operator/pkg/constants"
corev1 "k8s.io/api/core/v1"

View File

@ -4,7 +4,7 @@ import (
"fmt"
"strings"
"github.com/jenkinsci/kubernetes-operator/pkg/apis/jenkins/v1alpha2"
"github.com/jenkinsci/kubernetes-operator/api/v1alpha2"
"github.com/jenkinsci/kubernetes-operator/pkg/constants"
corev1 "k8s.io/api/core/v1"

View File

@ -3,7 +3,8 @@ package resources
import (
"testing"
"github.com/jenkinsci/kubernetes-operator/pkg/apis/jenkins/v1alpha2"
"github.com/jenkinsci/kubernetes-operator/api/v1alpha2"
"github.com/stretchr/testify/assert"
)

View File

@ -5,7 +5,7 @@ import (
"k8s.io/apimachinery/pkg/util/intstr"
)
func NewSimpleProbe(uri string, port string, scheme corev1.URIScheme, initialDelaySeconds int32) *corev1.Probe {
func NewProbe(uri string, port string, scheme corev1.URIScheme, initialDelaySeconds, timeoutSeconds, failureThreshold int32) *corev1.Probe {
return &corev1.Probe{
Handler: corev1.Handler{
HTTPGet: &corev1.HTTPGetAction{
@ -15,12 +15,9 @@ func NewSimpleProbe(uri string, port string, scheme corev1.URIScheme, initialDel
},
},
InitialDelaySeconds: initialDelaySeconds,
TimeoutSeconds: timeoutSeconds,
FailureThreshold: failureThreshold,
SuccessThreshold: int32(1),
PeriodSeconds: int32(1),
}
}
func NewProbe(uri string, port string, scheme corev1.URIScheme, initialDelaySeconds, timeoutSeconds, failureThreshold int32) *corev1.Probe {
p := NewSimpleProbe(uri, port, scheme, initialDelaySeconds)
p.TimeoutSeconds = timeoutSeconds
p.FailureThreshold = failureThreshold
return p
}

View File

@ -3,9 +3,9 @@ package resources
import (
"testing"
"github.com/jenkinsci/kubernetes-operator/pkg/apis/jenkins/v1alpha2"
"github.com/stretchr/testify/assert"
"github.com/jenkinsci/kubernetes-operator/api/v1alpha2"
"github.com/stretchr/testify/assert"
corev1 "k8s.io/api/core/v1"
)

View File

@ -1,17 +1,15 @@
package resources
import (
"github.com/jenkinsci/kubernetes-operator/pkg/apis/jenkins/v1alpha2"
"github.com/jenkinsci/kubernetes-operator/api/v1alpha2"
routev1 "github.com/openshift/api/route/v1"
"k8s.io/apimachinery/pkg/runtime/schema"
"k8s.io/apimachinery/pkg/util/intstr"
"k8s.io/client-go/discovery"
"k8s.io/client-go/kubernetes"
)
//RouteKind the kind name for route
const RouteKind = "Route"
var isRouteAPIAvailable = false
var routeAPIChecked = false

View File

@ -4,8 +4,8 @@ import (
"fmt"
"text/template"
"github.com/jenkinsci/kubernetes-operator/api/v1alpha2"
"github.com/jenkinsci/kubernetes-operator/internal/render"
"github.com/jenkinsci/kubernetes-operator/pkg/apis/jenkins/v1alpha2"
"github.com/jenkinsci/kubernetes-operator/pkg/constants"
corev1 "k8s.io/api/core/v1"

View File

@ -2,17 +2,16 @@ package resources
import (
"fmt"
"net"
"os"
"strings"
"github.com/jenkinsci/kubernetes-operator/pkg/apis/jenkins/v1alpha2"
"github.com/jenkinsci/kubernetes-operator/api/v1alpha2"
"github.com/jenkinsci/kubernetes-operator/pkg/constants"
"github.com/operator-framework/operator-sdk/pkg/k8sutil"
stackerr "github.com/pkg/errors"
stackerr "github.com/pkg/errors"
corev1 "k8s.io/api/core/v1"
"k8s.io/apimachinery/pkg/util/intstr"
"net"
"strings"
)
//ServiceKind the kind name for Service
@ -71,7 +70,7 @@ func GetJenkinsSlavesServiceFQDN(jenkins *v1alpha2.Jenkins, kubernetesClusterDom
// GetClusterDomain returns Kubernetes cluster domain, default to "cluster.local"
func getClusterDomain(kubernetesClusterDomain string) (string, error) {
isRunningInCluster, err := isRunningInCluster()
isRunningInCluster, err := IsRunningInCluster()
if !isRunningInCluster {
return kubernetesClusterDomain, nil
}
@ -93,13 +92,13 @@ func getClusterDomain(kubernetesClusterDomain string) (string, error) {
return kubernetesClusterDomain, nil
}
func isRunningInCluster() (bool, error) {
_, err := k8sutil.GetOperatorNamespace()
if err != nil {
if err == k8sutil.ErrNoNamespace || err == k8sutil.ErrRunLocal {
return false, nil
}
return false, stackerr.WithStack(err)
func IsRunningInCluster() (bool, error) {
const inClusterNamespacePath = "/var/run/secrets/kubernetes.io/serviceaccount/namespace"
_, err := os.Stat(inClusterNamespacePath)
if os.IsNotExist(err) {
return false, nil
} else if err == nil {
return true, nil
}
return true, nil
return false, err
}

View File

@ -4,19 +4,19 @@ import (
"context"
"fmt"
"github.com/jenkinsci/kubernetes-operator/pkg/apis/jenkins/v1alpha2"
"k8s.io/apimachinery/pkg/util/intstr"
"github.com/jenkinsci/kubernetes-operator/api/v1alpha2"
"github.com/jenkinsci/kubernetes-operator/pkg/configuration/base/resources"
routev1 "github.com/openshift/api/route/v1"
stackerr "github.com/pkg/errors"
apierrors "k8s.io/apimachinery/pkg/api/errors"
metav1 "k8s.io/apimachinery/pkg/apis/meta/v1"
"k8s.io/apimachinery/pkg/types"
"k8s.io/apimachinery/pkg/util/intstr"
)
// createRoute takes the ServiceName and Creates the Route based on it
func (r *ReconcileJenkinsBaseConfiguration) createRoute(meta metav1.ObjectMeta, serviceName string, config *v1alpha2.Jenkins) error {
func (r *JenkinsBaseConfigurationReconciler) createRoute(meta metav1.ObjectMeta, serviceName string, config *v1alpha2.Jenkins) error {
route := routev1.Route{}
name := fmt.Sprintf("jenkins-%s", config.ObjectMeta.Name)
err := r.Client.Get(context.TODO(), types.NamespacedName{Name: name, Namespace: meta.Namespace}, &route)

View File

@ -3,7 +3,7 @@ package base
import (
"context"
"github.com/jenkinsci/kubernetes-operator/pkg/apis/jenkins/v1alpha2"
"github.com/jenkinsci/kubernetes-operator/api/v1alpha2"
"github.com/jenkinsci/kubernetes-operator/pkg/configuration/base/resources"
stackerr "github.com/pkg/errors"
@ -13,7 +13,7 @@ import (
"k8s.io/apimachinery/pkg/types"
)
func (r *ReconcileJenkinsBaseConfiguration) createService(meta metav1.ObjectMeta, name string, config v1alpha2.Service, targetPort int32) error {
func (r *JenkinsBaseConfigurationReconciler) createService(meta metav1.ObjectMeta, name string, config v1alpha2.Service, targetPort int32) error {
service := corev1.Service{}
err := r.Client.Get(context.TODO(), types.NamespacedName{Name: name, Namespace: meta.Namespace}, &service)
if err != nil && apierrors.IsNotFound(err) {

View File

@ -14,7 +14,7 @@ import (
"k8s.io/apimachinery/pkg/types"
)
func (r *ReconcileJenkinsBaseConfiguration) createServiceAccount(meta metav1.ObjectMeta) error {
func (r *JenkinsBaseConfigurationReconciler) createServiceAccount(meta metav1.ObjectMeta) error {
serviceAccount := &corev1.ServiceAccount{}
err := r.Client.Get(context.TODO(), types.NamespacedName{Name: meta.Name, Namespace: meta.Namespace}, serviceAccount)
annotations := r.Configuration.Jenkins.Spec.ServiceAccount.Annotations

View File

@ -6,7 +6,7 @@ import (
"regexp"
"strings"
"github.com/jenkinsci/kubernetes-operator/pkg/apis/jenkins/v1alpha2"
"github.com/jenkinsci/kubernetes-operator/api/v1alpha2"
"github.com/jenkinsci/kubernetes-operator/pkg/configuration/base/resources"
"github.com/jenkinsci/kubernetes-operator/pkg/constants"
"github.com/jenkinsci/kubernetes-operator/pkg/plugins"
@ -23,7 +23,7 @@ var (
)
// Validate validates Jenkins CR Spec.master section
func (r *ReconcileJenkinsBaseConfiguration) Validate(jenkins *v1alpha2.Jenkins) ([]string, error) {
func (r *JenkinsBaseConfigurationReconciler) Validate(jenkins *v1alpha2.Jenkins) ([]string, error) {
var messages []string
if msg := r.validateReservedVolumes(); len(msg) > 0 {
@ -70,7 +70,7 @@ func (r *ReconcileJenkinsBaseConfiguration) Validate(jenkins *v1alpha2.Jenkins)
return messages, nil
}
func (r *ReconcileJenkinsBaseConfiguration) validateJenkinsMasterContainerCommand() []string {
func (r *JenkinsBaseConfigurationReconciler) validateJenkinsMasterContainerCommand() []string {
masterContainer := r.Configuration.GetJenkinsMasterContainer()
if masterContainer == nil {
return []string{}
@ -102,7 +102,7 @@ func (r *ReconcileJenkinsBaseConfiguration) validateJenkinsMasterContainerComman
return []string{}
}
func (r *ReconcileJenkinsBaseConfiguration) validateImagePullSecrets() ([]string, error) {
func (r *JenkinsBaseConfigurationReconciler) validateImagePullSecrets() ([]string, error) {
var messages []string
for _, sr := range r.Configuration.Jenkins.Spec.Master.ImagePullSecrets {
msg, err := r.validateImagePullSecret(sr.Name)
@ -116,7 +116,7 @@ func (r *ReconcileJenkinsBaseConfiguration) validateImagePullSecrets() ([]string
return messages, nil
}
func (r *ReconcileJenkinsBaseConfiguration) validateImagePullSecret(secretName string) ([]string, error) {
func (r *JenkinsBaseConfigurationReconciler) validateImagePullSecret(secretName string) ([]string, error) {
var messages []string
secret := &corev1.Secret{}
err := r.Client.Get(context.TODO(), types.NamespacedName{Name: secretName, Namespace: r.Configuration.Jenkins.ObjectMeta.Namespace}, secret)
@ -142,7 +142,7 @@ func (r *ReconcileJenkinsBaseConfiguration) validateImagePullSecret(secretName s
return messages, nil
}
func (r *ReconcileJenkinsBaseConfiguration) validateVolumes() ([]string, error) {
func (r *JenkinsBaseConfigurationReconciler) validateVolumes() ([]string, error) {
var messages []string
for _, volume := range r.Configuration.Jenkins.Spec.Master.Volumes {
switch {
@ -170,7 +170,7 @@ func (r *ReconcileJenkinsBaseConfiguration) validateVolumes() ([]string, error)
return messages, nil
}
func (r *ReconcileJenkinsBaseConfiguration) validatePersistentVolumeClaim(volume corev1.Volume) ([]string, error) {
func (r *JenkinsBaseConfigurationReconciler) validatePersistentVolumeClaim(volume corev1.Volume) ([]string, error) {
var messages []string
pvc := &corev1.PersistentVolumeClaim{}
@ -184,7 +184,7 @@ func (r *ReconcileJenkinsBaseConfiguration) validatePersistentVolumeClaim(volume
return messages, nil
}
func (r *ReconcileJenkinsBaseConfiguration) validateConfigMapVolume(volume corev1.Volume) ([]string, error) {
func (r *JenkinsBaseConfigurationReconciler) validateConfigMapVolume(volume corev1.Volume) ([]string, error) {
var messages []string
if volume.ConfigMap.Optional != nil && *volume.ConfigMap.Optional {
return nil, nil
@ -201,7 +201,7 @@ func (r *ReconcileJenkinsBaseConfiguration) validateConfigMapVolume(volume corev
return messages, nil
}
func (r *ReconcileJenkinsBaseConfiguration) validateSecretVolume(volume corev1.Volume) ([]string, error) {
func (r *JenkinsBaseConfigurationReconciler) validateSecretVolume(volume corev1.Volume) ([]string, error) {
var messages []string
if volume.Secret.Optional != nil && *volume.Secret.Optional {
return nil, nil
@ -218,7 +218,7 @@ func (r *ReconcileJenkinsBaseConfiguration) validateSecretVolume(volume corev1.V
return messages, nil
}
func (r *ReconcileJenkinsBaseConfiguration) validateReservedVolumes() []string {
func (r *JenkinsBaseConfigurationReconciler) validateReservedVolumes() []string {
var messages []string
for _, baseVolume := range resources.GetJenkinsMasterPodBaseVolumes(r.Configuration.Jenkins) {
@ -232,7 +232,7 @@ func (r *ReconcileJenkinsBaseConfiguration) validateReservedVolumes() []string {
return messages
}
func (r *ReconcileJenkinsBaseConfiguration) validateContainer(container v1alpha2.Container) []string {
func (r *JenkinsBaseConfigurationReconciler) validateContainer(container v1alpha2.Container) []string {
var messages []string
if container.Image == "" {
messages = append(messages, "Image not set")
@ -253,7 +253,7 @@ func (r *ReconcileJenkinsBaseConfiguration) validateContainer(container v1alpha2
return messages
}
func (r *ReconcileJenkinsBaseConfiguration) validateContainerVolumeMounts(container v1alpha2.Container) []string {
func (r *JenkinsBaseConfigurationReconciler) validateContainerVolumeMounts(container v1alpha2.Container) []string {
var messages []string
allVolumes := append(resources.GetJenkinsMasterPodBaseVolumes(r.Configuration.Jenkins), r.Configuration.Jenkins.Spec.Master.Volumes...)
@ -277,7 +277,7 @@ func (r *ReconcileJenkinsBaseConfiguration) validateContainerVolumeMounts(contai
return messages
}
func (r *ReconcileJenkinsBaseConfiguration) validateJenkinsMasterPodEnvs() []string {
func (r *JenkinsBaseConfigurationReconciler) validateJenkinsMasterPodEnvs() []string {
var messages []string
baseEnvs := resources.GetJenkinsMasterContainerBaseEnvs(r.Configuration.Jenkins)
baseEnvNames := map[string]string{}
@ -316,7 +316,7 @@ func (r *ReconcileJenkinsBaseConfiguration) validateJenkinsMasterPodEnvs() []str
return messages
}
func (r *ReconcileJenkinsBaseConfiguration) validatePlugins(requiredBasePlugins []plugins.Plugin, basePlugins, userPlugins []v1alpha2.Plugin) []string {
func (r *JenkinsBaseConfigurationReconciler) validatePlugins(requiredBasePlugins []plugins.Plugin, basePlugins, userPlugins []v1alpha2.Plugin) []string {
var messages []string
allPlugins := map[plugins.Plugin][]plugins.Plugin{}
@ -353,7 +353,7 @@ func (r *ReconcileJenkinsBaseConfiguration) validatePlugins(requiredBasePlugins
return messages
}
func (r *ReconcileJenkinsBaseConfiguration) verifyBasePlugins(requiredBasePlugins []plugins.Plugin, basePlugins []v1alpha2.Plugin) []string {
func (r *JenkinsBaseConfigurationReconciler) verifyBasePlugins(requiredBasePlugins []plugins.Plugin, basePlugins []v1alpha2.Plugin) []string {
var messages []string
for _, requiredBasePlugin := range requiredBasePlugins {
@ -372,7 +372,7 @@ func (r *ReconcileJenkinsBaseConfiguration) verifyBasePlugins(requiredBasePlugin
return messages
}
func (r *ReconcileJenkinsBaseConfiguration) validateCustomization(customization v1alpha2.Customization, name string) ([]string, error) {
func (r *JenkinsBaseConfigurationReconciler) validateCustomization(customization v1alpha2.Customization, name string) ([]string, error) {
var messages []string
if len(customization.Secret.Name) == 0 && len(customization.Configurations) == 0 {
return nil, nil

View File

@ -5,7 +5,7 @@ import (
"fmt"
"testing"
"github.com/jenkinsci/kubernetes-operator/pkg/apis/jenkins/v1alpha2"
"github.com/jenkinsci/kubernetes-operator/api/v1alpha2"
"github.com/jenkinsci/kubernetes-operator/pkg/client"
"github.com/jenkinsci/kubernetes-operator/pkg/configuration"
"github.com/jenkinsci/kubernetes-operator/pkg/configuration/base/resources"
@ -161,7 +161,7 @@ func TestReconcileJenkinsBaseConfiguration_validateImagePullSecrets(t *testing.T
},
}
fakeClient := fake.NewFakeClient()
fakeClient := fake.NewClientBuilder().Build()
err := fakeClient.Create(context.TODO(), secret)
assert.NoError(t, err)
@ -187,7 +187,7 @@ func TestReconcileJenkinsBaseConfiguration_validateImagePullSecrets(t *testing.T
},
}
fakeClient := fake.NewFakeClient()
fakeClient := fake.NewClientBuilder().Build()
baseReconcileLoop := New(configuration.Configuration{
Client: fakeClient,
@ -221,7 +221,7 @@ func TestReconcileJenkinsBaseConfiguration_validateImagePullSecrets(t *testing.T
},
}
fakeClient := fake.NewFakeClient()
fakeClient := fake.NewClientBuilder().Build()
err := fakeClient.Create(context.TODO(), secret)
assert.NoError(t, err)
@ -257,7 +257,7 @@ func TestReconcileJenkinsBaseConfiguration_validateImagePullSecrets(t *testing.T
},
}
fakeClient := fake.NewFakeClient()
fakeClient := fake.NewClientBuilder().Build()
err := fakeClient.Create(context.TODO(), secret)
assert.NoError(t, err)
@ -293,7 +293,7 @@ func TestReconcileJenkinsBaseConfiguration_validateImagePullSecrets(t *testing.T
},
}
fakeClient := fake.NewFakeClient()
fakeClient := fake.NewClientBuilder().Build()
err := fakeClient.Create(context.TODO(), secret)
assert.NoError(t, err)
@ -329,7 +329,7 @@ func TestReconcileJenkinsBaseConfiguration_validateImagePullSecrets(t *testing.T
},
}
fakeClient := fake.NewFakeClient()
fakeClient := fake.NewClientBuilder().Build()
err := fakeClient.Create(context.TODO(), secret)
assert.NoError(t, err)
@ -569,7 +569,7 @@ func TestValidateConfigMapVolume(t *testing.T) {
},
},
}
fakeClient := fake.NewFakeClient()
fakeClient := fake.NewClientBuilder().Build()
baseReconcileLoop := New(configuration.Configuration{
Jenkins: &v1alpha2.Jenkins{ObjectMeta: metav1.ObjectMeta{Name: "example"}},
@ -596,7 +596,7 @@ func TestValidateConfigMapVolume(t *testing.T) {
},
},
}
fakeClient := fake.NewFakeClient()
fakeClient := fake.NewClientBuilder().Build()
err := fakeClient.Create(context.TODO(), &configMap)
assert.NoError(t, err)
baseReconcileLoop := New(configuration.Configuration{
@ -624,7 +624,7 @@ func TestValidateConfigMapVolume(t *testing.T) {
},
},
}
fakeClient := fake.NewFakeClient()
fakeClient := fake.NewClientBuilder().Build()
baseReconcileLoop := New(configuration.Configuration{
Client: fakeClient,
Jenkins: jenkins,
@ -634,7 +634,7 @@ func TestValidateConfigMapVolume(t *testing.T) {
assert.NoError(t, err)
assert.Equal(t, got, []string{"ConfigMap 'configmap-name' not found for volume '{volume-name {nil nil nil nil nil nil nil nil nil nil nil nil nil nil nil nil nil nil &ConfigMapVolumeSource{LocalObjectReference:LocalObjectReference{Name:configmap-name,},Items:[]KeyToPath{},DefaultMode:nil,Optional:*false,} nil nil nil nil nil nil nil nil nil}}'"})
assert.Equal(t, got, []string{"ConfigMap 'configmap-name' not found for volume '{volume-name {nil nil nil nil nil nil nil nil nil nil nil nil nil nil nil nil nil nil &ConfigMapVolumeSource{LocalObjectReference:LocalObjectReference{Name:configmap-name,},Items:[]KeyToPath{},DefaultMode:nil,Optional:*false,} nil nil nil nil nil nil nil nil nil nil}}'"})
})
}
@ -649,7 +649,7 @@ func TestValidateSecretVolume(t *testing.T) {
},
},
}
fakeClient := fake.NewFakeClient()
fakeClient := fake.NewClientBuilder().Build()
baseReconcileLoop := New(configuration.Configuration{
Jenkins: &v1alpha2.Jenkins{ObjectMeta: metav1.ObjectMeta{Name: "example"}},
Client: fakeClient,
@ -673,7 +673,7 @@ func TestValidateSecretVolume(t *testing.T) {
},
},
}
fakeClient := fake.NewFakeClient()
fakeClient := fake.NewClientBuilder().Build()
err := fakeClient.Create(context.TODO(), &secret)
assert.NoError(t, err)
baseReconcileLoop := New(configuration.Configuration{
@ -699,7 +699,7 @@ func TestValidateSecretVolume(t *testing.T) {
},
},
}
fakeClient := fake.NewFakeClient()
fakeClient := fake.NewClientBuilder().Build()
baseReconcileLoop := New(configuration.Configuration{
Client: fakeClient,
Jenkins: jenkins,
@ -708,7 +708,7 @@ func TestValidateSecretVolume(t *testing.T) {
assert.NoError(t, err)
assert.Equal(t, got, []string{"Secret 'secret-name' not found for volume '{volume-name {nil nil nil nil nil &SecretVolumeSource{SecretName:secret-name,Items:[]KeyToPath{},DefaultMode:nil,Optional:*false,} nil nil nil nil nil nil nil nil nil nil nil nil nil nil nil nil nil nil nil nil nil nil}}'"})
assert.Equal(t, got, []string{"Secret 'secret-name' not found for volume '{volume-name {nil nil nil nil nil &SecretVolumeSource{SecretName:secret-name,Items:[]KeyToPath{},DefaultMode:nil,Optional:*false,} nil nil nil nil nil nil nil nil nil nil nil nil nil nil nil nil nil nil nil nil nil nil nil}}'"})
})
}
@ -722,7 +722,7 @@ func TestValidateCustomization(t *testing.T) {
}
t.Run("empty", func(t *testing.T) {
customization := v1alpha2.Customization{}
fakeClient := fake.NewFakeClient()
fakeClient := fake.NewClientBuilder().Build()
baseReconcileLoop := New(configuration.Configuration{
Jenkins: jenkins,
Client: fakeClient,
@ -744,7 +744,7 @@ func TestValidateCustomization(t *testing.T) {
Namespace: defaultNamespace,
},
}
fakeClient := fake.NewFakeClient()
fakeClient := fake.NewClientBuilder().Build()
baseReconcileLoop := New(configuration.Configuration{
Jenkins: jenkins,
Client: fakeClient,
@ -775,7 +775,7 @@ func TestValidateCustomization(t *testing.T) {
Namespace: defaultNamespace,
},
}
fakeClient := fake.NewFakeClient()
fakeClient := fake.NewClientBuilder().Build()
baseReconcileLoop := New(configuration.Configuration{
Jenkins: jenkins,
Client: fakeClient,
@ -802,7 +802,7 @@ func TestValidateCustomization(t *testing.T) {
Namespace: defaultNamespace,
},
}
fakeClient := fake.NewFakeClient()
fakeClient := fake.NewClientBuilder().Build()
baseReconcileLoop := New(configuration.Configuration{
Jenkins: jenkins,
Client: fakeClient,
@ -827,7 +827,7 @@ func TestValidateCustomization(t *testing.T) {
Namespace: defaultNamespace,
},
}
fakeClient := fake.NewFakeClient()
fakeClient := fake.NewClientBuilder().Build()
baseReconcileLoop := New(configuration.Configuration{
Jenkins: jenkins,
Client: fakeClient,

View File

@ -3,10 +3,9 @@ package configuration
import (
"bytes"
"context"
"strings"
"time"
"github.com/jenkinsci/kubernetes-operator/pkg/apis/jenkins/v1alpha2"
"github.com/jenkinsci/kubernetes-operator/api/v1alpha2"
jenkinsclient "github.com/jenkinsci/kubernetes-operator/pkg/client"
"github.com/jenkinsci/kubernetes-operator/pkg/configuration/base/resources"
"github.com/jenkinsci/kubernetes-operator/pkg/notifications/event"
@ -89,7 +88,7 @@ func (c *Configuration) IsJenkinsTerminating(pod corev1.Pod) bool {
// CreateResource is creating kubernetes resource and references it to Jenkins CR
func (c *Configuration) CreateResource(obj metav1.Object) error {
runtimeObj, ok := obj.(runtime.Object)
clientObj, ok := obj.(client.Object)
if !ok {
return stackerr.Errorf("is not a %T a runtime.Object", obj)
}
@ -99,25 +98,25 @@ func (c *Configuration) CreateResource(obj metav1.Object) error {
return stackerr.WithStack(err)
}
return c.Client.Create(context.TODO(), runtimeObj) // don't wrap error
return c.Client.Create(context.TODO(), clientObj) // don't wrap error
}
// UpdateResource is updating kubernetes resource and references it to Jenkins CR.
func (c *Configuration) UpdateResource(obj metav1.Object) error {
runtimeObj, ok := obj.(runtime.Object)
clientObj, ok := obj.(client.Object)
if !ok {
return stackerr.Errorf("is not a %T a runtime.Object", obj)
}
// set Jenkins instance as the owner and controller, don't check error(can be already set)
// set Jenkins instance as the owner and controller, don't check errors(can be already set)
_ = controllerutil.SetControllerReference(c.Jenkins, obj, c.Scheme)
return c.Client.Update(context.TODO(), runtimeObj) // don't wrap error
return c.Client.Update(context.TODO(), clientObj) // don't wrap error
}
// CreateOrUpdateResource is creating or updating kubernetes resource and references it to Jenkins CR.
func (c *Configuration) CreateOrUpdateResource(obj metav1.Object) error {
runtimeObj, ok := obj.(runtime.Object)
clientObj, ok := obj.(client.Object)
if !ok {
return stackerr.Errorf("is not a %T a runtime.Object", obj)
}
@ -125,7 +124,7 @@ func (c *Configuration) CreateOrUpdateResource(obj metav1.Object) error {
// set Jenkins instance as the owner and controller, don't check error(can be already set)
_ = controllerutil.SetControllerReference(c.Jenkins, obj, c.Scheme)
err := c.Client.Create(context.TODO(), runtimeObj)
err := c.Client.Create(context.TODO(), clientObj)
if err != nil && errors.IsAlreadyExists(err) {
return c.UpdateResource(obj)
} else if err != nil && !errors.IsAlreadyExists(err) {
@ -202,7 +201,7 @@ func (c *Configuration) getJenkinsAPIUrl() (string, error) {
return "", err
}
jenkinsURL := c.JenkinsAPIConnectionSettings.BuildJenkinsAPIUrl(service.Name, service.Namespace, service.Spec.Ports[0].Port, service.Spec.Ports[0].NodePort)
if prefix, ok := GetJenkinsOpts(*c.Jenkins)["prefix"]; ok {
if prefix, ok := resources.GetJenkinsOpts(*c.Jenkins)["prefix"]; ok {
jenkinsURL += prefix
}
return jenkinsURL, nil
@ -278,29 +277,3 @@ func (c *Configuration) GetJenkinsClientFromSecret() (jenkinsclient.Jenkins, err
string(credentialsSecret.Data[resources.OperatorCredentialsSecretUserNameKey]),
string(credentialsSecret.Data[resources.OperatorCredentialsSecretTokenKey]))
}
// GetJenkinsOpts gets JENKINS_OPTS env parameter, parses it's values and returns it as a map`
func GetJenkinsOpts(jenkins v1alpha2.Jenkins) map[string]string {
envs := jenkins.Spec.Master.Containers[0].Env
jenkinsOpts := make(map[string]string)
for key, value := range envs {
if value.Name == "JENKINS_OPTS" {
jenkinsOptsEnv := envs[key]
jenkinsOptsWithDashes := jenkinsOptsEnv.Value
if len(jenkinsOptsWithDashes) == 0 {
return nil
}
jenkinsOptsWithEqOperators := strings.Split(jenkinsOptsWithDashes, " ")
for _, vx := range jenkinsOptsWithEqOperators {
opt := strings.Split(vx, "=")
jenkinsOpts[strings.ReplaceAll(opt[0], "--", "")] = opt[1]
}
return jenkinsOpts
}
}
return nil
}

View File

@ -4,7 +4,7 @@ import (
"fmt"
"strings"
"github.com/jenkinsci/kubernetes-operator/pkg/apis/jenkins/v1alpha2"
"github.com/jenkinsci/kubernetes-operator/api/v1alpha2"
jenkinsclient "github.com/jenkinsci/kubernetes-operator/pkg/client"
"github.com/jenkinsci/kubernetes-operator/pkg/configuration/base/resources"
"github.com/jenkinsci/kubernetes-operator/pkg/groovy"
@ -31,7 +31,7 @@ func New(jenkinsClient jenkinsclient.Jenkins, k8sClient k8s.Client, jenkins *v1a
}
// Ensure configures Jenkins with help Configuration as a code plugin
func (c *configurationAsCode) Ensure(jenkins *v1alpha2.Jenkins) (requeue bool, err error) {
func (c *configurationAsCode) Ensure(_ *v1alpha2.Jenkins) (requeue bool, err error) {
requeue, err = c.groovyClient.WaitForSecretSynchronization(resources.ConfigurationAsCodeSecretVolumePath)
if err != nil || requeue {
return requeue, err

View File

@ -3,9 +3,7 @@ package user
import (
"strings"
"github.com/jenkinsci/kubernetes-operator/pkg/apis/jenkins/v1alpha2"
"github.com/go-logr/logr"
"github.com/jenkinsci/kubernetes-operator/api/v1alpha2"
jenkinsclient "github.com/jenkinsci/kubernetes-operator/pkg/client"
"github.com/jenkinsci/kubernetes-operator/pkg/configuration"
"github.com/jenkinsci/kubernetes-operator/pkg/configuration/backuprestore"
@ -14,6 +12,8 @@ import (
"github.com/jenkinsci/kubernetes-operator/pkg/configuration/user/seedjobs"
"github.com/jenkinsci/kubernetes-operator/pkg/groovy"
"github.com/jenkinsci/kubernetes-operator/pkg/log"
"github.com/go-logr/logr"
"sigs.k8s.io/controller-runtime/pkg/reconcile"
)

View File

@ -8,9 +8,8 @@ import (
"reflect"
"text/template"
"github.com/go-logr/logr"
"github.com/jenkinsci/kubernetes-operator/api/v1alpha2"
"github.com/jenkinsci/kubernetes-operator/internal/render"
"github.com/jenkinsci/kubernetes-operator/pkg/apis/jenkins/v1alpha2"
jenkinsclient "github.com/jenkinsci/kubernetes-operator/pkg/client"
"github.com/jenkinsci/kubernetes-operator/pkg/configuration"
"github.com/jenkinsci/kubernetes-operator/pkg/configuration/base/resources"
@ -18,6 +17,8 @@ import (
"github.com/jenkinsci/kubernetes-operator/pkg/groovy"
"github.com/jenkinsci/kubernetes-operator/pkg/log"
"github.com/jenkinsci/kubernetes-operator/pkg/notifications/reason"
"github.com/go-logr/logr"
stackerr "github.com/pkg/errors"
appsv1 "k8s.io/api/apps/v1"
corev1 "k8s.io/api/core/v1"
@ -228,7 +229,7 @@ func (s *seedJobs) EnsureSeedJobs(jenkins *v1alpha2.Jenkins) (done bool, err err
seedJobIDs := s.getAllSeedJobIDs(*jenkins)
if !reflect.DeepEqual(seedJobIDs, jenkins.Status.CreatedSeedJobs) {
jenkins.Status.CreatedSeedJobs = seedJobIDs
return false, stackerr.WithStack(s.Client.Update(context.TODO(), jenkins))
return false, stackerr.WithStack(s.Client.Status().Update(context.TODO(), jenkins))
}
return true, nil
@ -267,8 +268,15 @@ func (s *seedJobs) createJobs(jenkins *v1alpha2.Jenkins) (requeue bool, err erro
}
hash := sha256.New()
hash.Write([]byte(groovyScript))
hash.Write([]byte(credentialValue))
_, err = hash.Write([]byte(groovyScript))
if err != nil {
return true, err
}
_, err = hash.Write([]byte(credentialValue))
if err != nil {
return true, err
}
requeue, err := groovyClient.EnsureSingle(seedJob.ID, fmt.Sprintf("%s.groovy", seedJob.ID), base64.URLEncoding.EncodeToString(hash.Sum(nil)), groovyScript)
if err != nil {
return true, err

View File

@ -4,9 +4,7 @@ import (
"context"
"testing"
"k8s.io/apimachinery/pkg/api/errors"
"github.com/jenkinsci/kubernetes-operator/pkg/apis/jenkins/v1alpha2"
"github.com/jenkinsci/kubernetes-operator/api/v1alpha2"
jenkinsclient "github.com/jenkinsci/kubernetes-operator/pkg/client"
"github.com/jenkinsci/kubernetes-operator/pkg/configuration"
"github.com/jenkinsci/kubernetes-operator/pkg/configuration/base/resources"
@ -16,6 +14,7 @@ import (
"github.com/stretchr/testify/assert"
appsv1 "k8s.io/api/apps/v1"
corev1 "k8s.io/api/core/v1"
"k8s.io/apimachinery/pkg/api/errors"
"k8s.io/apimachinery/pkg/api/resource"
metav1 "k8s.io/apimachinery/pkg/apis/meta/v1"
"k8s.io/apimachinery/pkg/types"
@ -74,7 +73,7 @@ func TestEnsureSeedJobs(t *testing.T) {
defer ctrl.Finish()
jenkinsClient := jenkinsclient.NewMockJenkins(ctrl)
fakeClient := fake.NewFakeClient()
fakeClient := fake.NewClientBuilder().Build()
err := v1alpha2.SchemeBuilder.AddToScheme(scheme.Scheme)
assert.NoError(t, err)
@ -126,7 +125,7 @@ func TestEnsureSeedJobs(t *testing.T) {
jenkins.Spec.SeedJobs = []v1alpha2.SeedJob{}
jenkinsClient := jenkinsclient.NewMockJenkins(ctrl)
fakeClient := fake.NewFakeClient()
fakeClient := fake.NewClientBuilder().Build()
err := v1alpha2.SchemeBuilder.AddToScheme(scheme.Scheme)
assert.NoError(t, err)
@ -174,7 +173,7 @@ func TestCreateAgent(t *testing.T) {
jenkins := jenkinsCustomResource()
jenkinsClient := jenkinsclient.NewMockJenkins(ctrl)
fakeClient := fake.NewFakeClient()
fakeClient := fake.NewClientBuilder().Build()
err := v1alpha2.SchemeBuilder.AddToScheme(scheme.Scheme)
assert.NoError(t, err)

View File

@ -7,7 +7,8 @@ import (
"fmt"
"strings"
"github.com/jenkinsci/kubernetes-operator/pkg/apis/jenkins/v1alpha2"
"github.com/jenkinsci/kubernetes-operator/api/v1alpha2"
stackerr "github.com/pkg/errors"
"github.com/robfig/cron"
v1 "k8s.io/api/core/v1"

View File

@ -4,7 +4,7 @@ import (
"context"
"testing"
"github.com/jenkinsci/kubernetes-operator/pkg/apis/jenkins/v1alpha2"
"github.com/jenkinsci/kubernetes-operator/api/v1alpha2"
"github.com/jenkinsci/kubernetes-operator/pkg/configuration"
"github.com/stretchr/testify/assert"
@ -57,6 +57,10 @@ func TestValidateSeedJobs(t *testing.T) {
Name: "deploy-keys",
Namespace: "default",
}
jenkinsObjectMeta := metav1.ObjectMeta{
Name: "cr",
Namespace: "default",
}
t.Run("Valid with public repository and without private key", func(t *testing.T) {
jenkins := v1alpha2.Jenkins{
Spec: v1alpha2.JenkinsSpec{
@ -73,7 +77,7 @@ func TestValidateSeedJobs(t *testing.T) {
},
}
fakeClient := fake.NewFakeClient()
fakeClient := fake.NewClientBuilder().Build()
config := configuration.Configuration{
Client: fakeClient,
@ -102,7 +106,7 @@ func TestValidateSeedJobs(t *testing.T) {
},
}
fakeClient := fake.NewFakeClient()
fakeClient := fake.NewClientBuilder().Build()
config := configuration.Configuration{
Client: fakeClient,
@ -120,6 +124,7 @@ func TestValidateSeedJobs(t *testing.T) {
})
t.Run("Valid with private key and secret", func(t *testing.T) {
jenkins := v1alpha2.Jenkins{
ObjectMeta: jenkinsObjectMeta,
Spec: v1alpha2.JenkinsSpec{
SeedJobs: []v1alpha2.SeedJob{
{
@ -141,7 +146,7 @@ func TestValidateSeedJobs(t *testing.T) {
PrivateKeySecretKey: []byte(fakePrivateKey),
},
}
fakeClient := fake.NewFakeClient()
fakeClient := fake.NewClientBuilder().Build()
err := fakeClient.Create(context.TODO(), secret)
assert.NoError(t, err)
@ -160,6 +165,7 @@ func TestValidateSeedJobs(t *testing.T) {
})
t.Run("Invalid private key in secret", func(t *testing.T) {
jenkins := v1alpha2.Jenkins{
ObjectMeta: jenkinsObjectMeta,
Spec: v1alpha2.JenkinsSpec{
SeedJobs: []v1alpha2.SeedJob{
{
@ -181,7 +187,7 @@ func TestValidateSeedJobs(t *testing.T) {
PrivateKeySecretKey: []byte(fakeInvalidPrivateKey),
},
}
fakeClient := fake.NewFakeClient()
fakeClient := fake.NewClientBuilder().Build()
err := fakeClient.Create(context.TODO(), secret)
assert.NoError(t, err)
@ -201,6 +207,7 @@ func TestValidateSeedJobs(t *testing.T) {
})
t.Run("Invalid with PrivateKey and empty Secret data", func(t *testing.T) {
jenkins := v1alpha2.Jenkins{
ObjectMeta: jenkinsObjectMeta,
Spec: v1alpha2.JenkinsSpec{
SeedJobs: []v1alpha2.SeedJob{
{
@ -222,7 +229,7 @@ func TestValidateSeedJobs(t *testing.T) {
PrivateKeySecretKey: []byte(""),
},
}
fakeClient := fake.NewFakeClient()
fakeClient := fake.NewClientBuilder().Build()
err := fakeClient.Create(context.TODO(), secret)
assert.NoError(t, err)
@ -256,7 +263,7 @@ func TestValidateSeedJobs(t *testing.T) {
},
}
fakeClient := fake.NewFakeClient()
fakeClient := fake.NewClientBuilder().Build()
config := configuration.Configuration{
Client: fakeClient,
@ -286,7 +293,7 @@ func TestValidateSeedJobs(t *testing.T) {
},
}
fakeClient := fake.NewFakeClient()
fakeClient := fake.NewClientBuilder().Build()
config := configuration.Configuration{
Client: fakeClient,
@ -316,7 +323,7 @@ func TestValidateSeedJobs(t *testing.T) {
},
}
fakeClient := fake.NewFakeClient()
fakeClient := fake.NewClientBuilder().Build()
config := configuration.Configuration{
Client: fakeClient,
@ -346,7 +353,7 @@ func TestValidateSeedJobs(t *testing.T) {
},
}
fakeClient := fake.NewFakeClient()
fakeClient := fake.NewClientBuilder().Build()
config := configuration.Configuration{
Client: fakeClient,
@ -364,6 +371,7 @@ func TestValidateSeedJobs(t *testing.T) {
})
t.Run("Valid with username and password", func(t *testing.T) {
jenkins := v1alpha2.Jenkins{
ObjectMeta: jenkinsObjectMeta,
Spec: v1alpha2.JenkinsSpec{
SeedJobs: []v1alpha2.SeedJob{
{
@ -385,7 +393,7 @@ func TestValidateSeedJobs(t *testing.T) {
PasswordSecretKey: []byte("some-password"),
},
}
fakeClient := fake.NewFakeClient()
fakeClient := fake.NewClientBuilder().Build()
err := fakeClient.Create(context.TODO(), secret)
assert.NoError(t, err)
@ -404,6 +412,7 @@ func TestValidateSeedJobs(t *testing.T) {
})
t.Run("Invalid with empty username", func(t *testing.T) {
jenkins := v1alpha2.Jenkins{
ObjectMeta: jenkinsObjectMeta,
Spec: v1alpha2.JenkinsSpec{
SeedJobs: []v1alpha2.SeedJob{
{
@ -425,7 +434,7 @@ func TestValidateSeedJobs(t *testing.T) {
PasswordSecretKey: []byte("some-password"),
},
}
fakeClient := fake.NewFakeClient()
fakeClient := fake.NewClientBuilder().Build()
err := fakeClient.Create(context.TODO(), secret)
assert.NoError(t, err)
@ -445,6 +454,7 @@ func TestValidateSeedJobs(t *testing.T) {
})
t.Run("Invalid with empty password", func(t *testing.T) {
jenkins := v1alpha2.Jenkins{
ObjectMeta: jenkinsObjectMeta,
Spec: v1alpha2.JenkinsSpec{
SeedJobs: []v1alpha2.SeedJob{
{
@ -466,7 +476,7 @@ func TestValidateSeedJobs(t *testing.T) {
PasswordSecretKey: []byte(""),
},
}
fakeClient := fake.NewFakeClient()
fakeClient := fake.NewClientBuilder().Build()
err := fakeClient.Create(context.TODO(), secret)
assert.NoError(t, err)
@ -486,6 +496,7 @@ func TestValidateSeedJobs(t *testing.T) {
})
t.Run("Invalid without username", func(t *testing.T) {
jenkins := v1alpha2.Jenkins{
ObjectMeta: jenkinsObjectMeta,
Spec: v1alpha2.JenkinsSpec{
SeedJobs: []v1alpha2.SeedJob{
{
@ -506,7 +517,7 @@ func TestValidateSeedJobs(t *testing.T) {
PasswordSecretKey: []byte("some-password"),
},
}
fakeClient := fake.NewFakeClient()
fakeClient := fake.NewClientBuilder().Build()
err := fakeClient.Create(context.TODO(), secret)
assert.NoError(t, err)
@ -526,6 +537,7 @@ func TestValidateSeedJobs(t *testing.T) {
})
t.Run("Invalid without password", func(t *testing.T) {
jenkins := v1alpha2.Jenkins{
ObjectMeta: jenkinsObjectMeta,
Spec: v1alpha2.JenkinsSpec{
SeedJobs: []v1alpha2.SeedJob{
{
@ -546,7 +558,7 @@ func TestValidateSeedJobs(t *testing.T) {
UsernameSecretKey: []byte("some-username"),
},
}
fakeClient := fake.NewFakeClient()
fakeClient := fake.NewClientBuilder().Build()
err := fakeClient.Create(context.TODO(), secret)
assert.NoError(t, err)
@ -581,7 +593,7 @@ func TestValidateSeedJobs(t *testing.T) {
},
}
fakeClient := fake.NewFakeClient()
fakeClient := fake.NewClientBuilder().Build()
config := configuration.Configuration{
Client: fakeClient,
@ -615,7 +627,7 @@ func TestValidateSeedJobs(t *testing.T) {
},
}
fakeClient := fake.NewFakeClient()
fakeClient := fake.NewClientBuilder().Build()
config := configuration.Configuration{
Client: fakeClient,
@ -647,7 +659,7 @@ func TestValidateSeedJobs(t *testing.T) {
},
}
fakeClient := fake.NewFakeClient()
fakeClient := fake.NewClientBuilder().Build()
config := configuration.Configuration{
Client: fakeClient,
@ -685,7 +697,7 @@ func TestValidateSeedJobs(t *testing.T) {
},
}
fakeClient := fake.NewFakeClient()
fakeClient := fake.NewClientBuilder().Build()
config := configuration.Configuration{
Client: fakeClient,
@ -717,7 +729,7 @@ func TestValidateSeedJobs(t *testing.T) {
},
}
fakeClient := fake.NewFakeClient()
fakeClient := fake.NewClientBuilder().Build()
config := configuration.Configuration{
Client: fakeClient,
@ -755,7 +767,7 @@ func TestValidateSeedJobs(t *testing.T) {
},
}
fakeClient := fake.NewFakeClient()
fakeClient := fake.NewClientBuilder().Build()
config := configuration.Configuration{
Client: fakeClient,

View File

@ -1,7 +1,7 @@
package user
import (
"github.com/jenkinsci/kubernetes-operator/pkg/apis/jenkins/v1alpha2"
"github.com/jenkinsci/kubernetes-operator/api/v1alpha2"
"github.com/jenkinsci/kubernetes-operator/pkg/configuration/backuprestore"
"github.com/jenkinsci/kubernetes-operator/pkg/configuration/user/seedjobs"
)

View File

@ -1,52 +0,0 @@
package jenkins
import (
"github.com/jenkinsci/kubernetes-operator/pkg/apis/jenkins/v1alpha2"
jenkinsclient "github.com/jenkinsci/kubernetes-operator/pkg/client"
"github.com/jenkinsci/kubernetes-operator/pkg/configuration"
"github.com/jenkinsci/kubernetes-operator/pkg/notifications/event"
"k8s.io/apimachinery/pkg/runtime"
"k8s.io/client-go/kubernetes"
"k8s.io/client-go/rest"
"sigs.k8s.io/controller-runtime/pkg/client"
"sigs.k8s.io/controller-runtime/pkg/manager"
"sigs.k8s.io/controller-runtime/pkg/reconcile"
)
// ReconcileJenkins reconciles a Jenkins object.
type ReconcileJenkins struct {
client client.Client
scheme *runtime.Scheme
jenkinsAPIConnectionSettings jenkinsclient.JenkinsAPIConnectionSettings
clientSet kubernetes.Clientset
config rest.Config
notificationEvents *chan event.Event
KubernetesClusterDomain string
}
func (r *ReconcileJenkins) newReconcilierConfiguration(jenkins *v1alpha2.Jenkins) configuration.Configuration {
config := configuration.Configuration{
Client: r.client,
ClientSet: r.clientSet,
Notifications: r.notificationEvents,
Jenkins: jenkins,
Scheme: r.scheme,
Config: &r.config,
JenkinsAPIConnectionSettings: r.jenkinsAPIConnectionSettings,
KubernetesClusterDomain: r.KubernetesClusterDomain,
}
return config
}
// newReconciler returns a newReconcilierConfiguration reconcile.Reconciler.
func newReconciler(mgr manager.Manager, jenkinsAPIConnectionSettings jenkinsclient.JenkinsAPIConnectionSettings, kubernetesClusterDomain string, clientSet kubernetes.Clientset, config rest.Config, notificationEvents *chan event.Event) reconcile.Reconciler {
return &ReconcileJenkins{
client: mgr.GetClient(),
scheme: mgr.GetScheme(),
jenkinsAPIConnectionSettings: jenkinsAPIConnectionSettings,
clientSet: clientSet,
config: config,
notificationEvents: notificationEvents,
KubernetesClusterDomain: kubernetesClusterDomain,
}
}

View File

@ -1,150 +0,0 @@
package jenkinsimage
import (
"context"
jenkinsv1alpha2 "github.com/jenkinsci/kubernetes-operator/pkg/apis/jenkins/v1alpha2"
"github.com/jenkinsci/kubernetes-operator/pkg/configuration/base/resources"
"github.com/pkg/errors"
corev1 "k8s.io/api/core/v1"
apierrors "k8s.io/apimachinery/pkg/api/errors"
"k8s.io/apimachinery/pkg/runtime"
"k8s.io/apimachinery/pkg/types"
"sigs.k8s.io/controller-runtime/pkg/client"
"sigs.k8s.io/controller-runtime/pkg/controller"
"sigs.k8s.io/controller-runtime/pkg/controller/controllerutil"
"sigs.k8s.io/controller-runtime/pkg/handler"
logf "sigs.k8s.io/controller-runtime/pkg/log"
"sigs.k8s.io/controller-runtime/pkg/manager"
"sigs.k8s.io/controller-runtime/pkg/reconcile"
"sigs.k8s.io/controller-runtime/pkg/source"
)
var log = logf.Log.WithName("controller_jenkinsimage")
// Add creates a new JenkinsImage Controller and adds it to the Manager. The Manager will set fields on the Controller
// and Start it when the Manager is Started.
func Add(mgr manager.Manager) error {
r := &ReconcileJenkinsImage{client: mgr.GetClient(), scheme: mgr.GetScheme()}
return add(mgr, r)
}
// add adds a new Controller to mgr with r as the reconcile.Reconciler
func add(mgr manager.Manager, r reconcile.Reconciler) error {
// Create a new controller
c, err := controller.New("jenkinsimage-controller", mgr, controller.Options{Reconciler: r})
if err != nil {
return err
}
// Watch for changes to primary resource JenkinsImage
eventHandlerForObject := &handler.EnqueueRequestForObject{}
src := &source.Kind{Type: &jenkinsv1alpha2.JenkinsImage{}}
err = c.Watch(src, eventHandlerForObject)
if err != nil {
return errors.WithStack(err)
}
// Watch for changes to secondary resource Pods and requeue the owner JenkinsImage
eventHandlerForOwner := &handler.EnqueueRequestForOwner{
IsController: true,
OwnerType: &jenkinsv1alpha2.JenkinsImage{},
}
err = c.Watch(&source.Kind{Type: &corev1.Pod{}}, eventHandlerForOwner)
if err != nil {
return errors.WithStack(err)
}
// Watch for changes to secondary ConfigMap and requeue the owner JenkinsImage
err = c.Watch(&source.Kind{Type: &corev1.ConfigMap{}}, eventHandlerForOwner)
if err != nil {
return errors.WithStack(err)
}
return nil
}
// blank assignment to verify that ReconcileJenkinsImage implements reconcile.Reconciler
var _ reconcile.Reconciler = &ReconcileJenkinsImage{}
// ReconcileJenkinsImage reconciles a JenkinsImage object
type ReconcileJenkinsImage struct {
// This client, initialized using mgr.Client() above, is a split client
// that reads objects from the cache and writes to the apiserver
client client.Client
scheme *runtime.Scheme
}
// Reconcile reads that state of the cluster for a JenkinsImage object and makes changes based on the state read
// and what is in the JenkinsImage.Spec
// The Controller will requeue the Request to be processed again if the returned error is non-nil or
// Result.Requeue is true, otherwise upon completion it will remove the work from the queue.
func (r *ReconcileJenkinsImage) Reconcile(request reconcile.Request) (reconcile.Result, error) {
reqLogger := log.WithValues("Request.Namespace", request.Namespace, "Request.Name", request.Name)
reqLogger.Info("Reconciling JenkinsImage")
// Fetch the JenkinsImage instance
instance := &jenkinsv1alpha2.JenkinsImage{}
err := r.client.Get(context.TODO(), request.NamespacedName, instance)
if err != nil {
if apierrors.IsNotFound(err) {
// Request object not found, could have been deleted after reconcile request.
// Owned objects are automatically garbage collected. For additional cleanup logic use finalizers.
// Return and don't requeue
return reconcile.Result{}, nil
}
// Error reading the object - requeue the request.
return reconcile.Result{}, err
}
// Define a new ConfigMap containing the Dockerfile used to build the image
dockerfile := resources.NewDockerfileConfigMap(instance)
// Set JenkinsImage instance as the owner and controller
if err := controllerutil.SetControllerReference(instance, dockerfile, r.scheme); err != nil {
return reconcile.Result{}, err
}
// Check if this ConfigMap already exists
foundConfigMap := &corev1.ConfigMap{}
err = r.client.Get(context.TODO(), types.NamespacedName{Name: dockerfile.Name, Namespace: dockerfile.Namespace}, foundConfigMap)
if err != nil && apierrors.IsNotFound(err) {
reqLogger.Info("Creating a new ConfigMap", "ConfigMap.Namespace", dockerfile.Namespace, "ConfigMap.Name", dockerfile.Name)
err = r.client.Create(context.TODO(), dockerfile)
if err != nil {
return reconcile.Result{}, err
}
// ConfigMap created successfully - don't requeue
return reconcile.Result{}, nil
} else if err != nil {
return reconcile.Result{}, err
}
// ConfigMap already exists - don't requeue
reqLogger.Info("Skip reconcile: ConfigMap already exists", "ConfigMap.Namespace", foundConfigMap.Namespace, "ConfigMap.Name", foundConfigMap.Name)
// Define a new Pod object
pod := resources.NewBuilderPod(instance)
// Set JenkinsImage instance as the owner and controller
if err := controllerutil.SetControllerReference(instance, pod, r.scheme); err != nil {
return reconcile.Result{}, err
}
// Check if this Pod already exists
foundPod := &corev1.Pod{}
err = r.client.Get(context.TODO(), types.NamespacedName{Name: pod.Name, Namespace: pod.Namespace}, foundPod)
if err != nil && apierrors.IsNotFound(err) {
reqLogger.Info("Creating a new Pod", "Pod.Namespace", pod.Namespace, "Pod.Name", pod.Name)
err = r.client.Create(context.TODO(), pod)
if err != nil {
return reconcile.Result{}, err
}
// Pod created successfully - don't requeue
return reconcile.Result{}, nil
} else if err != nil {
return reconcile.Result{}, err
}
// Pod already exists - don't requeue
reqLogger.Info("Skip reconcile: Pod already exists", "Pod.Namespace", foundPod.Namespace, "Pod.Name", foundPod.Name)
return reconcile.Result{}, nil
}

View File

@ -54,7 +54,6 @@ func initializeEventRecorder(config *rest.Config, component string) (record.Even
return nil, errors.WithStack(err)
}
eventBroadcaster := record.NewBroadcaster()
//eventBroadcaster.StartLogging(glog.Infof) TODO integrate with proper logger
eventBroadcaster.StartRecordingToSink(
&typedcorev1.EventSinkImpl{
Interface: client.CoreV1().Events("")})

View File

@ -8,7 +8,7 @@ import (
"sort"
"strings"
"github.com/jenkinsci/kubernetes-operator/pkg/apis/jenkins/v1alpha2"
"github.com/jenkinsci/kubernetes-operator/api/v1alpha2"
jenkinsclient "github.com/jenkinsci/kubernetes-operator/pkg/client"
"github.com/jenkinsci/kubernetes-operator/pkg/log"
@ -77,7 +77,7 @@ func (g *Groovy) EnsureSingle(source, name, hash, groovyScript string) (requeue
g.jenkins.Status.AppliedGroovyScripts = appliedGroovyScripts
return true, g.k8sClient.Update(context.TODO(), g.jenkins)
return true, g.k8sClient.Status().Update(context.TODO(), g.jenkins)
}
// WaitForSecretSynchronization runs groovy script which waits to synchronize secrets in pod by k8s
@ -96,7 +96,10 @@ func (g *Groovy) WaitForSecretSynchronization(secretsPath string) (requeue bool,
for secretKey, secretValue := range secret.Data {
toCalculate[secretKey] = string(secretValue)
}
hash := g.calculateHash(toCalculate)
hash, err := g.calculateHash(toCalculate)
if err != nil {
return true, errors.WithStack(err)
}
name := "synchronizing-secret.groovy"
if g.isGroovyScriptAlreadyApplied(g.customization.Secret.Name, name, hash) {
@ -137,7 +140,10 @@ func (g *Groovy) Ensure(filter func(name string) bool, updateGroovyScript func(g
continue
}
hash := g.calculateCustomizationHash(*secret, name, groovyScript)
hash, err := g.calculateCustomizationHash(*secret, name, groovyScript)
if err != nil {
return true, errors.WithStack(err)
}
if g.isGroovyScriptAlreadyApplied(configMap.Name, name, hash) {
continue
}
@ -153,7 +159,7 @@ func (g *Groovy) Ensure(filter func(name string) bool, updateGroovyScript func(g
return false, nil
}
func (g *Groovy) calculateCustomizationHash(secret corev1.Secret, key, groovyScript string) string {
func (g *Groovy) calculateCustomizationHash(secret corev1.Secret, key, groovyScript string) (string, error) {
toCalculate := map[string]string{}
for secretKey, secretValue := range secret.Data {
toCalculate[secretKey] = string(secretValue)
@ -173,7 +179,7 @@ func (g *Groovy) isGroovyScriptAlreadyApplied(source, name, hash string) bool {
return false
}
func (g *Groovy) calculateHash(data map[string]string) string {
func (g *Groovy) calculateHash(data map[string]string) (string, error) {
hash := sha256.New()
var keys []string
@ -182,10 +188,16 @@ func (g *Groovy) calculateHash(data map[string]string) string {
}
sort.Strings(keys)
for _, key := range keys {
hash.Write([]byte(key))
hash.Write([]byte(data[key]))
_, err := hash.Write([]byte(key))
if err != nil {
return "", err
}
_, err = hash.Write([]byte(data[key]))
if err != nil {
return "", err
}
}
return base64.StdEncoding.EncodeToString(hash.Sum(nil))
return base64.StdEncoding.EncodeToString(hash.Sum(nil)), nil
}
// AddSecretsLoaderToGroovyScript modify groovy scripts to load Kubernetes secrets into groovy map

View File

@ -6,11 +6,11 @@ import (
"strings"
"testing"
"github.com/jenkinsci/kubernetes-operator/pkg/apis/jenkins/v1alpha2"
"github.com/jenkinsci/kubernetes-operator/api/v1alpha2"
jenkinsclient "github.com/jenkinsci/kubernetes-operator/pkg/client"
"github.com/jenkinsci/kubernetes-operator/pkg/log"
"github.com/golang/mock/gomock"
"github.com/jenkinsci/kubernetes-operator/pkg/log"
"github.com/stretchr/testify/assert"
"github.com/stretchr/testify/require"
corev1 "k8s.io/api/core/v1"
@ -43,7 +43,7 @@ func TestGroovy_EnsureSingle(t *testing.T) {
}
err := v1alpha2.SchemeBuilder.AddToScheme(scheme.Scheme)
require.NoError(t, err)
fakeClient := fake.NewFakeClient()
fakeClient := fake.NewClientBuilder().Build()
err = fakeClient.Create(ctx, jenkins)
require.NoError(t, err)
@ -89,7 +89,7 @@ func TestGroovy_EnsureSingle(t *testing.T) {
}
err := v1alpha2.SchemeBuilder.AddToScheme(scheme.Scheme)
require.NoError(t, err)
fakeClient := fake.NewFakeClient()
fakeClient := fake.NewClientBuilder().Build()
err = fakeClient.Create(ctx, jenkins)
require.NoError(t, err)
@ -125,7 +125,7 @@ func TestGroovy_EnsureSingle(t *testing.T) {
}
err := v1alpha2.SchemeBuilder.AddToScheme(scheme.Scheme)
require.NoError(t, err)
fakeClient := fake.NewFakeClient()
fakeClient := fake.NewClientBuilder().Build()
err = fakeClient.Create(ctx, jenkins)
require.NoError(t, err)
@ -197,7 +197,7 @@ func TestGroovy_EnsureSingle(t *testing.T) {
}
err := v1alpha2.SchemeBuilder.AddToScheme(scheme.Scheme)
require.NoError(t, err)
fakeClient := fake.NewFakeClient()
fakeClient := fake.NewClientBuilder().Build()
err = fakeClient.Create(ctx, jenkins)
require.NoError(t, err)
@ -257,7 +257,7 @@ func TestGroovy_EnsureSingle(t *testing.T) {
}
err := v1alpha2.SchemeBuilder.AddToScheme(scheme.Scheme)
require.NoError(t, err)
fakeClient := fake.NewFakeClient()
fakeClient := fake.NewClientBuilder().Build()
err = fakeClient.Create(ctx, jenkins)
require.NoError(t, err)
@ -289,7 +289,7 @@ func TestGroovy_EnsureSingle(t *testing.T) {
}
err := v1alpha2.SchemeBuilder.AddToScheme(scheme.Scheme)
require.NoError(t, err)
fakeClient := fake.NewFakeClient()
fakeClient := fake.NewClientBuilder().Build()
err = fakeClient.Create(ctx, jenkins)
require.NoError(t, err)
@ -323,7 +323,7 @@ func TestGroovy_EnsureSingle(t *testing.T) {
}
err := v1alpha2.SchemeBuilder.AddToScheme(scheme.Scheme)
require.NoError(t, err)
fakeClient := fake.NewFakeClient()
fakeClient := fake.NewClientBuilder().Build()
err = fakeClient.Create(ctx, jenkins)
require.NoError(t, err)
@ -392,7 +392,7 @@ func TestGroovy_Ensure(t *testing.T) {
}
err := v1alpha2.SchemeBuilder.AddToScheme(scheme.Scheme)
require.NoError(t, err)
fakeClient := fake.NewFakeClient()
fakeClient := fake.NewClientBuilder().Build()
err = fakeClient.Create(ctx, jenkins)
require.NoError(t, err)
err = fakeClient.Create(ctx, configMap)
@ -452,7 +452,7 @@ func TestGroovy_Ensure(t *testing.T) {
}
err := v1alpha2.SchemeBuilder.AddToScheme(scheme.Scheme)
require.NoError(t, err)
fakeClient := fake.NewFakeClient()
fakeClient := fake.NewClientBuilder().Build()
err = fakeClient.Create(ctx, jenkins)
require.NoError(t, err)
err = fakeClient.Create(ctx, configMap)
@ -511,7 +511,7 @@ func TestGroovy_Ensure(t *testing.T) {
}
err := v1alpha2.SchemeBuilder.AddToScheme(scheme.Scheme)
require.NoError(t, err)
fakeClient := fake.NewFakeClient()
fakeClient := fake.NewClientBuilder().Build()
err = fakeClient.Create(ctx, jenkins)
require.NoError(t, err)
err = fakeClient.Create(ctx, configMap)
@ -577,7 +577,7 @@ func TestGroovy_Ensure(t *testing.T) {
}
err := v1alpha2.SchemeBuilder.AddToScheme(scheme.Scheme)
require.NoError(t, err)
fakeClient := fake.NewFakeClient()
fakeClient := fake.NewClientBuilder().Build()
err = fakeClient.Create(ctx, jenkins)
require.NoError(t, err)
err = fakeClient.Create(ctx, secret)

View File

@ -1,7 +1,7 @@
package event
import (
"github.com/jenkinsci/kubernetes-operator/pkg/apis/jenkins/v1alpha2"
"github.com/jenkinsci/kubernetes-operator/api/v1alpha2"
"github.com/jenkinsci/kubernetes-operator/pkg/notifications/reason"
)
@ -28,7 +28,4 @@ const (
// PhaseUser is user-defined configuration of Jenkins
PhaseUser Phase = "user"
// PhaseUnknown is untraceable type of configuration
PhaseUnknown Phase = "unknown"
)

View File

@ -6,7 +6,7 @@ import (
"strings"
"time"
"github.com/jenkinsci/kubernetes-operator/pkg/apis/jenkins/v1alpha2"
"github.com/jenkinsci/kubernetes-operator/api/v1alpha2"
"github.com/jenkinsci/kubernetes-operator/pkg/notifications/event"
"github.com/jenkinsci/kubernetes-operator/pkg/notifications/provider"

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