diff --git a/.dockerignore b/.dockerignore
new file mode 100644
index 00000000..243f81a5
--- /dev/null
+++ b/.dockerignore
@@ -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
diff --git a/.github/workflows/auto-tests.yaml b/.github/workflows/auto-tests.yaml
index 359fcb2d..6231e0d4 100644
--- a/.github/workflows/auto-tests.yaml
+++ b/.github/workflows/auto-tests.yaml
@@ -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.*$'
\ No newline at end of file
+#TODO Helm e2e test
\ No newline at end of file
diff --git a/.gitignore b/.gitignore
index 5a3cd2b3..ff5b041f 100644
--- a/.gitignore
+++ b/.gitignore
@@ -88,3 +88,6 @@ tags
### IntelliJ IDEA ###
*.iml
+
+bin
+testbin/*
\ No newline at end of file
diff --git a/Dockerfile b/Dockerfile
new file mode 100644
index 00000000..ff840ea4
--- /dev/null
+++ b/Dockerfile
@@ -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"]
diff --git a/Makefile b/Makefile
index 0fcfdcb9..823aa480 100644
--- a/Makefile
+++ b/Makefile
@@ -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);
\ No newline at end of file
diff --git a/PROJECT b/PROJECT
new file mode 100644
index 00000000..7ae7f502
--- /dev/null
+++ b/PROJECT
@@ -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: {}
diff --git a/pkg/apis/jenkins/v1alpha2/doc.go b/api/v1alpha2/doc.go
similarity index 82%
rename from pkg/apis/jenkins/v1alpha2/doc.go
rename to api/v1alpha2/doc.go
index 5f149aee..62442c14 100644
--- a/pkg/apis/jenkins/v1alpha2/doc.go
+++ b/api/v1alpha2/doc.go
@@ -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
diff --git a/pkg/apis/jenkins/v1alpha2/jenkins_types.go b/api/v1alpha2/jenkins_types.go
similarity index 97%
rename from pkg/apis/jenkins/v1alpha2/jenkins_types.go
rename to api/v1alpha2/jenkins_types.go
index 61b377c9..0895cd96 100644
--- a/pkg/apis/jenkins/v1alpha2/jenkins_types.go
+++ b/api/v1alpha2/jenkins_types.go
@@ -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"`
diff --git a/pkg/apis/jenkins/v1alpha2/register.go b/api/v1alpha2/register.go
similarity index 80%
rename from pkg/apis/jenkins/v1alpha2/register.go
rename to api/v1alpha2/register.go
index 57aa81e4..cecb9df5 100644
--- a/pkg/apis/jenkins/v1alpha2/register.go
+++ b/api/v1alpha2/register.go
@@ -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{})
}
diff --git a/pkg/apis/jenkins/v1alpha2/zz_generated.deepcopy.go b/api/v1alpha2/zz_generated.deepcopy.go
similarity index 79%
rename from pkg/apis/jenkins/v1alpha2/zz_generated.deepcopy.go
rename to api/v1alpha2/zz_generated.deepcopy.go
index f390614c..566f8d1d 100644
--- a/pkg/apis/jenkins/v1alpha2/zz_generated.deepcopy.go
+++ b/api/v1alpha2/zz_generated.deepcopy.go
@@ -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.
diff --git a/build/Dockerfile b/build/Dockerfile
deleted file mode 100644
index 1615bd64..00000000
--- a/build/Dockerfile
+++ /dev/null
@@ -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" ]
diff --git a/cmd/manager/main.go b/cmd/manager/main.go
deleted file mode 100644
index 165aed93..00000000
--- a/cmd/manager/main.go
+++ /dev/null
@@ -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)
-}
diff --git a/config.base.env b/config.base.env
index dda8893c..21c422f7 100644
--- a/config.base.env
+++ b/config.base.env
@@ -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
diff --git a/config.minikube.env b/config.minikube.env
index 309181c9..fcb35b14 100644
--- a/config.minikube.env
+++ b/config.minikube.env
@@ -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
diff --git a/config/all_in_one_v1alpha2.yaml b/config/all_in_one_v1alpha2.yaml
new file mode 100644
index 00000000..a8f2ef4e
--- /dev/null
+++ b/config/all_in_one_v1alpha2.yaml
@@ -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
diff --git a/config/certmanager/certificate.yaml b/config/certmanager/certificate.yaml
new file mode 100644
index 00000000..12c86a38
--- /dev/null
+++ b/config/certmanager/certificate.yaml
@@ -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
diff --git a/config/certmanager/kustomization.yaml b/config/certmanager/kustomization.yaml
new file mode 100644
index 00000000..bebea5a5
--- /dev/null
+++ b/config/certmanager/kustomization.yaml
@@ -0,0 +1,5 @@
+resources:
+- certificate.yaml
+
+configurations:
+- kustomizeconfig.yaml
diff --git a/config/certmanager/kustomizeconfig.yaml b/config/certmanager/kustomizeconfig.yaml
new file mode 100644
index 00000000..90d7c313
--- /dev/null
+++ b/config/certmanager/kustomizeconfig.yaml
@@ -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
diff --git a/config/crd/bases/jenkins.io_jenkins.yaml b/config/crd/bases/jenkins.io_jenkins.yaml
new file mode 100644
index 00000000..0ecdbf00
--- /dev/null
+++ b/config/crd/bases/jenkins.io_jenkins.yaml
@@ -0,0 +1,3412 @@
+
+---
+apiVersion: apiextensions.k8s.io/v1
+kind: CustomResourceDefinition
+metadata:
+ annotations:
+ controller-gen.kubebuilder.io/version: v0.4.1
+ creationTimestamp: null
+ name: jenkins.jenkins.io
+spec:
+ group: jenkins.io
+ names:
+ kind: Jenkins
+ listKind: JenkinsList
+ plural: jenkins
+ singular: jenkins
+ scope: Namespaced
+ versions:
+ - name: v1alpha2
+ schema:
+ openAPIV3Schema:
+ description: Jenkins is the Schema for the jenkins API
+ properties:
+ apiVersion:
+ 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
+ kind:
+ 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
+ metadata:
+ type: object
+ spec:
+ description: Spec defines the desired state of the Jenkins
+ properties:
+ backup:
+ description: 'Backup defines configuration of Jenkins backup More
+ info: https://jenkinsci.github.io/kubernetes-operator/docs/getting-started/latest/configure-backup-and-restore/'
+ properties:
+ action:
+ description: Action defines action which performs backup in backup
+ container sidecar
+ properties:
+ exec:
+ description: Exec specifies the action to take.
+ properties:
+ command:
+ description: Command is the command line to execute inside
+ the container, the working directory for the command is
+ root ('/') in the container's filesystem. The command
+ is simply exec'd, it is not run inside a shell, so traditional
+ shell instructions ('|', etc) won't work. To use a shell,
+ you need to explicitly call out to that shell. Exit
+ status of 0 is treated as live/healthy and non-zero
+ is unhealthy.
+ items:
+ type: string
+ type: array
+ type: object
+ type: object
+ containerName:
+ description: ContainerName is the container name responsible for
+ backup operation
+ type: string
+ interval:
+ description: Interval tells how often make backup in seconds Defaults
+ to 30.
+ format: int64
+ type: integer
+ makeBackupBeforePodDeletion:
+ description: MakeBackupBeforePodDeletion tells operator to make
+ backup before Jenkins master pod deletion
+ type: boolean
+ required:
+ - action
+ - containerName
+ - interval
+ - makeBackupBeforePodDeletion
+ type: object
+ configurationAsCode:
+ description: ConfigurationAsCode defines configuration of Jenkins
+ customization via Configuration as Code Jenkins plugin
+ properties:
+ configurations:
+ items:
+ description: ConfigMapRef is reference to Kubernetes ConfigMap.
+ properties:
+ name:
+ type: string
+ required:
+ - name
+ type: object
+ type: array
+ secret:
+ description: SecretRef is reference to Kubernetes secret.
+ properties:
+ name:
+ type: string
+ required:
+ - name
+ type: object
+ required:
+ - configurations
+ - secret
+ type: object
+ groovyScripts:
+ description: GroovyScripts defines configuration of Jenkins customization
+ via groovy scripts
+ properties:
+ configurations:
+ items:
+ description: ConfigMapRef is reference to Kubernetes ConfigMap.
+ properties:
+ name:
+ type: string
+ required:
+ - name
+ type: object
+ type: array
+ secret:
+ description: SecretRef is reference to Kubernetes secret.
+ properties:
+ name:
+ type: string
+ required:
+ - name
+ type: object
+ required:
+ - configurations
+ - secret
+ type: object
+ jenkinsAPISettings:
+ description: JenkinsAPISettings defines configuration used by the
+ operator to gain admin access to the Jenkins API
+ properties:
+ authorizationStrategy:
+ description: AuthorizationStrategy defines authorization strategy
+ of the operator for the Jenkins API
+ type: string
+ required:
+ - authorizationStrategy
+ type: object
+ master:
+ description: Master represents Jenkins master pod properties and Jenkins
+ plugins. Every single change here requires a pod restart.
+ properties:
+ annotations:
+ additionalProperties:
+ type: string
+ description: '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'
+ type: object
+ basePlugins:
+ description: 'BasePlugins contains plugins required by operator
+ Defaults to : - name: kubernetes version: "1.28.6" - name: workflow-job
+ version: "2.40" - name: workflow-aggregator version: "2.6" -
+ name: git version: "4.5.0" - name: job-dsl version: "1.77" -
+ name: configuration-as-code version: "1.46" - name: kubernetes-credentials-provider
+ version: "0.15"'
+ items:
+ description: Plugin defines Jenkins plugin.
+ properties:
+ downloadURL:
+ description: DownloadURL is the custom url from where plugin
+ has to be downloaded.
+ type: string
+ name:
+ description: Name is the name of Jenkins plugin
+ type: string
+ version:
+ description: Version is the version of Jenkins plugin
+ type: string
+ required:
+ - name
+ - version
+ type: object
+ type: array
+ containers:
+ description: 'List of containers belonging to the pod. Containers
+ cannot currently be added or removed. There must be at least
+ one container in a Pod. Defaults to: - image: jenkins/jenkins:lts imagePullPolicy:
+ Always livenessProbe: failureThreshold: 12 httpGet: path:
+ /login port: http scheme: HTTP initialDelaySeconds:
+ 80 periodSeconds: 10 successThreshold: 1 timeoutSeconds:
+ 5 name: jenkins-master readinessProbe: failureThreshold:
+ 3 httpGet: path: /login port: http scheme:
+ HTTP initialDelaySeconds: 30 periodSeconds: 10 successThreshold:
+ 1 timeoutSeconds: 1 resources: limits: cpu:
+ 1500m memory: 3Gi requests: cpu: "1" memory:
+ 600Mi'
+ items:
+ description: Container defines Kubernetes container attributes.
+ properties:
+ args:
+ description: 'Arguments to the entrypoint. The docker image''s
+ CMD is used if this is not provided. Variable references
+ $(VAR_NAME) are expanded using the container''s environment.
+ If a variable cannot be resolved, the reference in the
+ input string will be unchanged. The $(VAR_NAME) syntax
+ can be escaped with a double $$, ie: $$(VAR_NAME). Escaped
+ references will never be expanded, regardless of whether
+ the variable exists or not. More info: https://kubernetes.io/docs/tasks/inject-data-application/define-command-argument-container/#running-a-command-in-a-shell'
+ items:
+ type: string
+ type: array
+ command:
+ description: 'Entrypoint array. Not executed within a shell.
+ The docker image''s ENTRYPOINT is used if this is not
+ provided. Variable references $(VAR_NAME) are expanded
+ using the container''s environment. If a variable cannot
+ be resolved, the reference in the input string will be
+ unchanged. The $(VAR_NAME) syntax can be escaped with
+ a double $$, ie: $$(VAR_NAME). Escaped references will
+ never be expanded, regardless of whether the variable
+ exists or not. More info: https://kubernetes.io/docs/tasks/inject-data-application/define-command-argument-container/#running-a-command-in-a-shell'
+ items:
+ type: string
+ type: array
+ env:
+ description: List of environment variables to set in the
+ container.
+ items:
+ description: EnvVar represents an environment variable
+ present in a Container.
+ properties:
+ name:
+ description: Name of the environment variable. Must
+ be a C_IDENTIFIER.
+ type: string
+ value:
+ description: 'Variable references $(VAR_NAME) are
+ expanded using the previous defined environment
+ variables in the container and any service environment
+ variables. If a variable cannot be resolved, the
+ reference in the input string will be unchanged.
+ The $(VAR_NAME) syntax can be escaped with a double
+ $$, ie: $$(VAR_NAME). Escaped references will never
+ be expanded, regardless of whether the variable
+ exists or not. Defaults to "".'
+ type: string
+ valueFrom:
+ description: Source for the environment variable's
+ value. Cannot be used if value is not empty.
+ properties:
+ configMapKeyRef:
+ description: Selects a key of a ConfigMap.
+ properties:
+ key:
+ description: The key to select.
+ type: string
+ name:
+ description: 'Name of the referent. More info:
+ https://kubernetes.io/docs/concepts/overview/working-with-objects/names/#names
+ TODO: Add other useful fields. apiVersion,
+ kind, uid?'
+ type: string
+ optional:
+ description: Specify whether the ConfigMap
+ or its key must be defined
+ type: boolean
+ required:
+ - key
+ type: object
+ fieldRef:
+ description: 'Selects a field of the pod: supports
+ metadata.name, metadata.namespace, `metadata.labels['''']`,
+ `metadata.annotations['''']`, spec.nodeName,
+ spec.serviceAccountName, status.hostIP, status.podIP,
+ status.podIPs.'
+ properties:
+ apiVersion:
+ description: Version of the schema the FieldPath
+ is written in terms of, defaults to "v1".
+ type: string
+ fieldPath:
+ description: Path of the field to select in
+ the specified API version.
+ type: string
+ required:
+ - fieldPath
+ type: object
+ resourceFieldRef:
+ description: 'Selects a resource of the container:
+ only resources limits and requests (limits.cpu,
+ limits.memory, limits.ephemeral-storage, requests.cpu,
+ requests.memory and requests.ephemeral-storage)
+ are currently supported.'
+ properties:
+ containerName:
+ description: 'Container name: required for
+ volumes, optional for env vars'
+ type: string
+ divisor:
+ anyOf:
+ - type: integer
+ - type: string
+ description: Specifies the output format of
+ the exposed resources, defaults to "1"
+ pattern: ^(\+|-)?(([0-9]+(\.[0-9]*)?)|(\.[0-9]+))(([KMGTPE]i)|[numkMGTPE]|([eE](\+|-)?(([0-9]+(\.[0-9]*)?)|(\.[0-9]+))))?$
+ x-kubernetes-int-or-string: true
+ resource:
+ description: 'Required: resource to select'
+ type: string
+ required:
+ - resource
+ type: object
+ secretKeyRef:
+ description: Selects a key of a secret in the
+ pod's namespace
+ properties:
+ key:
+ description: The key of the secret to select
+ from. Must be a valid secret key.
+ type: string
+ name:
+ description: 'Name of the referent. More info:
+ https://kubernetes.io/docs/concepts/overview/working-with-objects/names/#names
+ TODO: Add other useful fields. apiVersion,
+ kind, uid?'
+ type: string
+ optional:
+ description: Specify whether the Secret or
+ its key must be defined
+ type: boolean
+ required:
+ - key
+ type: object
+ type: object
+ required:
+ - name
+ type: object
+ type: array
+ envFrom:
+ description: List of sources to populate environment variables
+ in the container. The keys defined within a source must
+ be a C_IDENTIFIER. All invalid keys will be reported as
+ an event when the container is starting. When a key exists
+ in multiple sources, the value associated with the last
+ source will take precedence. Values defined by an Env
+ with a duplicate key will take precedence.
+ items:
+ description: EnvFromSource represents the source of a
+ set of ConfigMaps
+ properties:
+ configMapRef:
+ description: The ConfigMap to select from
+ properties:
+ name:
+ description: 'Name of the referent. More info:
+ https://kubernetes.io/docs/concepts/overview/working-with-objects/names/#names
+ TODO: Add other useful fields. apiVersion, kind,
+ uid?'
+ type: string
+ optional:
+ description: Specify whether the ConfigMap must
+ be defined
+ type: boolean
+ type: object
+ prefix:
+ description: An optional identifier to prepend to
+ each key in the ConfigMap. Must be a C_IDENTIFIER.
+ type: string
+ secretRef:
+ description: The Secret to select from
+ properties:
+ name:
+ description: 'Name of the referent. More info:
+ https://kubernetes.io/docs/concepts/overview/working-with-objects/names/#names
+ TODO: Add other useful fields. apiVersion, kind,
+ uid?'
+ type: string
+ optional:
+ description: Specify whether the Secret must be
+ defined
+ type: boolean
+ type: object
+ type: object
+ type: array
+ image:
+ description: 'Docker image name. More info: https://kubernetes.io/docs/concepts/containers/images'
+ type: string
+ imagePullPolicy:
+ description: Image pull policy. One of Always, Never, IfNotPresent.
+ Defaults to Always.
+ type: string
+ lifecycle:
+ description: Actions that the management system should take
+ in response to container lifecycle events.
+ properties:
+ postStart:
+ description: 'PostStart is called immediately after
+ a container is created. If the handler fails, the
+ container is terminated and restarted according to
+ its restart policy. Other management of the container
+ blocks until the hook completes. More info: https://kubernetes.io/docs/concepts/containers/container-lifecycle-hooks/#container-hooks'
+ properties:
+ exec:
+ description: One and only one of the following should
+ be specified. Exec specifies the action to take.
+ properties:
+ command:
+ description: Command is the command line to
+ execute inside the container, the working
+ directory for the command is root ('/') in
+ the container's filesystem. The command is
+ simply exec'd, it is not run inside a shell,
+ so traditional shell instructions ('|', etc)
+ won't work. To use a shell, you need to explicitly
+ call out to that shell. Exit status of 0 is
+ treated as live/healthy and non-zero is unhealthy.
+ items:
+ type: string
+ type: array
+ type: object
+ httpGet:
+ description: HTTPGet specifies the http request
+ to perform.
+ properties:
+ host:
+ description: Host name to connect to, defaults
+ to the pod IP. You probably want to set "Host"
+ in httpHeaders instead.
+ type: string
+ httpHeaders:
+ description: Custom headers to set in the request.
+ HTTP allows repeated headers.
+ items:
+ description: HTTPHeader describes a custom
+ header to be used in HTTP probes
+ properties:
+ name:
+ description: The header field name
+ type: string
+ value:
+ description: The header field value
+ type: string
+ required:
+ - name
+ - value
+ type: object
+ type: array
+ path:
+ description: Path to access on the HTTP server.
+ type: string
+ port:
+ anyOf:
+ - type: integer
+ - type: string
+ description: Name or number of the port to access
+ on the container. Number must be in the range
+ 1 to 65535. Name must be an IANA_SVC_NAME.
+ x-kubernetes-int-or-string: true
+ scheme:
+ description: Scheme to use for connecting to
+ the host. Defaults to HTTP.
+ type: string
+ required:
+ - port
+ type: object
+ tcpSocket:
+ description: 'TCPSocket specifies an action involving
+ a TCP port. TCP hooks not yet supported TODO:
+ implement a realistic TCP lifecycle hook'
+ properties:
+ host:
+ description: 'Optional: Host name to connect
+ to, defaults to the pod IP.'
+ type: string
+ port:
+ anyOf:
+ - type: integer
+ - type: string
+ description: Number or name of the port to access
+ on the container. Number must be in the range
+ 1 to 65535. Name must be an IANA_SVC_NAME.
+ x-kubernetes-int-or-string: true
+ required:
+ - port
+ type: object
+ type: object
+ preStop:
+ description: 'PreStop is called immediately before a
+ container is terminated due to an API request or management
+ event such as liveness/startup probe failure, preemption,
+ resource contention, etc. The handler is not called
+ if the container crashes or exits. The reason for
+ termination is passed to the handler. The Pod''s termination
+ grace period countdown begins before the PreStop hooked
+ is executed. Regardless of the outcome of the handler,
+ the container will eventually terminate within the
+ Pod''s termination grace period. Other management
+ of the container blocks until the hook completes or
+ until the termination grace period is reached. More
+ info: https://kubernetes.io/docs/concepts/containers/container-lifecycle-hooks/#container-hooks'
+ properties:
+ exec:
+ description: One and only one of the following should
+ be specified. Exec specifies the action to take.
+ properties:
+ command:
+ description: Command is the command line to
+ execute inside the container, the working
+ directory for the command is root ('/') in
+ the container's filesystem. The command is
+ simply exec'd, it is not run inside a shell,
+ so traditional shell instructions ('|', etc)
+ won't work. To use a shell, you need to explicitly
+ call out to that shell. Exit status of 0 is
+ treated as live/healthy and non-zero is unhealthy.
+ items:
+ type: string
+ type: array
+ type: object
+ httpGet:
+ description: HTTPGet specifies the http request
+ to perform.
+ properties:
+ host:
+ description: Host name to connect to, defaults
+ to the pod IP. You probably want to set "Host"
+ in httpHeaders instead.
+ type: string
+ httpHeaders:
+ description: Custom headers to set in the request.
+ HTTP allows repeated headers.
+ items:
+ description: HTTPHeader describes a custom
+ header to be used in HTTP probes
+ properties:
+ name:
+ description: The header field name
+ type: string
+ value:
+ description: The header field value
+ type: string
+ required:
+ - name
+ - value
+ type: object
+ type: array
+ path:
+ description: Path to access on the HTTP server.
+ type: string
+ port:
+ anyOf:
+ - type: integer
+ - type: string
+ description: Name or number of the port to access
+ on the container. Number must be in the range
+ 1 to 65535. Name must be an IANA_SVC_NAME.
+ x-kubernetes-int-or-string: true
+ scheme:
+ description: Scheme to use for connecting to
+ the host. Defaults to HTTP.
+ type: string
+ required:
+ - port
+ type: object
+ tcpSocket:
+ description: 'TCPSocket specifies an action involving
+ a TCP port. TCP hooks not yet supported TODO:
+ implement a realistic TCP lifecycle hook'
+ properties:
+ host:
+ description: 'Optional: Host name to connect
+ to, defaults to the pod IP.'
+ type: string
+ port:
+ anyOf:
+ - type: integer
+ - type: string
+ description: Number or name of the port to access
+ on the container. Number must be in the range
+ 1 to 65535. Name must be an IANA_SVC_NAME.
+ x-kubernetes-int-or-string: true
+ required:
+ - port
+ type: object
+ type: object
+ type: object
+ livenessProbe:
+ description: Periodic probe of container liveness. Container
+ will be restarted if the probe fails.
+ properties:
+ exec:
+ description: One and only one of the following should
+ be specified. Exec specifies the action to take.
+ properties:
+ command:
+ description: Command is the command line to execute
+ inside the container, the working directory for
+ the command is root ('/') in the container's
+ filesystem. The command is simply exec'd, it is
+ not run inside a shell, so traditional shell instructions
+ ('|', etc) won't work. To use a shell, you need
+ to explicitly call out to that shell. Exit status
+ of 0 is treated as live/healthy and non-zero is
+ unhealthy.
+ items:
+ type: string
+ type: array
+ type: object
+ failureThreshold:
+ description: Minimum consecutive failures for the probe
+ to be considered failed after having succeeded. Defaults
+ to 3. Minimum value is 1.
+ format: int32
+ type: integer
+ httpGet:
+ description: HTTPGet specifies the http request to perform.
+ properties:
+ host:
+ description: Host name to connect to, defaults to
+ the pod IP. You probably want to set "Host" in
+ httpHeaders instead.
+ type: string
+ httpHeaders:
+ description: Custom headers to set in the request.
+ HTTP allows repeated headers.
+ items:
+ description: HTTPHeader describes a custom header
+ to be used in HTTP probes
+ properties:
+ name:
+ description: The header field name
+ type: string
+ value:
+ description: The header field value
+ type: string
+ required:
+ - name
+ - value
+ type: object
+ type: array
+ path:
+ description: Path to access on the HTTP server.
+ type: string
+ port:
+ anyOf:
+ - type: integer
+ - type: string
+ description: Name or number of the port to access
+ on the container. Number must be in the range
+ 1 to 65535. Name must be an IANA_SVC_NAME.
+ x-kubernetes-int-or-string: true
+ scheme:
+ description: Scheme to use for connecting to the
+ host. Defaults to HTTP.
+ type: string
+ required:
+ - port
+ type: object
+ initialDelaySeconds:
+ description: 'Number of seconds after the container
+ has started before liveness probes are initiated.
+ More info: https://kubernetes.io/docs/concepts/workloads/pods/pod-lifecycle#container-probes'
+ format: int32
+ type: integer
+ periodSeconds:
+ description: How often (in seconds) to perform the probe.
+ Default to 10 seconds. Minimum value is 1.
+ format: int32
+ type: integer
+ successThreshold:
+ description: Minimum consecutive successes for the probe
+ to be considered successful after having failed. Defaults
+ to 1. Must be 1 for liveness and startup. Minimum
+ value is 1.
+ format: int32
+ type: integer
+ tcpSocket:
+ description: 'TCPSocket specifies an action involving
+ a TCP port. TCP hooks not yet supported TODO: implement
+ a realistic TCP lifecycle hook'
+ properties:
+ host:
+ description: 'Optional: Host name to connect to,
+ defaults to the pod IP.'
+ type: string
+ port:
+ anyOf:
+ - type: integer
+ - type: string
+ description: Number or name of the port to access
+ on the container. Number must be in the range
+ 1 to 65535. Name must be an IANA_SVC_NAME.
+ x-kubernetes-int-or-string: true
+ required:
+ - port
+ type: object
+ timeoutSeconds:
+ description: 'Number of seconds after which the probe
+ times out. Defaults to 1 second. Minimum value is
+ 1. More info: https://kubernetes.io/docs/concepts/workloads/pods/pod-lifecycle#container-probes'
+ format: int32
+ type: integer
+ type: object
+ name:
+ description: Name of the container specified as a DNS_LABEL.
+ Each container in a pod must have a unique name (DNS_LABEL).
+ type: string
+ ports:
+ description: List of ports to expose from the container.
+ Exposing a port here gives the system additional information
+ about the network connections a container uses, but is
+ primarily informational. Not specifying a port here DOES
+ NOT prevent that port from being exposed. Any port which
+ is listening on the default "0.0.0.0" address inside a
+ container will be accessible from the network.
+ items:
+ description: ContainerPort represents a network port in
+ a single container.
+ properties:
+ containerPort:
+ description: Number of port to expose on the pod's
+ IP address. This must be a valid port number, 0
+ < x < 65536.
+ format: int32
+ type: integer
+ hostIP:
+ description: What host IP to bind the external port
+ to.
+ type: string
+ hostPort:
+ description: Number of port to expose on the host.
+ If specified, this must be a valid port number,
+ 0 < x < 65536. If HostNetwork is specified, this
+ must match ContainerPort. Most containers do not
+ need this.
+ format: int32
+ type: integer
+ name:
+ description: If specified, this must be an IANA_SVC_NAME
+ and unique within the pod. Each named port in a
+ pod must have a unique name. Name for the port that
+ can be referred to by services.
+ type: string
+ protocol:
+ default: TCP
+ description: Protocol for port. Must be UDP, TCP,
+ or SCTP. Defaults to "TCP".
+ type: string
+ required:
+ - containerPort
+ type: object
+ type: array
+ readinessProbe:
+ description: Periodic probe of container service readiness.
+ Container will be removed from service endpoints if the
+ probe fails.
+ properties:
+ exec:
+ description: One and only one of the following should
+ be specified. Exec specifies the action to take.
+ properties:
+ command:
+ description: Command is the command line to execute
+ inside the container, the working directory for
+ the command is root ('/') in the container's
+ filesystem. The command is simply exec'd, it is
+ not run inside a shell, so traditional shell instructions
+ ('|', etc) won't work. To use a shell, you need
+ to explicitly call out to that shell. Exit status
+ of 0 is treated as live/healthy and non-zero is
+ unhealthy.
+ items:
+ type: string
+ type: array
+ type: object
+ failureThreshold:
+ description: Minimum consecutive failures for the probe
+ to be considered failed after having succeeded. Defaults
+ to 3. Minimum value is 1.
+ format: int32
+ type: integer
+ httpGet:
+ description: HTTPGet specifies the http request to perform.
+ properties:
+ host:
+ description: Host name to connect to, defaults to
+ the pod IP. You probably want to set "Host" in
+ httpHeaders instead.
+ type: string
+ httpHeaders:
+ description: Custom headers to set in the request.
+ HTTP allows repeated headers.
+ items:
+ description: HTTPHeader describes a custom header
+ to be used in HTTP probes
+ properties:
+ name:
+ description: The header field name
+ type: string
+ value:
+ description: The header field value
+ type: string
+ required:
+ - name
+ - value
+ type: object
+ type: array
+ path:
+ description: Path to access on the HTTP server.
+ type: string
+ port:
+ anyOf:
+ - type: integer
+ - type: string
+ description: Name or number of the port to access
+ on the container. Number must be in the range
+ 1 to 65535. Name must be an IANA_SVC_NAME.
+ x-kubernetes-int-or-string: true
+ scheme:
+ description: Scheme to use for connecting to the
+ host. Defaults to HTTP.
+ type: string
+ required:
+ - port
+ type: object
+ initialDelaySeconds:
+ description: 'Number of seconds after the container
+ has started before liveness probes are initiated.
+ More info: https://kubernetes.io/docs/concepts/workloads/pods/pod-lifecycle#container-probes'
+ format: int32
+ type: integer
+ periodSeconds:
+ description: How often (in seconds) to perform the probe.
+ Default to 10 seconds. Minimum value is 1.
+ format: int32
+ type: integer
+ successThreshold:
+ description: Minimum consecutive successes for the probe
+ to be considered successful after having failed. Defaults
+ to 1. Must be 1 for liveness and startup. Minimum
+ value is 1.
+ format: int32
+ type: integer
+ tcpSocket:
+ description: 'TCPSocket specifies an action involving
+ a TCP port. TCP hooks not yet supported TODO: implement
+ a realistic TCP lifecycle hook'
+ properties:
+ host:
+ description: 'Optional: Host name to connect to,
+ defaults to the pod IP.'
+ type: string
+ port:
+ anyOf:
+ - type: integer
+ - type: string
+ description: Number or name of the port to access
+ on the container. Number must be in the range
+ 1 to 65535. Name must be an IANA_SVC_NAME.
+ x-kubernetes-int-or-string: true
+ required:
+ - port
+ type: object
+ timeoutSeconds:
+ description: 'Number of seconds after which the probe
+ times out. Defaults to 1 second. Minimum value is
+ 1. More info: https://kubernetes.io/docs/concepts/workloads/pods/pod-lifecycle#container-probes'
+ format: int32
+ type: integer
+ type: object
+ resources:
+ description: 'Compute Resources required by this container.
+ More info: https://kubernetes.io/docs/concepts/configuration/manage-compute-resources-container/'
+ properties:
+ limits:
+ additionalProperties:
+ anyOf:
+ - type: integer
+ - type: string
+ pattern: ^(\+|-)?(([0-9]+(\.[0-9]*)?)|(\.[0-9]+))(([KMGTPE]i)|[numkMGTPE]|([eE](\+|-)?(([0-9]+(\.[0-9]*)?)|(\.[0-9]+))))?$
+ x-kubernetes-int-or-string: true
+ description: 'Limits describes the maximum amount of
+ compute resources allowed. More info: https://kubernetes.io/docs/concepts/configuration/manage-compute-resources-container/'
+ type: object
+ requests:
+ additionalProperties:
+ anyOf:
+ - type: integer
+ - type: string
+ pattern: ^(\+|-)?(([0-9]+(\.[0-9]*)?)|(\.[0-9]+))(([KMGTPE]i)|[numkMGTPE]|([eE](\+|-)?(([0-9]+(\.[0-9]*)?)|(\.[0-9]+))))?$
+ x-kubernetes-int-or-string: true
+ description: 'Requests describes the minimum amount
+ of compute resources required. If Requests is omitted
+ for a container, it defaults to Limits if that is
+ explicitly specified, otherwise to an implementation-defined
+ value. More info: https://kubernetes.io/docs/concepts/configuration/manage-compute-resources-container/'
+ type: object
+ type: object
+ securityContext:
+ description: 'Security options the pod should run with.
+ More info: https://kubernetes.io/docs/concepts/policy/security-context/
+ More info: https://kubernetes.io/docs/tasks/configure-pod-container/security-context/'
+ properties:
+ allowPrivilegeEscalation:
+ description: 'AllowPrivilegeEscalation controls whether
+ a process can gain more privileges than its parent
+ process. This bool directly controls if the no_new_privs
+ flag will be set on the container process. AllowPrivilegeEscalation
+ is true always when the container is: 1) run as Privileged
+ 2) has CAP_SYS_ADMIN'
+ type: boolean
+ capabilities:
+ description: The capabilities to add/drop when running
+ containers. Defaults to the default set of capabilities
+ granted by the container runtime.
+ properties:
+ add:
+ description: Added capabilities
+ items:
+ description: Capability represent POSIX capabilities
+ type
+ type: string
+ type: array
+ drop:
+ description: Removed capabilities
+ items:
+ description: Capability represent POSIX capabilities
+ type
+ type: string
+ type: array
+ type: object
+ privileged:
+ description: Run container in privileged mode. Processes
+ in privileged containers are essentially equivalent
+ to root on the host. Defaults to false.
+ type: boolean
+ procMount:
+ description: procMount denotes the type of proc mount
+ to use for the containers. The default is DefaultProcMount
+ which uses the container runtime defaults for readonly
+ paths and masked paths. This requires the ProcMountType
+ feature flag to be enabled.
+ type: string
+ readOnlyRootFilesystem:
+ description: Whether this container has a read-only
+ root filesystem. Default is false.
+ type: boolean
+ runAsGroup:
+ description: The GID to run the entrypoint of the container
+ process. Uses runtime default if unset. May also be
+ set in PodSecurityContext. If set in both SecurityContext
+ and PodSecurityContext, the value specified in SecurityContext
+ takes precedence.
+ format: int64
+ type: integer
+ runAsNonRoot:
+ description: Indicates that the container must run as
+ a non-root user. If true, the Kubelet will validate
+ the image at runtime to ensure that it does not run
+ as UID 0 (root) and fail to start the container if
+ it does. If unset or false, no such validation will
+ be performed. May also be set in PodSecurityContext. If
+ set in both SecurityContext and PodSecurityContext,
+ the value specified in SecurityContext takes precedence.
+ type: boolean
+ runAsUser:
+ description: The UID to run the entrypoint of the container
+ process. Defaults to user specified in image metadata
+ if unspecified. May also be set in PodSecurityContext. If
+ set in both SecurityContext and PodSecurityContext,
+ the value specified in SecurityContext takes precedence.
+ format: int64
+ type: integer
+ seLinuxOptions:
+ description: The SELinux context to be applied to the
+ container. If unspecified, the container runtime will
+ allocate a random SELinux context for each container. May
+ also be set in PodSecurityContext. If set in both
+ SecurityContext and PodSecurityContext, the value
+ specified in SecurityContext takes precedence.
+ properties:
+ level:
+ description: Level is SELinux level label that applies
+ to the container.
+ type: string
+ role:
+ description: Role is a SELinux role label that applies
+ to the container.
+ type: string
+ type:
+ description: Type is a SELinux type label that applies
+ to the container.
+ type: string
+ user:
+ description: User is a SELinux user label that applies
+ to the container.
+ type: string
+ type: object
+ seccompProfile:
+ description: The seccomp options to use by this container.
+ If seccomp options are provided at both the pod &
+ container level, the container options override the
+ pod options.
+ properties:
+ localhostProfile:
+ description: localhostProfile indicates a profile
+ defined in a file on the node should be used.
+ The profile must be preconfigured on the node
+ to work. Must be a descending path, relative to
+ the kubelet's configured seccomp profile location.
+ Must only be set if type is "Localhost".
+ type: string
+ type:
+ description: "type indicates which kind of seccomp
+ profile will be applied. Valid options are: \n
+ Localhost - a profile defined in a file on the
+ node should be used. RuntimeDefault - the container
+ runtime default profile should be used. Unconfined
+ - no profile should be applied."
+ type: string
+ required:
+ - type
+ type: object
+ windowsOptions:
+ description: The Windows specific settings applied to
+ all containers. If unspecified, the options from the
+ PodSecurityContext will be used. If set in both SecurityContext
+ and PodSecurityContext, the value specified in SecurityContext
+ takes precedence.
+ properties:
+ gmsaCredentialSpec:
+ description: GMSACredentialSpec is where the GMSA
+ admission webhook (https://github.com/kubernetes-sigs/windows-gmsa)
+ inlines the contents of the GMSA credential spec
+ named by the GMSACredentialSpecName field.
+ type: string
+ gmsaCredentialSpecName:
+ description: GMSACredentialSpecName is the name
+ of the GMSA credential spec to use.
+ type: string
+ runAsUserName:
+ description: The UserName in Windows to run the
+ entrypoint of the container process. Defaults
+ to the user specified in image metadata if unspecified.
+ May also be set in PodSecurityContext. If set
+ in both SecurityContext and PodSecurityContext,
+ the value specified in SecurityContext takes precedence.
+ type: string
+ type: object
+ type: object
+ volumeMounts:
+ description: Pod volumes to mount into the container's filesystem.
+ items:
+ description: VolumeMount describes a mounting of a Volume
+ within a container.
+ properties:
+ mountPath:
+ description: Path within the container at which the
+ volume should be mounted. Must not contain ':'.
+ type: string
+ mountPropagation:
+ description: mountPropagation determines how mounts
+ are propagated from the host to container and the
+ other way around. When not set, MountPropagationNone
+ is used. This field is beta in 1.10.
+ type: string
+ name:
+ description: This must match the Name of a Volume.
+ type: string
+ readOnly:
+ description: Mounted read-only if true, read-write
+ otherwise (false or unspecified). Defaults to false.
+ type: boolean
+ subPath:
+ description: Path within the volume from which the
+ container's volume should be mounted. Defaults to
+ "" (volume's root).
+ type: string
+ subPathExpr:
+ description: Expanded path within the volume from
+ which the container's volume should be mounted.
+ Behaves similarly to SubPath but environment variable
+ references $(VAR_NAME) are expanded using the container's
+ environment. Defaults to "" (volume's root). SubPathExpr
+ and SubPath are mutually exclusive.
+ type: string
+ required:
+ - mountPath
+ - name
+ type: object
+ type: array
+ workingDir:
+ description: Container's working directory. If not specified,
+ the container runtime's default will be used, which might
+ be configured in the container image.
+ type: string
+ required:
+ - image
+ - imagePullPolicy
+ - name
+ - resources
+ type: object
+ type: array
+ disableCSRFProtection:
+ description: DisableCSRFProtection allows you to toggle CSRF Protection
+ on Jenkins
+ type: boolean
+ imagePullSecrets:
+ description: 'ImagePullSecrets is an optional list of references
+ to secrets in the same namespace to use for pulling any of the
+ images used by this PodSpec. If specified, these secrets will
+ be passed to individual puller implementations for them to use.
+ For example, in the case of docker, only DockerConfig type secrets
+ are honored. More info: https://kubernetes.io/docs/concepts/containers/images#specifying-imagepullsecrets-on-a-pod'
+ items:
+ description: LocalObjectReference contains enough information
+ to let you locate the referenced object inside the same namespace.
+ properties:
+ name:
+ description: 'Name of the referent. More info: https://kubernetes.io/docs/concepts/overview/working-with-objects/names/#names
+ TODO: Add other useful fields. apiVersion, kind, uid?'
+ type: string
+ type: object
+ type: array
+ labels:
+ additionalProperties:
+ type: string
+ description: '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. More info:
+ http://kubernetes.io/docs/user-guide/labels'
+ type: object
+ nodeSelector:
+ additionalProperties:
+ type: string
+ description: 'NodeSelector is a selector which must be true for
+ the pod to fit on a node. Selector which must match a node''s
+ labels for the pod to be scheduled on that node. More info:
+ https://kubernetes.io/docs/concepts/configuration/assign-pod-node/'
+ type: object
+ plugins:
+ description: Plugins contains plugins required by user
+ items:
+ description: Plugin defines Jenkins plugin.
+ properties:
+ downloadURL:
+ description: DownloadURL is the custom url from where plugin
+ has to be downloaded.
+ type: string
+ name:
+ description: Name is the name of Jenkins plugin
+ type: string
+ version:
+ description: Version is the version of Jenkins plugin
+ type: string
+ required:
+ - name
+ - version
+ type: object
+ type: array
+ priorityClassName:
+ description: PriorityClassName for Jenkins master pod
+ type: string
+ securityContext:
+ description: 'SecurityContext that applies to all the containers
+ of the Jenkins Master. As per kubernetes specification, it can
+ be overridden for each container individually. Defaults to:
+ runAsUser: 1000 fsGroup: 1000'
+ properties:
+ fsGroup:
+ description: "A special supplemental group that applies to
+ all containers in a pod. Some volume types allow the Kubelet
+ to change the ownership of that volume to be owned by the
+ pod: \n 1. The owning GID will be the FSGroup 2. The setgid
+ bit is set (new files created in the volume will be owned
+ by FSGroup) 3. The permission bits are OR'd with rw-rw----
+ \n If unset, the Kubelet will not modify the ownership and
+ permissions of any volume."
+ format: int64
+ type: integer
+ fsGroupChangePolicy:
+ description: 'fsGroupChangePolicy defines behavior of changing
+ ownership and permission of the volume before being exposed
+ inside Pod. This field will only apply to volume types which
+ support fsGroup based ownership(and permissions). It will
+ have no effect on ephemeral volume types such as: secret,
+ configmaps and emptydir. Valid values are "OnRootMismatch"
+ and "Always". If not specified, "Always" is used.'
+ type: string
+ runAsGroup:
+ description: The GID to run the entrypoint of the container
+ process. Uses runtime default if unset. May also be set
+ in SecurityContext. If set in both SecurityContext and
+ PodSecurityContext, the value specified in SecurityContext
+ takes precedence for that container.
+ format: int64
+ type: integer
+ runAsNonRoot:
+ description: Indicates that the container must run as a non-root
+ user. If true, the Kubelet will validate the image at runtime
+ to ensure that it does not run as UID 0 (root) and fail
+ to start the container if it does. If unset or false, no
+ such validation will be performed. May also be set in SecurityContext. If
+ set in both SecurityContext and PodSecurityContext, the
+ value specified in SecurityContext takes precedence.
+ type: boolean
+ runAsUser:
+ description: The UID to run the entrypoint of the container
+ process. Defaults to user specified in image metadata if
+ unspecified. May also be set in SecurityContext. If set
+ in both SecurityContext and PodSecurityContext, the value
+ specified in SecurityContext takes precedence for that container.
+ format: int64
+ type: integer
+ seLinuxOptions:
+ description: The SELinux context to be applied to all containers.
+ If unspecified, the container runtime will allocate a random
+ SELinux context for each container. May also be set in
+ SecurityContext. If set in both SecurityContext and PodSecurityContext,
+ the value specified in SecurityContext takes precedence
+ for that container.
+ properties:
+ level:
+ description: Level is SELinux level label that applies
+ to the container.
+ type: string
+ role:
+ description: Role is a SELinux role label that applies
+ to the container.
+ type: string
+ type:
+ description: Type is a SELinux type label that applies
+ to the container.
+ type: string
+ user:
+ description: User is a SELinux user label that applies
+ to the container.
+ type: string
+ type: object
+ seccompProfile:
+ description: The seccomp options to use by the containers
+ in this pod.
+ properties:
+ localhostProfile:
+ description: localhostProfile indicates a profile defined
+ in a file on the node should be used. The profile must
+ be preconfigured on the node to work. Must be a descending
+ path, relative to the kubelet's configured seccomp profile
+ location. Must only be set if type is "Localhost".
+ type: string
+ type:
+ description: "type indicates which kind of seccomp profile
+ will be applied. Valid options are: \n Localhost - a
+ profile defined in a file on the node should be used.
+ RuntimeDefault - the container runtime default profile
+ should be used. Unconfined - no profile should be applied."
+ type: string
+ required:
+ - type
+ type: object
+ supplementalGroups:
+ description: A list of groups applied to the first process
+ run in each container, in addition to the container's primary
+ GID. If unspecified, no groups will be added to any container.
+ items:
+ format: int64
+ type: integer
+ type: array
+ sysctls:
+ description: Sysctls hold a list of namespaced sysctls used
+ for the pod. Pods with unsupported sysctls (by the container
+ runtime) might fail to launch.
+ items:
+ description: Sysctl defines a kernel parameter to be set
+ properties:
+ name:
+ description: Name of a property to set
+ type: string
+ value:
+ description: Value of a property to set
+ type: string
+ required:
+ - name
+ - value
+ type: object
+ type: array
+ windowsOptions:
+ description: The Windows specific settings applied to all
+ containers. If unspecified, the options within a container's
+ SecurityContext will be used. If set in both SecurityContext
+ and PodSecurityContext, the value specified in SecurityContext
+ takes precedence.
+ properties:
+ gmsaCredentialSpec:
+ description: GMSACredentialSpec is where the GMSA admission
+ webhook (https://github.com/kubernetes-sigs/windows-gmsa)
+ inlines the contents of the GMSA credential spec named
+ by the GMSACredentialSpecName field.
+ type: string
+ gmsaCredentialSpecName:
+ description: GMSACredentialSpecName is the name of the
+ GMSA credential spec to use.
+ type: string
+ runAsUserName:
+ description: The UserName in Windows to run the entrypoint
+ of the container process. Defaults to the user specified
+ in image metadata if unspecified. May also be set in
+ PodSecurityContext. If set in both SecurityContext and
+ PodSecurityContext, the value specified in SecurityContext
+ takes precedence.
+ type: string
+ type: object
+ type: object
+ tolerations:
+ description: If specified, the pod's tolerations.
+ items:
+ description: The pod this Toleration is attached to tolerates
+ any taint that matches the triple using
+ the matching operator .
+ properties:
+ effect:
+ description: Effect indicates the taint effect to match.
+ Empty means match all taint effects. When specified, allowed
+ values are NoSchedule, PreferNoSchedule and NoExecute.
+ type: string
+ key:
+ description: Key is the taint key that the toleration applies
+ to. Empty means match all taint keys. If the key is empty,
+ operator must be Exists; this combination means to match
+ all values and all keys.
+ type: string
+ operator:
+ description: Operator represents a key's relationship to
+ the value. Valid operators are Exists and Equal. Defaults
+ to Equal. Exists is equivalent to wildcard for value,
+ so that a pod can tolerate all taints of a particular
+ category.
+ type: string
+ tolerationSeconds:
+ description: TolerationSeconds represents the period of
+ time the toleration (which must be of effect NoExecute,
+ otherwise this field is ignored) tolerates the taint.
+ By default, it is not set, which means tolerate the taint
+ forever (do not evict). Zero and negative values will
+ be treated as 0 (evict immediately) by the system.
+ format: int64
+ type: integer
+ value:
+ description: Value is the taint value the toleration matches
+ to. If the operator is Exists, the value should be empty,
+ otherwise just a regular string.
+ type: string
+ type: object
+ type: array
+ volumes:
+ description: 'List of volumes that can be mounted by containers
+ belonging to the pod. More info: https://kubernetes.io/docs/concepts/storage/volumes'
+ items:
+ description: Volume represents a named volume in a pod that
+ may be accessed by any container in the pod.
+ properties:
+ awsElasticBlockStore:
+ description: 'AWSElasticBlockStore represents an AWS Disk
+ resource that is attached to a kubelet''s host machine
+ and then exposed to the pod. More info: https://kubernetes.io/docs/concepts/storage/volumes#awselasticblockstore'
+ properties:
+ fsType:
+ description: 'Filesystem type of the volume that you
+ want to mount. Tip: Ensure that the filesystem type
+ is supported by the host operating system. Examples:
+ "ext4", "xfs", "ntfs". Implicitly inferred to be "ext4"
+ if unspecified. More info: https://kubernetes.io/docs/concepts/storage/volumes#awselasticblockstore
+ TODO: how do we prevent errors in the filesystem from
+ compromising the machine'
+ type: string
+ partition:
+ description: 'The partition in the volume that you want
+ to mount. If omitted, the default is to mount by volume
+ name. Examples: For volume /dev/sda1, you specify
+ the partition as "1". Similarly, the volume partition
+ for /dev/sda is "0" (or you can leave the property
+ empty).'
+ format: int32
+ type: integer
+ readOnly:
+ description: 'Specify "true" to force and set the ReadOnly
+ property in VolumeMounts to "true". If omitted, the
+ default is "false". More info: https://kubernetes.io/docs/concepts/storage/volumes#awselasticblockstore'
+ type: boolean
+ volumeID:
+ description: 'Unique ID of the persistent disk resource
+ in AWS (Amazon EBS volume). More info: https://kubernetes.io/docs/concepts/storage/volumes#awselasticblockstore'
+ type: string
+ required:
+ - volumeID
+ type: object
+ azureDisk:
+ description: AzureDisk represents an Azure Data Disk mount
+ on the host and bind mount to the pod.
+ properties:
+ cachingMode:
+ description: 'Host Caching mode: None, Read Only, Read
+ Write.'
+ type: string
+ diskName:
+ description: The Name of the data disk in the blob storage
+ type: string
+ diskURI:
+ description: The URI the data disk in the blob storage
+ type: string
+ fsType:
+ description: Filesystem type to mount. Must be a filesystem
+ type supported by the host operating system. Ex. "ext4",
+ "xfs", "ntfs". Implicitly inferred to be "ext4" if
+ unspecified.
+ type: string
+ kind:
+ description: 'Expected values Shared: multiple blob
+ disks per storage account Dedicated: single blob
+ disk per storage account Managed: azure managed data
+ disk (only in managed availability set). defaults
+ to shared'
+ type: string
+ readOnly:
+ description: Defaults to false (read/write). ReadOnly
+ here will force the ReadOnly setting in VolumeMounts.
+ type: boolean
+ required:
+ - diskName
+ - diskURI
+ type: object
+ azureFile:
+ description: AzureFile represents an Azure File Service
+ mount on the host and bind mount to the pod.
+ properties:
+ readOnly:
+ description: Defaults to false (read/write). ReadOnly
+ here will force the ReadOnly setting in VolumeMounts.
+ type: boolean
+ secretName:
+ description: the name of secret that contains Azure
+ Storage Account Name and Key
+ type: string
+ shareName:
+ description: Share Name
+ type: string
+ required:
+ - secretName
+ - shareName
+ type: object
+ cephfs:
+ description: CephFS represents a Ceph FS mount on the host
+ that shares a pod's lifetime
+ properties:
+ monitors:
+ description: 'Required: Monitors is a collection of
+ Ceph monitors More info: https://examples.k8s.io/volumes/cephfs/README.md#how-to-use-it'
+ items:
+ type: string
+ type: array
+ path:
+ description: 'Optional: Used as the mounted root, rather
+ than the full Ceph tree, default is /'
+ type: string
+ readOnly:
+ description: 'Optional: Defaults to false (read/write).
+ ReadOnly here will force the ReadOnly setting in VolumeMounts.
+ More info: https://examples.k8s.io/volumes/cephfs/README.md#how-to-use-it'
+ type: boolean
+ secretFile:
+ description: 'Optional: SecretFile is the path to key
+ ring for User, default is /etc/ceph/user.secret More
+ info: https://examples.k8s.io/volumes/cephfs/README.md#how-to-use-it'
+ type: string
+ secretRef:
+ description: 'Optional: SecretRef is reference to the
+ authentication secret for User, default is empty.
+ More info: https://examples.k8s.io/volumes/cephfs/README.md#how-to-use-it'
+ properties:
+ name:
+ description: 'Name of the referent. More info: https://kubernetes.io/docs/concepts/overview/working-with-objects/names/#names
+ TODO: Add other useful fields. apiVersion, kind,
+ uid?'
+ type: string
+ type: object
+ user:
+ description: 'Optional: User is the rados user name,
+ default is admin More info: https://examples.k8s.io/volumes/cephfs/README.md#how-to-use-it'
+ type: string
+ required:
+ - monitors
+ type: object
+ cinder:
+ description: 'Cinder represents a cinder volume attached
+ and mounted on kubelets host machine. More info: https://examples.k8s.io/mysql-cinder-pd/README.md'
+ properties:
+ fsType:
+ description: 'Filesystem type to mount. Must be a filesystem
+ type supported by the host operating system. Examples:
+ "ext4", "xfs", "ntfs". Implicitly inferred to be "ext4"
+ if unspecified. More info: https://examples.k8s.io/mysql-cinder-pd/README.md'
+ type: string
+ readOnly:
+ description: 'Optional: Defaults to false (read/write).
+ ReadOnly here will force the ReadOnly setting in VolumeMounts.
+ More info: https://examples.k8s.io/mysql-cinder-pd/README.md'
+ type: boolean
+ secretRef:
+ description: 'Optional: points to a secret object containing
+ parameters used to connect to OpenStack.'
+ properties:
+ name:
+ description: 'Name of the referent. More info: https://kubernetes.io/docs/concepts/overview/working-with-objects/names/#names
+ TODO: Add other useful fields. apiVersion, kind,
+ uid?'
+ type: string
+ type: object
+ volumeID:
+ description: 'volume id used to identify the volume
+ in cinder. More info: https://examples.k8s.io/mysql-cinder-pd/README.md'
+ type: string
+ required:
+ - volumeID
+ type: object
+ configMap:
+ description: ConfigMap represents a configMap that should
+ populate this volume
+ properties:
+ defaultMode:
+ description: 'Optional: mode bits used to set permissions
+ on created files by default. Must be an octal value
+ between 0000 and 0777 or a decimal value between 0
+ and 511. YAML accepts both octal and decimal values,
+ JSON requires decimal values for mode bits. Defaults
+ to 0644. Directories within the path are not affected
+ by this setting. This might be in conflict with other
+ options that affect the file mode, like fsGroup, and
+ the result can be other mode bits set.'
+ format: int32
+ type: integer
+ items:
+ description: If unspecified, each key-value pair in
+ the Data field of the referenced ConfigMap will be
+ projected into the volume as a file whose name is
+ the key and content is the value. If specified, the
+ listed keys will be projected into the specified paths,
+ and unlisted keys will not be present. If a key is
+ specified which is not present in the ConfigMap, the
+ volume setup will error unless it is marked optional.
+ Paths must be relative and may not contain the '..'
+ path or start with '..'.
+ items:
+ description: Maps a string key to a path within a
+ volume.
+ properties:
+ key:
+ description: The key to project.
+ type: string
+ mode:
+ description: 'Optional: mode bits used to set
+ permissions on this file. Must be an octal value
+ between 0000 and 0777 or a decimal value between
+ 0 and 511. YAML accepts both octal and decimal
+ values, JSON requires decimal values for mode
+ bits. If not specified, the volume defaultMode
+ will be used. This might be in conflict with
+ other options that affect the file mode, like
+ fsGroup, and the result can be other mode bits
+ set.'
+ format: int32
+ type: integer
+ path:
+ description: The relative path of the file to
+ map the key to. May not be an absolute path.
+ May not contain the path element '..'. May not
+ start with the string '..'.
+ type: string
+ required:
+ - key
+ - path
+ type: object
+ type: array
+ name:
+ description: 'Name of the referent. More info: https://kubernetes.io/docs/concepts/overview/working-with-objects/names/#names
+ TODO: Add other useful fields. apiVersion, kind, uid?'
+ type: string
+ optional:
+ description: Specify whether the ConfigMap or its keys
+ must be defined
+ type: boolean
+ type: object
+ csi:
+ description: CSI (Container Storage Interface) represents
+ ephemeral storage that is handled by certain external
+ CSI drivers (Beta feature).
+ properties:
+ driver:
+ description: Driver is the name of the CSI driver that
+ handles this volume. Consult with your admin for the
+ correct name as registered in the cluster.
+ type: string
+ fsType:
+ description: Filesystem type to mount. Ex. "ext4", "xfs",
+ "ntfs". If not provided, the empty value is passed
+ to the associated CSI driver which will determine
+ the default filesystem to apply.
+ type: string
+ nodePublishSecretRef:
+ description: NodePublishSecretRef is a reference to
+ the secret object containing sensitive information
+ to pass to the CSI driver to complete the CSI NodePublishVolume
+ and NodeUnpublishVolume calls. This field is optional,
+ and may be empty if no secret is required. If the
+ secret object contains more than one secret, all secret
+ references are passed.
+ properties:
+ name:
+ description: 'Name of the referent. More info: https://kubernetes.io/docs/concepts/overview/working-with-objects/names/#names
+ TODO: Add other useful fields. apiVersion, kind,
+ uid?'
+ type: string
+ type: object
+ readOnly:
+ description: Specifies a read-only configuration for
+ the volume. Defaults to false (read/write).
+ type: boolean
+ volumeAttributes:
+ additionalProperties:
+ type: string
+ description: VolumeAttributes stores driver-specific
+ properties that are passed to the CSI driver. Consult
+ your driver's documentation for supported values.
+ type: object
+ required:
+ - driver
+ type: object
+ downwardAPI:
+ description: DownwardAPI represents downward API about the
+ pod that should populate this volume
+ properties:
+ defaultMode:
+ description: 'Optional: mode bits to use on created
+ files by default. Must be a Optional: mode bits used
+ to set permissions on created files by default. Must
+ be an octal value between 0000 and 0777 or a decimal
+ value between 0 and 511. YAML accepts both octal and
+ decimal values, JSON requires decimal values for mode
+ bits. Defaults to 0644. Directories within the path
+ are not affected by this setting. This might be in
+ conflict with other options that affect the file mode,
+ like fsGroup, and the result can be other mode bits
+ set.'
+ format: int32
+ type: integer
+ items:
+ description: Items is a list of downward API volume
+ file
+ items:
+ description: DownwardAPIVolumeFile represents information
+ to create the file containing the pod field
+ properties:
+ fieldRef:
+ description: 'Required: Selects a field of the
+ pod: only annotations, labels, name and namespace
+ are supported.'
+ properties:
+ apiVersion:
+ description: Version of the schema the FieldPath
+ is written in terms of, defaults to "v1".
+ type: string
+ fieldPath:
+ description: Path of the field to select in
+ the specified API version.
+ type: string
+ required:
+ - fieldPath
+ type: object
+ mode:
+ description: 'Optional: mode bits used to set
+ permissions on this file, must be an octal value
+ between 0000 and 0777 or a decimal value between
+ 0 and 511. YAML accepts both octal and decimal
+ values, JSON requires decimal values for mode
+ bits. If not specified, the volume defaultMode
+ will be used. This might be in conflict with
+ other options that affect the file mode, like
+ fsGroup, and the result can be other mode bits
+ set.'
+ format: int32
+ type: integer
+ path:
+ description: 'Required: Path is the relative
+ path name of the file to be created. Must not
+ be absolute or contain the ''..'' path. Must
+ be utf-8 encoded. The first item of the relative
+ path must not start with ''..'''
+ type: string
+ resourceFieldRef:
+ description: 'Selects a resource of the container:
+ only resources limits and requests (limits.cpu,
+ limits.memory, requests.cpu and requests.memory)
+ are currently supported.'
+ properties:
+ containerName:
+ description: 'Container name: required for
+ volumes, optional for env vars'
+ type: string
+ divisor:
+ anyOf:
+ - type: integer
+ - type: string
+ description: Specifies the output format of
+ the exposed resources, defaults to "1"
+ pattern: ^(\+|-)?(([0-9]+(\.[0-9]*)?)|(\.[0-9]+))(([KMGTPE]i)|[numkMGTPE]|([eE](\+|-)?(([0-9]+(\.[0-9]*)?)|(\.[0-9]+))))?$
+ x-kubernetes-int-or-string: true
+ resource:
+ description: 'Required: resource to select'
+ type: string
+ required:
+ - resource
+ type: object
+ required:
+ - path
+ type: object
+ type: array
+ type: object
+ emptyDir:
+ description: 'EmptyDir represents a temporary directory
+ that shares a pod''s lifetime. More info: https://kubernetes.io/docs/concepts/storage/volumes#emptydir'
+ properties:
+ medium:
+ description: 'What type of storage medium should back
+ this directory. The default is "" which means to use
+ the node''s default medium. Must be an empty string
+ (default) or Memory. More info: https://kubernetes.io/docs/concepts/storage/volumes#emptydir'
+ type: string
+ sizeLimit:
+ anyOf:
+ - type: integer
+ - type: string
+ description: 'Total amount of local storage required
+ for this EmptyDir volume. The size limit is also applicable
+ for memory medium. The maximum usage on memory medium
+ EmptyDir would be the minimum value between the SizeLimit
+ specified here and the sum of memory limits of all
+ containers in a pod. The default is nil which means
+ that the limit is undefined. More info: http://kubernetes.io/docs/user-guide/volumes#emptydir'
+ pattern: ^(\+|-)?(([0-9]+(\.[0-9]*)?)|(\.[0-9]+))(([KMGTPE]i)|[numkMGTPE]|([eE](\+|-)?(([0-9]+(\.[0-9]*)?)|(\.[0-9]+))))?$
+ x-kubernetes-int-or-string: true
+ type: object
+ ephemeral:
+ description: "Ephemeral represents a volume that is handled
+ by a cluster storage driver (Alpha feature). The volume's
+ lifecycle is tied to the pod that defines it - it will
+ be created before the pod starts, and deleted when the
+ pod is removed. \n Use this if: a) the volume is only
+ needed while the pod runs, b) features of normal volumes
+ like restoring from snapshot or capacity tracking are
+ needed, c) the storage driver is specified through a storage
+ class, and d) the storage driver supports dynamic volume
+ provisioning through a PersistentVolumeClaim (see EphemeralVolumeSource
+ for more information on the connection between this
+ volume type and PersistentVolumeClaim). \n Use PersistentVolumeClaim
+ or one of the vendor-specific APIs for volumes that persist
+ for longer than the lifecycle of an individual pod. \n
+ Use CSI for light-weight local ephemeral volumes if the
+ CSI driver is meant to be used that way - see the documentation
+ of the driver for more information. \n A pod can use both
+ types of ephemeral volumes and persistent volumes at the
+ same time."
+ properties:
+ readOnly:
+ description: Specifies a read-only configuration for
+ the volume. Defaults to false (read/write).
+ type: boolean
+ volumeClaimTemplate:
+ description: "Will be used to create a stand-alone PVC
+ to provision the volume. The pod in which this EphemeralVolumeSource
+ is embedded will be the owner of the PVC, i.e. the
+ PVC will be deleted together with the pod. The name
+ of the PVC will be `-` where
+ `` is the name from the `PodSpec.Volumes`
+ array entry. Pod validation will reject the pod if
+ the concatenated name is not valid for a PVC (for
+ example, too long). \n An existing PVC with that name
+ that is not owned by the pod will *not* be used for
+ the pod to avoid using an unrelated volume by mistake.
+ Starting the pod is then blocked until the unrelated
+ PVC is removed. If such a pre-created PVC is meant
+ to be used by the pod, the PVC has to updated with
+ an owner reference to the pod once the pod exists.
+ Normally this should not be necessary, but it may
+ be useful when manually reconstructing a broken cluster.
+ \n This field is read-only and no changes will be
+ made by Kubernetes to the PVC after it has been created.
+ \n Required, must not be nil."
+ properties:
+ metadata:
+ description: May contain labels and annotations
+ that will be copied into the PVC when creating
+ it. No other fields are allowed and will be rejected
+ during validation.
+ type: object
+ spec:
+ description: The specification for the PersistentVolumeClaim.
+ The entire content is copied unchanged into the
+ PVC that gets created from this template. The
+ same fields as in a PersistentVolumeClaim are
+ also valid here.
+ properties:
+ accessModes:
+ description: 'AccessModes contains the desired
+ access modes the volume should have. More
+ info: https://kubernetes.io/docs/concepts/storage/persistent-volumes#access-modes-1'
+ items:
+ type: string
+ type: array
+ dataSource:
+ description: 'This field can be used to specify
+ either: * An existing VolumeSnapshot object
+ (snapshot.storage.k8s.io/VolumeSnapshot) *
+ An existing PVC (PersistentVolumeClaim) *
+ An existing custom resource that implements
+ data population (Alpha) In order to use custom
+ resource types that implement data population,
+ the AnyVolumeDataSource feature gate must
+ be enabled. If the provisioner or an external
+ controller can support the specified data
+ source, it will create a new volume based
+ on the contents of the specified data source.'
+ properties:
+ apiGroup:
+ description: APIGroup is the group for the
+ resource being referenced. If APIGroup
+ is not specified, the specified Kind must
+ be in the core API group. For any other
+ third-party types, APIGroup is required.
+ type: string
+ kind:
+ description: Kind is the type of resource
+ being referenced
+ type: string
+ name:
+ description: Name is the name of resource
+ being referenced
+ type: string
+ required:
+ - kind
+ - name
+ type: object
+ resources:
+ description: 'Resources represents the minimum
+ resources the volume should have. More info:
+ https://kubernetes.io/docs/concepts/storage/persistent-volumes#resources'
+ properties:
+ limits:
+ additionalProperties:
+ anyOf:
+ - type: integer
+ - type: string
+ pattern: ^(\+|-)?(([0-9]+(\.[0-9]*)?)|(\.[0-9]+))(([KMGTPE]i)|[numkMGTPE]|([eE](\+|-)?(([0-9]+(\.[0-9]*)?)|(\.[0-9]+))))?$
+ x-kubernetes-int-or-string: true
+ description: 'Limits describes the maximum
+ amount of compute resources allowed. More
+ info: https://kubernetes.io/docs/concepts/configuration/manage-compute-resources-container/'
+ type: object
+ requests:
+ additionalProperties:
+ anyOf:
+ - type: integer
+ - type: string
+ pattern: ^(\+|-)?(([0-9]+(\.[0-9]*)?)|(\.[0-9]+))(([KMGTPE]i)|[numkMGTPE]|([eE](\+|-)?(([0-9]+(\.[0-9]*)?)|(\.[0-9]+))))?$
+ x-kubernetes-int-or-string: true
+ description: 'Requests describes the minimum
+ amount of compute resources required.
+ If Requests is omitted for a container,
+ it defaults to Limits if that is explicitly
+ specified, otherwise to an implementation-defined
+ value. More info: https://kubernetes.io/docs/concepts/configuration/manage-compute-resources-container/'
+ type: object
+ type: object
+ selector:
+ description: A label query over volumes to consider
+ for binding.
+ properties:
+ matchExpressions:
+ description: matchExpressions is a list
+ of label selector requirements. The requirements
+ are ANDed.
+ items:
+ description: A label selector requirement
+ is a selector that contains values,
+ a key, and an operator that relates
+ the key and values.
+ properties:
+ key:
+ description: key is the label key
+ that the selector applies to.
+ type: string
+ operator:
+ description: operator represents a
+ key's relationship to a set of values.
+ Valid operators are In, NotIn, Exists
+ and DoesNotExist.
+ type: string
+ values:
+ description: values is an array of
+ string values. If the operator is
+ In or NotIn, the values array must
+ be non-empty. If the operator is
+ Exists or DoesNotExist, the values
+ array must be empty. This array
+ is replaced during a strategic merge
+ patch.
+ items:
+ type: string
+ type: array
+ required:
+ - key
+ - operator
+ type: object
+ type: array
+ matchLabels:
+ additionalProperties:
+ type: string
+ description: matchLabels is a map of {key,value}
+ pairs. A single {key,value} in the matchLabels
+ map is equivalent to an element of matchExpressions,
+ whose key field is "key", the operator
+ is "In", and the values array contains
+ only "value". The requirements are ANDed.
+ type: object
+ type: object
+ storageClassName:
+ description: 'Name of the StorageClass required
+ by the claim. More info: https://kubernetes.io/docs/concepts/storage/persistent-volumes#class-1'
+ type: string
+ volumeMode:
+ description: volumeMode defines what type of
+ volume is required by the claim. Value of
+ Filesystem is implied when not included in
+ claim spec.
+ type: string
+ volumeName:
+ description: VolumeName is the binding reference
+ to the PersistentVolume backing this claim.
+ type: string
+ type: object
+ required:
+ - spec
+ type: object
+ type: object
+ fc:
+ description: FC represents a Fibre Channel resource that
+ is attached to a kubelet's host machine and then exposed
+ to the pod.
+ properties:
+ fsType:
+ description: 'Filesystem type to mount. Must be a filesystem
+ type supported by the host operating system. Ex. "ext4",
+ "xfs", "ntfs". Implicitly inferred to be "ext4" if
+ unspecified. TODO: how do we prevent errors in the
+ filesystem from compromising the machine'
+ type: string
+ lun:
+ description: 'Optional: FC target lun number'
+ format: int32
+ type: integer
+ readOnly:
+ description: 'Optional: Defaults to false (read/write).
+ ReadOnly here will force the ReadOnly setting in VolumeMounts.'
+ type: boolean
+ targetWWNs:
+ description: 'Optional: FC target worldwide names (WWNs)'
+ items:
+ type: string
+ type: array
+ wwids:
+ description: 'Optional: FC volume world wide identifiers
+ (wwids) Either wwids or combination of targetWWNs
+ and lun must be set, but not both simultaneously.'
+ items:
+ type: string
+ type: array
+ type: object
+ flexVolume:
+ description: FlexVolume represents a generic volume resource
+ that is provisioned/attached using an exec based plugin.
+ properties:
+ driver:
+ description: Driver is the name of the driver to use
+ for this volume.
+ type: string
+ fsType:
+ description: Filesystem type to mount. Must be a filesystem
+ type supported by the host operating system. Ex. "ext4",
+ "xfs", "ntfs". The default filesystem depends on FlexVolume
+ script.
+ type: string
+ options:
+ additionalProperties:
+ type: string
+ description: 'Optional: Extra command options if any.'
+ type: object
+ readOnly:
+ description: 'Optional: Defaults to false (read/write).
+ ReadOnly here will force the ReadOnly setting in VolumeMounts.'
+ type: boolean
+ secretRef:
+ description: 'Optional: SecretRef is reference to the
+ secret object containing sensitive information to
+ pass to the plugin scripts. This may be empty if no
+ secret object is specified. If the secret object contains
+ more than one secret, all secrets are passed to the
+ plugin scripts.'
+ properties:
+ name:
+ description: 'Name of the referent. More info: https://kubernetes.io/docs/concepts/overview/working-with-objects/names/#names
+ TODO: Add other useful fields. apiVersion, kind,
+ uid?'
+ type: string
+ type: object
+ required:
+ - driver
+ type: object
+ flocker:
+ description: Flocker represents a Flocker volume attached
+ to a kubelet's host machine. This depends on the Flocker
+ control service being running
+ properties:
+ datasetName:
+ description: Name of the dataset stored as metadata
+ -> name on the dataset for Flocker should be considered
+ as deprecated
+ type: string
+ datasetUUID:
+ description: UUID of the dataset. This is unique identifier
+ of a Flocker dataset
+ type: string
+ type: object
+ gcePersistentDisk:
+ description: 'GCEPersistentDisk represents a GCE Disk resource
+ that is attached to a kubelet''s host machine and then
+ exposed to the pod. More info: https://kubernetes.io/docs/concepts/storage/volumes#gcepersistentdisk'
+ properties:
+ fsType:
+ description: 'Filesystem type of the volume that you
+ want to mount. Tip: Ensure that the filesystem type
+ is supported by the host operating system. Examples:
+ "ext4", "xfs", "ntfs". Implicitly inferred to be "ext4"
+ if unspecified. More info: https://kubernetes.io/docs/concepts/storage/volumes#gcepersistentdisk
+ TODO: how do we prevent errors in the filesystem from
+ compromising the machine'
+ type: string
+ partition:
+ description: 'The partition in the volume that you want
+ to mount. If omitted, the default is to mount by volume
+ name. Examples: For volume /dev/sda1, you specify
+ the partition as "1". Similarly, the volume partition
+ for /dev/sda is "0" (or you can leave the property
+ empty). More info: https://kubernetes.io/docs/concepts/storage/volumes#gcepersistentdisk'
+ format: int32
+ type: integer
+ pdName:
+ description: 'Unique name of the PD resource in GCE.
+ Used to identify the disk in GCE. More info: https://kubernetes.io/docs/concepts/storage/volumes#gcepersistentdisk'
+ type: string
+ readOnly:
+ description: 'ReadOnly here will force the ReadOnly
+ setting in VolumeMounts. Defaults to false. More info:
+ https://kubernetes.io/docs/concepts/storage/volumes#gcepersistentdisk'
+ type: boolean
+ required:
+ - pdName
+ type: object
+ gitRepo:
+ description: 'GitRepo represents a git repository at a particular
+ revision. DEPRECATED: GitRepo is deprecated. To provision
+ a container with a git repo, mount an EmptyDir into an
+ InitContainer that clones the repo using git, then mount
+ the EmptyDir into the Pod''s container.'
+ properties:
+ directory:
+ description: Target directory name. Must not contain
+ or start with '..'. If '.' is supplied, the volume
+ directory will be the git repository. Otherwise,
+ if specified, the volume will contain the git repository
+ in the subdirectory with the given name.
+ type: string
+ repository:
+ description: Repository URL
+ type: string
+ revision:
+ description: Commit hash for the specified revision.
+ type: string
+ required:
+ - repository
+ type: object
+ glusterfs:
+ description: 'Glusterfs represents a Glusterfs mount on
+ the host that shares a pod''s lifetime. More info: https://examples.k8s.io/volumes/glusterfs/README.md'
+ properties:
+ endpoints:
+ description: 'EndpointsName is the endpoint name that
+ details Glusterfs topology. More info: https://examples.k8s.io/volumes/glusterfs/README.md#create-a-pod'
+ type: string
+ path:
+ description: 'Path is the Glusterfs volume path. More
+ info: https://examples.k8s.io/volumes/glusterfs/README.md#create-a-pod'
+ type: string
+ readOnly:
+ description: 'ReadOnly here will force the Glusterfs
+ volume to be mounted with read-only permissions. Defaults
+ to false. More info: https://examples.k8s.io/volumes/glusterfs/README.md#create-a-pod'
+ type: boolean
+ required:
+ - endpoints
+ - path
+ type: object
+ hostPath:
+ description: 'HostPath represents a pre-existing file or
+ directory on the host machine that is directly exposed
+ to the container. This is generally used for system agents
+ or other privileged things that are allowed to see the
+ host machine. Most containers will NOT need this. More
+ info: https://kubernetes.io/docs/concepts/storage/volumes#hostpath
+ --- TODO(jonesdl) We need to restrict who can use host
+ directory mounts and who can/can not mount host directories
+ as read/write.'
+ properties:
+ path:
+ description: 'Path of the directory on the host. If
+ the path is a symlink, it will follow the link to
+ the real path. More info: https://kubernetes.io/docs/concepts/storage/volumes#hostpath'
+ type: string
+ type:
+ description: 'Type for HostPath Volume Defaults to ""
+ More info: https://kubernetes.io/docs/concepts/storage/volumes#hostpath'
+ type: string
+ required:
+ - path
+ type: object
+ iscsi:
+ description: 'ISCSI represents an ISCSI Disk resource that
+ is attached to a kubelet''s host machine and then exposed
+ to the pod. More info: https://examples.k8s.io/volumes/iscsi/README.md'
+ properties:
+ chapAuthDiscovery:
+ description: whether support iSCSI Discovery CHAP authentication
+ type: boolean
+ chapAuthSession:
+ description: whether support iSCSI Session CHAP authentication
+ type: boolean
+ fsType:
+ description: 'Filesystem type of the volume that you
+ want to mount. Tip: Ensure that the filesystem type
+ is supported by the host operating system. Examples:
+ "ext4", "xfs", "ntfs". Implicitly inferred to be "ext4"
+ if unspecified. More info: https://kubernetes.io/docs/concepts/storage/volumes#iscsi
+ TODO: how do we prevent errors in the filesystem from
+ compromising the machine'
+ type: string
+ initiatorName:
+ description: Custom iSCSI Initiator Name. If initiatorName
+ is specified with iscsiInterface simultaneously, new
+ iSCSI interface : will
+ be created for the connection.
+ type: string
+ iqn:
+ description: Target iSCSI Qualified Name.
+ type: string
+ iscsiInterface:
+ description: iSCSI Interface Name that uses an iSCSI
+ transport. Defaults to 'default' (tcp).
+ type: string
+ lun:
+ description: iSCSI Target Lun number.
+ format: int32
+ type: integer
+ portals:
+ description: iSCSI Target Portal List. The portal is
+ either an IP or ip_addr:port if the port is other
+ than default (typically TCP ports 860 and 3260).
+ items:
+ type: string
+ type: array
+ readOnly:
+ description: ReadOnly here will force the ReadOnly setting
+ in VolumeMounts. Defaults to false.
+ type: boolean
+ secretRef:
+ description: CHAP Secret for iSCSI target and initiator
+ authentication
+ properties:
+ name:
+ description: 'Name of the referent. More info: https://kubernetes.io/docs/concepts/overview/working-with-objects/names/#names
+ TODO: Add other useful fields. apiVersion, kind,
+ uid?'
+ type: string
+ type: object
+ targetPortal:
+ description: iSCSI Target Portal. The Portal is either
+ an IP or ip_addr:port if the port is other than default
+ (typically TCP ports 860 and 3260).
+ type: string
+ required:
+ - iqn
+ - lun
+ - targetPortal
+ type: object
+ name:
+ description: 'Volume''s name. Must be a DNS_LABEL and unique
+ within the pod. More info: https://kubernetes.io/docs/concepts/overview/working-with-objects/names/#names'
+ type: string
+ nfs:
+ description: 'NFS represents an NFS mount on the host that
+ shares a pod''s lifetime More info: https://kubernetes.io/docs/concepts/storage/volumes#nfs'
+ properties:
+ path:
+ description: 'Path that is exported by the NFS server.
+ More info: https://kubernetes.io/docs/concepts/storage/volumes#nfs'
+ type: string
+ readOnly:
+ description: 'ReadOnly here will force the NFS export
+ to be mounted with read-only permissions. Defaults
+ to false. More info: https://kubernetes.io/docs/concepts/storage/volumes#nfs'
+ type: boolean
+ server:
+ description: 'Server is the hostname or IP address of
+ the NFS server. More info: https://kubernetes.io/docs/concepts/storage/volumes#nfs'
+ type: string
+ required:
+ - path
+ - server
+ type: object
+ persistentVolumeClaim:
+ description: 'PersistentVolumeClaimVolumeSource represents
+ a reference to a PersistentVolumeClaim in the same namespace.
+ More info: https://kubernetes.io/docs/concepts/storage/persistent-volumes#persistentvolumeclaims'
+ properties:
+ claimName:
+ description: 'ClaimName is the name of a PersistentVolumeClaim
+ in the same namespace as the pod using this volume.
+ More info: https://kubernetes.io/docs/concepts/storage/persistent-volumes#persistentvolumeclaims'
+ type: string
+ readOnly:
+ description: Will force the ReadOnly setting in VolumeMounts.
+ Default false.
+ type: boolean
+ required:
+ - claimName
+ type: object
+ photonPersistentDisk:
+ description: PhotonPersistentDisk represents a PhotonController
+ persistent disk attached and mounted on kubelets host
+ machine
+ properties:
+ fsType:
+ description: Filesystem type to mount. Must be a filesystem
+ type supported by the host operating system. Ex. "ext4",
+ "xfs", "ntfs". Implicitly inferred to be "ext4" if
+ unspecified.
+ type: string
+ pdID:
+ description: ID that identifies Photon Controller persistent
+ disk
+ type: string
+ required:
+ - pdID
+ type: object
+ portworxVolume:
+ description: PortworxVolume represents a portworx volume
+ attached and mounted on kubelets host machine
+ properties:
+ fsType:
+ description: FSType represents the filesystem type to
+ mount Must be a filesystem type supported by the host
+ operating system. Ex. "ext4", "xfs". Implicitly inferred
+ to be "ext4" if unspecified.
+ type: string
+ readOnly:
+ description: Defaults to false (read/write). ReadOnly
+ here will force the ReadOnly setting in VolumeMounts.
+ type: boolean
+ volumeID:
+ description: VolumeID uniquely identifies a Portworx
+ volume
+ type: string
+ required:
+ - volumeID
+ type: object
+ projected:
+ description: Items for all in one resources secrets, configmaps,
+ and downward API
+ properties:
+ defaultMode:
+ description: Mode bits used to set permissions on created
+ files by default. Must be an octal value between 0000
+ and 0777 or a decimal value between 0 and 511. YAML
+ accepts both octal and decimal values, JSON requires
+ decimal values for mode bits. Directories within the
+ path are not affected by this setting. This might
+ be in conflict with other options that affect the
+ file mode, like fsGroup, and the result can be other
+ mode bits set.
+ format: int32
+ type: integer
+ sources:
+ description: list of volume projections
+ items:
+ description: Projection that may be projected along
+ with other supported volume types
+ properties:
+ configMap:
+ description: information about the configMap data
+ to project
+ properties:
+ items:
+ description: If unspecified, each key-value
+ pair in the Data field of the referenced
+ ConfigMap will be projected into the volume
+ as a file whose name is the key and content
+ is the value. If specified, the listed keys
+ will be projected into the specified paths,
+ and unlisted keys will not be present. If
+ a key is specified which is not present
+ in the ConfigMap, the volume setup will
+ error unless it is marked optional. Paths
+ must be relative and may not contain the
+ '..' path or start with '..'.
+ items:
+ description: Maps a string key to a path
+ within a volume.
+ properties:
+ key:
+ description: The key to project.
+ type: string
+ mode:
+ description: 'Optional: mode bits used
+ to set permissions on this file. Must
+ be an octal value between 0000 and
+ 0777 or a decimal value between 0
+ and 511. YAML accepts both octal and
+ decimal values, JSON requires decimal
+ values for mode bits. If not specified,
+ the volume defaultMode will be used.
+ This might be in conflict with other
+ options that affect the file mode,
+ like fsGroup, and the result can be
+ other mode bits set.'
+ format: int32
+ type: integer
+ path:
+ description: The relative path of the
+ file to map the key to. May not be
+ an absolute path. May not contain
+ the path element '..'. May not start
+ with the string '..'.
+ type: string
+ required:
+ - key
+ - path
+ type: object
+ type: array
+ name:
+ description: 'Name of the referent. More info:
+ https://kubernetes.io/docs/concepts/overview/working-with-objects/names/#names
+ TODO: Add other useful fields. apiVersion,
+ kind, uid?'
+ type: string
+ optional:
+ description: Specify whether the ConfigMap
+ or its keys must be defined
+ type: boolean
+ type: object
+ downwardAPI:
+ description: information about the downwardAPI
+ data to project
+ properties:
+ items:
+ description: Items is a list of DownwardAPIVolume
+ file
+ items:
+ description: DownwardAPIVolumeFile represents
+ information to create the file containing
+ the pod field
+ properties:
+ fieldRef:
+ description: 'Required: Selects a field
+ of the pod: only annotations, labels,
+ name and namespace are supported.'
+ properties:
+ apiVersion:
+ description: Version of the schema
+ the FieldPath is written in terms
+ of, defaults to "v1".
+ type: string
+ fieldPath:
+ description: Path of the field to
+ select in the specified API version.
+ type: string
+ required:
+ - fieldPath
+ type: object
+ mode:
+ description: 'Optional: mode bits used
+ to set permissions on this file, must
+ be an octal value between 0000 and
+ 0777 or a decimal value between 0
+ and 511. YAML accepts both octal and
+ decimal values, JSON requires decimal
+ values for mode bits. If not specified,
+ the volume defaultMode will be used.
+ This might be in conflict with other
+ options that affect the file mode,
+ like fsGroup, and the result can be
+ other mode bits set.'
+ format: int32
+ type: integer
+ path:
+ description: 'Required: Path is the
+ relative path name of the file to
+ be created. Must not be absolute or
+ contain the ''..'' path. Must be utf-8
+ encoded. The first item of the relative
+ path must not start with ''..'''
+ type: string
+ resourceFieldRef:
+ description: 'Selects a resource of
+ the container: only resources limits
+ and requests (limits.cpu, limits.memory,
+ requests.cpu and requests.memory)
+ are currently supported.'
+ properties:
+ containerName:
+ description: 'Container name: required
+ for volumes, optional for env
+ vars'
+ type: string
+ divisor:
+ anyOf:
+ - type: integer
+ - type: string
+ description: Specifies the output
+ format of the exposed resources,
+ defaults to "1"
+ pattern: ^(\+|-)?(([0-9]+(\.[0-9]*)?)|(\.[0-9]+))(([KMGTPE]i)|[numkMGTPE]|([eE](\+|-)?(([0-9]+(\.[0-9]*)?)|(\.[0-9]+))))?$
+ x-kubernetes-int-or-string: true
+ resource:
+ description: 'Required: resource
+ to select'
+ type: string
+ required:
+ - resource
+ type: object
+ required:
+ - path
+ type: object
+ type: array
+ type: object
+ secret:
+ description: information about the secret data
+ to project
+ properties:
+ items:
+ description: If unspecified, each key-value
+ pair in the Data field of the referenced
+ Secret will be projected into the volume
+ as a file whose name is the key and content
+ is the value. If specified, the listed keys
+ will be projected into the specified paths,
+ and unlisted keys will not be present. If
+ a key is specified which is not present
+ in the Secret, the volume setup will error
+ unless it is marked optional. Paths must
+ be relative and may not contain the '..'
+ path or start with '..'.
+ items:
+ description: Maps a string key to a path
+ within a volume.
+ properties:
+ key:
+ description: The key to project.
+ type: string
+ mode:
+ description: 'Optional: mode bits used
+ to set permissions on this file. Must
+ be an octal value between 0000 and
+ 0777 or a decimal value between 0
+ and 511. YAML accepts both octal and
+ decimal values, JSON requires decimal
+ values for mode bits. If not specified,
+ the volume defaultMode will be used.
+ This might be in conflict with other
+ options that affect the file mode,
+ like fsGroup, and the result can be
+ other mode bits set.'
+ format: int32
+ type: integer
+ path:
+ description: The relative path of the
+ file to map the key to. May not be
+ an absolute path. May not contain
+ the path element '..'. May not start
+ with the string '..'.
+ type: string
+ required:
+ - key
+ - path
+ type: object
+ type: array
+ name:
+ description: 'Name of the referent. More info:
+ https://kubernetes.io/docs/concepts/overview/working-with-objects/names/#names
+ TODO: Add other useful fields. apiVersion,
+ kind, uid?'
+ type: string
+ optional:
+ description: Specify whether the Secret or
+ its key must be defined
+ type: boolean
+ type: object
+ serviceAccountToken:
+ description: information about the serviceAccountToken
+ data to project
+ properties:
+ audience:
+ description: Audience is the intended audience
+ of the token. A recipient of a token must
+ identify itself with an identifier specified
+ in the audience of the token, and otherwise
+ should reject the token. The audience defaults
+ to the identifier of the apiserver.
+ type: string
+ expirationSeconds:
+ description: ExpirationSeconds is the requested
+ duration of validity of the service account
+ token. As the token approaches expiration,
+ the kubelet volume plugin will proactively
+ rotate the service account token. The kubelet
+ will start trying to rotate the token if
+ the token is older than 80 percent of its
+ time to live or if the token is older than
+ 24 hours.Defaults to 1 hour and must be
+ at least 10 minutes.
+ format: int64
+ type: integer
+ path:
+ description: Path is the path relative to
+ the mount point of the file to project the
+ token into.
+ type: string
+ required:
+ - path
+ type: object
+ type: object
+ type: array
+ type: object
+ quobyte:
+ description: Quobyte represents a Quobyte mount on the host
+ that shares a pod's lifetime
+ properties:
+ group:
+ description: Group to map volume access to Default is
+ no group
+ type: string
+ readOnly:
+ description: ReadOnly here will force the Quobyte volume
+ to be mounted with read-only permissions. Defaults
+ to false.
+ type: boolean
+ registry:
+ description: Registry represents a single or multiple
+ Quobyte Registry services specified as a string as
+ host:port pair (multiple entries are separated with
+ commas) which acts as the central registry for volumes
+ type: string
+ tenant:
+ description: Tenant owning the given Quobyte volume
+ in the Backend Used with dynamically provisioned Quobyte
+ volumes, value is set by the plugin
+ type: string
+ user:
+ description: User to map volume access to Defaults to
+ serivceaccount user
+ type: string
+ volume:
+ description: Volume is a string that references an already
+ created Quobyte volume by name.
+ type: string
+ required:
+ - registry
+ - volume
+ type: object
+ rbd:
+ description: 'RBD represents a Rados Block Device mount
+ on the host that shares a pod''s lifetime. More info:
+ https://examples.k8s.io/volumes/rbd/README.md'
+ properties:
+ fsType:
+ description: 'Filesystem type of the volume that you
+ want to mount. Tip: Ensure that the filesystem type
+ is supported by the host operating system. Examples:
+ "ext4", "xfs", "ntfs". Implicitly inferred to be "ext4"
+ if unspecified. More info: https://kubernetes.io/docs/concepts/storage/volumes#rbd
+ TODO: how do we prevent errors in the filesystem from
+ compromising the machine'
+ type: string
+ image:
+ description: 'The rados image name. More info: https://examples.k8s.io/volumes/rbd/README.md#how-to-use-it'
+ type: string
+ keyring:
+ description: 'Keyring is the path to key ring for RBDUser.
+ Default is /etc/ceph/keyring. More info: https://examples.k8s.io/volumes/rbd/README.md#how-to-use-it'
+ type: string
+ monitors:
+ description: 'A collection of Ceph monitors. More info:
+ https://examples.k8s.io/volumes/rbd/README.md#how-to-use-it'
+ items:
+ type: string
+ type: array
+ pool:
+ description: 'The rados pool name. Default is rbd. More
+ info: https://examples.k8s.io/volumes/rbd/README.md#how-to-use-it'
+ type: string
+ readOnly:
+ description: 'ReadOnly here will force the ReadOnly
+ setting in VolumeMounts. Defaults to false. More info:
+ https://examples.k8s.io/volumes/rbd/README.md#how-to-use-it'
+ type: boolean
+ secretRef:
+ description: 'SecretRef is name of the authentication
+ secret for RBDUser. If provided overrides keyring.
+ Default is nil. More info: https://examples.k8s.io/volumes/rbd/README.md#how-to-use-it'
+ properties:
+ name:
+ description: 'Name of the referent. More info: https://kubernetes.io/docs/concepts/overview/working-with-objects/names/#names
+ TODO: Add other useful fields. apiVersion, kind,
+ uid?'
+ type: string
+ type: object
+ user:
+ description: 'The rados user name. Default is admin.
+ More info: https://examples.k8s.io/volumes/rbd/README.md#how-to-use-it'
+ type: string
+ required:
+ - image
+ - monitors
+ type: object
+ scaleIO:
+ description: ScaleIO represents a ScaleIO persistent volume
+ attached and mounted on Kubernetes nodes.
+ properties:
+ fsType:
+ description: Filesystem type to mount. Must be a filesystem
+ type supported by the host operating system. Ex. "ext4",
+ "xfs", "ntfs". Default is "xfs".
+ type: string
+ gateway:
+ description: The host address of the ScaleIO API Gateway.
+ type: string
+ protectionDomain:
+ description: The name of the ScaleIO Protection Domain
+ for the configured storage.
+ type: string
+ readOnly:
+ description: Defaults to false (read/write). ReadOnly
+ here will force the ReadOnly setting in VolumeMounts.
+ type: boolean
+ secretRef:
+ description: SecretRef references to the secret for
+ ScaleIO user and other sensitive information. If this
+ is not provided, Login operation will fail.
+ properties:
+ name:
+ description: 'Name of the referent. More info: https://kubernetes.io/docs/concepts/overview/working-with-objects/names/#names
+ TODO: Add other useful fields. apiVersion, kind,
+ uid?'
+ type: string
+ type: object
+ sslEnabled:
+ description: Flag to enable/disable SSL communication
+ with Gateway, default false
+ type: boolean
+ storageMode:
+ description: Indicates whether the storage for a volume
+ should be ThickProvisioned or ThinProvisioned. Default
+ is ThinProvisioned.
+ type: string
+ storagePool:
+ description: The ScaleIO Storage Pool associated with
+ the protection domain.
+ type: string
+ system:
+ description: The name of the storage system as configured
+ in ScaleIO.
+ type: string
+ volumeName:
+ description: The name of a volume already created in
+ the ScaleIO system that is associated with this volume
+ source.
+ type: string
+ required:
+ - gateway
+ - secretRef
+ - system
+ type: object
+ secret:
+ description: 'Secret represents a secret that should populate
+ this volume. More info: https://kubernetes.io/docs/concepts/storage/volumes#secret'
+ properties:
+ defaultMode:
+ description: 'Optional: mode bits used to set permissions
+ on created files by default. Must be an octal value
+ between 0000 and 0777 or a decimal value between 0
+ and 511. YAML accepts both octal and decimal values,
+ JSON requires decimal values for mode bits. Defaults
+ to 0644. Directories within the path are not affected
+ by this setting. This might be in conflict with other
+ options that affect the file mode, like fsGroup, and
+ the result can be other mode bits set.'
+ format: int32
+ type: integer
+ items:
+ description: If unspecified, each key-value pair in
+ the Data field of the referenced Secret will be projected
+ into the volume as a file whose name is the key and
+ content is the value. If specified, the listed keys
+ will be projected into the specified paths, and unlisted
+ keys will not be present. If a key is specified which
+ is not present in the Secret, the volume setup will
+ error unless it is marked optional. Paths must be
+ relative and may not contain the '..' path or start
+ with '..'.
+ items:
+ description: Maps a string key to a path within a
+ volume.
+ properties:
+ key:
+ description: The key to project.
+ type: string
+ mode:
+ description: 'Optional: mode bits used to set
+ permissions on this file. Must be an octal value
+ between 0000 and 0777 or a decimal value between
+ 0 and 511. YAML accepts both octal and decimal
+ values, JSON requires decimal values for mode
+ bits. If not specified, the volume defaultMode
+ will be used. This might be in conflict with
+ other options that affect the file mode, like
+ fsGroup, and the result can be other mode bits
+ set.'
+ format: int32
+ type: integer
+ path:
+ description: The relative path of the file to
+ map the key to. May not be an absolute path.
+ May not contain the path element '..'. May not
+ start with the string '..'.
+ type: string
+ required:
+ - key
+ - path
+ type: object
+ type: array
+ optional:
+ description: Specify whether the Secret or its keys
+ must be defined
+ type: boolean
+ secretName:
+ description: 'Name of the secret in the pod''s namespace
+ to use. More info: https://kubernetes.io/docs/concepts/storage/volumes#secret'
+ type: string
+ type: object
+ storageos:
+ description: StorageOS represents a StorageOS volume attached
+ and mounted on Kubernetes nodes.
+ properties:
+ fsType:
+ description: Filesystem type to mount. Must be a filesystem
+ type supported by the host operating system. Ex. "ext4",
+ "xfs", "ntfs". Implicitly inferred to be "ext4" if
+ unspecified.
+ type: string
+ readOnly:
+ description: Defaults to false (read/write). ReadOnly
+ here will force the ReadOnly setting in VolumeMounts.
+ type: boolean
+ secretRef:
+ description: SecretRef specifies the secret to use for
+ obtaining the StorageOS API credentials. If not specified,
+ default values will be attempted.
+ properties:
+ name:
+ description: 'Name of the referent. More info: https://kubernetes.io/docs/concepts/overview/working-with-objects/names/#names
+ TODO: Add other useful fields. apiVersion, kind,
+ uid?'
+ type: string
+ type: object
+ volumeName:
+ description: VolumeName is the human-readable name of
+ the StorageOS volume. Volume names are only unique
+ within a namespace.
+ type: string
+ volumeNamespace:
+ description: VolumeNamespace specifies the scope of
+ the volume within StorageOS. If no namespace is specified
+ then the Pod's namespace will be used. This allows
+ the Kubernetes name scoping to be mirrored within
+ StorageOS for tighter integration. Set VolumeName
+ to any name to override the default behaviour. Set
+ to "default" if you are not using namespaces within
+ StorageOS. Namespaces that do not pre-exist within
+ StorageOS will be created.
+ type: string
+ type: object
+ vsphereVolume:
+ description: VsphereVolume represents a vSphere volume attached
+ and mounted on kubelets host machine
+ properties:
+ fsType:
+ description: Filesystem type to mount. Must be a filesystem
+ type supported by the host operating system. Ex. "ext4",
+ "xfs", "ntfs". Implicitly inferred to be "ext4" if
+ unspecified.
+ type: string
+ storagePolicyID:
+ description: Storage Policy Based Management (SPBM)
+ profile ID associated with the StoragePolicyName.
+ type: string
+ storagePolicyName:
+ description: Storage Policy Based Management (SPBM)
+ profile name.
+ type: string
+ volumePath:
+ description: Path that identifies vSphere volume vmdk
+ type: string
+ required:
+ - volumePath
+ type: object
+ required:
+ - name
+ type: object
+ type: array
+ required:
+ - disableCSRFProtection
+ type: object
+ notifications:
+ 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
+ items:
+ description: Notification is a service configuration used to send
+ notifications about Jenkins status.
+ properties:
+ level:
+ description: NotificationLevel defines the level of a Notification.
+ type: string
+ mailgun:
+ description: Mailgun is handler for Mailgun email service notification
+ channel.
+ properties:
+ apiKeySecretKeySelector:
+ description: SecretKeySelector selects a key of a Secret.
+ properties:
+ key:
+ description: The key of the secret to select from. Must
+ be a valid secret key.
+ type: string
+ secret:
+ description: The name of the secret in the pod's namespace
+ to select from.
+ properties:
+ name:
+ description: 'Name of the referent. More info: https://kubernetes.io/docs/concepts/overview/working-with-objects/names/#names
+ TODO: Add other useful fields. apiVersion, kind,
+ uid?'
+ type: string
+ type: object
+ required:
+ - key
+ - secret
+ type: object
+ domain:
+ type: string
+ from:
+ type: string
+ recipient:
+ type: string
+ required:
+ - apiKeySecretKeySelector
+ - domain
+ - from
+ - recipient
+ type: object
+ name:
+ type: string
+ slack:
+ description: Slack is handler for Slack notification channel.
+ properties:
+ webHookURLSecretKeySelector:
+ description: The web hook URL to Slack App
+ properties:
+ key:
+ description: The key of the secret to select from. Must
+ be a valid secret key.
+ type: string
+ secret:
+ description: The name of the secret in the pod's namespace
+ to select from.
+ properties:
+ name:
+ description: 'Name of the referent. More info: https://kubernetes.io/docs/concepts/overview/working-with-objects/names/#names
+ TODO: Add other useful fields. apiVersion, kind,
+ uid?'
+ type: string
+ type: object
+ required:
+ - key
+ - secret
+ type: object
+ required:
+ - webHookURLSecretKeySelector
+ type: object
+ smtp:
+ description: SMTP is handler for sending emails via this protocol.
+ properties:
+ from:
+ type: string
+ passwordSecretKeySelector:
+ description: SecretKeySelector selects a key of a Secret.
+ properties:
+ key:
+ description: The key of the secret to select from. Must
+ be a valid secret key.
+ type: string
+ secret:
+ description: The name of the secret in the pod's namespace
+ to select from.
+ properties:
+ name:
+ description: 'Name of the referent. More info: https://kubernetes.io/docs/concepts/overview/working-with-objects/names/#names
+ TODO: Add other useful fields. apiVersion, kind,
+ uid?'
+ type: string
+ type: object
+ required:
+ - key
+ - secret
+ type: object
+ port:
+ type: integer
+ server:
+ type: string
+ tlsInsecureSkipVerify:
+ type: boolean
+ to:
+ type: string
+ usernameSecretKeySelector:
+ description: SecretKeySelector selects a key of a Secret.
+ properties:
+ key:
+ description: The key of the secret to select from. Must
+ be a valid secret key.
+ type: string
+ secret:
+ description: The name of the secret in the pod's namespace
+ to select from.
+ properties:
+ name:
+ description: 'Name of the referent. More info: https://kubernetes.io/docs/concepts/overview/working-with-objects/names/#names
+ TODO: Add other useful fields. apiVersion, kind,
+ uid?'
+ type: string
+ type: object
+ required:
+ - key
+ - secret
+ type: object
+ required:
+ - from
+ - passwordSecretKeySelector
+ - port
+ - server
+ - to
+ - usernameSecretKeySelector
+ type: object
+ teams:
+ description: MicrosoftTeams is handler for Microsoft MicrosoftTeams
+ notification channel.
+ properties:
+ webHookURLSecretKeySelector:
+ description: The web hook URL to MicrosoftTeams App
+ properties:
+ key:
+ description: The key of the secret to select from. Must
+ be a valid secret key.
+ type: string
+ secret:
+ description: The name of the secret in the pod's namespace
+ to select from.
+ properties:
+ name:
+ description: 'Name of the referent. More info: https://kubernetes.io/docs/concepts/overview/working-with-objects/names/#names
+ TODO: Add other useful fields. apiVersion, kind,
+ uid?'
+ type: string
+ type: object
+ required:
+ - key
+ - secret
+ type: object
+ required:
+ - webHookURLSecretKeySelector
+ type: object
+ verbose:
+ type: boolean
+ required:
+ - level
+ - name
+ - verbose
+ type: object
+ type: array
+ restore:
+ description: 'Backup defines configuration of Jenkins backup restore
+ More info: https://jenkinsci.github.io/kubernetes-operator/docs/getting-started/latest/configure-backup-and-restore/'
+ properties:
+ action:
+ description: Action defines action which performs restore backup
+ in restore container sidecar
+ properties:
+ exec:
+ description: Exec specifies the action to take.
+ properties:
+ command:
+ description: Command is the command line to execute inside
+ the container, the working directory for the command is
+ root ('/') in the container's filesystem. The command
+ is simply exec'd, it is not run inside a shell, so traditional
+ shell instructions ('|', etc) won't work. To use a shell,
+ you need to explicitly call out to that shell. Exit
+ status of 0 is treated as live/healthy and non-zero
+ is unhealthy.
+ items:
+ type: string
+ type: array
+ type: object
+ type: object
+ containerName:
+ description: ContainerName is the container name responsible for
+ restore backup operation
+ type: string
+ getLatestAction:
+ description: GetLatestAction defines action which returns the
+ latest backup number. If there is no backup "-1" should be returned.
+ properties:
+ exec:
+ description: Exec specifies the action to take.
+ properties:
+ command:
+ description: Command is the command line to execute inside
+ the container, the working directory for the command is
+ root ('/') in the container's filesystem. The command
+ is simply exec'd, it is not run inside a shell, so traditional
+ shell instructions ('|', etc) won't work. To use a shell,
+ you need to explicitly call out to that shell. Exit
+ status of 0 is treated as live/healthy and non-zero
+ is unhealthy.
+ items:
+ type: string
+ type: array
+ type: object
+ type: object
+ recoveryOnce:
+ description: RecoveryOnce if want to restore specific backup set
+ this field and then Jenkins will be restarted and desired backup
+ will be restored
+ format: int64
+ type: integer
+ required:
+ - action
+ - containerName
+ type: object
+ roles:
+ description: Roles defines list of extra RBAC roles for the Jenkins
+ Master pod service account
+ items:
+ description: RoleRef contains information that points to the role
+ being used
+ properties:
+ apiGroup:
+ description: APIGroup is the group for the resource being referenced
+ type: string
+ kind:
+ description: Kind is the type of resource being referenced
+ type: string
+ name:
+ description: Name is the name of resource being referenced
+ type: string
+ required:
+ - apiGroup
+ - kind
+ - name
+ type: object
+ type: array
+ seedJobs:
+ description: 'SeedJobs defines list of Jenkins Seed Job configurations
+ More info: https://jenkinsci.github.io/kubernetes-operator/docs/getting-started/latest/configuration#configure-seed-jobs-and-pipelines'
+ items:
+ description: 'SeedJob defines configuration for seed job More info:
+ https://jenkinsci.github.io/kubernetes-operator/docs/getting-started/latest/configuration/#configure-seed-jobs-and-pipelines.'
+ properties:
+ additionalClasspath:
+ description: AdditionalClasspath is setting for Job DSL API
+ plugin to set Additional Classpath
+ type: string
+ bitbucketPushTrigger:
+ description: BitbucketPushTrigger is used for Bitbucket web
+ hooks
+ type: boolean
+ buildPeriodically:
+ description: BuildPeriodically is setting for scheduled trigger
+ type: string
+ credentialID:
+ description: CredentialID is the Kubernetes secret name which
+ stores repository access credentials
+ type: string
+ credentialType:
+ description: JenkinsCredentialType is the https://jenkinsci.github.io/kubernetes-credentials-provider-plugin/
+ credential type
+ type: string
+ description:
+ description: Description is the description of the seed job
+ type: string
+ failOnMissingPlugin:
+ description: FailOnMissingPlugin is setting for Job DSL API
+ plugin that fails job if required plugin is missing
+ type: boolean
+ githubPushTrigger:
+ description: GitHubPushTrigger is used for GitHub web hooks
+ type: boolean
+ id:
+ description: ID is the unique seed job name
+ type: string
+ ignoreMissingFiles:
+ description: IgnoreMissingFiles is setting for Job DSL API plugin
+ to ignore files that miss
+ type: boolean
+ pollSCM:
+ description: PollSCM is setting for polling changes in SCM
+ type: string
+ repositoryBranch:
+ description: RepositoryBranch is the repository branch where
+ are seed job definitions
+ type: string
+ repositoryUrl:
+ description: RepositoryURL is the repository access URL. Can
+ be SSH or HTTPS.
+ type: string
+ targets:
+ description: Targets is the repository path where are seed job
+ definitions
+ type: string
+ unstableOnDeprecation:
+ description: UnstableOnDeprecation is setting for Job DSL API
+ plugin that sets build status as unstable if build using deprecated
+ features
+ type: boolean
+ type: object
+ type: array
+ service:
+ description: 'Service is Kubernetes service of Jenkins master HTTP
+ pod Defaults to : port: 8080 type: ClusterIP'
+ properties:
+ annotations:
+ additionalProperties:
+ type: string
+ description: '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'
+ type: object
+ labels:
+ additionalProperties:
+ type: string
+ description: 'Route service traffic to pods with label keys and
+ values matching this selector. If empty or not present, the
+ service is assumed to have an external process managing its
+ endpoints, which Kubernetes will not modify. Only applies to
+ types ClusterIP, NodePort, and LoadBalancer. Ignored if type
+ is ExternalName. More info: https://kubernetes.io/docs/concepts/services-networking/service/'
+ type: object
+ loadBalancerIP:
+ description: 'Only applies to Service Type: LoadBalancer LoadBalancer
+ will get created with the IP specified in this field. This feature
+ depends on whether the underlying cloud-provider supports specifying
+ the loadBalancerIP when a load balancer is created. This field
+ will be ignored if the cloud-provider does not support the feature.'
+ type: string
+ loadBalancerSourceRanges:
+ description: 'If specified and supported by the platform, this
+ will restrict traffic through the cloud-provider load-balancer
+ will be restricted to the specified client IPs. This field will
+ be ignored if the cloud-provider does not support the feature."
+ More info: https://kubernetes.io/docs/tasks/administer-cluster/securing-a-cluster/#restricting-cloud-metadata-api-access'
+ items:
+ type: string
+ type: array
+ nodePort:
+ description: 'The port on each node on which this service is exposed
+ when type=NodePort or LoadBalancer. Usually assigned by the
+ system. If specified, it will be allocated to the service if
+ unused or else creation of the service will fail. Default is
+ to auto-allocate a port if the ServiceType of this Service requires
+ one. More info: https://kubernetes.io/docs/concepts/services-networking/service/#type-nodeport'
+ format: int32
+ type: integer
+ port:
+ description: 'The port that are exposed by this service. More
+ info: https://kubernetes.io/docs/concepts/services-networking/service/#virtual-ips-and-service-proxies'
+ format: int32
+ type: integer
+ type:
+ description: 'Type determines how the Service is exposed. Defaults
+ to ClusterIP. Valid options are ExternalName, ClusterIP, NodePort,
+ and LoadBalancer. "ExternalName" maps to the specified externalName.
+ "ClusterIP" allocates a cluster-internal IP address for load-balancing
+ to endpoints. Endpoints are determined by the selector or if
+ that is not specified, by manual construction of an Endpoints
+ object. If clusterIP is "None", no virtual IP is allocated and
+ the endpoints are published as a set of endpoints rather than
+ a stable IP. "NodePort" builds on ClusterIP and allocates a
+ port on every node which routes to the clusterIP. "LoadBalancer"
+ builds on NodePort and creates an external load-balancer (if
+ supported in the current cloud) which routes to the clusterIP.
+ More info: https://kubernetes.io/docs/concepts/services-networking/service/#publishing-services---service-types'
+ type: string
+ type: object
+ serviceAccount:
+ description: ServiceAccount defines Jenkins master service account
+ attributes
+ properties:
+ annotations:
+ additionalProperties:
+ type: string
+ description: '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'
+ type: object
+ type: object
+ slaveService:
+ description: 'Service is Kubernetes service of Jenkins slave pods
+ Defaults to : port: 50000 type: ClusterIP'
+ properties:
+ annotations:
+ additionalProperties:
+ type: string
+ description: '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'
+ type: object
+ labels:
+ additionalProperties:
+ type: string
+ description: 'Route service traffic to pods with label keys and
+ values matching this selector. If empty or not present, the
+ service is assumed to have an external process managing its
+ endpoints, which Kubernetes will not modify. Only applies to
+ types ClusterIP, NodePort, and LoadBalancer. Ignored if type
+ is ExternalName. More info: https://kubernetes.io/docs/concepts/services-networking/service/'
+ type: object
+ loadBalancerIP:
+ description: 'Only applies to Service Type: LoadBalancer LoadBalancer
+ will get created with the IP specified in this field. This feature
+ depends on whether the underlying cloud-provider supports specifying
+ the loadBalancerIP when a load balancer is created. This field
+ will be ignored if the cloud-provider does not support the feature.'
+ type: string
+ loadBalancerSourceRanges:
+ description: 'If specified and supported by the platform, this
+ will restrict traffic through the cloud-provider load-balancer
+ will be restricted to the specified client IPs. This field will
+ be ignored if the cloud-provider does not support the feature."
+ More info: https://kubernetes.io/docs/tasks/administer-cluster/securing-a-cluster/#restricting-cloud-metadata-api-access'
+ items:
+ type: string
+ type: array
+ nodePort:
+ description: 'The port on each node on which this service is exposed
+ when type=NodePort or LoadBalancer. Usually assigned by the
+ system. If specified, it will be allocated to the service if
+ unused or else creation of the service will fail. Default is
+ to auto-allocate a port if the ServiceType of this Service requires
+ one. More info: https://kubernetes.io/docs/concepts/services-networking/service/#type-nodeport'
+ format: int32
+ type: integer
+ port:
+ description: 'The port that are exposed by this service. More
+ info: https://kubernetes.io/docs/concepts/services-networking/service/#virtual-ips-and-service-proxies'
+ format: int32
+ type: integer
+ type:
+ description: 'Type determines how the Service is exposed. Defaults
+ to ClusterIP. Valid options are ExternalName, ClusterIP, NodePort,
+ and LoadBalancer. "ExternalName" maps to the specified externalName.
+ "ClusterIP" allocates a cluster-internal IP address for load-balancing
+ to endpoints. Endpoints are determined by the selector or if
+ that is not specified, by manual construction of an Endpoints
+ object. If clusterIP is "None", no virtual IP is allocated and
+ the endpoints are published as a set of endpoints rather than
+ a stable IP. "NodePort" builds on ClusterIP and allocates a
+ port on every node which routes to the clusterIP. "LoadBalancer"
+ builds on NodePort and creates an external load-balancer (if
+ supported in the current cloud) which routes to the clusterIP.
+ More info: https://kubernetes.io/docs/concepts/services-networking/service/#publishing-services---service-types'
+ type: string
+ type: object
+ required:
+ - jenkinsAPISettings
+ - master
+ type: object
+ status:
+ description: Status defines the observed state of Jenkins
+ properties:
+ appliedGroovyScripts:
+ description: AppliedGroovyScripts is a list with all applied groovy
+ scripts in Jenkins by the operator
+ items:
+ description: AppliedGroovyScript is the applied groovy script in
+ Jenkins by the operator.
+ properties:
+ configurationType:
+ description: ConfigurationType is the name of the configuration
+ type(base-groovy, user-groovy, user-casc)
+ type: string
+ hash:
+ description: Hash is the hash of the groovy script and secrets
+ which it uses
+ type: string
+ name:
+ description: Name is the name of the groovy script
+ type: string
+ source:
+ description: Source is the name of source where is located groovy
+ script
+ type: string
+ required:
+ - configurationType
+ - hash
+ - name
+ - source
+ type: object
+ type: array
+ backupDoneBeforePodDeletion:
+ description: BackupDoneBeforePodDeletion tells if backup before pod
+ deletion has been made
+ type: boolean
+ baseConfigurationCompletedTime:
+ description: BaseConfigurationCompletedTime is a time when Jenkins
+ base configuration phase has been completed
+ format: date-time
+ type: string
+ createdSeedJobs:
+ description: CreatedSeedJobs contains list of seed job id already
+ created in Jenkins
+ items:
+ type: string
+ type: array
+ lastBackup:
+ description: LastBackup is the latest backup number
+ format: int64
+ type: integer
+ operatorVersion:
+ description: OperatorVersion is the operator version which manages
+ this CR
+ type: string
+ pendingBackup:
+ description: PendingBackup is the pending backup number
+ format: int64
+ type: integer
+ provisionStartTime:
+ description: ProvisionStartTime is a time when Jenkins master pod
+ has been created
+ format: date-time
+ type: string
+ restoredBackup:
+ description: RestoredBackup is the restored backup number after Jenkins
+ master pod restart
+ format: int64
+ type: integer
+ userAndPasswordHash:
+ description: UserAndPasswordHash is a SHA256 hash made from user and
+ password
+ type: string
+ userConfigurationCompletedTime:
+ description: UserConfigurationCompletedTime is a time when Jenkins
+ user configuration phase has been completed
+ format: date-time
+ type: string
+ type: object
+ type: object
+ served: true
+ storage: true
+ subresources:
+ status: {}
+status:
+ acceptedNames:
+ kind: ""
+ plural: ""
+ conditions: []
+ storedVersions: []
diff --git a/config/crd/kustomization.yaml b/config/crd/kustomization.yaml
new file mode 100644
index 00000000..73fdb226
--- /dev/null
+++ b/config/crd/kustomization.yaml
@@ -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
diff --git a/config/crd/kustomizeconfig.yaml b/config/crd/kustomizeconfig.yaml
new file mode 100644
index 00000000..ec5c150a
--- /dev/null
+++ b/config/crd/kustomizeconfig.yaml
@@ -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
diff --git a/config/crd/patches/cainjection_in_jenkins.yaml b/config/crd/patches/cainjection_in_jenkins.yaml
new file mode 100644
index 00000000..aa6b4a50
--- /dev/null
+++ b/config/crd/patches/cainjection_in_jenkins.yaml
@@ -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
diff --git a/config/crd/patches/webhook_in_jenkins.yaml b/config/crd/patches/webhook_in_jenkins.yaml
new file mode 100644
index 00000000..f34b8ecc
--- /dev/null
+++ b/config/crd/patches/webhook_in_jenkins.yaml
@@ -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
diff --git a/config/default/kustomization.yaml b/config/default/kustomization.yaml
new file mode 100644
index 00000000..d903f4cf
--- /dev/null
+++ b/config/default/kustomization.yaml
@@ -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
diff --git a/config/default/manager_auth_proxy_patch.yaml b/config/default/manager_auth_proxy_patch.yaml
new file mode 100644
index 00000000..28a07150
--- /dev/null
+++ b/config/default/manager_auth_proxy_patch.yaml
@@ -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"
diff --git a/config/default/manager_config_patch.yaml b/config/default/manager_config_patch.yaml
new file mode 100644
index 00000000..4043379a
--- /dev/null
+++ b/config/default/manager_config_patch.yaml
@@ -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
diff --git a/config/manager/controller_manager_config.yaml b/config/manager/controller_manager_config.yaml
new file mode 100644
index 00000000..e8205453
--- /dev/null
+++ b/config/manager/controller_manager_config.yaml
@@ -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
diff --git a/config/manager/kustomization.yaml b/config/manager/kustomization.yaml
new file mode 100644
index 00000000..5e793dd1
--- /dev/null
+++ b/config/manager/kustomization.yaml
@@ -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
diff --git a/config/manager/manager.yaml b/config/manager/manager.yaml
new file mode 100644
index 00000000..e0c31927
--- /dev/null
+++ b/config/manager/manager.yaml
@@ -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
diff --git a/config/prometheus/kustomization.yaml b/config/prometheus/kustomization.yaml
new file mode 100644
index 00000000..ed137168
--- /dev/null
+++ b/config/prometheus/kustomization.yaml
@@ -0,0 +1,2 @@
+resources:
+- monitor.yaml
diff --git a/config/prometheus/monitor.yaml b/config/prometheus/monitor.yaml
new file mode 100644
index 00000000..713c6413
--- /dev/null
+++ b/config/prometheus/monitor.yaml
@@ -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
diff --git a/config/rbac/auth_proxy_client_clusterrole.yaml b/config/rbac/auth_proxy_client_clusterrole.yaml
new file mode 100644
index 00000000..bd4af137
--- /dev/null
+++ b/config/rbac/auth_proxy_client_clusterrole.yaml
@@ -0,0 +1,7 @@
+apiVersion: rbac.authorization.k8s.io/v1
+kind: ClusterRole
+metadata:
+ name: metrics-reader
+rules:
+- nonResourceURLs: ["/metrics"]
+ verbs: ["get"]
diff --git a/config/rbac/auth_proxy_role.yaml b/config/rbac/auth_proxy_role.yaml
new file mode 100644
index 00000000..618f5e41
--- /dev/null
+++ b/config/rbac/auth_proxy_role.yaml
@@ -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"]
diff --git a/config/rbac/auth_proxy_role_binding.yaml b/config/rbac/auth_proxy_role_binding.yaml
new file mode 100644
index 00000000..95543354
--- /dev/null
+++ b/config/rbac/auth_proxy_role_binding.yaml
@@ -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
diff --git a/config/rbac/auth_proxy_service.yaml b/config/rbac/auth_proxy_service.yaml
new file mode 100644
index 00000000..022ddfd9
--- /dev/null
+++ b/config/rbac/auth_proxy_service.yaml
@@ -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
diff --git a/config/rbac/jenkins_editor_role.yaml b/config/rbac/jenkins_editor_role.yaml
new file mode 100644
index 00000000..b9ccf6ab
--- /dev/null
+++ b/config/rbac/jenkins_editor_role.yaml
@@ -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
diff --git a/config/rbac/jenkins_viewer_role.yaml b/config/rbac/jenkins_viewer_role.yaml
new file mode 100644
index 00000000..a4c1ccb1
--- /dev/null
+++ b/config/rbac/jenkins_viewer_role.yaml
@@ -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
diff --git a/config/rbac/kustomization.yaml b/config/rbac/kustomization.yaml
new file mode 100644
index 00000000..66c28338
--- /dev/null
+++ b/config/rbac/kustomization.yaml
@@ -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
diff --git a/config/rbac/leader_election_role.yaml b/config/rbac/leader_election_role.yaml
new file mode 100644
index 00000000..96f0afa7
--- /dev/null
+++ b/config/rbac/leader_election_role.yaml
@@ -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
diff --git a/config/rbac/leader_election_role_binding.yaml b/config/rbac/leader_election_role_binding.yaml
new file mode 100644
index 00000000..b4857aa3
--- /dev/null
+++ b/config/rbac/leader_election_role_binding.yaml
@@ -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
diff --git a/config/rbac/role.yaml b/config/rbac/role.yaml
new file mode 100644
index 00000000..c0d31da6
--- /dev/null
+++ b/config/rbac/role.yaml
@@ -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
diff --git a/config/rbac/role_binding.yaml b/config/rbac/role_binding.yaml
new file mode 100644
index 00000000..1d8f4c44
--- /dev/null
+++ b/config/rbac/role_binding.yaml
@@ -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
diff --git a/config/samples/jenkins.io_v1alpha2_jenkins.yaml b/config/samples/jenkins.io_v1alpha2_jenkins.yaml
new file mode 100644
index 00000000..c44357b6
--- /dev/null
+++ b/config/samples/jenkins.io_v1alpha2_jenkins.yaml
@@ -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
diff --git a/config/samples/kustomization.yaml b/config/samples/kustomization.yaml
new file mode 100644
index 00000000..3eafa3e2
--- /dev/null
+++ b/config/samples/kustomization.yaml
@@ -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
diff --git a/config/scorecard/bases/config.yaml b/config/scorecard/bases/config.yaml
new file mode 100644
index 00000000..c7704784
--- /dev/null
+++ b/config/scorecard/bases/config.yaml
@@ -0,0 +1,7 @@
+apiVersion: scorecard.operatorframework.io/v1alpha3
+kind: Configuration
+metadata:
+ name: config
+stages:
+- parallel: true
+ tests: []
diff --git a/config/scorecard/kustomization.yaml b/config/scorecard/kustomization.yaml
new file mode 100644
index 00000000..d73509ee
--- /dev/null
+++ b/config/scorecard/kustomization.yaml
@@ -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
diff --git a/config/scorecard/patches/basic.config.yaml b/config/scorecard/patches/basic.config.yaml
new file mode 100644
index 00000000..d4347861
--- /dev/null
+++ b/config/scorecard/patches/basic.config.yaml
@@ -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
diff --git a/config/scorecard/patches/olm.config.yaml b/config/scorecard/patches/olm.config.yaml
new file mode 100644
index 00000000..890bf057
--- /dev/null
+++ b/config/scorecard/patches/olm.config.yaml
@@ -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
diff --git a/pkg/controller/jenkins/handler.go b/controllers/handler.go
similarity index 80%
rename from pkg/controller/jenkins/handler.go
rename to controllers/handler.go
index e03a909f..627d01f9 100644
--- a/pkg/controller/jenkins/handler.go
+++ b/controllers/handler.go
@@ -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)
}
diff --git a/pkg/controller/jenkins/jenkins_controller.go b/controllers/jenkins_controller.go
similarity index 76%
rename from pkg/controller/jenkins/jenkins_controller.go
rename to controllers/jenkins_controller.go
index 5be62cde..875250f2 100644
--- a/pkg/controller/jenkins/jenkins_controller.go
+++ b/controllers/jenkins_controller.go
@@ -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
-}
diff --git a/go.mod b/go.mod
index cb297196..a274cd35 100644
--- a/go.mod
+++ b/go.mod
@@ -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
)
diff --git a/go.sum b/go.sum
index 3c47e83a..3d7039d2 100644
--- a/go.sum
+++ b/go.sum
@@ -1,170 +1,190 @@
bazil.org/fuse v0.0.0-20160811212531-371fbbdaa898/go.mod h1:Xbm+BRKSBEpa4q4hTSxohYNQpsxXPbPry4JJWOB3LB8=
-bitbucket.org/bertimus9/systemstat v0.0.0-20180207000608-0eeff89b0690/go.mod h1:Ulb78X89vxKYgdL24HMTiXYHlyHEvruOj1ZPlqeNEZM=
-bou.ke/monkey v1.0.1/go.mod h1:FgHuK96Rv2Nlf+0u1OOVDpCMdsWyOFmeeketDHE7LIg=
cloud.google.com/go v0.26.0/go.mod h1:aQUYkXzVsufM+DwF1aE+0xfcU+56JwCaLick0ClmMTw=
cloud.google.com/go v0.34.0/go.mod h1:aQUYkXzVsufM+DwF1aE+0xfcU+56JwCaLick0ClmMTw=
cloud.google.com/go v0.37.4/go.mod h1:NHPJ89PdicEuT9hdPXMROBD91xc5uRDxsMtSB16k7hw=
-cloud.google.com/go v0.38.0 h1:ROfEUZz+Gh5pa62DJWXSaonyu3StP6EA6lPEXPI6mCo=
cloud.google.com/go v0.38.0/go.mod h1:990N+gfupTy94rShfmMCWGDn0LpTmnzTp2qbd1dvSRU=
+cloud.google.com/go v0.44.1/go.mod h1:iSa0KzasP4Uvy3f1mN/7PiObzGgflwredwwASm/v6AU=
+cloud.google.com/go v0.44.2/go.mod h1:60680Gw3Yr4ikxnPRS/oxxkBccT6SA1yMk63TGekxKY=
+cloud.google.com/go v0.45.1/go.mod h1:RpBamKRgapWJb87xiFSdk4g1CME7QZg3uwTez+TSTjc=
+cloud.google.com/go v0.46.3/go.mod h1:a6bKKbmY7er1mI7TEI4lsAkts/mkhTSZK8w33B4RAg0=
+cloud.google.com/go v0.50.0/go.mod h1:r9sluTvynVuxRIOHXQEHMFffphuXHOMZMycpNR5e6To=
+cloud.google.com/go v0.51.0 h1:PvKAVQWCtlGUSlZkGW3QLelKaWq7KYv/MW1EboG8bfM=
+cloud.google.com/go v0.51.0/go.mod h1:hWtGJ6gnXH+KgDv+V0zFGDvpi07n3z8ZNj3T1RW0Gcw=
+cloud.google.com/go v0.52.0/go.mod h1:pXajvRH/6o3+F9jDHZWQ5PbGhn+o8w9qiu/CffaVdO4=
+cloud.google.com/go v0.53.0/go.mod h1:fp/UouUEsRkN6ryDKNW/Upv/JBKnv6WDthjR6+vze6M=
+cloud.google.com/go v0.54.0 h1:3ithwDMr7/3vpAMXiH+ZQnYbuIsh+OPhUPMFC9enmn0=
+cloud.google.com/go v0.54.0/go.mod h1:1rq2OEkV3YMf6n/9ZvGWI3GWw0VoqH/1x2nd8Is/bPc=
+cloud.google.com/go/bigquery v1.0.1/go.mod h1:i/xbL2UlR5RvWAURpBYZTtm/cXjCha9lbfbpx4poX+o=
+cloud.google.com/go/bigquery v1.3.0/go.mod h1:PjpwJnslEMmckchkHFfq+HTD2DmtT67aNFKH1/VBDHE=
+cloud.google.com/go/bigquery v1.4.0/go.mod h1:S8dzgnTigyfTmLBfrtrhyYhwRxG72rYxvftPBK2Dvzc=
+cloud.google.com/go/datastore v1.0.0/go.mod h1:LXYbyblFSglQ5pkeyhO+Qmw7ukd3C+pD7TKLgZqpHYE=
+cloud.google.com/go/datastore v1.1.0/go.mod h1:umbIZjpQpHh4hmRpGhH4tLFup+FVzqBi1b3c64qFpCk=
+cloud.google.com/go/firestore v1.1.0/go.mod h1:ulACoGHTpvq5r8rxGJ4ddJZBZqakUQqClKRT5SZwBmk=
+cloud.google.com/go/pubsub v1.0.1/go.mod h1:R0Gpsv3s54REJCy4fxDixWD93lHJMoZTyQ2kNxGRt3I=
+cloud.google.com/go/pubsub v1.1.0/go.mod h1:EwwdRX2sKPjnvnqCa270oGRyludottCI76h+R3AArQw=
+cloud.google.com/go/pubsub v1.2.0/go.mod h1:jhfEVHT8odbXTkndysNHCcx0awwzvfOlguIAii9o8iA=
+cloud.google.com/go/storage v1.0.0/go.mod h1:IhtSnM/ZTZV8YYJWCY8RULGVqBDmpoyjwiyrjsg+URw=
+cloud.google.com/go/storage v1.5.0/go.mod h1:tpKbwo567HUNpVclU5sGELwQWBDZ8gh0ZeosJ0Rtdos=
+cloud.google.com/go/storage v1.6.0/go.mod h1:N7U0C8pVQ/+NIKOBQyamJIeKQKkZ+mxpohlUTyfDhBk=
+dmitri.shuralyov.com/gpu/mtl v0.0.0-20190408044501-666a987793e9/go.mod h1:H6x//7gZCb22OMCxBHrMx7a5I7Hp++hsVxbQ4BYO7hU=
github.com/Azure/azure-sdk-for-go v16.2.1+incompatible/go.mod h1:9XXNKU+eRnpl9moKnB4QOLf1HestfXbmab5FXxiDBjc=
-github.com/Azure/azure-sdk-for-go v32.5.0+incompatible/go.mod h1:9XXNKU+eRnpl9moKnB4QOLf1HestfXbmab5FXxiDBjc=
github.com/Azure/go-ansiterm v0.0.0-20170929234023-d6e3b3328b78/go.mod h1:LmzpDX56iTiv29bbRTIsUNlaFfuhWRQBWjQdVyAevI8=
-github.com/Azure/go-autorest v12.2.0+incompatible h1:2Fxszbg492oAJrcvJlgyVaTqnQYRkxmEK6VPCLLVpBI=
-github.com/Azure/go-autorest v12.2.0+incompatible/go.mod h1:r+4oMnoxhatjLLJ6zxSWATqVooLgysK6ZNox3g/xq24=
-github.com/Azure/go-autorest/autorest v0.9.0 h1:MRvx8gncNaXJqOoLmhNjUAKh33JJF8LyxPhomEtOsjs=
+github.com/Azure/go-autorest v10.8.1+incompatible/go.mod h1:r+4oMnoxhatjLLJ6zxSWATqVooLgysK6ZNox3g/xq24=
+github.com/Azure/go-autorest v14.2.0+incompatible h1:V5VMDjClD3GiElqLWO7mz2MxNAK/vTfRHdAubSIPRgs=
+github.com/Azure/go-autorest v14.2.0+incompatible/go.mod h1:r+4oMnoxhatjLLJ6zxSWATqVooLgysK6ZNox3g/xq24=
github.com/Azure/go-autorest/autorest v0.9.0/go.mod h1:xyHB1BMZT0cuDHU7I0+g046+BFDTQ8rEZB0s4Yfa6bI=
-github.com/Azure/go-autorest/autorest/adal v0.5.0 h1:q2gDruN08/guU9vAjuPWff0+QIrpH6ediguzdAzXAUU=
+github.com/Azure/go-autorest/autorest v0.9.6 h1:5YWtOnckcudzIw8lPPBcWOnmIFWMtHci1ZWAZulMSx0=
+github.com/Azure/go-autorest/autorest v0.9.6/go.mod h1:/FALq9T/kS7b5J5qsQ+RSTUdAmGFqi0vUdVNNx8q630=
+github.com/Azure/go-autorest/autorest v0.11.1 h1:eVvIXUKiTgv++6YnWb42DUA1YL7qDugnKP0HljexdnQ=
+github.com/Azure/go-autorest/autorest v0.11.1/go.mod h1:JFgpikqFJ/MleTTxwepExTKnFUKKszPS8UavbQYUMuw=
github.com/Azure/go-autorest/autorest/adal v0.5.0/go.mod h1:8Z9fGy2MpX0PvDjB1pEgQTmVqjGhiHBW7RJJEciWzS0=
-github.com/Azure/go-autorest/autorest/date v0.1.0 h1:YGrhWfrgtFs84+h0o46rJrlmsZtyZRg470CqAXTZaGM=
+github.com/Azure/go-autorest/autorest/adal v0.8.2 h1:O1X4oexUxnZCaEUGsvMnr8ZGj8HI37tNezwY4npRqA0=
+github.com/Azure/go-autorest/autorest/adal v0.8.2/go.mod h1:ZjhuQClTqx435SRJ2iMlOxPYt3d2C/T/7TiQCVZSn3Q=
+github.com/Azure/go-autorest/autorest/adal v0.9.0/go.mod h1:/c022QCutn2P7uY+/oQWWNcK9YU+MH96NgK+jErpbcg=
+github.com/Azure/go-autorest/autorest/adal v0.9.5 h1:Y3bBUV4rTuxenJJs41HU3qmqsb+auo+a3Lz+PlJPpL0=
+github.com/Azure/go-autorest/autorest/adal v0.9.5/go.mod h1:B7KF7jKIeC9Mct5spmyCB/A8CG/sEz1vwIRGv/bbw7A=
github.com/Azure/go-autorest/autorest/date v0.1.0/go.mod h1:plvfp3oPSKwf2DNjlBjWF/7vwR+cUD/ELuzDCXwHUVA=
+github.com/Azure/go-autorest/autorest/date v0.2.0 h1:yW+Zlqf26583pE43KhfnhFcdmSWlm5Ew6bxipnr/tbM=
+github.com/Azure/go-autorest/autorest/date v0.2.0/go.mod h1:vcORJHLJEh643/Ioh9+vPmf1Ij9AEBM5FuBIXLmIy0g=
+github.com/Azure/go-autorest/autorest/date v0.3.0 h1:7gUk1U5M/CQbp9WoqinNzJar+8KY+LPI6wiWrP/myHw=
+github.com/Azure/go-autorest/autorest/date v0.3.0/go.mod h1:BI0uouVdmngYNUzGWeSYnokU+TrmwEsOqdt8Y6sso74=
github.com/Azure/go-autorest/autorest/mocks v0.1.0/go.mod h1:OTyCOPRA2IgIlWxVYxBee2F5Gr4kF2zd2J5cFRaIDN0=
github.com/Azure/go-autorest/autorest/mocks v0.2.0/go.mod h1:OTyCOPRA2IgIlWxVYxBee2F5Gr4kF2zd2J5cFRaIDN0=
-github.com/Azure/go-autorest/autorest/to v0.2.0/go.mod h1:GunWKJp1AEqgMaGLV+iocmRAJWqST1wQYhyyjXJ3SJc=
-github.com/Azure/go-autorest/autorest/validation v0.1.0/go.mod h1:Ha3z/SqBeaalWQvokg3NZAlQTalVMtOIAs1aGK7G6u8=
+github.com/Azure/go-autorest/autorest/mocks v0.3.0 h1:qJumjCaCudz+OcqE9/XtEPfvtOjOmKaui4EOpFI6zZc=
+github.com/Azure/go-autorest/autorest/mocks v0.3.0/go.mod h1:a8FDP3DYzQ4RYfVAxAN3SVSiiO77gL2j2ronKKP0syM=
+github.com/Azure/go-autorest/autorest/mocks v0.4.0/go.mod h1:LTp+uSrOhSkaKrUy935gNZuuIPPVsHlr9DSOxSayd+k=
+github.com/Azure/go-autorest/autorest/mocks v0.4.1/go.mod h1:LTp+uSrOhSkaKrUy935gNZuuIPPVsHlr9DSOxSayd+k=
github.com/Azure/go-autorest/logger v0.1.0 h1:ruG4BSDXONFRrZZJ2GUXDiUyVpayPmb1GnWeHDdaNKY=
github.com/Azure/go-autorest/logger v0.1.0/go.mod h1:oExouG+K6PryycPJfVSxi/koC6LSNgds39diKLz7Vrc=
+github.com/Azure/go-autorest/logger v0.2.0 h1:e4RVHVZKC5p6UANLJHkM4OfR1UKZPj8Wt8Pcx+3oqrE=
+github.com/Azure/go-autorest/logger v0.2.0/go.mod h1:T9E3cAhj2VqvPOtCYAvby9aBXkZmbF5NWuPV8+WeEW8=
github.com/Azure/go-autorest/tracing v0.5.0 h1:TRn4WjSnkcSy5AEG3pnbtFSwNtwzjr4VYyQflFE619k=
github.com/Azure/go-autorest/tracing v0.5.0/go.mod h1:r/s2XiOKccPW3HrqB+W0TQzfbtp2fGCgRFtBroKn4Dk=
-github.com/BurntSushi/toml v0.3.0/go.mod h1:xHWCNGjB5oqiDr8zfno3MHue2Ht5sIBksp03qcyfWMU=
+github.com/Azure/go-autorest/tracing v0.6.0 h1:TYi4+3m5t6K48TGI9AUdb+IzbnSxvnvUMfuitfgcfuo=
+github.com/Azure/go-autorest/tracing v0.6.0/go.mod h1:+vhtPC754Xsa23ID7GlGsrdKBpUA79WCAKPPZVC2DeU=
github.com/BurntSushi/toml v0.3.1 h1:WXkYYl6Yr3qBf1K79EBnL4mak0OimBfB0XUf9Vl28OQ=
github.com/BurntSushi/toml v0.3.1/go.mod h1:xHWCNGjB5oqiDr8zfno3MHue2Ht5sIBksp03qcyfWMU=
-github.com/DATA-DOG/go-sqlmock v1.3.3/go.mod h1:f/Ixk793poVmq4qj/V1dPUg2JEAKC73Q5eFN3EC/SaM=
-github.com/DATA-DOG/go-sqlmock v1.4.1/go.mod h1:f/Ixk793poVmq4qj/V1dPUg2JEAKC73Q5eFN3EC/SaM=
-github.com/Djarvur/go-err113 v0.0.0-20200410182137-af658d038157 h1:hY39LwQHh+1kaovmIjOrlqnXNX6tygSRfLkkK33IkZU=
-github.com/Djarvur/go-err113 v0.0.0-20200410182137-af658d038157/go.mod h1:4UJr5HIiMZrwgkSPdsjy2uOQExX/WEILpIrO9UPGuXs=
-github.com/GoogleCloudPlatform/k8s-cloud-provider v0.0.0-20190822182118-27a4ced34534/go.mod h1:iroGtC8B3tQiqtds1l+mgk/BBOrxbqjH+eUfFQYRc14=
-github.com/JeffAshton/win_pdh v0.0.0-20161109143554-76bb4ee9f0ab/go.mod h1:3VYc5hodBMJ5+l/7J4xAyMeuM2PNuepvHlGs8yilUCA=
+github.com/BurntSushi/xgb v0.0.0-20160522181843-27f122750802/go.mod h1:IVnqGOEym/WlBOVXweHU+Q+/VP0lqqI8lqeDx9IjBqo=
+github.com/DATA-DOG/go-sqlmock v1.5.0/go.mod h1:f/Ixk793poVmq4qj/V1dPUg2JEAKC73Q5eFN3EC/SaM=
+github.com/Knetic/govaluate v3.0.1-0.20171022003610-9aa49832a739+incompatible/go.mod h1:r7JcOSlj0wfOMncg0iLm8Leh48TZaKVeNIfJntJ2wa0=
github.com/MakeNowJust/heredoc v0.0.0-20170808103936-bb23615498cd/go.mod h1:64YHyfSL2R96J44Nlwm39UHepQbyR5q10x7iYa1ks2E=
-github.com/MakeNowJust/heredoc v0.0.0-20171113091838-e9091a26100e/go.mod h1:64YHyfSL2R96J44Nlwm39UHepQbyR5q10x7iYa1ks2E=
github.com/Masterminds/goutils v1.1.0/go.mod h1:8cTjp+g8YejhMuvIA5y2vz3BpJxksy863GQaJW2MFNU=
-github.com/Masterminds/semver v1.5.0/go.mod h1:MB6lktGJrhw8PrUyiEoblNEGEQ+RzHPF078ddwwvV3Y=
-github.com/Masterminds/semver/v3 v3.0.1/go.mod h1:VPu/7SZ7ePZ3QOrcuXROw5FAcLl4a0cBrbBpGY/8hQs=
-github.com/Masterminds/semver/v3 v3.0.3/go.mod h1:VPu/7SZ7ePZ3QOrcuXROw5FAcLl4a0cBrbBpGY/8hQs=
-github.com/Masterminds/sprig/v3 v3.0.0/go.mod h1:NEUY/Qq8Gdm2xgYA+NwJM6wmfdRV9xkh8h/Rld20R0U=
-github.com/Masterminds/sprig/v3 v3.0.2/go.mod h1:oesJ8kPONMONaZgtiHNzUShJbksypC5kWczhZAf6+aU=
-github.com/Masterminds/vcs v1.13.0/go.mod h1:N09YCmOQr6RLxC6UNHzuVwAdodYbbnycGHSmwVJjcKA=
+github.com/Masterminds/semver/v3 v3.1.0/go.mod h1:VPu/7SZ7ePZ3QOrcuXROw5FAcLl4a0cBrbBpGY/8hQs=
+github.com/Masterminds/sprig/v3 v3.1.0/go.mod h1:ONGMf7UfYGAbMXCZmQLy8x3lCDIPrEZE/rU8pmrbihA=
+github.com/Masterminds/squirrel v1.4.0/go.mod h1:yaPeOnPG5ZRwL9oKdTsO/prlkPbXWZlRVMQ/gGlzIuA=
github.com/Masterminds/vcs v1.13.1/go.mod h1:N09YCmOQr6RLxC6UNHzuVwAdodYbbnycGHSmwVJjcKA=
github.com/Microsoft/go-winio v0.4.11/go.mod h1:VhR8bwka0BXejwEJY73c50VrPtXAaKcyvVC4A4RozmA=
-github.com/Microsoft/go-winio v0.4.12/go.mod h1:VhR8bwka0BXejwEJY73c50VrPtXAaKcyvVC4A4RozmA=
github.com/Microsoft/go-winio v0.4.15-0.20190919025122-fc70bd9a86b5/go.mod h1:tTuCMEN+UleMWgg9dVx4Hu52b1bJo+59jBh3ajtinzw=
-github.com/Microsoft/hcsshim v0.0.0-20190417211021-672e52e9209d/go.mod h1:Op3hHsoHPAvb6lceZHDtd9OkTew38wNoXnJs8iY7rUg=
-github.com/Microsoft/hcsshim v0.8.6/go.mod h1:Op3hHsoHPAvb6lceZHDtd9OkTew38wNoXnJs8iY7rUg=
github.com/Microsoft/hcsshim v0.8.7/go.mod h1:OHd7sQqRFrYd3RmSgbgji+ctCwkbq2wbEYNSzOYtcBQ=
-github.com/NYTimes/gziphandler v1.0.1/go.mod h1:3wb06e3pkSAbeQ52E9H9iFoQsEEwGN64994WTCIhntQ=
+github.com/Microsoft/hcsshim v0.8.9/go.mod h1:5692vkUqntj1idxauYlpoINNKeqCiG6Sg38RRsjT5y8=
+github.com/NYTimes/gziphandler v0.0.0-20170623195520-56545f4a5d46/go.mod h1:3wb06e3pkSAbeQ52E9H9iFoQsEEwGN64994WTCIhntQ=
github.com/Nvveen/Gotty v0.0.0-20120604004816-cd527374f1e5/go.mod h1:lmUJ/7eu/Q8D7ML55dXQrVaamCz2vxCfdQBasLZfHKk=
github.com/OneOfOne/xxhash v1.2.2/go.mod h1:HSdplMjZKSmBqAxg5vPj2TmRDmfkzw+cTzAElWljhcU=
-github.com/OpenPeeDeeP/depguard v1.0.1 h1:VlW4R6jmBIv3/u1JNlawEvJMM4J+dPORPaZasQee8Us=
-github.com/OpenPeeDeeP/depguard v1.0.1/go.mod h1:xsIw86fROiiwelg+jB2uM9PiKihMMmUx/1V+TNhjQvM=
+github.com/PuerkitoBio/purell v1.0.0/go.mod h1:c11w/QuzBsJSee3cPx9rAFu61PvFxuPbtSwDGJws/X0=
github.com/PuerkitoBio/purell v1.1.0/go.mod h1:c11w/QuzBsJSee3cPx9rAFu61PvFxuPbtSwDGJws/X0=
github.com/PuerkitoBio/purell v1.1.1 h1:WEQqlqaGbrPkxLJWfBwQmfEAE1Z7ONdDLqrN38tNFfI=
github.com/PuerkitoBio/purell v1.1.1/go.mod h1:c11w/QuzBsJSee3cPx9rAFu61PvFxuPbtSwDGJws/X0=
+github.com/PuerkitoBio/urlesc v0.0.0-20160726150825-5bd2802263f2/go.mod h1:uGdkoq3SwY9Y+13GIhn11/XLaGBb4BfwItxLd5jeuXE=
github.com/PuerkitoBio/urlesc v0.0.0-20170810143723-de5bf2ad4578 h1:d+Bc7a5rLufV/sSk/8dngufqelfh6jnri85riMAaF/M=
github.com/PuerkitoBio/urlesc v0.0.0-20170810143723-de5bf2ad4578/go.mod h1:uGdkoq3SwY9Y+13GIhn11/XLaGBb4BfwItxLd5jeuXE=
-github.com/Rican7/retry v0.1.0/go.mod h1:FgOROf8P5bebcC1DS0PdOQiqGUridaZvikzUmkFW6gg=
github.com/Shopify/logrus-bugsnag v0.0.0-20171204204709-577dee27f20d/go.mod h1:HI8ITrYtUY+O+ZhtlqUnD8+KwNPOyugEhfP9fdUIaEQ=
github.com/Shopify/sarama v1.19.0/go.mod h1:FVkBWblsNy7DGZRfXLU0O9RCGt5g3g3yEuWXgklEdEo=
github.com/Shopify/toxiproxy v2.1.4+incompatible/go.mod h1:OXgGpZ6Cli1/URJOF1DMxUHB2q5Ap20/P/eIdh4G0pI=
-github.com/StackExchange/wmi v0.0.0-20180116203802-5d049714c4a6/go.mod h1:3eOhrUMpNV+6aFIbp5/iudMxNCF27Vw2OZgy4xEx0Fg=
+github.com/VividCortex/gohistogram v1.0.0/go.mod h1:Pf5mBqqDxYaXu3hDrrU+w6nw50o/4+TcAqDqk/vUH7g=
+github.com/afex/hystrix-go v0.0.0-20180502004556-fa1af6a1f4f5/go.mod h1:SkGFH1ia65gfNATL8TAiHDNxPzPdmEL5uirI2Uyuz6c=
+github.com/agnivade/levenshtein v1.0.1/go.mod h1:CURSv5d9Uaml+FovSIICkLbAUZ9S4RqaHDIsdSBg7lM=
github.com/alecthomas/template v0.0.0-20160405071501-a0175ee3bccc/go.mod h1:LOuyumcjzFXgccqObfd/Ljyb9UuFJ6TxHnclSeseNhc=
+github.com/alecthomas/template v0.0.0-20190718012654-fb15b899a751 h1:JYp7IbQjafoB+tBA3gMyHYHrpOtNuDiK/uB5uXxq5wM=
github.com/alecthomas/template v0.0.0-20190718012654-fb15b899a751/go.mod h1:LOuyumcjzFXgccqObfd/Ljyb9UuFJ6TxHnclSeseNhc=
github.com/alecthomas/units v0.0.0-20151022065526-2efee857e7cf/go.mod h1:ybxpYRFXyAe+OPACYpWeL0wqObRcbAqCMya13uyzqw0=
+github.com/alecthomas/units v0.0.0-20190717042225-c3de453c63f4 h1:Hs82Z41s6SdL1CELW+XaDYmOH4hkBN4/N9og/AsOv7E=
github.com/alecthomas/units v0.0.0-20190717042225-c3de453c63f4/go.mod h1:ybxpYRFXyAe+OPACYpWeL0wqObRcbAqCMya13uyzqw0=
+github.com/andreyvit/diff v0.0.0-20170406064948-c7f18ee00883/go.mod h1:rCTlJbsFo29Kk6CurOXKm700vrz8f0KW0JNfpkRJY/8=
github.com/antihax/optional v0.0.0-20180407024304-ca021399b1a6/go.mod h1:V8iCPQYkqmusNa815XgQio277wI47sdRh1dUOLdyC6Q=
github.com/apache/thrift v0.12.0/go.mod h1:cp2SuWMxlEZw2r+iP2GNCdIi4C1qmUzdZFSVb+bacwQ=
+github.com/apache/thrift v0.13.0/go.mod h1:cp2SuWMxlEZw2r+iP2GNCdIi4C1qmUzdZFSVb+bacwQ=
github.com/armon/circbuf v0.0.0-20150827004946-bbbad097214e/go.mod h1:3U/XgcO3hCbHZ8TKRvWD2dDTCfh9M9ya+I9JpbB7O8o=
github.com/armon/consul-api v0.0.0-20180202201655-eb2c6b5be1b6/go.mod h1:grANhF5doyWs3UAsr3K4I6qtAmlQcZDesFNEHPZAzj8=
+github.com/armon/go-metrics v0.0.0-20180917152333-f0300d1749da/go.mod h1:Q73ZrmVTwzkszR9V5SSuryQ31EELlFMUz1kKyl939pY=
+github.com/armon/go-radix v0.0.0-20180808171621-7fddfc383310/go.mod h1:ufUuZ+zHj4x4TnLV4JWEpy2hxWSpsRywHrMgIH9cCH8=
+github.com/aryann/difflib v0.0.0-20170710044230-e206f873d14a/go.mod h1:DAHtR1m6lCRdSC2Tm3DSWRPvIPr6xNKyeHdqDQSQT+A=
github.com/asaskevich/govalidator v0.0.0-20180720115003-f9ffefc3facf/go.mod h1:lB+ZfQJz7igIIfQNfa7Ml4HSf2uFQQRzpGGRXenZAgY=
github.com/asaskevich/govalidator v0.0.0-20190424111038-f61b66f89f4a/go.mod h1:lB+ZfQJz7igIIfQNfa7Ml4HSf2uFQQRzpGGRXenZAgY=
-github.com/asaskevich/govalidator v0.0.0-20200108200545-475eaeb16496/go.mod h1:oGkLhpf+kjZl6xBf758TQhh5XrAeiJv/7FRz/2spLIg=
-github.com/auth0/go-jwt-middleware v0.0.0-20170425171159-5493cabe49f7/go.mod h1:LWMyo4iOLWXHGdBki7NIht1kHru/0wM179h+d3g8ATM=
+github.com/asaskevich/govalidator v0.0.0-20200428143746-21a406dcc535/go.mod h1:oGkLhpf+kjZl6xBf758TQhh5XrAeiJv/7FRz/2spLIg=
+github.com/aws/aws-lambda-go v1.13.3/go.mod h1:4UKl9IzQMoD+QF79YdCuzCwp8VbmG4VAQwij/eHl5CU=
github.com/aws/aws-sdk-go v1.15.11/go.mod h1:mFuSZ37Z9YOHbQEwBWztmVzqXrEkub65tZoCYDt7FT0=
-github.com/aws/aws-sdk-go v1.16.26/go.mod h1:KmX6BPdI08NWTb3/sm4ZGu5ShLoqVDhKgpiN924inxo=
github.com/aws/aws-sdk-go v1.17.7/go.mod h1:KmX6BPdI08NWTb3/sm4ZGu5ShLoqVDhKgpiN924inxo=
-github.com/bazelbuild/bazel-gazelle v0.0.0-20181012220611-c728ce9f663e/go.mod h1:uHBSeeATKpVazAACZBDPL/Nk/UhQDDsJWDlqYJo8/Us=
-github.com/bazelbuild/buildtools v0.0.0-20180226164855-80c7f0d45d7e/go.mod h1:5JP0TXzWDHXv8qvxRC4InIazwdyDseBDbzESUMKk1yU=
+github.com/aws/aws-sdk-go v1.27.0/go.mod h1:KmX6BPdI08NWTb3/sm4ZGu5ShLoqVDhKgpiN924inxo=
+github.com/aws/aws-sdk-go-v2 v0.18.0/go.mod h1:JWVYvqSMppoMJC0x5wdwiImzgXTI9FuZwxzkQq9wy+g=
github.com/beorn7/perks v0.0.0-20160804104726-4c0e84591b9a/go.mod h1:Dwedo/Wpr24TaqPxmxbtue+5NUziq4I4S80YR8gNf3Q=
github.com/beorn7/perks v0.0.0-20180321164747-3a771d992973/go.mod h1:Dwedo/Wpr24TaqPxmxbtue+5NUziq4I4S80YR8gNf3Q=
github.com/beorn7/perks v1.0.0/go.mod h1:KWe93zE9D1o94FZ5RNwFwVgaQK1VOXiVxmqh+CedLV8=
github.com/beorn7/perks v1.0.1 h1:VlbKKnNfV8bJzeqoa4cOKqO6bYr3WgKZxO8Z16+hsOM=
github.com/beorn7/perks v1.0.1/go.mod h1:G2ZrVWU2WbWT9wwq4/hrbKbnv/1ERSJQ0ibhJ6rlkpw=
-github.com/bifurcation/mint v0.0.0-20180715133206-93c51c6ce115/go.mod h1:zVt7zX3K/aDCk9Tj+VM7YymsX66ERvzCJzw8rFCX2JU=
+github.com/bgentry/speakeasy v0.1.0/go.mod h1:+zsyZBPWlz7T6j88CTgSN5bM796AkVf0kBD4zp0CCIs=
github.com/bitly/go-hostpool v0.0.0-20171023180738-a3a6125de932/go.mod h1:NOuUCSz6Q9T7+igc/hlvDOUdtWKryOrtFyIVABv/p7k=
github.com/bitly/go-simplejson v0.5.0/go.mod h1:cXHtHw4XUPsvGaxgjIAn8PhEWG9NfngEKAMDJEczWVA=
+github.com/bketelsen/crypt v0.0.3-0.20200106085610-5cbc8cc4026c/go.mod h1:MKsuJmJgSg28kpZDP6UIiPt0e0Oz0kqKNGyRaWEPv84=
github.com/blang/semver v3.1.0+incompatible/go.mod h1:kRBLl5iJ+tD4TcOOxsy/0fnwebNt5EWlYSAyrTnjyyk=
github.com/blang/semver v3.5.0+incompatible/go.mod h1:kRBLl5iJ+tD4TcOOxsy/0fnwebNt5EWlYSAyrTnjyyk=
github.com/blang/semver v3.5.1+incompatible/go.mod h1:kRBLl5iJ+tD4TcOOxsy/0fnwebNt5EWlYSAyrTnjyyk=
github.com/bmizerany/assert v0.0.0-20160611221934-b7ed37b82869/go.mod h1:Ekp36dRnpXw/yCqJaO+ZrUyxD+3VXMFFr56k5XYrpB4=
-github.com/bndr/gojenkins v0.0.0-20181125150310-de43c03cf849 h1:nV1eKvUlIKhH4/atJIT9tpClzSRTs7TIHS/dRY1EQCw=
-github.com/bndr/gojenkins v0.0.0-20181125150310-de43c03cf849/go.mod h1:J2FxlujWW87NJJrdysyctcDllRVYUONGGlHX16134P4=
-github.com/boltdb/bolt v1.3.1/go.mod h1:clJnj/oiGkjum5o1McbSZDSLxVThjynRyGBgiAx27Ps=
-github.com/bombsimon/wsl/v3 v3.0.0 h1:w9f49xQatuaeTJFaNP4SpiWSR5vfT6IstPtM62JjcqA=
-github.com/bombsimon/wsl/v3 v3.0.0/go.mod h1:st10JtZYLE4D5sC7b8xV4zTKZwAQjCH/Hy2Pm1FNZIc=
-github.com/brancz/gojsontoyaml v0.0.0-20190425155809-e8bd32d46b3d/go.mod h1:IyUJYN1gvWjtLF5ZuygmxbnsAyP3aJS6cHzIuZY50B0=
+github.com/bndr/gojenkins v1.0.1 h1:DFIuamRSmXoI/CwB44txuRf8xaHZNejZge/Lui4RYD4=
+github.com/bndr/gojenkins v1.0.1/go.mod h1:J2FxlujWW87NJJrdysyctcDllRVYUONGGlHX16134P4=
github.com/bshuster-repo/logrus-logstash-hook v0.4.1/go.mod h1:zsTqEiSzDgAa/8GZR7E1qaXrhYNDKBYy5/dWPTIflbk=
github.com/bugsnag/bugsnag-go v0.0.0-20141110184014-b1d153021fcd/go.mod h1:2oa8nejYd4cQ/b0hMIopN0lCRxU0bueqREvZLWFrtK8=
-github.com/bugsnag/bugsnag-go v1.5.0/go.mod h1:2oa8nejYd4cQ/b0hMIopN0lCRxU0bueqREvZLWFrtK8=
+github.com/bugsnag/bugsnag-go v1.5.3/go.mod h1:2oa8nejYd4cQ/b0hMIopN0lCRxU0bueqREvZLWFrtK8=
github.com/bugsnag/osext v0.0.0-20130617224835-0dd3f918b21b/go.mod h1:obH5gd0BsqsP2LwDJ9aOkm/6J86V6lyAXCoQWGw3K50=
github.com/bugsnag/panicwrap v0.0.0-20151223152923-e2c28503fcd0/go.mod h1:D/8v3kj0zr8ZAKg1AQ6crr+5VwKN5eIywRkfhyM/+dE=
github.com/bugsnag/panicwrap v1.2.0/go.mod h1:D/8v3kj0zr8ZAKg1AQ6crr+5VwKN5eIywRkfhyM/+dE=
-github.com/caddyserver/caddy v1.0.3/go.mod h1:G+ouvOY32gENkJC+jhgl62TyhvqEsFaDiZ4uw0RzP1E=
-github.com/campoy/embedmd v1.0.0/go.mod h1:oxyr9RCiSXg0M3VJ3ks0UGfp98BpSSGr0kpiX3MzVl8=
-github.com/cenkalti/backoff v2.1.1+incompatible/go.mod h1:90ReRw6GdpyfrHakVjL/QHaoyV4aDUVVkXQJJJ3NXXM=
+github.com/casbin/casbin/v2 v2.1.2/go.mod h1:YcPU1XXisHhLzuxH9coDNf2FbKpjGlbCg3n9yuLkIJQ=
+github.com/cenkalti/backoff v2.2.1+incompatible/go.mod h1:90ReRw6GdpyfrHakVjL/QHaoyV4aDUVVkXQJJJ3NXXM=
github.com/census-instrumentation/opencensus-proto v0.2.1/go.mod h1:f6KPmirojxKA12rnyqOA5BBL4O983OfeGPqjHWSTneU=
-github.com/cespare/prettybench v0.0.0-20150116022406-03b8cfe5406c/go.mod h1:Xe6ZsFhtM8HrDku0pxJ3/Lr51rwykrzgFwpmTzleatY=
github.com/cespare/xxhash v1.1.0 h1:a6HrQnmkObjyL+Gs60czilIUGqrzKutQD6XZog3p+ko=
github.com/cespare/xxhash v1.1.0/go.mod h1:XrSqR1VqqWfGrhpAt58auRo0WTKS1nRRg3ghfAqPWnc=
-github.com/cespare/xxhash/v2 v2.1.0 h1:yTUvW7Vhb89inJ+8irsUqiWjh8iT6sQPZiQzI6ReGkA=
-github.com/cespare/xxhash/v2 v2.1.0/go.mod h1:dgIUBU3pDso/gPgZ1osOZ0iQf77oPR28Tjxl5dIMyVM=
github.com/cespare/xxhash/v2 v2.1.1 h1:6MnRN8NT7+YBpUIWxHtefFZOKTAPgGjpQSxqLNn0+qY=
github.com/cespare/xxhash/v2 v2.1.1/go.mod h1:VGX0DQ3Q6kWi7AoAeZDth3/j3BFtOZR5XLFGgcrjCOs=
github.com/chai2010/gettext-go v0.0.0-20160711120539-c6fed771bfd5/go.mod h1:/iP1qXHoty45bqomnu2LM+VVyAEdWN+vtSHGlQgyxbw=
-github.com/checkpoint-restore/go-criu v0.0.0-20190109184317-bdb7599cd87b/go.mod h1:TrMrLQfeENAPYPRsJuq3jsqdlRh3lvi6trTZJG8+tho=
-github.com/cheekybits/genny v0.0.0-20170328200008-9127e812e1e9/go.mod h1:+tQajlRqAUrPI7DOSpB0XAqZYtQakVtB7wXkRAgjxjQ=
+github.com/chzyer/logex v1.1.10/go.mod h1:+Ywpsq7O8HXn0nuIou7OrIPyXbp3wmkHB+jjWRnGsAI=
+github.com/chzyer/readline v0.0.0-20180603132655-2972be24d48e/go.mod h1:nSuG5e5PlCu98SY8svDHJxuZscDgtXS6KTTbou5AhLI=
+github.com/chzyer/test v0.0.0-20180213035817-a1ea475d72b1/go.mod h1:Q3SI9o4m/ZMnBNeIyt5eFwwo7qiLfzFZmjNmxjkiQlU=
+github.com/clbanning/x2j v0.0.0-20191024224557-825249438eec/go.mod h1:jMjuTZXRI4dUb/I5gc9Hdhagfvm9+RyrPryS/auMzxE=
github.com/client9/misspell v0.3.4/go.mod h1:qj6jICC3Q7zFZvVWo7KLAzC3yx5G7kyvSDkc90ppPyw=
-github.com/cloudflare/cfssl v0.0.0-20180726162950-56268a613adf/go.mod h1:yMWuSON2oQp+43nFtAV/uvKQIFpSPerB57DCt9t8sSA=
-github.com/clusterhq/flocker-go v0.0.0-20160920122132-2b8b7259d313/go.mod h1:P1wt9Z3DP8O6W3rvwCt0REIlshg1InHImaLW0t3ObY0=
+github.com/cncf/udpa/go v0.0.0-20191209042840-269d4d468f6f/go.mod h1:M8M6+tZqaGXZJjfX53e64911xZQV5JYwmTeXPW+k8Sc=
github.com/cockroachdb/apd v1.1.0/go.mod h1:8Sl8LxpKi29FqWXR16WEFZRNSz3SoPzUzeMeY4+DwBQ=
github.com/cockroachdb/cockroach-go v0.0.0-20181001143604-e0a95dfd547c/go.mod h1:XGLbWH/ujMcbPbhZq52Nv6UrCghb1yGn//133kEsvDk=
-github.com/codegangsta/negroni v1.0.0/go.mod h1:v0y3T5G7Y1UlFfyxFn/QLRU4a2EuNau2iZY63YTKWo0=
-github.com/container-storage-interface/spec v1.1.0/go.mod h1:6URME8mwIBbpVyZV93Ce5St17xBiQJQY67NDsuohiy4=
+github.com/cockroachdb/datadriven v0.0.0-20190809214429-80d97fb3cbaa/go.mod h1:zn76sxSg3SzpJ0PPJaLDCu+Bu0Lg3sKTORVIj19EIF8=
+github.com/codahale/hdrhistogram v0.0.0-20161010025455-3a0bb77429bd/go.mod h1:sE/e/2PUdi/liOCUjSTXgM1o87ZssimdTWN964YiIeI=
github.com/containerd/cgroups v0.0.0-20190919134610-bf292b21730f/go.mod h1:OApqhQ4XNSNC13gXIwDjhOQxjWa/NxkwZXJ1EvqT0ko=
-github.com/containerd/console v0.0.0-20170925154832-84eeaae905fa/go.mod h1:Tj/on1eG8kiEhd0+fhSDzsPAFESxzBBvdyEgyryXffw=
github.com/containerd/console v0.0.0-20180822173158-c12b1e7919c1/go.mod h1:Tj/on1eG8kiEhd0+fhSDzsPAFESxzBBvdyEgyryXffw=
-github.com/containerd/containerd v1.0.2/go.mod h1:bC6axHOhabU15QhwfG7w5PipXdVtMXFTttgp+kVtyUA=
github.com/containerd/containerd v1.2.7/go.mod h1:bC6axHOhabU15QhwfG7w5PipXdVtMXFTttgp+kVtyUA=
-github.com/containerd/containerd v1.3.0-beta.2.0.20190823190603-4a2f61c4f2b4/go.mod h1:bC6axHOhabU15QhwfG7w5PipXdVtMXFTttgp+kVtyUA=
github.com/containerd/containerd v1.3.0-beta.2.0.20190828155532-0293cbd26c69/go.mod h1:bC6axHOhabU15QhwfG7w5PipXdVtMXFTttgp+kVtyUA=
-github.com/containerd/containerd v1.3.0/go.mod h1:bC6axHOhabU15QhwfG7w5PipXdVtMXFTttgp+kVtyUA=
github.com/containerd/containerd v1.3.2/go.mod h1:bC6axHOhabU15QhwfG7w5PipXdVtMXFTttgp+kVtyUA=
-github.com/containerd/continuity v0.0.0-20181203112020-004b46473808/go.mod h1:GL3xCUCBDV3CZiTSEKksMWbLE66hEyuu9qyDOOqM47Y=
+github.com/containerd/containerd v1.3.4/go.mod h1:bC6axHOhabU15QhwfG7w5PipXdVtMXFTttgp+kVtyUA=
github.com/containerd/continuity v0.0.0-20190426062206-aaeac12a7ffc/go.mod h1:GL3xCUCBDV3CZiTSEKksMWbLE66hEyuu9qyDOOqM47Y=
github.com/containerd/continuity v0.0.0-20200107194136-26c1120b8d41/go.mod h1:Dq467ZllaHgAtVp4p1xUQWBrFXR9s/wyoTpG8zOJGkY=
+github.com/containerd/continuity v0.0.0-20200413184840-d3ef23f19fbb/go.mod h1:Dq467ZllaHgAtVp4p1xUQWBrFXR9s/wyoTpG8zOJGkY=
github.com/containerd/fifo v0.0.0-20190226154929-a9fb20d87448/go.mod h1:ODA38xgv3Kuk8dQz2ZQXpnv/UZZUHUCL7pnLehbXgQI=
github.com/containerd/go-runc v0.0.0-20180907222934-5a6d9f37cfa3/go.mod h1:IV7qH3hrUgRmyYrtgEeGWJfWbgcHL9CSRruz2Vqcph0=
github.com/containerd/ttrpc v0.0.0-20190828154514-0e0f228740de/go.mod h1:PvCDdDGpgqzQIzDW1TphrGLssLDZp2GuS+X5DkEJB8o=
+github.com/containerd/ttrpc v1.0.1/go.mod h1:UAxOpgT9ziI0gJrmKvgcZivgxOp8iFPSk8httJEt98Y=
github.com/containerd/typeurl v0.0.0-20180627222232-a93fcdb778cd/go.mod h1:Cm3kwCdlkCfMSHURc+r6fwoGH6/F1hH3S4sg0rLFWPc=
-github.com/containerd/typeurl v0.0.0-20190228175220-2a93cfde8c20/go.mod h1:Cm3kwCdlkCfMSHURc+r6fwoGH6/F1hH3S4sg0rLFWPc=
-github.com/containernetworking/cni v0.7.1/go.mod h1:LGwApLUm2FpoOfxTDEeq8T9ipbpZ61X79hmU3w8FmsY=
-github.com/coredns/corefile-migration v1.0.2/go.mod h1:OFwBp/Wc9dJt5cAZzHWMNhK1r5L0p0jDwIBc6j8NC8E=
-github.com/coreos/bbolt v1.3.1-coreos.6/go.mod h1:iRUV2dpdMOn7Bo10OQBFzIJO9kkE559Wcmn+qkEiiKk=
github.com/coreos/bbolt v1.3.2/go.mod h1:iRUV2dpdMOn7Bo10OQBFzIJO9kkE559Wcmn+qkEiiKk=
github.com/coreos/etcd v3.3.10+incompatible/go.mod h1:uF7uidLiAD3TWHmW31ZFd/JWoc32PjwdhPthX9715RE=
-github.com/coreos/etcd v3.3.15+incompatible/go.mod h1:uF7uidLiAD3TWHmW31ZFd/JWoc32PjwdhPthX9715RE=
+github.com/coreos/etcd v3.3.13+incompatible/go.mod h1:uF7uidLiAD3TWHmW31ZFd/JWoc32PjwdhPthX9715RE=
github.com/coreos/go-etcd v2.0.0+incompatible/go.mod h1:Jez6KQU2B/sWsbdaef3ED8NzMklzPG4d5KIOhIy30Tk=
github.com/coreos/go-oidc v2.1.0+incompatible/go.mod h1:CgnwVTmzoESiwO9qyAFEMiHoZ1nMCKZlZ9V6mm3/LKc=
github.com/coreos/go-semver v0.2.0/go.mod h1:nnelYz7RCh+5ahJtPPxZlU+153eP4D4r3EedlOD2RNk=
github.com/coreos/go-semver v0.3.0/go.mod h1:nnelYz7RCh+5ahJtPPxZlU+153eP4D4r3EedlOD2RNk=
github.com/coreos/go-systemd v0.0.0-20180511133405-39ca1b05acc7/go.mod h1:F5haX7vjVVG0kc13fIWeqUViNPyEJxv/OmvnBo0Yme4=
github.com/coreos/go-systemd v0.0.0-20190321100706-95778dfbb74e/go.mod h1:F5haX7vjVVG0kc13fIWeqUViNPyEJxv/OmvnBo0Yme4=
-github.com/coreos/go-systemd v0.0.0-20190620071333-e64a0ec8b42a/go.mod h1:F5haX7vjVVG0kc13fIWeqUViNPyEJxv/OmvnBo0Yme4=
+github.com/coreos/pkg v0.0.0-20160727233714-3ac0863d7acf/go.mod h1:E3G3o1h8I7cfcXa63jLwjI0eiQQMgzzUDFVpN/nH/eA=
github.com/coreos/pkg v0.0.0-20180108230652-97fdf19511ea/go.mod h1:E3G3o1h8I7cfcXa63jLwjI0eiQQMgzzUDFVpN/nH/eA=
github.com/coreos/pkg v0.0.0-20180928190104-399ea9e2e55f/go.mod h1:E3G3o1h8I7cfcXa63jLwjI0eiQQMgzzUDFVpN/nH/eA=
-github.com/coreos/prometheus-operator v0.35.1 h1:Cl/0axMpifGsKTNMwQB5PWciOSkNFa/LOdCVQJ9n/Jc=
-github.com/coreos/prometheus-operator v0.35.1/go.mod h1:XHYZUStZWcwd1yk/1DjZv/fywqKIyAJ6pSwvIr+v9BQ=
-github.com/coreos/rkt v1.30.0/go.mod h1:O634mlH6U7qk87poQifK6M2rsFNt+FyUTWNMnP1hF1U=
github.com/cpuguy83/go-md2man v1.0.10/go.mod h1:SmD6nW6nTyfqj6ABTjUi3V3JVMnlJmwcJI5acqYI6dE=
+github.com/cpuguy83/go-md2man/v2 v2.0.0-20190314233015-f79a8a8ca69d/go.mod h1:maD7wRr/U5Z6m/iR4s+kqSMx2CaBsrgA7czyZG/E6dU=
+github.com/cpuguy83/go-md2man/v2 v2.0.0/go.mod h1:maD7wRr/U5Z6m/iR4s+kqSMx2CaBsrgA7czyZG/E6dU=
github.com/creack/pty v1.1.7/go.mod h1:lj5s0c3V2DBrqTV7llrYr5NG6My20zk30Fl46Y7DoTY=
-github.com/creack/pty v1.1.9/go.mod h1:oKZEueFk5CKHvIhNR5MUki03XCEU+Q6VDXinZuGJ33E=
github.com/cyphar/filepath-securejoin v0.2.2/go.mod h1:FpkQEhXnPnOthhzymB7CGsFk2G9VLXONKD9G7QGMM+4=
github.com/cznic/b v0.0.0-20180115125044-35e9bbe41f07/go.mod h1:URriBxXwVq5ijiJ12C7iIZqlA69nTlI+LgI6/pwftG8=
github.com/cznic/fileutil v0.0.0-20180108211300-6a051e75936f/go.mod h1:8S58EK26zhXSxzv7NQFpnliaOQsmDUxvoQO3rt154Vg=
@@ -180,9 +200,9 @@ github.com/davecgh/go-spew v1.1.0/go.mod h1:J7Y8YcW2NihsgmVo/mv3lAwl/skON4iLHjSs
github.com/davecgh/go-spew v1.1.1 h1:vj9j/u1bqnvCEfJOwUhtlOARqs3+rkHYY13jYWTU97c=
github.com/davecgh/go-spew v1.1.1/go.mod h1:J7Y8YcW2NihsgmVo/mv3lAwl/skON4iLHjSsI+c5H38=
github.com/daviddengcn/go-colortext v0.0.0-20160507010035-511bcaf42ccd/go.mod h1:dv4zxwHi5C/8AeI+4gX4dCWOIvNi7I6JCSX0HvlKPgE=
-github.com/deislabs/oras v0.7.0/go.mod h1:sqMKPG3tMyIX9xwXUBRLhZ24o+uT4y6jgBD2RzUTKDM=
github.com/deislabs/oras v0.8.1/go.mod h1:Mx0rMSbBNaNfY9hjpccEnxkOqJL6KGjtxNHPLC4G4As=
github.com/denisenkom/go-mssqldb v0.0.0-20190515213511-eb9f6a1743f3/go.mod h1:zAg7JM8CkOJ43xKXIj7eRO9kmWm/TW578qo+oDO6tuM=
+github.com/denisenkom/go-mssqldb v0.0.0-20191001013358-cfbb681360f0/go.mod h1:xbL0rPBG9cCiLr28tMa8zpbdarY27NDyej4t/EjAShU=
github.com/denverdino/aliyungo v0.0.0-20190125010748-a747050bb1ba/go.mod h1:dV8lFg6daOBZbT6/BDGIz6Y3WFGn8juu6G+CQ6LHtl0=
github.com/dgrijalva/jwt-go v0.0.0-20170104182250-a601269ab70c/go.mod h1:E3ru+11k8xSBh+hMPgOLZmtrrCbhqsmaPHjLKYnJCaQ=
github.com/dgrijalva/jwt-go v3.2.0+incompatible h1:7qlOGliEKZXTDg6OTjfoBKDXWrumCAMpl/TFQ4/5kLM=
@@ -190,28 +210,26 @@ github.com/dgrijalva/jwt-go v3.2.0+incompatible/go.mod h1:E3ru+11k8xSBh+hMPgOLZm
github.com/dgryski/go-sip13 v0.0.0-20181026042036-e10d5fee7954/go.mod h1:vAd38F8PWV+bWy6jNmig1y/TA+kYO4g3RSRF0IAv0no=
github.com/dhui/dktest v0.3.0/go.mod h1:cyzIUfGsBEbZ6BT7tnXqAShHSXCZhSNmFl70sZ7c1yc=
github.com/dnaeon/go-vcr v1.0.1/go.mod h1:aBB1+wY4s93YsC3HHjMBMrwTj2R9FHDzUr9KyGc8n1E=
-github.com/docker/cli v0.0.0-20190506213505-d88565df0c2d/go.mod h1:JLrzqnKDaYBop7H2jaqPtU4hHvMKP+vjCwu2uszcLI8=
github.com/docker/cli v0.0.0-20200130152716-5d0cf8839492/go.mod h1:JLrzqnKDaYBop7H2jaqPtU4hHvMKP+vjCwu2uszcLI8=
github.com/docker/distribution v0.0.0-20191216044856-a8371794149d/go.mod h1:0+TTO4EOBfRPhZXAeF1Vu+W3hHZ8eLp8PgKVZlcvtFY=
github.com/docker/distribution v2.7.0+incompatible/go.mod h1:J2gT2udsDAN96Uj4KfcMRqY0/ypR+oyYUYmja8H+y+w=
-github.com/docker/distribution v2.7.1-0.20190205005809-0d3efadf0154+incompatible/go.mod h1:J2gT2udsDAN96Uj4KfcMRqY0/ypR+oyYUYmja8H+y+w=
github.com/docker/distribution v2.7.1+incompatible h1:a5mlkVzth6W5A4fOsS3D2EO5BUmsJpcB+cRlLU7cSug=
github.com/docker/distribution v2.7.1+incompatible/go.mod h1:J2gT2udsDAN96Uj4KfcMRqY0/ypR+oyYUYmja8H+y+w=
-github.com/docker/docker-credential-helpers v0.6.1/go.mod h1:WRaJzqw3CTB9bk10avuGsjVBZsD05qeibJ1/TYlvc0Y=
+github.com/docker/docker v0.7.3-0.20190103212154-2b7e084dc98b/go.mod h1:eEKB0N0r5NX/I1kEveEz05bcu8tLC/8azJZsviup8Sk=
+github.com/docker/docker v0.7.3-0.20190327010347-be7ac8be2ae0/go.mod h1:eEKB0N0r5NX/I1kEveEz05bcu8tLC/8azJZsviup8Sk=
+github.com/docker/docker v0.7.3-0.20190817195342-4760db040282/go.mod h1:eEKB0N0r5NX/I1kEveEz05bcu8tLC/8azJZsviup8Sk=
+github.com/docker/docker v1.4.2-0.20200203170920-46ec8731fbce/go.mod h1:eEKB0N0r5NX/I1kEveEz05bcu8tLC/8azJZsviup8Sk=
github.com/docker/docker-credential-helpers v0.6.3/go.mod h1:WRaJzqw3CTB9bk10avuGsjVBZsD05qeibJ1/TYlvc0Y=
-github.com/docker/go-connections v0.3.0/go.mod h1:Gbd7IOopHjR8Iph03tsViu4nIes5XhDvyHbTtUxmeec=
github.com/docker/go-connections v0.4.0/go.mod h1:Gbd7IOopHjR8Iph03tsViu4nIes5XhDvyHbTtUxmeec=
+github.com/docker/go-events v0.0.0-20190806004212-e31b211e4f1c/go.mod h1:Uw6UezgYA44ePAFQYUehOuCzmy5zmg/+nl2ZfMWGkpA=
github.com/docker/go-metrics v0.0.0-20180209012529-399ea8c73916/go.mod h1:/u0gXw0Gay3ceNrsHubL3BtdOL2fHf93USgMTe0W5dI=
-github.com/docker/go-metrics v0.0.0-20181218153428-b84716841b82/go.mod h1:/u0gXw0Gay3ceNrsHubL3BtdOL2fHf93USgMTe0W5dI=
+github.com/docker/go-metrics v0.0.1/go.mod h1:cG1hvH2utMXtqgqqYE9plW6lDxS3/5ayHzueweSI3Vw=
github.com/docker/go-units v0.3.3/go.mod h1:fgPhTUdO+D/Jk86RDLlptpiXQzgHJF7gydDDbaIK4Dk=
github.com/docker/go-units v0.4.0/go.mod h1:fgPhTUdO+D/Jk86RDLlptpiXQzgHJF7gydDDbaIK4Dk=
-github.com/docker/libnetwork v0.0.0-20180830151422-a9cd636e3789/go.mod h1:93m0aTqz6z+g32wla4l4WxTrdtvBRmVzYRkYvasA5Z8=
github.com/docker/libtrust v0.0.0-20150114040149-fa567046d9b1/go.mod h1:cyGadeNEkKy96OOhEzfZl+yxihPEzKnqJwvfuSUqbZE=
github.com/docker/libtrust v0.0.0-20160708172513-aabc10ec26b7/go.mod h1:cyGadeNEkKy96OOhEzfZl+yxihPEzKnqJwvfuSUqbZE=
github.com/docker/spdystream v0.0.0-20160310174837-449fdfce4d96 h1:cenwrSVm+Z7QLSV/BsnenAOcDXdX4cMv4wP0B/5QbPg=
github.com/docker/spdystream v0.0.0-20160310174837-449fdfce4d96/go.mod h1:Qh8CwZgvJUkLughtfhJv5dyTYa91l1fOUCrgjqmcifM=
-github.com/docker/spdystream v0.0.0-20181023171402-6480d4af844c h1:ZfSZ3P3BedhKGUhzj7BQlPSU4OvT6tfOKe3DVHzOA7s=
-github.com/docker/spdystream v0.0.0-20181023171402-6480d4af844c/go.mod h1:Qh8CwZgvJUkLughtfhJv5dyTYa91l1fOUCrgjqmcifM=
github.com/docopt/docopt-go v0.0.0-20180111231733-ee0de3bc6815/go.mod h1:WwZ+bS3ebgob9U8Nd0kOddGdZWjyMGR8Wziv+TBNwSE=
github.com/dustin/go-humanize v0.0.0-20171111073723-bb3d318650d4/go.mod h1:HtrtbFcZ19U5GC7JDqmcUSB87Iq5E25KnS6fMYU6eOk=
github.com/dustin/go-humanize v1.0.0/go.mod h1:HtrtbFcZ19U5GC7JDqmcUSB87Iq5E25KnS6fMYU6eOk=
@@ -219,83 +237,91 @@ github.com/eapache/go-resiliency v1.1.0/go.mod h1:kFI+JgMyC7bLPUVY133qvEBtVayf5m
github.com/eapache/go-xerial-snappy v0.0.0-20180814174437-776d5712da21/go.mod h1:+020luEh2TKB4/GOp8oxxtq0Daoen/Cii55CzbTV6DU=
github.com/eapache/queue v1.1.0/go.mod h1:6eCeP0CKFpHLu8blIFXhExK/dRa7WDZfr6jVFPTqq+I=
github.com/edsrzf/mmap-go v0.0.0-20170320065105-0bce6a688712/go.mod h1:YO35OhQPt3KJa3ryjFM5Bs14WD66h8eGKpfaBNrHW5M=
-github.com/elazarl/goproxy v0.0.0-20170405201442-c4fc26588b6e/go.mod h1:/Zj4wYkgs4iZTTu3o/KG3Itv/qCCa8VVMlb3i9OVuzc=
-github.com/elazarl/goproxy v0.0.0-20190711103511-473e67f1d7d2/go.mod h1:/Zj4wYkgs4iZTTu3o/KG3Itv/qCCa8VVMlb3i9OVuzc=
-github.com/elazarl/goproxy/ext v0.0.0-20190711103511-473e67f1d7d2/go.mod h1:gNh8nYJoAm43RfaxurUnxr+N1PwuFV3ZMl/efxlIlY8=
+github.com/edsrzf/mmap-go v1.0.0/go.mod h1:YO35OhQPt3KJa3ryjFM5Bs14WD66h8eGKpfaBNrHW5M=
+github.com/elazarl/goproxy v0.0.0-20180725130230-947c36da3153 h1:yUdfgN0XgIJw7foRItutHYUIhlcKzcSf5vDpdhQAKTc=
+github.com/elazarl/goproxy v0.0.0-20180725130230-947c36da3153/go.mod h1:/Zj4wYkgs4iZTTu3o/KG3Itv/qCCa8VVMlb3i9OVuzc=
+github.com/emersion/go-sasl v0.0.0-20190704090222-36b50694675c h1:Spm8jy+jWYG/Dn6ygbq/LBW/6M27kg59GK+FkKjexuw=
github.com/emersion/go-sasl v0.0.0-20190704090222-36b50694675c/go.mod h1:G/dpzLu16WtQpBfQ/z3LYiYJn3ZhKSGWn83fyoyQe/k=
-github.com/emersion/go-sasl v0.0.0-20190817083125-240c8404624e h1:ba7YsgX5OV8FjGi5ZWml8Jng6oBrJAb3ahqWMJ5Ce8Q=
-github.com/emersion/go-sasl v0.0.0-20190817083125-240c8404624e/go.mod h1:G/dpzLu16WtQpBfQ/z3LYiYJn3ZhKSGWn83fyoyQe/k=
+github.com/emersion/go-sasl v0.0.0-20200509203442-7bfe0ed36a21 h1:OJyUGMJTzHTd1XQp98QTaHernxMYzRaOasRir9hUlFQ=
+github.com/emersion/go-sasl v0.0.0-20200509203442-7bfe0ed36a21/go.mod h1:iL2twTeMvZnrg54ZoPDNfJaJaqy0xIQFuBdrLsmspwQ=
github.com/emersion/go-smtp v0.11.2 h1:5PO2Kwsx+HXuytntCfMvcworC/iq45TPGkwjnaBZFSg=
github.com/emersion/go-smtp v0.11.2/go.mod h1:byi9Y32SuKwjTJt9DO2tTWYjtF3lEh154tE1AcaJQSY=
+github.com/emersion/go-smtp v0.14.0 h1:RYW203p+EcPjL8Z/ZpT9lZ6iOc8MG1MQzEx1UKEkXlA=
+github.com/emersion/go-smtp v0.14.0/go.mod h1:qm27SGYgoIPRot6ubfQ/GpiPy/g3PaZAVRxiO/sDUgQ=
+github.com/emicklei/go-restful v0.0.0-20170410110728-ff4f55a20633/go.mod h1:otzb+WCGbkyDHkqmQmT5YD2WR4BBwUdeQoFo8l/7tVs=
github.com/emicklei/go-restful v2.9.5+incompatible h1:spTtZBk5DYEvbxMVutUuTyh1Ao2r4iyvLdACqsl/Ljk=
github.com/emicklei/go-restful v2.9.5+incompatible/go.mod h1:otzb+WCGbkyDHkqmQmT5YD2WR4BBwUdeQoFo8l/7tVs=
-github.com/emicklei/go-restful v2.9.6+incompatible/go.mod h1:otzb+WCGbkyDHkqmQmT5YD2WR4BBwUdeQoFo8l/7tVs=
-github.com/emicklei/go-restful v2.11.1+incompatible h1:CjKsv3uWcCMvySPQYKxO8XX3f9zD4FeZRsW4G0B4ffE=
-github.com/emicklei/go-restful v2.11.1+incompatible/go.mod h1:otzb+WCGbkyDHkqmQmT5YD2WR4BBwUdeQoFo8l/7tVs=
+github.com/envoyproxy/go-control-plane v0.6.9/go.mod h1:SBwIajubJHhxtWwsL9s8ss4safvEdbitLhGGK48rN6g=
+github.com/envoyproxy/go-control-plane v0.9.0/go.mod h1:YTl/9mNaCwkRvm6d1a2C3ymFceY/DCBVvsKhRF0iEA4=
github.com/envoyproxy/go-control-plane v0.9.1-0.20191026205805-5f8ba28d4473/go.mod h1:YTl/9mNaCwkRvm6d1a2C3ymFceY/DCBVvsKhRF0iEA4=
+github.com/envoyproxy/go-control-plane v0.9.4/go.mod h1:6rpuAdCZL397s3pYoYcLgu1mIlRU8Am5FuJP05cCM98=
github.com/envoyproxy/protoc-gen-validate v0.1.0/go.mod h1:iSmxcyjqTsJpI2R4NaDN7+kN2VEUnK/pcBlmesArF7c=
-github.com/euank/go-kmsg-parser v2.0.0+incompatible/go.mod h1:MhmAMZ8V4CYH4ybgdRwPr2TU5ThnS43puaKEMpja1uw=
-github.com/evanphx/json-patch v4.1.0+incompatible/go.mod h1:50XU6AFN0ol/bzJsmQLiYLvXMP4fmwYFNcr97nuDLSk=
github.com/evanphx/json-patch v4.2.0+incompatible/go.mod h1:50XU6AFN0ol/bzJsmQLiYLvXMP4fmwYFNcr97nuDLSk=
-github.com/evanphx/json-patch v4.5.0+incompatible h1:ouOWdg56aJriqS0huScTkVXPC5IcNrDCXZ6OoTAWu7M=
github.com/evanphx/json-patch v4.5.0+incompatible/go.mod h1:50XU6AFN0ol/bzJsmQLiYLvXMP4fmwYFNcr97nuDLSk=
+github.com/evanphx/json-patch v4.9.0+incompatible h1:kLcOMZeuLAJvL2BPWLMIj5oaZQobrkAqrL+WFZwQses=
+github.com/evanphx/json-patch v4.9.0+incompatible/go.mod h1:50XU6AFN0ol/bzJsmQLiYLvXMP4fmwYFNcr97nuDLSk=
github.com/exponent-io/jsonpath v0.0.0-20151013193312-d6023ce2651d/go.mod h1:ZZMPRZwes7CROmyNKgQzC3XPs6L/G2EJLHddWejkmf4=
+github.com/facebookgo/ensure v0.0.0-20160127193407-b4ab57deab51 h1:0JZ+dUmQeA8IIVUMzysrX4/AKuQwWhV2dYQuPZdvdSQ=
github.com/facebookgo/ensure v0.0.0-20160127193407-b4ab57deab51/go.mod h1:Yg+htXGokKKdzcwhuNDwVvN+uBxDGXJ7G/VN1d8fa64=
+github.com/facebookgo/stack v0.0.0-20160209184415-751773369052 h1:JWuenKqqX8nojtoVVWjGfOF9635RETekkoH6Cc9SX0A=
github.com/facebookgo/stack v0.0.0-20160209184415-751773369052/go.mod h1:UbMTZqLaRiH3MsBH8va0n7s1pQYcu3uTb8G4tygF4Zg=
+github.com/facebookgo/subset v0.0.0-20150612182917-8dac2c3c4870 h1:E2s37DuLxFhQDg5gKsWoLBOB0n+ZW8s599zru8FJ2/Y=
github.com/facebookgo/subset v0.0.0-20150612182917-8dac2c3c4870/go.mod h1:5tD+neXqOorC30/tWg0LCSkrqj/AR6gu8yY8/fpw1q0=
github.com/fatih/camelcase v1.0.0/go.mod h1:yN2Sb0lFhZJUdVvtELVWefmrXpuZESvPmqwoZc+/fpc=
-github.com/fatih/color v1.6.0/go.mod h1:Zm6kSWBoL9eyXnKyktHP6abPY2pDugNf5KwzbycvMj4=
-github.com/fatih/color v1.7.0 h1:DkWD4oS2D8LGGgTQ6IvwJJXSL5Vp2ffcQg58nFV38Ys=
github.com/fatih/color v1.7.0/go.mod h1:Zm6kSWBoL9eyXnKyktHP6abPY2pDugNf5KwzbycvMj4=
github.com/fatih/structtag v1.1.0/go.mod h1:mBJUNpUnHmRKrKlQQlmCrh5PuhftFbNv8Ys4/aAZl94=
-github.com/flynn/go-shlex v0.0.0-20150515145356-3f9db97f8568/go.mod h1:xEzjJPgXI435gkrCt3MPfRiAkVrwSbHsst4LCFVfpJc=
-github.com/fortytw2/leaktest v1.3.0/go.mod h1:jDsjWgpAGjm2CA7WthBh/CdZYEPF31XHquHwclZch5g=
-github.com/fsnotify/fsnotify v1.4.7 h1:IXs+QLmnXW2CcXuY+8Mzv/fWEsPGWxqefPtCP5CnV9I=
+github.com/form3tech-oss/jwt-go v3.2.2+incompatible h1:TcekIExNqud5crz4xD2pavyTgWiPvpYe4Xau31I0PRk=
+github.com/form3tech-oss/jwt-go v3.2.2+incompatible/go.mod h1:pbq4aXjuKjdthFRnoDwaVPLA+WlJuPGy+QneDUgJi2k=
+github.com/franela/goblin v0.0.0-20200105215937-c9ffbefa60db/go.mod h1:7dvUGVsVBjqR7JHJk0brhHOZYGmfBYOrK0ZhYMEtBr4=
+github.com/franela/goreq v0.0.0-20171204163338-bcd34c9993f8/go.mod h1:ZhphrRTfi2rbfLwlschooIH4+wKKDR4Pdxhh+TRoA20=
github.com/fsnotify/fsnotify v1.4.7/go.mod h1:jwhsz4b93w/PPRr/qN1Yymfu8t87LnFCMoQvtojpjFo=
+github.com/fsnotify/fsnotify v1.4.9 h1:hsms1Qyu0jgnwNXIxa+/V/PDsU6CfLf6CNO8H7IWoS4=
+github.com/fsnotify/fsnotify v1.4.9/go.mod h1:znqG4EE+3YCdAaPaxE2ZRY/06pZUdp0tY4IgpuI1SZQ=
github.com/fsouza/fake-gcs-server v1.7.0/go.mod h1:5XIRs4YvwNbNoz+1JF8j6KLAyDh7RHGAyAK3EP2EsNk=
github.com/garyburd/redigo v0.0.0-20150301180006-535138d7bcd7/go.mod h1:NR3MbYisc3/PwhQ00EMzDiPmrwpPxAn5GI05/YaO1SY=
github.com/garyburd/redigo v1.6.0/go.mod h1:NR3MbYisc3/PwhQ00EMzDiPmrwpPxAn5GI05/YaO1SY=
-github.com/ghodss/yaml v0.0.0-20180820084758-c7ce16629ff4/go.mod h1:4dBDuWmgqj2HViK6kFavaiC9ZROes6MMH2rRYeMEF04=
+github.com/ghodss/yaml v0.0.0-20150909031657-73d445a93680/go.mod h1:4dBDuWmgqj2HViK6kFavaiC9ZROes6MMH2rRYeMEF04=
+github.com/ghodss/yaml v1.0.0 h1:wQHKEahhL6wmXdzwWG11gIVCkOv05bNOh+Rxn0yngAk=
github.com/ghodss/yaml v1.0.0/go.mod h1:4dBDuWmgqj2HViK6kFavaiC9ZROes6MMH2rRYeMEF04=
-github.com/ghodss/yaml v1.0.1-0.20190212211648-25d852aebe32 h1:Mn26/9ZMNWSw9C9ERFA1PUxfmGpolnw2v0bKOREu5ew=
-github.com/ghodss/yaml v1.0.1-0.20190212211648-25d852aebe32/go.mod h1:GIjDIg/heH5DOkXY3YJ/wNhfHsQHoXGjl8G8amsYQ1I=
github.com/globalsign/mgo v0.0.0-20180905125535-1ca0a4f7cbcb/go.mod h1:xkRDCp4j0OGD1HRkm4kmhM+pmpv3AKq5SU7GMg4oO/Q=
github.com/globalsign/mgo v0.0.0-20181015135952-eeefdecb41b8/go.mod h1:xkRDCp4j0OGD1HRkm4kmhM+pmpv3AKq5SU7GMg4oO/Q=
-github.com/go-acme/lego v2.5.0+incompatible/go.mod h1:yzMNe9CasVUhkquNvti5nAtPmG94USbYxYrZfTkIn0M=
-github.com/go-bindata/go-bindata v3.1.1+incompatible/go.mod h1:xK8Dsgwmeed+BBsSy2XTopBn/8uK2HWuGSnA11C3Joo=
-github.com/go-bindata/go-bindata v3.1.2+incompatible/go.mod h1:xK8Dsgwmeed+BBsSy2XTopBn/8uK2HWuGSnA11C3Joo=
+github.com/go-bindata/go-bindata/v3 v3.1.3/go.mod h1:1/zrpXsLD8YDIbhZRqXzm1Ghc7NhEvIN9+Z6R5/xH4I=
github.com/go-chi/chi v4.0.0+incompatible h1:SiLLEDyAkqNnw+T/uDTf3aFB9T4FTrwMpuYrgaRcnW4=
github.com/go-chi/chi v4.0.0+incompatible/go.mod h1:eB3wogJHnLi3x/kFX2A+IbTBlXxmMeXJVKy9tTv1XzQ=
-github.com/go-critic/go-critic v0.4.1 h1:4DTQfT1wWwLg/hzxwD9bkdhDQrdJtxe6DUTadPlrIeE=
-github.com/go-critic/go-critic v0.4.1/go.mod h1:7/14rZGnZbY6E38VEGk2kVhoq6itzc1E68facVDK23g=
+github.com/go-gl/glfw v0.0.0-20190409004039-e6da0acd62b1/go.mod h1:vR7hzQXu2zJy9AVAgeJqvqgH9Q5CA+iKCZ2gyEVpxRU=
+github.com/go-gl/glfw/v3.3/glfw v0.0.0-20191125211704-12ad95a8df72/go.mod h1:tQ2UAYgL5IevRw8kRxooKSPJfGvJ9fJQFa0TUsXzTg8=
+github.com/go-gl/glfw/v3.3/glfw v0.0.0-20200222043503-6f7a984d4dc4/go.mod h1:tQ2UAYgL5IevRw8kRxooKSPJfGvJ9fJQFa0TUsXzTg8=
github.com/go-ini/ini v1.25.4/go.mod h1:ByCAeIL28uOIIG0E3PJtZPDL8WnHpFKFOtgjp+3Ies8=
github.com/go-kit/kit v0.8.0/go.mod h1:xBxKIO96dXMWWy0MnWVtmwkA9/13aqxPnvrjFYMA2as=
github.com/go-kit/kit v0.9.0/go.mod h1:xBxKIO96dXMWWy0MnWVtmwkA9/13aqxPnvrjFYMA2as=
-github.com/go-lintpack/lintpack v0.5.2 h1:DI5mA3+eKdWeJ40nU4d6Wc26qmdG8RCi/btYq0TuRN0=
-github.com/go-lintpack/lintpack v0.5.2/go.mod h1:NwZuYi2nUHho8XEIZ6SIxihrnPoqBTDqfpXvXAN0sXM=
+github.com/go-kit/kit v0.10.0/go.mod h1:xUsJbQ/Fp4kEt7AFgCuvyX4a71u8h9jB8tj/ORgOZ7o=
github.com/go-logfmt/logfmt v0.3.0/go.mod h1:Qt1PoO58o5twSAckw1HlFXLmHsOX5/0LbT9GBnD5lWE=
github.com/go-logfmt/logfmt v0.4.0/go.mod h1:3RMwSq7FuexP4Kalkev3ejPJsZTpXXBr9+V4qmtdjCk=
-github.com/go-logr/logr v0.1.0 h1:M1Tv3VzNlEHg6uyACnRdtrploV2P7wZqH8BoQMtz0cg=
+github.com/go-logfmt/logfmt v0.5.0/go.mod h1:wCYkCAKZfumFQihp8CzCvQ3paCTfi41vtzG1KdI/P7A=
github.com/go-logr/logr v0.1.0/go.mod h1:ixOQHD9gLJUVQQ2ZOR7zLEifBX6tGkNJF4QyIY7sIas=
+github.com/go-logr/logr v0.2.0/go.mod h1:z6/tIYblkpsD+a4lm/fGIIU9mZ+XfAiaFtq7xTgseGU=
+github.com/go-logr/logr v0.3.0 h1:q4c+kbcR0d5rSurhBR8dIgieOaYpXtsdTYfx22Cu6rs=
+github.com/go-logr/logr v0.3.0/go.mod h1:z6/tIYblkpsD+a4lm/fGIIU9mZ+XfAiaFtq7xTgseGU=
github.com/go-logr/zapr v0.1.0/go.mod h1:tabnROwaDl0UNxkVeFRbY8bwB37GwRv0P8lg6aAiEnk=
-github.com/go-logr/zapr v0.1.1 h1:qXBXPDdNncunGs7XeEpsJt8wCjYBygluzfdLO0G5baE=
-github.com/go-logr/zapr v0.1.1/go.mod h1:tabnROwaDl0UNxkVeFRbY8bwB37GwRv0P8lg6aAiEnk=
-github.com/go-ole/go-ole v1.2.1/go.mod h1:7FAglXiTm7HKlQRDeOQ6ZNUHidzCWXuZWq/1dTyBNF8=
+github.com/go-logr/zapr v0.2.0 h1:v6Ji8yBW77pva6NkJKQdHLAJKrIJKRHz0RXwPqCHSR4=
+github.com/go-logr/zapr v0.2.0/go.mod h1:qhKdvif7YF5GI9NWEpyxTSSBdGmzkNguibrdCNVPunU=
github.com/go-openapi/analysis v0.0.0-20180825180245-b006789cd277/go.mod h1:k70tL6pCuVxPJOHXQ+wIac1FUrvNkHolPie/cLEU6hI=
github.com/go-openapi/analysis v0.17.0/go.mod h1:IowGgpVeD0vNm45So8nr+IcQ3pxVtpRoBWb8PVZO0ik=
github.com/go-openapi/analysis v0.18.0/go.mod h1:IowGgpVeD0vNm45So8nr+IcQ3pxVtpRoBWb8PVZO0ik=
github.com/go-openapi/analysis v0.19.2/go.mod h1:3P1osvZa9jKjb8ed2TPng3f0i/UY9snX6gxi44djMjk=
+github.com/go-openapi/analysis v0.19.5/go.mod h1:hkEAkxagaIvIP7VTn8ygJNkd4kAYON2rCu0v0ObL0AU=
github.com/go-openapi/errors v0.17.0/go.mod h1:LcZQpmvG4wyF5j4IhA73wkLFQg+QJXOQHVjmcZxhka0=
github.com/go-openapi/errors v0.18.0/go.mod h1:LcZQpmvG4wyF5j4IhA73wkLFQg+QJXOQHVjmcZxhka0=
github.com/go-openapi/errors v0.19.2/go.mod h1:qX0BLWsyaKfvhluLejVpVNwNRdXZhEbTA4kxxpKBC94=
+github.com/go-openapi/jsonpointer v0.0.0-20160704185906-46af16f9f7b1/go.mod h1:+35s3my2LFTysnkMfxsJBAMHj/DoqoB9knIWoYG/Vk0=
github.com/go-openapi/jsonpointer v0.17.0/go.mod h1:cOnomiV+CVVwFLk0A/MExoFMjwdsUdVpsRhURCKh+3M=
github.com/go-openapi/jsonpointer v0.18.0/go.mod h1:cOnomiV+CVVwFLk0A/MExoFMjwdsUdVpsRhURCKh+3M=
github.com/go-openapi/jsonpointer v0.19.2/go.mod h1:3akKfEdA7DF1sugOqz1dVQHBcuDBPKZGEoHC/NkiQRg=
github.com/go-openapi/jsonpointer v0.19.3 h1:gihV7YNZK1iK6Tgwwsxo2rJbD1GTbdm72325Bq8FI3w=
github.com/go-openapi/jsonpointer v0.19.3/go.mod h1:Pl9vOtqEWErmShwVjC8pYs9cog34VGT37dQOVbmoatg=
+github.com/go-openapi/jsonreference v0.0.0-20160704190145-13c6e3589ad9/go.mod h1:W3Z9FmVs9qj+KR4zFKmDPGiLdk1D9Rlm7cyMvf57TTg=
github.com/go-openapi/jsonreference v0.17.0/go.mod h1:g4xxGn04lDIRh0GJb5QlpE3HfopLOL6uZrK/VgnsK9I=
github.com/go-openapi/jsonreference v0.18.0/go.mod h1:g4xxGn04lDIRh0GJb5QlpE3HfopLOL6uZrK/VgnsK9I=
-github.com/go-openapi/jsonreference v0.19.2 h1:o20suLFB4Ri0tuzpWtyHlh7E7HnkqTNLq6aR6WVNS1w=
github.com/go-openapi/jsonreference v0.19.2/go.mod h1:jMjeRr2HHw6nAVajTXJ4eiUwohSTlpa0o73RUL1owJc=
github.com/go-openapi/jsonreference v0.19.3 h1:5cxNfTy0UVC3X8JL5ymxzyoUZmo8iZb+jeTWn7tUa8o=
github.com/go-openapi/jsonreference v0.19.3/go.mod h1:rjx6GuL8TTa9VaixXglHmQmIL98+wF9xc8zWvFonSJ8=
@@ -303,547 +329,466 @@ github.com/go-openapi/loads v0.17.0/go.mod h1:72tmFy5wsWx89uEVddd0RjRWPZm92WRLhf
github.com/go-openapi/loads v0.18.0/go.mod h1:72tmFy5wsWx89uEVddd0RjRWPZm92WRLhf7AC+0+OOU=
github.com/go-openapi/loads v0.19.0/go.mod h1:72tmFy5wsWx89uEVddd0RjRWPZm92WRLhf7AC+0+OOU=
github.com/go-openapi/loads v0.19.2/go.mod h1:QAskZPMX5V0C2gvfkGZzJlINuP7Hx/4+ix5jWFxsNPs=
+github.com/go-openapi/loads v0.19.4/go.mod h1:zZVHonKd8DXyxyw4yfnVjPzBjIQcLt0CCsn0N0ZrQsk=
github.com/go-openapi/runtime v0.0.0-20180920151709-4f900dc2ade9/go.mod h1:6v9a6LTXWQCdL8k1AO3cvqx5OtZY/Y9wKTgaoP6YRfA=
github.com/go-openapi/runtime v0.19.0/go.mod h1:OwNfisksmmaZse4+gpV3Ne9AyMOlP1lt4sK4FXt0O64=
+github.com/go-openapi/runtime v0.19.4/go.mod h1:X277bwSUBxVlCYR3r7xgZZGKVvBd/29gLDlFGtJ8NL4=
+github.com/go-openapi/spec v0.0.0-20160808142527-6aced65f8501/go.mod h1:J8+jY1nAiCcj+friV/PDoE1/3eeccG9LYBs0tYvLOWc=
github.com/go-openapi/spec v0.17.0/go.mod h1:XkF/MOi14NmjsfZ8VtAKf8pIlbZzyoTvZsdfssdxcBI=
github.com/go-openapi/spec v0.18.0/go.mod h1:XkF/MOi14NmjsfZ8VtAKf8pIlbZzyoTvZsdfssdxcBI=
github.com/go-openapi/spec v0.19.2/go.mod h1:sCxk3jxKgioEJikev4fgkNmwS+3kuYdJtcsZsD5zxMY=
-github.com/go-openapi/spec v0.19.4 h1:ixzUSnHTd6hCemgtAJgluaTSGYpLNpJY4mA2DIkdOAo=
-github.com/go-openapi/spec v0.19.4/go.mod h1:FpwSN1ksY1eteniUU7X0N/BgJ7a4WvBFVA8Lj9mJglo=
+github.com/go-openapi/spec v0.19.3 h1:0XRyw8kguri6Yw4SxhsQA/atC88yqrk0+G4YhI2wabc=
+github.com/go-openapi/spec v0.19.3/go.mod h1:FpwSN1ksY1eteniUU7X0N/BgJ7a4WvBFVA8Lj9mJglo=
github.com/go-openapi/strfmt v0.17.0/go.mod h1:P82hnJI0CXkErkXi8IKjPbNBM6lV6+5pLP5l494TcyU=
github.com/go-openapi/strfmt v0.18.0/go.mod h1:P82hnJI0CXkErkXi8IKjPbNBM6lV6+5pLP5l494TcyU=
github.com/go-openapi/strfmt v0.19.0/go.mod h1:+uW+93UVvGGq2qGaZxdDeJqSAqBqBdl+ZPMF/cC8nDY=
+github.com/go-openapi/strfmt v0.19.3/go.mod h1:0yX7dbo8mKIvc3XSKp7MNfxw4JytCfCD6+bY1AVL9LU=
+github.com/go-openapi/swag v0.0.0-20160704191624-1d0bd113de87/go.mod h1:DXUve3Dpr1UfpPtxFw+EFuQ41HhCWZfha5jSVRG7C7I=
github.com/go-openapi/swag v0.17.0/go.mod h1:AByQ+nYG6gQg71GINrmuDXCPWdL640yX49/kXLo40Tg=
github.com/go-openapi/swag v0.18.0/go.mod h1:AByQ+nYG6gQg71GINrmuDXCPWdL640yX49/kXLo40Tg=
github.com/go-openapi/swag v0.19.2/go.mod h1:POnQmlKehdgb5mhVOsnJFsivZCEZ/vjK9gh66Z9tfKk=
-github.com/go-openapi/swag v0.19.4/go.mod h1:POnQmlKehdgb5mhVOsnJFsivZCEZ/vjK9gh66Z9tfKk=
github.com/go-openapi/swag v0.19.5 h1:lTz6Ys4CmqqCQmZPBlbQENR1/GucA2bzYTE12Pw4tFY=
github.com/go-openapi/swag v0.19.5/go.mod h1:POnQmlKehdgb5mhVOsnJFsivZCEZ/vjK9gh66Z9tfKk=
github.com/go-openapi/validate v0.18.0/go.mod h1:Uh4HdOzKt19xGIGm1qHf/ofbX1YQ4Y+MYsct2VUrAJ4=
github.com/go-openapi/validate v0.19.2/go.mod h1:1tRCw7m3jtI8eNWEEliiAqUIcBztB2KDnRCRMUi7GTA=
-github.com/go-ozzo/ozzo-validation v3.5.0+incompatible/go.mod h1:gsEKFIVnabGBt6mXmxK0MoFy+cZoTJY6mu5Ll3LVLBU=
+github.com/go-openapi/validate v0.19.5/go.mod h1:8DJv2CVJQ6kGNpFW6eV9N3JviE1C85nY1c2z52x1Gk4=
github.com/go-sql-driver/mysql v1.4.0/go.mod h1:zAC/RDZ24gD3HViQzih4MyKcchzm+sOG5ZlKdlhCg5w=
github.com/go-sql-driver/mysql v1.4.1/go.mod h1:zAC/RDZ24gD3HViQzih4MyKcchzm+sOG5ZlKdlhCg5w=
github.com/go-stack/stack v1.8.0/go.mod h1:v0f6uXyyMGvRgIKkXu+yp6POWl0qKG85gN/melR3HDY=
-github.com/go-toolsmith/astcast v1.0.0 h1:JojxlmI6STnFVG9yOImLeGREv8W2ocNUM+iOhR6jE7g=
-github.com/go-toolsmith/astcast v1.0.0/go.mod h1:mt2OdQTeAQcY4DQgPSArJjHCcOwlX+Wl/kwN+LbLGQ4=
-github.com/go-toolsmith/astcopy v1.0.0 h1:OMgl1b1MEpjFQ1m5ztEO06rz5CUd3oBv9RF7+DyvdG8=
-github.com/go-toolsmith/astcopy v1.0.0/go.mod h1:vrgyG+5Bxrnz4MZWPF+pI4R8h3qKRjjyvV/DSez4WVQ=
-github.com/go-toolsmith/astequal v0.0.0-20180903214952-dcb477bfacd6/go.mod h1:H+xSiq0+LtiDC11+h1G32h7Of5O3CYFJ99GVbS5lDKY=
-github.com/go-toolsmith/astequal v1.0.0 h1:4zxD8j3JRFNyLN46lodQuqz3xdKSrur7U/sr0SDS/gQ=
-github.com/go-toolsmith/astequal v1.0.0/go.mod h1:H+xSiq0+LtiDC11+h1G32h7Of5O3CYFJ99GVbS5lDKY=
-github.com/go-toolsmith/astfmt v0.0.0-20180903215011-8f8ee99c3086/go.mod h1:mP93XdblcopXwlyN4X4uodxXQhldPGZbcEJIimQHrkg=
-github.com/go-toolsmith/astfmt v1.0.0 h1:A0vDDXt+vsvLEdbMFJAUBI/uTbRw1ffOPnxsILnFL6k=
-github.com/go-toolsmith/astfmt v1.0.0/go.mod h1:cnWmsOAuq4jJY6Ct5YWlVLmcmLMn1JUPuQIHCY7CJDw=
-github.com/go-toolsmith/astinfo v0.0.0-20180906194353-9809ff7efb21/go.mod h1:dDStQCHtmZpYOmjRP/8gHHnCCch3Zz3oEgCdZVdtweU=
-github.com/go-toolsmith/astp v0.0.0-20180903215135-0af7e3c24f30/go.mod h1:SV2ur98SGypH1UjcPpCatrV5hPazG6+IfNHbkDXBRrk=
-github.com/go-toolsmith/astp v1.0.0 h1:alXE75TXgcmupDsMK1fRAy0YUzLzqPVvBKoyWV+KPXg=
-github.com/go-toolsmith/astp v1.0.0/go.mod h1:RSyrtpVlfTFGDYRbrjyWP1pYu//tSFcvdYrA8meBmLI=
-github.com/go-toolsmith/pkgload v0.0.0-20181119091011-e9e65178eee8/go.mod h1:WoMrjiy4zvdS+Bg6z9jZH82QXwkcgCBX6nOfnmdaHks=
-github.com/go-toolsmith/pkgload v1.0.0/go.mod h1:5eFArkbO80v7Z0kdngIxsRXRMTaX4Ilcwuh3clNrQJc=
-github.com/go-toolsmith/strparse v1.0.0 h1:Vcw78DnpCAKlM20kSbAyO4mPfJn/lyYA4BJUDxe2Jb4=
-github.com/go-toolsmith/strparse v1.0.0/go.mod h1:YI2nUKP9YGZnL/L1/DLFBfixrcjslWct4wyljWhSRy8=
-github.com/go-toolsmith/typep v1.0.0 h1:zKymWyA1TRYvqYrYDrfEMZULyrhcnGY3x7LDKU2XQaA=
-github.com/go-toolsmith/typep v1.0.0/go.mod h1:JSQCQMUPdRlMZFswiq3TGpNp1GMktqkR2Ns5AIQkATU=
-github.com/go-xmlfmt/xmlfmt v0.0.0-20191208150333-d5b6f63a941b/go.mod h1:aUCEOzzezBEjDBbFBoSiya/gduyIiWYRP6CnSFIV8AM=
github.com/gobuffalo/envy v1.6.5/go.mod h1:N+GkhhZ/93bGZc6ZKhJLP6+m+tCNPKwgSpH9kaifseQ=
-github.com/gobuffalo/envy v1.6.15/go.mod h1:n7DRkBerg/aorDM8kbduw5dN3oXGswK5liaSCx4T5NI=
-github.com/gobuffalo/envy v1.7.0 h1:GlXgaiBkmrYMHco6t4j7SacKO4XUjvh5pwXh0f4uxXU=
github.com/gobuffalo/envy v1.7.0/go.mod h1:n7DRkBerg/aorDM8kbduw5dN3oXGswK5liaSCx4T5NI=
-github.com/gobuffalo/logger v1.0.0/go.mod h1:2zbswyIUa45I+c+FLXuWl9zSWEiVuthsk8ze5s8JvPs=
+github.com/gobuffalo/envy v1.7.1/go.mod h1:FurDp9+EDPE4aIUS3ZLyD+7/9fpx7YRt/ukY6jIHf0w=
+github.com/gobuffalo/flect v0.2.0/go.mod h1:W3K3X9ksuZfir8f/LrfVtWmCDQFfayuylOJ7sz/Fj80=
+github.com/gobuffalo/flect v0.2.2/go.mod h1:vmkQwuZYhN5Pc4ljYQZzP+1sq+NEkK+lh20jmEmX3jc=
+github.com/gobuffalo/logger v1.0.1/go.mod h1:2zbswyIUa45I+c+FLXuWl9zSWEiVuthsk8ze5s8JvPs=
github.com/gobuffalo/packd v0.3.0/go.mod h1:zC7QkmNkYVGKPw4tHpBQ+ml7W/3tIebgeo1b36chA3Q=
-github.com/gobuffalo/packr v1.30.1/go.mod h1:ljMyFO2EcrnzsHsN99cvbq055Y9OhRrIaviy289eRuk=
-github.com/gobuffalo/packr/v2 v2.5.1/go.mod h1:8f9c96ITobJlPzI44jj+4tHnEKNt0xXWSVlXRN9X1Iw=
-github.com/gobwas/glob v0.2.3 h1:A4xDbljILXROh+kObIiy5kIaPYD8e96x1tgBhUI5J+Y=
+github.com/gobuffalo/packr/v2 v2.7.1/go.mod h1:qYEvAazPaVxy7Y7KR0W8qYEE+RymX74kETFqjFoFlOc=
github.com/gobwas/glob v0.2.3/go.mod h1:d3Ez4x06l9bZtSvzIay5+Yzi0fmZzPgnTbPcKjJAkT8=
github.com/gocql/gocql v0.0.0-20190301043612-f6df8288f9b4/go.mod h1:4Fw1eo5iaEhDUs8XyuhSVCVy52Jq3L+/3GJgYkwc+/0=
github.com/godbus/dbus v0.0.0-20190422162347-ade71ed3457e/go.mod h1:bBOAhwG1umN6/6ZUMtDFBMQR8jRg9O75tm9K00oMsK4=
-github.com/godbus/dbus v4.1.0+incompatible/go.mod h1:/YcGZj5zSblfDWMMoOzV4fas9FZnQYTkDnsGvmh2Grw=
-github.com/gofrs/flock v0.0.0-20190320160742-5135e617513b/go.mod h1:F1TvTiK9OcQqauNUHlbJvyl9Qa1QvF/gOUDKA14jxHU=
-github.com/gofrs/flock v0.7.1 h1:DP+LD/t0njgoPBvT5MJLeliUIVQR03hiKR6vezdwHlc=
-github.com/gofrs/flock v0.7.1/go.mod h1:F1TvTiK9OcQqauNUHlbJvyl9Qa1QvF/gOUDKA14jxHU=
-github.com/gofrs/uuid v3.2.0+incompatible/go.mod h1:b2aQJv3Z4Fp6yNu3cdSllBxTCLRxnplIgP/c0N/04lM=
+github.com/godror/godror v0.13.3/go.mod h1:2ouUT4kdhUBk7TAkHWD4SN0CdI0pgEQbo8FVHhbSKWg=
+github.com/gofrs/flock v0.8.0/go.mod h1:F1TvTiK9OcQqauNUHlbJvyl9Qa1QvF/gOUDKA14jxHU=
+github.com/gofrs/uuid v3.3.0+incompatible/go.mod h1:b2aQJv3Z4Fp6yNu3cdSllBxTCLRxnplIgP/c0N/04lM=
+github.com/gogo/googleapis v1.1.0/go.mod h1:gf4bu3Q80BeJ6H1S1vYPm8/ELATdvryBaNFGgqEef3s=
github.com/gogo/protobuf v1.1.1/go.mod h1:r8qH/GZQm5c6nD/R0oafs1akxWv10x8SbQlK7atdtwQ=
github.com/gogo/protobuf v1.2.0/go.mod h1:r8qH/GZQm5c6nD/R0oafs1akxWv10x8SbQlK7atdtwQ=
github.com/gogo/protobuf v1.2.1/go.mod h1:hp+jE20tsWTFYpLwKvXlhS1hjn+gTNwPg2I6zVXpSg4=
-github.com/gogo/protobuf v1.2.2-0.20190723190241-65acae22fc9d/go.mod h1:SlYgWuQ5SjCEi6WLHjHCa1yvBfUnHcTbrrZtXPKa29o=
-github.com/gogo/protobuf v1.2.2-0.20190730201129-28a6bbf47e48/go.mod h1:SlYgWuQ5SjCEi6WLHjHCa1yvBfUnHcTbrrZtXPKa29o=
github.com/gogo/protobuf v1.3.1 h1:DqDEcV5aeaTmdFBePNpYsp3FlcVH/2ISVVM9Qf8PSls=
github.com/gogo/protobuf v1.3.1/go.mod h1:SlYgWuQ5SjCEi6WLHjHCa1yvBfUnHcTbrrZtXPKa29o=
github.com/golang-migrate/migrate/v4 v4.6.2/go.mod h1:JYi6reN3+Z734VZ0akNuyOJNcrg45ZL7LDBMW3WGJL0=
-github.com/golang/glog v0.0.0-20160126235308-23def4e6c14b h1:VKtxabqXZkF25pY9ekfRL6a582T4P37/31XEstQ5p58=
+github.com/golang-sql/civil v0.0.0-20190719163853-cb61b32ac6fe/go.mod h1:8vg3r2VgvsThLBIFL93Qb5yWzgyZWhEmBwUJWevAkK0=
github.com/golang/glog v0.0.0-20160126235308-23def4e6c14b/go.mod h1:SBH7ygxi8pfUlaOkMMuAQtPIUF8ecWP5IEl/CR7VP2Q=
github.com/golang/groupcache v0.0.0-20160516000752-02826c3e7903/go.mod h1:cIg4eruTrX1D+g88fzRXU5OdNfaM+9IcxsU14FzY7Hc=
-github.com/golang/groupcache v0.0.0-20180513044358-24b0969c4cb7/go.mod h1:cIg4eruTrX1D+g88fzRXU5OdNfaM+9IcxsU14FzY7Hc=
-github.com/golang/groupcache v0.0.0-20181024230925-c65c006176ff/go.mod h1:cIg4eruTrX1D+g88fzRXU5OdNfaM+9IcxsU14FzY7Hc=
-github.com/golang/groupcache v0.0.0-20190129154638-5b532d6fd5ef h1:veQD95Isof8w9/WXiA+pa3tz3fJXkt5B7QaRBrM62gk=
github.com/golang/groupcache v0.0.0-20190129154638-5b532d6fd5ef/go.mod h1:cIg4eruTrX1D+g88fzRXU5OdNfaM+9IcxsU14FzY7Hc=
-github.com/golang/groupcache v0.0.0-20191027212112-611e8accdfc9 h1:uHTyIjqVhYRhLbJ8nIiOJHkEZZ+5YoOsAbD3sk82NiE=
-github.com/golang/groupcache v0.0.0-20191027212112-611e8accdfc9/go.mod h1:cIg4eruTrX1D+g88fzRXU5OdNfaM+9IcxsU14FzY7Hc=
-github.com/golang/lint v0.0.0-20180702182130-06c8688daad7/go.mod h1:tluoj9z5200jBnyusfRPU2LqT6J+DAorxEvtC7LHB+E=
+github.com/golang/groupcache v0.0.0-20190702054246-869f871628b6/go.mod h1:cIg4eruTrX1D+g88fzRXU5OdNfaM+9IcxsU14FzY7Hc=
+github.com/golang/groupcache v0.0.0-20191227052852-215e87163ea7 h1:5ZkaAPbicIKTF2I64qf5Fh8Aa83Q/dnOafMYV0OMwjA=
+github.com/golang/groupcache v0.0.0-20191227052852-215e87163ea7/go.mod h1:cIg4eruTrX1D+g88fzRXU5OdNfaM+9IcxsU14FzY7Hc=
+github.com/golang/groupcache v0.0.0-20200121045136-8c9f03a8e57e h1:1r7pUrabqp18hOBcwBwiTsbnFeTZHV9eER/QT5JVZxY=
+github.com/golang/groupcache v0.0.0-20200121045136-8c9f03a8e57e/go.mod h1:cIg4eruTrX1D+g88fzRXU5OdNfaM+9IcxsU14FzY7Hc=
github.com/golang/mock v1.1.1/go.mod h1:oTYuIxOrZwtPieC+H1uAHpcLFnEyAGVDL/k47Jfbm0A=
github.com/golang/mock v1.2.0/go.mod h1:oTYuIxOrZwtPieC+H1uAHpcLFnEyAGVDL/k47Jfbm0A=
github.com/golang/mock v1.3.1 h1:qGJ6qTW+x6xX/my+8YUVl4WNpX9B7+/l2tRsHGZ7f2s=
github.com/golang/mock v1.3.1/go.mod h1:sBzyDLLjw3U8JLTeZvSv8jJB+tU5PVekmnlKIyFUx0Y=
-github.com/golang/protobuf v1.0.0/go.mod h1:6lQm79b+lXiMfvg/cZm0SGofjICqVBUtrP5yJMmIC1U=
+github.com/golang/mock v1.4.0/go.mod h1:UOMv5ysSaYNkG+OFQykRIcU/QvvxJf3p21QfJ2Bt3cw=
+github.com/golang/mock v1.4.1 h1:ocYkMQY5RrXTYgXl7ICpV0IXwlEQGwKIsery4gyXa1U=
+github.com/golang/mock v1.4.1/go.mod h1:UOMv5ysSaYNkG+OFQykRIcU/QvvxJf3p21QfJ2Bt3cw=
+github.com/golang/protobuf v0.0.0-20161109072736-4bd1920723d7/go.mod h1:6lQm79b+lXiMfvg/cZm0SGofjICqVBUtrP5yJMmIC1U=
github.com/golang/protobuf v1.2.0/go.mod h1:6lQm79b+lXiMfvg/cZm0SGofjICqVBUtrP5yJMmIC1U=
github.com/golang/protobuf v1.3.1/go.mod h1:6lQm79b+lXiMfvg/cZm0SGofjICqVBUtrP5yJMmIC1U=
-github.com/golang/protobuf v1.3.2 h1:6nsPYzhq5kReh6QImI3k5qWzO4PEbvbIW2cwSfR/6xs=
github.com/golang/protobuf v1.3.2/go.mod h1:6lQm79b+lXiMfvg/cZm0SGofjICqVBUtrP5yJMmIC1U=
+github.com/golang/protobuf v1.3.3/go.mod h1:vzj43D7+SQXF/4pzW/hwtAqwc6iTitCiVSaWz5lYuqw=
+github.com/golang/protobuf v1.3.4/go.mod h1:vzj43D7+SQXF/4pzW/hwtAqwc6iTitCiVSaWz5lYuqw=
+github.com/golang/protobuf v1.4.0-rc.1/go.mod h1:ceaxUfeHdC40wWswd/P6IGgMaK3YpKi5j83Wpe3EHw8=
+github.com/golang/protobuf v1.4.0-rc.1.0.20200221234624-67d41d38c208/go.mod h1:xKAWHe0F5eneWXFV3EuXVDTCmh+JuBKY0li0aMyXATA=
+github.com/golang/protobuf v1.4.0-rc.2/go.mod h1:LlEzMj4AhA7rCAGe4KMBDvJI+AwstrUpVNzEA03Pprs=
+github.com/golang/protobuf v1.4.0-rc.4.0.20200313231945-b860323f09d0/go.mod h1:WU3c8KckQ9AFe+yFwt9sWVRKCVIyN9cPHBJSNnbL67w=
+github.com/golang/protobuf v1.4.0/go.mod h1:jodUvKwWbYaEsadDk5Fwe5c77LiNKVO9IDvqG2KuDX0=
+github.com/golang/protobuf v1.4.1/go.mod h1:U8fpvMrcmy5pZrNK1lt4xCsGvpyWQ/VVv6QDs8UjoX8=
+github.com/golang/protobuf v1.4.2 h1:+Z5KGCizgyZCbGh1KZqA0fcLLkwbsjIzS4aV2v7wJX0=
+github.com/golang/protobuf v1.4.2/go.mod h1:oDoupMAO8OvCJWAcko0GGGIgR6R6ocIYbsSw735rRwI=
+github.com/golang/protobuf v1.4.3 h1:JjCZWpVbqXDqFVmTfYWEVTMIYrL/NPdPSCHPJ0T/raM=
+github.com/golang/protobuf v1.4.3/go.mod h1:oDoupMAO8OvCJWAcko0GGGIgR6R6ocIYbsSw735rRwI=
github.com/golang/snappy v0.0.0-20170215233205-553a64147049/go.mod h1:/XxbfmMg8lxefKM7IXC3fBNl/7bRcc72aCRzEWrmP2Q=
github.com/golang/snappy v0.0.0-20180518054509-2e65f85255db/go.mod h1:/XxbfmMg8lxefKM7IXC3fBNl/7bRcc72aCRzEWrmP2Q=
github.com/golang/snappy v0.0.1/go.mod h1:/XxbfmMg8lxefKM7IXC3fBNl/7bRcc72aCRzEWrmP2Q=
-github.com/golangci/check v0.0.0-20180506172741-cfe4005ccda2 h1:23T5iq8rbUYlhpt5DB4XJkc6BU31uODLD1o1gKvZmD0=
-github.com/golangci/check v0.0.0-20180506172741-cfe4005ccda2/go.mod h1:k9Qvh+8juN+UKMCS/3jFtGICgW8O96FVaZsaxdzDkR4=
-github.com/golangci/dupl v0.0.0-20180902072040-3e9179ac440a h1:w8hkcTqaFpzKqonE9uMCefW1WDie15eSP/4MssdenaM=
-github.com/golangci/dupl v0.0.0-20180902072040-3e9179ac440a/go.mod h1:ryS0uhF+x9jgbj/N71xsEqODy9BN81/GonCZiOzirOk=
-github.com/golangci/errcheck v0.0.0-20181223084120-ef45e06d44b6 h1:YYWNAGTKWhKpcLLt7aSj/odlKrSrelQwlovBpDuf19w=
-github.com/golangci/errcheck v0.0.0-20181223084120-ef45e06d44b6/go.mod h1:DbHgvLiFKX1Sh2T1w8Q/h4NAI8MHIpzCdnBUDTXU3I0=
-github.com/golangci/go-misc v0.0.0-20180628070357-927a3d87b613 h1:9kfjN3AdxcbsZBf8NjltjWihK2QfBBBZuv91cMFfDHw=
-github.com/golangci/go-misc v0.0.0-20180628070357-927a3d87b613/go.mod h1:SyvUF2NxV+sN8upjjeVYr5W7tyxaT1JVtvhKhOn2ii8=
-github.com/golangci/goconst v0.0.0-20180610141641-041c5f2b40f3 h1:pe9JHs3cHHDQgOFXJJdYkK6fLz2PWyYtP4hthoCMvs8=
-github.com/golangci/goconst v0.0.0-20180610141641-041c5f2b40f3/go.mod h1:JXrF4TWy4tXYn62/9x8Wm/K/dm06p8tCKwFRDPZG/1o=
-github.com/golangci/gocyclo v0.0.0-20180528134321-2becd97e67ee h1:J2XAy40+7yz70uaOiMbNnluTg7gyQhtGqLQncQh+4J8=
-github.com/golangci/gocyclo v0.0.0-20180528134321-2becd97e67ee/go.mod h1:ozx7R9SIwqmqf5pRP90DhR2Oay2UIjGuKheCBCNwAYU=
-github.com/golangci/gofmt v0.0.0-20190930125516-244bba706f1a h1:iR3fYXUjHCR97qWS8ch1y9zPNsgXThGwjKPrYfqMPks=
-github.com/golangci/gofmt v0.0.0-20190930125516-244bba706f1a/go.mod h1:9qCChq59u/eW8im404Q2WWTrnBUQKjpNYKMbU4M7EFU=
-github.com/golangci/golangci-lint v1.26.0 h1:CLLGRSA9BLMiNvsWPXHioYAdfIx9tkgdVWyA6bIdYCo=
-github.com/golangci/golangci-lint v1.26.0/go.mod h1:tefbO6RcigFzvTnDC+Y51kntVGgkuCAVsC+mnfbPruc=
-github.com/golangci/ineffassign v0.0.0-20190609212857-42439a7714cc h1:gLLhTLMk2/SutryVJ6D4VZCU3CUqr8YloG7FPIBWFpI=
-github.com/golangci/ineffassign v0.0.0-20190609212857-42439a7714cc/go.mod h1:e5tpTHCfVze+7EpLEozzMB3eafxo2KT5veNg1k6byQU=
-github.com/golangci/lint-1 v0.0.0-20191013205115-297bf364a8e0 h1:MfyDlzVjl1hoaPzPD4Gpb/QgoRfSBR0jdhwGyAWwMSA=
-github.com/golangci/lint-1 v0.0.0-20191013205115-297bf364a8e0/go.mod h1:66R6K6P6VWk9I95jvqGxkqJxVWGFy9XlDwLwVz1RCFg=
-github.com/golangci/maligned v0.0.0-20180506175553-b1d89398deca h1:kNY3/svz5T29MYHubXix4aDDuE3RWHkPvopM/EDv/MA=
-github.com/golangci/maligned v0.0.0-20180506175553-b1d89398deca/go.mod h1:tvlJhZqDe4LMs4ZHD0oMUlt9G2LWuDGoisJTBzLMV9o=
-github.com/golangci/misspell v0.0.0-20180809174111-950f5d19e770 h1:EL/O5HGrF7Jaq0yNhBLucz9hTuRzj2LdwGBOaENgxIk=
-github.com/golangci/misspell v0.0.0-20180809174111-950f5d19e770/go.mod h1:dEbvlSfYbMQDtrpRMQU675gSDLDNa8sCPPChZ7PhiVA=
-github.com/golangci/prealloc v0.0.0-20180630174525-215b22d4de21 h1:leSNB7iYzLYSSx3J/s5sVf4Drkc68W2wm4Ixh/mr0us=
-github.com/golangci/prealloc v0.0.0-20180630174525-215b22d4de21/go.mod h1:tf5+bzsHdTM0bsB7+8mt0GUMvjCgwLpTapNZHU8AajI=
-github.com/golangci/revgrep v0.0.0-20180526074752-d9c87f5ffaf0 h1:HVfrLniijszjS1aiNg8JbBMO2+E1WIQ+j/gL4SQqGPg=
-github.com/golangci/revgrep v0.0.0-20180526074752-d9c87f5ffaf0/go.mod h1:qOQCunEYvmd/TLamH+7LlVccLvUH5kZNhbCgTHoBbp4=
-github.com/golangci/unconvert v0.0.0-20180507085042-28b1c447d1f4 h1:zwtduBRr5SSWhqsYNgcuWO2kFlpdOZbP0+yRjmvPGys=
-github.com/golangci/unconvert v0.0.0-20180507085042-28b1c447d1f4/go.mod h1:Izgrg8RkN3rCIMLGE9CyYmU9pY2Jer6DgANEnZ/L/cQ=
github.com/golangplus/bytes v0.0.0-20160111154220-45c989fe5450/go.mod h1:Bk6SMAONeMXrxql8uvOKuAZSu8aM5RUGv+1C6IJaEho=
github.com/golangplus/fmt v0.0.0-20150411045040-2a5d6d7d2995/go.mod h1:lJgMEyOkYFkPcDKwRXegd+iM6E7matEszMG5HhwytU8=
github.com/golangplus/testing v0.0.0-20180327235837-af21d9c3145e/go.mod h1:0AA//k/eakGydO4jKRoRL2j92ZKSzTgj9tclaCrvXHk=
github.com/google/btree v0.0.0-20180813153112-4030bb1f1f0c/go.mod h1:lNA+9X1NB3Zf8V7Ke586lFgjr2dZNuvo3lPJSGZ5JPQ=
github.com/google/btree v1.0.0 h1:0udJVsspx3VBr5FwtLhQQtuAsVc79tTq0ocGIPAU6qo=
github.com/google/btree v1.0.0/go.mod h1:lNA+9X1NB3Zf8V7Ke586lFgjr2dZNuvo3lPJSGZ5JPQ=
-github.com/google/cadvisor v0.34.0/go.mod h1:1nql6U13uTHaLYB8rLS5x9IJc2qT6Xd/Tr1sTX6NE48=
-github.com/google/certificate-transparency-go v1.0.21/go.mod h1:QeJfpSbVSfYc7RgB3gJFj9cbuQMMchQxrWXz8Ruopmg=
github.com/google/go-cmp v0.2.0/go.mod h1:oXzfMopK8JAjlY9xF4vHSVASa0yLyX7SntLO5aqRK0M=
github.com/google/go-cmp v0.3.0/go.mod h1:8QqcDgzrUqlUb/G2PQTWiueGozuR1884gddMywk6iLU=
-github.com/google/go-cmp v0.3.1 h1:Xye71clBPdm5HgqGwUkwhbynsUJZhDbS20FvLhQ2izg=
github.com/google/go-cmp v0.3.1/go.mod h1:8QqcDgzrUqlUb/G2PQTWiueGozuR1884gddMywk6iLU=
-github.com/google/go-cmp v0.4.0 h1:xsAVV57WRhGj6kEIi8ReJzQlHHqcBYCElAvkovg3B/4=
github.com/google/go-cmp v0.4.0/go.mod h1:v8dTdLbMG2kIc/vJvl+f65V22dbkXbowE6jgT/gNBxE=
+github.com/google/go-cmp v0.5.0/go.mod h1:v8dTdLbMG2kIc/vJvl+f65V22dbkXbowE6jgT/gNBxE=
+github.com/google/go-cmp v0.5.2 h1:X2ev0eStA3AbceY54o37/0PQ/UWqKEiiO2dKL5OPaFM=
+github.com/google/go-cmp v0.5.2/go.mod h1:v8dTdLbMG2kIc/vJvl+f65V22dbkXbowE6jgT/gNBxE=
github.com/google/go-github v17.0.0+incompatible/go.mod h1:zLgOLi98H3fifZn+44m+umXrS52loVEgC2AApnigrVQ=
github.com/google/go-querystring v1.0.0/go.mod h1:odCYkC5MyYFN7vkCjXpyrEuKhc/BUO6wN/zVPAxq5ck=
-github.com/google/gofuzz v0.0.0-20170612174753-24818f796faf/go.mod h1:HP5RmnzzSNb993RKQDq4+1A4ia9nllfqcQFTQJedwGI=
-github.com/google/gofuzz v1.0.0 h1:A8PeW59pxE9IoFRqBp37U+mSNaQoZ46F1f0f863XSXw=
github.com/google/gofuzz v1.0.0/go.mod h1:dBl0BpW6vV/+mYPU4Po3pmUjxk6FQPldtuIdl/M65Eg=
+github.com/google/gofuzz v1.1.0 h1:Hsa8mG0dQ46ij8Sl2AYJDUv1oA9/d6Vk+3LG99Oe02g=
+github.com/google/gofuzz v1.1.0/go.mod h1:dBl0BpW6vV/+mYPU4Po3pmUjxk6FQPldtuIdl/M65Eg=
github.com/google/martian v2.1.0+incompatible/go.mod h1:9I4somxYTbIHy5NJKHRl3wXiIaQGbYVAs8BPL6v8lEs=
github.com/google/pprof v0.0.0-20181206194817-3ea8567a2e57/go.mod h1:zfwlbNMJ+OItoe0UupaVj+oy1omPYYDuagoSzA8v9mc=
+github.com/google/pprof v0.0.0-20190515194954-54271f7e092f/go.mod h1:zfwlbNMJ+OItoe0UupaVj+oy1omPYYDuagoSzA8v9mc=
+github.com/google/pprof v0.0.0-20191218002539-d4f498aebedc/go.mod h1:ZgVRPoUq/hfqzAqh7sHMqb3I9Rq5C59dIz2SbBwJ4eM=
+github.com/google/pprof v0.0.0-20200212024743-f11f1df84d12/go.mod h1:ZgVRPoUq/hfqzAqh7sHMqb3I9Rq5C59dIz2SbBwJ4eM=
+github.com/google/pprof v0.0.0-20200229191704-1ebb73c60ed3/go.mod h1:ZgVRPoUq/hfqzAqh7sHMqb3I9Rq5C59dIz2SbBwJ4eM=
github.com/google/renameio v0.1.0/go.mod h1:KWCgfxg9yswjAJkECMjeO8J8rahYeXnNhOm40UhjYkI=
-github.com/google/shlex v0.0.0-20191202100458-e7afc7fbc510/go.mod h1:pupxD2MaaD3pAXIBCelhxNneeOaAeabZDe5s4K6zSpQ=
github.com/google/uuid v1.0.0/go.mod h1:TIyPZe4MgqvfeYDBFedMoGGpEw/LqOeaOT+nhxU+yHo=
github.com/google/uuid v1.1.1 h1:Gkbcsh/GbpXz7lPftLA3P6TYMwjCLYm83jiFQZF/3gY=
github.com/google/uuid v1.1.1/go.mod h1:TIyPZe4MgqvfeYDBFedMoGGpEw/LqOeaOT+nhxU+yHo=
+github.com/google/uuid v1.1.2 h1:EVhdT+1Kseyi1/pUmXKaFxYsDNy9RQYkMWRH68J/W7Y=
+github.com/google/uuid v1.1.2/go.mod h1:TIyPZe4MgqvfeYDBFedMoGGpEw/LqOeaOT+nhxU+yHo=
github.com/googleapis/gax-go/v2 v2.0.4/go.mod h1:0Wqv26UfaUD9n4G6kQubkQ+KchISgw+vpHVxEJEs9eg=
+github.com/googleapis/gax-go/v2 v2.0.5/go.mod h1:DWXyrwAJ9X0FpwwEdw+IPEYBICEFu5mhpdKc/us6bOk=
github.com/googleapis/gnostic v0.0.0-20170729233727-0c5108395e2d/go.mod h1:sJBsCZ4ayReDTBIg8b9dl28c5xFWyhBTVRp3pOg5EKY=
-github.com/googleapis/gnostic v0.2.0/go.mod h1:sJBsCZ4ayReDTBIg8b9dl28c5xFWyhBTVRp3pOg5EKY=
-github.com/googleapis/gnostic v0.3.0/go.mod h1:sJBsCZ4ayReDTBIg8b9dl28c5xFWyhBTVRp3pOg5EKY=
-github.com/googleapis/gnostic v0.3.1 h1:WeAefnSUHlBb0iJKwxFDZdbfGwkd7xRNuV+IpXMJhYk=
+github.com/googleapis/gnostic v0.1.0/go.mod h1:sJBsCZ4ayReDTBIg8b9dl28c5xFWyhBTVRp3pOg5EKY=
github.com/googleapis/gnostic v0.3.1/go.mod h1:on+2t9HRStVgn95RSsFWFz+6Q0Snyqv1awfrALZdbtU=
+github.com/googleapis/gnostic v0.4.1/go.mod h1:LRhVm6pbyptWbWbuZ38d1eyptfvIytN3ir6b65WBswg=
+github.com/googleapis/gnostic v0.5.1 h1:A8Yhf6EtqTv9RMsU6MQTyrtV1TjWlR6xU9BsZIwuTCM=
+github.com/googleapis/gnostic v0.5.1/go.mod h1:6U4PtQXGIEt/Z3h5MAT7FNofLnw9vXk2cUuW7uA/OeU=
github.com/gophercloud/gophercloud v0.1.0/go.mod h1:vxM41WHh5uqHVBMZHzuwNOHh8XEoIEcSTewFxm1c5g8=
-github.com/gophercloud/gophercloud v0.2.0 h1:lD2Bce2xBAMNNcFZ0dObTpXkGLlVIb33RPVUNVpw6ic=
-github.com/gophercloud/gophercloud v0.2.0/go.mod h1:vxM41WHh5uqHVBMZHzuwNOHh8XEoIEcSTewFxm1c5g8=
github.com/gopherjs/gopherjs v0.0.0-20181017120253-0766667cb4d1/go.mod h1:wJfORRmW1u3UXTncJ5qlYoELFm8eSnnEO6hX4iZ3EWY=
github.com/gorilla/context v1.1.1/go.mod h1:kBGZzfjB9CEq2AlWe17Uuf7NDRt0dE0s8S51q0aT7Yg=
github.com/gorilla/handlers v0.0.0-20150720190736-60c7bfde3e33/go.mod h1:Qkdc/uu4tH4g6mTK6auzZ766c4CA0Ng8+o/OAirnOIQ=
-github.com/gorilla/handlers v1.4.0/go.mod h1:Qkdc/uu4tH4g6mTK6auzZ766c4CA0Ng8+o/OAirnOIQ=
+github.com/gorilla/handlers v1.4.2/go.mod h1:Qkdc/uu4tH4g6mTK6auzZ766c4CA0Ng8+o/OAirnOIQ=
github.com/gorilla/mux v1.6.2/go.mod h1:1lud6UwP+6orDFRuTfBEV8e9/aOM/c4fVVCaMa2zaAs=
-github.com/gorilla/mux v1.7.0/go.mod h1:1lud6UwP+6orDFRuTfBEV8e9/aOM/c4fVVCaMa2zaAs=
github.com/gorilla/mux v1.7.1/go.mod h1:1lud6UwP+6orDFRuTfBEV8e9/aOM/c4fVVCaMa2zaAs=
github.com/gorilla/mux v1.7.2/go.mod h1:1lud6UwP+6orDFRuTfBEV8e9/aOM/c4fVVCaMa2zaAs=
+github.com/gorilla/mux v1.7.3/go.mod h1:1lud6UwP+6orDFRuTfBEV8e9/aOM/c4fVVCaMa2zaAs=
+github.com/gorilla/websocket v0.0.0-20170926233335-4201258b820c/go.mod h1:E7qHFY5m1UJ88s3WnNqhKjPHQ0heANvMoAMk2YaljkQ=
github.com/gorilla/websocket v1.4.0/go.mod h1:E7qHFY5m1UJ88s3WnNqhKjPHQ0heANvMoAMk2YaljkQ=
-github.com/gostaticanalysis/analysisutil v0.0.0-20190318220348-4088753ea4d3 h1:JVnpOZS+qxli+rgVl98ILOXVNbW+kb5wcxeGx8ShUIw=
-github.com/gostaticanalysis/analysisutil v0.0.0-20190318220348-4088753ea4d3/go.mod h1:eEOZF4jCKGi+aprrirO9e7WKB3beBRtWgqGunKl6pKE=
-github.com/gosuri/uitable v0.0.1/go.mod h1:tKR86bXuXPZazfOTG1FIzvjIdXzd0mo4Vtn16vt0PJo=
+github.com/gorilla/websocket v1.4.2/go.mod h1:YR8l580nyteQvAITg2hZ9XVh4b55+EU/adAjf1fMHhE=
github.com/gosuri/uitable v0.0.4/go.mod h1:tKR86bXuXPZazfOTG1FIzvjIdXzd0mo4Vtn16vt0PJo=
-github.com/gregjones/httpcache v0.0.0-20170728041850-787624de3eb7 h1:6TSoaYExHper8PYsJu23GWVNOyYRCSnIFyxKgLSZ54w=
-github.com/gregjones/httpcache v0.0.0-20170728041850-787624de3eb7/go.mod h1:FecbI9+v66THATjSRHfNgh1IVFe/9kFxbXtjV0ctIMA=
-github.com/gregjones/httpcache v0.0.0-20181110185634-c63ab54fda8f/go.mod h1:FecbI9+v66THATjSRHfNgh1IVFe/9kFxbXtjV0ctIMA=
-github.com/gregjones/httpcache v0.0.0-20190203031600-7a902570cb17 h1:prg2TTpTOcJF1jRWL2zSU1FQNgB0STAFNux8GK82y8k=
-github.com/gregjones/httpcache v0.0.0-20190203031600-7a902570cb17/go.mod h1:FecbI9+v66THATjSRHfNgh1IVFe/9kFxbXtjV0ctIMA=
-github.com/grpc-ecosystem/go-grpc-middleware v0.0.0-20190222133341-cfaf5686ec79/go.mod h1:FiyG127CGDf3tlThmgyCl78X/SZQqEOJBCDaAfeWzPs=
+github.com/gregjones/httpcache v0.0.0-20180305231024-9cad4c3443a7 h1:pdN6V1QBWetyv/0+wjACpqVH+eVULgEjkurDLq3goeM=
+github.com/gregjones/httpcache v0.0.0-20180305231024-9cad4c3443a7/go.mod h1:FecbI9+v66THATjSRHfNgh1IVFe/9kFxbXtjV0ctIMA=
github.com/grpc-ecosystem/go-grpc-middleware v1.0.0/go.mod h1:FiyG127CGDf3tlThmgyCl78X/SZQqEOJBCDaAfeWzPs=
-github.com/grpc-ecosystem/go-grpc-middleware v1.1.0/go.mod h1:f5nM7jw/oeRSadq3xCzHAvxcr8HZnzsqU6ILg/0NiiE=
+github.com/grpc-ecosystem/go-grpc-middleware v1.0.1-0.20190118093823-f849b5445de4/go.mod h1:FiyG127CGDf3tlThmgyCl78X/SZQqEOJBCDaAfeWzPs=
github.com/grpc-ecosystem/go-grpc-prometheus v1.2.0/go.mod h1:8NvIoxWQoOIhqOTXgfV/d3M/q6VIi02HzZEHgUlZvzk=
-github.com/grpc-ecosystem/grpc-gateway v1.3.0/go.mod h1:RSKVYQBd5MCa4OVpNdGskqpgL2+G+NZTnrVHpWWfpdw=
github.com/grpc-ecosystem/grpc-gateway v1.9.0/go.mod h1:vNeuVxBJEsws4ogUvrchl83t/GYV9WGTSLVdBhOQFDY=
-github.com/grpc-ecosystem/grpc-gateway v1.9.4/go.mod h1:vNeuVxBJEsws4ogUvrchl83t/GYV9WGTSLVdBhOQFDY=
-github.com/grpc-ecosystem/grpc-health-probe v0.2.1-0.20181220223928-2bf0a5b182db/go.mod h1:uBKkC2RbarFsvS5jMJHpVhTLvGlGQj9JJwkaePE3FWI=
+github.com/grpc-ecosystem/grpc-gateway v1.9.5/go.mod h1:vNeuVxBJEsws4ogUvrchl83t/GYV9WGTSLVdBhOQFDY=
+github.com/grpc-ecosystem/grpc-health-probe v0.3.2/go.mod h1:izVOQ4RWbjUR6lm4nn+VLJyQ+FyaiGmprEYgI04Gs7U=
github.com/hailocab/go-hostpool v0.0.0-20160125115350-e80d13ce29ed/go.mod h1:tMWxXQ9wFIaZeTI9F+hmhFiGpFmhOHzyShyFUhRm0H4=
+github.com/hashicorp/consul/api v1.1.0/go.mod h1:VmuI/Lkw1nC05EYQWNKwWGbkg+FbDBtguAZLlVdkD9Q=
+github.com/hashicorp/consul/api v1.3.0/go.mod h1:MmDNSzIMUjNpY/mQ398R4bk2FnqQLoPndWW5VkKPlCE=
+github.com/hashicorp/consul/sdk v0.1.1/go.mod h1:VKf9jXwCTEY1QZP2MOLRhb5i/I/ssyNV1vwHyQBF0x8=
+github.com/hashicorp/consul/sdk v0.3.0/go.mod h1:VKf9jXwCTEY1QZP2MOLRhb5i/I/ssyNV1vwHyQBF0x8=
github.com/hashicorp/errwrap v0.0.0-20141028054710-7554cd9344ce/go.mod h1:YH+1FKiLXxHSkmPseP+kNlulaMuP3n2brvKWEqk/Jc4=
github.com/hashicorp/errwrap v1.0.0/go.mod h1:YH+1FKiLXxHSkmPseP+kNlulaMuP3n2brvKWEqk/Jc4=
+github.com/hashicorp/go-cleanhttp v0.5.1/go.mod h1:JpRdi6/HCYpAwUzNwuwqhbovhLtngrth3wmdIIUrZ80=
+github.com/hashicorp/go-immutable-radix v1.0.0/go.mod h1:0y9vanUI8NX6FsYoO3zeMjhV/C5i9g4Q3DwcSNZ4P60=
+github.com/hashicorp/go-msgpack v0.5.3/go.mod h1:ahLV/dePpqEmjfWmKiqvPkv/twdG7iPBM1vqhUKIvfM=
github.com/hashicorp/go-multierror v0.0.0-20161216184304-ed905158d874/go.mod h1:JMRHfdO9jKNzS/+BTlxCjKNQHg/jZAft8U7LloJvN7I=
github.com/hashicorp/go-multierror v1.0.0/go.mod h1:dHtQlpGsu+cZNNAkkCN/P3hoUDHhCYQXV3UM06sGGrk=
+github.com/hashicorp/go-rootcerts v1.0.0/go.mod h1:K6zTfqpRlCUIjkwsN4Z+hiSfzSTQa6eBIzfwKfwNnHU=
+github.com/hashicorp/go-sockaddr v1.0.0/go.mod h1:7Xibr9yA9JjQq1JpNB2Vw7kxv8xerXegt+ozgdvDeDU=
github.com/hashicorp/go-syslog v1.0.0/go.mod h1:qPfqrKkXGihmCqbJM2mZgkZGvKG1dFdvsLplgctolz4=
-github.com/hashicorp/go-version v1.1.0/go.mod h1:fltr4n8CU8Ke44wwGCBoEymUuxUHl09ZGVZPK5anwXA=
-github.com/hashicorp/golang-lru v0.0.0-20180201235237-0fb14efe8c47/go.mod h1:/m3WP610KZHVQ1SGc6re/UDhFvYD7pJ4Ao+sR/qLZy8=
+github.com/hashicorp/go-uuid v1.0.0/go.mod h1:6SBZvOh/SIDV7/2o3Jml5SYk/TvGqwFJ/bN7x4byOro=
+github.com/hashicorp/go-uuid v1.0.1/go.mod h1:6SBZvOh/SIDV7/2o3Jml5SYk/TvGqwFJ/bN7x4byOro=
+github.com/hashicorp/go-version v1.2.0/go.mod h1:fltr4n8CU8Ke44wwGCBoEymUuxUHl09ZGVZPK5anwXA=
+github.com/hashicorp/go.net v0.0.1/go.mod h1:hjKkEWcCURg++eb33jQU7oqQcI9XDCnUzHA0oac0k90=
github.com/hashicorp/golang-lru v0.5.0/go.mod h1:/m3WP610KZHVQ1SGc6re/UDhFvYD7pJ4Ao+sR/qLZy8=
-github.com/hashicorp/golang-lru v0.5.1 h1:0hERBMJE1eitiLkihrMvRVBYAkpHzc/J3QdDN+dAcgU=
github.com/hashicorp/golang-lru v0.5.1/go.mod h1:/m3WP610KZHVQ1SGc6re/UDhFvYD7pJ4Ao+sR/qLZy8=
-github.com/hashicorp/golang-lru v0.5.3 h1:YPkqC67at8FYaadspW/6uE0COsBxS2656RLEr8Bppgk=
-github.com/hashicorp/golang-lru v0.5.3/go.mod h1:iADmTwqILo4mZ8BN3D2Q6+9jd8WM5uGBxy+E8yxSoD4=
-github.com/hashicorp/hcl v1.0.0 h1:0Anlzjpi4vEasTeNFn2mLJgTSwt0+6sfsiTG8qcWGx4=
+github.com/hashicorp/golang-lru v0.5.4 h1:YDjusn29QI/Das2iO9M0BHnIbxPeyuCHsjMW+lJfyTc=
+github.com/hashicorp/golang-lru v0.5.4/go.mod h1:iADmTwqILo4mZ8BN3D2Q6+9jd8WM5uGBxy+E8yxSoD4=
github.com/hashicorp/hcl v1.0.0/go.mod h1:E5yfLk+7swimpb2L/Alb/PJmXilQ/rhwaUYs4T20WEQ=
-github.com/heketi/heketi v9.0.0+incompatible/go.mod h1:bB9ly3RchcQqsQ9CpyaQwvva7RS5ytVoSoholZQON6o=
-github.com/heketi/rest v0.0.0-20180404230133-aa6a65207413/go.mod h1:BeS3M108VzVlmAue3lv2WcGuPAX94/KN63MUURzbYSI=
-github.com/heketi/tests v0.0.0-20151005000721-f3775cbcefd6/go.mod h1:xGMAM8JLi7UkZt1i4FQeQy0R2T8GLUwQhOP5M1gBhy4=
-github.com/heketi/utils v0.0.0-20170317161834-435bc5bdfa64/go.mod h1:RYlF4ghFZPPmk2TC5REt5OFwvfb6lzxFWrTWB+qs28s=
-github.com/helm/helm-2to3 v0.2.0/go.mod h1:jQUVAWB0bM7zNIqKPIfHFzuFSK0kHYovJrjO+hqcvRk=
-github.com/helm/helm-2to3 v0.5.1/go.mod h1:AXFpQX2cSQpss+47ROPEeu7Sm4+CRJ1jKWCEQdHP3/c=
+github.com/hashicorp/logutils v1.0.0/go.mod h1:QIAnNjmIWmVIIkWDTG1z5v++HQmx9WQRO+LraFDTW64=
+github.com/hashicorp/mdns v1.0.0/go.mod h1:tL+uN++7HEJ6SQLQ2/p+z2pH24WQKWjBPkE0mNTz8vQ=
+github.com/hashicorp/memberlist v0.1.3/go.mod h1:ajVTdAv/9Im8oMAAj5G31PhhMCZJV2pPBoIllUwCN7I=
+github.com/hashicorp/serf v0.8.2/go.mod h1:6hOLApaqBFA1NXqRQAsxw9QxuDEvNxSQRwA/JwenrHc=
github.com/hpcloud/tail v1.0.0/go.mod h1:ab1qPbhIpdTxEkNHXyeSf5vhxWSCs/tWer42PpOxQnU=
-github.com/huandu/xstrings v1.2.0/go.mod h1:DvyZB1rfVYsBIigL8HwpZgxHwXozlTgGqn63UyNX5k4=
-github.com/iancoleman/strcase v0.0.0-20190422225806-e506e3ef7365/go.mod h1:SK73tn/9oHe+/Y0h39VT4UCxmurVJkR5NA7kMEAOgSE=
+github.com/huandu/xstrings v1.3.1/go.mod h1:y5/lhBue+AyNmUVz9RLU9xbLR0o4KIIExikq4ovT0aE=
+github.com/hudl/fargo v1.3.0/go.mod h1:y3CKSmjA+wD2gak7sUSXTAoopbhU08POFhmITJgmKTg=
+github.com/iancoleman/strcase v0.0.0-20191112232945-16388991a334/go.mod h1:SK73tn/9oHe+/Y0h39VT4UCxmurVJkR5NA7kMEAOgSE=
+github.com/ianlancetaylor/demangle v0.0.0-20181102032728-5e5cf60278f6/go.mod h1:aSSvb/t6k1mPoxDqO4vJh6VOCGPwU4O0C2/Eqndh1Sc=
github.com/imdario/mergo v0.3.5/go.mod h1:2EnlNZ0deacrJVfApfmtdGgDfMuh/nq6Ok1EcJh5FfA=
github.com/imdario/mergo v0.3.6/go.mod h1:2EnlNZ0deacrJVfApfmtdGgDfMuh/nq6Ok1EcJh5FfA=
-github.com/imdario/mergo v0.3.7 h1:Y+UAYTZ7gDEuOfhxKWy+dvb5dRQ6rJjFSdX2HZY1/gI=
-github.com/imdario/mergo v0.3.7/go.mod h1:2EnlNZ0deacrJVfApfmtdGgDfMuh/nq6Ok1EcJh5FfA=
-github.com/imdario/mergo v0.3.8 h1:CGgOkSJeqMRmt0D9XLWExdT4m4F1vd3FV3VPt+0VxkQ=
github.com/imdario/mergo v0.3.8/go.mod h1:2EnlNZ0deacrJVfApfmtdGgDfMuh/nq6Ok1EcJh5FfA=
-github.com/improbable-eng/thanos v0.3.2/go.mod h1:GZewVGILKuJVPNRn7L4Zw+7X96qzFOwj63b22xYGXBE=
+github.com/imdario/mergo v0.3.10 h1:6q5mVkdH/vYmqngx7kZQTjJ5HRsx+ImorDIEQ+beJgc=
+github.com/imdario/mergo v0.3.10/go.mod h1:jmQim1M+e3UYxmgPu/WyfjB3N3VflVyUjjjwH0dnCYA=
github.com/inconshreveable/mousetrap v1.0.0 h1:Z8tu5sraLXCXIcARxBp/8cbvlwVa7Z1NHg9XEKhtSvM=
github.com/inconshreveable/mousetrap v1.0.0/go.mod h1:PxqpIevigyE2G7u3NXJIT2ANytuPF1OarO4DADm73n8=
+github.com/influxdata/influxdb1-client v0.0.0-20191209144304-8bf82d3c094d/go.mod h1:qj24IKcXYK6Iy9ceXlo3Tc+vtHo9lIhSX5JddghvEPo=
github.com/jackc/fake v0.0.0-20150926172116-812a484cc733/go.mod h1:WrMFNQdiFJ80sQsxDoMokWK1W5TQtxBFNpzWTD84ibQ=
github.com/jackc/pgx v3.2.0+incompatible/go.mod h1:0ZGrqGqkRlliWnWB4zKnWtjbSWbGkVEFm4TeybAXq+I=
-github.com/jimstudt/http-authentication v0.0.0-20140401203705-3eca13d6893a/go.mod h1:wK6yTYYcgjHE1Z1QtXACPDjcFJyBskHEdagmnq3vsP8=
-github.com/jingyugao/rowserrcheck v0.0.0-20191204022205-72ab7603b68a h1:GmsqmapfzSJkm28dhRoHz2tLRbJmqhU86IPgBtN3mmk=
-github.com/jingyugao/rowserrcheck v0.0.0-20191204022205-72ab7603b68a/go.mod h1:xRskid8CManxVta/ALEhJha/pweKBaVG6fWgc0yH25s=
-github.com/jirfag/go-printf-func-name v0.0.0-20191110105641-45db9963cdd3 h1:jNYPNLe3d8smommaoQlK7LOA5ESyUJJ+Wf79ZtA7Vp4=
-github.com/jirfag/go-printf-func-name v0.0.0-20191110105641-45db9963cdd3/go.mod h1:HEWGJkRDzjJY2sqdDwxccsGicWEf9BQOZsq2tV+xzM0=
+github.com/jessevdk/go-flags v1.4.0/go.mod h1:4FA24M0QyGHXBuZZK/XkWh8h0e1EYbRYJSGM75WSRxI=
github.com/jmespath/go-jmespath v0.0.0-20160202185014-0b12d6b521d8/go.mod h1:Nht3zPeWKUH0NzdCt2Blrr5ys8VGpn0CEB0cQHVjt7k=
github.com/jmespath/go-jmespath v0.0.0-20160803190731-bd40a432e4c7/go.mod h1:Nht3zPeWKUH0NzdCt2Blrr5ys8VGpn0CEB0cQHVjt7k=
github.com/jmespath/go-jmespath v0.0.0-20180206201540-c2b33e8439af/go.mod h1:Nht3zPeWKUH0NzdCt2Blrr5ys8VGpn0CEB0cQHVjt7k=
github.com/jmoiron/sqlx v1.2.0/go.mod h1:1FEQNm3xlJgrMD+FBdI9+xvCksHtbpVBBw5dYhBSsks=
-github.com/jmoiron/sqlx v1.2.1-0.20190826204134-d7d95172beb5/go.mod h1:1FEQNm3xlJgrMD+FBdI9+xvCksHtbpVBBw5dYhBSsks=
github.com/joefitzgerald/rainbow-reporter v0.1.0/go.mod h1:481CNgqmVHQZzdIbN52CupLJyoVwB10FQ/IQlF1pdL8=
-github.com/joho/godotenv v1.3.0 h1:Zjp+RcGpHhGlrMbJzXTrZZPrWj+1vfm90La1wgB6Bhc=
github.com/joho/godotenv v1.3.0/go.mod h1:7hK45KPybAkOC6peb+G5yklZfMxEjkZhHbwpqxOKXbg=
github.com/jonboulle/clockwork v0.1.0/go.mod h1:Ii8DK3G1RaLaWxj9trq07+26W01tbo22gdxWY5EU2bo=
github.com/json-iterator/go v1.1.6/go.mod h1:+SdeFBvtyEkXs7REEP0seUULqWtbJapLOCVDaaPEHmU=
-github.com/json-iterator/go v1.1.7 h1:KfgG9LzI+pYjr4xvmz/5H4FXjokeP+rlHLhv3iH62Fo=
github.com/json-iterator/go v1.1.7/go.mod h1:KdQUCv79m/52Kvf8AW2vK1V8akMuk1QjK/uOdHXbAo4=
-github.com/json-iterator/go v1.1.9 h1:9yzud/Ht36ygwatGx56VwCZtlI/2AD15T1X2sjSuGns=
-github.com/json-iterator/go v1.1.9/go.mod h1:KdQUCv79m/52Kvf8AW2vK1V8akMuk1QjK/uOdHXbAo4=
-github.com/jsonnet-bundler/jsonnet-bundler v0.1.0/go.mod h1:YKsSFc9VFhhLITkJS3X2PrRqWG9u2Jq99udTdDjQLfM=
+github.com/json-iterator/go v1.1.8/go.mod h1:KdQUCv79m/52Kvf8AW2vK1V8akMuk1QjK/uOdHXbAo4=
+github.com/json-iterator/go v1.1.10 h1:Kz6Cvnvv2wGdaG/V8yMvfkmNiXq9Ya2KUv4rouJJr68=
+github.com/json-iterator/go v1.1.10/go.mod h1:KdQUCv79m/52Kvf8AW2vK1V8akMuk1QjK/uOdHXbAo4=
github.com/jstemmer/go-junit-report v0.0.0-20190106144839-af01ea7f8024/go.mod h1:6v2b51hI/fHJwM22ozAgKL4VKDeJcHhJFhtBdhmNjmU=
+github.com/jstemmer/go-junit-report v0.9.1/go.mod h1:Brl9GWCQeLvo8nXZwPNNblvFj/XSXhF0NWZEnDohbsk=
github.com/jtolds/gls v4.20.0+incompatible/go.mod h1:QJZ7F/aHp+rZTRtaJ1ow/lLfFfVYBRgL+9YlvaHOwJU=
github.com/julienschmidt/httprouter v1.2.0/go.mod h1:SYymIcj16QtmaHHD7aYtjjsJG7VTCxuUUipMqKk8s4w=
-github.com/kardianos/osext v0.0.0-20170510131534-ae77be60afb1/go.mod h1:1NbS8ALrpOvjt0rHPNLyCIeMtbizbir8U//inJ+zuB8=
github.com/kardianos/osext v0.0.0-20190222173326-2bc1f35cddc0/go.mod h1:1NbS8ALrpOvjt0rHPNLyCIeMtbizbir8U//inJ+zuB8=
-github.com/karrick/godirwalk v1.7.5/go.mod h1:2c9FRhkDxdIbgkOnCEvnSWs71Bhugbl46shStcFDJ34=
-github.com/karrick/godirwalk v1.10.12/go.mod h1:RoGL9dQei4vP9ilrpETWE8CLOZ1kiN0LhBygSwrAsHA=
github.com/kisielk/errcheck v1.1.0/go.mod h1:EZBBE59ingxPouuu3KfxchcWSUPOHkagtvWXihfKN4Q=
github.com/kisielk/errcheck v1.2.0/go.mod h1:/BMXB+zMLi60iA8Vv6Ksmxu/1UDYcXs4uQLJ+jE2L00=
-github.com/kisielk/gotool v1.0.0 h1:AV2c/EiW3KqPNT9ZKl07ehoAGi4C5/01Cfbblndcapg=
github.com/kisielk/gotool v1.0.0/go.mod h1:XhKaO+MFFWcvkIS/tQcRk01m1F5IRFswLeQ+oQHNcck=
-github.com/klauspost/compress v1.4.0/go.mod h1:RyIbtBH6LamlWaDj8nUwkbUhJ87Yi3uG0guNDohfE1A=
-github.com/klauspost/compress v1.4.1/go.mod h1:RyIbtBH6LamlWaDj8nUwkbUhJ87Yi3uG0guNDohfE1A=
-github.com/klauspost/cpuid v0.0.0-20180405133222-e7e905edc00e/go.mod h1:Pj4uuM528wm8OyEC2QMXAi2YiTZ96dNQPGgoMS4s3ek=
-github.com/klauspost/cpuid v1.2.0/go.mod h1:Pj4uuM528wm8OyEC2QMXAi2YiTZ96dNQPGgoMS4s3ek=
github.com/konsorten/go-windows-terminal-sequences v1.0.1/go.mod h1:T0+1ngSBFLxvqU3pZ+m/2kptfBszLMUkC4ZK/EgS/cQ=
-github.com/konsorten/go-windows-terminal-sequences v1.0.2 h1:DB17ag19krx9CFsz4o3enTrPXyIXCl+2iCXH/aMAp9s=
github.com/konsorten/go-windows-terminal-sequences v1.0.2/go.mod h1:T0+1ngSBFLxvqU3pZ+m/2kptfBszLMUkC4ZK/EgS/cQ=
+github.com/konsorten/go-windows-terminal-sequences v1.0.3 h1:CE8S1cTafDpPvMhIxNJKvHsGVBgn1xWYf1NbHQhywc8=
+github.com/konsorten/go-windows-terminal-sequences v1.0.3/go.mod h1:T0+1ngSBFLxvqU3pZ+m/2kptfBszLMUkC4ZK/EgS/cQ=
github.com/kr/logfmt v0.0.0-20140226030751-b84e30acd515/go.mod h1:+0opPa2QZZtGFBFZlji/RkVcI2GknAs/DXo4wKdlNEc=
github.com/kr/pretty v0.1.0/go.mod h1:dAy3ld7l9f0ibDNOQOHHMYYIIbhfbHSm3C4ZsoJORNo=
+github.com/kr/pretty v0.2.0 h1:s5hAObm+yFO5uHYt5dYjxi2rXrsnmRpJx4OYvIWUaQs=
+github.com/kr/pretty v0.2.0/go.mod h1:ipq/a2n7PKx3OHsz4KJII5eveXtPO4qwEXGdVfWzfnI=
github.com/kr/pty v1.1.1/go.mod h1:pFQYn66WHrOpPYNljwOMqo10TkYh1fy3cYio2l3bCsQ=
-github.com/kr/pty v1.1.4/go.mod h1:pFQYn66WHrOpPYNljwOMqo10TkYh1fy3cYio2l3bCsQ=
github.com/kr/pty v1.1.5/go.mod h1:9r2w37qlBe7rQ6e1fg1S/9xpWHSnaqNdHD3WcMdbPDA=
-github.com/kr/pty v1.1.8/go.mod h1:O1sed60cT9XZ5uDucP5qwvh+TE3NnUj51EiZO/lmSfw=
+github.com/kr/text v0.1.0 h1:45sCR5RtlFHMR4UwH9sdQ5TC8v0qDQCHnXt+kaKSTVE=
github.com/kr/text v0.1.0/go.mod h1:4Jbv+DJW3UT/LiOwJeYQe1efqtUx/iVham/4vfdArNI=
-github.com/kr/text v0.2.0/go.mod h1:eLer722TekiGuMkidMxC/pM04lWEeraHUUmBw8l2grE=
github.com/kshvakov/clickhouse v1.3.5/go.mod h1:DMzX7FxRymoNkVgizH0DWAL8Cur7wHLgx3MUnGwJqpE=
-github.com/kylelemons/godebug v0.0.0-20170820004349-d65d576e9348/go.mod h1:B69LEHPfb2qLo0BaaOLcbitczOKLWTsrBG9LczfCD4k=
+github.com/lann/builder v0.0.0-20180802200727-47ae307949d0/go.mod h1:dXGbAdH5GtBTC4WfIxhKZfyBF/HBFgRZSWwZ9g/He9o=
+github.com/lann/ps v0.0.0-20150810152359-62de8c46ede0/go.mod h1:vmVJ0l/dxyfGW6FmdpVm2joNMFikkuWg0EoCKLGUMNw=
github.com/lib/pq v1.0.0/go.mod h1:5WUZQaWbwv1U+lTReE5YruASi9Al49XbQIvNi/34Woo=
github.com/lib/pq v1.2.0/go.mod h1:5WUZQaWbwv1U+lTReE5YruASi9Al49XbQIvNi/34Woo=
-github.com/libopenstorage/openstorage v1.0.0/go.mod h1:Sp1sIObHjat1BeXhfMqLZ14wnOzEhNx2YQedreMcUyc=
+github.com/lib/pq v1.8.0/go.mod h1:AlVN5x4E4T544tWzH6hKfbfQvm3HdbOxrmggDNAPY9o=
+github.com/liggitt/tabwriter v0.0.0-20181228230101-89fcab3d43de h1:9TO3cAIGXtEhnIaL+V+BEER86oLrvS+kWobKpbJuye0=
github.com/liggitt/tabwriter v0.0.0-20181228230101-89fcab3d43de/go.mod h1:zAbeS9B/r2mtpb6U+EI2rYA5OAXxsYw6wTamcNW+zcE=
+github.com/lightstep/lightstep-tracer-common/golang/gogo v0.0.0-20190605223551-bc2310a04743/go.mod h1:qklhhLq1aX+mtWk9cPHPzaBjWImj5ULL6C7HFJtXQMM=
+github.com/lightstep/lightstep-tracer-go v0.18.1/go.mod h1:jlF1pusYV4pidLvZ+XD0UBX0ZE6WURAspgAczcDHrL4=
github.com/lithammer/dedent v1.1.0/go.mod h1:jrXYCQtgg0nJiN+StA2KgR7w6CiQNv9Fd/Z9BP0jIOc=
-github.com/logrusorgru/aurora v0.0.0-20181002194514-a7b3b318ed4e/go.mod h1:7rIyQOR62GCctdiQpZ/zOJlFyk6y+94wXzv6RNZgaR4=
-github.com/lpabon/godbc v0.1.1/go.mod h1:Jo9QV0cf3U6jZABgiJ2skINAXb9j8m51r07g4KI92ZA=
-github.com/lucas-clemente/aes12 v0.0.0-20171027163421-cd47fb39b79f/go.mod h1:JpH9J1c9oX6otFSgdUHwUBUizmKlrMjxWnIAjff4m04=
-github.com/lucas-clemente/quic-clients v0.1.0/go.mod h1:y5xVIEoObKqULIKivu+gD/LU90pL73bTdtQjPBvtCBk=
-github.com/lucas-clemente/quic-go v0.10.2/go.mod h1:hvaRS9IHjFLMq76puFJeWNfmn+H70QZ/CXoxqw9bzao=
-github.com/lucas-clemente/quic-go-certificates v0.0.0-20160823095156-d2f86524cced/go.mod h1:NCcRLrOTZbzhZvixZLlERbJtDtYsmMw8Jc4vS8Z0g58=
+github.com/lyft/protoc-gen-validate v0.0.13/go.mod h1:XbGvPuh87YZc5TdIa2/I4pLk0QoUACkjt2znoq26NVQ=
github.com/magiconair/properties v1.8.0/go.mod h1:PppfXfuXeibc/6YijjN8zIbojt8czPbwD3XqdrwzmxQ=
-github.com/magiconair/properties v1.8.1 h1:ZC2Vc7/ZFkGmsVC9KvOjumD+G5lXy2RtTKyzRKO2BQ4=
github.com/magiconair/properties v1.8.1/go.mod h1:PppfXfuXeibc/6YijjN8zIbojt8czPbwD3XqdrwzmxQ=
-github.com/mailgun/mailgun-go/v3 v3.6.0 h1:oQWhyDTFjSiuO6vx1PRlfLZ7Fu+oK0Axn0UTREh3k/g=
-github.com/mailgun/mailgun-go/v3 v3.6.0/go.mod h1:E81I5Agcfi/u1szdehi6p6ttdRX/UD3Rq2SrUzwyFIU=
+github.com/mailgun/mailgun-go/v3 v3.6.4 h1:+cvbZRgLSHivbz/w1iWLmxVl6Bqf4geD2D7QMj4+8PE=
+github.com/mailgun/mailgun-go/v3 v3.6.4/go.mod h1:ZjVnH8S0dR2BLjvkZc/rxwerdcirzlA12LQDuGAadR0=
+github.com/mailru/easyjson v0.0.0-20160728113105-d5b7844b561a/go.mod h1:C1wdFJiN94OJF2b5HbByQZoLdCWB1Yqtg26g4irojpc=
github.com/mailru/easyjson v0.0.0-20180823135443-60711f1a8329/go.mod h1:C1wdFJiN94OJF2b5HbByQZoLdCWB1Yqtg26g4irojpc=
github.com/mailru/easyjson v0.0.0-20190312143242-1de009706dbe/go.mod h1:C1wdFJiN94OJF2b5HbByQZoLdCWB1Yqtg26g4irojpc=
github.com/mailru/easyjson v0.0.0-20190614124828-94de47d64c63/go.mod h1:C1wdFJiN94OJF2b5HbByQZoLdCWB1Yqtg26g4irojpc=
-github.com/mailru/easyjson v0.0.0-20190626092158-b2ccc519800e h1:hB2xlXdHp/pmPZq0y3QnmWAArdw9PqbmotexnWx/FU8=
github.com/mailru/easyjson v0.0.0-20190626092158-b2ccc519800e/go.mod h1:C1wdFJiN94OJF2b5HbByQZoLdCWB1Yqtg26g4irojpc=
github.com/mailru/easyjson v0.7.0 h1:aizVhC/NAAcKWb+5QsU1iNOZb4Yws5UO2I+aIprQITM=
github.com/mailru/easyjson v0.7.0/go.mod h1:KAzv3t3aY1NaHWoQz1+4F1ccyAH66Jk7yos7ldAVICs=
-github.com/maorfr/helm-plugin-utils v0.0.0-20181205064038-588190cb5e3b/go.mod h1:p3gwmRSFqbWw6plBpR0sKl3n3vpu8kX70gvCJKMvvCA=
-github.com/maorfr/helm-plugin-utils v0.0.0-20200216074820-36d2fcf6ae86/go.mod h1:p3gwmRSFqbWw6plBpR0sKl3n3vpu8kX70gvCJKMvvCA=
-github.com/maratori/testpackage v1.0.1 h1:QtJ5ZjqapShm0w5DosRjg0PRlSdAdlx+W6cCKoALdbQ=
-github.com/maratori/testpackage v1.0.1/go.mod h1:ddKdw+XG0Phzhx8BFDTKgpWP4i7MpApTE5fXSKAqwDU=
-github.com/markbates/inflect v1.0.4 h1:5fh1gzTFhfae06u3hzHYO9xe3l3v3nW5Pwt3naLTP5g=
github.com/markbates/inflect v1.0.4/go.mod h1:1fR9+pO2KHEO9ZRtto13gDwwZaAKstQzferVeWqbgNs=
github.com/marstr/guid v1.1.0/go.mod h1:74gB1z2wpxxInTG6yaqA7KrtM0NZ+RbrcqDvYHefzho=
-github.com/marten-seemann/qtls v0.2.3/go.mod h1:xzjG7avBwGGbdZ8dTGxlBnLArsVKLvwmjgmPuiQEcYk=
-github.com/martinlindhe/base36 v1.0.0/go.mod h1:+AtEs8xrBpCeYgSLoY/aJ6Wf37jtBuR0s35750M27+8=
-github.com/matoous/godox v0.0.0-20190911065817-5d6d842e92eb h1:RHba4YImhrUVQDHUCe2BNSOz4tVy2yGyXhvYDvxGgeE=
-github.com/matoous/godox v0.0.0-20190911065817-5d6d842e92eb/go.mod h1:1BELzlh859Sh1c6+90blK8lbYy0kwQf1bYlBhBysy1s=
-github.com/mattbaird/jsonpatch v0.0.0-20171005235357-81af80346b1a/go.mod h1:M1qoD/MqPgTZIk0EWKB38wE28ACRfVcn+cU08jyArI0=
github.com/mattn/go-colorable v0.0.9/go.mod h1:9vuHe8Xs5qXnSaW/c/ABM9alt+Vo+STaOChaDxuIBZU=
-github.com/mattn/go-colorable v0.1.4 h1:snbPLB8fVfU9iwbbo30TPtbLRzwWu6aJS6Xh4eaaviA=
-github.com/mattn/go-colorable v0.1.4/go.mod h1:U0ppj6V5qS13XJ6of8GYAs25YV2eR4EVcfRqFIhoBtE=
+github.com/mattn/go-colorable v0.1.2/go.mod h1:U0ppj6V5qS13XJ6of8GYAs25YV2eR4EVcfRqFIhoBtE=
github.com/mattn/go-isatty v0.0.3/go.mod h1:M+lRXTBqGeGNdLjl/ufCoiOlB5xdOkqRJdNxMWT7Zi4=
-github.com/mattn/go-isatty v0.0.6/go.mod h1:Iq45c/XA43vh69/j3iqttzPXn0bhXyGjM0Hdxcsrc5s=
+github.com/mattn/go-isatty v0.0.4/go.mod h1:M+lRXTBqGeGNdLjl/ufCoiOlB5xdOkqRJdNxMWT7Zi4=
github.com/mattn/go-isatty v0.0.8/go.mod h1:Iq45c/XA43vh69/j3iqttzPXn0bhXyGjM0Hdxcsrc5s=
-github.com/mattn/go-isatty v0.0.12 h1:wuysRhFDzyxgEmMf5xjvJ2M9dZoWAXNNr5LSBS7uHXY=
-github.com/mattn/go-isatty v0.0.12/go.mod h1:cbi8OIDigv2wuxKPP5vlRcQ1OAZbq2CE4Kysco4FUpU=
+github.com/mattn/go-oci8 v0.0.7/go.mod h1:wjDx6Xm9q7dFtHJvIlrI99JytznLw5wQ4R+9mNXJwGI=
+github.com/mattn/go-runewidth v0.0.2/go.mod h1:LwmH8dsx7+W8Uxz3IHJYH5QSwggIsqBzpuz5H//U1FU=
github.com/mattn/go-runewidth v0.0.4/go.mod h1:LwmH8dsx7+W8Uxz3IHJYH5QSwggIsqBzpuz5H//U1FU=
-github.com/mattn/go-shellwords v1.0.5/go.mod h1:3xCvwCdWdlDJUrvuMn7Wuy9eWs4pE8vqg+NOMyg4B2o=
-github.com/mattn/go-shellwords v1.0.9/go.mod h1:EZzvwXDESEeg03EKmM+RmDnNOPKG4lLtQsUlTZDWQ8Y=
+github.com/mattn/go-shellwords v1.0.10/go.mod h1:EZzvwXDESEeg03EKmM+RmDnNOPKG4lLtQsUlTZDWQ8Y=
github.com/mattn/go-sqlite3 v1.9.0/go.mod h1:FPy6KqzDD04eiIsT53CuJW3U88zkxoIYsOqkbpncsNc=
github.com/mattn/go-sqlite3 v1.10.0/go.mod h1:FPy6KqzDD04eiIsT53CuJW3U88zkxoIYsOqkbpncsNc=
-github.com/mattn/goveralls v0.0.2/go.mod h1:8d1ZMHsd7fW6IRPKQh46F2WRpyib5/X4FOpevwGNQEw=
-github.com/matttproud/golang_protobuf_extensions v1.0.1 h1:4hp9jkHxhMHkqkrB3Ix0jegS5sx/RkqARlsWZ6pIwiU=
+github.com/mattn/go-sqlite3 v1.12.0/go.mod h1:FPy6KqzDD04eiIsT53CuJW3U88zkxoIYsOqkbpncsNc=
github.com/matttproud/golang_protobuf_extensions v1.0.1/go.mod h1:D8He9yQNgCq6Z5Ld7szi9bcBfOoFv/3dc6xSMkL2PC0=
-github.com/maxbrunsfeld/counterfeiter/v6 v6.2.1/go.mod h1:F9YacGpnZbLQMzuPI0rR6op21YvNu/RjL705LJJpM3k=
+github.com/matttproud/golang_protobuf_extensions v1.0.2-0.20181231171920-c182affec369 h1:I0XW9+e1XWDxdcEniV4rQAIOPUGDq67JSCiRCgGCZLI=
+github.com/matttproud/golang_protobuf_extensions v1.0.2-0.20181231171920-c182affec369/go.mod h1:BSXmuO+STAnVfrANrmjBb36TMTDstsz7MSK+HVaYKv4=
github.com/maxbrunsfeld/counterfeiter/v6 v6.2.2/go.mod h1:eD9eIE7cdwcMi9rYluz88Jz2VyhSmden33/aXg4oVIY=
-github.com/mesos/mesos-go v0.0.9/go.mod h1:kPYCMQ9gsOXVAle1OsoY4I1+9kPu8GHkf88aV59fDr4=
-github.com/mholt/certmagic v0.6.2-0.20190624175158-6a42ef9fe8c2/go.mod h1:g4cOPxcjV0oFq3qwpjSA30LReKD8AoIfwAY9VvG35NY=
-github.com/miekg/dns v0.0.0-20181005163659-0d29b283ac0f/go.mod h1:W1PPwlIAgtquWBMBEV9nkV9Cazfe8ScdGz/Lj7v3Nrg=
-github.com/miekg/dns v1.1.3/go.mod h1:W1PPwlIAgtquWBMBEV9nkV9Cazfe8ScdGz/Lj7v3Nrg=
-github.com/miekg/dns v1.1.4/go.mod h1:W1PPwlIAgtquWBMBEV9nkV9Cazfe8ScdGz/Lj7v3Nrg=
+github.com/miekg/dns v1.0.14/go.mod h1:W1PPwlIAgtquWBMBEV9nkV9Cazfe8ScdGz/Lj7v3Nrg=
github.com/mikefarah/yaml/v2 v2.4.0/go.mod h1:ahVqZF4n1W4NqwvVnZzC4es67xsW9uR/RRf2RRxieJU=
github.com/mikefarah/yq/v2 v2.4.1/go.mod h1:i8SYf1XdgUvY2OFwSqGAtWOOgimD2McJ6iutoxRm4k0=
-github.com/mindprince/gonvml v0.0.0-20171110221305-fee913ce8fb2/go.mod h1:2eu9pRWp8mo84xCg6KswZ+USQHjwgRhNp06sozOdsTY=
-github.com/mistifyio/go-zfs v2.1.1+incompatible/go.mod h1:8AuVvqP/mXw1px98n46wfvcGfQ4ci2FwoAjKYxuo3Z4=
+github.com/mitchellh/cli v1.0.0/go.mod h1:hNIlj7HEI86fIcpObd7a0FcrxTWetlwJDGcceTlRvqc=
github.com/mitchellh/copystructure v1.0.0/go.mod h1:SNtv71yrdKgLRyLFxmLdkAbkKEFWgYaq1OVrnRcwhnw=
-github.com/mitchellh/go-homedir v1.1.0 h1:lukF9ziXFxDFPkA1vsr5zpc1XuPDn/wFntq5mG+4E0Y=
+github.com/mitchellh/go-homedir v1.0.0/go.mod h1:SfyaCUpYCn1Vlf4IUYiD9fPX4A5wJrkLzIz1N1q0pr0=
github.com/mitchellh/go-homedir v1.1.0/go.mod h1:SfyaCUpYCn1Vlf4IUYiD9fPX4A5wJrkLzIz1N1q0pr0=
-github.com/mitchellh/go-ps v0.0.0-20190716172923-621e5597135b/go.mod h1:r1VsdOzOPt1ZSrGZWFoNhsAedKnEd6r9Np1+5blZCWk=
+github.com/mitchellh/go-testing-interface v1.0.0/go.mod h1:kRemZodwjscx+RGhAo8eIhFbs2+BFgRtFPeD/KE+zxI=
github.com/mitchellh/go-wordwrap v1.0.0/go.mod h1:ZXFpozHsX6DPmq2I0TCekCxypsnAUbP2oI0UX1GXzOo=
-github.com/mitchellh/hashstructure v0.0.0-20170609045927-2bca23e0e452/go.mod h1:QjSHrPWS+BGUVBYkbTZWEnOh3G1DutKwClXU/ABz6AQ=
-github.com/mitchellh/hashstructure v1.0.0/go.mod h1:QjSHrPWS+BGUVBYkbTZWEnOh3G1DutKwClXU/ABz6AQ=
-github.com/mitchellh/mapstructure v1.1.2 h1:fmNYVwqnSfB9mZU6OS2O6GsXM+wcskZDuKQzvN1EDeE=
+github.com/mitchellh/gox v0.4.0/go.mod h1:Sd9lOJ0+aimLBi73mGofS1ycjY8lL3uZM3JPS42BGNg=
+github.com/mitchellh/iochan v1.0.0/go.mod h1:JwYml1nuB7xOzsp52dPpHFffvOCDupsG0QubkSMEySY=
+github.com/mitchellh/mapstructure v0.0.0-20160808181253-ca63d7c062ee/go.mod h1:FVVH3fgwuzCH5S8UJGiWEs2h04kUh9fWfEaFds41c1Y=
github.com/mitchellh/mapstructure v1.1.2/go.mod h1:FVVH3fgwuzCH5S8UJGiWEs2h04kUh9fWfEaFds41c1Y=
github.com/mitchellh/osext v0.0.0-20151018003038-5e2d6d41470f/go.mod h1:OkQIRizQZAeMln+1tSwduZz7+Af5oFlKirV/MSYes2A=
github.com/mitchellh/reflectwalk v1.0.0/go.mod h1:mSTlrgnPZtwu0c4WaC2kGObEpuNDbx0jmZXqmk4esnw=
-github.com/moby/moby v0.7.3-0.20190826074503-38ab9da00309/go.mod h1:fDXVQ6+S340veQPv35CzDahGBmHsiclFwfEygB/TWMc=
+github.com/moby/term v0.0.0-20200312100748-672ec06f55cd/go.mod h1:DdlQx2hp0Ss5/fLikoLlEeIYiATotOjgB//nb973jeo=
github.com/modern-go/concurrent v0.0.0-20180228061459-e0a39a4cb421/go.mod h1:6dJC0mAP4ikYIbvyc7fijjWJddQyLn8Ig3JB5CqoB9Q=
github.com/modern-go/concurrent v0.0.0-20180306012644-bacd9c7ef1dd h1:TRLaZ9cD/w8PVh93nsPXa1VrQ6jlwL5oN8l14QlcNfg=
github.com/modern-go/concurrent v0.0.0-20180306012644-bacd9c7ef1dd/go.mod h1:6dJC0mAP4ikYIbvyc7fijjWJddQyLn8Ig3JB5CqoB9Q=
github.com/modern-go/reflect2 v0.0.0-20180701023420-4b7aa43c6742/go.mod h1:bx2lNnkwVCuqBIxFjflWJWanXIb3RllmbCylyMrvgv0=
github.com/modern-go/reflect2 v1.0.1 h1:9f412s+6RmYXLWZSEzVVgPGK7C2PphHj5RJrvfx9AWI=
github.com/modern-go/reflect2 v1.0.1/go.mod h1:bx2lNnkwVCuqBIxFjflWJWanXIb3RllmbCylyMrvgv0=
-github.com/mohae/deepcopy v0.0.0-20170603005431-491d3605edfb/go.mod h1:TaXosZuwdSHYgviHp1DAtfrULt5eUgsSMsZf+YrPgl8=
github.com/morikuni/aec v0.0.0-20170113033406-39771216ff4c/go.mod h1:BbKIizmSmc5MMPqRYbxO4ZU0S0+P200+tUnFx7PXmsc=
github.com/morikuni/aec v1.0.0/go.mod h1:BbKIizmSmc5MMPqRYbxO4ZU0S0+P200+tUnFx7PXmsc=
-github.com/mozilla/tls-observatory v0.0.0-20200220173314-aae45faa4006/go.mod h1:SrKMQvPiws7F7iqYp8/TX+IhxCYhzr6N/1yb8cwHsGk=
-github.com/mrunalp/fileutils v0.0.0-20160930181131-4ee1cc9a8058/go.mod h1:x8F1gnqOkIEiO4rqoeEEEqQbo7HjGMTvyoq3gej4iT0=
github.com/munnerz/goautoneg v0.0.0-20120707110453-a547fc61f48d/go.mod h1:+n7T8mK8HuQTcFwEeznm/DIxMOiR9yIdICNftLE1DvQ=
-github.com/munnerz/goautoneg v0.0.0-20190414153302-2ae31c8b6b30/go.mod h1:+n7T8mK8HuQTcFwEeznm/DIxMOiR9yIdICNftLE1DvQ=
-github.com/mvdan/xurls v1.1.0/go.mod h1:tQlNn3BED8bE/15hnSL2HLkDeLWpNPAwtw7wkEq44oU=
+github.com/munnerz/goautoneg v0.0.0-20191010083416-a7dc8b61c822/go.mod h1:+n7T8mK8HuQTcFwEeznm/DIxMOiR9yIdICNftLE1DvQ=
github.com/mwitkow/go-conntrack v0.0.0-20161129095857-cc309e4a2223/go.mod h1:qRWi+5nqEBWmkhHvq77mSJWrCKwh8bxhgT7d/eI7P4U=
github.com/mxk/go-flowrate v0.0.0-20140419014527-cca7078d478f/go.mod h1:ZdcZmHo+o7JKHSa8/e818NopupXU1YMK5fe1lsApnBw=
-github.com/nakabonne/nestif v0.3.0 h1:+yOViDGhg8ygGrmII72nV9B/zGxY188TYpfolntsaPw=
-github.com/nakabonne/nestif v0.3.0/go.mod h1:dI314BppzXjJ4HsCnbo7XzrJHPszZsjnk5wEBSYHI2c=
github.com/nakagami/firebirdsql v0.0.0-20190310045651-3c02a58cfed8/go.mod h1:86wM1zFnC6/uDBfZGNwB65O+pR2OFi5q/YQaEUid1qA=
-github.com/naoina/go-stringutil v0.1.0/go.mod h1:XJ2SJL9jCtBh+P9q5btrd/Ylo8XwT/h1USek5+NqSA0=
-github.com/naoina/toml v0.1.1/go.mod h1:NBIhNtsFMo3G2szEBne+bO4gS192HuIYRqfvOWb4i1E=
-github.com/nbutton23/zxcvbn-go v0.0.0-20180912185939-ae427f1e4c1d h1:AREM5mwr4u1ORQBMvzfzBgpsctsbQikCVpvC+tX285E=
-github.com/nbutton23/zxcvbn-go v0.0.0-20180912185939-ae427f1e4c1d/go.mod h1:o96djdrsSGy3AWPyBgZMAGfxZNfgntdJG+11KU4QvbU=
+github.com/nats-io/jwt v0.3.0/go.mod h1:fRYCDE99xlTsqUzISS1Bi75UBJ6ljOJQOAAu5VglpSg=
+github.com/nats-io/jwt v0.3.2/go.mod h1:/euKqTS1ZD+zzjYrY7pseZrTtWQSjujC7xjPc8wL6eU=
+github.com/nats-io/nats-server/v2 v2.1.2/go.mod h1:Afk+wRZqkMQs/p45uXdrVLuab3gwv3Z8C4HTBu8GD/k=
+github.com/nats-io/nats.go v1.9.1/go.mod h1:ZjDU1L/7fJ09jvUSRVBR2e7+RnLiiIQyqyzEE/Zbp4w=
+github.com/nats-io/nkeys v0.1.0/go.mod h1:xpnFELMwJABBLVhffcfd1MZx6VsNRFpEugbxziKVo7w=
+github.com/nats-io/nkeys v0.1.3/go.mod h1:xpnFELMwJABBLVhffcfd1MZx6VsNRFpEugbxziKVo7w=
+github.com/nats-io/nuid v1.0.1/go.mod h1:19wcPz3Ph3q0Jbyiqsd0kePYG7A95tJPxeL+1OSON2c=
github.com/ncw/swift v1.0.47/go.mod h1:23YIA4yWVnGwv2dQlN4bB7egfYX6YLn0Yo/S6zZO/ZM=
-github.com/niemeyer/pretty v0.0.0-20200227124842-a10e7caefd8e/go.mod h1:zD1mROLANZcx1PVRCS0qkT7pwLkGfwJo4zjcN/Tysno=
+github.com/nxadm/tail v1.4.4 h1:DQuhQpB1tVlglWS2hLQ5OV6B5r8aGxSrPc5Qo6uTN78=
+github.com/nxadm/tail v1.4.4/go.mod h1:kenIhsEOeOJmVchQTgglprH7qJGnHDVpk1VPCcaMI8A=
+github.com/oklog/oklog v0.3.2/go.mod h1:FCV+B7mhrz4o+ueLpx+KqkyXRGMWOYEvfiXtdGtbWGs=
github.com/oklog/run v1.0.0/go.mod h1:dlhp/R75TPv97u0XWUtDeV/lRKWPKSdTuV0TZvrmrQA=
github.com/oklog/ulid v1.3.1/go.mod h1:CirwcVhetQ6Lv90oh/F+FBtV6XMibvdAFo93nm5qn4U=
-github.com/onsi/ginkgo v1.4.0/go.mod h1:lLunBs/Ym6LB5Z9jYTR76FiuTmxDTDusOGeTQH+WWjE=
+github.com/olekukonko/tablewriter v0.0.0-20170122224234-a0225b3f23b5/go.mod h1:vsDQFd/mU46D+Z4whnwzcISnGGzXWMclvtLoiIKAKIo=
+github.com/olekukonko/tablewriter v0.0.1/go.mod h1:vsDQFd/mU46D+Z4whnwzcISnGGzXWMclvtLoiIKAKIo=
+github.com/olekukonko/tablewriter v0.0.2/go.mod h1:rSAaSIOAGT9odnlyGlUfAJaoc5w2fSBUmeGDbRWPxyQ=
+github.com/onsi/ginkgo v0.0.0-20170829012221-11459a886d9c/go.mod h1:lLunBs/Ym6LB5Z9jYTR76FiuTmxDTDusOGeTQH+WWjE=
github.com/onsi/ginkgo v1.6.0/go.mod h1:lLunBs/Ym6LB5Z9jYTR76FiuTmxDTDusOGeTQH+WWjE=
github.com/onsi/ginkgo v1.7.0/go.mod h1:lLunBs/Ym6LB5Z9jYTR76FiuTmxDTDusOGeTQH+WWjE=
github.com/onsi/ginkgo v1.8.0/go.mod h1:lLunBs/Ym6LB5Z9jYTR76FiuTmxDTDusOGeTQH+WWjE=
github.com/onsi/ginkgo v1.10.1/go.mod h1:lLunBs/Ym6LB5Z9jYTR76FiuTmxDTDusOGeTQH+WWjE=
+github.com/onsi/ginkgo v1.11.0/go.mod h1:lLunBs/Ym6LB5Z9jYTR76FiuTmxDTDusOGeTQH+WWjE=
github.com/onsi/ginkgo v1.12.0/go.mod h1:oUhWkIvk5aDxtKvDDuw8gItl8pKl42LzjC9KZE0HfGg=
-github.com/onsi/gomega v1.3.0/go.mod h1:C1qb7wdrVGGVU+Z6iS04AVkA3Q65CEZX59MT0QO5uiA=
+github.com/onsi/ginkgo v1.12.1/go.mod h1:zj2OWP4+oCPe1qIXoGWkgMRwljMUYCdkwsT2108oapk=
+github.com/onsi/ginkgo v1.14.1 h1:jMU0WaQrP0a/YAEq8eJmJKjBoMs+pClEr1vDMlM/Do4=
+github.com/onsi/ginkgo v1.14.1/go.mod h1:iSB4RoI2tjJc9BBv4NKIKWKya62Rps+oPG/Lv9klQyY=
+github.com/onsi/gomega v0.0.0-20170829124025-dcabb60a477c/go.mod h1:C1qb7wdrVGGVU+Z6iS04AVkA3Q65CEZX59MT0QO5uiA=
github.com/onsi/gomega v1.4.3/go.mod h1:ex+gbHU/CVuBBDIJjb2X0qEXbFg53c61hWP/1CpauHY=
github.com/onsi/gomega v1.5.0/go.mod h1:ex+gbHU/CVuBBDIJjb2X0qEXbFg53c61hWP/1CpauHY=
github.com/onsi/gomega v1.7.0/go.mod h1:ex+gbHU/CVuBBDIJjb2X0qEXbFg53c61hWP/1CpauHY=
github.com/onsi/gomega v1.7.1/go.mod h1:XdKZgCCFLUoM/7CFJVPcG8C1xQ1AJ0vpAezJrB7JYyY=
+github.com/onsi/gomega v1.8.1/go.mod h1:Ho0h+IUsWyvy1OpqCwxlQ/21gkhVunqlU8fDGcoTdcA=
github.com/onsi/gomega v1.9.0/go.mod h1:Ho0h+IUsWyvy1OpqCwxlQ/21gkhVunqlU8fDGcoTdcA=
+github.com/onsi/gomega v1.10.1/go.mod h1:iN09h71vgCQne3DLsj+A5owkum+a2tYe+TOCB1ybHNo=
+github.com/onsi/gomega v1.10.2 h1:aY/nuoWlKJud2J6U0E3NWsjlg+0GtwXxgEqthRdzlcs=
+github.com/onsi/gomega v1.10.2/go.mod h1:iN09h71vgCQne3DLsj+A5owkum+a2tYe+TOCB1ybHNo=
+github.com/op/go-logging v0.0.0-20160315200505-970db520ece7/go.mod h1:HzydrMdWErDVzsI23lYNej1Htcns9BCg93Dk0bBINWk=
github.com/opencontainers/go-digest v0.0.0-20170106003457-a6d0ee40d420/go.mod h1:cMLVZDEM3+U2I4VmLI6N8jQYUd2OVphdqWwCJHrFt2s=
github.com/opencontainers/go-digest v0.0.0-20180430190053-c9281466c8b2/go.mod h1:cMLVZDEM3+U2I4VmLI6N8jQYUd2OVphdqWwCJHrFt2s=
-github.com/opencontainers/go-digest v1.0.0-rc1 h1:WzifXhOVOEOuFYOJAW6aQqW0TooG2iki3E3Ii+WN7gQ=
github.com/opencontainers/go-digest v1.0.0-rc1/go.mod h1:cMLVZDEM3+U2I4VmLI6N8jQYUd2OVphdqWwCJHrFt2s=
+github.com/opencontainers/go-digest v1.0.0 h1:apOUWs51W5PlhuyGyz9FCeeBIOUDA/6nW8Oi/yOhh5U=
+github.com/opencontainers/go-digest v1.0.0/go.mod h1:0JzlMkj0TRzQZfJkVvzbP0HBR3IKzErnv2BNG4W4MAM=
github.com/opencontainers/image-spec v1.0.0/go.mod h1:BtxoFyWECRxE4U/7sNtV5W15zMzWCbyJoFRP3s7yZA0=
github.com/opencontainers/image-spec v1.0.1/go.mod h1:BtxoFyWECRxE4U/7sNtV5W15zMzWCbyJoFRP3s7yZA0=
+github.com/opencontainers/image-spec v1.0.2-0.20190823105129-775207bd45b6/go.mod h1:BtxoFyWECRxE4U/7sNtV5W15zMzWCbyJoFRP3s7yZA0=
github.com/opencontainers/runc v0.0.0-20190115041553-12f6a991201f/go.mod h1:qT5XzbpPznkRYVz/mWwUaVBUv2rmF59PVA73FjuZG0U=
github.com/opencontainers/runc v0.1.1/go.mod h1:qT5XzbpPznkRYVz/mWwUaVBUv2rmF59PVA73FjuZG0U=
-github.com/opencontainers/runc v1.0.0-rc2.0.20190611121236-6cc515888830/go.mod h1:qT5XzbpPznkRYVz/mWwUaVBUv2rmF59PVA73FjuZG0U=
github.com/opencontainers/runtime-spec v0.1.2-0.20190507144316-5b71a03e2700/go.mod h1:jwyrGlmzljRJv/Fgzds9SsS/C5hL+LL3ko9hs6T5lQ0=
-github.com/opencontainers/runtime-spec v1.0.0/go.mod h1:jwyrGlmzljRJv/Fgzds9SsS/C5hL+LL3ko9hs6T5lQ0=
github.com/opencontainers/runtime-tools v0.0.0-20181011054405-1d69bd0f9c39/go.mod h1:r3f7wjNzSs2extwzU3Y+6pKfobzPh+kKFJ3ofN+3nfs=
-github.com/opencontainers/selinux v1.2.2/go.mod h1:+BLncwf63G4dgOzykXAxcmnFlUaOlkDdmw/CqsW6pjs=
-github.com/openshift/api v0.0.0-20200205133042-34f0ec8dab87/go.mod h1:fT6U/JfG8uZzemTRwZA2kBDJP5nWz7v05UHnty/D+pk=
-github.com/openshift/api v3.9.1-0.20190924102528-32369d4db2ad+incompatible h1:6il8W875Oq9vycPkRV5TteLP9IfMEX3lyOl5yN+CtdI=
-github.com/openshift/api v3.9.1-0.20190924102528-32369d4db2ad+incompatible/go.mod h1:dh9o4Fs58gpFXGSYfnVxGR9PnV53I8TW84pQaJDdGiY=
-github.com/openshift/client-go v0.0.0-20190923180330-3b6373338c9b/go.mod h1:6rzn+JTr7+WYS2E1TExP4gByoABxMznR6y2SnUIkmxk=
-github.com/openshift/origin v0.0.0-20160503220234-8f127d736703/go.mod h1:0Rox5r9C8aQn6j1oAOQ0c1uC86mYbUFObzjBRvUKHII=
-github.com/openshift/prom-label-proxy v0.1.1-0.20191016113035-b8153a7f39f1/go.mod h1:p5MuxzsYP1JPsNGwtjtcgRHHlGziCJJfztff91nNixw=
+github.com/openshift/api v3.9.0+incompatible h1:fJ/KsefYuZAjmrr3+5U9yZIZbTOpVkDDLDLFresAeYs=
+github.com/openshift/api v3.9.0+incompatible/go.mod h1:dh9o4Fs58gpFXGSYfnVxGR9PnV53I8TW84pQaJDdGiY=
+github.com/opentracing-contrib/go-observer v0.0.0-20170622124052-a52f23424492/go.mod h1:Ngi6UdF0k5OKD5t5wlmGhe/EDKPoUM3BXZSSfIuJbis=
+github.com/opentracing/basictracer-go v1.0.0/go.mod h1:QfBfYuafItcjQuMwinw9GhYKwFXS9KnPs5lxoYwgW74=
+github.com/opentracing/opentracing-go v1.0.2/go.mod h1:UkNAQd3GIcIGf0SeVgPpRdFStlNbqXla1AfSYxPUl2o=
github.com/opentracing/opentracing-go v1.1.0/go.mod h1:UkNAQd3GIcIGf0SeVgPpRdFStlNbqXla1AfSYxPUl2o=
+github.com/openzipkin-contrib/zipkin-go-opentracing v0.4.5/go.mod h1:/wsWhb9smxSfWAKL3wpBW7V8scJMt8N8gnaMCS9E/cA=
github.com/openzipkin/zipkin-go v0.1.6/go.mod h1:QgAqvLzwWbR/WpD4A3cGpPtJrZXNIiJc5AZX7/PBEpw=
-github.com/operator-framework/api v0.0.0-20200120235816-80fd2f1a09c9/go.mod h1:S5IdlJvmKkF84K2tBvsrqJbI2FVy03P88R75snpRxJo=
-github.com/operator-framework/api v0.1.1/go.mod h1:yzNYR7qyJqRGOOp+bT6Z/iYSbSPNxeh3Si93Gx/3OBY=
-github.com/operator-framework/operator-lifecycle-manager v0.0.0-20191115003340-16619cd27fa5/go.mod h1:zL34MNy92LPutBH5gQK+gGhtgTUlZZX03I2G12vWHF4=
-github.com/operator-framework/operator-lifecycle-manager v0.0.0-20200321030439-57b580e57e88/go.mod h1:7Ut8p9jJ8C6RZyyhZfZypmlibCIJwK5Wcc+WZDgLkOA=
-github.com/operator-framework/operator-registry v1.5.1/go.mod h1:agrQlkWOo1q8U1SAaLSS2WQ+Z9vswNT2M2HFib9iuLY=
-github.com/operator-framework/operator-registry v1.5.3/go.mod h1:agrQlkWOo1q8U1SAaLSS2WQ+Z9vswNT2M2HFib9iuLY=
-github.com/operator-framework/operator-registry v1.5.7-0.20200121213444-d8e2ec52c19a/go.mod h1:ekexcV4O8YMxdQuPb+Xco7MHfVmRIq7Jvj5e6NU7dHI=
-github.com/operator-framework/operator-registry v1.6.1/go.mod h1:sx4wWMiZtYhlUiaKscg3QQUPPM/c1bkrAs4n4KipDb4=
-github.com/operator-framework/operator-registry v1.6.2-0.20200330184612-11867930adb5/go.mod h1:SHff373z8asEkPo6aWpN0qId4Y/feQTjZxRF8PRhti8=
-github.com/operator-framework/operator-sdk v0.17.0 h1:+TTrGjXa+lm7g7Cm0UtFcgOjnw1x9/lBorydpsIIhOY=
-github.com/operator-framework/operator-sdk v0.17.0/go.mod h1:wmYi08aoUmtgfoUamURmssI4dkdFGNtSI1Egj+ZfBnk=
-github.com/otiai10/copy v1.0.1/go.mod h1:8bMCJrAqOtN/d9oyh5HR7HhLQMvcGMpGdwRDYsfOCHc=
-github.com/otiai10/copy v1.0.2/go.mod h1:c7RpqBkwMom4bYTSkLSym4VSJz/XtncWRAj/J4PEIMY=
+github.com/openzipkin/zipkin-go v0.2.1/go.mod h1:NaW6tEwdmWMaCDZzg8sh+IBNOxHMPnhQw8ySjnjRyN4=
+github.com/openzipkin/zipkin-go v0.2.2/go.mod h1:NaW6tEwdmWMaCDZzg8sh+IBNOxHMPnhQw8ySjnjRyN4=
+github.com/operator-framework/api v0.3.22/go.mod h1:GVNiB6AQucwdZz3ZFXNv9HtcLOzcFnr6O/QldzKG93g=
+github.com/operator-framework/api v0.4.0/go.mod h1:xXYReW8+PpSBHMxsf0e7uhtfQTLqIM1iz4X6zUs20+c=
+github.com/operator-framework/operator-lib v0.3.0/go.mod h1:LTp5UQd8ivq4MXqm/W/XHulHQ0RRoZXsAj73sNMAQxc=
+github.com/operator-framework/operator-registry v1.15.3/go.mod h1:CI7cu5ANoSQB54XWr/hXefm1PbKNJztSrhGBEDLCNCg=
+github.com/operator-framework/operator-sdk v1.3.0 h1:IjkCopydSvUgNA02mH5pHIiUJzuMxPpsjBUo3xghckY=
+github.com/operator-framework/operator-sdk v1.3.0/go.mod h1:uBStSTOWTHxWcrzKjseSxfPFBiKpU3tHziIDTPo60iM=
+github.com/otiai10/copy v1.2.0/go.mod h1:rrF5dJ5F0t/EWSYODDu4j9/vEeYHMkc8jt0zJChqQWw=
github.com/otiai10/curr v0.0.0-20150429015615-9b4961190c95/go.mod h1:9qAhocn7zKJG+0mI8eUu6xqkFDYS2kb2saOteoSB3cE=
-github.com/otiai10/curr v0.0.0-20190513014714-f5a3d24e5776/go.mod h1:3HNVkVOU7vZeFXocWuvtcS0XSFLcf2XUSDHkq9t1jU4=
-github.com/otiai10/mint v1.2.3/go.mod h1:YnfyPNhBvnY8bW4SGQHCs/aAFhkgySlMZbrF5U0bOVw=
-github.com/otiai10/mint v1.2.4/go.mod h1:d+b7n/0R3tdyUYYylALXpWQ/kTN+QobSq/4SRGBkR3M=
+github.com/otiai10/curr v1.0.0/go.mod h1:LskTG5wDwr8Rs+nNQ+1LlxRjAtTZZjtJW4rMXl6j4vs=
github.com/otiai10/mint v1.3.0/go.mod h1:F5AjcsTsWUqX+Na9fpHb52P8pcRX2CI6A3ctIT91xUo=
-github.com/pborman/uuid v1.2.0 h1:J7Q5mO4ysT1dv8hyrUGHb9+ooztCXu1D8MY8DZYsu3g=
+github.com/otiai10/mint v1.3.1/go.mod h1:/yxELlJQ0ufhjUwhshSj+wFjZ78CnZ48/1wtmBH1OTc=
+github.com/pact-foundation/pact-go v1.0.4/go.mod h1:uExwJY4kCzNPcHRj+hCR/HBbOOIwwtUjcrb0b5/5kLM=
+github.com/pascaldekloe/goe v0.0.0-20180627143212-57f6aae5913c/go.mod h1:lzWF7FIEvWOWxwDKqyGYQf6ZUaNfKdP144TG7ZOy1lc=
github.com/pborman/uuid v1.2.0/go.mod h1:X/NO0urCmaxf9VXbdlT7C2Yzkj2IKimNn4k+gtPdI/k=
-github.com/pelletier/go-toml v1.0.1/go.mod h1:5z9KED0ma1S8pY6P1sdut58dfprrGBbd/94hg7ilaic=
-github.com/pelletier/go-toml v1.2.0 h1:T5zMGML61Wp+FlcbWjRDT7yAxhJNAiPPLOFECq181zc=
github.com/pelletier/go-toml v1.2.0/go.mod h1:5z9KED0ma1S8pY6P1sdut58dfprrGBbd/94hg7ilaic=
+github.com/performancecopilot/speed v3.0.0+incompatible/go.mod h1:/CLtqpZ5gBg1M9iaPbIdPPGyKcA8hKdoy6hAWba7Yac=
github.com/peterbourgon/diskv v2.0.1+incompatible h1:UBdAOUP5p4RWqPBg048CAvpKN+vxiaj6gdUUzhl4XmI=
github.com/peterbourgon/diskv v2.0.1+incompatible/go.mod h1:uqqh8zWWbv1HBMNONnaR/tNboyR3/BZd58JJSHlUSCU=
-github.com/phayes/checkstyle v0.0.0-20170904204023-bfd46e6a821d/go.mod h1:3OzsM7FXDQlpCiw2j81fOmAwQLnZnLGXVKUzeKQXIAw=
-github.com/phayes/freeport v0.0.0-20171002181615-b8543db493a5/go.mod h1:iIss55rKnNBTvrwdmkUpLnDpZoAHvWaiq5+iMmen4AE=
github.com/phayes/freeport v0.0.0-20180830031419-95f893ade6f2/go.mod h1:iIss55rKnNBTvrwdmkUpLnDpZoAHvWaiq5+iMmen4AE=
+github.com/pierrec/lz4 v1.0.2-0.20190131084431-473cd7ce01a1/go.mod h1:3/3N9NVKO0jef7pBehbT1qWhCMrIgbYNnFAZCqQ5LRc=
github.com/pierrec/lz4 v2.0.5+incompatible/go.mod h1:pdkljMzZIN41W+lC3N2tnIh5sFi+IEE17M5jbnwPHcY=
github.com/pkg/errors v0.8.0/go.mod h1:bwawxfHBFNV+L2hUp1rHADufV3IMtnDRdf1r5NINEl0=
github.com/pkg/errors v0.8.1-0.20171018195549-f15c970de5b7/go.mod h1:bwawxfHBFNV+L2hUp1rHADufV3IMtnDRdf1r5NINEl0=
-github.com/pkg/errors v0.8.1 h1:iURUrRGxPUNPdy5/HRSm+Yj6okJ6UtLINN0Q9M4+h3I=
github.com/pkg/errors v0.8.1/go.mod h1:bwawxfHBFNV+L2hUp1rHADufV3IMtnDRdf1r5NINEl0=
github.com/pkg/errors v0.9.1 h1:FEBLx1zS214owpjy7qsBeixbURkuhQAwrK5UwLGTwt4=
github.com/pkg/errors v0.9.1/go.mod h1:bwawxfHBFNV+L2hUp1rHADufV3IMtnDRdf1r5NINEl0=
+github.com/pkg/profile v1.2.1/go.mod h1:hJw3o1OdXxsrSjjVksARp5W95eeEaEfptyVZyv6JUPA=
github.com/pmezard/go-difflib v1.0.0 h1:4DBwDE0NGyQoBHbLQYPwSUPoCMWR5BEzIk/f1lZbAQM=
github.com/pmezard/go-difflib v1.0.0/go.mod h1:iKH77koFhYxTK1pcRnkKkqfTogsbg7gZNVY4sRDYZ/4=
+github.com/posener/complete v1.1.1/go.mod h1:em0nMJCgc9GFtwrmVmEMR/ZL6WyhyjMBndrE9hABlRI=
github.com/pquerna/cachecontrol v0.0.0-20171018203845-0dec1b30a021/go.mod h1:prYjPmNq4d1NPVmpShWobRqXY3q7Vp+80DqgxxUrUIA=
-github.com/pquerna/ffjson v0.0.0-20180717144149-af8b230fcd20/go.mod h1:YARuvh7BUWHNhzDq2OM5tzR2RiCcN2D7sapiKyCel/M=
github.com/prometheus/client_golang v0.0.0-20180209125602-c332b6f63c06/go.mod h1:7SWBe2y4D6OKWSNQJUaRYU/AaXPKyh/dDVn+NZz0KFw=
github.com/prometheus/client_golang v0.9.1/go.mod h1:7SWBe2y4D6OKWSNQJUaRYU/AaXPKyh/dDVn+NZz0KFw=
-github.com/prometheus/client_golang v0.9.2/go.mod h1:OsXs2jCmiKlQ1lTBmv21f2mNfw4xf/QclQDMrYNZzcM=
github.com/prometheus/client_golang v0.9.3-0.20190127221311-3c4408c8b829/go.mod h1:p2iRAGwDERtqlqzRXnrOVns+ignqQo//hLXqYxZYVNs=
github.com/prometheus/client_golang v0.9.3/go.mod h1:/TN21ttK/J9q6uSwhBd54HahCDft0ttaMvbicHlPoso=
github.com/prometheus/client_golang v1.0.0/go.mod h1:db9x61etRT2tGnBNRi70OPL5FsnadC4Ky3P0J6CfImo=
github.com/prometheus/client_golang v1.1.0/go.mod h1:I1FGZT9+L76gKKOs5djB6ezCbFQP1xR9D75/vuwEF3g=
-github.com/prometheus/client_golang v1.2.1 h1:JnMpQc6ppsNgw9QPAGF6Dod479itz7lvlsMzzNayLOI=
-github.com/prometheus/client_golang v1.2.1/go.mod h1:XMU6Z2MjaRKVu/dC1qupJI9SiNkDYzz3xecMgSW/F+U=
-github.com/prometheus/client_golang v1.5.1 h1:bdHYieyGlH+6OLEk2YQha8THib30KP0/yD0YH9m6xcA=
-github.com/prometheus/client_golang v1.5.1/go.mod h1:e9GMxYsXl05ICDXkRhurwBS4Q3OK1iX/F2sw+iXX5zU=
+github.com/prometheus/client_golang v1.3.0/go.mod h1:hJaj2vgQTGQmVCsAACORcieXFeDPbaTKGT+JTgUa3og=
+github.com/prometheus/client_golang v1.7.1 h1:NTGy1Ja9pByO+xAeH/qiWnLrKtr3hJPNjaVUwnjpdpA=
+github.com/prometheus/client_golang v1.7.1/go.mod h1:PY5Wy2awLA44sXw4AOSfFBetzPP4j5+D6mVACh+pe2M=
github.com/prometheus/client_model v0.0.0-20171117100541-99fa1f4be8e5/go.mod h1:MbSGuTsp3dbXC40dX6PRTWyKYBIrTGTE9sqQNg2J8bo=
github.com/prometheus/client_model v0.0.0-20180712105110-5c3871d89910/go.mod h1:MbSGuTsp3dbXC40dX6PRTWyKYBIrTGTE9sqQNg2J8bo=
github.com/prometheus/client_model v0.0.0-20190115171406-56726106282f/go.mod h1:MbSGuTsp3dbXC40dX6PRTWyKYBIrTGTE9sqQNg2J8bo=
github.com/prometheus/client_model v0.0.0-20190129233127-fd36f4220a90/go.mod h1:xMI15A0UPsDsEKsMN9yxemIoYk6Tm2C1GtYGdfGttqA=
-github.com/prometheus/client_model v0.0.0-20190812154241-14fe0d1b01d4 h1:gQz4mCbXsO+nc9n1hCxHcGA3Zx3Eo+UHZoInFGUIXNM=
github.com/prometheus/client_model v0.0.0-20190812154241-14fe0d1b01d4/go.mod h1:xMI15A0UPsDsEKsMN9yxemIoYk6Tm2C1GtYGdfGttqA=
+github.com/prometheus/client_model v0.1.0/go.mod h1:xMI15A0UPsDsEKsMN9yxemIoYk6Tm2C1GtYGdfGttqA=
github.com/prometheus/client_model v0.2.0 h1:uq5h0d+GuxiXLJLNABMgp2qUWDPiLvgCzz2dUR+/W/M=
github.com/prometheus/client_model v0.2.0/go.mod h1:xMI15A0UPsDsEKsMN9yxemIoYk6Tm2C1GtYGdfGttqA=
github.com/prometheus/common v0.0.0-20180110214958-89604d197083/go.mod h1:daVV7qP5qjZbuso7PdcryaAu0sAZbrN9i7WWcTMWvro=
github.com/prometheus/common v0.0.0-20181113130724-41aa239b4cce/go.mod h1:daVV7qP5qjZbuso7PdcryaAu0sAZbrN9i7WWcTMWvro=
-github.com/prometheus/common v0.0.0-20181126121408-4724e9255275/go.mod h1:daVV7qP5qjZbuso7PdcryaAu0sAZbrN9i7WWcTMWvro=
github.com/prometheus/common v0.2.0/go.mod h1:TNfzLD0ON7rHzMJeJkieUDPYmFC7Snx/y86RQel1bk4=
github.com/prometheus/common v0.4.0/go.mod h1:TNfzLD0ON7rHzMJeJkieUDPYmFC7Snx/y86RQel1bk4=
github.com/prometheus/common v0.4.1/go.mod h1:TNfzLD0ON7rHzMJeJkieUDPYmFC7Snx/y86RQel1bk4=
github.com/prometheus/common v0.6.0/go.mod h1:eBmuwkDJBwy6iBfxCBob6t6dR6ENT/y+J+Zk0j9GMYc=
-github.com/prometheus/common v0.7.0 h1:L+1lyG48J1zAQXA3RBX/nG/B3gjlHq0zTt2tlbJLyCY=
github.com/prometheus/common v0.7.0/go.mod h1:DjGbpBbp5NYNiECxcL/VnbXCCaQpKd3tt26CguLLsqA=
-github.com/prometheus/common v0.9.1 h1:KOMtN28tlbam3/7ZKEYKHhKoJZYYj3gMH4uc62x7X7U=
-github.com/prometheus/common v0.9.1/go.mod h1:yhUN8i9wzaXS3w1O07YhxHEBxD+W35wd8bs7vj7HSQ4=
+github.com/prometheus/common v0.10.0 h1:RyRA7RzGXQZiW+tGMr7sxa85G1z0yOpM1qq5c8lNawc=
+github.com/prometheus/common v0.10.0/go.mod h1:Tlit/dnDKsSWFlCLTWaA1cyBgKHSMdTB80sz/V91rCo=
github.com/prometheus/procfs v0.0.0-20180125133057-cb4147076ac7/go.mod h1:c3At6R/oaqEKCNdg8wHV1ftS6bRYblBhIjjI8uT2IGk=
github.com/prometheus/procfs v0.0.0-20181005140218-185b4288413d/go.mod h1:c3At6R/oaqEKCNdg8wHV1ftS6bRYblBhIjjI8uT2IGk=
-github.com/prometheus/procfs v0.0.0-20181204211112-1dc9a6cbc91a/go.mod h1:c3At6R/oaqEKCNdg8wHV1ftS6bRYblBhIjjI8uT2IGk=
github.com/prometheus/procfs v0.0.0-20190117184657-bf6a532e95b1/go.mod h1:c3At6R/oaqEKCNdg8wHV1ftS6bRYblBhIjjI8uT2IGk=
-github.com/prometheus/procfs v0.0.0-20190129233650-316cf8ccfec5/go.mod h1:c3At6R/oaqEKCNdg8wHV1ftS6bRYblBhIjjI8uT2IGk=
github.com/prometheus/procfs v0.0.0-20190507164030-5867b95ac084/go.mod h1:TjEm7ze935MbeOT/UhFTIMYKhuLP4wbCsTZCD3I8kEA=
+github.com/prometheus/procfs v0.0.0-20190522114515-bc1a522cf7b1/go.mod h1:TjEm7ze935MbeOT/UhFTIMYKhuLP4wbCsTZCD3I8kEA=
github.com/prometheus/procfs v0.0.2/go.mod h1:TjEm7ze935MbeOT/UhFTIMYKhuLP4wbCsTZCD3I8kEA=
github.com/prometheus/procfs v0.0.3/go.mod h1:4A/X28fw3Fc593LaREMrKMqOKvUAntwMDaekg4FpcdQ=
-github.com/prometheus/procfs v0.0.5 h1:3+auTFlqw+ZaQYJARz6ArODtkaIwtvBTx3N2NehQlL8=
github.com/prometheus/procfs v0.0.5/go.mod h1:4A/X28fw3Fc593LaREMrKMqOKvUAntwMDaekg4FpcdQ=
-github.com/prometheus/procfs v0.0.8 h1:+fpWZdT24pJBiqJdAwYBjPSk+5YmQzYNPYzQsdzLkt8=
github.com/prometheus/procfs v0.0.8/go.mod h1:7Qr8sr6344vo1JqZ6HhLceV9o3AJ1Ff+GxbHq6oeK9A=
-github.com/prometheus/prometheus v2.3.2+incompatible/go.mod h1:oAIUtOny2rjMX0OWN5vPR5/q/twIROJvdqnQKDdil/s=
+github.com/prometheus/procfs v0.1.3 h1:F0+tqvhOksq22sc6iCHF5WGlWjdwj92p0udFh1VFBS8=
+github.com/prometheus/procfs v0.1.3/go.mod h1:lV6e/gmhEcM9IjHGsFOCxxuZ+z1YqCvr4OA4YeYWdaU=
github.com/prometheus/tsdb v0.7.1/go.mod h1:qhTCs0VvXwvX/y3TZrWD7rabWM+ijKTux40TwIPHuXU=
-github.com/prometheus/tsdb v0.8.0/go.mod h1:fSI0j+IUQrDd7+ZtR9WKIGtoYAYAJUKcKhYLG25tN4g=
-github.com/quasilyte/go-consistent v0.0.0-20190521200055-c6f3937de18c/go.mod h1:5STLWrekHfjyYwxBRVRXNOSewLJ3PWfDJd1VyTS21fI=
-github.com/quobyte/api v0.1.2/go.mod h1:jL7lIHrmqQ7yh05OJ+eEEdHr0u/kmT1Ff9iHd+4H6VI=
github.com/rcrowley/go-metrics v0.0.0-20181016184325-3113b8401b8a/go.mod h1:bCqnVzQkZxMG4s8nGwiZ5l3QUCyqpo9Y+/ZMZ9VjZe4=
-github.com/robfig/cron v0.0.0-20170526150127-736158dc09e1/go.mod h1:JGuDeoQd7Z6yL4zQhZ3OPEVHB7fL6Ka6skscFHfmt2k=
-github.com/robfig/cron v1.1.0/go.mod h1:JGuDeoQd7Z6yL4zQhZ3OPEVHB7fL6Ka6skscFHfmt2k=
github.com/robfig/cron v1.2.0 h1:ZjScXvvxeQ63Dbyxy76Fj3AT3Ut0aKsyd2/tl3DTMuQ=
github.com/robfig/cron v1.2.0/go.mod h1:JGuDeoQd7Z6yL4zQhZ3OPEVHB7fL6Ka6skscFHfmt2k=
github.com/rogpeppe/fastuuid v0.0.0-20150106093220-6724a57986af/go.mod h1:XWv6SoW27p1b0cqNHllgS5HIMJraePCO15w5zCzIWYg=
-github.com/rogpeppe/go-charset v0.0.0-20180617210344-2471d30d28b4/go.mod h1:qgYeAmZ5ZIpBWTGllZSQnw97Dj+woV0toclVaRGI8pc=
github.com/rogpeppe/go-internal v1.1.0/go.mod h1:M8bDsm7K2OlrFYOpmOWEs/qY81heoFRclV5y23lUDJ4=
-github.com/rogpeppe/go-internal v1.2.2/go.mod h1:M8bDsm7K2OlrFYOpmOWEs/qY81heoFRclV5y23lUDJ4=
github.com/rogpeppe/go-internal v1.3.0/go.mod h1:M8bDsm7K2OlrFYOpmOWEs/qY81heoFRclV5y23lUDJ4=
-github.com/rogpeppe/go-internal v1.5.0 h1:Usqs0/lDK/NqTkvrmKSwA/3XkZAs7ZAW/eLeQ2MVBTw=
-github.com/rogpeppe/go-internal v1.5.0/go.mod h1:xXDCJY+GAPziupqXw64V24skbSoqbTEfhy4qGm1nDQc=
-github.com/rubenv/sql-migrate v0.0.0-20191025130928-9355dd04f4b3/go.mod h1:WS0rl9eEliYI8DPnr3TOwz4439pay+qNgzJoVya/DmY=
-github.com/rubiojr/go-vhd v0.0.0-20160810183302-0bfd3b39853c/go.mod h1:DM5xW0nvfNNm2uytzsvhI3OnX8uzaRAg8UX/CnDqbto=
-github.com/russross/blackfriday v0.0.0-20170610170232-067529f716f4/go.mod h1:JO/DiYxRf+HjHt06OyowR9PTA263kcR/rfWxYHBV53g=
+github.com/rogpeppe/go-internal v1.3.2/go.mod h1:xXDCJY+GAPziupqXw64V24skbSoqbTEfhy4qGm1nDQc=
+github.com/rogpeppe/go-internal v1.4.0/go.mod h1:xXDCJY+GAPziupqXw64V24skbSoqbTEfhy4qGm1nDQc=
+github.com/rubenv/sql-migrate v0.0.0-20200616145509-8d140a17f351/go.mod h1:DCgfY80j8GYL7MLEfvcpSFvjD0L5yZq/aZUJmhZklyg=
github.com/russross/blackfriday v1.5.2/go.mod h1:JO/DiYxRf+HjHt06OyowR9PTA263kcR/rfWxYHBV53g=
-github.com/ryancurrah/gomodguard v1.0.4 h1:oCreMAt9GuFXDe9jW4HBpc3GjdX3R/sUEcLAGh1zPx8=
-github.com/ryancurrah/gomodguard v1.0.4/go.mod h1:9T/Cfuxs5StfsocWr4WzDL36HqnX0fVb9d5fSEaLhoE=
+github.com/russross/blackfriday/v2 v2.0.1/go.mod h1:+Rmxgy9KzJVeS9/2gXHxylqXiyQDYRxCVz55jmeOWTM=
+github.com/ryanuber/columnize v0.0.0-20160712163229-9b3edd62028f/go.mod h1:sm1tb6uqfes/u+d4ooFouqFdy9/2g9QGwK3SQygK0Ts=
+github.com/samuel/go-zookeeper v0.0.0-20190923202752-2cc03de413da/go.mod h1:gi+0XIa01GRL2eRQVjQkKGqKF3SF9vZR/HnPullcV2E=
github.com/satori/go.uuid v1.2.0/go.mod h1:dA0hQrYB0VpLJoorglMZABFdXlWrHn1NEOzdhQKdks0=
github.com/sclevine/spec v1.2.0/go.mod h1:W4J29eT/Kzv7/b9IWLB055Z+qvVC9vt0Arko24q7p+U=
-github.com/seccomp/libseccomp-golang v0.9.1/go.mod h1:GbW5+tmTXfcxTToHLXlScSlAvWlF4P2Ca7zGrPiEpWo=
-github.com/securego/gosec v0.0.0-20200316084457-7da9f46445fd h1:qB+l4fYZsH78xORC1aqVS0zNmgkQp4rkj2rvfxQMtzc=
-github.com/securego/gosec v0.0.0-20200316084457-7da9f46445fd/go.mod h1:NurAFZsWJAEZjogSwdVPlHkOZB3DOAU7gsPP8VFZCHc=
+github.com/sean-/seed v0.0.0-20170313163322-e2103e2c3529/go.mod h1:DxrIzT+xaE7yg65j358z/aeFdxmN0P9QXhEzd20vsDc=
github.com/sergi/go-diff v1.0.0/go.mod h1:0CfEIISq7TuYL3j771MWULgwwjU+GofnZX9QAmXWZgo=
-github.com/shirou/gopsutil v0.0.0-20190901111213-e4ec7b275ada/go.mod h1:WWnYX4lzhCH5h/3YBfyVA3VbLYjlMZZAQcW9ojMexNc=
-github.com/shirou/w32 v0.0.0-20160930032740-bb4de0191aa4/go.mod h1:qsXQc7+bwAM3Q1u/4XEfrquwF8Lw7D7y5cD8CuHnfIc=
github.com/shopspring/decimal v0.0.0-20180709203117-cd690d0c9e24/go.mod h1:M+9NzErvs504Cn4c5DxATwIqPbtswREoFCre64PpcG4=
-github.com/shurcooL/go v0.0.0-20180423040247-9e1955d9fb6e/go.mod h1:TDJrrUr11Vxrven61rcy3hJMUqaf/CLWYhHNPmT14Lk=
-github.com/shurcooL/go-goon v0.0.0-20170922171312-37c2f522c041/go.mod h1:N5mDOmsrJOB+vfqUK+7DmDyjhSLIIBnXo9lvZJj3MWQ=
+github.com/shurcooL/sanitized_anchor_name v1.0.0/go.mod h1:1NzhyTcUVG4SuEtjjoZeVRXNmyL/1OwPU0+IJeTBvfc=
github.com/sirupsen/logrus v1.0.4-0.20170822132746-89742aefa4b2/go.mod h1:pMByvHTf9Beacp5x1UXfOR9xyW/9antXMhjMPG0dEzc=
-github.com/sirupsen/logrus v1.0.5/go.mod h1:pMByvHTf9Beacp5x1UXfOR9xyW/9antXMhjMPG0dEzc=
github.com/sirupsen/logrus v1.2.0/go.mod h1:LxeOpSwHxABJmUn/MG1IvRgCAasNZTLOkJPxbbu5VWo=
github.com/sirupsen/logrus v1.4.1/go.mod h1:ni0Sbl8bgC9z8RoU9G6nDWqqs/fq4eDPysMBDgk/93Q=
-github.com/sirupsen/logrus v1.4.2 h1:SPIRibHv4MatM3XXNO2BJeFLZwZ2LvZgfQ5+UNI2im4=
github.com/sirupsen/logrus v1.4.2/go.mod h1:tLMulIdttU9McNUspp0xgXVQah82FyeX6MwdIuYE2rE=
-github.com/sirupsen/logrus v1.5.0 h1:1N5EYkVAPEywqZRJd7cwnRtCb6xJx7NH3T3WUTF980Q=
-github.com/sirupsen/logrus v1.5.0/go.mod h1:+F7Ogzej0PZc/94MaYx/nvG9jOFMD2osvC3s+Squfpo=
+github.com/sirupsen/logrus v1.6.0 h1:UBcNElsrwanuuMsnGSlYmtmgbb23qDR5dG+6X6Oo89I=
+github.com/sirupsen/logrus v1.6.0/go.mod h1:7uNnSEd1DgxDLC74fIahvMZmmYsHGZGEOFrfsX/uA88=
+github.com/sirupsen/logrus v1.7.0/go.mod h1:yWOB1SBYBC5VeMP7gHvWumXLIWorT60ONWic61uBYv0=
github.com/smartystreets/assertions v0.0.0-20180927180507-b2de0cb4f26d/go.mod h1:OnSkiWE9lh6wB0YB77sQom3nweQdgAjqCqsofrRNTgc=
github.com/smartystreets/goconvey v0.0.0-20190330032615-68dc04aab96a/go.mod h1:syvi0/a8iFYH4r/RixwvyeAJjdLS9QV7WQ/tjFTllLA=
github.com/smartystreets/goconvey v1.6.4/go.mod h1:syvi0/a8iFYH4r/RixwvyeAJjdLS9QV7WQ/tjFTllLA=
-github.com/soheilhy/cmux v0.1.3/go.mod h1:IM3LyeVVIOuxMH7sFAkER9+bJ4dT7Ms6E4xg4kGIyLM=
github.com/soheilhy/cmux v0.1.4/go.mod h1:IM3LyeVVIOuxMH7sFAkER9+bJ4dT7Ms6E4xg4kGIyLM=
-github.com/sourcegraph/go-diff v0.5.1 h1:gO6i5zugwzo1RVTvgvfwCOSVegNuvnNi6bAD1QCmkHs=
-github.com/sourcegraph/go-diff v0.5.1/go.mod h1:j2dHj3m8aZgQO8lMTcTnBcXkRRRqi34cd2MNlA9u1mE=
+github.com/sony/gobreaker v0.4.1/go.mod h1:ZKptC7FHNvhBz7dN2LGjPVBz2sZJmc0/PkyDJOjmxWY=
github.com/spaolacci/murmur3 v0.0.0-20180118202830-f09979ecbc72/go.mod h1:JwIasOWyU6f++ZhiEuf87xNszmSA2myDM2Kzu9HwQUA=
github.com/spf13/afero v1.1.2/go.mod h1:j4pytiNVoe2o6bmDsKpLACNPDBIoEAkihy7loJ1B0CQ=
-github.com/spf13/afero v1.2.2 h1:5jhuqJyZCZf2JRofRvN/nIFgIWNzPa3/Vz8mYylgbWc=
github.com/spf13/afero v1.2.2/go.mod h1:9ZxEEn6pIJ8Rxe320qSDBk6AsU0r9pR7Q4OcevTdifk=
-github.com/spf13/cast v1.3.0 h1:oget//CVOEoFewqQxwr0Ej5yjygnqGkvggSE/gB35Q8=
github.com/spf13/cast v1.3.0/go.mod h1:Qx5cxh0v+4UWYiBimWS+eyWzqEqokIECu5etghLkUJE=
+github.com/spf13/cast v1.3.1/go.mod h1:Qx5cxh0v+4UWYiBimWS+eyWzqEqokIECu5etghLkUJE=
github.com/spf13/cobra v0.0.2-0.20171109065643-2da4a54c5cee/go.mod h1:1l0Ry5zgKvJasoi3XT1TypsSe7PqH0Sj9dhYf7v3XqQ=
github.com/spf13/cobra v0.0.3/go.mod h1:1l0Ry5zgKvJasoi3XT1TypsSe7PqH0Sj9dhYf7v3XqQ=
-github.com/spf13/cobra v0.0.5 h1:f0B+LkLX6DtmRH1isoNA9VTtNUK9K8xYd28JNNfOv/s=
github.com/spf13/cobra v0.0.5/go.mod h1:3K3wKZymM7VvHMDS9+Akkh4K60UwM26emMESw8tLCHU=
-github.com/spf13/jwalterweatherman v1.0.0 h1:XHEdyB+EcvlqZamSM4ZOMGlc93t6AcsBEu9Gc1vn7yk=
+github.com/spf13/cobra v0.0.7/go.mod h1:/6GTrnGXV9HjY+aR4k0oJ5tcvakLuG6EuKReYlHNrgE=
+github.com/spf13/cobra v1.0.0/go.mod h1:/6GTrnGXV9HjY+aR4k0oJ5tcvakLuG6EuKReYlHNrgE=
+github.com/spf13/cobra v1.1.1 h1:KfztREH0tPxJJ+geloSLaAkaPkr4ki2Er5quFV1TDo4=
+github.com/spf13/cobra v1.1.1/go.mod h1:WnodtKOvamDL/PwE2M4iKs8aMDBZ5Q5klgD3qfVJQMI=
github.com/spf13/jwalterweatherman v1.0.0/go.mod h1:cQK4TGJAtQXfYWX+Ddv3mKDzgVb68N+wFjFa4jdeBTo=
-github.com/spf13/jwalterweatherman v1.1.0/go.mod h1:aNWZUN0dPAAO/Ljvb5BEdw96iTZ0EXowPYD95IqWIGo=
+github.com/spf13/pflag v0.0.0-20170130214245-9ff6c6923cff/go.mod h1:DYY7MBk1bdzusC3SYhjObp+wFpr4gzcvqqNjLnInEg4=
github.com/spf13/pflag v1.0.1-0.20171106142849-4c012f6dcd95/go.mod h1:DYY7MBk1bdzusC3SYhjObp+wFpr4gzcvqqNjLnInEg4=
github.com/spf13/pflag v1.0.1/go.mod h1:DYY7MBk1bdzusC3SYhjObp+wFpr4gzcvqqNjLnInEg4=
github.com/spf13/pflag v1.0.3/go.mod h1:DYY7MBk1bdzusC3SYhjObp+wFpr4gzcvqqNjLnInEg4=
@@ -851,154 +796,157 @@ github.com/spf13/pflag v1.0.5 h1:iy+VFUOCP1a+8yFto/drg2CJ5u0yRoB7fZw3DKv/JXA=
github.com/spf13/pflag v1.0.5/go.mod h1:McXfInJRrz4CZXVZOBLb0bTZqETkiAhM9Iw0y3An2Bg=
github.com/spf13/viper v1.3.2/go.mod h1:ZiWeW+zYFKm7srdB9IoDzzZXaJaI5eL9QjNiN/DMA2s=
github.com/spf13/viper v1.4.0/go.mod h1:PTJ7Z/lr49W6bUbkmS1V3by4uWynFiR9p7+dSq/yZzE=
-github.com/spf13/viper v1.6.1 h1:VPZzIkznI1YhVMRi6vNFLHSwhnhReBfgTxIPccpfdZk=
-github.com/spf13/viper v1.6.1/go.mod h1:t3iDnF5Jlj76alVNuyFBk5oUMCvsrkbvZK0WQdfDi5k=
-github.com/storageos/go-api v0.0.0-20180912212459-343b3eff91fc/go.mod h1:ZrLn+e0ZuF3Y65PNF6dIwbJPZqfmtCXxFm9ckv0agOY=
+github.com/spf13/viper v1.7.0/go.mod h1:8WkrPz2fc9jxqZNCJI/76HCieCp4Q8HaLFoCha5qpdg=
+github.com/stoewer/go-strcase v1.2.0/go.mod h1:IBiWB2sKIp3wVVQ3Y035++gc+knqhUQag1KpM8ahLw8=
+github.com/streadway/amqp v0.0.0-20190404075320-75d898a42a94/go.mod h1:AZpEONHx3DKn8O/DFsRAY58/XVQiIPMTMB1SddzLXVw=
+github.com/streadway/amqp v0.0.0-20190827072141-edfb9018d271/go.mod h1:AZpEONHx3DKn8O/DFsRAY58/XVQiIPMTMB1SddzLXVw=
+github.com/streadway/handy v0.0.0-20190108123426-d5acb3125c2a/go.mod h1:qNTQ5P5JnDBl6z3cMAg/SywNDC5ABu5ApDIw6lUbRmI=
github.com/stretchr/objx v0.1.0/go.mod h1:HFkY916IF+rwdDfMAkV7OtwuqBVzrE8GR6GFx+wExME=
github.com/stretchr/objx v0.1.1/go.mod h1:HFkY916IF+rwdDfMAkV7OtwuqBVzrE8GR6GFx+wExME=
-github.com/stretchr/objx v0.2.0 h1:Hbg2NidpLE8veEBkEZTL3CvlkUIVzuU9jDplZO54c48=
github.com/stretchr/objx v0.2.0/go.mod h1:qt09Ya8vawLte6SNmTgCsAVtYtaKzEcn8ATUoHMkEqE=
github.com/stretchr/testify v1.2.2/go.mod h1:a8OnRcib4nhh0OaRAV+Yts87kKdq0PP7pXfy6kDkUVs=
github.com/stretchr/testify v1.3.0/go.mod h1:M5WIy9Dh21IEIfnGCwXGc5bZfKNJtfHm1UVUgZn+9EI=
-github.com/stretchr/testify v1.4.0 h1:2E4SXV/wtOkTonXsotYi4li6zVWxYlZuYNCXe9XRJyk=
github.com/stretchr/testify v1.4.0/go.mod h1:j7eGeouHqKxXV5pUuKE4zz7dFj8WfuZ+81PSLYec5m4=
github.com/stretchr/testify v1.5.1 h1:nOGnQDM7FYENwehXlg/kFVnos3rEvtKTjRvOWSzb6H4=
github.com/stretchr/testify v1.5.1/go.mod h1:5W2xD1RspED5o8YsWQXVCued0rvSQ+mT+I5cxcmMvtA=
-github.com/subosito/gotenv v1.2.0 h1:Slr1R9HxAlEKefgq5jn9U+DnETlIUa6HfgEzj0g5d7s=
+github.com/stretchr/testify v1.6.1 h1:hDPOHmpOpP40lSULcqw7IrRb/u7w6RpDC9399XyoNd0=
+github.com/stretchr/testify v1.6.1/go.mod h1:6Fq8oRcR53rry900zMqJjRRixrwX3KX962/h/Wwjteg=
github.com/subosito/gotenv v1.2.0/go.mod h1:N0PQaV/YGNqwC0u51sEeR/aUtSLEXKX9iv69rRypqCw=
-github.com/syndtr/gocapability v0.0.0-20160928074757-e7cb7fa329f4/go.mod h1:hkRG7XYTFWNJGYcbNJQlaLq0fg1yr4J4t/NcTQtrfww=
github.com/syndtr/gocapability v0.0.0-20170704070218-db04d3cc01c8/go.mod h1:hkRG7XYTFWNJGYcbNJQlaLq0fg1yr4J4t/NcTQtrfww=
-github.com/tdakkota/asciicheck v0.0.0-20200416190851-d7f85be797a2 h1:Xr9gkxfOP0KQWXKNqmwe8vEeSUiUj4Rlee9CMVX2ZUQ=
-github.com/tdakkota/asciicheck v0.0.0-20200416190851-d7f85be797a2/go.mod h1:yHp0ai0Z9gUljN3o0xMhYJnH/IcvkdTBOX2fmJ93JEM=
-github.com/tetafro/godot v0.3.3 h1:uJjg8N+Ee10rAnaqJGet1WeI0YW4fiX9pKbwqnsqH6k=
-github.com/tetafro/godot v0.3.3/go.mod h1:pT6/T8+h6//L/LwQcFc4C0xpfy1euZwzS1sHdrFCms0=
-github.com/thecodeteam/goscaleio v0.1.0/go.mod h1:68sdkZAsK8bvEwBlbQnlLS+xU+hvLYM/iQ8KXej1AwM=
github.com/tidwall/pretty v0.0.0-20180105212114-65a9db5fad51/go.mod h1:XNkn88O1ChpSDQmQeStsy+sBenx6DDtFZJxhVysOjyk=
-github.com/timakin/bodyclose v0.0.0-20190930140734-f7f2e9bca95e h1:RumXZ56IrCj4CL+g1b9OL/oH0QnsF976bC8xQFYUD5Q=
-github.com/timakin/bodyclose v0.0.0-20190930140734-f7f2e9bca95e/go.mod h1:Qimiffbc6q9tBWlVV6x0P9sat/ao1xEkREYPPj9hphk=
+github.com/tidwall/pretty v1.0.0/go.mod h1:XNkn88O1ChpSDQmQeStsy+sBenx6DDtFZJxhVysOjyk=
github.com/tmc/grpc-websocket-proxy v0.0.0-20170815181823-89b8d40f7ca8/go.mod h1:ncp9v5uamzpCO7NfCPTXjqaC+bZgJeR0sMTm6dMHP7U=
github.com/tmc/grpc-websocket-proxy v0.0.0-20190109142713-0ad062ec5ee5/go.mod h1:ncp9v5uamzpCO7NfCPTXjqaC+bZgJeR0sMTm6dMHP7U=
-github.com/tommy-muehle/go-mnd v1.3.1-0.20200224220436-e6f9a994e8fa h1:RC4maTWLKKwb7p1cnoygsbKIgNlJqSYBeAFON3Ar8As=
-github.com/tommy-muehle/go-mnd v1.3.1-0.20200224220436-e6f9a994e8fa/go.mod h1:dSUh0FtTP8VhvkL1S+gUR1OKd9ZnSaozuI6r3m6wOig=
github.com/ugorji/go v1.1.4/go.mod h1:uQMGLiO92mf5W77hV/PUCpI3pbzQx3CRekS0kk+RGrc=
github.com/ugorji/go/codec v0.0.0-20181204163529-d75b2dcb6bc8/go.mod h1:VFNgLljTbGfSG7qAOspJ7OScBnGdDN/yBr0sguwnwf0=
-github.com/ultraware/funlen v0.0.2 h1:Av96YVBwwNSe4MLR7iI/BIa3VyI7/djnto/pK3Uxbdo=
-github.com/ultraware/funlen v0.0.2/go.mod h1:Dp4UiAus7Wdb9KUZsYWZEWiRzGuM2kXM1lPbfaF6xhA=
-github.com/ultraware/whitespace v0.0.4 h1:If7Va4cM03mpgrNH9k49/VOicWpGoG70XPBFFODYDsg=
-github.com/ultraware/whitespace v0.0.4/go.mod h1:aVMh/gQve5Maj9hQ/hg+F75lr/X5A89uZnzAmWSineA=
github.com/urfave/cli v0.0.0-20171014202726-7bc6a0acffa5/go.mod h1:70zkFmudgCuE/ngEzBv17Jvp/497gISqfk5gWijbERA=
-github.com/urfave/negroni v1.0.0/go.mod h1:Meg73S6kFm/4PpbYdq35yYWoCZ9mS/YSx+lKnmiohz4=
-github.com/uudashr/gocognit v1.0.1 h1:MoG2fZ0b/Eo7NXoIwCVFLG5JED3qgQz5/NEE+rOsjPs=
-github.com/uudashr/gocognit v1.0.1/go.mod h1:j44Ayx2KW4+oB6SWMv8KsmHzZrOInQav7D3cQMJ5JUM=
-github.com/valyala/bytebufferpool v1.0.0/go.mod h1:6bBcMArwyJ5K/AmCkWv1jt77kVWyCJ6HpOuEn7z0Csc=
-github.com/valyala/fasthttp v1.2.0/go.mod h1:4vX61m6KN+xDduDNwXrhIAVZaZaZiQ1luJk8LWSxF3s=
-github.com/valyala/quicktemplate v1.2.0/go.mod h1:EH+4AkTd43SvgIbQHYu59/cJyxDoOVRUAfrukLPuGJ4=
-github.com/valyala/tcplisten v0.0.0-20161114210144-ceec8f93295a/go.mod h1:v3UYOV9WzVtRmSR+PDvWpU/qWl4Wa5LApYYX4ZtKbio=
-github.com/vishvananda/netlink v0.0.0-20171020171820-b2de5d10e38e/go.mod h1:+SR5DhBJrl6ZM7CoCKvpw5BKroDKQ+PJqOg65H/2ktk=
-github.com/vishvananda/netns v0.0.0-20171111001504-be1fbeda1936/go.mod h1:ZjcWmFBXmLKZu9Nxj3WKYEafiSqer2rnvPr0en9UNpI=
-github.com/vmware/govmomi v0.20.1/go.mod h1:URlwyTFZX72RmxtxuaFL2Uj3fD1JTvZdx59bHWk6aFU=
+github.com/urfave/cli v1.20.0/go.mod h1:70zkFmudgCuE/ngEzBv17Jvp/497gISqfk5gWijbERA=
+github.com/urfave/cli v1.22.1/go.mod h1:Gos4lmkARVdJ6EkW0WaNv/tZAAMe9V7XWyB60NtXRu0=
+github.com/vektah/gqlparser v1.1.2/go.mod h1:1ycwN7Ij5njmMkPPAOaRFY4rET2Enx7IkVv3vaXspKw=
github.com/xanzy/go-gitlab v0.15.0/go.mod h1:8zdQa/ri1dfn8eS3Ir1SyfvOKlw7WBJ8DVThkpGiXrs=
github.com/xdg/scram v0.0.0-20180814205039-7eeb5667e42c/go.mod h1:lB8K/P019DLNhemzwFU4jHLhdvlE6uDZjXFejJXr49I=
github.com/xdg/stringprep v1.0.0/go.mod h1:Jhud4/sHMO4oL310DaZAKk9ZaJ08SJfe+sJh0HrGL1Y=
github.com/xeipuuv/gojsonpointer v0.0.0-20180127040702-4e3ac2762d5f/go.mod h1:N2zxlSyiKSe5eX1tZViRH5QA0qijqEDrYZiPEAiq3wU=
github.com/xeipuuv/gojsonreference v0.0.0-20180127040603-bd5ef7bd5415/go.mod h1:GwrjFmJcFw6At/Gs6z4yjiIwzuJ1/+UwLxMQDVQXShQ=
github.com/xeipuuv/gojsonschema v0.0.0-20180618132009-1d523034197f/go.mod h1:5yf86TLmAcydyeJq5YvxkGPE2fm/u4myDekKRoLuqhs=
-github.com/xeipuuv/gojsonschema v1.1.0/go.mod h1:5yf86TLmAcydyeJq5YvxkGPE2fm/u4myDekKRoLuqhs=
-github.com/xenolf/lego v0.0.0-20160613233155-a9d8cec0e656/go.mod h1:fwiGnfsIjG7OHPfOvgK7Y/Qo6+2Ox0iozjNTkZICKbY=
-github.com/xenolf/lego v0.3.2-0.20160613233155-a9d8cec0e656/go.mod h1:fwiGnfsIjG7OHPfOvgK7Y/Qo6+2Ox0iozjNTkZICKbY=
-github.com/xiang90/probing v0.0.0-20160813154853-07dd2e8dfe18/go.mod h1:UETIi67q53MR2AWcXfiuqkDkRtnGDLqkBTpCHuJHxtU=
+github.com/xeipuuv/gojsonschema v1.2.0/go.mod h1:anYRn/JVcOK2ZgGU+IjEV4nwlhoK5sQluxsYJ78Id3Y=
github.com/xiang90/probing v0.0.0-20190116061207-43a291ad63a2/go.mod h1:UETIi67q53MR2AWcXfiuqkDkRtnGDLqkBTpCHuJHxtU=
github.com/xlab/handysort v0.0.0-20150421192137-fb3537ed64a1/go.mod h1:QcJo0QPSfTONNIgpN5RA8prR7fF8nkF6cTWTcNerRO8=
github.com/xordataexchange/crypt v0.0.3-0.20170626215501-b2862e3d0a77/go.mod h1:aYKd//L2LvnjZzWKhF00oedf4jCCReLcmhLdhm1A27Q=
-github.com/yuin/goldmark v1.1.25/go.mod h1:3hX8gzYuyVAZsxl0MRgGTJEmQBFcNTphYh9decYSb74=
github.com/yuin/goldmark v1.1.27/go.mod h1:3hX8gzYuyVAZsxl0MRgGTJEmQBFcNTphYh9decYSb74=
+github.com/yuin/goldmark v1.2.1/go.mod h1:3hX8gzYuyVAZsxl0MRgGTJEmQBFcNTphYh9decYSb74=
github.com/yvasiyarov/go-metrics v0.0.0-20140926110328-57bccd1ccd43/go.mod h1:aX5oPXxHm3bOH+xeAttToC8pqch2ScQN/JoXYupl6xs=
github.com/yvasiyarov/go-metrics v0.0.0-20150112132944-c25f46c4b940/go.mod h1:aX5oPXxHm3bOH+xeAttToC8pqch2ScQN/JoXYupl6xs=
github.com/yvasiyarov/gorelic v0.0.0-20141212073537-a9bba5b9ab50/go.mod h1:NUSPSUX/bi6SeDMUh6brw0nXpxHnc96TguQh0+r/ssA=
-github.com/yvasiyarov/gorelic v0.0.6/go.mod h1:NUSPSUX/bi6SeDMUh6brw0nXpxHnc96TguQh0+r/ssA=
+github.com/yvasiyarov/gorelic v0.0.7/go.mod h1:NUSPSUX/bi6SeDMUh6brw0nXpxHnc96TguQh0+r/ssA=
github.com/yvasiyarov/newrelic_platform_go v0.0.0-20140908184405-b21fdbd4370f/go.mod h1:GlGEuHIJweS1mbCqG+7vt2nvWLzLLnRHbXz5JKd/Qbg=
+github.com/yvasiyarov/newrelic_platform_go v0.0.0-20160601141957-9c099fbc30e9/go.mod h1:GlGEuHIJweS1mbCqG+7vt2nvWLzLLnRHbXz5JKd/Qbg=
github.com/ziutek/mymysql v1.5.4/go.mod h1:LMSpPZ6DbqWFxNCHW77HeMg9I646SAhApZ/wKdgO/C0=
gitlab.com/nyarla/go-crypt v0.0.0-20160106005555-d9a5dc2b789b/go.mod h1:T3BPAOm2cqquPa0MKWeNkmOM5RQsRhkrwMWonFMN7fE=
go.etcd.io/bbolt v1.3.2/go.mod h1:IbVyRI1SCnLcuJnV2u8VeU0CEYM7e686BmAb1XKL+uU=
+go.etcd.io/bbolt v1.3.3/go.mod h1:IbVyRI1SCnLcuJnV2u8VeU0CEYM7e686BmAb1XKL+uU=
+go.etcd.io/bbolt v1.3.5/go.mod h1:G5EMThwa9y8QZGBClrRx5EY+Yw9kAhnjy3bSjsnlVTQ=
+go.etcd.io/etcd v0.0.0-20191023171146-3cf2f69b5738/go.mod h1:dnLIgRNXwCJa5e+c6mIZCrds/GIG4ncV9HhK5PX7jPg=
+go.etcd.io/etcd v0.5.0-alpha.5.0.20200819165624-17cef6e3e9d5/go.mod h1:skWido08r9w6Lq/w70DO5XYIKMu4QFu1+4VsqLQuJy8=
+go.mongodb.org/mongo-driver v1.0.3/go.mod h1:u7ryQJ+DOzQmeO7zB6MHyr8jkEQvC8vH7qLUO4lqsUM=
go.mongodb.org/mongo-driver v1.1.0/go.mod h1:u7ryQJ+DOzQmeO7zB6MHyr8jkEQvC8vH7qLUO4lqsUM=
+go.mongodb.org/mongo-driver v1.1.1/go.mod h1:u7ryQJ+DOzQmeO7zB6MHyr8jkEQvC8vH7qLUO4lqsUM=
+go.mongodb.org/mongo-driver v1.1.2/go.mod h1:u7ryQJ+DOzQmeO7zB6MHyr8jkEQvC8vH7qLUO4lqsUM=
go.opencensus.io v0.20.1/go.mod h1:6WKK9ahsWS3RSO+PY9ZHZUfv2irvY6gN279GOPZjmmk=
+go.opencensus.io v0.20.2/go.mod h1:6WKK9ahsWS3RSO+PY9ZHZUfv2irvY6gN279GOPZjmmk=
go.opencensus.io v0.21.0/go.mod h1:mSImk1erAIZhrmZN+AvHh14ztQfjbGwt4TtuofqLduU=
go.opencensus.io v0.22.0/go.mod h1:+kGneAE2xo2IficOXnaByMWTGM9T73dGwxeWcUqIpI8=
-go.uber.org/atomic v0.0.0-20181018215023-8dc6146f7569/go.mod h1:gD2HeocX3+yG+ygLZcrzQJaqmWj9AIm7n08wl/qW/PE=
+go.opencensus.io v0.22.2/go.mod h1:yxeiOL68Rb0Xd1ddK5vPZ/oVn4vY4Ynel7k9FzqtOIw=
+go.opencensus.io v0.22.3/go.mod h1:yxeiOL68Rb0Xd1ddK5vPZ/oVn4vY4Ynel7k9FzqtOIw=
go.uber.org/atomic v1.3.2/go.mod h1:gD2HeocX3+yG+ygLZcrzQJaqmWj9AIm7n08wl/qW/PE=
-go.uber.org/atomic v1.4.0 h1:cxzIVoETapQEqDhQu3QfnvXAV4AlzcvUCxkVUFw3+EU=
go.uber.org/atomic v1.4.0/go.mod h1:gD2HeocX3+yG+ygLZcrzQJaqmWj9AIm7n08wl/qW/PE=
+go.uber.org/atomic v1.5.0/go.mod h1:sABNBOSYdrvTF6hTgEIbc7YasKWGhgEQZyfxyTvoXHQ=
go.uber.org/atomic v1.6.0 h1:Ezj3JGmsOnG1MoRWQkPBsKLe9DwWD9QeXzTRzzldNVk=
go.uber.org/atomic v1.6.0/go.mod h1:sABNBOSYdrvTF6hTgEIbc7YasKWGhgEQZyfxyTvoXHQ=
-go.uber.org/multierr v0.0.0-20180122172545-ddea229ff1df/go.mod h1:wR5kodmAFQ0UK8QlbwjlSNy0Z68gJhDJUG5sjR94q/0=
-go.uber.org/multierr v1.1.0 h1:HoEmRHQPVSqub6w2z2d2EOVs2fjyFRGyofhKuyDq0QI=
+go.uber.org/goleak v1.1.10 h1:z+mqJhf6ss6BSfSM671tgKyZBFPTTJM+HLxnhPC3wu0=
+go.uber.org/goleak v1.1.10/go.mod h1:8a7PlsEVH3e/a/GLqe5IIrQx6GzcnRmZEufDUTk4A7A=
go.uber.org/multierr v1.1.0/go.mod h1:wR5kodmAFQ0UK8QlbwjlSNy0Z68gJhDJUG5sjR94q/0=
+go.uber.org/multierr v1.3.0/go.mod h1:VgVr7evmIr6uPjLBxg28wmKNXyqE9akIJ5XnfpiKl+4=
go.uber.org/multierr v1.5.0 h1:KCa4XfM8CWFCpxXRGok+Q0SS/0XBhMDbHHGABQLvD2A=
go.uber.org/multierr v1.5.0/go.mod h1:FeouvMocqHpRaaGuG9EjoKcStLC43Zu/fmqdUMPcKYU=
+go.uber.org/tools v0.0.0-20190618225709-2cfd321de3ee h1:0mgffUl7nfd+FpvXMVz4IDEaUSmT1ysygQC7qYo7sG4=
go.uber.org/tools v0.0.0-20190618225709-2cfd321de3ee/go.mod h1:vJERXedbb3MVM5f9Ejo0C68/HhF8uaILCdgjnY+goOA=
-go.uber.org/zap v0.0.0-20180814183419-67bc79d13d15/go.mod h1:vwi/ZaCAaUcBkycHslxD9B2zi4UTXhF60s6SWpuDF0Q=
-go.uber.org/zap v1.9.1/go.mod h1:vwi/ZaCAaUcBkycHslxD9B2zi4UTXhF60s6SWpuDF0Q=
-go.uber.org/zap v1.10.0 h1:ORx85nbTijNz8ljznvCMR1ZBIPKFn3jQrag10X2AsuM=
+go.uber.org/zap v1.8.0/go.mod h1:vwi/ZaCAaUcBkycHslxD9B2zi4UTXhF60s6SWpuDF0Q=
go.uber.org/zap v1.10.0/go.mod h1:vwi/ZaCAaUcBkycHslxD9B2zi4UTXhF60s6SWpuDF0Q=
-go.uber.org/zap v1.14.1 h1:nYDKopTbvAPq/NrUVZwT15y2lpROBiLLyoRTbXOYWOo=
-go.uber.org/zap v1.14.1/go.mod h1:Mb2vm2krFEG5DV0W9qcHBYFtp/Wku1cvYaqPsS/WYfc=
+go.uber.org/zap v1.13.0/go.mod h1:zwrFLgMcdUuIBviXEYEH1YKNaOBnKXsx2IPda5bBwHM=
+go.uber.org/zap v1.15.0 h1:ZZCA22JRF2gQE5FoNmhmrf7jeJJ2uhqDUNRYKm8dvmM=
+go.uber.org/zap v1.15.0/go.mod h1:Mb2vm2krFEG5DV0W9qcHBYFtp/Wku1cvYaqPsS/WYfc=
golang.org/x/crypto v0.0.0-20171113213409-9f005a07e0d3/go.mod h1:6SG95UA2DQfeDnfUPMdvaQW0Q7yPrPDi9nlGo2tz2b4=
-golang.org/x/crypto v0.0.0-20180426230345-b49d69b5da94/go.mod h1:6SG95UA2DQfeDnfUPMdvaQW0Q7yPrPDi9nlGo2tz2b4=
golang.org/x/crypto v0.0.0-20180904163835-0709b304e793/go.mod h1:6SG95UA2DQfeDnfUPMdvaQW0Q7yPrPDi9nlGo2tz2b4=
+golang.org/x/crypto v0.0.0-20181029021203-45a5f77698d3/go.mod h1:6SG95UA2DQfeDnfUPMdvaQW0Q7yPrPDi9nlGo2tz2b4=
golang.org/x/crypto v0.0.0-20181203042331-505ab145d0a9/go.mod h1:6SG95UA2DQfeDnfUPMdvaQW0Q7yPrPDi9nlGo2tz2b4=
-golang.org/x/crypto v0.0.0-20190123085648-057139ce5d2b/go.mod h1:6SG95UA2DQfeDnfUPMdvaQW0Q7yPrPDi9nlGo2tz2b4=
golang.org/x/crypto v0.0.0-20190211182817-74369b46fc67/go.mod h1:6SG95UA2DQfeDnfUPMdvaQW0Q7yPrPDi9nlGo2tz2b4=
-golang.org/x/crypto v0.0.0-20190228161510-8dd112bcdc25/go.mod h1:djNgcEr1/C05ACkg1iLfiJU5Ep61QUkGW8qpdssI0+w=
golang.org/x/crypto v0.0.0-20190308221718-c2843e01d9a2/go.mod h1:djNgcEr1/C05ACkg1iLfiJU5Ep61QUkGW8qpdssI0+w=
golang.org/x/crypto v0.0.0-20190320223903-b7391e95e576/go.mod h1:djNgcEr1/C05ACkg1iLfiJU5Ep61QUkGW8qpdssI0+w=
golang.org/x/crypto v0.0.0-20190325154230-a5d413f7728c/go.mod h1:djNgcEr1/C05ACkg1iLfiJU5Ep61QUkGW8qpdssI0+w=
golang.org/x/crypto v0.0.0-20190426145343-a29dc8fdc734/go.mod h1:yigFU9vqHzYiE8UmvKecakEJjdnWj3jj499lnFckfCI=
golang.org/x/crypto v0.0.0-20190510104115-cbcb75029529/go.mod h1:yigFU9vqHzYiE8UmvKecakEJjdnWj3jj499lnFckfCI=
+golang.org/x/crypto v0.0.0-20190605123033-f99c8df09eb5/go.mod h1:yigFU9vqHzYiE8UmvKecakEJjdnWj3jj499lnFckfCI=
golang.org/x/crypto v0.0.0-20190611184440-5c40567a22f8/go.mod h1:yigFU9vqHzYiE8UmvKecakEJjdnWj3jj499lnFckfCI=
+golang.org/x/crypto v0.0.0-20190617133340-57b3e21c3d56/go.mod h1:yigFU9vqHzYiE8UmvKecakEJjdnWj3jj499lnFckfCI=
golang.org/x/crypto v0.0.0-20190621222207-cc06ce4a13d4/go.mod h1:yigFU9vqHzYiE8UmvKecakEJjdnWj3jj499lnFckfCI=
-golang.org/x/crypto v0.0.0-20190911031432-227b76d455e7/go.mod h1:yigFU9vqHzYiE8UmvKecakEJjdnWj3jj499lnFckfCI=
+golang.org/x/crypto v0.0.0-20190701094942-4def268fd1a4/go.mod h1:yigFU9vqHzYiE8UmvKecakEJjdnWj3jj499lnFckfCI=
golang.org/x/crypto v0.0.0-20191011191535-87dc89f01550/go.mod h1:yigFU9vqHzYiE8UmvKecakEJjdnWj3jj499lnFckfCI=
-golang.org/x/crypto v0.0.0-20191028145041-f83a4685e152 h1:ZC1Xn5A1nlpSmQCIva4bZ3ob3lmhYIefc+GU+DLg1Ow=
-golang.org/x/crypto v0.0.0-20191028145041-f83a4685e152/go.mod h1:LzIPMQfyMNhhGPhUkYOs5KpL4U8rLKemX1yGLhDgUto=
-golang.org/x/crypto v0.0.0-20200128174031-69ecbb4d6d5d h1:9FCpayM9Egr1baVnV1SX0H87m+XB0B8S0hAMi99X/3U=
+golang.org/x/crypto v0.0.0-20191206172530-e9b2fee46413/go.mod h1:LzIPMQfyMNhhGPhUkYOs5KpL4U8rLKemX1yGLhDgUto=
golang.org/x/crypto v0.0.0-20200128174031-69ecbb4d6d5d/go.mod h1:LzIPMQfyMNhhGPhUkYOs5KpL4U8rLKemX1yGLhDgUto=
+golang.org/x/crypto v0.0.0-20200220183623-bac4c82f6975/go.mod h1:LzIPMQfyMNhhGPhUkYOs5KpL4U8rLKemX1yGLhDgUto=
+golang.org/x/crypto v0.0.0-20200414173820-0848c9571904/go.mod h1:LzIPMQfyMNhhGPhUkYOs5KpL4U8rLKemX1yGLhDgUto=
+golang.org/x/crypto v0.0.0-20200622213623-75b288015ac9 h1:psW17arqaxU48Z5kZ0CQnkZWQJsqcURM6tKiBApRjXI=
+golang.org/x/crypto v0.0.0-20200622213623-75b288015ac9/go.mod h1:LzIPMQfyMNhhGPhUkYOs5KpL4U8rLKemX1yGLhDgUto=
+golang.org/x/crypto v0.0.0-20201002170205-7f63de1d35b0 h1:hb9wdF1z5waM+dSIICn1l0DkLVDT3hqhhQsDNUmHPRE=
+golang.org/x/crypto v0.0.0-20201002170205-7f63de1d35b0/go.mod h1:LzIPMQfyMNhhGPhUkYOs5KpL4U8rLKemX1yGLhDgUto=
golang.org/x/exp v0.0.0-20190121172915-509febef88a4/go.mod h1:CJ0aWSM057203Lf6IL+f9T1iT9GByDxfZKAQTCR3kQA=
-golang.org/x/exp v0.0.0-20190125153040-c74c464bbbf2/go.mod h1:CJ0aWSM057203Lf6IL+f9T1iT9GByDxfZKAQTCR3kQA=
-golang.org/x/lint v0.0.0-20180702182130-06c8688daad7/go.mod h1:UVdnD1Gm6xHRNCYTkRU2/jEulfH38KcIWyp/GAMgvoE=
+golang.org/x/exp v0.0.0-20190306152737-a1d7652674e8/go.mod h1:CJ0aWSM057203Lf6IL+f9T1iT9GByDxfZKAQTCR3kQA=
+golang.org/x/exp v0.0.0-20190510132918-efd6b22b2522/go.mod h1:ZjyILWgesfNpC6sMxTJOJm9Kp84zZh5NQWvqDGG3Qr8=
+golang.org/x/exp v0.0.0-20190829153037-c13cbed26979/go.mod h1:86+5VVa7VpoJ4kLfm080zCjGlMRFzhUhsZKEZO7MGek=
+golang.org/x/exp v0.0.0-20191030013958-a1ab85dbe136/go.mod h1:JXzH8nQsPlswgeRAPE3MuO9GYsAcnJvJ4vnMwN/5qkY=
+golang.org/x/exp v0.0.0-20191129062945-2f5052295587/go.mod h1:2RIsYlXP63K8oxa1u096TMicItID8zy7Y6sNkU49FU4=
+golang.org/x/exp v0.0.0-20191227195350-da58074b4299/go.mod h1:2RIsYlXP63K8oxa1u096TMicItID8zy7Y6sNkU49FU4=
+golang.org/x/exp v0.0.0-20200119233911-0405dc783f0a/go.mod h1:2RIsYlXP63K8oxa1u096TMicItID8zy7Y6sNkU49FU4=
+golang.org/x/exp v0.0.0-20200207192155-f17229e696bd/go.mod h1:J/WKrq2StrnmMY6+EHIKF9dgMWnmCNThgcyBT1FY9mM=
+golang.org/x/exp v0.0.0-20200224162631-6cc2880d07d6/go.mod h1:3jZMyOhIsHpP37uCMkUooju7aAi5cS1Q23tOzKc+0MU=
+golang.org/x/image v0.0.0-20190227222117-0694c2d4d067/go.mod h1:kZ7UVZpmo3dzQBMxlp+ypCbDeSB+sBbTgSJuh5dn5js=
+golang.org/x/image v0.0.0-20190802002840-cff245a6509b/go.mod h1:FeLwcggjj3mMvU+oOTbSwawSJRM1uh48EjtB4UJZlP0=
golang.org/x/lint v0.0.0-20181026193005-c67002cb31c3/go.mod h1:UVdnD1Gm6xHRNCYTkRU2/jEulfH38KcIWyp/GAMgvoE=
golang.org/x/lint v0.0.0-20190227174305-5b3e6a55c961/go.mod h1:wehouNa3lNwaWXcvxsM5YxQ5yQlVC4a0KAMCusXpPoU=
golang.org/x/lint v0.0.0-20190301231843-5614ed5bae6f/go.mod h1:UVdnD1Gm6xHRNCYTkRU2/jEulfH38KcIWyp/GAMgvoE=
golang.org/x/lint v0.0.0-20190313153728-d0100b6bd8b3/go.mod h1:6SW0HCj/g11FgYtHlgUYUwCkIfeOF89ocIRzGO/8vkc=
-golang.org/x/lint v0.0.0-20190409202823-959b441ac422 h1:QzoH/1pFpZguR8NrRHLcO6jKqfv2zpuSqZLgdm7ZmjI=
golang.org/x/lint v0.0.0-20190409202823-959b441ac422/go.mod h1:6SW0HCj/g11FgYtHlgUYUwCkIfeOF89ocIRzGO/8vkc=
+golang.org/x/lint v0.0.0-20190909230951-414d861bb4ac/go.mod h1:6SW0HCj/g11FgYtHlgUYUwCkIfeOF89ocIRzGO/8vkc=
golang.org/x/lint v0.0.0-20190930215403-16217165b5de/go.mod h1:6SW0HCj/g11FgYtHlgUYUwCkIfeOF89ocIRzGO/8vkc=
+golang.org/x/lint v0.0.0-20191125180803-fdd1cda4f05f h1:J5lckAjkw6qYlOZNj90mLYNTEKDvWeuc1yieZ8qUzUE=
golang.org/x/lint v0.0.0-20191125180803-fdd1cda4f05f/go.mod h1:5qLYkcX4OjUUV8bRuDixDT3tpyyb+LUpUlRWLxfhWrs=
-golang.org/x/lint v0.0.0-20200302205851-738671d3881b h1:Wh+f8QHJXR411sJR8/vRBTZ7YapZaRvUcLFFJhusH0k=
+golang.org/x/lint v0.0.0-20200130185559-910be7a94367/go.mod h1:3xt1FjdF8hUf6vQPIChWIBhFzV8gjjsPE/fR3IyQdNY=
golang.org/x/lint v0.0.0-20200302205851-738671d3881b/go.mod h1:3xt1FjdF8hUf6vQPIChWIBhFzV8gjjsPE/fR3IyQdNY=
+golang.org/x/mobile v0.0.0-20190312151609-d3739f865fa6/go.mod h1:z+o9i4GpDbdi3rU15maQ/Ox0txvL9dWGYEHz965HBQE=
+golang.org/x/mobile v0.0.0-20190719004257-d2bd2a29d028/go.mod h1:E/iHnbuqvinMTCcRqshq8CkpyQDoeVncDDYHnLhea+o=
golang.org/x/mod v0.0.0-20190513183733-4bf6d317e70e/go.mod h1:mXi4GBBbnImb6dmsKGUJ2LatrhH/nqhxcFungHvyanc=
-golang.org/x/mod v0.1.1-0.20191105210325-c90efee705ee h1:WG0RUwxtNT4qqaXX3DPA8zHFNm/D9xaBpxzHt1WcA/E=
+golang.org/x/mod v0.1.0/go.mod h1:0QHyrYULN0/3qlju5TqG8bIK38QM8yzMo5ekMj3DlcY=
golang.org/x/mod v0.1.1-0.20191105210325-c90efee705ee/go.mod h1:QqPTAvyqsEbceGzBzNggFXnrqF1CaUcvgkdR5Ot7KZg=
-golang.org/x/mod v0.2.0 h1:KU7oHjnv3XNWfa5COkzUifxZmxp1TyI7ImMXqFxLwvQ=
+golang.org/x/mod v0.1.1-0.20191107180719-034126e5016b/go.mod h1:QqPTAvyqsEbceGzBzNggFXnrqF1CaUcvgkdR5Ot7KZg=
golang.org/x/mod v0.2.0/go.mod h1:s0Qsj1ACt9ePp/hMypM3fl4fZqREWJwdYDEqhRiZZUA=
-golang.org/x/net v0.0.0-20180112015858-5ccada7d0a7b/go.mod h1:mL1N/T3taQHkDXs73rZJwtUhF3w3ftmwwsq0BUmARs4=
+golang.org/x/mod v0.3.0 h1:RM4zey1++hCTbCVQfnWeKs9/IEsaBLA8vTkd0WVtmH4=
+golang.org/x/mod v0.3.0/go.mod h1:s0Qsj1ACt9ePp/hMypM3fl4fZqREWJwdYDEqhRiZZUA=
+golang.org/x/net v0.0.0-20170114055629-f2499483f923/go.mod h1:mL1N/T3taQHkDXs73rZJwtUhF3w3ftmwwsq0BUmARs4=
golang.org/x/net v0.0.0-20180724234803-3673e40ba225/go.mod h1:mL1N/T3taQHkDXs73rZJwtUhF3w3ftmwwsq0BUmARs4=
golang.org/x/net v0.0.0-20180826012351-8a410e7b638d/go.mod h1:mL1N/T3taQHkDXs73rZJwtUhF3w3ftmwwsq0BUmARs4=
golang.org/x/net v0.0.0-20180906233101-161cd47e91fd/go.mod h1:mL1N/T3taQHkDXs73rZJwtUhF3w3ftmwwsq0BUmARs4=
-golang.org/x/net v0.0.0-20180911220305-26e67e76b6c3/go.mod h1:mL1N/T3taQHkDXs73rZJwtUhF3w3ftmwwsq0BUmARs4=
golang.org/x/net v0.0.0-20181005035420-146acd28ed58/go.mod h1:mL1N/T3taQHkDXs73rZJwtUhF3w3ftmwwsq0BUmARs4=
-golang.org/x/net v0.0.0-20181102091132-c10e9556a7bc/go.mod h1:mL1N/T3taQHkDXs73rZJwtUhF3w3ftmwwsq0BUmARs4=
-golang.org/x/net v0.0.0-20181106065722-10aee1819953/go.mod h1:mL1N/T3taQHkDXs73rZJwtUhF3w3ftmwwsq0BUmARs4=
+golang.org/x/net v0.0.0-20181023162649-9b4f9f5ad519/go.mod h1:mL1N/T3taQHkDXs73rZJwtUhF3w3ftmwwsq0BUmARs4=
golang.org/x/net v0.0.0-20181108082009-03003ca0c849/go.mod h1:mL1N/T3taQHkDXs73rZJwtUhF3w3ftmwwsq0BUmARs4=
golang.org/x/net v0.0.0-20181114220301-adae6a3d119a/go.mod h1:mL1N/T3taQHkDXs73rZJwtUhF3w3ftmwwsq0BUmARs4=
golang.org/x/net v0.0.0-20181201002055-351d144fa1fc/go.mod h1:mL1N/T3taQHkDXs73rZJwtUhF3w3ftmwwsq0BUmARs4=
golang.org/x/net v0.0.0-20181220203305-927f97764cc3/go.mod h1:mL1N/T3taQHkDXs73rZJwtUhF3w3ftmwwsq0BUmARs4=
golang.org/x/net v0.0.0-20190108225652-1e06a53dbb7e/go.mod h1:mL1N/T3taQHkDXs73rZJwtUhF3w3ftmwwsq0BUmARs4=
golang.org/x/net v0.0.0-20190125091013-d26f9f9a57f3/go.mod h1:mL1N/T3taQHkDXs73rZJwtUhF3w3ftmwwsq0BUmARs4=
-golang.org/x/net v0.0.0-20190206173232-65e2d4e15006/go.mod h1:mL1N/T3taQHkDXs73rZJwtUhF3w3ftmwwsq0BUmARs4=
golang.org/x/net v0.0.0-20190213061140-3a22650c66bd/go.mod h1:mL1N/T3taQHkDXs73rZJwtUhF3w3ftmwwsq0BUmARs4=
golang.org/x/net v0.0.0-20190311183353-d8887717615a/go.mod h1:t9HGtf8HONx5eT2rtn7q6eTqICYqUVnKs3thJo3Qplg=
golang.org/x/net v0.0.0-20190320064053-1272bf9dcd53/go.mod h1:t9HGtf8HONx5eT2rtn7q6eTqICYqUVnKs3thJo3Qplg=
-golang.org/x/net v0.0.0-20190328230028-74de082e2cca/go.mod h1:t9HGtf8HONx5eT2rtn7q6eTqICYqUVnKs3thJo3Qplg=
golang.org/x/net v0.0.0-20190404232315-eb5bcb51f2a3/go.mod h1:t9HGtf8HONx5eT2rtn7q6eTqICYqUVnKs3thJo3Qplg=
golang.org/x/net v0.0.0-20190424112056-4829fb13d2c6/go.mod h1:t9HGtf8HONx5eT2rtn7q6eTqICYqUVnKs3thJo3Qplg=
golang.org/x/net v0.0.0-20190501004415-9ce7a6920f09/go.mod h1:t9HGtf8HONx5eT2rtn7q6eTqICYqUVnKs3thJo3Qplg=
@@ -1009,42 +957,55 @@ golang.org/x/net v0.0.0-20190613194153-d28f0bde5980/go.mod h1:z5CRVTTTmAJ677TzLL
golang.org/x/net v0.0.0-20190619014844-b5b0513f8c1b/go.mod h1:z5CRVTTTmAJ677TzLLGU+0bjPO0LkuOLi4/5GtJWs/s=
golang.org/x/net v0.0.0-20190620200207-3b0461eec859/go.mod h1:z5CRVTTTmAJ677TzLLGU+0bjPO0LkuOLi4/5GtJWs/s=
golang.org/x/net v0.0.0-20190628185345-da137c7871d7/go.mod h1:z5CRVTTTmAJ677TzLLGU+0bjPO0LkuOLi4/5GtJWs/s=
-golang.org/x/net v0.0.0-20190812203447-cdfb69ac37fc/go.mod h1:mL1N/T3taQHkDXs73rZJwtUhF3w3ftmwwsq0BUmARs4=
+golang.org/x/net v0.0.0-20190724013045-ca1201d0de80/go.mod h1:z5CRVTTTmAJ677TzLLGU+0bjPO0LkuOLi4/5GtJWs/s=
+golang.org/x/net v0.0.0-20190813141303-74dc4d7220e7/go.mod h1:z5CRVTTTmAJ677TzLLGU+0bjPO0LkuOLi4/5GtJWs/s=
golang.org/x/net v0.0.0-20190827160401-ba9fcec4b297/go.mod h1:z5CRVTTTmAJ677TzLLGU+0bjPO0LkuOLi4/5GtJWs/s=
-golang.org/x/net v0.0.0-20190923162816-aa69164e4478/go.mod h1:z5CRVTTTmAJ677TzLLGU+0bjPO0LkuOLi4/5GtJWs/s=
-golang.org/x/net v0.0.0-20191028085509-fe3aa8a45271 h1:N66aaryRB3Ax92gH0v3hp1QYZ3zWWCCUR/j8Ifh45Ss=
-golang.org/x/net v0.0.0-20191028085509-fe3aa8a45271/go.mod h1:z5CRVTTTmAJ677TzLLGU+0bjPO0LkuOLi4/5GtJWs/s=
+golang.org/x/net v0.0.0-20191004110552-13f9640d40b9/go.mod h1:z5CRVTTTmAJ677TzLLGU+0bjPO0LkuOLi4/5GtJWs/s=
+golang.org/x/net v0.0.0-20191021144547-ec77196f6094/go.mod h1:z5CRVTTTmAJ677TzLLGU+0bjPO0LkuOLi4/5GtJWs/s=
golang.org/x/net v0.0.0-20191209160850-c0dbc17a3553/go.mod h1:z5CRVTTTmAJ677TzLLGU+0bjPO0LkuOLi4/5GtJWs/s=
-golang.org/x/net v0.0.0-20200226121028-0de0cce0169b h1:0mm1VjtFUOIlE1SbDlwjYaDxZVDP2S5ou6y0gSgXHu8=
+golang.org/x/net v0.0.0-20200114155413-6afb5195e5aa/go.mod h1:z5CRVTTTmAJ677TzLLGU+0bjPO0LkuOLi4/5GtJWs/s=
+golang.org/x/net v0.0.0-20200202094626-16171245cfb2/go.mod h1:z5CRVTTTmAJ677TzLLGU+0bjPO0LkuOLi4/5GtJWs/s=
+golang.org/x/net v0.0.0-20200222125558-5a598a2470a0/go.mod h1:z5CRVTTTmAJ677TzLLGU+0bjPO0LkuOLi4/5GtJWs/s=
golang.org/x/net v0.0.0-20200226121028-0de0cce0169b/go.mod h1:z5CRVTTTmAJ677TzLLGU+0bjPO0LkuOLi4/5GtJWs/s=
+golang.org/x/net v0.0.0-20200301022130-244492dfa37a/go.mod h1:z5CRVTTTmAJ677TzLLGU+0bjPO0LkuOLi4/5GtJWs/s=
+golang.org/x/net v0.0.0-20200324143707-d3edc9973b7e/go.mod h1:qpuaurCH72eLCgpAm/N6yyVIVM9cpaDIP3A8BGJEC5A=
+golang.org/x/net v0.0.0-20200520004742-59133d7f0dd7/go.mod h1:qpuaurCH72eLCgpAm/N6yyVIVM9cpaDIP3A8BGJEC5A=
+golang.org/x/net v0.0.0-20200707034311-ab3426394381 h1:VXak5I6aEWmAXeQjA+QSZzlgNrpq9mjcfDemuexIKsU=
+golang.org/x/net v0.0.0-20200707034311-ab3426394381/go.mod h1:/O7V0waA8r7cgGh81Ro3o1hOxt32SMVPicZroKQ2sZA=
+golang.org/x/net v0.0.0-20200822124328-c89045814202/go.mod h1:/O7V0waA8r7cgGh81Ro3o1hOxt32SMVPicZroKQ2sZA=
+golang.org/x/net v0.0.0-20201110031124-69a78807bb2b h1:uwuIcX0g4Yl1NC5XAz37xsr2lTtcqevgzYNVt49waME=
+golang.org/x/net v0.0.0-20201110031124-69a78807bb2b/go.mod h1:sp8m0HH+o8qH0wwXwYZr8TS3Oi6o0r6Gce1SSxlDquU=
golang.org/x/oauth2 v0.0.0-20180821212333-d2e6202438be/go.mod h1:N/0e6XlmueqKjAGxoOufVs8QHGRruUQn6yWY3a++T0U=
golang.org/x/oauth2 v0.0.0-20181106182150-f42d05182288/go.mod h1:N/0e6XlmueqKjAGxoOufVs8QHGRruUQn6yWY3a++T0U=
golang.org/x/oauth2 v0.0.0-20190226205417-e64efc72b421/go.mod h1:gOpvHmFTYa4IltrdGE7lF6nIHvwfUNPOp7c8zoXwtLw=
golang.org/x/oauth2 v0.0.0-20190402181905-9f3314589c9a/go.mod h1:gOpvHmFTYa4IltrdGE7lF6nIHvwfUNPOp7c8zoXwtLw=
-golang.org/x/oauth2 v0.0.0-20190604053449-0f29369cfe45 h1:SVwTIAaPC2U/AvvLNZ2a7OVsmBpC8L5BlwK1whH3hm0=
golang.org/x/oauth2 v0.0.0-20190604053449-0f29369cfe45/go.mod h1:gOpvHmFTYa4IltrdGE7lF6nIHvwfUNPOp7c8zoXwtLw=
+golang.org/x/oauth2 v0.0.0-20191202225959-858c2ad4c8b6 h1:pE8b58s1HRDMi8RDc79m0HISf9D4TzseP40cEA6IGfs=
+golang.org/x/oauth2 v0.0.0-20191202225959-858c2ad4c8b6/go.mod h1:gOpvHmFTYa4IltrdGE7lF6nIHvwfUNPOp7c8zoXwtLw=
+golang.org/x/oauth2 v0.0.0-20200107190931-bf48bf16ab8d h1:TzXSXBo42m9gQenoE3b9BGiEpg5IG2JkU5FkPIawgtw=
+golang.org/x/oauth2 v0.0.0-20200107190931-bf48bf16ab8d/go.mod h1:gOpvHmFTYa4IltrdGE7lF6nIHvwfUNPOp7c8zoXwtLw=
golang.org/x/sync v0.0.0-20180314180146-1d60e4601c6f/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM=
golang.org/x/sync v0.0.0-20181108010431-42b317875d0f/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM=
golang.org/x/sync v0.0.0-20181221193216-37e7f081c4d4/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM=
golang.org/x/sync v0.0.0-20190227155943-e225da77a7e6/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM=
golang.org/x/sync v0.0.0-20190423024810-112230192c58/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM=
golang.org/x/sync v0.0.0-20190911185100-cd5d95a43a6e/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM=
-golang.org/x/sys v0.0.0-20180117170059-2c42eef0765b/go.mod h1:STP8DvDyc/dI5b8T5hshtkjS+E42TnysNCUPdjciGhY=
+golang.org/x/sync v0.0.0-20200625203802-6e8e738ad208/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM=
+golang.org/x/sys v0.0.0-20170830134202-bb24a47a89ea/go.mod h1:STP8DvDyc/dI5b8T5hshtkjS+E42TnysNCUPdjciGhY=
+golang.org/x/sys v0.0.0-20180823144017-11551d06cbcc/go.mod h1:STP8DvDyc/dI5b8T5hshtkjS+E42TnysNCUPdjciGhY=
golang.org/x/sys v0.0.0-20180830151530-49385e6e1522/go.mod h1:STP8DvDyc/dI5b8T5hshtkjS+E42TnysNCUPdjciGhY=
golang.org/x/sys v0.0.0-20180905080454-ebe1bf3edb33/go.mod h1:STP8DvDyc/dI5b8T5hshtkjS+E42TnysNCUPdjciGhY=
golang.org/x/sys v0.0.0-20180909124046-d0be0721c37e/go.mod h1:STP8DvDyc/dI5b8T5hshtkjS+E42TnysNCUPdjciGhY=
-golang.org/x/sys v0.0.0-20181004145325-8469e314837c/go.mod h1:STP8DvDyc/dI5b8T5hshtkjS+E42TnysNCUPdjciGhY=
+golang.org/x/sys v0.0.0-20181026203630-95b1ffbd15a5/go.mod h1:STP8DvDyc/dI5b8T5hshtkjS+E42TnysNCUPdjciGhY=
golang.org/x/sys v0.0.0-20181107165924-66b7b1311ac8/go.mod h1:STP8DvDyc/dI5b8T5hshtkjS+E42TnysNCUPdjciGhY=
golang.org/x/sys v0.0.0-20181116152217-5ac8a444bdc5/go.mod h1:STP8DvDyc/dI5b8T5hshtkjS+E42TnysNCUPdjciGhY=
golang.org/x/sys v0.0.0-20181122145206-62eef0e2fa9b/go.mod h1:STP8DvDyc/dI5b8T5hshtkjS+E42TnysNCUPdjciGhY=
golang.org/x/sys v0.0.0-20181205085412-a5c9d58dba9a/go.mod h1:STP8DvDyc/dI5b8T5hshtkjS+E42TnysNCUPdjciGhY=
golang.org/x/sys v0.0.0-20190102155601-82a175fd1598/go.mod h1:STP8DvDyc/dI5b8T5hshtkjS+E42TnysNCUPdjciGhY=
-golang.org/x/sys v0.0.0-20190124100055-b90733256f2e/go.mod h1:STP8DvDyc/dI5b8T5hshtkjS+E42TnysNCUPdjciGhY=
golang.org/x/sys v0.0.0-20190209173611-3b5209105503/go.mod h1:STP8DvDyc/dI5b8T5hshtkjS+E42TnysNCUPdjciGhY=
golang.org/x/sys v0.0.0-20190215142949-d0b11bdaac8a/go.mod h1:STP8DvDyc/dI5b8T5hshtkjS+E42TnysNCUPdjciGhY=
golang.org/x/sys v0.0.0-20190222072716-a9d3bda3a223/go.mod h1:STP8DvDyc/dI5b8T5hshtkjS+E42TnysNCUPdjciGhY=
-golang.org/x/sys v0.0.0-20190228124157-a34e9553db1e/go.mod h1:STP8DvDyc/dI5b8T5hshtkjS+E42TnysNCUPdjciGhY=
-golang.org/x/sys v0.0.0-20190310054646-10058d7d4faa/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
+golang.org/x/sys v0.0.0-20190312061237-fead79001313/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
golang.org/x/sys v0.0.0-20190321052220-f7bb7a8bee54/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
golang.org/x/sys v0.0.0-20190412213103-97732733099d/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
golang.org/x/sys v0.0.0-20190422165155-953cdadca894/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
@@ -1054,249 +1015,361 @@ golang.org/x/sys v0.0.0-20190507160741-ecd444e8653b/go.mod h1:h1NjWce9XRLGQEsW7w
golang.org/x/sys v0.0.0-20190514135907-3a4b5fb9f71f/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
golang.org/x/sys v0.0.0-20190515120540-06a5c4944438/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
golang.org/x/sys v0.0.0-20190602015325-4c4f7f33c9ed/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
+golang.org/x/sys v0.0.0-20190606165138-5da285871e9c/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
golang.org/x/sys v0.0.0-20190616124812-15dcb6c0061f/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
+golang.org/x/sys v0.0.0-20190624142023-c5567b49c5d0/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
golang.org/x/sys v0.0.0-20190626221950-04f50cda93cb/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
+golang.org/x/sys v0.0.0-20190726091711-fc99dfbffb4e/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
golang.org/x/sys v0.0.0-20190801041406-cbf593c0f2f3/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
+golang.org/x/sys v0.0.0-20190826190057-c7b8b68b1456/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
+golang.org/x/sys v0.0.0-20190904154756-749cb33beabd/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
golang.org/x/sys v0.0.0-20190916202348-b4ddaad3f8a3/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
-golang.org/x/sys v0.0.0-20191010194322-b09406accb47/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
-golang.org/x/sys v0.0.0-20191028164358-195ce5e7f934 h1:u/E0NqCIWRDAo9WCFo6Ko49njPFDLSd3z+X1HgWDMpE=
-golang.org/x/sys v0.0.0-20191028164358-195ce5e7f934/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
+golang.org/x/sys v0.0.0-20191001151750-bb3f8db39f24/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
+golang.org/x/sys v0.0.0-20191005200804-aed5e4c7ecf9/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
+golang.org/x/sys v0.0.0-20191020212454-3e7259c5e7c2/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
+golang.org/x/sys v0.0.0-20191022100944-742c48ecaeb7/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
+golang.org/x/sys v0.0.0-20191026070338-33540a1f6037/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
golang.org/x/sys v0.0.0-20191120155948-bd437916bb0e/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
-golang.org/x/sys v0.0.0-20191210023423-ac6580df4449/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
-golang.org/x/sys v0.0.0-20200116001909-b77594299b42/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
-golang.org/x/sys v0.0.0-20200122134326-e047566fdf82 h1:ywK/j/KkyTHcdyYSZNXGjMwgmDSfjglYZ3vStQ/gSCU=
+golang.org/x/sys v0.0.0-20191204072324-ce4227a45e2e/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
+golang.org/x/sys v0.0.0-20191220142924-d4481acd189f/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
+golang.org/x/sys v0.0.0-20191228213918-04cbcbbfeed8/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
+golang.org/x/sys v0.0.0-20200106162015-b016eb3dc98e/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
+golang.org/x/sys v0.0.0-20200113162924-86b910548bc1/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
+golang.org/x/sys v0.0.0-20200120151820-655fe14d7479/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
golang.org/x/sys v0.0.0-20200122134326-e047566fdf82/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
+golang.org/x/sys v0.0.0-20200202164722-d101bd2416d5/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
+golang.org/x/sys v0.0.0-20200212091648-12a6c2dcc1e4/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
+golang.org/x/sys v0.0.0-20200223170610-d5e6a3e2c0ae/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
+golang.org/x/sys v0.0.0-20200302150141-5c8b2ff67527/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
+golang.org/x/sys v0.0.0-20200323222414-85ca7c5b95cd/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
+golang.org/x/sys v0.0.0-20200519105757-fe76b779f299/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
+golang.org/x/sys v0.0.0-20200615200032-f1bc736245b1/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
+golang.org/x/sys v0.0.0-20200622214017-ed371f2e16b4 h1:5/PjkGUjvEU5Gl6BxmvKRPpqo2uNMv4rcHBMwzk/st8=
+golang.org/x/sys v0.0.0-20200622214017-ed371f2e16b4/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
+golang.org/x/sys v0.0.0-20200625212154-ddb9806d33ae/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
+golang.org/x/sys v0.0.0-20200930185726-fdedc70b468f/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
+golang.org/x/sys v0.0.0-20201112073958-5cba982894dd h1:5CtCZbICpIOFdgO940moixOPjc0178IU44m4EjOO5IY=
+golang.org/x/sys v0.0.0-20201112073958-5cba982894dd/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
+golang.org/x/text v0.0.0-20160726164857-2910a502d2bf/go.mod h1:NqM8EUOU14njkJ3fqMW+pc6Ldnwhi/IjpwHt7yyuwOQ=
+golang.org/x/text v0.0.0-20170915032832-14c0d48ead0c/go.mod h1:NqM8EUOU14njkJ3fqMW+pc6Ldnwhi/IjpwHt7yyuwOQ=
golang.org/x/text v0.3.0/go.mod h1:NqM8EUOU14njkJ3fqMW+pc6Ldnwhi/IjpwHt7yyuwOQ=
-golang.org/x/text v0.3.1-0.20171227012246-e19ae1496984/go.mod h1:NqM8EUOU14njkJ3fqMW+pc6Ldnwhi/IjpwHt7yyuwOQ=
golang.org/x/text v0.3.1-0.20180807135948-17ff2d5776d2/go.mod h1:NqM8EUOU14njkJ3fqMW+pc6Ldnwhi/IjpwHt7yyuwOQ=
-golang.org/x/text v0.3.2 h1:tW2bmiBqwgJj/UpqtC8EpXEZVYOwU0yG4iWbprSVAcs=
golang.org/x/text v0.3.2/go.mod h1:bEr9sfX3Q8Zfm5fL9x+3itogRgK3+ptLWKqgva+5dAk=
+golang.org/x/text v0.3.3 h1:cokOdA+Jmi5PJGXLlLllQSgYigAEfHXJAERHVMaCc2k=
+golang.org/x/text v0.3.3/go.mod h1:5Zoc/QRtKVWzQhOtBMvqHzDpF6irO9z98xDceosuGiQ=
+golang.org/x/text v0.3.4 h1:0YWbFKbhXG/wIiuHDSKpS0Iy7FSA+u45VtBMfQcFTTc=
+golang.org/x/text v0.3.4/go.mod h1:5Zoc/QRtKVWzQhOtBMvqHzDpF6irO9z98xDceosuGiQ=
+golang.org/x/time v0.0.0-20180412165947-fbb02b2291d2/go.mod h1:tRJNPiyCQ0inRvYxbN9jk5I+vvW/OXSQhTDSoE431IQ=
golang.org/x/time v0.0.0-20181108054448-85acf8d2951c/go.mod h1:tRJNPiyCQ0inRvYxbN9jk5I+vvW/OXSQhTDSoE431IQ=
golang.org/x/time v0.0.0-20190308202827-9d24e82272b4/go.mod h1:tRJNPiyCQ0inRvYxbN9jk5I+vvW/OXSQhTDSoE431IQ=
-golang.org/x/time v0.0.0-20191024005414-555d28b269f0 h1:/5xXl8Y5W96D+TtHSlonuFqGHIWVuyCkGJLwGh9JJFs=
golang.org/x/time v0.0.0-20191024005414-555d28b269f0/go.mod h1:tRJNPiyCQ0inRvYxbN9jk5I+vvW/OXSQhTDSoE431IQ=
-golang.org/x/tools v0.0.0-20170824195420-5d2fd3ccab98/go.mod h1:n7NCudcB/nEzxVGmLbDWY5pfWTLqBcC2KZ6jyYvM4mQ=
+golang.org/x/time v0.0.0-20200630173020-3af7569d3a1e h1:EHBhcS0mlXEAVwNyO2dLfjToGsyY4j24pTs2ScHnX7s=
+golang.org/x/time v0.0.0-20200630173020-3af7569d3a1e/go.mod h1:tRJNPiyCQ0inRvYxbN9jk5I+vvW/OXSQhTDSoE431IQ=
golang.org/x/tools v0.0.0-20180221164845-07fd8470d635/go.mod h1:n7NCudcB/nEzxVGmLbDWY5pfWTLqBcC2KZ6jyYvM4mQ=
-golang.org/x/tools v0.0.0-20180525024113-a5b4c53f6e8b/go.mod h1:n7NCudcB/nEzxVGmLbDWY5pfWTLqBcC2KZ6jyYvM4mQ=
golang.org/x/tools v0.0.0-20180828015842-6cd1fcedba52/go.mod h1:n7NCudcB/nEzxVGmLbDWY5pfWTLqBcC2KZ6jyYvM4mQ=
golang.org/x/tools v0.0.0-20180917221912-90fa682c2a6e/go.mod h1:n7NCudcB/nEzxVGmLbDWY5pfWTLqBcC2KZ6jyYvM4mQ=
+golang.org/x/tools v0.0.0-20181011042414-1f849cf54d09/go.mod h1:n7NCudcB/nEzxVGmLbDWY5pfWTLqBcC2KZ6jyYvM4mQ=
golang.org/x/tools v0.0.0-20181030221726-6c7e314b6563/go.mod h1:n7NCudcB/nEzxVGmLbDWY5pfWTLqBcC2KZ6jyYvM4mQ=
-golang.org/x/tools v0.0.0-20181117154741-2ddaf7f79a09/go.mod h1:n7NCudcB/nEzxVGmLbDWY5pfWTLqBcC2KZ6jyYvM4mQ=
-golang.org/x/tools v0.0.0-20190110163146-51295c7ec13a/go.mod h1:n7NCudcB/nEzxVGmLbDWY5pfWTLqBcC2KZ6jyYvM4mQ=
golang.org/x/tools v0.0.0-20190114222345-bf090417da8b/go.mod h1:n7NCudcB/nEzxVGmLbDWY5pfWTLqBcC2KZ6jyYvM4mQ=
-golang.org/x/tools v0.0.0-20190206041539-40960b6deb8e/go.mod h1:n7NCudcB/nEzxVGmLbDWY5pfWTLqBcC2KZ6jyYvM4mQ=
-golang.org/x/tools v0.0.0-20190213015956-f7e1b50d2251/go.mod h1:n7NCudcB/nEzxVGmLbDWY5pfWTLqBcC2KZ6jyYvM4mQ=
-golang.org/x/tools v0.0.0-20190221204921-83362c3779f5/go.mod h1:9Yl7xja0Znq3iFh3HoIrodX9oNMXvdceNzlUR8zjMvY=
+golang.org/x/tools v0.0.0-20190125232054-d66bd3c5d5a6/go.mod h1:n7NCudcB/nEzxVGmLbDWY5pfWTLqBcC2KZ6jyYvM4mQ=
golang.org/x/tools v0.0.0-20190226205152-f727befe758c/go.mod h1:9Yl7xja0Znq3iFh3HoIrodX9oNMXvdceNzlUR8zjMvY=
golang.org/x/tools v0.0.0-20190311212946-11955173bddd/go.mod h1:LCzVGOaR6xXOjkQ3onu1FJEFr0SW1gC7cKk1uF8kGRs=
-golang.org/x/tools v0.0.0-20190311215038-5c2858a9cfe5/go.mod h1:LCzVGOaR6xXOjkQ3onu1FJEFr0SW1gC7cKk1uF8kGRs=
+golang.org/x/tools v0.0.0-20190312151545-0bb0c0a6e846/go.mod h1:LCzVGOaR6xXOjkQ3onu1FJEFr0SW1gC7cKk1uF8kGRs=
golang.org/x/tools v0.0.0-20190312170243-e65039ee4138/go.mod h1:LCzVGOaR6xXOjkQ3onu1FJEFr0SW1gC7cKk1uF8kGRs=
-golang.org/x/tools v0.0.0-20190322203728-c1a832b0ad89/go.mod h1:LCzVGOaR6xXOjkQ3onu1FJEFr0SW1gC7cKk1uF8kGRs=
golang.org/x/tools v0.0.0-20190328211700-ab21143f2384/go.mod h1:LCzVGOaR6xXOjkQ3onu1FJEFr0SW1gC7cKk1uF8kGRs=
golang.org/x/tools v0.0.0-20190425150028-36563e24a262/go.mod h1:RgjU9mgBXZiqYHBnxXauZ1Gv1EHHAz9KjViQ78xBX0Q=
golang.org/x/tools v0.0.0-20190425222832-ad9eeb80039a/go.mod h1:RgjU9mgBXZiqYHBnxXauZ1Gv1EHHAz9KjViQ78xBX0Q=
golang.org/x/tools v0.0.0-20190506145303-2d16b83fe98c/go.mod h1:RgjU9mgBXZiqYHBnxXauZ1Gv1EHHAz9KjViQ78xBX0Q=
-golang.org/x/tools v0.0.0-20190521203540-521d6ed310dd/go.mod h1:RgjU9mgBXZiqYHBnxXauZ1Gv1EHHAz9KjViQ78xBX0Q=
golang.org/x/tools v0.0.0-20190524140312-2c0ae7006135/go.mod h1:RgjU9mgBXZiqYHBnxXauZ1Gv1EHHAz9KjViQ78xBX0Q=
+golang.org/x/tools v0.0.0-20190606124116-d0a3d012864b/go.mod h1:/rFqwRUd4F7ZHNgwSSTFct+R/Kf4OFW1sUzUTQQTgfc=
golang.org/x/tools v0.0.0-20190614205625-5aca471b1d59/go.mod h1:/rFqwRUd4F7ZHNgwSSTFct+R/Kf4OFW1sUzUTQQTgfc=
+golang.org/x/tools v0.0.0-20190617190820-da514acc4774/go.mod h1:/rFqwRUd4F7ZHNgwSSTFct+R/Kf4OFW1sUzUTQQTgfc=
golang.org/x/tools v0.0.0-20190621195816-6e04913cbbac/go.mod h1:/rFqwRUd4F7ZHNgwSSTFct+R/Kf4OFW1sUzUTQQTgfc=
-golang.org/x/tools v0.0.0-20190624180213-70d37148ca0c/go.mod h1:/rFqwRUd4F7ZHNgwSSTFct+R/Kf4OFW1sUzUTQQTgfc=
+golang.org/x/tools v0.0.0-20190624222133-a101b041ded4/go.mod h1:/rFqwRUd4F7ZHNgwSSTFct+R/Kf4OFW1sUzUTQQTgfc=
+golang.org/x/tools v0.0.0-20190628153133-6cdbf07be9d0/go.mod h1:/rFqwRUd4F7ZHNgwSSTFct+R/Kf4OFW1sUzUTQQTgfc=
golang.org/x/tools v0.0.0-20190706070813-72ffa07ba3db/go.mod h1:jcCCGcm9btYwXyDqrUWc6MKQKKGJCWEQ3AfLSRIbEuI=
-golang.org/x/tools v0.0.0-20190719005602-e377ae9d6386/go.mod h1:jcCCGcm9btYwXyDqrUWc6MKQKKGJCWEQ3AfLSRIbEuI=
-golang.org/x/tools v0.0.0-20190910044552-dd2b5c81c578/go.mod h1:b+2E5dAYhXwXZwtnZ6UAqBI28+e2cm9otk0dWdXHAEo=
-golang.org/x/tools v0.0.0-20191018212557-ed542cd5b28a/go.mod h1:b+2E5dAYhXwXZwtnZ6UAqBI28+e2cm9otk0dWdXHAEo=
+golang.org/x/tools v0.0.0-20190816200558-6889da9d5479/go.mod h1:b+2E5dAYhXwXZwtnZ6UAqBI28+e2cm9otk0dWdXHAEo=
+golang.org/x/tools v0.0.0-20190911174233-4f2ddba30aff/go.mod h1:b+2E5dAYhXwXZwtnZ6UAqBI28+e2cm9otk0dWdXHAEo=
+golang.org/x/tools v0.0.0-20190920225731-5eefd052ad72/go.mod h1:b+2E5dAYhXwXZwtnZ6UAqBI28+e2cm9otk0dWdXHAEo=
+golang.org/x/tools v0.0.0-20191004055002-72853e10c5a3/go.mod h1:b+2E5dAYhXwXZwtnZ6UAqBI28+e2cm9otk0dWdXHAEo=
+golang.org/x/tools v0.0.0-20191012152004-8de300cfc20a/go.mod h1:b+2E5dAYhXwXZwtnZ6UAqBI28+e2cm9otk0dWdXHAEo=
golang.org/x/tools v0.0.0-20191029041327-9cc4af7d6b2c/go.mod h1:b+2E5dAYhXwXZwtnZ6UAqBI28+e2cm9otk0dWdXHAEo=
golang.org/x/tools v0.0.0-20191029190741-b9c20aec41a5/go.mod h1:b+2E5dAYhXwXZwtnZ6UAqBI28+e2cm9otk0dWdXHAEo=
golang.org/x/tools v0.0.0-20191030203535-5e247c9ad0a0/go.mod h1:b+2E5dAYhXwXZwtnZ6UAqBI28+e2cm9otk0dWdXHAEo=
golang.org/x/tools v0.0.0-20191108193012-7d206e10da11/go.mod h1:b+2E5dAYhXwXZwtnZ6UAqBI28+e2cm9otk0dWdXHAEo=
+golang.org/x/tools v0.0.0-20191112195655-aa38f8e97acc/go.mod h1:b+2E5dAYhXwXZwtnZ6UAqBI28+e2cm9otk0dWdXHAEo=
+golang.org/x/tools v0.0.0-20191113191852-77e3bb0ad9e7/go.mod h1:b+2E5dAYhXwXZwtnZ6UAqBI28+e2cm9otk0dWdXHAEo=
+golang.org/x/tools v0.0.0-20191115202509-3a792d9c32b2/go.mod h1:b+2E5dAYhXwXZwtnZ6UAqBI28+e2cm9otk0dWdXHAEo=
golang.org/x/tools v0.0.0-20191119224855-298f0cb1881e/go.mod h1:b+2E5dAYhXwXZwtnZ6UAqBI28+e2cm9otk0dWdXHAEo=
golang.org/x/tools v0.0.0-20191125144606-a911d9008d1f/go.mod h1:b+2E5dAYhXwXZwtnZ6UAqBI28+e2cm9otk0dWdXHAEo=
-golang.org/x/tools v0.0.0-20200115044656-831fdb1e1868/go.mod h1:TB2adYChydJhpapKDTa4BR/hXlZSLoq2Wpct/0txZ28=
+golang.org/x/tools v0.0.0-20191130070609-6e064ea0cf2d/go.mod h1:b+2E5dAYhXwXZwtnZ6UAqBI28+e2cm9otk0dWdXHAEo=
+golang.org/x/tools v0.0.0-20191216173652-a0e659d51361/go.mod h1:TB2adYChydJhpapKDTa4BR/hXlZSLoq2Wpct/0txZ28=
+golang.org/x/tools v0.0.0-20191227053925-7b8e75db28f4/go.mod h1:TB2adYChydJhpapKDTa4BR/hXlZSLoq2Wpct/0txZ28=
+golang.org/x/tools v0.0.0-20200103221440-774c71fcf114/go.mod h1:TB2adYChydJhpapKDTa4BR/hXlZSLoq2Wpct/0txZ28=
+golang.org/x/tools v0.0.0-20200117161641-43d50277825c/go.mod h1:TB2adYChydJhpapKDTa4BR/hXlZSLoq2Wpct/0txZ28=
+golang.org/x/tools v0.0.0-20200122220014-bf1340f18c4a/go.mod h1:TB2adYChydJhpapKDTa4BR/hXlZSLoq2Wpct/0txZ28=
golang.org/x/tools v0.0.0-20200130002326-2f3ba24bd6e7/go.mod h1:TB2adYChydJhpapKDTa4BR/hXlZSLoq2Wpct/0txZ28=
-golang.org/x/tools v0.0.0-20200210192313-1ace956b0e17 h1:a/Fd23DJvg1CaeDH0dYHahE+hCI0v9rFgxSNIThoUcM=
-golang.org/x/tools v0.0.0-20200210192313-1ace956b0e17/go.mod h1:TB2adYChydJhpapKDTa4BR/hXlZSLoq2Wpct/0txZ28=
-golang.org/x/tools v0.0.0-20200228224639-71482053b885/go.mod h1:TB2adYChydJhpapKDTa4BR/hXlZSLoq2Wpct/0txZ28=
-golang.org/x/tools v0.0.0-20200324003944-a576cf524670/go.mod h1:Sl4aGygMT6LrqrWclx+PTx3U+LnKx/seiNR+3G19Ar8=
-golang.org/x/tools v0.0.0-20200327195553-82bb89366a1e/go.mod h1:Sl4aGygMT6LrqrWclx+PTx3U+LnKx/seiNR+3G19Ar8=
-golang.org/x/tools v0.0.0-20200414032229-332987a829c3/go.mod h1:EkVYQZoAsY45+roYkvgYkIh4xh/qjgUK9TdY2XT94GE=
-golang.org/x/tools v0.0.0-20200422022333-3d57cf2e726e/go.mod h1:EkVYQZoAsY45+roYkvgYkIh4xh/qjgUK9TdY2XT94GE=
-golang.org/x/tools v0.0.0-20200426102838-f3a5411a4c3b h1:zSzQJAznWxAh9fZxiPy2FZo+ZZEYoYFYYDYdOrU7AaM=
-golang.org/x/tools v0.0.0-20200426102838-f3a5411a4c3b/go.mod h1:EkVYQZoAsY45+roYkvgYkIh4xh/qjgUK9TdY2XT94GE=
-golang.org/x/tools v0.0.0-20200504022951-6b6965ac5dd1 h1:C8rdnd6KieI73Z2Av0sS0t4kW+geIH/M8kNX8Hmvn9E=
-golang.org/x/tools v0.0.0-20200504022951-6b6965ac5dd1/go.mod h1:EkVYQZoAsY45+roYkvgYkIh4xh/qjgUK9TdY2XT94GE=
-golang.org/x/tools v0.0.0-20200505023115-26f46d2f7ef8 h1:BMFHd4OFnFtWX46Xj4DN6vvT1btiBxyq+s0orYBqcQY=
-golang.org/x/tools v0.0.0-20200505023115-26f46d2f7ef8/go.mod h1:EkVYQZoAsY45+roYkvgYkIh4xh/qjgUK9TdY2XT94GE=
+golang.org/x/tools v0.0.0-20200204074204-1cc6d1ef6c74/go.mod h1:TB2adYChydJhpapKDTa4BR/hXlZSLoq2Wpct/0txZ28=
+golang.org/x/tools v0.0.0-20200207183749-b753a1ba74fa/go.mod h1:TB2adYChydJhpapKDTa4BR/hXlZSLoq2Wpct/0txZ28=
+golang.org/x/tools v0.0.0-20200212150539-ea181f53ac56/go.mod h1:TB2adYChydJhpapKDTa4BR/hXlZSLoq2Wpct/0txZ28=
+golang.org/x/tools v0.0.0-20200224181240-023911ca70b2/go.mod h1:TB2adYChydJhpapKDTa4BR/hXlZSLoq2Wpct/0txZ28=
+golang.org/x/tools v0.0.0-20200304193943-95d2e580d8eb/go.mod h1:o4KQGtdN14AW+yjsvvwRTJJuXz8XRtIHtEnmAXLyFUw=
+golang.org/x/tools v0.0.0-20200403190813-44a64ad78b9b/go.mod h1:EkVYQZoAsY45+roYkvgYkIh4xh/qjgUK9TdY2XT94GE=
+golang.org/x/tools v0.0.0-20200616133436-c1934b75d054 h1:HHeAlu5H9b71C+Fx0K+1dGgVFN1DM1/wz4aoGOA5qS8=
+golang.org/x/tools v0.0.0-20200616133436-c1934b75d054/go.mod h1:EkVYQZoAsY45+roYkvgYkIh4xh/qjgUK9TdY2XT94GE=
+golang.org/x/tools v0.0.0-20200616195046-dc31b401abb5 h1:UaoXseXAWUJUcuJ2E2oczJdLxAJXL0lOmVaBl7kuk+I=
+golang.org/x/tools v0.0.0-20200616195046-dc31b401abb5/go.mod h1:EkVYQZoAsY45+roYkvgYkIh4xh/qjgUK9TdY2XT94GE=
+golang.org/x/tools v0.0.0-20201014231627-1610a49f37af/go.mod h1:z6u4i615ZeAfBE4XtMziQW1fSVJXACjjbWkB/mvPzlU=
golang.org/x/xerrors v0.0.0-20190717185122-a985d3407aa7/go.mod h1:I/5z698sn9Ka8TeJc9MKroUUfqBBauWjQqLJ2OPfmY0=
-golang.org/x/xerrors v0.0.0-20191011141410-1b5146add898 h1:/atklqdjdhuosWIl6AIbOeHJjicWYPqR9bpxqxYG2pA=
golang.org/x/xerrors v0.0.0-20191011141410-1b5146add898/go.mod h1:I/5z698sn9Ka8TeJc9MKroUUfqBBauWjQqLJ2OPfmY0=
golang.org/x/xerrors v0.0.0-20191204190536-9bdfabe68543 h1:E7g+9GITq07hpfrRu66IVDexMakfv52eLZ2CXBWiKr4=
golang.org/x/xerrors v0.0.0-20191204190536-9bdfabe68543/go.mod h1:I/5z698sn9Ka8TeJc9MKroUUfqBBauWjQqLJ2OPfmY0=
-gomodules.xyz/jsonpatch/v2 v2.0.1 h1:xyiBuvkD2g5n7cYzx6u2sxQvsAy4QJsZFCzGVdzOXZ0=
+golang.org/x/xerrors v0.0.0-20200804184101-5ec99f83aff1 h1:go1bK/D/BFZV2I8cIQd1NKEZ+0owSTG1fDTci4IqFcE=
+golang.org/x/xerrors v0.0.0-20200804184101-5ec99f83aff1/go.mod h1:I/5z698sn9Ka8TeJc9MKroUUfqBBauWjQqLJ2OPfmY0=
gomodules.xyz/jsonpatch/v2 v2.0.1/go.mod h1:IhYNNY4jnS53ZnfE4PAmpKtDpTCj1JFXc+3mwe7XcUU=
+gomodules.xyz/jsonpatch/v2 v2.1.0 h1:Phva6wqu+xR//Njw6iorylFFgn/z547tw5Ne3HZPQ+k=
+gomodules.xyz/jsonpatch/v2 v2.1.0/go.mod h1:IhYNNY4jnS53ZnfE4PAmpKtDpTCj1JFXc+3mwe7XcUU=
gomodules.xyz/jsonpatch/v3 v3.0.1/go.mod h1:CBhndykehEwTOlEfnsfJwvkFQbSN8YZFr9M+cIHAJto=
gomodules.xyz/orderedmap v0.1.0/go.mod h1:g9/TPUCm1t2gwD3j3zfV8uylyYhVdCNSi+xCEIu7yTU=
-gonum.org/v1/gonum v0.0.0-20190331200053-3d26580ed485/go.mod h1:2ltnJ7xHfj0zHS40VVPYEAAMTa3ZGguvHGBSJeRWqE0=
-gonum.org/v1/gonum v0.0.0-20190710053202-4340aa3071a0/go.mod h1:03dgh78c4UvU1WksguQ/lvJQXbezKQGJSrwwRq5MraQ=
-gonum.org/v1/netlib v0.0.0-20190313105609-8cb42192e0e0/go.mod h1:wa6Ws7BG/ESfp6dHfk7C6KdzKA7wR7u/rKwOGE66zvw=
google.golang.org/api v0.0.0-20160322025152-9bf6e6e569ff/go.mod h1:4mhQ8q/RsB7i+udVvVy5NUi08OU8ZlA0gRVgrF7VFY0=
google.golang.org/api v0.3.1/go.mod h1:6wY9I6uQWHQ8EM57III9mq/AjF+i8G65rmVagqKMtkk=
google.golang.org/api v0.3.2/go.mod h1:6wY9I6uQWHQ8EM57III9mq/AjF+i8G65rmVagqKMtkk=
google.golang.org/api v0.4.0/go.mod h1:8k5glujaEP+g9n7WNsDg8QP6cUVNI86fCNMcbazEtwE=
-google.golang.org/api v0.6.1-0.20190607001116-5213b8090861/go.mod h1:btoxGiFvQNVUZQ8W08zLtrVS08CNpINPEfxXxgJL1Q4=
+google.golang.org/api v0.7.0/go.mod h1:WtwebWUNSVBH/HAw79HIFXZNqEvBhG+Ra+ax0hx3E3M=
+google.golang.org/api v0.8.0/go.mod h1:o4eAsZoiT+ibD93RtjEohWalFOjRDx6CVaqeizhEnKg=
+google.golang.org/api v0.9.0/go.mod h1:o4eAsZoiT+ibD93RtjEohWalFOjRDx6CVaqeizhEnKg=
+google.golang.org/api v0.13.0/go.mod h1:iLdEw5Ide6rF15KTC1Kkl0iskquN2gFfn9o9XIsbkAI=
+google.golang.org/api v0.14.0/go.mod h1:iLdEw5Ide6rF15KTC1Kkl0iskquN2gFfn9o9XIsbkAI=
+google.golang.org/api v0.15.0/go.mod h1:iLdEw5Ide6rF15KTC1Kkl0iskquN2gFfn9o9XIsbkAI=
+google.golang.org/api v0.17.0/go.mod h1:BwFmGc8tA3vsd7r/7kR8DY7iEEGSU04BFxCo5jP/sfE=
+google.golang.org/api v0.18.0/go.mod h1:BwFmGc8tA3vsd7r/7kR8DY7iEEGSU04BFxCo5jP/sfE=
+google.golang.org/api v0.20.0/go.mod h1:BwFmGc8tA3vsd7r/7kR8DY7iEEGSU04BFxCo5jP/sfE=
google.golang.org/appengine v1.1.0/go.mod h1:EbEs0AVv82hx2wNQdGPgUI5lhzA/G0D9YwlJXL52JkM=
+google.golang.org/appengine v1.2.0/go.mod h1:xpcJRLb0r/rnEns0DIKYYv+WjYCduHsrkT7/EB5XEv4=
google.golang.org/appengine v1.3.0/go.mod h1:xpcJRLb0r/rnEns0DIKYYv+WjYCduHsrkT7/EB5XEv4=
google.golang.org/appengine v1.4.0/go.mod h1:xpcJRLb0r/rnEns0DIKYYv+WjYCduHsrkT7/EB5XEv4=
-google.golang.org/appengine v1.5.0 h1:KxkO13IPW4Lslp2bz+KHP2E3gtFlrIGNThxkZQ3g+4c=
google.golang.org/appengine v1.5.0/go.mod h1:xpcJRLb0r/rnEns0DIKYYv+WjYCduHsrkT7/EB5XEv4=
-google.golang.org/appengine v1.6.5 h1:tycE03LOZYQNhDpS27tcQdAzLCVMaj7QT2SXxebnpCM=
+google.golang.org/appengine v1.6.1/go.mod h1:i06prIuMbXzDqacNJfV5OdTW448YApPu5ww/cMBSeb0=
google.golang.org/appengine v1.6.5/go.mod h1:8WjMMxjGQR8xUklV/ARdw2HLXBOI7O7uCIDZVag1xfc=
+google.golang.org/appengine v1.6.6 h1:lMO5rYAqUxkmaj76jAkRUvt5JZgFymx/+Q5Mzfivuhc=
+google.golang.org/appengine v1.6.6/go.mod h1:8WjMMxjGQR8xUklV/ARdw2HLXBOI7O7uCIDZVag1xfc=
google.golang.org/cloud v0.0.0-20151119220103-975617b05ea8/go.mod h1:0H1ncTHf11KCFhTc/+EFRbzSCOZx+VUbRMk55Yv5MYk=
google.golang.org/genproto v0.0.0-20180817151627-c66870c02cf8/go.mod h1:JiN7NxoALGmiZfu7CAH4rXhgtRTLTxftemlI0sWmxmc=
-google.golang.org/genproto v0.0.0-20190128161407-8ac453e89fca/go.mod h1:L3J43x8/uS+qIUoksaLKe6OS3nUKxOKuIFz1sl2/jx4=
google.golang.org/genproto v0.0.0-20190307195333-5fe7a883aa19/go.mod h1:VzzqZJRnGkLBvHegQrXjBqPurQTc5/KpmUdxsrq26oE=
google.golang.org/genproto v0.0.0-20190404172233-64821d5d2107/go.mod h1:VzzqZJRnGkLBvHegQrXjBqPurQTc5/KpmUdxsrq26oE=
google.golang.org/genproto v0.0.0-20190418145605-e7d98fc518a7/go.mod h1:VzzqZJRnGkLBvHegQrXjBqPurQTc5/KpmUdxsrq26oE=
google.golang.org/genproto v0.0.0-20190425155659-357c62f0e4bb/go.mod h1:VzzqZJRnGkLBvHegQrXjBqPurQTc5/KpmUdxsrq26oE=
google.golang.org/genproto v0.0.0-20190502173448-54afdca5d873/go.mod h1:VzzqZJRnGkLBvHegQrXjBqPurQTc5/KpmUdxsrq26oE=
+google.golang.org/genproto v0.0.0-20190530194941-fb225487d101/go.mod h1:z3L6/3dTEVtUr6QSP8miRzeRqwQOioJ9I66odjN4I7s=
+google.golang.org/genproto v0.0.0-20190801165951-fa694d86fc64/go.mod h1:DMBHOl98Agz4BDEuKkezgsaosCRResVns1a3J2ZsMNc=
google.golang.org/genproto v0.0.0-20190819201941-24fa4b261c55/go.mod h1:DMBHOl98Agz4BDEuKkezgsaosCRResVns1a3J2ZsMNc=
-google.golang.org/genproto v0.0.0-20191028173616-919d9bdd9fe6/go.mod h1:n3cpQtvxv34hfy77yVDNjmbRyujviMdxYliBSkLhpCc=
+google.golang.org/genproto v0.0.0-20190911173649-1774047e7e51/go.mod h1:IbNlFCBrqXvoKpeg0TB2l7cyZUmoaFKYIwrEpbDKLA8=
+google.golang.org/genproto v0.0.0-20191009194640-548a555dbc03/go.mod h1:n3cpQtvxv34hfy77yVDNjmbRyujviMdxYliBSkLhpCc=
+google.golang.org/genproto v0.0.0-20191108220845-16a3f7862a1a/go.mod h1:n3cpQtvxv34hfy77yVDNjmbRyujviMdxYliBSkLhpCc=
+google.golang.org/genproto v0.0.0-20191115194625-c23dd37a84c9/go.mod h1:n3cpQtvxv34hfy77yVDNjmbRyujviMdxYliBSkLhpCc=
+google.golang.org/genproto v0.0.0-20191216164720-4f79533eabd1/go.mod h1:n3cpQtvxv34hfy77yVDNjmbRyujviMdxYliBSkLhpCc=
+google.golang.org/genproto v0.0.0-20191230161307-f3c370f40bfb/go.mod h1:n3cpQtvxv34hfy77yVDNjmbRyujviMdxYliBSkLhpCc=
+google.golang.org/genproto v0.0.0-20200115191322-ca5a22157cba/go.mod h1:n3cpQtvxv34hfy77yVDNjmbRyujviMdxYliBSkLhpCc=
+google.golang.org/genproto v0.0.0-20200117163144-32f20d992d24/go.mod h1:n3cpQtvxv34hfy77yVDNjmbRyujviMdxYliBSkLhpCc=
+google.golang.org/genproto v0.0.0-20200122232147-0452cf42e150/go.mod h1:n3cpQtvxv34hfy77yVDNjmbRyujviMdxYliBSkLhpCc=
+google.golang.org/genproto v0.0.0-20200204135345-fa8e72b47b90/go.mod h1:GmwEX6Z4W5gMy59cAlVYjN9JhxgbQH6Gn+gFDQe2lzA=
+google.golang.org/genproto v0.0.0-20200212174721-66ed5ce911ce/go.mod h1:55QSHmfGQM9UVYDPBsyGGes0y52j32PQ3BqQfXhyH3c=
+google.golang.org/genproto v0.0.0-20200224152610-e50cd9704f63/go.mod h1:55QSHmfGQM9UVYDPBsyGGes0y52j32PQ3BqQfXhyH3c=
+google.golang.org/genproto v0.0.0-20200305110556-506484158171/go.mod h1:55QSHmfGQM9UVYDPBsyGGes0y52j32PQ3BqQfXhyH3c=
+google.golang.org/genproto v0.0.0-20200526211855-cb27e3aa2013/go.mod h1:NbSheEEYHJ7i3ixzK3sjbqSGDJWnxyFXZblF3eUsNvo=
+google.golang.org/genproto v0.0.0-20200701001935-0939c5918c31/go.mod h1:FWY/as6DDZQgahTzZj3fqbO1CbirC29ZNUFHwi0/+no=
google.golang.org/grpc v0.0.0-20160317175043-d3ddb4469d5a/go.mod h1:yo6s7OP7yaDglbqo1J04qKzAhqBH6lvTonzMVmEdcZw=
-google.golang.org/grpc v1.16.0/go.mod h1:0JHn/cJsOMiMfNA9+DeHDlAU7KAAB5GDlYFpa9MZMio=
google.golang.org/grpc v1.17.0/go.mod h1:6QZJwpn2B+Zp71q/5VxRsJ6NXXVCE5NRUHRo+f3cWCs=
google.golang.org/grpc v1.19.0/go.mod h1:mqu4LbDTu4XGKhr4mRzUsmM4RtVoemTSY81AxZiDr8c=
+google.golang.org/grpc v1.20.0/go.mod h1:chYK+tFQF0nDUGJgXMSgLCQk3phJEuONr2DCgLDdAQM=
google.golang.org/grpc v1.20.1/go.mod h1:10oTOabMzJvdu6/UiuZezV6QK5dSlG84ov/aaiqXj38=
google.golang.org/grpc v1.21.0/go.mod h1:oYelfM1adQP15Ek0mdvEgi9Df8B9CZIaU1084ijfRaM=
+google.golang.org/grpc v1.21.1/go.mod h1:oYelfM1adQP15Ek0mdvEgi9Df8B9CZIaU1084ijfRaM=
+google.golang.org/grpc v1.22.1/go.mod h1:Y5yQAOtifL1yxbo5wqy6BxZv8vAUGQwXBOALyacEbxg=
google.golang.org/grpc v1.23.0/go.mod h1:Y5yQAOtifL1yxbo5wqy6BxZv8vAUGQwXBOALyacEbxg=
+google.golang.org/grpc v1.23.1/go.mod h1:Y5yQAOtifL1yxbo5wqy6BxZv8vAUGQwXBOALyacEbxg=
google.golang.org/grpc v1.24.0/go.mod h1:XDChyiUovWa60DnaeDeZmSW86xtLtjtZbwvSiRnRtcA=
+google.golang.org/grpc v1.25.1/go.mod h1:c3i+UQWmh7LiEpx4sFZnkU36qjEYZ0imhYfXVyQciAY=
+google.golang.org/grpc v1.26.0/go.mod h1:qbnxyOmOxrQa7FizSgH+ReBfzJrCY1pSN7KXBS8abTk=
google.golang.org/grpc v1.27.0/go.mod h1:qbnxyOmOxrQa7FizSgH+ReBfzJrCY1pSN7KXBS8abTk=
+google.golang.org/grpc v1.27.1/go.mod h1:qbnxyOmOxrQa7FizSgH+ReBfzJrCY1pSN7KXBS8abTk=
+google.golang.org/grpc v1.30.0/go.mod h1:N36X2cJ7JwdamYAgDz+s+rVMFjt3numwzf/HckM8pak=
+google.golang.org/grpc/cmd/protoc-gen-go-grpc v0.0.0-20200709232328-d8193ee9cc3e/go.mod h1:6Kw0yEErY5E/yWrBtf03jp27GLLJujG4z/JK95pnjjw=
+google.golang.org/protobuf v0.0.0-20200109180630-ec00e32a8dfd/go.mod h1:DFci5gLYBciE7Vtevhsrf46CRTquxDuWsQurQQe4oz8=
+google.golang.org/protobuf v0.0.0-20200221191635-4d8936d0db64/go.mod h1:kwYJMbMJ01Woi6D6+Kah6886xMZcty6N08ah7+eCXa0=
+google.golang.org/protobuf v0.0.0-20200228230310-ab0ca4ff8a60/go.mod h1:cfTl7dwQJ+fmap5saPgwCLgHXTUD7jkjRqWcaiX5VyM=
+google.golang.org/protobuf v1.20.1-0.20200309200217-e05f789c0967/go.mod h1:A+miEFZTKqfCUM6K7xSMQL9OKL/b6hQv+e19PK+JZNE=
+google.golang.org/protobuf v1.21.0/go.mod h1:47Nbq4nVaFHyn7ilMalzfO3qCViNmqZ2kzikPIcrTAo=
+google.golang.org/protobuf v1.22.0/go.mod h1:EGpADcykh3NcUnDUJcl1+ZksZNG86OlYog2l/sGQquU=
+google.golang.org/protobuf v1.23.0/go.mod h1:EGpADcykh3NcUnDUJcl1+ZksZNG86OlYog2l/sGQquU=
+google.golang.org/protobuf v1.23.1-0.20200526195155-81db48ad09cc/go.mod h1:EGpADcykh3NcUnDUJcl1+ZksZNG86OlYog2l/sGQquU=
+google.golang.org/protobuf v1.24.0 h1:UhZDfRO8JRQru4/+LlLE0BRKGF8L+PICnvYZmx/fEGA=
+google.golang.org/protobuf v1.24.0/go.mod h1:r/3tXBNzIEhYS9I1OUVjXDlt8tc493IdKGjtUeSXeh4=
+google.golang.org/protobuf v1.25.0 h1:Ejskq+SyPohKW+1uil0JJMtmHCgJPJ/qWTxr8qp+R4c=
+google.golang.org/protobuf v1.25.0/go.mod h1:9JNX74DMeImyA3h4bdi1ymwjUzf21/xIlbajtzgsN7c=
gopkg.in/airbrake/gobrake.v2 v2.0.9/go.mod h1:/h5ZAUhDkGaJfjzjKLSjv6zCL6O0LLBxU4K+aSYdM/U=
+gopkg.in/alecthomas/kingpin.v2 v2.2.6 h1:jMFz6MfLP0/4fUyZle81rXUoxOBFi19VUFKVDOQfozc=
gopkg.in/alecthomas/kingpin.v2 v2.2.6/go.mod h1:FMv+mEhP44yOT+4EoQTLFTRgOQ1FBLkstjWtayDeSgw=
gopkg.in/alexcesaro/quotedprintable.v3 v3.0.0-20150716171945-2caba252f4dc h1:2gGKlE2+asNV9m7xrywl36YYNnBG5ZQ0r/BOOxqPpmk=
gopkg.in/alexcesaro/quotedprintable.v3 v3.0.0-20150716171945-2caba252f4dc/go.mod h1:m7x9LTH6d71AHyAX77c9yqWCCa3UKHcVEj9y7hAtKDk=
gopkg.in/check.v1 v0.0.0-20161208181325-20d25e280405/go.mod h1:Co6ibVJAznAaIkqp8huTwlJQCZ016jof/cbN4VW5Yz0=
gopkg.in/check.v1 v1.0.0-20141024133853-64131543e789/go.mod h1:Co6ibVJAznAaIkqp8huTwlJQCZ016jof/cbN4VW5Yz0=
gopkg.in/check.v1 v1.0.0-20180628173108-788fd7840127/go.mod h1:Co6ibVJAznAaIkqp8huTwlJQCZ016jof/cbN4VW5Yz0=
+gopkg.in/check.v1 v1.0.0-20190902080502-41f04d3bba15 h1:YR8cESwS4TdDjEe65xsg0ogRM/Nc3DYOhEAlW+xobZo=
gopkg.in/check.v1 v1.0.0-20190902080502-41f04d3bba15/go.mod h1:Co6ibVJAznAaIkqp8huTwlJQCZ016jof/cbN4VW5Yz0=
-gopkg.in/check.v1 v1.0.0-20200227125254-8fa46927fb4f/go.mod h1:Co6ibVJAznAaIkqp8huTwlJQCZ016jof/cbN4VW5Yz0=
+gopkg.in/cheggaaa/pb.v1 v1.0.25/go.mod h1:V/YB90LKu/1FcN3WVnfiiE5oMCibMjukxqG/qStrOgw=
gopkg.in/errgo.v2 v2.1.0/go.mod h1:hNsd1EY+bozCKY1Ytp96fpM3vjJbqLJn88ws8XvfDNI=
-gopkg.in/fsnotify.v1 v1.4.7 h1:xOHLXZwVvI9hhs+cLKq5+I5onOuwQLhQwiu63xxlHs4=
gopkg.in/fsnotify.v1 v1.4.7/go.mod h1:Tz8NjZHkW78fSQdbUxIjBTcgA1z1m8ZHf0WmKUhAMys=
-gopkg.in/gcfg.v1 v1.2.0/go.mod h1:yesOnuUOFQAhST5vPY4nbZsb/huCgGGXlipJsBn0b3o=
+gopkg.in/gcfg.v1 v1.2.3/go.mod h1:yesOnuUOFQAhST5vPY4nbZsb/huCgGGXlipJsBn0b3o=
gopkg.in/gemnasium/logrus-airbrake-hook.v2 v2.1.2/go.mod h1:Xk6kEKp8OKb+X14hQBKWaSkCsqBpgog8nAV2xsGOxlo=
gopkg.in/gomail.v2 v2.0.0-20160411212932-81ebce5c23df h1:n7WqCuqOuCbNr617RXOY0AWRXxgwEyPp2z+p0+hgMuE=
gopkg.in/gomail.v2 v2.0.0-20160411212932-81ebce5c23df/go.mod h1:LRQQ+SO6ZHR7tOkpBDuZnXENFzX8qRjMDMyPD6BRkCw=
gopkg.in/gorp.v1 v1.7.2/go.mod h1:Wo3h+DBQZIxATwftsglhdD/62zRFPhGhTiu5jUJmCaw=
gopkg.in/imdario/mergo.v0 v0.3.7/go.mod h1:9qPP6AGrlC1G2PTNXko614FwGZvorN7MiBU0Eppok+U=
-gopkg.in/inf.v0 v0.9.0/go.mod h1:cWUDdTG/fYaXco+Dcufb5Vnc6Gp2YChqWtbxRZE0mXw=
gopkg.in/inf.v0 v0.9.1 h1:73M5CoZyi3ZLMOyDlQh031Cx6N9NDJ2Vvfl76EDAgDc=
gopkg.in/inf.v0 v0.9.1/go.mod h1:cWUDdTG/fYaXco+Dcufb5Vnc6Gp2YChqWtbxRZE0mXw=
-gopkg.in/ini.v1 v1.51.0 h1:AQvPpx3LzTDM0AjnIRlVFwFFGC+npRopjZxLJj6gdno=
gopkg.in/ini.v1 v1.51.0/go.mod h1:pNLf8WUiyNEtQjuu5G5vTm06TEv9tsIgeAvK8hOrP4k=
-gopkg.in/mcuadros/go-syslog.v2 v2.2.1/go.mod h1:l5LPIyOOyIdQquNg+oU6Z3524YwrcqEm0aKH+5zpt2U=
gopkg.in/natefinch/lumberjack.v2 v2.0.0/go.mod h1:l0ndWWf7gzL7RNwBG7wST/UCcT4T24xpD6X8LsfU/+k=
gopkg.in/op/go-logging.v1 v1.0.0-20160211212156-b2cb9fa56473/go.mod h1:N1eN2tsCx0Ydtgjl4cqmbRCsY4/+z4cYDeqwZTk6zog=
gopkg.in/resty.v1 v1.12.0/go.mod h1:mDo4pnntr5jdWRML875a/NmxYqAlA73dVijT2AXvQQo=
-gopkg.in/square/go-jose.v1 v1.1.2/go.mod h1:QpYS+a4WhS+DTlyQIi6Ka7MS3SuR9a055rgXNEe6EiA=
gopkg.in/square/go-jose.v2 v2.2.2/go.mod h1:M9dMgbHiYLoDGQrXy7OpJDJWiKiU//h+vD76mk0e1AI=
+gopkg.in/tomb.v1 v1.0.0-20141024135613-dd632973f1e7 h1:uRGJdciOHaEIrze2W8Q3AKkepLTh2hOroT7a+7czfdQ=
gopkg.in/tomb.v1 v1.0.0-20141024135613-dd632973f1e7/go.mod h1:dt/ZhP58zS4L8KSrWDmTeBkI65Dw0HsyUHuEVlX15mw=
-gopkg.in/warnings.v0 v0.1.1/go.mod h1:jksf8JmL6Qr/oQM2OXTHunEvvTAsrWBLb6OOjuVWRNI=
+gopkg.in/warnings.v0 v0.1.2/go.mod h1:jksf8JmL6Qr/oQM2OXTHunEvvTAsrWBLb6OOjuVWRNI=
gopkg.in/yaml.v2 v2.0.0-20170812160011-eb3733d160e7/go.mod h1:JAlM8MvJe8wmxCU4Bli9HhUf9+ttbYbLASfIpnQbh74=
-gopkg.in/yaml.v2 v2.0.0/go.mod h1:JAlM8MvJe8wmxCU4Bli9HhUf9+ttbYbLASfIpnQbh74=
-gopkg.in/yaml.v2 v2.1.1/go.mod h1:hI93XBmqTisBFMUTm0b8Fm+jr3Dg1NNxqwp+5A1VGuI=
gopkg.in/yaml.v2 v2.2.1/go.mod h1:hI93XBmqTisBFMUTm0b8Fm+jr3Dg1NNxqwp+5A1VGuI=
gopkg.in/yaml.v2 v2.2.2/go.mod h1:hI93XBmqTisBFMUTm0b8Fm+jr3Dg1NNxqwp+5A1VGuI=
-gopkg.in/yaml.v2 v2.2.4 h1:/eiJrUcujPVeJ3xlSWaiNi3uSVmDGBK1pDHUHAnao1I=
gopkg.in/yaml.v2 v2.2.4/go.mod h1:hI93XBmqTisBFMUTm0b8Fm+jr3Dg1NNxqwp+5A1VGuI=
gopkg.in/yaml.v2 v2.2.5/go.mod h1:hI93XBmqTisBFMUTm0b8Fm+jr3Dg1NNxqwp+5A1VGuI=
-gopkg.in/yaml.v2 v2.2.8 h1:obN1ZagJSUGI0Ek/LBmuj4SNLPfIny3KsKFopxRdj10=
gopkg.in/yaml.v2 v2.2.8/go.mod h1:hI93XBmqTisBFMUTm0b8Fm+jr3Dg1NNxqwp+5A1VGuI=
-gotest.tools v2.1.0+incompatible/go.mod h1:DsYFclhRJ6vuDpmuTbkuFWG+y2sxOXAzmJt81HFBacw=
+gopkg.in/yaml.v2 v2.3.0 h1:clyUAQHOM3G0M3f5vQj7LuJrETvjVot3Z5el9nffUtU=
+gopkg.in/yaml.v2 v2.3.0/go.mod h1:hI93XBmqTisBFMUTm0b8Fm+jr3Dg1NNxqwp+5A1VGuI=
+gopkg.in/yaml.v3 v3.0.0-20190905181640-827449938966/go.mod h1:K4uyk7z7BCEPqu6E+C64Yfv1cQ7kz7rIZviUmN+EgEM=
+gopkg.in/yaml.v3 v3.0.0-20200313102051-9f266ea9e77c/go.mod h1:K4uyk7z7BCEPqu6E+C64Yfv1cQ7kz7rIZviUmN+EgEM=
+gopkg.in/yaml.v3 v3.0.0-20200615113413-eeeca48fe776 h1:tQIYjPdBoyREyB9XMu+nnTclpTYkz2zFM+lzLJFO4gQ=
+gopkg.in/yaml.v3 v3.0.0-20200615113413-eeeca48fe776/go.mod h1:K4uyk7z7BCEPqu6E+C64Yfv1cQ7kz7rIZviUmN+EgEM=
gotest.tools v2.2.0+incompatible/go.mod h1:DsYFclhRJ6vuDpmuTbkuFWG+y2sxOXAzmJt81HFBacw=
-gotest.tools/gotestsum v0.3.5/go.mod h1:Mnf3e5FUzXbkCfynWBGOwLssY7gTQgCHObK9tMpAriY=
-helm.sh/helm/v3 v3.0.0/go.mod h1:sI7B9yfvMgxtTPMWdk1jSKJ2aa59UyP9qhPydqW6mgo=
-helm.sh/helm/v3 v3.0.1/go.mod h1:sI7B9yfvMgxtTPMWdk1jSKJ2aa59UyP9qhPydqW6mgo=
-helm.sh/helm/v3 v3.1.0/go.mod h1:WYsFJuMASa/4XUqLyv54s0U/f3mlAaRErGmyy4z921g=
-helm.sh/helm/v3 v3.1.2/go.mod h1:WYsFJuMASa/4XUqLyv54s0U/f3mlAaRErGmyy4z921g=
+gotest.tools/v3 v3.0.2/go.mod h1:3SzNCllyD9/Y+b5r9JIKQ474KzkZyqLqEfYqMsX94Bk=
+helm.sh/helm/v3 v3.4.1/go.mod h1:MeRlXlmCr5CWYKvqIPgXrSmcIXJpv7qcsKV3uTvcZSM=
honnef.co/go/tools v0.0.0-20180728063816-88497007e858/go.mod h1:rf3lG4BRIbNafJWhAfAdb/ePZxsR/4RtNHQocxwk9r4=
honnef.co/go/tools v0.0.0-20190102054323-c2f93a96b099/go.mod h1:rf3lG4BRIbNafJWhAfAdb/ePZxsR/4RtNHQocxwk9r4=
honnef.co/go/tools v0.0.0-20190106161140-3f1c8253044a/go.mod h1:rf3lG4BRIbNafJWhAfAdb/ePZxsR/4RtNHQocxwk9r4=
honnef.co/go/tools v0.0.0-20190418001031-e561f6794a2a/go.mod h1:rf3lG4BRIbNafJWhAfAdb/ePZxsR/4RtNHQocxwk9r4=
honnef.co/go/tools v0.0.0-20190523083050-ea95bdfd59fc/go.mod h1:rf3lG4BRIbNafJWhAfAdb/ePZxsR/4RtNHQocxwk9r4=
-honnef.co/go/tools v0.0.1-2019.2.2/go.mod h1:a3bituU0lyd329TUQxRnasdCoJDkEUEAqEt0JzvZhAg=
honnef.co/go/tools v0.0.1-2019.2.3 h1:3JgtbtFHMiCmsznwGVTUWbgGov+pVqnlf1dEJTNAXeM=
honnef.co/go/tools v0.0.1-2019.2.3/go.mod h1:a3bituU0lyd329TUQxRnasdCoJDkEUEAqEt0JzvZhAg=
-k8s.io/api v0.0.0-20191016110408-35e52d86657a h1:VVUE9xTCXP6KUPMf92cQmN88orz600ebexcRRaBTepQ=
-k8s.io/api v0.0.0-20191016110408-35e52d86657a/go.mod h1:/L5qH+AD540e7Cetbui1tuJeXdmNhO8jM6VkXeDdDhQ=
-k8s.io/apiextensions-apiserver v0.0.0-20191016113550-5357c4baaf65 h1:kThoiqgMsSwBdMK/lPgjtYTsEjbUU9nXCA9DyU3feok=
-k8s.io/apiextensions-apiserver v0.0.0-20191016113550-5357c4baaf65/go.mod h1:5BINdGqggRXXKnDgpwoJ7PyQH8f+Ypp02fvVNcIFy9s=
-k8s.io/apimachinery v0.0.0-20191004115801-a2eda9f80ab8 h1:Iieh/ZEgT3BWwbLD5qEKcY06jKuPEl6zC7gPSehoLw4=
-k8s.io/apimachinery v0.0.0-20191004115801-a2eda9f80ab8/go.mod h1:llRdnznGEAqC3DcNm6yEj472xaFVfLM7hnYofMb12tQ=
-k8s.io/apiserver v0.0.0-20191016112112-5190913f932d/go.mod h1:7OqfAolfWxUM/jJ/HBLyE+cdaWFBUoo5Q5pHgJVj2ws=
-k8s.io/autoscaler v0.0.0-20190607113959-1b4f1855cb8e/go.mod h1:QEXezc9uKPT91dwqhSJq3GNI3B1HxFRQHiku9kmrsSA=
-k8s.io/cli-runtime v0.0.0-20191016114015-74ad18325ed5 h1:8ZfMjkMBzcXEawLsYHg9lDM7aLEVso3NiVKfUTnN56A=
-k8s.io/cli-runtime v0.0.0-20191016114015-74ad18325ed5/go.mod h1:sDl6WKSQkDM6zS1u9F49a0VooQ3ycYFBFLqd2jf2Xfo=
-k8s.io/client-go v0.0.0-20191016111102-bec269661e48 h1:C2XVy2z0dV94q9hSSoCuTPp1KOG7IegvbdXuz9VGxoU=
-k8s.io/client-go v0.0.0-20191016111102-bec269661e48/go.mod h1:hrwktSwYGI4JK+TJA3dMaFyyvHVi/aLarVHpbs8bgCU=
-k8s.io/cloud-provider v0.0.0-20191016115326-20453efc2458/go.mod h1:O5SO5xcgxrjJV9EC9R/47RuBpbk5YX9URDBlg++FA5o=
-k8s.io/cluster-bootstrap v0.0.0-20191016115129-c07a134afb42/go.mod h1:MzCL6kLExQuHruGaqibd8cugC8nw8QRxm3+lzR5l8SI=
-k8s.io/code-generator v0.0.0-20181117043124-c2090bec4d9b h1:KH0fUlgdFZH8UMxJ/FDCYHpczfSQKefetq5NjL6BVF0=
-k8s.io/code-generator v0.0.0-20181117043124-c2090bec4d9b/go.mod h1:MYiN+ZJZ9HkETbgVZdWw2AsuAi9PZ4V80cwfuf2axe8=
-k8s.io/component-base v0.0.0-20191016111319-039242c015a9/go.mod h1:SuWowIgd/dtU/m/iv8OD9eOxp3QZBBhTIiWMsBQvKjI=
-k8s.io/cri-api v0.0.0-20190828162817-608eb1dad4ac/go.mod h1:BvtUaNBr0fEpzb11OfrQiJLsLPtqbmulpo1fPwcpP6Q=
-k8s.io/csi-translation-lib v0.0.0-20191016115521-756ffa5af0bd/go.mod h1:lf1VBseeLanBpSXD0N9tuPx1ylI8sA0j6f+rckCKiIk=
+honnef.co/go/tools v0.0.1-2020.1.3/go.mod h1:X/FiERA/W4tHapMX5mGpAtMSVEeEUOyHaw9vFzvIQ3k=
+k8s.io/api v0.18.0/go.mod h1:q2HRQkfDzHMBZL9l/y9rH63PkQl4vae0xRT+8prbrK8=
+k8s.io/api v0.18.2/go.mod h1:SJCWI7OLzhZSvbY7U8zwNl9UA4o1fizoug34OV/2r78=
+k8s.io/api v0.19.2 h1:q+/krnHWKsL7OBZg/rxnycsl9569Pud76UJ77MvKXms=
+k8s.io/api v0.19.2/go.mod h1:IQpK0zFQ1xc5iNIQPqzgoOwuFugaYHK4iCknlAQP9nI=
+k8s.io/api v0.19.3/go.mod h1:VF+5FT1B74Pw3KxMdKyinLo+zynBaMBiAfGMuldcNDs=
+k8s.io/api v0.19.4 h1:I+1I4cgJYuCDgiLNjKx7SLmIbwgj9w7N7Zr5vSIdwpo=
+k8s.io/api v0.19.4/go.mod h1:SbtJ2aHCItirzdJ36YslycFNzWADYH3tgOhvBEFtZAk=
+k8s.io/api v0.20.2 h1:y/HR22XDZY3pniu9hIFDLpUCPq2w5eQ6aV/VFQ7uJMw=
+k8s.io/api v0.20.2/go.mod h1:d7n6Ehyzx+S+cE3VhTGfVNNqtGc/oL9DCdYYahlurV8=
+k8s.io/apiextensions-apiserver v0.18.2/go.mod h1:q3faSnRGmYimiocj6cHQ1I3WpLqmDgJFlKL37fC4ZvY=
+k8s.io/apiextensions-apiserver v0.19.2 h1:oG84UwiDsVDu7dlsGQs5GySmQHCzMhknfhFExJMz9tA=
+k8s.io/apiextensions-apiserver v0.19.2/go.mod h1:EYNjpqIAvNZe+svXVx9j4uBaVhTB4C94HkY3w058qcg=
+k8s.io/apiextensions-apiserver v0.19.3 h1:WZxBypSHW4SdXHbdPTS/Jy7L2la6Niggs8BuU5o+avo=
+k8s.io/apiextensions-apiserver v0.19.3/go.mod h1:igVEkrE9TzInc1tYE7qSqxaLg/rEAp6B5+k9Q7+IC8Q=
+k8s.io/apiextensions-apiserver v0.19.4 h1:D9ak9T012tb3vcGFWYmbQuj9SCC8YM4zhA4XZqsAQC4=
+k8s.io/apiextensions-apiserver v0.19.4/go.mod h1:B9rpH/nu4JBCtuUp3zTTk8DEjZUupZTBEec7/2zNRYw=
+k8s.io/apimachinery v0.18.0/go.mod h1:9SnR/e11v5IbyPCGbvJViimtJ0SwHG4nfZFjU77ftcA=
+k8s.io/apimachinery v0.18.2/go.mod h1:9SnR/e11v5IbyPCGbvJViimtJ0SwHG4nfZFjU77ftcA=
+k8s.io/apimachinery v0.19.2 h1:5Gy9vQpAGTKHPVOh5c4plE274X8D/6cuEiTO2zve7tc=
+k8s.io/apimachinery v0.19.2/go.mod h1:DnPGDnARWFvYa3pMHgSxtbZb7gpzzAZ1pTfaUNDVlmA=
+k8s.io/apimachinery v0.19.3/go.mod h1:DnPGDnARWFvYa3pMHgSxtbZb7gpzzAZ1pTfaUNDVlmA=
+k8s.io/apimachinery v0.19.4 h1:+ZoddM7nbzrDCp0T3SWnyxqf8cbWPT2fkZImoyvHUG0=
+k8s.io/apimachinery v0.19.4/go.mod h1:DnPGDnARWFvYa3pMHgSxtbZb7gpzzAZ1pTfaUNDVlmA=
+k8s.io/apimachinery v0.20.2 h1:hFx6Sbt1oG0n6DZ+g4bFt5f6BoMkOjKWsQFu077M3Vg=
+k8s.io/apimachinery v0.20.2/go.mod h1:WlLqWAHZGg07AeltaI0MV5uk1Omp8xaN0JGLY6gkRpU=
+k8s.io/apiserver v0.18.2/go.mod h1:Xbh066NqrZO8cbsoenCwyDJ1OSi8Ag8I2lezeHxzwzw=
+k8s.io/apiserver v0.19.2/go.mod h1:FreAq0bJ2vtZFj9Ago/X0oNGC51GfubKK/ViOKfVAOA=
+k8s.io/apiserver v0.19.3/go.mod h1:bx6dMm+H6ifgKFpCQT/SAhPwhzoeIMlHIaibomUDec0=
+k8s.io/apiserver v0.19.4/go.mod h1:X8WRHCR1UGZDd7HpV0QDc1h/6VbbpAeAGyxSh8yzZXw=
+k8s.io/cli-runtime v0.18.0/go.mod h1:1eXfmBsIJosjn9LjEBUd2WVPoPAY9XGTqTFcPMIBsUQ=
+k8s.io/cli-runtime v0.19.3/go.mod h1:q+l845i5/uWzcUpCrl+L4f3XLaJi8ZeLVQ/decwty0A=
+k8s.io/cli-runtime v0.19.4/go.mod h1:m8G32dVbKOeaX1foGhleLEvNd6REvU7YnZyWn5//9rw=
+k8s.io/cli-runtime v0.20.2 h1:W0/FHdbApnl9oB7xdG643c/Zaf7TZT+43I+zKxwqvhU=
+k8s.io/cli-runtime v0.20.2/go.mod h1:FjH6uIZZZP3XmwrXWeeYCbgxcrD6YXxoAykBaWH0VdM=
+k8s.io/client-go v0.18.0/go.mod h1:uQSYDYs4WhVZ9i6AIoEZuwUggLVEF64HOD37boKAtF8=
+k8s.io/client-go v0.18.2/go.mod h1:Xcm5wVGXX9HAA2JJ2sSBUn3tCJ+4SVlCbl2MNNv+CIU=
+k8s.io/client-go v0.19.2 h1:gMJuU3xJZs86L1oQ99R4EViAADUPMHHtS9jFshasHSc=
+k8s.io/client-go v0.19.2/go.mod h1:S5wPhCqyDNAlzM9CnEdgTGV4OqhsW3jGO1UM1epwfJA=
+k8s.io/client-go v0.19.3/go.mod h1:+eEMktZM+MG0KO+PTkci8xnbCZHvj9TqR6Q1XDUIJOM=
+k8s.io/client-go v0.19.4 h1:85D3mDNoLF+xqpyE9Dh/OtrJDyJrSRKkHmDXIbEzer8=
+k8s.io/client-go v0.19.4/go.mod h1:ZrEy7+wj9PjH5VMBCuu/BDlvtUAku0oVFk4MmnW9mWA=
+k8s.io/client-go v0.20.2 h1:uuf+iIAbfnCSw8IGAv/Rg0giM+2bOzHLOsbbrwrdhNQ=
+k8s.io/client-go v0.20.2/go.mod h1:kH5brqWqp7HDxUFKoEgiI4v8G1xzbe9giaCenUWJzgE=
+k8s.io/code-generator v0.18.0/go.mod h1:+UHX5rSbxmR8kzS+FAv7um6dtYrZokQvjHpDSYRVkTc=
+k8s.io/code-generator v0.18.2/go.mod h1:+UHX5rSbxmR8kzS+FAv7um6dtYrZokQvjHpDSYRVkTc=
+k8s.io/code-generator v0.19.2 h1:7uaWJll6fyCPj2j3sfNN1AiY2gZU1VFN2dFR2uoxGWI=
+k8s.io/code-generator v0.19.2/go.mod h1:moqLn7w0t9cMs4+5CQyxnfA/HV8MF6aAVENF+WZZhgk=
+k8s.io/code-generator v0.19.3/go.mod h1:moqLn7w0t9cMs4+5CQyxnfA/HV8MF6aAVENF+WZZhgk=
+k8s.io/code-generator v0.19.4/go.mod h1:moqLn7w0t9cMs4+5CQyxnfA/HV8MF6aAVENF+WZZhgk=
+k8s.io/component-base v0.18.0/go.mod h1:u3BCg0z1uskkzrnAKFzulmYaEpZF7XC9Pf/uFyb1v2c=
+k8s.io/component-base v0.18.2/go.mod h1:kqLlMuhJNHQ9lz8Z7V5bxUUtjFZnrypArGl58gmDfUM=
+k8s.io/component-base v0.19.2 h1:jW5Y9RcZTb79liEhW3XDVTW7MuvEGP0tQZnfSX6/+gs=
+k8s.io/component-base v0.19.2/go.mod h1:g5LrsiTiabMLZ40AR6Hl45f088DevyGY+cCE2agEIVo=
+k8s.io/component-base v0.19.3/go.mod h1:WhLWSIefQn8W8jxSLl5WNiR6z8oyMe/8Zywg7alOkRc=
+k8s.io/component-base v0.19.4 h1:HobPRToQ8KJ9ubRju6PUAk9I5V1GNMJZ4PyWbiWA0uI=
+k8s.io/component-base v0.19.4/go.mod h1:ZzuSLlsWhajIDEkKF73j64Gz/5o0AgON08FgRbEPI70=
k8s.io/gengo v0.0.0-20190128074634-0689ccc1d7d6/go.mod h1:ezvh/TsK7cY6rbqRK0oQQ8IAqLxYwwyPxAX1Pzy0ii0=
-k8s.io/gengo v0.0.0-20190822140433-26a664648505/go.mod h1:ezvh/TsK7cY6rbqRK0oQQ8IAqLxYwwyPxAX1Pzy0ii0=
-k8s.io/gengo v0.0.0-20191010091904-7fa3014cb28f h1:eW/6wVuHNZgQJmFesyAxu0cvj0WAHHUuGaLbPcmNY3Q=
-k8s.io/gengo v0.0.0-20191010091904-7fa3014cb28f/go.mod h1:ezvh/TsK7cY6rbqRK0oQQ8IAqLxYwwyPxAX1Pzy0ii0=
-k8s.io/heapster v1.2.0-beta.1/go.mod h1:h1uhptVXMwC8xtZBYsPXKVi8fpdlYkTs6k949KozGrM=
-k8s.io/helm v2.16.1+incompatible/go.mod h1:LZzlS4LQBHfciFOurYBFkCMTaZ0D1l+p0teMg7TSULI=
-k8s.io/helm v2.16.3+incompatible/go.mod h1:LZzlS4LQBHfciFOurYBFkCMTaZ0D1l+p0teMg7TSULI=
-k8s.io/klog v0.2.0/go.mod h1:Gq+BEi5rUBO/HRz0bTSXDUcqjScdoY3a9IHpCEIOOfk=
+k8s.io/gengo v0.0.0-20200114144118-36b2048a9120/go.mod h1:ezvh/TsK7cY6rbqRK0oQQ8IAqLxYwwyPxAX1Pzy0ii0=
+k8s.io/gengo v0.0.0-20200413195148-3a45101e95ac/go.mod h1:ezvh/TsK7cY6rbqRK0oQQ8IAqLxYwwyPxAX1Pzy0ii0=
+k8s.io/gengo v0.0.0-20200428234225-8167cfdcfc14 h1:t4L10Qfx/p7ASH3gXCdIUtPbbIuegCoUJf3TMSFekjw=
+k8s.io/gengo v0.0.0-20200428234225-8167cfdcfc14/go.mod h1:ezvh/TsK7cY6rbqRK0oQQ8IAqLxYwwyPxAX1Pzy0ii0=
+k8s.io/klog v0.0.0-20181102134211-b9b56d5dfc92/go.mod h1:Gq+BEi5rUBO/HRz0bTSXDUcqjScdoY3a9IHpCEIOOfk=
k8s.io/klog v0.3.0/go.mod h1:Gq+BEi5rUBO/HRz0bTSXDUcqjScdoY3a9IHpCEIOOfk=
-k8s.io/klog v0.3.3/go.mod h1:Gq+BEi5rUBO/HRz0bTSXDUcqjScdoY3a9IHpCEIOOfk=
-k8s.io/klog v0.4.0/go.mod h1:4Bi6QPql/J/LkTDqv7R/cd3hPo4k2DG6Ptcz060Ez5I=
k8s.io/klog v1.0.0 h1:Pt+yjF5aB1xDSVbau4VsWe+dQNzA0qv1LlXdC2dF6Q8=
k8s.io/klog v1.0.0/go.mod h1:4Bi6QPql/J/LkTDqv7R/cd3hPo4k2DG6Ptcz060Ez5I=
-k8s.io/kube-aggregator v0.0.0-20191016112429-9587704a8ad4/go.mod h1:+aW0UZgSXdTSHTIFnWnueEuXjOqerDUxGIw6Ygr+vYY=
-k8s.io/kube-controller-manager v0.0.0-20191016114939-2b2b218dc1df/go.mod h1:WgrTcPKYAfNa9C0LV1UeK+XqfbSOUH1WGq/vX5UiW40=
-k8s.io/kube-openapi v0.0.0-20180711000925-0cf8f7e6ed1d h1:mn2F9UzCk6KGa7M/d2ibLyRtBQm7n6QvbCjDe/cDWSg=
-k8s.io/kube-openapi v0.0.0-20180711000925-0cf8f7e6ed1d/go.mod h1:BXM9ceUBTj2QnfH2MK1odQs778ajze1RxcmP6S8RVVc=
-k8s.io/kube-proxy v0.0.0-20191016114407-2e83b6f20229/go.mod h1:2Hxci1uzXO5ipP0h9n2+h18fvNkBTpYlckk5dOPu8zg=
-k8s.io/kube-scheduler v0.0.0-20191016114748-65049c67a58b/go.mod h1:BgDUHHC5Wl0xcBUQgo2XEprE5nG5i9tlRR4iNgEFbL0=
-k8s.io/kube-state-metrics v1.7.2 h1:6vdtgXrrRRMSgnyDmgua+qvgCYv954JNfxXAtDkeLVQ=
-k8s.io/kube-state-metrics v1.7.2/go.mod h1:U2Y6DRi07sS85rmVPmBFlmv+2peBcL8IWGjM+IjYA/E=
-k8s.io/kubectl v0.0.0-20191016120415-2ed914427d51/go.mod h1:gL826ZTIfD4vXTGlmzgTbliCAT9NGiqpCqK2aNYv5MQ=
-k8s.io/kubelet v0.0.0-20191016114556-7841ed97f1b2/go.mod h1:SBvrtLbuePbJygVXGGCMtWKH07+qrN2dE1iMnteSG8E=
+k8s.io/klog/v2 v2.0.0/go.mod h1:PBfzABfn139FHAV07az/IF9Wp1bkk3vpT2XSJ76fSDE=
+k8s.io/klog/v2 v2.2.0 h1:XRvcwJozkgZ1UQJmfMGpvRthQHOvihEhYtDfAaxMz/A=
+k8s.io/klog/v2 v2.2.0/go.mod h1:Od+F08eJP+W3HUb4pSrPpgp9DGU4GzlpG/TmITuYh/Y=
+k8s.io/klog/v2 v2.4.0 h1:7+X0fUguPyrKEC4WjH8iGDg3laWgMo5tMnRTIGTTxGQ=
+k8s.io/klog/v2 v2.4.0/go.mod h1:Od+F08eJP+W3HUb4pSrPpgp9DGU4GzlpG/TmITuYh/Y=
+k8s.io/kube-openapi v0.0.0-20200121204235-bf4fb3bd569c/go.mod h1:GRQhZsXIAJ1xR0C9bd8UpWHZ5plfAS9fzPjJuQ6JL3E=
+k8s.io/kube-openapi v0.0.0-20200805222855-6aeccd4b50c6 h1:+WnxoVtG8TMiudHBSEtrVL1egv36TkkJm+bA8AxicmQ=
+k8s.io/kube-openapi v0.0.0-20200805222855-6aeccd4b50c6/go.mod h1:UuqjUnNftUyPE5H64/qeyjQoUZhGpeFDVdxjTeEVN2o=
+k8s.io/kube-openapi v0.0.0-20201113171705-d219536bb9fd h1:sOHNzJIkytDF6qadMNKhhDRpc6ODik8lVC6nOur7B2c=
+k8s.io/kube-openapi v0.0.0-20201113171705-d219536bb9fd/go.mod h1:WOJ3KddDSol4tAGcJo0Tvi+dK12EcqSLqcWsryKMpfM=
+k8s.io/kubectl v0.18.0/go.mod h1:LOkWx9Z5DXMEg5KtOjHhRiC1fqJPLyCr3KtQgEolCkU=
+k8s.io/kubectl v0.19.3/go.mod h1:t5cscfrAuHUvEGNyNJjPKt+rGlaJzk8jrKYHXxEsANE=
+k8s.io/kubectl v0.19.4/go.mod h1:XPmlu4DJEYgD83pvZFeKF8+MSvGnYGqunbFSrJsqHv0=
k8s.io/kubernetes v1.13.0/go.mod h1:ocZa8+6APFNC2tX1DZASIbocyYT5jHzqFVsY5aoB7Jk=
-k8s.io/kubernetes v1.16.0/go.mod h1:nlP2zevWKRGKuaaVbKIwozU0Rjg9leVDXkL4YTtjmVs=
-k8s.io/kubernetes v1.16.2/go.mod h1:SmhGgKfQ30imqjFVj8AI+iW+zSyFsswNErKYeTfgoH0=
-k8s.io/legacy-cloud-providers v0.0.0-20191016115753-cf0698c3a16b/go.mod h1:tKW3pKqdRW8pMveUTpF5pJuCjQxg6a25iLo+Z9BXVH0=
-k8s.io/metrics v0.0.0-20191016113814-3b1a734dba6e/go.mod h1:ve7/vMWeY5lEBkZf6Bt5TTbGS3b8wAxwGbdXAsufjRs=
-k8s.io/repo-infra v0.0.0-20181204233714-00fe14e3d1a3/go.mod h1:+G1xBfZDfVFsm1Tj/HNCvg4QqWx8rJ2Fxpqr1rqp/gQ=
-k8s.io/sample-apiserver v0.0.0-20191016112829-06bb3c9d77c9/go.mod h1:sXltHZrQa4jdKL14nOFRRUhhzpmbnRF0qGuAhRQbaxc=
-k8s.io/utils v0.0.0-20190308190857-21c4ce38f2a7/go.mod h1:8k8uAuAQ0rXslZKaEWd0c3oVhZz7sSzSiPnVZayjIX0=
-k8s.io/utils v0.0.0-20190801114015-581e00157fb1 h1:+ySTxfHnfzZb9ys375PXNlLhkJPLKgHajBU0N62BDvE=
-k8s.io/utils v0.0.0-20190801114015-581e00157fb1/go.mod h1:sZAwmy6armz5eXlNoLmJcl4F1QuKu7sr+mFQ0byX7Ew=
-k8s.io/utils v0.0.0-20191010214722-8d271d903fe4 h1:Gi+/O1saihwDqnlmC8Vhv1M5Sp4+rbOmK9TbsLn8ZEA=
-k8s.io/utils v0.0.0-20191010214722-8d271d903fe4/go.mod h1:sZAwmy6armz5eXlNoLmJcl4F1QuKu7sr+mFQ0byX7Ew=
-mvdan.cc/interfacer v0.0.0-20180901003855-c20040233aed h1:WX1yoOaKQfddO/mLzdV4wptyWgoH/6hwLs7QHTixo0I=
-mvdan.cc/interfacer v0.0.0-20180901003855-c20040233aed/go.mod h1:Xkxe497xwlCKkIaQYRfC7CSLworTXY9RMqwhhCm+8Nc=
-mvdan.cc/lint v0.0.0-20170908181259-adc824a0674b h1:DxJ5nJdkhDlLok9K6qO+5290kphDJbHOQO1DFFFTeBo=
-mvdan.cc/lint v0.0.0-20170908181259-adc824a0674b/go.mod h1:2odslEg/xrtNQqCYg2/jCoyKnw3vv5biOc3JnIcYfL4=
-mvdan.cc/unparam v0.0.0-20190720180237-d51796306d8f h1:Cq7MalBHYACRd6EesksG1Q8EoIAKOsiZviGKbOLIej4=
-mvdan.cc/unparam v0.0.0-20190720180237-d51796306d8f/go.mod h1:4G1h5nDURzA3bwVMZIVpwbkw+04kSxk3rAtzlimaUJw=
-rsc.io/letsencrypt v0.0.1/go.mod h1:buyQKZ6IXrRnB7TdkHP0RyEybLx18HHyOSoTyoOLqNY=
+k8s.io/metrics v0.18.0/go.mod h1:8aYTW18koXqjLVKL7Ds05RPMX9ipJZI3mywYvBOxXd4=
+k8s.io/metrics v0.19.3/go.mod h1:Eap/Lk1FiAIjkaArFuv41v+ph6dbDpVGwAg7jMI+4vg=
+k8s.io/metrics v0.19.4/go.mod h1:a0gvAzrxQPw2ouBqnXI7X9qlggpPkKAFgWU/Py+KZiU=
+k8s.io/utils v0.0.0-20200324210504-a9aa75ae1b89/go.mod h1:sZAwmy6armz5eXlNoLmJcl4F1QuKu7sr+mFQ0byX7Ew=
+k8s.io/utils v0.0.0-20200729134348-d5654de09c73/go.mod h1:jPW/WVKK9YHAvNhRxK0md/EJ228hCsBRufyofKtW8HA=
+k8s.io/utils v0.0.0-20200912215256-4140de9c8800 h1:9ZNvfPvVIEsp/T1ez4GQuzCcCTEQWhovSofhqR73A6g=
+k8s.io/utils v0.0.0-20200912215256-4140de9c8800/go.mod h1:jPW/WVKK9YHAvNhRxK0md/EJ228hCsBRufyofKtW8HA=
+k8s.io/utils v0.0.0-20201110183641-67b214c5f920 h1:CbnUZsM497iRC5QMVkHwyl8s2tB3g7yaSHkYPkpgelw=
+k8s.io/utils v0.0.0-20201110183641-67b214c5f920/go.mod h1:jPW/WVKK9YHAvNhRxK0md/EJ228hCsBRufyofKtW8HA=
+rsc.io/binaryregexp v0.2.0/go.mod h1:qTv7/COck+e2FymRvadv62gMdZztPaShugOCi3I+8D8=
rsc.io/letsencrypt v0.0.3/go.mod h1:buyQKZ6IXrRnB7TdkHP0RyEybLx18HHyOSoTyoOLqNY=
-sigs.k8s.io/controller-runtime v0.4.0 h1:wATM6/m+3w8lj8FXNaO6Fs/rq/vqoOjO1Q116Z9NPsg=
-sigs.k8s.io/controller-runtime v0.4.0/go.mod h1:ApC79lpY3PHW9xj/w9pj+lYkLgwAAUZwfXkME1Lajns=
-sigs.k8s.io/controller-tools v0.1.11-0.20190411181648-9d55346c2bde h1:ZkaHf5rNYzIB6CB82keKMQNv7xxkqT0ylOBdfJPfi+k=
-sigs.k8s.io/controller-tools v0.1.11-0.20190411181648-9d55346c2bde/go.mod h1:ATWLRP3WGxuAN9HcT2LaKHReXIH+EZGzRuMHuxjXfhQ=
+rsc.io/quote/v3 v3.1.0/go.mod h1:yEA65RcK8LyAZtP9Kv3t0HmxON59tX3rD+tICJqUlj0=
+rsc.io/sampler v1.3.0/go.mod h1:T1hPZKmBbMNahiBKFy5HrXp6adAjACjK9JXDnKaTXpA=
+sigs.k8s.io/apiserver-network-proxy/konnectivity-client v0.0.7/go.mod h1:PHgbrJT7lCHcxMU+mDHEm+nx46H4zuuHZkDP6icnhu0=
+sigs.k8s.io/apiserver-network-proxy/konnectivity-client v0.0.9/go.mod h1:dzAXnQbTRyDlZPJX2SUPEqvnB+j7AJjtlox7PEwigU0=
+sigs.k8s.io/controller-runtime v0.6.0/go.mod h1:CpYf5pdNY/B352A1TFLAS2JVSlnGQ5O2cftPHndTroo=
+sigs.k8s.io/controller-runtime v0.7.0 h1:bU20IBBEPccWz5+zXpLnpVsgBYxqclaHu1pVDl/gEt8=
+sigs.k8s.io/controller-runtime v0.7.0/go.mod h1:pJ3YBrJiAqMAZKi6UVGuE98ZrroV1p+pIhoHsMm9wdU=
+sigs.k8s.io/controller-tools v0.3.0/go.mod h1:enhtKGfxZD1GFEoMgP8Fdbu+uKQ/cq1/WGJhdVChfvI=
+sigs.k8s.io/controller-tools v0.4.1 h1:VkuV0MxlRPmRu5iTgBZU4UxUX2LiR99n3sdQGRxZF4w=
+sigs.k8s.io/controller-tools v0.4.1/go.mod h1:G9rHdZMVlBDocIxGkK3jHLWqcTMNvveypYJwrvYKjWU=
+sigs.k8s.io/kubebuilder/v2 v2.3.2-0.20201214213149-0a807f4e9428/go.mod h1:J/D/179LBZhQOhRvmMRNbje/Bk+PjbN0/fzUupmO7+U=
sigs.k8s.io/kustomize v2.0.3+incompatible h1:JUufWFNlI44MdtnjUqVnvh29rR37PQFzPbLXqhyOyX0=
sigs.k8s.io/kustomize v2.0.3+incompatible/go.mod h1:MkjgH3RdOWrievjo6c9T245dYlB5QeXV4WCbnt/PEpU=
-sigs.k8s.io/structured-merge-diff v0.0.0-20190817042607-6149e4549fca/go.mod h1:IIgPezJWb76P0hotTxzDbWsMYB8APh18qZnxkomBpxA=
-sigs.k8s.io/testing_frameworks v0.1.2/go.mod h1:ToQrwSC3s8Xf/lADdZp3Mktcql9CG0UAmdJG9th5i0w=
-sigs.k8s.io/yaml v1.1.0 h1:4A07+ZFc2wgJwo8YNlQpr1rVlgUDlxXHhPJciaPY5gs=
+sigs.k8s.io/structured-merge-diff/v3 v3.0.0-20200116222232-67a7b8c61874/go.mod h1:PlARxl6Hbt/+BC80dRLi1qAmnMqwqDg62YvvVkZjemw=
+sigs.k8s.io/structured-merge-diff/v3 v3.0.0/go.mod h1:PlARxl6Hbt/+BC80dRLi1qAmnMqwqDg62YvvVkZjemw=
+sigs.k8s.io/structured-merge-diff/v4 v4.0.1 h1:YXTMot5Qz/X1iBRJhAt+vI+HVttY0WkSqqhKxQ0xVbA=
+sigs.k8s.io/structured-merge-diff/v4 v4.0.1/go.mod h1:bJZC9H9iH24zzfZ/41RGcq60oK1F7G282QMXDPYydCw=
+sigs.k8s.io/structured-merge-diff/v4 v4.0.2 h1:YHQV7Dajm86OuqnIR6zAelnDWBRjo+YhYV9PmGrh1s8=
+sigs.k8s.io/structured-merge-diff/v4 v4.0.2/go.mod h1:bJZC9H9iH24zzfZ/41RGcq60oK1F7G282QMXDPYydCw=
sigs.k8s.io/yaml v1.1.0/go.mod h1:UJmg0vDUVViEyp3mgSv9WPwZCDxu4rQW1olrI1uml+o=
-sourcegraph.com/sqs/pbtypes v0.0.0-20180604144634-d3ebe8f20ae4 h1:JPJh2pk3+X4lXAkZIk2RuE/7/FoK9maXw+TNPJhVS/c=
-sourcegraph.com/sqs/pbtypes v0.0.0-20180604144634-d3ebe8f20ae4/go.mod h1:ketZ/q3QxT9HOBeFhu6RdvsftgpsbFHBF5Cas6cDKZ0=
+sigs.k8s.io/yaml v1.2.0 h1:kr/MCeFWJWTwyaHoR9c8EjH9OumOmoF9YGiZd7lFm/Q=
+sigs.k8s.io/yaml v1.2.0/go.mod h1:yfXDCHCao9+ENCvLSE62v9VSji2MKu5jeNfTrofGhJc=
+sourcegraph.com/sourcegraph/appdash v0.0.0-20190731080439-ebfcffb1b5c0/go.mod h1:hI742Nqp5OhwiqlzhgfbWU4mW4yO10fP+LoT9WOswdU=
vbom.ml/util v0.0.0-20160121211510-db5cfe13f5cc/go.mod h1:so/NYdZXCz+E3ZpW0uAoCj6uzU2+8OWDFv/HxUSs7kI=
diff --git a/hack/boilerplate.go.txt b/hack/boilerplate.go.txt
new file mode 100644
index 00000000..45dbbbbc
--- /dev/null
+++ b/hack/boilerplate.go.txt
@@ -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.
+*/
\ No newline at end of file
diff --git a/main.go b/main.go
new file mode 100644
index 00000000..0ef69846
--- /dev/null
+++ b/main.go
@@ -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: ¬ificationEvents,
+ 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)
+}
diff --git a/pkg/apis/apis.go b/pkg/apis/apis.go
deleted file mode 100644
index e2d7b6af..00000000
--- a/pkg/apis/apis.go
+++ /dev/null
@@ -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)
-}
diff --git a/pkg/apis/jenkins/v1alpha2/jenkinsimage_types.go b/pkg/apis/jenkins/v1alpha2/jenkinsimage_types.go
deleted file mode 100644
index 4cd63654..00000000
--- a/pkg/apis/jenkins/v1alpha2/jenkinsimage_types.go
+++ /dev/null
@@ -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{})
-}
diff --git a/pkg/apis/jenkins/v1alpha2/zz_generated.openapi.go b/pkg/apis/jenkins/v1alpha2/zz_generated.openapi.go
deleted file mode 100644
index 77ada573..00000000
--- a/pkg/apis/jenkins/v1alpha2/zz_generated.openapi.go
+++ /dev/null
@@ -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"},
- }
-}
diff --git a/pkg/configuration/backuprestore/backuprestore.go b/pkg/configuration/backuprestore/backuprestore.go
index 3805443d..1ee8b9d7 100644
--- a/pkg/configuration/backuprestore/backuprestore.go
+++ b/pkg/configuration/backuprestore/backuprestore.go
@@ -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))
}
diff --git a/pkg/configuration/base/configmap.go b/pkg/configuration/base/configmap.go
index fb3185e1..236afe86 100644
--- a/pkg/configuration/base/configmap.go
+++ b/pkg/configuration/base/configmap.go
@@ -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
diff --git a/pkg/configuration/base/container.go b/pkg/configuration/base/container.go
index 7ca65856..99d7f436 100644
--- a/pkg/configuration/base/container.go
+++ b/pkg/configuration/base/container.go
@@ -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))
diff --git a/pkg/configuration/base/deployment.go b/pkg/configuration/base/deployment.go
index 0e63e1a1..533a3a61 100644
--- a/pkg/configuration/base/deployment.go
+++ b/pkg/configuration/base/deployment.go
@@ -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
diff --git a/pkg/configuration/base/label.go b/pkg/configuration/base/label.go
index 2f92a641..415d5ecb 100644
--- a/pkg/configuration/base/label.go
+++ b/pkg/configuration/base/label.go
@@ -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 {
diff --git a/pkg/configuration/base/plugin.go b/pkg/configuration/base/plugin.go
index 4847f6c5..b396c3f2 100644
--- a/pkg/configuration/base/plugin.go
+++ b/pkg/configuration/base/plugin.go
@@ -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)
diff --git a/pkg/configuration/base/pod.go b/pkg/configuration/base/pod.go
index 873f367d..579e085f 100644
--- a/pkg/configuration/base/pod.go
+++ b/pkg/configuration/base/pod.go
@@ -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)
}
diff --git a/pkg/configuration/base/rbac.go b/pkg/configuration/base/rbac.go
index e7525118..711e1d29 100644
--- a/pkg/configuration/base/rbac.go
+++ b/pkg/configuration/base/rbac.go
@@ -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)
}
diff --git a/pkg/configuration/base/reconcile_test.go b/pkg/configuration/base/reconcile_test.go
index fbf53341..246d3529 100644
--- a/pkg/configuration/base/reconcile_test.go
+++ b/pkg/configuration/base/reconcile_test.go
@@ -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)
})
}
diff --git a/pkg/configuration/base/reconcile.go b/pkg/configuration/base/reconciler.go
similarity index 80%
rename from pkg/configuration/base/reconcile.go
rename to pkg/configuration/base/reconciler.go
index 2f07becf..e314ad08 100644
--- a/pkg/configuration/base/reconcile.go
+++ b/pkg/configuration/base/reconciler.go
@@ -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))
- }
- }
-}
diff --git a/pkg/configuration/base/resources/base_configuration_configmap.go b/pkg/configuration/base/resources/base_configuration_configmap.go
index ef16c63e..1e6e1983 100644
--- a/pkg/configuration/base/resources/base_configuration_configmap.go
+++ b/pkg/configuration/base/resources/base_configuration_configmap.go
@@ -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"
diff --git a/pkg/configuration/base/resources/builder.go b/pkg/configuration/base/resources/builder.go
deleted file mode 100644
index 9ad27de8..00000000
--- a/pkg/configuration/base/resources/builder.go
+++ /dev/null
@@ -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
-}
diff --git a/pkg/configuration/base/resources/deployment.go b/pkg/configuration/base/resources/deployment.go
index 00b46a47..8c8d2a44 100644
--- a/pkg/configuration/base/resources/deployment.go
+++ b/pkg/configuration/base/resources/deployment.go
@@ -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"
diff --git a/pkg/configuration/base/resources/init_configuration_configmap.go b/pkg/configuration/base/resources/init_configuration_configmap.go
index 9a3f4db6..bebed79f 100644
--- a/pkg/configuration/base/resources/init_configuration_configmap.go
+++ b/pkg/configuration/base/resources/init_configuration_configmap.go
@@ -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"
diff --git a/pkg/configuration/base/resources/meta.go b/pkg/configuration/base/resources/meta.go
index 91b3d9e4..5e6af861 100644
--- a/pkg/configuration/base/resources/meta.go
+++ b/pkg/configuration/base/resources/meta.go
@@ -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"
diff --git a/pkg/configuration/base/resources/operator_credentials_secret.go b/pkg/configuration/base/resources/operator_credentials_secret.go
index 34ea194d..babd8276 100644
--- a/pkg/configuration/base/resources/operator_credentials_secret.go
+++ b/pkg/configuration/base/resources/operator_credentials_secret.go
@@ -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"
diff --git a/pkg/configuration/base/resources/pod.go b/pkg/configuration/base/resources/pod.go
index 265a75ab..9ee2ef0c 100644
--- a/pkg/configuration/base/resources/pod.go
+++ b/pkg/configuration/base/resources/pod.go
@@ -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"
diff --git a/pkg/configuration/base/resources/pod_test.go b/pkg/configuration/base/resources/pod_test.go
index bb23047f..988c9fb7 100644
--- a/pkg/configuration/base/resources/pod_test.go
+++ b/pkg/configuration/base/resources/pod_test.go
@@ -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"
)
diff --git a/pkg/configuration/base/resources/probe.go b/pkg/configuration/base/resources/probe.go
index 545933ae..f59c1534 100644
--- a/pkg/configuration/base/resources/probe.go
+++ b/pkg/configuration/base/resources/probe.go
@@ -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
-}
diff --git a/pkg/configuration/base/resources/resources_test.go b/pkg/configuration/base/resources/resources_test.go
index 64742f77..aa5f9c1b 100644
--- a/pkg/configuration/base/resources/resources_test.go
+++ b/pkg/configuration/base/resources/resources_test.go
@@ -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"
)
diff --git a/pkg/configuration/base/resources/route.go b/pkg/configuration/base/resources/route.go
index e5299279..dbcc5215 100644
--- a/pkg/configuration/base/resources/route.go
+++ b/pkg/configuration/base/resources/route.go
@@ -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
diff --git a/pkg/configuration/base/resources/scripts_configmap.go b/pkg/configuration/base/resources/scripts_configmap.go
index 14e0b36f..2014a42e 100644
--- a/pkg/configuration/base/resources/scripts_configmap.go
+++ b/pkg/configuration/base/resources/scripts_configmap.go
@@ -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"
diff --git a/pkg/configuration/base/resources/service.go b/pkg/configuration/base/resources/service.go
index 507b206b..92b07308 100644
--- a/pkg/configuration/base/resources/service.go
+++ b/pkg/configuration/base/resources/service.go
@@ -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
}
diff --git a/pkg/configuration/base/route.go b/pkg/configuration/base/route.go
index 585b178f..f55a15d6 100644
--- a/pkg/configuration/base/route.go
+++ b/pkg/configuration/base/route.go
@@ -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)
diff --git a/pkg/configuration/base/service.go b/pkg/configuration/base/service.go
index 3c9f2130..7112e20d 100644
--- a/pkg/configuration/base/service.go
+++ b/pkg/configuration/base/service.go
@@ -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) {
diff --git a/pkg/configuration/base/serviceaccount.go b/pkg/configuration/base/serviceaccount.go
index 2002a766..ea30d29f 100644
--- a/pkg/configuration/base/serviceaccount.go
+++ b/pkg/configuration/base/serviceaccount.go
@@ -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
diff --git a/pkg/configuration/base/validate.go b/pkg/configuration/base/validate.go
index 85911d79..ae3bf622 100644
--- a/pkg/configuration/base/validate.go
+++ b/pkg/configuration/base/validate.go
@@ -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
diff --git a/pkg/configuration/base/validate_test.go b/pkg/configuration/base/validate_test.go
index 96eb0183..d585e545 100644
--- a/pkg/configuration/base/validate_test.go
+++ b/pkg/configuration/base/validate_test.go
@@ -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,
diff --git a/pkg/configuration/configuration.go b/pkg/configuration/configuration.go
index 53416583..1f651b83 100644
--- a/pkg/configuration/configuration.go
+++ b/pkg/configuration/configuration.go
@@ -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
-}
diff --git a/pkg/configuration/user/casc/casc.go b/pkg/configuration/user/casc/casc.go
index 0d06e284..6e7bff4c 100644
--- a/pkg/configuration/user/casc/casc.go
+++ b/pkg/configuration/user/casc/casc.go
@@ -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
diff --git a/pkg/configuration/user/reconcile.go b/pkg/configuration/user/reconcile.go
index cb9710f8..d5671e2a 100644
--- a/pkg/configuration/user/reconcile.go
+++ b/pkg/configuration/user/reconcile.go
@@ -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"
)
diff --git a/pkg/configuration/user/seedjobs/seedjobs.go b/pkg/configuration/user/seedjobs/seedjobs.go
index 3707a786..cb9b3725 100644
--- a/pkg/configuration/user/seedjobs/seedjobs.go
+++ b/pkg/configuration/user/seedjobs/seedjobs.go
@@ -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
diff --git a/pkg/configuration/user/seedjobs/seedjobs_test.go b/pkg/configuration/user/seedjobs/seedjobs_test.go
index d0fb3776..45582e72 100644
--- a/pkg/configuration/user/seedjobs/seedjobs_test.go
+++ b/pkg/configuration/user/seedjobs/seedjobs_test.go
@@ -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)
diff --git a/pkg/configuration/user/seedjobs/validate.go b/pkg/configuration/user/seedjobs/validate.go
index 8e52529f..84c78ad4 100644
--- a/pkg/configuration/user/seedjobs/validate.go
+++ b/pkg/configuration/user/seedjobs/validate.go
@@ -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"
diff --git a/pkg/configuration/user/seedjobs/validate_test.go b/pkg/configuration/user/seedjobs/validate_test.go
index 02bfbbe9..22007a3e 100644
--- a/pkg/configuration/user/seedjobs/validate_test.go
+++ b/pkg/configuration/user/seedjobs/validate_test.go
@@ -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,
diff --git a/pkg/configuration/user/validate.go b/pkg/configuration/user/validate.go
index af0b61ff..d004fa37 100644
--- a/pkg/configuration/user/validate.go
+++ b/pkg/configuration/user/validate.go
@@ -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"
)
diff --git a/pkg/controller/jenkins/reconciler.go b/pkg/controller/jenkins/reconciler.go
deleted file mode 100644
index 9fdd30e1..00000000
--- a/pkg/controller/jenkins/reconciler.go
+++ /dev/null
@@ -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,
- }
-}
diff --git a/pkg/controller/jenkinsimage/jenkinsimage_controller.go b/pkg/controller/jenkinsimage/jenkinsimage_controller.go
deleted file mode 100644
index 92d81c25..00000000
--- a/pkg/controller/jenkinsimage/jenkinsimage_controller.go
+++ /dev/null
@@ -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
-}
diff --git a/pkg/event/event.go b/pkg/event/event.go
index ca0a9e0a..ce0713eb 100644
--- a/pkg/event/event.go
+++ b/pkg/event/event.go
@@ -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("")})
diff --git a/pkg/groovy/groovy.go b/pkg/groovy/groovy.go
index 9244575b..ae4f2249 100644
--- a/pkg/groovy/groovy.go
+++ b/pkg/groovy/groovy.go
@@ -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
diff --git a/pkg/groovy/groovy_test.go b/pkg/groovy/groovy_test.go
index 94fe20b1..8a97f637 100644
--- a/pkg/groovy/groovy_test.go
+++ b/pkg/groovy/groovy_test.go
@@ -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)
diff --git a/pkg/notifications/event/event.go b/pkg/notifications/event/event.go
index 6dec551a..ed73389d 100644
--- a/pkg/notifications/event/event.go
+++ b/pkg/notifications/event/event.go
@@ -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"
)
diff --git a/pkg/notifications/mailgun/mailgun.go b/pkg/notifications/mailgun/mailgun.go
index 72729407..41ae54d5 100644
--- a/pkg/notifications/mailgun/mailgun.go
+++ b/pkg/notifications/mailgun/mailgun.go
@@ -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"
diff --git a/pkg/notifications/mailgun/mailgun_test.go b/pkg/notifications/mailgun/mailgun_test.go
index b621a651..e7e48795 100644
--- a/pkg/notifications/mailgun/mailgun_test.go
+++ b/pkg/notifications/mailgun/mailgun_test.go
@@ -5,7 +5,7 @@ import (
"strings"
"testing"
- "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"
"github.com/jenkinsci/kubernetes-operator/pkg/notifications/reason"
@@ -24,7 +24,7 @@ func TestGenerateMessages(t *testing.T) {
res := reason.NewUndefined(reason.KubernetesSource, []string{"test-string"}, "test-verbose")
s := MailGun{
- k8sClient: fake.NewFakeClient(),
+ k8sClient: fake.NewClientBuilder().Build(),
config: v1alpha2.Notification{
Verbose: true,
},
@@ -65,7 +65,7 @@ func TestGenerateMessages(t *testing.T) {
res := reason.NewUndefined(reason.KubernetesSource, []string{"nil"}, "nil")
s := MailGun{
- k8sClient: fake.NewFakeClient(),
+ k8sClient: fake.NewClientBuilder().Build(),
config: v1alpha2.Notification{
Verbose: true,
},
@@ -106,7 +106,7 @@ func TestGenerateMessages(t *testing.T) {
res := reason.NewUndefined(reason.KubernetesSource, []string{""}, "")
s := MailGun{
- k8sClient: fake.NewFakeClient(),
+ k8sClient: fake.NewClientBuilder().Build(),
config: v1alpha2.Notification{
Verbose: true,
},
@@ -147,7 +147,7 @@ func TestGenerateMessages(t *testing.T) {
res := reason.NewUndefined(reason.KubernetesSource, []string{"ąśćńółżź"}, "ąśćńółżź")
s := MailGun{
- k8sClient: fake.NewFakeClient(),
+ k8sClient: fake.NewClientBuilder().Build(),
config: v1alpha2.Notification{
Verbose: true,
},
diff --git a/pkg/notifications/msteams/msteams.go b/pkg/notifications/msteams/msteams.go
index 1048a4ee..036d8e50 100644
--- a/pkg/notifications/msteams/msteams.go
+++ b/pkg/notifications/msteams/msteams.go
@@ -8,7 +8,7 @@ import (
"net/http"
"strings"
- "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"
diff --git a/pkg/notifications/msteams/msteams_test.go b/pkg/notifications/msteams/msteams_test.go
index f0e413e5..520af78f 100644
--- a/pkg/notifications/msteams/msteams_test.go
+++ b/pkg/notifications/msteams/msteams_test.go
@@ -8,7 +8,7 @@ import (
"strings"
"testing"
- "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"
"github.com/jenkinsci/kubernetes-operator/pkg/notifications/reason"
@@ -32,11 +32,11 @@ var (
)
func TestTeams_Send(t *testing.T) {
- fakeClient := fake.NewFakeClient()
+ fakeClient := fake.NewClientBuilder().Build()
testURLSelectorKeyName := "test-url-selector"
testSecretName := "test-secret"
- event := event.Event{
+ e := event.Event{
Jenkins: v1alpha2.Jenkins{
ObjectMeta: metav1.ObjectMeta{
Name: testCrName,
@@ -66,27 +66,27 @@ func TestTeams_Send(t *testing.T) {
t.Fatal(err)
}
- assert.Equal(t, message.Title, provider.NotificationTitle(event))
- assert.Equal(t, message.ThemeColor, teams.getStatusColor(event.Level))
+ assert.Equal(t, message.Title, provider.NotificationTitle(e))
+ assert.Equal(t, message.ThemeColor, teams.getStatusColor(e.Level))
mainSection := message.Sections[0]
- reason := strings.Join(event.Reason.Short(), "\n\n - ")
+ reasonString := strings.Join(e.Reason.Short(), "\n\n - ")
- assert.Equal(t, mainSection.Text, reason)
+ assert.Equal(t, mainSection.Text, reasonString)
for _, fact := range mainSection.Facts {
switch fact.Name {
case provider.PhaseFieldName:
- assert.Equal(t, fact.Value, string(event.Phase))
+ assert.Equal(t, fact.Value, string(e.Phase))
case provider.CrNameFieldName:
- assert.Equal(t, fact.Value, event.Jenkins.Name)
+ assert.Equal(t, fact.Value, e.Jenkins.Name)
case provider.MessageFieldName:
- assert.Equal(t, fact.Value, reason)
+ assert.Equal(t, fact.Value, reasonString)
case provider.LevelFieldName:
- assert.Equal(t, fact.Value, string(event.Level))
+ assert.Equal(t, fact.Value, string(e.Level))
case provider.NamespaceFieldName:
- assert.Equal(t, fact.Value, event.Jenkins.Namespace)
+ assert.Equal(t, fact.Value, e.Jenkins.Namespace)
default:
t.Errorf("Found unexpected '%+v' fact", fact)
}
@@ -109,7 +109,7 @@ func TestTeams_Send(t *testing.T) {
err := fakeClient.Create(context.TODO(), secret)
assert.NoError(t, err)
- err = teams.Send(event)
+ err = teams.Send(e)
assert.NoError(t, err)
}
@@ -123,7 +123,7 @@ func TestGenerateMessages(t *testing.T) {
s := Teams{
httpClient: http.Client{},
- k8sClient: fake.NewFakeClient(),
+ k8sClient: fake.NewClientBuilder().Build(),
config: v1alpha2.Notification{
Verbose: true,
},
@@ -166,7 +166,7 @@ func TestGenerateMessages(t *testing.T) {
s := Teams{
httpClient: http.Client{},
- k8sClient: fake.NewFakeClient(),
+ k8sClient: fake.NewClientBuilder().Build(),
config: v1alpha2.Notification{
Verbose: true,
},
@@ -209,7 +209,7 @@ func TestGenerateMessages(t *testing.T) {
s := Teams{
httpClient: http.Client{},
- k8sClient: fake.NewFakeClient(),
+ k8sClient: fake.NewClientBuilder().Build(),
config: v1alpha2.Notification{
Verbose: true,
},
@@ -251,7 +251,7 @@ func TestGenerateMessages(t *testing.T) {
res := reason.NewUndefined(reason.KubernetesSource, []string{"ąśćńółżź"}, "ąśćńółżź")
s := Teams{
httpClient: http.Client{},
- k8sClient: fake.NewFakeClient(),
+ k8sClient: fake.NewClientBuilder().Build(),
config: v1alpha2.Notification{
Verbose: true,
},
diff --git a/pkg/notifications/provider/provider.go b/pkg/notifications/provider/provider.go
index 36bf7b29..b9b188ff 100644
--- a/pkg/notifications/provider/provider.go
+++ b/pkg/notifications/provider/provider.go
@@ -1,7 +1,7 @@
package provider
import (
- "github.com/jenkinsci/kubernetes-operator/pkg/apis/jenkins/v1alpha2"
+ "github.com/jenkinsci/kubernetes-operator/api/v1alpha2"
"github.com/jenkinsci/kubernetes-operator/pkg/notifications/event"
)
diff --git a/pkg/notifications/sender.go b/pkg/notifications/sender.go
index 4e100c88..6e167b3c 100644
--- a/pkg/notifications/sender.go
+++ b/pkg/notifications/sender.go
@@ -6,7 +6,7 @@ import (
"reflect"
"strings"
- "github.com/jenkinsci/kubernetes-operator/pkg/apis/jenkins/v1alpha2"
+ "github.com/jenkinsci/kubernetes-operator/api/v1alpha2"
k8sevent "github.com/jenkinsci/kubernetes-operator/pkg/event"
"github.com/jenkinsci/kubernetes-operator/pkg/log"
"github.com/jenkinsci/kubernetes-operator/pkg/notifications/event"
diff --git a/pkg/notifications/slack/slack.go b/pkg/notifications/slack/slack.go
index eaeb9f9b..acda1847 100644
--- a/pkg/notifications/slack/slack.go
+++ b/pkg/notifications/slack/slack.go
@@ -7,7 +7,7 @@ import (
"net/http"
"strings"
- "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"
diff --git a/pkg/notifications/slack/slack_test.go b/pkg/notifications/slack/slack_test.go
index 84e8fc59..e1734088 100644
--- a/pkg/notifications/slack/slack_test.go
+++ b/pkg/notifications/slack/slack_test.go
@@ -8,7 +8,7 @@ import (
"strings"
"testing"
- "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"
"github.com/jenkinsci/kubernetes-operator/pkg/notifications/reason"
@@ -32,7 +32,7 @@ var (
)
func TestSlack_Send(t *testing.T) {
- fakeClient := fake.NewFakeClient()
+ fakeClient := fake.NewClientBuilder().Build()
testURLSelectorKeyName := "test-url-selector"
testSecretName := "test-secret"
@@ -126,7 +126,7 @@ func TestGenerateMessage(t *testing.T) {
s := Slack{
httpClient: http.Client{},
- k8sClient: fake.NewFakeClient(),
+ k8sClient: fake.NewClientBuilder().Build(),
config: v1alpha2.Notification{
Verbose: true,
},
@@ -172,7 +172,7 @@ func TestGenerateMessage(t *testing.T) {
s := Slack{
httpClient: http.Client{},
- k8sClient: fake.NewFakeClient(),
+ k8sClient: fake.NewClientBuilder().Build(),
config: v1alpha2.Notification{
Verbose: true,
},
@@ -218,7 +218,7 @@ func TestGenerateMessage(t *testing.T) {
s := Slack{
httpClient: http.Client{},
- k8sClient: fake.NewFakeClient(),
+ k8sClient: fake.NewClientBuilder().Build(),
config: v1alpha2.Notification{
Verbose: true,
},
@@ -264,7 +264,7 @@ func TestGenerateMessage(t *testing.T) {
s := Slack{
httpClient: http.Client{},
- k8sClient: fake.NewFakeClient(),
+ k8sClient: fake.NewClientBuilder().Build(),
config: v1alpha2.Notification{
Verbose: true,
},
diff --git a/pkg/notifications/smtp/smtp.go b/pkg/notifications/smtp/smtp.go
index 156cc00b..418e8b3a 100644
--- a/pkg/notifications/smtp/smtp.go
+++ b/pkg/notifications/smtp/smtp.go
@@ -6,7 +6,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/notifications/event"
"github.com/jenkinsci/kubernetes-operator/pkg/notifications/provider"
diff --git a/pkg/notifications/smtp/smtp_test.go b/pkg/notifications/smtp/smtp_test.go
index 2e49123c..fee09c92 100644
--- a/pkg/notifications/smtp/smtp_test.go
+++ b/pkg/notifications/smtp/smtp_test.go
@@ -12,7 +12,7 @@ import (
"testing"
"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/reason"
@@ -58,7 +58,7 @@ type testServer struct {
}
// Login handles a login command with username and password.
-func (bkd *testServer) Login(state *smtp.ConnectionState, username, password string) (smtp.Session, error) {
+func (bkd *testServer) Login(_ *smtp.ConnectionState, username, password string) (smtp.Session, error) {
if username != testSMTPUsername || password != testSMTPPassword {
return nil, errors.New("invalid username or password")
}
@@ -66,7 +66,7 @@ func (bkd *testServer) Login(state *smtp.ConnectionState, username, password str
}
// AnonymousLogin requires clients to authenticate using SMTP AUTH before sending emails
-func (bkd *testServer) AnonymousLogin(state *smtp.ConnectionState) (smtp.Session, error) {
+func (bkd *testServer) AnonymousLogin(_ *smtp.ConnectionState) (smtp.Session, error) {
return nil, smtp.ErrAuthRequired
}
@@ -128,7 +128,7 @@ func (s *testSession) Logout() error {
}
func TestSMTP_Send(t *testing.T) {
- event := event.Event{
+ e := event.Event{
Jenkins: v1alpha2.Jenkins{
ObjectMeta: metav1.ObjectMeta{
Name: testCrName,
@@ -140,7 +140,7 @@ func TestSMTP_Send(t *testing.T) {
Reason: testReason,
}
- fakeClient := fake.NewFakeClient()
+ fakeClient := fake.NewClientBuilder().Build()
testUsernameSelectorKeyName := "test-username-selector"
testPasswordSelectorKeyName := "test-password-selector"
testSecretName := "test-secret"
@@ -167,7 +167,7 @@ func TestSMTP_Send(t *testing.T) {
},
}}
- ts := &testServer{event: event}
+ ts := &testServer{event: e}
// Create fake SMTP server
@@ -205,7 +205,7 @@ func TestSMTP_Send(t *testing.T) {
assert.NoError(t, err)
}()
- err = smtpClient.Send(event)
+ err = smtpClient.Send(e)
assert.NoError(t, err)
}
@@ -231,7 +231,7 @@ func TestGenerateMessage(t *testing.T) {
Reason: res,
}
s := SMTP{
- k8sClient: fake.NewFakeClient(),
+ k8sClient: fake.NewClientBuilder().Build(),
config: v1alpha2.Notification{
LoggingLevel: level,
SMTP: &v1alpha2.SMTP{
@@ -264,7 +264,7 @@ func TestGenerateMessage(t *testing.T) {
Reason: res,
}
s := SMTP{
- k8sClient: fake.NewFakeClient(),
+ k8sClient: fake.NewClientBuilder().Build(),
config: v1alpha2.Notification{
LoggingLevel: level,
SMTP: &v1alpha2.SMTP{
@@ -297,7 +297,7 @@ func TestGenerateMessage(t *testing.T) {
Reason: res,
}
s := SMTP{
- k8sClient: fake.NewFakeClient(),
+ k8sClient: fake.NewClientBuilder().Build(),
config: v1alpha2.Notification{
LoggingLevel: level,
SMTP: &v1alpha2.SMTP{
diff --git a/pkg/plugins/base_plugins.go b/pkg/plugins/base_plugins.go
index 21b83ba6..7f8a761b 100644
--- a/pkg/plugins/base_plugins.go
+++ b/pkg/plugins/base_plugins.go
@@ -1,11 +1,11 @@
package plugins
const (
- configurationAsCodePlugin = "configuration-as-code:1.46"
+ configurationAsCodePlugin = "configuration-as-code:1.47"
gitPlugin = "git:4.5.0"
jobDslPlugin = "job-dsl:1.77"
kubernetesCredentialsProviderPlugin = "kubernetes-credentials-provider:0.15"
- kubernetesPlugin = "kubernetes:1.28.6"
+ kubernetesPlugin = "kubernetes:1.29.0"
workflowAggregatorPlugin = "workflow-aggregator:2.6"
workflowJobPlugin = "workflow-job:2.40"
)
diff --git a/test/e2e/configuration_test.go b/test/e2e/configuration_test.go
index 81a1e033..aa759670 100644
--- a/test/e2e/configuration_test.go
+++ b/test/e2e/configuration_test.go
@@ -3,10 +3,8 @@ package e2e
import (
"context"
"fmt"
- "testing"
- "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"
"github.com/jenkinsci/kubernetes-operator/pkg/configuration/base/resources"
@@ -15,9 +13,8 @@ import (
"github.com/jenkinsci/kubernetes-operator/pkg/plugins"
"github.com/bndr/gojenkins"
- framework "github.com/operator-framework/operator-sdk/pkg/test"
- "github.com/stretchr/testify/assert"
- "github.com/stretchr/testify/require"
+ . "github.com/onsi/ginkgo"
+ . "github.com/onsi/gomega"
corev1 "k8s.io/api/core/v1"
"k8s.io/apimachinery/pkg/api/resource"
metav1 "k8s.io/apimachinery/pkg/apis/meta/v1"
@@ -26,147 +23,9 @@ import (
const e2e = "e2e"
-func TestConfiguration(t *testing.T) {
- t.Parallel()
- namespace, ctx := setupTest(t)
+func createUserConfigurationSecret(namespace string, stringData map[string]string) {
+ By("creating user configuration secret")
- defer showLogsAndCleanup(t, ctx)
-
- jenkinsCRName := e2e
- numberOfExecutors := 6
- numberOfExecutorsEnvName := "NUMBER_OF_EXECUTORS"
- systemMessage := "Configuration as Code integration works!!!"
- systemMessageEnvName := "SYSTEM_MESSAGE"
- priorityClassName := ""
- mySeedJob := seedJobConfig{
- SeedJob: v1alpha2.SeedJob{
- ID: "jenkins-operator",
- CredentialID: "jenkins-operator",
- JenkinsCredentialType: v1alpha2.NoJenkinsCredentialCredentialType,
- Targets: "cicd/jobs/*.jenkins",
- Description: "Jenkins Operator repository",
- RepositoryBranch: "master",
- RepositoryURL: "https://github.com/jenkinsci/kubernetes-operator.git",
- PollSCM: "1 1 1 1 1",
- UnstableOnDeprecation: true,
- BuildPeriodically: "1 1 1 1 1",
- FailOnMissingPlugin: true,
- IgnoreMissingFiles: true,
- //AdditionalClasspath: can fail with the seed job agent
- GitHubPushTrigger: true,
- },
- }
- groovyScripts := v1alpha2.GroovyScripts{
- Customization: v1alpha2.Customization{
- Configurations: []v1alpha2.ConfigMapRef{
- {
- Name: userConfigurationConfigMapName,
- },
- },
- Secret: v1alpha2.SecretRef{
- Name: userConfigurationSecretName,
- },
- },
- }
-
- casc := v1alpha2.ConfigurationAsCode{
- Customization: v1alpha2.Customization{
- Configurations: []v1alpha2.ConfigMapRef{
- {
- Name: userConfigurationConfigMapName,
- },
- },
- Secret: v1alpha2.SecretRef{
- Name: userConfigurationSecretName,
- },
- },
- }
-
- stringData := make(map[string]string)
- stringData[systemMessageEnvName] = systemMessage
- stringData[numberOfExecutorsEnvName] = fmt.Sprintf("%d", numberOfExecutors)
-
- // base
- createUserConfigurationSecret(t, namespace, stringData)
- createUserConfigurationConfigMap(t, namespace, numberOfExecutorsEnvName, fmt.Sprintf("${%s}", systemMessageEnvName))
- jenkins := createJenkinsCR(t, jenkinsCRName, namespace, &[]v1alpha2.SeedJob{mySeedJob.SeedJob}, groovyScripts, casc, priorityClassName)
- createDefaultLimitsForContainersInNamespace(t, namespace)
- createKubernetesCredentialsProviderSecret(t, namespace, mySeedJob)
- waitForJenkinsBaseConfigurationToComplete(t, jenkins)
- verifyJenkinsMasterPodAttributes(t, jenkins)
- verifyServices(t, jenkins)
- jenkinsClient, cleanUpFunc := verifyJenkinsAPIConnection(t, jenkins, namespace)
- defer cleanUpFunc()
- verifyPlugins(t, jenkinsClient, jenkins)
-
- // user
- waitForJenkinsUserConfigurationToComplete(t, jenkins)
- verifyUserConfiguration(t, jenkinsClient, numberOfExecutors, systemMessage)
- verifyJenkinsSeedJobs(t, jenkinsClient, []seedJobConfig{mySeedJob})
-}
-
-func TestPlugins(t *testing.T) {
- t.Parallel()
- namespace, ctx := setupTest(t)
- // Deletes test namespace
- defer showLogsAndCleanup(t, ctx)
-
- jobID := "k8s-e2e"
-
- priorityClassName := ""
- seedJobs := &[]v1alpha2.SeedJob{
- {
- ID: "jenkins-operator",
- CredentialID: "jenkins-operator",
- JenkinsCredentialType: v1alpha2.NoJenkinsCredentialCredentialType,
- Targets: "cicd/jobs/k8s.jenkins",
- Description: "Jenkins Operator repository",
- RepositoryBranch: "master",
- RepositoryURL: "https://github.com/jenkinsci/kubernetes-operator.git",
- },
- }
-
- jenkins := createJenkinsCR(t, "k8s-e2e", namespace, seedJobs, v1alpha2.GroovyScripts{}, v1alpha2.ConfigurationAsCode{}, priorityClassName)
- waitForJenkinsUserConfigurationToComplete(t, jenkins)
-
- jenkinsClient, cleanUpFunc := verifyJenkinsAPIConnection(t, jenkins, namespace)
- defer cleanUpFunc()
- waitForJob(t, jenkinsClient, jobID)
- job, err := jenkinsClient.GetJob(jobID)
-
- require.NoError(t, err, job)
- i, err := job.InvokeSimple(map[string]string{})
- require.NoError(t, err, i)
- // FIXME: waitForJobToFinish use
- time.Sleep(100 * time.Second) // wait for the build to complete
-
- job, err = jenkinsClient.GetJob(jobID)
- require.NoError(t, err, job)
- build, err := job.GetLastBuild()
- require.NoError(t, err)
- assert.True(t, build.IsGood())
-}
-
-func TestPriorityClass(t *testing.T) {
- if skipTestPriorityClass {
- t.Skip()
- }
- t.Parallel()
- namespace, ctx := setupTest(t)
- defer showLogsAndCleanup(t, ctx)
-
- jenkinsCRName := "k8s-ete-priority-class-existing"
- // One of the existing priority classes
- priorityClassName := "system-cluster-critical"
-
- jenkins := createJenkinsCR(t, jenkinsCRName, namespace, nil, v1alpha2.GroovyScripts{}, v1alpha2.ConfigurationAsCode{}, priorityClassName)
- waitForJenkinsBaseConfigurationToComplete(t, jenkins)
- verifyJenkinsMasterPodAttributes(t, jenkins)
- _, cleanUpFunc := verifyJenkinsAPIConnection(t, jenkins, namespace)
- defer cleanUpFunc()
-}
-
-func createUserConfigurationSecret(t *testing.T, namespace string, stringData map[string]string) {
userConfiguration := &corev1.Secret{
ObjectMeta: metav1.ObjectMeta{
Name: userConfigurationSecretName,
@@ -175,13 +34,13 @@ func createUserConfigurationSecret(t *testing.T, namespace string, stringData ma
StringData: stringData,
}
- t.Logf("User configuration secret %+v", *userConfiguration)
- if err := framework.Global.Client.Create(context.TODO(), userConfiguration, nil); err != nil {
- t.Fatal(err)
- }
+ _, _ = fmt.Fprintf(GinkgoWriter, "User configuration secret %+v\n", *userConfiguration)
+ Expect(k8sClient.Create(context.TODO(), userConfiguration)).Should(Succeed())
}
-func createUserConfigurationConfigMap(t *testing.T, namespace string, numberOfExecutorsSecretKeyName string, systemMessage string) {
+func createUserConfigurationConfigMap(namespace string, numberOfExecutorsSecretKeyName string, systemMessage string) {
+ By("creating user configuration config map")
+
userConfiguration := &corev1.ConfigMap{
ObjectMeta: metav1.ObjectMeta{
Name: userConfigurationConfigMapName,
@@ -203,13 +62,13 @@ unclassified:
},
}
- t.Logf("User configuration %+v", *userConfiguration)
- if err := framework.Global.Client.Create(context.TODO(), userConfiguration, nil); err != nil {
- t.Fatal(err)
- }
+ _, _ = fmt.Fprintf(GinkgoWriter, "User configuration %+v\n", *userConfiguration)
+ Expect(k8sClient.Create(context.TODO(), userConfiguration)).Should(Succeed())
}
-func createDefaultLimitsForContainersInNamespace(t *testing.T, namespace string) {
+func createDefaultLimitsForContainersInNamespace(namespace string) {
+ By("creating default limits for containers in namespace")
+
limitRange := &corev1.LimitRange{
ObjectMeta: metav1.ObjectMeta{
Name: e2e,
@@ -232,31 +91,34 @@ func createDefaultLimitsForContainersInNamespace(t *testing.T, namespace string)
},
}
- t.Logf("LimitRange %+v", *limitRange)
- if err := framework.Global.Client.Create(context.TODO(), limitRange, nil); err != nil {
- t.Fatal(err)
- }
+ _, _ = fmt.Fprintf(GinkgoWriter, "LimitRange %+v\n", *limitRange)
+ Expect(k8sClient.Create(context.TODO(), limitRange)).Should(Succeed())
}
-func verifyJenkinsMasterPodAttributes(t *testing.T, jenkins *v1alpha2.Jenkins) {
- jenkinsPod := getJenkinsMasterPod(t, jenkins)
- jenkins = getJenkins(t, jenkins.Namespace, jenkins.Name)
+func verifyJenkinsMasterPodAttributes(jenkins *v1alpha2.Jenkins) {
+ By("creating Jenkins master pod properly")
- assertMapContainsElementsFromAnotherMap(t, jenkins.Spec.Master.Annotations, jenkinsPod.ObjectMeta.Annotations)
- assert.Equal(t, jenkins.Spec.Master.NodeSelector, jenkinsPod.Spec.NodeSelector)
+ jenkinsPod := getJenkinsMasterPod(jenkins)
+ jenkins = getJenkins(jenkins.Namespace, jenkins.Name)
- assert.Equal(t, resources.JenkinsMasterContainerName, jenkinsPod.Spec.Containers[0].Name)
- assert.Equal(t, len(jenkins.Spec.Master.Containers), len(jenkinsPod.Spec.Containers))
+ assertMapContainsElementsFromAnotherMap(jenkins.Spec.Master.Annotations, jenkinsPod.ObjectMeta.Annotations)
+ Expect(jenkinsPod.Spec.NodeSelector).Should(Equal(jenkins.Spec.Master.NodeSelector))
- assert.Equal(t, jenkins.Spec.Master.SecurityContext, jenkinsPod.Spec.SecurityContext)
- assert.Equal(t, jenkins.Spec.Master.Containers[0].Command, jenkinsPod.Spec.Containers[0].Command)
+ Expect(jenkinsPod.Spec.Containers[0].Name).Should(Equal(resources.JenkinsMasterContainerName))
+ Expect(jenkinsPod.Spec.Containers).Should(HaveLen(len(jenkins.Spec.Master.Containers)))
- assert.Equal(t, resources.GetJenkinsMasterPodLabels(*jenkins), jenkinsPod.Labels)
- assert.Equal(t, jenkins.Spec.Master.PriorityClassName, jenkinsPod.Spec.PriorityClassName)
+ if jenkins.Spec.Master.SecurityContext == nil {
+ jenkins.Spec.Master.SecurityContext = &corev1.PodSecurityContext{}
+ }
+ Expect(jenkinsPod.Spec.SecurityContext).Should(Equal(jenkins.Spec.Master.SecurityContext))
+ Expect(jenkinsPod.Spec.Containers[0].Command).Should(Equal(jenkins.Spec.Master.Containers[0].Command))
+
+ Expect(jenkinsPod.Labels).Should(Equal(resources.GetJenkinsMasterPodLabels(*jenkins)))
+ Expect(jenkinsPod.Spec.PriorityClassName).Should(Equal(jenkins.Spec.Master.PriorityClassName))
for _, actualContainer := range jenkinsPod.Spec.Containers {
if actualContainer.Name == resources.JenkinsMasterContainerName {
- verifyContainer(t, resources.NewJenkinsMasterContainer(jenkins), actualContainer)
+ verifyContainer(resources.NewJenkinsMasterContainer(jenkins), actualContainer)
continue
}
@@ -269,11 +131,11 @@ func verifyJenkinsMasterPodAttributes(t *testing.T, jenkins *v1alpha2.Jenkins) {
}
if expectedContainer == nil {
- t.Errorf("Container '%+v' not found in pod", actualContainer)
+ Fail(fmt.Sprintf("Container '%+v' not found in pod", actualContainer))
continue
}
- verifyContainer(t, *expectedContainer, actualContainer)
+ verifyContainer(*expectedContainer, actualContainer)
}
for _, expectedVolume := range jenkins.Spec.Master.Volumes {
@@ -281,58 +143,54 @@ func verifyJenkinsMasterPodAttributes(t *testing.T, jenkins *v1alpha2.Jenkins) {
for _, actualVolume := range jenkinsPod.Spec.Volumes {
if expectedVolume.Name == actualVolume.Name {
volumeFound = true
- assert.Equal(t, expectedVolume, actualVolume)
+ Expect(actualVolume).Should(Equal(expectedVolume))
}
}
if !volumeFound {
- t.Errorf("Missing volume '+%v', actaul volumes '%+v'", expectedVolume, jenkinsPod.Spec.Volumes)
+ Fail(fmt.Sprintf("Missing volume '+%v', actaul volumes '%+v'", expectedVolume, jenkinsPod.Spec.Volumes))
}
}
-
- t.Log("Jenkins pod attributes are valid")
}
-func verifyContainer(t *testing.T, expected corev1.Container, actual corev1.Container) {
- assert.Equal(t, expected.Args, actual.Args, expected.Name, expected.Name)
- assert.Equal(t, expected.Command, actual.Command, expected.Name)
- assert.ElementsMatch(t, expected.Env, actual.Env, expected.Name)
- assert.Equal(t, expected.EnvFrom, actual.EnvFrom, expected.Name)
- assert.Equal(t, expected.Image, actual.Image, expected.Name)
- assert.Equal(t, expected.ImagePullPolicy, actual.ImagePullPolicy, expected.Name)
- assert.Equal(t, expected.Lifecycle, actual.Lifecycle, expected.Name)
- assert.Equal(t, expected.LivenessProbe, actual.LivenessProbe, expected.Name)
- assert.Equal(t, expected.Ports, actual.Ports, expected.Name)
- assert.Equal(t, expected.ReadinessProbe, actual.ReadinessProbe, expected.Name)
- assert.Equal(t, expected.Resources, actual.Resources, expected.Name)
- assert.Equal(t, expected.SecurityContext, actual.SecurityContext, expected.Name)
- assert.Equal(t, expected.WorkingDir, actual.WorkingDir, expected.Name)
+func verifyContainer(expected corev1.Container, actual corev1.Container) {
+ Expect(actual.Args).Should(Equal(expected.Args), expected.Name)
+ Expect(actual.Command).Should(Equal(expected.Command), expected.Name)
+ Expect(actual.Env).Should(ConsistOf(expected.Env), expected.Name)
+ Expect(actual.EnvFrom).Should(Equal(expected.EnvFrom), expected.Name)
+ Expect(actual.Image).Should(Equal(expected.Image), expected.Name)
+ Expect(actual.ImagePullPolicy).Should(Equal(expected.ImagePullPolicy), expected.Name)
+ Expect(actual.Lifecycle).Should(Equal(expected.Lifecycle), expected.Name)
+ Expect(actual.LivenessProbe).Should(Equal(expected.LivenessProbe), expected.Name)
+ Expect(actual.Ports).Should(Equal(expected.Ports), expected.Name)
+ Expect(actual.ReadinessProbe).Should(Equal(expected.ReadinessProbe), expected.Name)
+ Expect(actual.Resources).Should(Equal(expected.Resources), expected.Name)
+ Expect(actual.SecurityContext).Should(Equal(expected.SecurityContext), expected.Name)
+ Expect(actual.WorkingDir).Should(Equal(expected.WorkingDir), expected.Name)
if !base.CompareContainerVolumeMounts(expected, actual) {
- t.Errorf("Volume mounts are different in container '%s': expected '%+v', actual '%+v'",
- expected.Name, expected.VolumeMounts, actual.VolumeMounts)
+ Fail(fmt.Sprintf("Volume mounts are different in container '%s': expected '%+v', actual '%+v'",
+ expected.Name, expected.VolumeMounts, expected.VolumeMounts))
}
}
-func verifyPlugins(t *testing.T, jenkinsClient jenkinsclient.Jenkins, jenkins *v1alpha2.Jenkins) {
+func verifyPlugins(jenkinsClient jenkinsclient.Jenkins, jenkins *v1alpha2.Jenkins) {
+ By("installing plugins in Jenkins instance")
+
installedPlugins, err := jenkinsClient.GetPlugins(1)
- if err != nil {
- t.Fatal(err)
- }
+ Expect(err).NotTo(HaveOccurred())
for _, basePlugin := range plugins.BasePlugins() {
if found, ok := isPluginValid(installedPlugins, basePlugin); !ok {
- t.Fatalf("Invalid plugin '%s', actual '%+v'", basePlugin, found)
+ Fail(fmt.Sprintf("Invalid plugin '%s', actual '%+v'", basePlugin, found))
}
}
for _, userPlugin := range jenkins.Spec.Master.Plugins {
plugin := plugins.Plugin{Name: userPlugin.Name, Version: userPlugin.Version}
if found, ok := isPluginValid(installedPlugins, plugin); !ok {
- t.Fatalf("Invalid plugin '%s', actual '%+v'", plugin, found)
+ Fail(fmt.Sprintf("Invalid plugin '%s', actual '%+v'", plugin, found))
}
}
-
- t.Log("All plugins have been installed")
}
func isPluginValid(plugins *gojenkins.Plugins, requiredPlugin plugins.Plugin) (*gojenkins.Plugin, bool) {
@@ -348,13 +206,15 @@ func isPluginValid(plugins *gojenkins.Plugins, requiredPlugin plugins.Plugin) (*
return p, requiredPlugin.Version == p.Version
}
-func verifyUserConfiguration(t *testing.T, jenkinsClient jenkinsclient.Jenkins, amountOfExecutors int, systemMessage string) {
+func verifyUserConfiguration(jenkinsClient jenkinsclient.Jenkins, amountOfExecutors int, systemMessage string) {
+ By("configuring Jenkins by groovy scripts")
+
checkConfigurationViaGroovyScript := fmt.Sprintf(`
if (!new Integer(%d).equals(Jenkins.instance.numExecutors)) {
throw new Exception("Configuration via groovy scripts failed")
}`, amountOfExecutors)
logs, err := jenkinsClient.ExecuteScript(checkConfigurationViaGroovyScript)
- assert.NoError(t, err, logs)
+ Expect(err).NotTo(HaveOccurred(), logs)
checkSecretLoaderViaGroovyScript := fmt.Sprintf(`
if (!new Integer(%d).equals(new Integer(secrets['NUMBER_OF_EXECUTORS']))) {
@@ -363,30 +223,33 @@ if (!new Integer(%d).equals(new Integer(secrets['NUMBER_OF_EXECUTORS']))) {
loader := groovy.AddSecretsLoaderToGroovyScript("/var/jenkins/groovy-scripts-secrets")
logs, err = jenkinsClient.ExecuteScript(loader(checkSecretLoaderViaGroovyScript))
- assert.NoError(t, err, logs)
+ Expect(err).NotTo(HaveOccurred(), logs)
+ By("configuring Jenkins by CasC")
checkConfigurationAsCode := fmt.Sprintf(`
if (!"%s".equals(Jenkins.instance.systemMessage)) {
throw new Exception("Configuration as code failed")
}`, systemMessage)
logs, err = jenkinsClient.ExecuteScript(checkConfigurationAsCode)
- assert.NoError(t, err, logs)
+ Expect(err).NotTo(HaveOccurred(), logs)
}
-func verifyServices(t *testing.T, jenkins *v1alpha2.Jenkins) {
- jenkinsHTTPService := getJenkinsService(t, jenkins, "http")
- jenkinsSlaveService := getJenkinsService(t, jenkins, "slave")
- assert.Equal(t, intstr.IntOrString{IntVal: constants.DefaultHTTPPortInt32, Type: intstr.Int}, jenkinsHTTPService.Spec.Ports[0].TargetPort)
- assert.Equal(t, intstr.IntOrString{IntVal: constants.DefaultSlavePortInt32, Type: intstr.Int}, jenkinsSlaveService.Spec.Ports[0].TargetPort)
+func verifyServices(jenkins *v1alpha2.Jenkins) {
+ By("creating Jenkins services properly")
+
+ jenkinsHTTPService := getJenkinsService(jenkins, "http")
+ jenkinsSlaveService := getJenkinsService(jenkins, "slave")
+ Expect(jenkinsHTTPService.Spec.Ports[0].TargetPort).Should(Equal(intstr.IntOrString{IntVal: constants.DefaultHTTPPortInt32, Type: intstr.Int}))
+ Expect(jenkinsSlaveService.Spec.Ports[0].TargetPort).Should(Equal(intstr.IntOrString{IntVal: constants.DefaultSlavePortInt32, Type: intstr.Int}))
}
-func assertMapContainsElementsFromAnotherMap(t *testing.T, expected map[string]string, actual map[string]string) {
+func assertMapContainsElementsFromAnotherMap(expected map[string]string, actual map[string]string) {
for key, expectedValue := range expected {
actualValue, keyExists := actual[key]
if !keyExists {
- assert.Failf(t, "key '%s' doesn't exist in map '%+v'", key, actual)
+ Fail(fmt.Sprintf("key '%s' doesn't exist in map '%+v'", key, actual))
continue
}
- assert.Equal(t, expectedValue, actualValue, expected, actual)
+ Expect(actualValue).Should(Equal(expectedValue), key, expected, actual)
}
}
diff --git a/test/e2e/helm_test.go b/test/e2e/helm_test.go
index 6637bdac..dd915044 100644
--- a/test/e2e/helm_test.go
+++ b/test/e2e/helm_test.go
@@ -2,6 +2,8 @@
package e2e
+// TODO
+/*
import (
"fmt"
"os/exec"
@@ -66,3 +68,4 @@ func TestDeployHelmChart(t *testing.T) {
waitForJenkinsBaseConfigurationToComplete(t, jenkins)
waitForJenkinsUserConfigurationToComplete(t, jenkins)
}
+*/
diff --git a/test/e2e/jenkins.go b/test/e2e/jenkins.go
deleted file mode 100644
index 73a118b0..00000000
--- a/test/e2e/jenkins.go
+++ /dev/null
@@ -1,253 +0,0 @@
-package e2e
-
-import (
- "context"
- "fmt"
- "testing"
-
- "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"
- "github.com/jenkinsci/kubernetes-operator/pkg/configuration/base/resources"
- "github.com/jenkinsci/kubernetes-operator/pkg/constants"
-
- framework "github.com/operator-framework/operator-sdk/pkg/test"
- "github.com/stretchr/testify/require"
- corev1 "k8s.io/api/core/v1"
- rbacv1 "k8s.io/api/rbac/v1"
- metav1 "k8s.io/apimachinery/pkg/apis/meta/v1"
- "k8s.io/apimachinery/pkg/fields"
- "k8s.io/apimachinery/pkg/labels"
- "k8s.io/apimachinery/pkg/types"
- "k8s.io/apimachinery/pkg/util/intstr"
- "k8s.io/client-go/kubernetes"
-)
-
-const (
- userConfigurationConfigMapName = "user-config"
- userConfigurationSecretName = "user-secret"
-)
-
-func getJenkins(t *testing.T, namespace, name string) *v1alpha2.Jenkins {
- jenkins := &v1alpha2.Jenkins{}
- namespaceName := types.NamespacedName{Namespace: namespace, Name: name}
- if err := framework.Global.Client.Get(context.TODO(), namespaceName, jenkins); err != nil {
- t.Fatal(err)
- }
-
- return jenkins
-}
-
-func getJenkinsMasterPod(t *testing.T, jenkins *v1alpha2.Jenkins) *corev1.Pod {
- lo := metav1.ListOptions{
- LabelSelector: labels.SelectorFromSet(resources.GetJenkinsMasterPodLabels(*jenkins)).String(),
- }
- podList, err := framework.Global.KubeClient.CoreV1().Pods(jenkins.ObjectMeta.Namespace).List(lo)
- if err != nil {
- t.Fatal(err)
- }
- if len(podList.Items) != 1 {
- t.Fatalf("Jenkins pod not found, pod list: %+v", podList)
- }
- return &podList.Items[0]
-}
-
-func createJenkinsCR(t *testing.T, name, namespace string, seedJob *[]v1alpha2.SeedJob, groovyScripts v1alpha2.GroovyScripts, casc v1alpha2.ConfigurationAsCode, priorityClassName string) *v1alpha2.Jenkins {
- var seedJobs []v1alpha2.SeedJob
- if seedJob != nil {
- seedJobs = append(seedJobs, *seedJob...)
- }
-
- jenkins := &v1alpha2.Jenkins{
- TypeMeta: v1alpha2.JenkinsTypeMeta(),
- ObjectMeta: metav1.ObjectMeta{
- Name: name,
- Namespace: namespace,
- },
- Spec: v1alpha2.JenkinsSpec{
- GroovyScripts: groovyScripts,
- ConfigurationAsCode: casc,
- Master: v1alpha2.JenkinsMaster{
- Annotations: map[string]string{"test": "label"},
- Containers: []v1alpha2.Container{
- {
- Name: resources.JenkinsMasterContainerName,
- Env: []corev1.EnvVar{
- {
- Name: "TEST_ENV",
- Value: "test_env_value",
- },
- },
- ReadinessProbe: &corev1.Probe{
- Handler: corev1.Handler{
- HTTPGet: &corev1.HTTPGetAction{
- Path: "/login",
- Port: intstr.FromString("http"),
- Scheme: corev1.URISchemeHTTP,
- },
- },
- InitialDelaySeconds: int32(80),
- TimeoutSeconds: int32(4),
- FailureThreshold: int32(10),
- },
- LivenessProbe: &corev1.Probe{
- Handler: corev1.Handler{
- HTTPGet: &corev1.HTTPGetAction{
- Path: "/login",
- Port: intstr.FromString("http"),
- Scheme: corev1.URISchemeHTTP,
- },
- },
- InitialDelaySeconds: int32(80),
- TimeoutSeconds: int32(4),
- FailureThreshold: int32(10),
- },
- VolumeMounts: []corev1.VolumeMount{
- {
- Name: "plugins-cache",
- MountPath: "/usr/share/jenkins/ref/plugins",
- },
- },
- },
- {
- Name: "envoyproxy",
- Image: "envoyproxy/envoy-alpine:v1.14.1",
- },
- },
- Plugins: []v1alpha2.Plugin{
- {Name: "audit-trail", Version: "3.7"},
- {Name: "simple-theme-plugin", Version: "0.6"},
- {Name: "github", Version: "1.32.0"},
- {Name: "devoptics", Version: "1.1905", DownloadURL: "https://jenkins-updates.cloudbees.com/download/plugins/devoptics/1.1905/devoptics.hpi"},
- },
- PriorityClassName: priorityClassName,
- NodeSelector: map[string]string{"kubernetes.io/os": "linux"},
- Volumes: []corev1.Volume{
- {
- Name: "plugins-cache",
- VolumeSource: corev1.VolumeSource{
- EmptyDir: &corev1.EmptyDirVolumeSource{},
- },
- },
- },
- },
- SeedJobs: seedJobs,
- Service: v1alpha2.Service{
- Type: corev1.ServiceTypeNodePort,
- Port: constants.DefaultHTTPPortInt32,
- },
- },
- }
- jenkins.Spec.Roles = []rbacv1.RoleRef{
- {
- APIGroup: "rbac.authorization.k8s.io",
- Kind: "Role",
- Name: resources.GetResourceName(jenkins),
- },
- }
- updateJenkinsCR(t, jenkins)
-
- t.Logf("Jenkins CR %+v", *jenkins)
- if err := framework.Global.Client.Create(context.TODO(), jenkins, nil); err != nil {
- t.Fatal(err)
- }
-
- return jenkins
-}
-
-func createJenkinsAPIClientFromServiceAccount(t *testing.T, jenkins *v1alpha2.Jenkins, jenkinsAPIURL string) (jenkinsclient.Jenkins, error) {
- t.Log("Creating Jenkins API client from service account")
- podName := resources.GetJenkinsMasterPodName(jenkins)
-
- clientSet, err := kubernetes.NewForConfig(framework.Global.KubeConfig)
- if err != nil {
- return nil, err
- }
- config := configuration.Configuration{Jenkins: jenkins, ClientSet: *clientSet, Config: framework.Global.KubeConfig}
- r := base.New(config, jenkinsclient.JenkinsAPIConnectionSettings{})
-
- token, _, err := r.Configuration.Exec(podName, resources.JenkinsMasterContainerName, []string{"cat", "/var/run/secrets/kubernetes.io/serviceaccount/token"})
- if err != nil {
- return nil, err
- }
-
- return jenkinsclient.NewBearerTokenAuthorization(jenkinsAPIURL, token.String())
-}
-
-func createJenkinsAPIClientFromSecret(t *testing.T, jenkins *v1alpha2.Jenkins, jenkinsAPIURL string) (jenkinsclient.Jenkins, error) {
- t.Log("Creating Jenkins API client from secret")
-
- adminSecret := &corev1.Secret{}
- namespaceName := types.NamespacedName{Namespace: jenkins.Namespace, Name: resources.GetOperatorCredentialsSecretName(jenkins)}
- if err := framework.Global.Client.Get(context.TODO(), namespaceName, adminSecret); err != nil {
- return nil, err
- }
-
- return jenkinsclient.NewUserAndPasswordAuthorization(
- jenkinsAPIURL,
- string(adminSecret.Data[resources.OperatorCredentialsSecretUserNameKey]),
- string(adminSecret.Data[resources.OperatorCredentialsSecretTokenKey]),
- )
-}
-
-func verifyJenkinsAPIConnection(t *testing.T, jenkins *v1alpha2.Jenkins, namespace string) (jenkinsclient.Jenkins, func()) {
- var service corev1.Service
- err := framework.Global.Client.Get(context.TODO(), types.NamespacedName{
- Namespace: jenkins.Namespace,
- Name: resources.GetJenkinsHTTPServiceName(jenkins),
- }, &service)
- require.NoError(t, err)
-
- podName := resources.GetJenkinsMasterPodName(jenkins)
- port, cleanUpFunc, waitFunc, portForwardFunc, err := setupPortForwardToPod(t, namespace, podName, int(constants.DefaultHTTPPortInt32))
- if err != nil {
- t.Fatal(err)
- }
- go portForwardFunc()
- waitFunc()
-
- jenkinsAPIURL := jenkinsclient.JenkinsAPIConnectionSettings{
- Hostname: "localhost",
- Port: port,
- UseNodePort: false,
- }.BuildJenkinsAPIUrl(service.Name, service.Namespace, service.Spec.Ports[0].Port, service.Spec.Ports[0].NodePort)
-
- var client jenkinsclient.Jenkins
- if jenkins.Spec.JenkinsAPISettings.AuthorizationStrategy == v1alpha2.ServiceAccountAuthorizationStrategy {
- client, err = createJenkinsAPIClientFromServiceAccount(t, jenkins, jenkinsAPIURL)
- } else {
- client, err = createJenkinsAPIClientFromSecret(t, jenkins, jenkinsAPIURL)
- }
-
- if err != nil {
- defer cleanUpFunc()
- t.Fatal(err)
- }
-
- t.Log("I can establish connection to Jenkins API")
- return client, cleanUpFunc
-}
-
-func restartJenkinsMasterPod(t *testing.T, jenkins *v1alpha2.Jenkins) {
- t.Log("Restarting Jenkins master pod")
- jenkinsPod := getJenkinsMasterPod(t, jenkins)
- err := framework.Global.Client.Delete(context.TODO(), jenkinsPod)
- if err != nil {
- t.Fatal(err)
- }
- t.Log("Jenkins master pod has been restarted")
-}
-
-func getJenkinsService(t *testing.T, jenkins *v1alpha2.Jenkins, serviceKind string) *corev1.Service {
- serviceName := constants.OperatorName + "-" + serviceKind + "-" + jenkins.ObjectMeta.Name
- lo := metav1.ListOptions{
- FieldSelector: fields.SelectorFromSet(fields.Set{"metadata.name": serviceName}).String(),
- }
- serviceList, err := framework.Global.KubeClient.CoreV1().Services(jenkins.Namespace).List(lo)
-
- require.NoError(t, err)
- require.Equal(t, 1, len(serviceList.Items), fmt.Sprintf("'%s' service not found", serviceName))
-
- return &serviceList.Items[0]
-}
diff --git a/test/e2e/jenkins_configuration_test.go b/test/e2e/jenkins_configuration_test.go
new file mode 100644
index 00000000..dbaa9aeb
--- /dev/null
+++ b/test/e2e/jenkins_configuration_test.go
@@ -0,0 +1,196 @@
+package e2e
+
+import (
+ "fmt"
+
+ "github.com/jenkinsci/kubernetes-operator/api/v1alpha2"
+
+ . "github.com/onsi/ginkgo"
+ corev1 "k8s.io/api/core/v1"
+ // +kubebuilder:scaffold:imports
+)
+
+var _ = Describe("Jenkins controller configuration", func() {
+
+ const (
+ jenkinsCRName = e2e
+ numberOfExecutors = 6
+ numberOfExecutorsEnvName = "NUMBER_OF_EXECUTORS"
+ systemMessage = "Configuration as Code integration works!!!"
+ systemMessageEnvName = "SYSTEM_MESSAGE"
+ priorityClassName = ""
+ )
+
+ var (
+ namespace *corev1.Namespace
+ jenkins *v1alpha2.Jenkins
+ mySeedJob = seedJobConfig{
+ SeedJob: v1alpha2.SeedJob{
+ ID: "jenkins-operator",
+ CredentialID: "jenkins-operator",
+ JenkinsCredentialType: v1alpha2.NoJenkinsCredentialCredentialType,
+ Targets: "cicd/jobs/*.jenkins",
+ Description: "Jenkins Operator repository",
+ RepositoryBranch: "master",
+ RepositoryURL: "https://github.com/jenkinsci/kubernetes-operator.git",
+ PollSCM: "1 1 1 1 1",
+ UnstableOnDeprecation: true,
+ BuildPeriodically: "1 1 1 1 1",
+ FailOnMissingPlugin: true,
+ IgnoreMissingFiles: true,
+ //AdditionalClasspath: can fail with the seed job agent
+ GitHubPushTrigger: true,
+ },
+ }
+ groovyScripts = v1alpha2.GroovyScripts{
+ Customization: v1alpha2.Customization{
+ Configurations: []v1alpha2.ConfigMapRef{
+ {
+ Name: userConfigurationConfigMapName,
+ },
+ },
+ Secret: v1alpha2.SecretRef{
+ Name: userConfigurationSecretName,
+ },
+ },
+ }
+ casc = v1alpha2.ConfigurationAsCode{
+ Customization: v1alpha2.Customization{
+ Configurations: []v1alpha2.ConfigMapRef{
+ {
+ Name: userConfigurationConfigMapName,
+ },
+ },
+ Secret: v1alpha2.SecretRef{
+ Name: userConfigurationSecretName,
+ },
+ },
+ }
+ userConfigurationSecretData = map[string]string{
+ systemMessageEnvName: systemMessage,
+ numberOfExecutorsEnvName: fmt.Sprintf("%d", numberOfExecutors),
+ }
+ )
+
+ BeforeEach(func() {
+ namespace = createNamespace()
+
+ createUserConfigurationSecret(namespace.Name, userConfigurationSecretData)
+ createUserConfigurationConfigMap(namespace.Name, numberOfExecutorsEnvName, fmt.Sprintf("${%s}", systemMessageEnvName))
+ jenkins = createJenkinsCR(jenkinsCRName, namespace.Name, &[]v1alpha2.SeedJob{mySeedJob.SeedJob}, groovyScripts, casc, priorityClassName)
+ createDefaultLimitsForContainersInNamespace(namespace.Name)
+ createKubernetesCredentialsProviderSecret(namespace.Name, mySeedJob)
+ })
+
+ AfterEach(func() {
+ destroyNamespace(namespace)
+ })
+
+ Context("when deploying CR to cluster", func() {
+ It("creates Jenkins instance and configures it", func() {
+ waitForJenkinsBaseConfigurationToComplete(jenkins)
+ verifyJenkinsMasterPodAttributes(jenkins)
+ verifyServices(jenkins)
+ jenkinsClient, cleanUpFunc := verifyJenkinsAPIConnection(jenkins, namespace.Name)
+ defer cleanUpFunc()
+ verifyPlugins(jenkinsClient, jenkins)
+ waitForJenkinsUserConfigurationToComplete(jenkins)
+ verifyUserConfiguration(jenkinsClient, numberOfExecutors, systemMessage)
+ verifyJenkinsSeedJobs(jenkinsClient, []seedJobConfig{mySeedJob})
+ })
+ })
+})
+
+var _ = Describe("Jenkins controller priority class", func() {
+
+ const (
+ jenkinsCRName = "k8s-ete-priority-class-existing"
+ priorityClassName = "system-cluster-critical"
+ )
+
+ var (
+ namespace *corev1.Namespace
+ jenkins *v1alpha2.Jenkins
+ groovyScripts = v1alpha2.GroovyScripts{
+ Customization: v1alpha2.Customization{
+ Configurations: []v1alpha2.ConfigMapRef{},
+ },
+ }
+ casc = v1alpha2.ConfigurationAsCode{
+ Customization: v1alpha2.Customization{
+ Configurations: []v1alpha2.ConfigMapRef{},
+ },
+ }
+ )
+
+ BeforeEach(func() {
+ namespace = createNamespace()
+ jenkins = createJenkinsCR(jenkinsCRName, namespace.Name, nil, groovyScripts, casc, priorityClassName)
+ })
+
+ AfterEach(func() {
+ destroyNamespace(namespace)
+ })
+
+ Context("when deploying CR with priority class to cluster", func() {
+ It("creates Jenkins instance and configures it", func() {
+ waitForJenkinsBaseConfigurationToComplete(jenkins)
+ verifyJenkinsMasterPodAttributes(jenkins)
+ })
+ })
+})
+
+var _ = Describe("Jenkins controller plugins test", func() {
+
+ const (
+ jenkinsCRName = e2e
+ priorityClassName = ""
+ jobID = "k8s-e2e"
+ )
+
+ var (
+ namespace *corev1.Namespace
+ jenkins *v1alpha2.Jenkins
+ mySeedJob = seedJobConfig{
+ SeedJob: v1alpha2.SeedJob{
+ ID: "jenkins-operator",
+ CredentialID: "jenkins-operator",
+ JenkinsCredentialType: v1alpha2.NoJenkinsCredentialCredentialType,
+ Targets: "cicd/jobs/k8s.jenkins",
+ Description: "Jenkins Operator repository",
+ RepositoryBranch: "master",
+ RepositoryURL: "https://github.com/jenkinsci/kubernetes-operator.git",
+ },
+ }
+ groovyScripts = v1alpha2.GroovyScripts{
+ Customization: v1alpha2.Customization{
+ Configurations: []v1alpha2.ConfigMapRef{},
+ },
+ }
+ casc = v1alpha2.ConfigurationAsCode{
+ Customization: v1alpha2.Customization{
+ Configurations: []v1alpha2.ConfigMapRef{},
+ },
+ }
+ )
+
+ BeforeEach(func() {
+ namespace = createNamespace()
+ jenkins = createJenkinsCR(jenkinsCRName, namespace.Name, &[]v1alpha2.SeedJob{mySeedJob.SeedJob}, groovyScripts, casc, priorityClassName)
+ })
+
+ AfterEach(func() {
+ destroyNamespace(namespace)
+ })
+
+ Context("when deploying CR with a SeedJob to cluster", func() {
+ It("runs kubernetes plugin job successfully", func() {
+ waitForJenkinsUserConfigurationToComplete(jenkins)
+ jenkinsClient, cleanUpFunc := verifyJenkinsAPIConnection(jenkins, namespace.Name)
+ defer cleanUpFunc()
+ waitForJobCreation(jenkinsClient, jobID)
+ verifyJobCanBeRun(jenkinsClient, jobID)
+ verifyJobHasBeenRunCorrectly(jenkinsClient, jobID)
+ })
+ })
+})
diff --git a/test/e2e/jenkins_pod_restart_test.go b/test/e2e/jenkins_pod_restart_test.go
new file mode 100644
index 00000000..46cda3a2
--- /dev/null
+++ b/test/e2e/jenkins_pod_restart_test.go
@@ -0,0 +1,108 @@
+package e2e
+
+import (
+ "github.com/jenkinsci/kubernetes-operator/api/v1alpha2"
+
+ . "github.com/onsi/ginkgo"
+ . "github.com/onsi/gomega"
+ corev1 "k8s.io/api/core/v1"
+ // +kubebuilder:scaffold:imports
+)
+
+var _ = Describe("Jenkins controller", func() {
+
+ const (
+ jenkinsCRName = e2e
+ priorityClassName = ""
+ )
+
+ var (
+ namespace *corev1.Namespace
+ jenkins *v1alpha2.Jenkins
+ groovyScripts = v1alpha2.GroovyScripts{
+ Customization: v1alpha2.Customization{
+ Configurations: []v1alpha2.ConfigMapRef{},
+ },
+ }
+ casc = v1alpha2.ConfigurationAsCode{
+ Customization: v1alpha2.Customization{
+ Configurations: []v1alpha2.ConfigMapRef{},
+ },
+ }
+ )
+
+ BeforeEach(func() {
+ namespace = createNamespace()
+
+ configureAuthorizationToUnSecure(namespace.Name, userConfigurationConfigMapName)
+ jenkins = createJenkinsCR(jenkinsCRName, namespace.Name, nil, groovyScripts, casc, priorityClassName)
+ })
+
+ AfterEach(func() {
+ destroyNamespace(namespace)
+ })
+
+ Context("when restarting Jenkins master pod", func() {
+ It("new Jenkins Master pod should be created", func() {
+ waitForJenkinsBaseConfigurationToComplete(jenkins)
+ restartJenkinsMasterPod(jenkins)
+ waitForRecreateJenkinsMasterPod(jenkins)
+ checkBaseConfigurationCompleteTimeIsNotSet(jenkins)
+ waitForJenkinsBaseConfigurationToComplete(jenkins)
+ })
+ })
+})
+
+var _ = Describe("Jenkins controller", func() {
+
+ const (
+ jenkinsCRName = e2e
+ priorityClassName = ""
+ )
+
+ var (
+ namespace *corev1.Namespace
+ jenkins *v1alpha2.Jenkins
+ groovyScripts = v1alpha2.GroovyScripts{
+ Customization: v1alpha2.Customization{
+ Configurations: []v1alpha2.ConfigMapRef{
+ {
+ Name: userConfigurationConfigMapName,
+ },
+ },
+ },
+ }
+ casc = v1alpha2.ConfigurationAsCode{
+ Customization: v1alpha2.Customization{
+ Configurations: []v1alpha2.ConfigMapRef{},
+ },
+ }
+ )
+
+ BeforeEach(func() {
+ namespace = createNamespace()
+
+ configureAuthorizationToUnSecure(namespace.Name, userConfigurationConfigMapName)
+ jenkins = createJenkinsCRSafeRestart(jenkinsCRName, namespace.Name, nil, groovyScripts, casc, priorityClassName)
+ })
+
+ AfterEach(func() {
+ destroyNamespace(namespace)
+ })
+
+ Context("when running Jenkins safe restart", func() {
+ It("authorization strategy is not overwritten", func() {
+ waitForJenkinsBaseConfigurationToComplete(jenkins)
+ waitForJenkinsUserConfigurationToComplete(jenkins)
+ jenkinsClient, cleanUpFunc := verifyJenkinsAPIConnection(jenkins, namespace.Name)
+ defer cleanUpFunc()
+ checkIfAuthorizationStrategyUnsecuredIsSet(jenkinsClient)
+
+ err := jenkinsClient.SafeRestart()
+ Expect(err).NotTo(HaveOccurred())
+ waitForJenkinsSafeRestart(jenkinsClient)
+
+ checkIfAuthorizationStrategyUnsecuredIsSet(jenkinsClient)
+ })
+ })
+})
diff --git a/test/e2e/jenkins_restore_backup_test.go b/test/e2e/jenkins_restore_backup_test.go
new file mode 100644
index 00000000..eeb474d8
--- /dev/null
+++ b/test/e2e/jenkins_restore_backup_test.go
@@ -0,0 +1,61 @@
+package e2e
+
+import (
+ "github.com/jenkinsci/kubernetes-operator/api/v1alpha2"
+
+ . "github.com/onsi/ginkgo"
+ corev1 "k8s.io/api/core/v1"
+ // +kubebuilder:scaffold:imports
+)
+
+var _ = Describe("Jenkins controller backup and restore", func() {
+
+ const (
+ jenkinsCRName = e2e
+ jobID = "e2e-jenkins-operator"
+ )
+
+ var (
+ namespace *corev1.Namespace
+ jenkins *v1alpha2.Jenkins
+ )
+
+ BeforeEach(func() {
+ namespace = createNamespace()
+
+ createPVC(namespace.Name)
+ jenkins = createJenkinsWithBackupAndRestoreConfigured(jenkinsCRName, namespace.Name)
+ })
+
+ AfterEach(func() {
+ destroyNamespace(namespace)
+ })
+
+ Context("when deploying CR with backup enabled to cluster", func() {
+ It("performs backups before pod deletion and restores them even Jenkins status is restarted", func() {
+ waitForJenkinsUserConfigurationToComplete(jenkins)
+ jenkinsClient, cleanUpFunc := verifyJenkinsAPIConnection(jenkins, namespace.Name)
+ defer cleanUpFunc()
+ waitForJobCreation(jenkinsClient, jobID)
+ verifyJobCanBeRun(jenkinsClient, jobID)
+
+ jenkins = getJenkins(jenkins.Namespace, jenkins.Name)
+ restartJenkinsMasterPod(jenkins)
+ waitForRecreateJenkinsMasterPod(jenkins)
+ waitForJenkinsUserConfigurationToComplete(jenkins)
+ jenkinsClient2, cleanUpFunc2 := verifyJenkinsAPIConnection(jenkins, namespace.Name)
+ defer cleanUpFunc2()
+ waitForJobCreation(jenkinsClient2, jobID)
+ verifyJobBuildsAfterRestoreBackup(jenkinsClient2, jobID)
+
+ resetJenkinsStatus(jenkins)
+ jenkins = getJenkins(jenkins.Namespace, jenkins.Name)
+ checkBaseConfigurationCompleteTimeIsNotSet(jenkins)
+ waitForJenkinsUserConfigurationToComplete(jenkins)
+ jenkinsClient3, cleanUpFunc3 := verifyJenkinsAPIConnection(jenkins, namespace.Name)
+ defer cleanUpFunc3()
+ waitForJobCreation(jenkinsClient3, jobID)
+ verifyJobBuildsAfterRestoreBackup(jenkinsClient3, jobID)
+ })
+ })
+})
diff --git a/test/e2e/jenkins_test.go b/test/e2e/jenkins_test.go
new file mode 100644
index 00000000..725925b7
--- /dev/null
+++ b/test/e2e/jenkins_test.go
@@ -0,0 +1,352 @@
+package e2e
+
+import (
+ "context"
+ "fmt"
+
+ "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/constants"
+
+ . "github.com/onsi/ginkgo"
+ . "github.com/onsi/gomega"
+ corev1 "k8s.io/api/core/v1"
+ rbacv1 "k8s.io/api/rbac/v1"
+ metav1 "k8s.io/apimachinery/pkg/apis/meta/v1"
+ "k8s.io/apimachinery/pkg/labels"
+ "k8s.io/apimachinery/pkg/types"
+ "k8s.io/apimachinery/pkg/util/intstr"
+ "k8s.io/client-go/kubernetes"
+ "sigs.k8s.io/controller-runtime/pkg/client"
+)
+
+const (
+ userConfigurationConfigMapName = "user-config"
+ userConfigurationSecretName = "user-secret"
+)
+
+func getJenkins(namespace, name string) *v1alpha2.Jenkins {
+ jenkins := &v1alpha2.Jenkins{}
+ namespaceName := types.NamespacedName{Namespace: namespace, Name: name}
+ Expect(k8sClient.Get(context.TODO(), namespaceName, jenkins)).Should(Succeed())
+ return jenkins
+}
+
+func getJenkinsMasterPod(jenkins *v1alpha2.Jenkins) *corev1.Pod {
+ lo := &client.ListOptions{
+ LabelSelector: labels.SelectorFromSet(resources.GetJenkinsMasterPodLabels(*jenkins)),
+ Namespace: jenkins.Namespace,
+ }
+ pods := &corev1.PodList{}
+ Expect(k8sClient.List(context.TODO(), pods, lo)).Should(Succeed())
+ Expect(pods.Items).Should(HaveLen(1), fmt.Sprintf("Jenkins pod not found, pod list: %+v", pods.Items))
+ return &pods.Items[0]
+}
+
+func createJenkinsCR(name, namespace string, seedJob *[]v1alpha2.SeedJob, groovyScripts v1alpha2.GroovyScripts, casc v1alpha2.ConfigurationAsCode, priorityClassName string) *v1alpha2.Jenkins {
+ var seedJobs []v1alpha2.SeedJob
+ if seedJob != nil {
+ seedJobs = append(seedJobs, *seedJob...)
+ }
+
+ jenkins := &v1alpha2.Jenkins{
+ TypeMeta: v1alpha2.JenkinsTypeMeta(),
+ ObjectMeta: metav1.ObjectMeta{
+ Name: name,
+ Namespace: namespace,
+ },
+ Spec: v1alpha2.JenkinsSpec{
+ GroovyScripts: groovyScripts,
+ ConfigurationAsCode: casc,
+ Master: v1alpha2.JenkinsMaster{
+ Annotations: map[string]string{"test": "label"},
+ Containers: []v1alpha2.Container{
+ {
+ Name: resources.JenkinsMasterContainerName,
+ Env: []corev1.EnvVar{
+ {
+ Name: "TEST_ENV",
+ Value: "test_env_value",
+ },
+ },
+ ReadinessProbe: &corev1.Probe{
+ Handler: corev1.Handler{
+ HTTPGet: &corev1.HTTPGetAction{
+ Path: "/login",
+ Port: intstr.FromString("http"),
+ Scheme: corev1.URISchemeHTTP,
+ },
+ },
+ InitialDelaySeconds: int32(100),
+ TimeoutSeconds: int32(4),
+ FailureThreshold: int32(12),
+ SuccessThreshold: int32(1),
+ PeriodSeconds: int32(1),
+ },
+ LivenessProbe: &corev1.Probe{
+ Handler: corev1.Handler{
+ HTTPGet: &corev1.HTTPGetAction{
+ Path: "/login",
+ Port: intstr.FromString("http"),
+ Scheme: corev1.URISchemeHTTP,
+ },
+ },
+ InitialDelaySeconds: int32(80),
+ TimeoutSeconds: int32(4),
+ FailureThreshold: int32(10),
+ SuccessThreshold: int32(1),
+ PeriodSeconds: int32(1),
+ },
+ VolumeMounts: []corev1.VolumeMount{
+ {
+ Name: "plugins-cache",
+ MountPath: "/usr/share/jenkins/ref/plugins",
+ },
+ },
+ },
+ {
+ Name: "envoyproxy",
+ Image: "envoyproxy/envoy-alpine:v1.14.1",
+ },
+ },
+ Plugins: []v1alpha2.Plugin{
+ {Name: "audit-trail", Version: "3.7"},
+ {Name: "simple-theme-plugin", Version: "0.6"},
+ {Name: "github", Version: "1.32.0"},
+ {Name: "devoptics", Version: "1.1934", DownloadURL: "https://jenkins-updates.cloudbees.com/download/plugins/devoptics/1.1934/devoptics.hpi"},
+ },
+ PriorityClassName: priorityClassName,
+ NodeSelector: map[string]string{"kubernetes.io/os": "linux"},
+ Volumes: []corev1.Volume{
+ {
+ Name: "plugins-cache",
+ VolumeSource: corev1.VolumeSource{
+ EmptyDir: &corev1.EmptyDirVolumeSource{},
+ },
+ },
+ },
+ },
+ SeedJobs: seedJobs,
+ Service: v1alpha2.Service{
+ Type: corev1.ServiceTypeNodePort,
+ Port: constants.DefaultHTTPPortInt32,
+ },
+ },
+ }
+ jenkins.Spec.Roles = []rbacv1.RoleRef{
+ {
+ APIGroup: "rbac.authorization.k8s.io",
+ Kind: "Role",
+ Name: resources.GetResourceName(jenkins),
+ },
+ }
+ updateJenkinsCR(jenkins)
+
+ _, _ = fmt.Fprintf(GinkgoWriter, "Jenkins CR %+v\n", *jenkins)
+
+ Expect(k8sClient.Create(context.TODO(), jenkins)).Should(Succeed())
+
+ return jenkins
+}
+
+func createJenkinsCRSafeRestart(name, namespace string, seedJob *[]v1alpha2.SeedJob, groovyScripts v1alpha2.GroovyScripts, casc v1alpha2.ConfigurationAsCode, priorityClassName string) *v1alpha2.Jenkins {
+ var seedJobs []v1alpha2.SeedJob
+ if seedJob != nil {
+ seedJobs = append(seedJobs, *seedJob...)
+ }
+
+ jenkins := &v1alpha2.Jenkins{
+ TypeMeta: v1alpha2.JenkinsTypeMeta(),
+ ObjectMeta: metav1.ObjectMeta{
+ Name: name,
+ Namespace: namespace,
+ },
+ Spec: v1alpha2.JenkinsSpec{
+ GroovyScripts: groovyScripts,
+ ConfigurationAsCode: casc,
+ Master: v1alpha2.JenkinsMaster{
+ Annotations: map[string]string{"test": "label"},
+ Containers: []v1alpha2.Container{
+ {
+ Name: resources.JenkinsMasterContainerName,
+ Env: []corev1.EnvVar{
+ {
+ Name: "TEST_ENV",
+ Value: "test_env_value",
+ },
+ },
+ ReadinessProbe: &corev1.Probe{
+ Handler: corev1.Handler{
+ HTTPGet: &corev1.HTTPGetAction{
+ Path: "/login",
+ Port: intstr.FromString("http"),
+ Scheme: corev1.URISchemeHTTP,
+ },
+ },
+ InitialDelaySeconds: int32(100),
+ TimeoutSeconds: int32(4),
+ FailureThreshold: int32(12),
+ SuccessThreshold: int32(1),
+ PeriodSeconds: int32(10),
+ },
+ LivenessProbe: &corev1.Probe{
+ Handler: corev1.Handler{
+ HTTPGet: &corev1.HTTPGetAction{
+ Path: "/login",
+ Port: intstr.FromString("http"),
+ Scheme: corev1.URISchemeHTTP,
+ },
+ },
+ InitialDelaySeconds: int32(100),
+ TimeoutSeconds: int32(5),
+ FailureThreshold: int32(12),
+ SuccessThreshold: int32(1),
+ PeriodSeconds: int32(5),
+ },
+ VolumeMounts: []corev1.VolumeMount{
+ {
+ Name: "plugins-cache",
+ MountPath: "/usr/share/jenkins/ref/plugins",
+ },
+ },
+ },
+ {
+ Name: "envoyproxy",
+ Image: "envoyproxy/envoy-alpine:v1.14.1",
+ },
+ },
+ Plugins: []v1alpha2.Plugin{
+ {Name: "audit-trail", Version: "3.7"},
+ {Name: "simple-theme-plugin", Version: "0.6"},
+ {Name: "github", Version: "1.32.0"},
+ {Name: "devoptics", Version: "1.1934", DownloadURL: "https://jenkins-updates.cloudbees.com/download/plugins/devoptics/1.1934/devoptics.hpi"},
+ },
+ PriorityClassName: priorityClassName,
+ NodeSelector: map[string]string{"kubernetes.io/os": "linux"},
+ Volumes: []corev1.Volume{
+ {
+ Name: "plugins-cache",
+ VolumeSource: corev1.VolumeSource{
+ EmptyDir: &corev1.EmptyDirVolumeSource{},
+ },
+ },
+ },
+ },
+ SeedJobs: seedJobs,
+ Service: v1alpha2.Service{
+ Type: corev1.ServiceTypeNodePort,
+ Port: constants.DefaultHTTPPortInt32,
+ },
+ },
+ }
+ jenkins.Spec.Roles = []rbacv1.RoleRef{
+ {
+ APIGroup: "rbac.authorization.k8s.io",
+ Kind: "Role",
+ Name: resources.GetResourceName(jenkins),
+ },
+ }
+ updateJenkinsCR(jenkins)
+
+ _, _ = fmt.Fprintf(GinkgoWriter, "Jenkins CR %+v\n", *jenkins)
+
+ Expect(k8sClient.Create(context.TODO(), jenkins)).Should(Succeed())
+
+ return jenkins
+}
+
+func createJenkinsAPIClientFromServiceAccount(jenkins *v1alpha2.Jenkins, jenkinsAPIURL string) (jenkinsclient.Jenkins, error) {
+ podName := resources.GetJenkinsMasterPodName(jenkins)
+
+ clientSet, err := kubernetes.NewForConfig(cfg)
+ if err != nil {
+ return nil, err
+ }
+ config := configuration.Configuration{Jenkins: jenkins, ClientSet: *clientSet, Config: cfg}
+ r := base.New(config, jenkinsclient.JenkinsAPIConnectionSettings{})
+
+ token, _, err := r.Configuration.Exec(podName, resources.JenkinsMasterContainerName, []string{"cat", "/var/run/secrets/kubernetes.io/serviceaccount/token"})
+ if err != nil {
+ return nil, err
+ }
+
+ return jenkinsclient.NewBearerTokenAuthorization(jenkinsAPIURL, token.String())
+}
+
+func createJenkinsAPIClientFromSecret(jenkins *v1alpha2.Jenkins, jenkinsAPIURL string) (jenkinsclient.Jenkins, error) {
+ _, _ = fmt.Fprintf(GinkgoWriter, "Creating Jenkins API client from secret\n")
+
+ adminSecret := &corev1.Secret{}
+ namespaceName := types.NamespacedName{Namespace: jenkins.Namespace, Name: resources.GetOperatorCredentialsSecretName(jenkins)}
+ if err := k8sClient.Get(context.TODO(), namespaceName, adminSecret); err != nil {
+ return nil, err
+ }
+
+ return jenkinsclient.NewUserAndPasswordAuthorization(
+ jenkinsAPIURL,
+ string(adminSecret.Data[resources.OperatorCredentialsSecretUserNameKey]),
+ string(adminSecret.Data[resources.OperatorCredentialsSecretTokenKey]),
+ )
+}
+
+func verifyJenkinsAPIConnection(jenkins *v1alpha2.Jenkins, namespace string) (jenkinsclient.Jenkins, func()) {
+ By("establishing Jenkins API connection")
+
+ var service corev1.Service
+ err := k8sClient.Get(context.TODO(), types.NamespacedName{
+ Namespace: jenkins.Namespace,
+ Name: resources.GetJenkinsHTTPServiceName(jenkins),
+ }, &service)
+ Expect(err).NotTo(HaveOccurred())
+
+ podName := resources.GetJenkinsMasterPodName(jenkins)
+ port, cleanUpFunc, waitFunc, portForwardFunc, err := setupPortForwardToPod(namespace, podName, int(constants.DefaultHTTPPortInt32))
+ Expect(err).NotTo(HaveOccurred())
+ go portForwardFunc()
+ waitFunc()
+
+ jenkinsAPIURL := jenkinsclient.JenkinsAPIConnectionSettings{
+ Hostname: "localhost",
+ Port: port,
+ UseNodePort: false,
+ }.BuildJenkinsAPIUrl(service.Name, service.Namespace, service.Spec.Ports[0].Port, service.Spec.Ports[0].NodePort)
+
+ var jenkinsClient jenkinsclient.Jenkins
+ if jenkins.Spec.JenkinsAPISettings.AuthorizationStrategy == v1alpha2.ServiceAccountAuthorizationStrategy {
+ jenkinsClient, err = createJenkinsAPIClientFromServiceAccount(jenkins, jenkinsAPIURL)
+ } else {
+ jenkinsClient, err = createJenkinsAPIClientFromSecret(jenkins, jenkinsAPIURL)
+ }
+
+ if err != nil {
+ defer cleanUpFunc()
+ Fail(err.Error())
+ }
+
+ _, _ = fmt.Fprintf(GinkgoWriter, "I can establish connection to Jenkins API\n")
+ return jenkinsClient, cleanUpFunc
+}
+
+func restartJenkinsMasterPod(jenkins *v1alpha2.Jenkins) {
+ _, _ = fmt.Fprintf(GinkgoWriter, "Restarting Jenkins master pod\n")
+ jenkinsPod := getJenkinsMasterPod(jenkins)
+ Expect(k8sClient.Delete(context.TODO(), jenkinsPod)).Should(Succeed())
+
+ Eventually(func() (bool, error) {
+ jenkinsPod = getJenkinsMasterPod(jenkins)
+ return jenkinsPod.DeletionTimestamp != nil, nil
+ }, 30*retryInterval, retryInterval).Should(BeTrue())
+
+ _, _ = fmt.Fprintf(GinkgoWriter, "Jenkins master pod has been restarted\n")
+}
+
+func getJenkinsService(jenkins *v1alpha2.Jenkins, serviceKind string) *corev1.Service {
+ service := &corev1.Service{}
+ serviceName := constants.OperatorName + "-" + serviceKind + "-" + jenkins.ObjectMeta.Name
+ Expect(k8sClient.Get(context.TODO(), client.ObjectKey{Name: serviceName, Namespace: jenkins.Namespace}, service)).Should(Succeed())
+
+ return service
+}
diff --git a/test/e2e/main_test.go b/test/e2e/main_test.go
deleted file mode 100644
index c1494109..00000000
--- a/test/e2e/main_test.go
+++ /dev/null
@@ -1,69 +0,0 @@
-package e2e
-
-import (
- "flag"
- "testing"
-
- "github.com/jenkinsci/kubernetes-operator/pkg/apis"
- "github.com/jenkinsci/kubernetes-operator/pkg/apis/jenkins/v1alpha2"
- "github.com/jenkinsci/kubernetes-operator/pkg/constants"
-
- framework "github.com/operator-framework/operator-sdk/pkg/test"
- "github.com/operator-framework/operator-sdk/pkg/test/e2eutil"
- metav1 "k8s.io/apimachinery/pkg/apis/meta/v1"
-)
-
-const (
- jenkinsOperatorDeploymentName = constants.OperatorName
- seedJobConfigurationParameterName = "seed-job-config"
-)
-
-var (
- seedJobConfigurationFile *string
-)
-
-func TestMain(m *testing.M) {
- seedJobConfigurationFile = flag.String(seedJobConfigurationParameterName, "", "path to seed job config")
-
- framework.MainEntry(m)
-}
-
-func setupTest(t *testing.T) (string, *framework.Context) {
- ctx := framework.NewContext(t)
- err := ctx.InitializeClusterResources(nil)
- if err != nil {
- t.Fatalf("could not initialize cluster resources: %v", err)
- }
-
- defer func() {
- showLogsIfTestHasFailed(t, ctx)
- if t.Failed() && ctx != nil {
- ctx.Cleanup()
- }
- }()
-
- jenkinsServiceList := &v1alpha2.JenkinsList{
- TypeMeta: metav1.TypeMeta{
- Kind: v1alpha2.Kind,
- APIVersion: v1alpha2.SchemeGroupVersion.String(),
- },
- }
- err = framework.AddToFrameworkScheme(apis.AddToScheme, jenkinsServiceList)
- if err != nil {
- t.Fatalf("could not add scheme to framework scheme: %v", err)
- }
-
- namespace, err := ctx.GetOperatorNamespace()
- if err != nil {
- t.Fatalf("could not get namespace: %v", err)
- }
- t.Logf("Test namespace '%s'", namespace)
-
- // wait for jenkins-operator to be ready
- err = e2eutil.WaitForDeployment(t, framework.Global.KubeClient, namespace, jenkinsOperatorDeploymentName, 1, retryInterval, timeout)
- if err != nil {
- t.Fatal(err)
- }
-
- return namespace, ctx
-}
diff --git a/test/e2e/mode_kubernetes.go b/test/e2e/mode_kubernetes.go
index f17f0730..9408844c 100644
--- a/test/e2e/mode_kubernetes.go
+++ b/test/e2e/mode_kubernetes.go
@@ -4,17 +4,14 @@
package e2e
import (
- "testing"
-
- "github.com/jenkinsci/kubernetes-operator/pkg/apis/jenkins/v1alpha2"
+ "github.com/jenkinsci/kubernetes-operator/api/v1alpha2"
)
const (
- skipTestSafeRestart = false
- skipTestPriorityClass = false
+//skipTestSafeRestart = false
+//skipTestPriorityClass = false
)
-func updateJenkinsCR(t *testing.T, jenkins *v1alpha2.Jenkins) {
- t.Log("Update Jenkins CR")
+func updateJenkinsCR(jenkins *v1alpha2.Jenkins) {
// do nothing
}
diff --git a/test/e2e/mode_openshift.go b/test/e2e/mode_openshift.go
index d6aa054d..a2f6f584 100644
--- a/test/e2e/mode_openshift.go
+++ b/test/e2e/mode_openshift.go
@@ -3,9 +3,7 @@
package e2e
import (
- "testing"
-
- "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"
corev1 "k8s.io/api/core/v1"
@@ -16,9 +14,7 @@ const (
skipTestPriorityClass = true
)
-func updateJenkinsCR(t *testing.T, jenkins *v1alpha2.Jenkins) {
- t.Log("Update Jenkins CR: OpenShift")
-
+func updateJenkinsCR(jenkins *v1alpha2.Jenkins) {
jenkins.Spec.Master.Containers[0].Image = "quay.io/openshift/origin-jenkins"
jenkins.Spec.Master.Containers[0].Command = []string{
"bash",
diff --git a/test/e2e/mode_openshift_oauth.go b/test/e2e/mode_openshift_oauth.go
index a832aed4..30c31d5a 100644
--- a/test/e2e/mode_openshift_oauth.go
+++ b/test/e2e/mode_openshift_oauth.go
@@ -2,6 +2,8 @@
package e2e
+// TODO
+/*
import (
"context"
"testing"
@@ -100,3 +102,4 @@ func updateJenkinsCR(t *testing.T, jenkins *v1alpha2.Jenkins) {
jenkins.Spec.Master.Plugins = jenkins.Spec.Master.Plugins[0:3] // remove devoptics plugin
}
}
+*/
diff --git a/test/e2e/operator.go b/test/e2e/operator.go
deleted file mode 100644
index ad810e19..00000000
--- a/test/e2e/operator.go
+++ /dev/null
@@ -1,155 +0,0 @@
-package e2e
-
-import (
- "bytes"
- "fmt"
- "io"
- "sort"
- "testing"
-
- framework "github.com/operator-framework/operator-sdk/pkg/test"
- v1 "k8s.io/api/core/v1"
- "k8s.io/api/events/v1beta1"
- metav1 "k8s.io/apimachinery/pkg/apis/meta/v1"
- "k8s.io/apimachinery/pkg/labels"
-)
-
-var (
- podLogTailLimit int64 = 15
- kubernetesEventsLimit int64 = 15
- // MUST match the labels in the deployment manifest: deploy/operator.yaml
- operatorPodLabels = map[string]string{
- "name": "jenkins-operator",
- }
-)
-
-func getOperatorPod(namespace string) (*v1.Pod, error) {
- listOptions := metav1.ListOptions{
- LabelSelector: labels.SelectorFromSet(operatorPodLabels).String(),
- }
-
- podList, err := framework.Global.KubeClient.CoreV1().Pods(namespace).List(listOptions)
- if err != nil {
- return nil, err
- }
- if len(podList.Items) != 1 {
- return nil, fmt.Errorf("expected exactly one pod, got: '%+v'", podList)
- }
-
- return &podList.Items[0], nil
-}
-
-func getOperatorLogs(namespace string) (string, error) {
- pod, err := getOperatorPod(namespace)
- if err != nil {
- return "", err
- }
-
- logOptions := v1.PodLogOptions{TailLines: &podLogTailLimit}
- req := framework.Global.KubeClient.CoreV1().Pods(pod.Namespace).GetLogs(pod.Name, &logOptions)
- podLogs, err := req.Stream()
- if err != nil {
- return "", err
- }
-
- defer func() {
- if podLogs != nil {
- _ = podLogs.Close()
- }
- }()
-
- buf := new(bytes.Buffer)
- _, err = io.Copy(buf, podLogs)
- if err != nil {
- return "", err
- }
-
- logs := buf.String()
- return logs, nil
-}
-
-func printOperatorLogs(t *testing.T, namespace string) {
- t.Logf("Operator logs in '%s' namespace:\n", namespace)
- logs, err := getOperatorLogs(namespace)
- if err != nil {
- t.Errorf("Couldn't get the operator pod logs: %s", err)
- } else {
- t.Logf("Last %d lines of log from operator:\n %s", podLogTailLimit, logs)
- }
-}
-
-func getKubernetesEvents(namespace string) ([]v1beta1.Event, error) {
- listOptions := metav1.ListOptions{
- Limit: kubernetesEventsLimit,
- }
-
- events, err := framework.Global.KubeClient.EventsV1beta1().Events(namespace).List(listOptions)
- if err != nil {
- return nil, err
- }
-
- sort.SliceStable(events.Items, func(i, j int) bool {
- return events.Items[i].CreationTimestamp.Unix() < events.Items[j].CreationTimestamp.Unix()
- })
-
- return events.Items, nil
-}
-
-func printKubernetesEvents(t *testing.T, namespace string) {
- t.Logf("Kubernetes events in '%s' namespace:\n", namespace)
- events, err := getKubernetesEvents(namespace)
- if err != nil {
- t.Errorf("Couldn't get kubernetes events: %s", err)
- } else {
- t.Logf("Last %d events from kubernetes:\n", kubernetesEventsLimit)
-
- for _, event := range events {
- t.Logf("%+v\n\n", event)
- }
- }
-}
-
-func getKubernetesPods(namespace string) (*v1.PodList, error) {
- return framework.Global.KubeClient.CoreV1().Pods(namespace).List(metav1.ListOptions{})
-}
-
-func printKubernetesPods(t *testing.T, namespace string) {
- t.Logf("All pods in '%s' namespace:\n", namespace)
- podList, err := getKubernetesPods(namespace)
- if err != nil {
- t.Errorf("Couldn't get kubernetes pods: %s", err)
- }
-
- for _, pod := range podList.Items {
- t.Logf("%+v\n\n", pod)
- }
-}
-
-func showLogsIfTestHasFailed(t *testing.T, ctx *framework.Context) {
- namespace, err := ctx.GetOperatorNamespace()
- if err != nil {
- t.Fatalf("Failed to get '%s' namespace", err)
- }
-
- if t.Failed() {
- t.Log("Test failed. Bellow here you can check logs:")
-
- printKubernetesEvents(t, namespace)
- printKubernetesPods(t, namespace)
- printOperatorLogs(t, namespace)
- }
-}
-
-func showLogsAndCleanup(t *testing.T, ctx *framework.Context) {
- namespace, err := ctx.GetOperatorNamespace()
- if err != nil {
- t.Fatalf("Failed to get '%s' namespace", err)
- }
-
- showLogsIfTestHasFailed(t, ctx)
-
- ctx.Cleanup()
- if err = waitUntilNamespaceDestroyed(namespace); err != nil {
- t.Fatalf("Failed to wait for namespace until destroyed '%s'", err)
- }
-}
diff --git a/test/e2e/port_forward.go b/test/e2e/port_forward_test.go
similarity index 85%
rename from test/e2e/port_forward.go
rename to test/e2e/port_forward_test.go
index 49efcd78..12f318af 100644
--- a/test/e2e/port_forward.go
+++ b/test/e2e/port_forward_test.go
@@ -7,9 +7,9 @@ import (
"net/url"
"os"
"strings"
- "testing"
- framework "github.com/operator-framework/operator-sdk/pkg/test"
+ . "github.com/onsi/ginkgo"
+ . "github.com/onsi/gomega"
v1 "k8s.io/api/core/v1"
metav1 "k8s.io/apimachinery/pkg/apis/meta/v1"
"k8s.io/cli-runtime/pkg/genericclioptions"
@@ -49,11 +49,9 @@ func getFreePort() (int, error) {
return l.Addr().(*net.TCPAddr).Port, nil
}
-func setupPortForwardToPod(t *testing.T, namespace, podName string, podPort int) (port int, cleanUpFunc func(), waitFunc func(), portForwardFunc func(), err error) {
+func setupPortForwardToPod(namespace, podName string, podPort int) (port int, cleanUpFunc func(), waitFunc func(), portForwardFunc func(), err error) {
port, err = getFreePort()
- if err != nil {
- t.Fatal(err)
- }
+ Expect(err).NotTo(HaveOccurred())
stream := genericclioptions.IOStreams{
In: os.Stdin,
@@ -68,7 +66,7 @@ func setupPortForwardToPod(t *testing.T, namespace, podName string, podPort int)
readyCh := make(chan struct{})
req := portForwardToPodRequest{
- config: framework.Global.KubeConfig,
+ config: cfg,
pod: v1.Pod{
ObjectMeta: metav1.ObjectMeta{
Name: podName,
@@ -83,9 +81,9 @@ func setupPortForwardToPod(t *testing.T, namespace, podName string, podPort int)
}
waitFunc = func() {
- t.Log("Waiting for the port-forward.")
+ _, _ = fmt.Fprintf(GinkgoWriter, "Waiting for the port-forward.\n")
<-readyCh
- t.Log("The port-forward is established.")
+ _, _ = fmt.Fprintf(GinkgoWriter, "The port-forward is established.\n")
}
portForwardFunc = func() {
@@ -96,7 +94,7 @@ func setupPortForwardToPod(t *testing.T, namespace, podName string, podPort int)
}
cleanUpFunc = func() {
- t.Log("Closing port-forward")
+ _, _ = fmt.Fprintf(GinkgoWriter, "Closing port-forward\n")
close(stopCh)
}
diff --git a/test/e2e/restart_test.go b/test/e2e/restart_test.go
index 6f36d968..f5eb145b 100644
--- a/test/e2e/restart_test.go
+++ b/test/e2e/restart_test.go
@@ -2,67 +2,19 @@ package e2e
import (
"context"
- "testing"
+ "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"
- framework "github.com/operator-framework/operator-sdk/pkg/test"
- "github.com/stretchr/testify/require"
+ . "github.com/onsi/ginkgo"
+ . "github.com/onsi/gomega"
corev1 "k8s.io/api/core/v1"
metav1 "k8s.io/apimachinery/pkg/apis/meta/v1"
"k8s.io/apimachinery/pkg/types"
)
-func TestJenkinsMasterPodRestart(t *testing.T) {
- t.Parallel()
- namespace, ctx := setupTest(t)
-
- defer showLogsAndCleanup(t, ctx)
-
- jenkins := createJenkinsCR(t, "e2e", namespace, nil, v1alpha2.GroovyScripts{}, v1alpha2.ConfigurationAsCode{}, "")
- waitForJenkinsBaseConfigurationToComplete(t, jenkins)
- restartJenkinsMasterPod(t, jenkins)
- waitForRecreateJenkinsMasterPod(t, jenkins)
- checkBaseConfigurationCompleteTimeIsNotSet(t, jenkins)
- waitForJenkinsBaseConfigurationToComplete(t, jenkins)
-}
-
-func TestSafeRestart(t *testing.T) {
- if skipTestSafeRestart {
- t.Skip()
- }
- t.Parallel()
- namespace, ctx := setupTest(t)
- // Deletes test namespace
- defer ctx.Cleanup()
-
- jenkinsCRName := "e2e"
- configureAuthorizationToUnSecure(t, namespace, userConfigurationConfigMapName)
- groovyScriptsConfig := v1alpha2.GroovyScripts{
- Customization: v1alpha2.Customization{
- Configurations: []v1alpha2.ConfigMapRef{
- {
- Name: userConfigurationConfigMapName,
- },
- },
- },
- }
- jenkins := createJenkinsCR(t, jenkinsCRName, namespace, nil, groovyScriptsConfig, v1alpha2.ConfigurationAsCode{}, "")
- waitForJenkinsBaseConfigurationToComplete(t, jenkins)
- waitForJenkinsUserConfigurationToComplete(t, jenkins)
- jenkinsClient, cleanUpFunc := verifyJenkinsAPIConnection(t, jenkins, namespace)
- defer cleanUpFunc()
- checkIfAuthorizationStrategyUnsecuredIsSet(t, jenkinsClient)
-
- err := jenkinsClient.SafeRestart()
- require.NoError(t, err)
- waitForJenkinsSafeRestart(t, jenkinsClient)
-
- checkIfAuthorizationStrategyUnsecuredIsSet(t, jenkinsClient)
-}
-
-func configureAuthorizationToUnSecure(t *testing.T, namespace, configMapName string) {
+func configureAuthorizationToUnSecure(namespace, configMapName string) {
limitRange := &corev1.ConfigMap{
ObjectMeta: metav1.ObjectMeta{
Name: configMapName,
@@ -81,31 +33,33 @@ jenkins.save()
},
}
- err := framework.Global.Client.Create(context.TODO(), limitRange, nil)
- require.NoError(t, err)
+ Expect(k8sClient.Create(context.TODO(), limitRange)).Should(Succeed())
}
-func checkIfAuthorizationStrategyUnsecuredIsSet(t *testing.T, jenkinsClient jenkinsclient.Jenkins) {
+func checkIfAuthorizationStrategyUnsecuredIsSet(jenkinsClient jenkinsclient.Jenkins) {
+ By("checking if Authorization Strategy Unsecured is set")
+
logs, err := jenkinsClient.ExecuteScript(`
import hudson.security.*
-
+
def jenkins = jenkins.model.Jenkins.getInstance()
-
+
if (!(jenkins.getAuthorizationStrategy() instanceof AuthorizationStrategy.Unsecured)) {
throw new Exception('AuthorizationStrategy.Unsecured is not set')
}
`)
- require.NoError(t, err, logs)
+ Expect(err).NotTo(HaveOccurred(), logs)
}
-func checkBaseConfigurationCompleteTimeIsNotSet(t *testing.T, jenkins *v1alpha2.Jenkins) {
- jenkinsStatus := &v1alpha2.Jenkins{}
- namespaceName := types.NamespacedName{Namespace: jenkins.Namespace, Name: jenkins.Name}
- err := framework.Global.Client.Get(context.TODO(), namespaceName, jenkinsStatus)
- if err != nil {
- t.Fatal(err)
- }
- if jenkinsStatus.Status.BaseConfigurationCompletedTime != nil {
- t.Fatalf("Status.BaseConfigurationCompletedTime is set after pod restart, status %+v", jenkinsStatus.Status)
- }
+func checkBaseConfigurationCompleteTimeIsNotSet(jenkins *v1alpha2.Jenkins) {
+ By("checking that Base Configuration's complete time is not set")
+
+ Eventually(func() (bool, error) {
+ actualJenkins := &v1alpha2.Jenkins{}
+ err := k8sClient.Get(context.TODO(), types.NamespacedName{Name: jenkins.Name, Namespace: jenkins.Namespace}, actualJenkins)
+ if err != nil {
+ return false, err
+ }
+ return actualJenkins.Status.BaseConfigurationCompletedTime == nil, nil
+ }, time.Duration(110)*retryInterval, time.Second).Should(BeTrue())
}
diff --git a/test/e2e/restorebackup_test.go b/test/e2e/restorebackup_test.go
index 2b09cf45..46d4b524 100644
--- a/test/e2e/restorebackup_test.go
+++ b/test/e2e/restorebackup_test.go
@@ -2,18 +2,16 @@ package e2e
import (
"context"
- "testing"
+ "fmt"
"time"
- "github.com/jenkinsci/kubernetes-operator/internal/try"
- "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/base/resources"
"github.com/jenkinsci/kubernetes-operator/pkg/constants"
- framework "github.com/operator-framework/operator-sdk/pkg/test"
- "github.com/stretchr/testify/assert"
- "github.com/stretchr/testify/require"
+ . "github.com/onsi/ginkgo"
+ . "github.com/onsi/gomega"
corev1 "k8s.io/api/core/v1"
"k8s.io/apimachinery/pkg/api/resource"
metav1 "k8s.io/apimachinery/pkg/apis/meta/v1"
@@ -21,64 +19,33 @@ import (
const pvcName = "pvc-jenkins"
-func TestBackupAndRestore(t *testing.T) {
- t.Parallel()
- namespace, ctx := setupTest(t)
+func waitForJobCreation(jenkinsClient client.Jenkins, jobID string) {
+ By("waiting for Jenkins job creation")
- defer showLogsAndCleanup(t, ctx)
-
- jobID := "e2e-jenkins-operator"
- createPVC(t, namespace)
- jenkins := createJenkinsWithBackupAndRestoreConfigured(t, "e2e", namespace)
- waitForJenkinsUserConfigurationToComplete(t, jenkins)
-
- jenkinsClient, cleanUpFunc := verifyJenkinsAPIConnection(t, jenkins, namespace)
- defer cleanUpFunc()
- waitForJob(t, jenkinsClient, jobID)
- job, err := jenkinsClient.GetJob(jobID)
- require.NoError(t, err, job)
- i, err := job.InvokeSimple(map[string]string{})
- require.NoError(t, err, i)
- // FIXME: waitForJobToFinish use
- time.Sleep(60 * time.Second) // wait for the build to complete
-
- jenkins = getJenkins(t, jenkins.Namespace, jenkins.Name)
- lastDoneBackup := jenkins.Status.LastBackup
- restartJenkinsMasterPod(t, jenkins)
- waitForRecreateJenkinsMasterPod(t, jenkins)
- waitForJenkinsUserConfigurationToComplete(t, jenkins)
- jenkins = getJenkins(t, jenkins.Namespace, jenkins.Name)
- assert.Equal(t, lastDoneBackup, jenkins.Status.RestoredBackup)
- jenkinsClient2, cleanUpFunc2 := verifyJenkinsAPIConnection(t, jenkins, namespace)
- defer cleanUpFunc2()
- waitForJob(t, jenkinsClient2, jobID)
- verifyJobBuildsAfterRestoreBackup(t, jenkinsClient2, jobID)
-
- jenkins = getJenkins(t, jenkins.Namespace, jenkins.Name)
- lastDoneBackup = jenkins.Status.LastBackup
- resetJenkinsStatus(t, jenkins)
- waitForJenkinsUserConfigurationToComplete(t, jenkins)
- jenkins = getJenkins(t, jenkins.Namespace, jenkins.Name)
- assert.Equal(t, lastDoneBackup, jenkins.Status.RestoredBackup)
-}
-
-func waitForJob(t *testing.T, jenkinsClient client.Jenkins, jobID string) {
- err := try.Until(func() (end bool, err error) {
+ var err error
+ Eventually(func() (bool, error) {
_, err = jenkinsClient.GetJob(jobID)
+ if err != nil {
+ return false, err
+ }
return err == nil, err
- }, time.Second*2, time.Minute*3)
- require.NoErrorf(t, err, "Jenkins job '%s' not created by seed job", jobID)
+ }, time.Minute*3, time.Second*2).Should(BeTrue())
+
+ Expect(err).NotTo(HaveOccurred())
}
-func verifyJobBuildsAfterRestoreBackup(t *testing.T, jenkinsClient client.Jenkins, jobID string) {
+func verifyJobBuildsAfterRestoreBackup(jenkinsClient client.Jenkins, jobID string) {
+ By("checking if job builds after restoring backup")
+
job, err := jenkinsClient.GetJob(jobID)
- require.NoError(t, err)
+ Expect(err).NotTo(HaveOccurred())
build, err := job.GetLastBuild()
- require.NoError(t, err)
- assert.Equal(t, int64(1), build.GetBuildNumber())
+ Expect(err).NotTo(HaveOccurred())
+
+ Expect(build.GetBuildNumber()).To(Equal(int64(1)))
}
-func createPVC(t *testing.T, namespace string) {
+func createPVC(namespace string) {
pvc := &corev1.PersistentVolumeClaim{
ObjectMeta: metav1.ObjectMeta{
Name: pvcName,
@@ -94,11 +61,10 @@ func createPVC(t *testing.T, namespace string) {
},
}
- err := framework.Global.Client.Create(context.TODO(), pvc, nil)
- require.NoError(t, err)
+ Expect(k8sClient.Create(context.TODO(), pvc)).Should(Succeed())
}
-func createJenkinsWithBackupAndRestoreConfigured(t *testing.T, name, namespace string) *v1alpha2.Jenkins {
+func createJenkinsWithBackupAndRestoreConfigured(name, namespace string) *v1alpha2.Jenkins {
containerName := "backup"
jenkins := &v1alpha2.Jenkins{
TypeMeta: v1alpha2.JenkinsTypeMeta(),
@@ -128,6 +94,16 @@ func createJenkinsWithBackupAndRestoreConfigured(t *testing.T, name, namespace s
},
},
},
+ GroovyScripts: v1alpha2.GroovyScripts{
+ Customization: v1alpha2.Customization{
+ Configurations: []v1alpha2.ConfigMapRef{},
+ },
+ },
+ ConfigurationAsCode: v1alpha2.ConfigurationAsCode{
+ Customization: v1alpha2.Customization{
+ Configurations: []v1alpha2.ConfigMapRef{},
+ },
+ },
Master: v1alpha2.JenkinsMaster{
Containers: []v1alpha2.Container{
{
@@ -199,18 +175,20 @@ func createJenkinsWithBackupAndRestoreConfigured(t *testing.T, name, namespace s
},
},
}
- updateJenkinsCR(t, jenkins)
- t.Logf("Jenkins CR %+v", *jenkins)
- err := framework.Global.Client.Create(context.TODO(), jenkins, nil)
- require.NoError(t, err)
+ updateJenkinsCR(jenkins)
+
+ _, _ = fmt.Fprintf(GinkgoWriter, "Jenkins CR %+v\n", *jenkins)
+
+ Expect(k8sClient.Create(context.TODO(), jenkins)).Should(Succeed())
return jenkins
}
-func resetJenkinsStatus(t *testing.T, jenkins *v1alpha2.Jenkins) {
- jenkins = getJenkins(t, jenkins.Namespace, jenkins.Name)
+func resetJenkinsStatus(jenkins *v1alpha2.Jenkins) {
+ By("resetting Jenkins status")
+
+ jenkins = getJenkins(jenkins.Namespace, jenkins.Name)
jenkins.Status = v1alpha2.JenkinsStatus{}
- err := framework.Global.Client.Update(context.TODO(), jenkins)
- require.NoError(t, err)
+ Expect(k8sClient.Status().Update(context.TODO(), jenkins)).Should(Succeed())
}
diff --git a/test/e2e/seedjobs_test.go b/test/e2e/seedjobs_test.go
index 8ac0b11c..5c176680 100644
--- a/test/e2e/seedjobs_test.go
+++ b/test/e2e/seedjobs_test.go
@@ -2,24 +2,20 @@ package e2e
import (
"context"
- "encoding/json"
"fmt"
- "io/ioutil"
- "os"
- "testing"
"text/template"
"time"
+ "github.com/jenkinsci/kubernetes-operator/api/v1alpha2"
"github.com/jenkinsci/kubernetes-operator/internal/render"
"github.com/jenkinsci/kubernetes-operator/internal/try"
- "github.com/jenkinsci/kubernetes-operator/pkg/apis/jenkins/v1alpha2"
jenkinsclient "github.com/jenkinsci/kubernetes-operator/pkg/client"
"github.com/jenkinsci/kubernetes-operator/pkg/configuration/user/seedjobs"
"github.com/jenkinsci/kubernetes-operator/pkg/constants"
- framework "github.com/operator-framework/operator-sdk/pkg/test"
- "github.com/stretchr/testify/assert"
- "github.com/stretchr/testify/require"
+ "github.com/bndr/gojenkins"
+ . "github.com/onsi/ginkgo"
+ . "github.com/onsi/gomega"
corev1 "k8s.io/api/core/v1"
metav1 "k8s.io/apimachinery/pkg/apis/meta/v1"
)
@@ -32,10 +28,12 @@ type seedJobConfig struct {
PrivateKey string `json:"privateKey,omitempty"`
}
+/*
type seedJobsConfig struct {
SeedJobs []seedJobConfig `json:"seedJobs,omitempty"`
}
+// FIXME
func TestSeedJobs(t *testing.T) {
t.Parallel()
if seedJobConfigurationFile == nil || len(*seedJobConfigurationFile) == 0 {
@@ -67,22 +65,23 @@ func TestSeedJobs(t *testing.T) {
verifyJenkinsSeedJobs(t, jenkinsClient, seedJobsConfig.SeedJobs)
}
-func loadSeedJobsConfig(t *testing.T) seedJobsConfig {
+func loadSeedJobsConfig() seedJobsConfig {
+ //seedJobConfigurationFile = flag.String(seedJobConfigurationParameterName, "", "path to seed job config")
jsonFile, err := os.Open(*seedJobConfigurationFile)
- assert.NoError(t, err)
+ Expect(err).NotTo(HaveOccurred())
defer func() { _ = jsonFile.Close() }()
byteValue, err := ioutil.ReadAll(jsonFile)
- assert.NoError(t, err)
+ Expect(err).NotTo(HaveOccurred())
var result seedJobsConfig
err = json.Unmarshal(byteValue, &result)
- assert.NoError(t, err)
- assert.NotEmpty(t, result.SeedJobs)
+ Expect(err).NotTo(HaveOccurred())
+ Expect(result.SeedJobs).NotTo(BeEmpty())
return result
}
-
-func createKubernetesCredentialsProviderSecret(t *testing.T, namespace string, config seedJobConfig) {
+*/
+func createKubernetesCredentialsProviderSecret(namespace string, config seedJobConfig) {
if config.JenkinsCredentialType == v1alpha2.NoJenkinsCredentialCredentialType {
return
}
@@ -105,31 +104,32 @@ func createKubernetesCredentialsProviderSecret(t *testing.T, namespace string, c
},
}
- err := framework.Global.Client.Create(context.TODO(), secret, nil)
- require.NoError(t, err)
+ Expect(k8sClient.Create(context.TODO(), secret)).Should(Succeed())
}
-func verifyJenkinsSeedJobs(t *testing.T, jenkinsClient jenkinsclient.Jenkins, seedJobs []seedJobConfig) {
+func verifyJenkinsSeedJobs(jenkinsClient jenkinsclient.Jenkins, seedJobs []seedJobConfig) {
+ By("creating Jenkins jobs by seed jobs")
+
var err error
for _, seedJob := range seedJobs {
if seedJob.JenkinsCredentialType == v1alpha2.BasicSSHCredentialType || seedJob.JenkinsCredentialType == v1alpha2.UsernamePasswordCredentialType {
err = verifyIfJenkinsCredentialExists(jenkinsClient, seedJob.CredentialID)
- assert.NoErrorf(t, err, "Jenkins credential '%s' not created for seed job ID '%s'", seedJob.CredentialID, seedJob.ID)
+ Expect(err).NotTo(HaveOccurred(), fmt.Sprintf("Jenkins credential '%s' not created for seed job ID '%s'\n", seedJob.CredentialID, seedJob.ID))
}
- verifySeedJobProperties(t, jenkinsClient, seedJob)
+ verifySeedJobProperties(jenkinsClient, seedJob)
for _, requireJobName := range seedJob.JobNames {
err = try.Until(func() (end bool, err error) {
_, err = jenkinsClient.GetJob(requireJobName)
return err == nil, err
}, time.Second*2, time.Minute*2)
- assert.NoErrorf(t, err, "Jenkins job '%s' not created by seed job ID '%s'", requireJobName, seedJob.ID)
+ Expect(err).NotTo(HaveOccurred(), fmt.Sprintf("Jenkins job '%s' not created by seed job ID '%s'\n", requireJobName, seedJob.ID))
}
}
}
-func verifySeedJobProperties(t *testing.T, jenkinsClient jenkinsclient.Jenkins, seedJob seedJobConfig) {
+func verifySeedJobProperties(jenkinsClient jenkinsclient.Jenkins, seedJob seedJobConfig) {
data := struct {
ID string
CredentialID string
@@ -163,10 +163,10 @@ func verifySeedJobProperties(t *testing.T, jenkinsClient jenkinsclient.Jenkins,
}
groovyScript, err := render.Render(verifySeedJobPropertiesGroovyScriptTemplate, data)
- assert.NoError(t, err, groovyScript)
+ Expect(err).NotTo(HaveOccurred(), groovyScript)
logs, err := jenkinsClient.ExecuteScript(groovyScript)
- assert.NoError(t, err, logs, groovyScript)
+ Expect(err).NotTo(HaveOccurred(), logs, groovyScript)
}
func verifyIfJenkinsCredentialExists(jenkinsClient jenkinsclient.Jenkins, credentialName string) error {
@@ -294,3 +294,36 @@ for (BuildStep step : jobRef.getBuildersList()) {
}
}
`))
+
+func verifyJobCanBeRun(jenkinsClient jenkinsclient.Jenkins, jobID string) {
+ By("retrieving created Jenkins job")
+ job, err := jenkinsClient.GetJob(jobID)
+ Expect(err).To(BeNil())
+
+ By("running Jenkins job")
+ _, err = job.InvokeSimple(map[string]string{})
+ Expect(err).To(BeNil())
+
+ // FIXME: waitForJobToFinish use
+ By("waiting for the job to finish")
+ time.Sleep(100 * time.Second) // wait for the build to complete
+}
+func verifyJobHasBeenRunCorrectly(jenkinsClient jenkinsclient.Jenkins, jobID string) {
+ By("retrieving finished job")
+
+ var (
+ err error
+ job *gojenkins.Job
+ build *gojenkins.Build
+ )
+
+ Eventually(func() (bool, error) {
+ job, err = jenkinsClient.GetJob(jobID)
+ Expect(err).To(BeNil())
+ build, err = job.GetLastBuild()
+ Expect(err).To(BeNil())
+
+ By("evaluating correctness of the outcome")
+ return build.IsGood(), err
+ }, time.Duration(110)*retryInterval, retryInterval).Should(BeTrue())
+}
diff --git a/test/e2e/suite_test.go b/test/e2e/suite_test.go
new file mode 100644
index 00000000..eca535e4
--- /dev/null
+++ b/test/e2e/suite_test.go
@@ -0,0 +1,163 @@
+package e2e
+
+import (
+ "context"
+ "flag"
+ "fmt"
+ "path/filepath"
+ "testing"
+ "time"
+
+ "github.com/jenkinsci/kubernetes-operator/api/v1alpha2"
+ "github.com/jenkinsci/kubernetes-operator/controllers"
+ jenkinsClient "github.com/jenkinsci/kubernetes-operator/pkg/client"
+ "github.com/jenkinsci/kubernetes-operator/pkg/constants"
+ "github.com/jenkinsci/kubernetes-operator/pkg/event"
+ "github.com/jenkinsci/kubernetes-operator/pkg/notifications"
+ e "github.com/jenkinsci/kubernetes-operator/pkg/notifications/event"
+
+ . "github.com/onsi/ginkgo"
+ . "github.com/onsi/gomega"
+ corev1 "k8s.io/api/core/v1"
+ metav1 "k8s.io/apimachinery/pkg/apis/meta/v1"
+ "k8s.io/client-go/kubernetes"
+ "k8s.io/client-go/kubernetes/scheme"
+ "k8s.io/client-go/rest"
+ ctrl "sigs.k8s.io/controller-runtime"
+ "sigs.k8s.io/controller-runtime/pkg/client"
+ "sigs.k8s.io/controller-runtime/pkg/envtest"
+ "sigs.k8s.io/controller-runtime/pkg/envtest/printer"
+ logf "sigs.k8s.io/controller-runtime/pkg/log"
+ "sigs.k8s.io/controller-runtime/pkg/log/zap"
+ // +kubebuilder:scaffold:imports
+)
+
+var (
+ cfg *rest.Config
+ k8sClient client.Client
+ testEnv *envtest.Environment
+
+ hostname *string
+ port *int
+ useNodePort *bool
+)
+
+func init() {
+ 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.")
+}
+
+func TestAPIs(t *testing.T) {
+ RegisterFailHandler(Fail)
+
+ RunSpecsWithDefaultAndCustomReporters(t,
+ "Controller Suite",
+ []Reporter{printer.NewlineReporter{}})
+}
+
+var _ = BeforeSuite(func(done Done) {
+ logf.SetLogger(zap.New(zap.WriteTo(GinkgoWriter), zap.UseDevMode(false)))
+
+ By("bootstrapping test environment")
+ useExistingCluster := true
+ testEnv = &envtest.Environment{
+ CRDDirectoryPaths: []string{filepath.Join("..", "..", "config", "crd", "bases")},
+ //BinaryAssetsDirectory: path.Join("..", "..", "testbin", "bin"),
+ UseExistingCluster: &useExistingCluster,
+ }
+
+ var err error
+ cfg, err = testEnv.Start()
+ Expect(err).NotTo(HaveOccurred())
+ Expect(cfg).NotTo(BeNil())
+
+ err = v1alpha2.AddToScheme(scheme.Scheme)
+ Expect(err).NotTo(HaveOccurred())
+
+ // +kubebuilder:scaffold:scheme
+
+ // setup manager
+ k8sManager, err := ctrl.NewManager(cfg, ctrl.Options{
+ Scheme: scheme.Scheme,
+ })
+ Expect(err).NotTo(HaveOccurred())
+
+ // setup controller
+ clientSet, err := kubernetes.NewForConfig(cfg)
+ Expect(err).NotTo(HaveOccurred())
+
+ // setup events
+ events, err := event.New(cfg, constants.OperatorName)
+ Expect(err).NotTo(HaveOccurred())
+ notificationEvents := make(chan e.Event)
+ go notifications.Listen(notificationEvents, events, k8sClient)
+
+ jenkinsAPIConnectionSettings := jenkinsClient.JenkinsAPIConnectionSettings{
+ Hostname: *hostname,
+ UseNodePort: *useNodePort,
+ Port: *port,
+ }
+
+ err = (&controllers.JenkinsReconciler{
+ Client: k8sManager.GetClient(),
+ Scheme: k8sManager.GetScheme(),
+ JenkinsAPIConnectionSettings: jenkinsAPIConnectionSettings,
+ ClientSet: *clientSet,
+ Config: *cfg,
+ NotificationEvents: ¬ificationEvents,
+ KubernetesClusterDomain: "cluster.local",
+ }).SetupWithManager(k8sManager)
+ Expect(err).NotTo(HaveOccurred())
+
+ go func() {
+ err = k8sManager.Start(ctrl.SetupSignalHandler())
+ Expect(err).NotTo(HaveOccurred())
+ }()
+
+ k8sClient = k8sManager.GetClient()
+ Expect(k8sClient).NotTo(BeNil())
+ close(done)
+}, 60)
+
+var _ = AfterSuite(func() {
+ By("tearing down the test environment")
+ err := testEnv.Stop()
+ Expect(err).NotTo(HaveOccurred())
+})
+
+func createNamespace() *corev1.Namespace {
+ By("creating temporary namespace")
+
+ namespace := &corev1.Namespace{
+ ObjectMeta: metav1.ObjectMeta{
+ Name: fmt.Sprintf("%d", time.Now().Unix()),
+ },
+ }
+ Expect(k8sClient.Create(context.TODO(), namespace)).Should(Succeed())
+ return namespace
+}
+
+func destroyNamespace(namespace *corev1.Namespace) {
+ By("deleting temporary namespace")
+
+ Expect(k8sClient.Delete(context.TODO(), namespace)).Should(Succeed())
+
+ Eventually(func() (bool, error) {
+ namespaces := &corev1.NamespaceList{}
+ err := k8sClient.List(context.TODO(), namespaces)
+ if err != nil {
+ return false, err
+ }
+
+ exists := false
+ for _, namespaceItem := range namespaces.Items {
+ if namespaceItem.Name == namespace.Name {
+ exists = true
+ break
+ }
+ }
+
+ return !exists, nil
+ }, time.Second*120, time.Second).Should(BeTrue())
+}
diff --git a/test/e2e/wait.go b/test/e2e/wait.go
deleted file mode 100644
index ff3919bb..00000000
--- a/test/e2e/wait.go
+++ /dev/null
@@ -1,134 +0,0 @@
-package e2e
-
-import (
- goctx "context"
- "net/http"
- "testing"
- "time"
-
- "golang.org/x/net/context"
-
- "github.com/jenkinsci/kubernetes-operator/internal/try"
- "github.com/jenkinsci/kubernetes-operator/pkg/apis/jenkins/v1alpha2"
- jenkinsclient "github.com/jenkinsci/kubernetes-operator/pkg/client"
- "github.com/jenkinsci/kubernetes-operator/pkg/configuration/base/resources"
-
- framework "github.com/operator-framework/operator-sdk/pkg/test"
- "github.com/pkg/errors"
- "github.com/stretchr/testify/assert"
- "github.com/stretchr/testify/require"
- v1 "k8s.io/api/core/v1"
- metav1 "k8s.io/apimachinery/pkg/apis/meta/v1"
- "k8s.io/apimachinery/pkg/labels"
- "k8s.io/apimachinery/pkg/types"
- "k8s.io/apimachinery/pkg/util/wait"
- "sigs.k8s.io/controller-runtime/pkg/client"
-)
-
-var (
- retryInterval = time.Second * 5
- timeout = time.Second * 60
-)
-
-// checkConditionFunc is used to check if a condition for the jenkins CR is set
-type checkConditionFunc func(*v1alpha2.Jenkins, error) bool
-
-func waitForJenkinsBaseConfigurationToComplete(t *testing.T, jenkins *v1alpha2.Jenkins) {
- t.Log("Waiting for Jenkins base configuration to complete")
- _, err := WaitUntilJenkinsConditionSet(retryInterval, 170, jenkins, func(jenkins *v1alpha2.Jenkins, err error) bool {
- t.Logf("Current Jenkins status: '%+v', error '%s'", jenkins.Status, err)
- return err == nil && jenkins.Status.BaseConfigurationCompletedTime != nil
- })
- if err != nil {
- t.Fatal(err)
- }
- t.Log("Jenkins pod is running")
-
- // update jenkins CR because Operator sets default values
- namespacedName := types.NamespacedName{Namespace: jenkins.Namespace, Name: jenkins.Name}
- err = framework.Global.Client.Get(goctx.TODO(), namespacedName, jenkins)
- assert.NoError(t, err)
-}
-
-func waitForRecreateJenkinsMasterPod(t *testing.T, jenkins *v1alpha2.Jenkins) {
- err := wait.Poll(retryInterval, 30*retryInterval, func() (bool, error) {
- lo := metav1.ListOptions{
- LabelSelector: labels.SelectorFromSet(resources.GetJenkinsMasterPodLabels(*jenkins)).String(),
- }
- podList, err := framework.Global.KubeClient.CoreV1().Pods(jenkins.ObjectMeta.Namespace).List(lo)
- if err != nil {
- return false, err
- }
- if len(podList.Items) != 1 {
- return false, nil
- }
-
- return podList.Items[0].DeletionTimestamp == nil, nil
- })
- if err != nil {
- t.Fatal(err)
- }
- t.Log("Jenkins pod has been recreated")
-}
-
-func waitForJenkinsUserConfigurationToComplete(t *testing.T, jenkins *v1alpha2.Jenkins) {
- t.Log("Waiting for Jenkins user configuration to complete")
- _, err := WaitUntilJenkinsConditionSet(retryInterval, 110, jenkins, func(jenkins *v1alpha2.Jenkins, err error) bool {
- t.Logf("Current Jenkins status: '%+v', error '%s'", jenkins.Status, err)
- return err == nil && jenkins.Status.UserConfigurationCompletedTime != nil
- })
- if err != nil {
- t.Fatal(err)
- }
- t.Log("Jenkins pod is running")
-}
-
-func waitForJenkinsSafeRestart(t *testing.T, jenkinsClient jenkinsclient.Jenkins) {
- err := try.Until(func() (end bool, err error) {
- status, err := jenkinsClient.Poll()
- if err != nil {
- return false, err
- }
- if status != http.StatusOK {
- return false, errors.Wrap(err, "couldn't poll data from Jenkins API")
- }
- return true, nil
- }, time.Second, time.Second*70)
- require.NoError(t, err)
-}
-
-// WaitUntilJenkinsConditionSet retries until the specified condition check becomes true for the jenkins CR
-func WaitUntilJenkinsConditionSet(retryInterval time.Duration, retries int, jenkins *v1alpha2.Jenkins, checkCondition checkConditionFunc) (*v1alpha2.Jenkins, error) {
- jenkinsStatus := &v1alpha2.Jenkins{}
- err := wait.Poll(retryInterval, time.Duration(retries)*retryInterval, func() (bool, error) {
- namespacedName := types.NamespacedName{Namespace: jenkins.Namespace, Name: jenkins.Name}
- err := framework.Global.Client.Get(goctx.TODO(), namespacedName, jenkinsStatus)
- return checkCondition(jenkinsStatus, err), nil
- })
- if err != nil {
- return nil, err
- }
- return jenkinsStatus, nil
-}
-
-func waitUntilNamespaceDestroyed(namespace string) error {
- err := try.Until(func() (bool, error) {
- var namespaceList v1.NamespaceList
- err := framework.Global.Client.List(context.TODO(), &namespaceList, &client.ListOptions{})
- if err != nil {
- return true, err
- }
-
- exists := false
- for _, namespaceItem := range namespaceList.Items {
- if namespaceItem.Name == namespace {
- exists = true
- break
- }
- }
-
- return !exists, nil
- }, time.Second, time.Second*120)
-
- return err
-}
diff --git a/test/e2e/wait_test.go b/test/e2e/wait_test.go
new file mode 100644
index 00000000..9d511c79
--- /dev/null
+++ b/test/e2e/wait_test.go
@@ -0,0 +1,97 @@
+package e2e
+
+import (
+ "context"
+ "fmt"
+ "net/http"
+ "time"
+
+ "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/onsi/ginkgo"
+ . "github.com/onsi/gomega"
+ corev1 "k8s.io/api/core/v1"
+ metav1 "k8s.io/apimachinery/pkg/apis/meta/v1"
+ "k8s.io/apimachinery/pkg/labels"
+ "k8s.io/apimachinery/pkg/types"
+ "sigs.k8s.io/controller-runtime/pkg/client"
+)
+
+var (
+ retryInterval = time.Second * 5
+)
+
+func waitForJenkinsBaseConfigurationToComplete(jenkins *v1alpha2.Jenkins) {
+ By("waiting for Jenkins base configuration phase to complete")
+
+ Eventually(func() (*metav1.Time, error) {
+ actualJenkins := &v1alpha2.Jenkins{}
+ err := k8sClient.Get(context.TODO(), types.NamespacedName{Name: jenkins.Name, Namespace: jenkins.Namespace}, actualJenkins)
+ if err != nil {
+ return nil, err
+ }
+
+ return actualJenkins.Status.BaseConfigurationCompletedTime, nil
+ }, time.Duration(170)*retryInterval, retryInterval).Should(Not(BeNil()))
+
+ _, _ = fmt.Fprintf(GinkgoWriter, "Jenkins pod is running\n")
+
+ // update jenkins CR because Operator sets default values
+ namespacedName := types.NamespacedName{Namespace: jenkins.Namespace, Name: jenkins.Name}
+ Expect(k8sClient.Get(context.TODO(), namespacedName, jenkins)).Should(Succeed())
+}
+
+func waitForRecreateJenkinsMasterPod(jenkins *v1alpha2.Jenkins) {
+ By("waiting for Jenkins Master Pod recreation")
+
+ Eventually(func() (bool, error) {
+ lo := &client.ListOptions{
+ LabelSelector: labels.SelectorFromSet(resources.GetJenkinsMasterPodLabels(*jenkins)),
+ Namespace: jenkins.Namespace,
+ }
+ pods := &corev1.PodList{}
+ err := k8sClient.List(context.TODO(), pods, lo)
+ if err != nil {
+ return false, err
+ }
+ if len(pods.Items) != 1 {
+ return false, nil
+ }
+
+ return pods.Items[0].DeletionTimestamp == nil, nil
+ }, 30*retryInterval, retryInterval).Should(BeTrue())
+
+ _, _ = fmt.Fprintf(GinkgoWriter, "Jenkins pod has been recreated\n")
+}
+
+func waitForJenkinsUserConfigurationToComplete(jenkins *v1alpha2.Jenkins) {
+ By("waiting for Jenkins user configuration phase to complete")
+
+ Eventually(func() (*metav1.Time, error) {
+ actualJenkins := &v1alpha2.Jenkins{}
+ err := k8sClient.Get(context.TODO(), types.NamespacedName{Name: jenkins.Name, Namespace: jenkins.Namespace}, actualJenkins)
+ if err != nil {
+ return nil, err
+ }
+ return actualJenkins.Status.UserConfigurationCompletedTime, nil
+ }, time.Duration(110)*retryInterval, retryInterval).Should(Not(BeNil()))
+ _, _ = fmt.Fprintf(GinkgoWriter, "Jenkins instance is up and ready\n")
+}
+
+func waitForJenkinsSafeRestart(jenkinsClient jenkinsclient.Jenkins) {
+ By("waiting for Jenkins safe restart")
+
+ Eventually(func() (bool, error) {
+ status, err := jenkinsClient.Poll()
+ _, _ = fmt.Fprintf(GinkgoWriter, "Safe restart status: %+v, err: %s\n", status, err)
+ if err != nil {
+ return false, err
+ }
+ if status != http.StatusOK {
+ return false, err
+ }
+ return true, nil
+ }, time.Second*200, time.Second*5).Should(BeTrue())
+}
diff --git a/tools.go b/tools.go
deleted file mode 100644
index 4da112f6..00000000
--- a/tools.go
+++ /dev/null
@@ -1,15 +0,0 @@
-// +build tools
-
-package tools
-
-import (
- // Code generators built at runtime.
- _ "k8s.io/code-generator/cmd/client-gen"
- _ "k8s.io/code-generator/cmd/conversion-gen"
- _ "k8s.io/code-generator/cmd/deepcopy-gen"
- _ "k8s.io/code-generator/cmd/informer-gen"
- _ "k8s.io/code-generator/cmd/lister-gen"
- _ "k8s.io/gengo/args"
- _ "k8s.io/kube-openapi/cmd/openapi-gen"
- _ "sigs.k8s.io/controller-tools/pkg/crd/generator"
-)
diff --git a/variables.mk b/variables.mk
new file mode 100644
index 00000000..0c310b53
--- /dev/null
+++ b/variables.mk
@@ -0,0 +1,101 @@
+SHELL := /bin/bash
+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 := ./
+
+# 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 | grep -v controllers)
+
+# 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
+
+PLATFORM = $(shell echo $(UNAME_S) | tr A-Z a-z)
+CPUS_NUMBER = 3
+##################### FROM OPERATOR SDK ########################
+
+# Default bundle image tag
+BUNDLE_IMG ?= controller-bundle:$(VERSION)
+# Options for 'bundle-build'
+ifneq ($(origin CHANNELS), undefined)
+BUNDLE_CHANNELS := --channels=$(CHANNELS)
+endif
+ifneq ($(origin DEFAULT_CHANNEL), undefined)
+BUNDLE_DEFAULT_CHANNEL := --default-channel=$(DEFAULT_CHANNEL)
+endif
+BUNDLE_METADATA_OPTS ?= $(BUNDLE_CHANNELS) $(BUNDLE_DEFAULT_CHANNEL)
+
+# Image URL to use all building/pushing image targets
+IMG ?= controller:latest
+# Produce CRDs that work back to Kubernetes 1.11 (no version conversion)
+CRD_OPTIONS ?= "crd:trivialVersions=true,preserveUnknownFields=false"
+
+# Get the currently used golang install path (in GOPATH/bin, unless GOBIN is set)
+ifeq (,$(shell go env GOBIN))
+GOBIN=$(shell go env GOPATH)/bin
+else
+GOBIN=$(shell go env GOBIN)
+endif
+
+ENVTEST_ASSETS_DIR=$(shell pwd)/testbin
+
+PROJECT_DIR := $(shell dirname $(abspath $(lastword $(MAKEFILE_LIST))))
\ No newline at end of file
diff --git a/website/content/en/docs/Developer Guide/Preview/_index.md b/website/content/en/docs/Developer Guide/Preview/_index.md
new file mode 100644
index 00000000..595fe717
--- /dev/null
+++ b/website/content/en/docs/Developer Guide/Preview/_index.md
@@ -0,0 +1,301 @@
+---
+title: "Developer Guide"
+linkTitle: "Developer Guide - Preview"
+weight: 60
+date: 2020-02-09
+description: >
+ Jenkins Operator for developers
+---
+
+{{% pageinfo %}}
+This document explains how to setup your development environment.
+{{% /pageinfo %}}
+
+## Prerequisites
+
+- [operator_sdk][operator_sdk] version 1.3.0
+- [git][git_tool]
+- [go][go_tool] version 1.15.6
+- [goimports, golint, checkmake and staticcheck][install_dev_tools]
+- [minikube][minikube] version 1.17.1 (preferred Hypervisor - [virtualbox][virtualbox]) (automatically downloaded)
+- [docker][docker_tool] version 17.03+
+
+## Clone repository and download dependencies
+
+```bash
+git clone git@github.com:jenkinsci/kubernetes-operator.git
+cd kubernetes-operator
+make go-dependencies
+```
+
+## Build and run with a minikube
+
+Start minikube instance configured for **Jenkins Operator**. Appropriate minikube version will be downloaded to bin folder.
+```bash
+make minikube-start
+```
+Next run **Jenkins Operator** locally.
+```bash
+make run
+```
+Console output indicating readiness of this phase:
+```bash
++ build
++ run
+kubectl config use-context minikube
+Switched to context "minikube".
+Watching 'default' namespace
+bin/manager --jenkins-api-hostname=192.168.99.252 --jenkins-api-port=0 --jenkins-api-use-nodeport=true --cluster-domain=cluster.local
+2021-02-08T14:14:45.263+0100 INFO cmd Version: v0.5.0
+2021-02-08T14:14:45.263+0100 INFO cmd Git commit: 305dbeda-dirty-dirty
+2021-02-08T14:14:45.264+0100 INFO cmd Go Version: go1.15.6
+2021-02-08T14:14:45.264+0100 INFO cmd Go OS/Arch: darwin/amd64
+2021-02-08T14:14:45.264+0100 INFO cmd Watch namespace: default
+2021-02-08T14:14:45.592+0100 INFO controller-runtime.metrics metrics server is starting to listen {"addr": "0.0.0.0:8383"}
+2021-02-08T14:14:45.599+0100 INFO cmd starting manager
+2021-02-08T14:14:45.599+0100 INFO controller-runtime.manager starting metrics server {"path": "/metrics"}
+2021-02-08T14:14:45.599+0100 INFO controller-runtime.manager.controller.jenkins Starting EventSource {"reconciler group": "jenkins.io", "reconciler kind": "Jenkins", "source": "kind source: jenkins.io/v1alpha2, Kind=Jenkins"}
+2021-02-08T14:14:45.700+0100 INFO controller-runtime.manager.controller.jenkins Starting EventSource {"reconciler group": "jenkins.io", "reconciler kind": "Jenkins", "source": "kind source: /, Kind="}
+2021-02-08T14:14:45.800+0100 INFO controller-runtime.manager.controller.jenkins Starting EventSource {"reconciler group": "jenkins.io", "reconciler kind": "Jenkins", "source": "kind source: /, Kind="}
+2021-02-08T14:14:45.901+0100 INFO controller-runtime.manager.controller.jenkins Starting EventSource {"reconciler group": "jenkins.io", "reconciler kind": "Jenkins", "source": "kind source: /, Kind="}
+2021-02-08T14:14:46.003+0100 INFO controller-runtime.manager.controller.jenkins Starting EventSource {"reconciler group": "jenkins.io", "reconciler kind": "Jenkins", "source": "kind source: core/v1, Kind=Secret"}
+2021-02-08T14:14:46.004+0100 INFO controller-runtime.manager.controller.jenkins Starting EventSource {"reconciler group": "jenkins.io", "reconciler kind": "Jenkins", "source": "kind source: core/v1, Kind=ConfigMap"}
+2021-02-08T14:14:46.004+0100 INFO controller-runtime.manager.controller.jenkins Starting EventSource {"reconciler group": "jenkins.io", "reconciler kind": "Jenkins", "source": "kind source: jenkins.io/v1alpha2, Kind=Jenkins"}
+2021-02-08T14:14:46.004+0100 INFO controller-runtime.manager.controller.jenkins Starting Controller {"reconciler group": "jenkins.io", "reconciler kind": "Jenkins"}
+2021-02-08T14:14:46.004+0100 INFO controller-runtime.manager.controller.jenkins Starting workers {"reconciler group": "jenkins.io", "reconciler kind": "Jenkins", "worker count": 1}
+
+```
+Lastly apply Jenkins Custom Resource to minikube cluster:
+```bash
+kubectl apply -f config/samples/jenkins.io_v1alpha2_jenkins.yaml
+
+{"level":"info","ts":1612790690.875426,"logger":"controller-jenkins","msg":"Setting default Jenkins container command","cr":"jenkins-example"}
+{"level":"info","ts":1612790690.8754492,"logger":"controller-jenkins","msg":"Setting default Jenkins container JAVA_OPTS environment variable","cr":"jenkins-example"}
+{"level":"info","ts":1612790690.875456,"logger":"controller-jenkins","msg":"Setting default operator plugins","cr":"jenkins-example"}
+{"level":"info","ts":1612790690.875463,"logger":"controller-jenkins","msg":"Setting default Jenkins master service","cr":"jenkins-example"}
+{"level":"info","ts":1612790690.875467,"logger":"controller-jenkins","msg":"Setting default Jenkins slave service","cr":"jenkins-example"}
+{"level":"info","ts":1612790690.881811,"logger":"controller-jenkins","msg":"*v1alpha2.Jenkins/jenkins-example has been updated","cr":"jenkins-example"}
+{"level":"info","ts":1612790691.252834,"logger":"controller-jenkins","msg":"Creating a new Jenkins Master Pod default/jenkins-jenkins-example","cr":"jenkins-example"}
+{"level":"info","ts":1612790691.322793,"logger":"controller-jenkins","msg":"Jenkins master pod restarted by operator:","cr":"jenkins-example"}
+{"level":"info","ts":1612790691.322817,"logger":"controller-jenkins","msg":"Jenkins Operator version has changed, actual '' new 'v0.5.0'","cr":"jenkins-example"}
+{"level":"info","ts":1612790691.3228202,"logger":"controller-jenkins","msg":"Jenkins CR has been replaced","cr":"jenkins-example"}
+{"level":"info","ts":1612790695.8789551,"logger":"controller-jenkins","msg":"Creating a new Jenkins Master Pod default/jenkins-jenkins-example","cr":"jenkins-example"}
+{"level":"warn","ts":1612790817.9423082,"logger":"controller-jenkins","msg":"Reconcile loop failed: couldn't init Jenkins API client: Get \"http://192.168.99.254:31998/api/json\": dial tcp 192.168.99.254:31998: connect: connection refused","cr":"jenkins-example"}
+{"level":"warn","ts":1612790817.9998221,"logger":"controller-jenkins","msg":"Reconcile loop failed: couldn't init Jenkins API client: Get \"http://192.168.99.254:31998/api/json\": dial tcp 192.168.99.254:31998: connect: connection refused","cr":"jenkins-example"}
+{"level":"info","ts":1612790818.581316,"logger":"controller-jenkins","msg":"base-groovy ConfigMap 'jenkins-operator-base-configuration-jenkins-example' name '1-basic-settings.groovy' running groovy script","cr":"jenkins-example"}
+...
+{"level":"info","ts":1612790820.9473379,"logger":"controller-jenkins","msg":"base-groovy ConfigMap 'jenkins-operator-base-configuration-jenkins-example' name '8-disable-job-dsl-script-approval.groovy' running groovy script","cr":"jenkins-example"}
+{"level":"info","ts":1612790821.244055,"logger":"controller-jenkins","msg":"Base configuration phase is complete, took 2m6s","cr":"jenkins-example"}
+{"level":"info","ts":1612790821.7953842,"logger":"controller-jenkins","msg":"Waiting for Seed Job Agent `seed-job-agent`...","cr":"jenkins-example"}
+...
+
+{"level":"info","ts":1612790851.843638,"logger":"controller-jenkins","msg":"Waiting for Seed Job Agent `seed-job-agent`...","cr":"jenkins-example"}
+{"level":"info","ts":1612790853.489524,"logger":"controller-jenkins","msg":"User configuration phase is complete, took 2m38s","cr":"jenkins-example"}
+
+Two log lines says that Jenkins Operator works correctly:
+
+* `Base configuration phase is complete` - ensures manifests, Jenkins pod, Jenkins configuration and Jenkins API token
+* `User configuration phase is complete` - ensures Jenkins restore, backup and seed jobs along with user configuration
+
+> Details about base and user phase can be found [here](https://jenkinsci.github.io/kubernetes-operator/docs/how-it-works/architecture-and-design/).
+
+
+```bash
+kubectl get jenkins -o yaml
+
+apiVersion: v1
+items:
+- apiVersion: jenkins.io/v1alpha2
+ kind: Jenkins
+ metadata:
+ ...
+ spec:
+ backup:
+ action: {}
+ containerName: ""
+ interval: 0
+ makeBackupBeforePodDeletion: false
+ configurationAsCode:
+ configurations: []
+ secret:
+ name: ""
+ groovyScripts:
+ configurations: []
+ secret:
+ name: ""
+ jenkinsAPISettings:
+ authorizationStrategy: createUser
+ master:
+ basePlugins:
+ ...
+ containers:
+ - command:
+ - bash
+ - -c
+ - /var/jenkins/scripts/init.sh && exec /sbin/tini -s -- /usr/local/bin/jenkins.sh
+ env:
+ - name: JAVA_OPTS
+ value: -XX:+UnlockExperimentalVMOptions -XX:+UseCGroupMemoryLimitForHeap
+ -XX:MaxRAMFraction=1 -Djenkins.install.runSetupWizard=false -Djava.awt.headless=true
+ image: jenkins/jenkins:2.263.3-lts-alpine
+ imagePullPolicy: Always
+ livenessProbe:
+ ...
+ readinessProbe:
+ ...
+ resources:
+ limits:
+ cpu: 1500m
+ memory: 3Gi
+ requests:
+ cpu: "1"
+ memory: 500Mi
+ disableCSRFProtection: false
+ restore:
+ action: {}
+ containerName: ""
+ getLatestAction: {}
+ seedJobs:
+ - additionalClasspath: ""
+ bitbucketPushTrigger: false
+ buildPeriodically: ""
+ description: Jenkins Operator repository
+ failOnMissingPlugin: false
+ githubPushTrigger: false
+ id: jenkins-operator
+ ignoreMissingFiles: false
+ pollSCM: ""
+ repositoryBranch: master
+ repositoryUrl: https://github.com/jenkinsci/kubernetes-operator.git
+ targets: cicd/jobs/*.jenkins
+ unstableOnDeprecation: false
+ service:
+ port: 8080
+ type: NodePort
+ serviceAccount: {}
+ slaveService:
+ port: 50000
+ type: ClusterIP
+ status:
+ appliedGroovyScripts:
+ - configurationType: base-groovy
+ hash: 2ownqpRyBjQYmzTRttUx7axok3CKe2E45frI5iRwH0w=
+ name: 1-basic-settings.groovy
+ source: jenkins-operator-base-configuration-jenkins-example
+ ...
+ baseConfigurationCompletedTime: "2021-02-08T13:27:01Z"
+ createdSeedJobs:
+ - jenkins-operator
+ operatorVersion: v0.5.0
+ provisionStartTime: "2021-02-08T13:24:55Z"
+ userAndPasswordHash: nnfZsWmFfAYlYyVYeKhWW2KB4L8mE61JUfetAsr9IMM=
+ userConfigurationCompletedTime: "2021-02-08T13:27:33Z"
+kind: List
+metadata:
+ resourceVersion: ""
+ selfLink: ""
+```
+
+```bash
+kubectl get po
+
+NAME READY STATUS RESTARTS AGE
+jenkins-jenkins-example 1/1 Running 0 23m
+seed-job-agent-jenkins-example-758cc7cc5c-82hbl 1/1 Running 0 21m
+
+```
+
+### Debug Jenkins Operator
+
+```bash
+make run OPERATOR_EXTRA_ARGS="--debug"
+```
+
+## Build and run with a remote Kubernetes cluster
+
+You can also run the controller locally and make it listen to a remote Kubernetes server.
+
+```bash
+make run NAMESPACE=default KUBECTL_CONTEXT=remote-k8s EXTRA_ARGS='--kubeconfig ~/.kube/config'
+```
+
+Once **Jenkins Operator** are up and running, apply Jenkins custom resource:
+
+```bash
+kubectl --context remote-k8s --namespace default apply -f deploy/crds/jenkins_v1alpha2_jenkins_cr.yaml
+kubectl --context remote-k8s --namespace default get jenkins -o yaml
+kubectl --context remote-k8s --namespace default get po
+```
+
+## Testing
+
+Tests are written using [Ginkgo](https://onsi.github.io/ginkgo/) with [Gomega](https://onsi.github.io/gomega/).
+
+Run unit tests with go fmt, lint, statickcheck, vet:
+
+```bash
+make verify
+```
+
+Run unit tests only:
+
+```bash
+make test
+```
+
+### Running E2E tests
+
+Run e2e tests with minikube:
+
+```bash
+make minikube-start
+make e2e
+```
+
+Run the specific e2e test:
+
+```bash
+make e2e E2E_TEST_SELECTOR='^TestConfiguration$'
+```
+
+### Building docker image on minikube
+
+To be able to work with the docker daemon on `minikube` machine run the following command before building an image:
+
+```bash
+eval $(bin/minikube docker-env)
+```
+
+### When `api/v1alpha2/jenkins_types.go` has changed
+
+Run:
+
+```bash
+make manifests
+```
+
+### Getting the Jenkins URL and basic credentials
+
+```bash
+minikube service jenkins-operator-http- --url
+kubectl get secret jenkins-operator-credentials- -o 'jsonpath={.data.user}' | base64 -d
+kubectl get secret jenkins-operator-credentials- -o 'jsonpath={.data.password}' | base64 -d
+```
+
+[dep_tool]:https://golang.github.io/dep/docs/installation.html
+[git_tool]:https://git-scm.com/downloads
+[go_tool]:https://golang.org/dl/
+[operator_sdk]:https://github.com/operator-framework/operator-sdk
+[fork_guide]:https://help.github.com/articles/fork-a-repo/
+[docker_tool]:https://docs.docker.com/install/
+[kubectl_tool]:https://kubernetes.io/docs/tasks/tools/install-kubectl/
+[minikube]:https://kubernetes.io/docs/tasks/tools/install-minikube/
+[virtualbox]:https://www.virtualbox.org/wiki/Downloads
+[install_dev_tools]:https://jenkinsci.github.io/kubernetes-operator/docs/developer-guide/tools/
+
+## Self-learning
+
+* [Tutorial: Deep Dive into the Operator Framework for... Melvin Hillsman, Michael Hrivnak, & Matt Dorn
+](https://www.youtube.com/watch?v=8_DaCcRMp5I)
+
+* [Operator Framework Training By OpenShift](https://www.katacoda.com/openshift/courses/operatorframework)
diff --git a/website/content/en/docs/Developer Guide/_index.md b/website/content/en/docs/Developer Guide/_index.md
index 17a331ec..b7a594c5 100644
--- a/website/content/en/docs/Developer Guide/_index.md
+++ b/website/content/en/docs/Developer Guide/_index.md
@@ -152,6 +152,7 @@ items:
restore:
action: {}
containerName: ""
+ getLatestAction: {}
seedJobs:
- additionalClasspath: ""
bitbucketPushTrigger: false
@@ -244,7 +245,6 @@ Run e2e tests with minikube:
```bash
make minikube-start
-eval $(minikube docker-env)
make e2e
```
diff --git a/website/content/en/docs/Getting Started/Preview/_index.md b/website/content/en/docs/Getting Started/Preview/_index.md
new file mode 100644
index 00000000..64cd1b66
--- /dev/null
+++ b/website/content/en/docs/Getting Started/Preview/_index.md
@@ -0,0 +1,18 @@
+---
+title: "Preview"
+linkTitle: "Preview"
+weight: 10
+date: 2021-01-18
+description: >
+ How to work with jenkins-operator to be released version
+---
+
+{{% pageinfo %}}
+This document describes a getting started guide for **Jenkins Operator** currently in preview version and an additional configuration.
+{{% /pageinfo %}}
+
+## First Steps
+
+Prepare your Kubernetes cluster and set up your `kubectl` access.
+
+Once you have running Kubernetes cluster you can focus on installing **Jenkins Operator** according to the [Installation](/kubernetes-operator/docs/installation/) guide.
diff --git a/website/content/en/docs/Getting Started/Preview/aks.md b/website/content/en/docs/Getting Started/Preview/aks.md
new file mode 100644
index 00000000..166643c9
--- /dev/null
+++ b/website/content/en/docs/Getting Started/Preview/aks.md
@@ -0,0 +1,24 @@
+---
+title: "AKS"
+linkTitle: "AKS"
+weight: 10
+date: 2021-01-18
+description: >
+ Additional configuration for Azure Kubernetes Service
+---
+
+Azure AKS managed Kubernetes service adds to every pod the following environment variables:
+
+```yaml
+- name: KUBERNETES_PORT_443_TCP_ADDR
+ value:
+- name: KUBERNETES_PORT
+ value: tcp://
+- name: KUBERNETES_PORT_443_TCP
+ value: tcp://
+- name: KUBERNETES_SERVICE_HOST
+ value:
+```
+
+The operator is aware of it and omits these environment variables when checking if a Jenkins pod environment has been changed. It prevents the
+restart of a Jenkins pod over and over again.
\ No newline at end of file
diff --git a/website/content/en/docs/Getting Started/Preview/configuration.md b/website/content/en/docs/Getting Started/Preview/configuration.md
new file mode 100644
index 00000000..19721edd
--- /dev/null
+++ b/website/content/en/docs/Getting Started/Preview/configuration.md
@@ -0,0 +1,315 @@
+---
+title: "Configuration"
+linkTitle: "Configuration"
+weight: 2
+date: 2021-01-25
+description: >
+ How to configure Jenkins with Operator
+---
+
+## Configure Seed Jobs and Pipelines
+
+Jenkins operator uses [job-dsl][job-dsl] and [kubernetes-credentials-provider][kubernetes-credentials-provider] plugins for configuring jobs
+and deploy keys.
+
+## Prepare job definitions and pipelines
+
+First you have to prepare pipelines and job definition in your GitHub repository using the following structure:
+
+```
+cicd/
+├── jobs
+│  └── k8s.jenkins
+└── pipelines
+ └── k8s.jenkins
+```
+
+**`cicd/jobs/k8s.jenkins`** is a job definition:
+
+```
+#!/usr/bin/env groovy
+
+pipelineJob('k8s-e2e') {
+ displayName('Kubernetes Plugin E2E Test')
+
+ logRotator {
+ numToKeep(10)
+ daysToKeep(30)
+ }
+
+ configure { project ->
+ project / 'properties' / 'org.jenkinsci.plugins.workflow.job.properties.DurabilityHintJobProperty' {
+ hint('PERFORMANCE_OPTIMIZED')
+ }
+ }
+
+ definition {
+ cpsScm {
+ scm {
+ git {
+ remote {
+ url('https://github.com/jenkinsci/kubernetes-operator.git')
+ credentials('jenkins-operator')
+ }
+ branches('*/master')
+ }
+ }
+ scriptPath('cicd/pipelines/k8s.jenkins')
+ }
+ }
+}
+```
+
+**`cicd/pipelines/k8s.jenkins`** is an actual Jenkins pipeline:
+
+```
+#!/usr/bin/env groovy
+
+def label = "k8s-${UUID.randomUUID().toString()}"
+def home = "/home/jenkins"
+def workspace = "${home}/workspace/build-jenkins-operator"
+def workdir = "${workspace}/src/github.com/jenkinsci/kubernetes-operator/"
+
+podTemplate(label: label,
+ containers: [
+ containerTemplate(name: 'alpine', image: 'alpine:3.11', ttyEnabled: true, command: 'cat'),
+ ],
+ ) {
+ node(label) {
+ stage('Run shell') {
+ container('alpine') {
+ sh 'echo "hello world"'
+ }
+ }
+ }
+}
+```
+
+## Configure Seed Jobs
+
+Jenkins Seed Jobs are configured using `Jenkins.spec.seedJobs` section from your custom resource manifest:
+
+```
+apiVersion: jenkins.io/v1alpha2
+kind: Jenkins
+metadata:
+ name: example
+spec:
+ seedJobs:
+ - id: jenkins-operator
+ targets: "cicd/jobs/*.jenkins"
+ description: "Jenkins Operator repository"
+ repositoryBranch: master
+ repositoryUrl: https://github.com/jenkinsci/kubernetes-operator.git
+```
+
+**Jenkins Operator** will automatically discover and configure all the seed jobs.
+
+You can verify if deploy keys were successfully configured in the Jenkins **Credentials** tab.
+
+
+
+You can verify if your pipelines were successfully configured in the Jenkins Seed Job console output.
+
+
+
+If your GitHub repository is **private** you have to configure SSH or username/password authentication.
+
+### SSH authentication
+
+#### Generate SSH Keys
+
+There are two methods of SSH private key generation:
+
+```bash
+$ openssl genrsa -out 2048
+```
+
+or
+
+```bash
+$ ssh-keygen -t rsa -b 2048
+$ ssh-keygen -p -f -m pem
+```
+
+Then copy content from generated file.
+
+#### Public key
+
+If you want to upload your public key to your Git server you need to extract it.
+
+If key was generated by `openssl` then you need to type this to extract public key:
+
+```bash
+$ openssl rsa -in -pubout > .pub
+```
+
+If key was generated by `ssh-keygen` the public key content is located in .pub and there is no need to extract public key
+
+#### Configure SSH authentication
+
+Configure a seed job like this:
+
+```
+apiVersion: jenkins.io/v1alpha2
+kind: Jenkins
+metadata:
+ name: example
+spec:
+ seedJobs:
+ - id: jenkins-operator-ssh
+ credentialType: basicSSHUserPrivateKey
+ credentialID: k8s-ssh
+ targets: "cicd/jobs/*.jenkins"
+ description: "Jenkins Operator repository"
+ repositoryBranch: master
+ repositoryUrl: ssh://git@github.com:jenkinsci/kubernetes-operator.git
+```
+
+and create a Kubernetes Secret (name of secret should be the same from `credentialID` field):
+
+```
+apiVersion: v1
+kind: Secret
+metadata:
+ name: k8s-ssh
+ labels:
+ "jenkins.io/credentials-type": "basicSSHUserPrivateKey"
+ annotations:
+ "jenkins.io/credentials-description" : "ssh github.com:jenkinsci/kubernetes-operator"
+stringData:
+ privateKey: |
+ -----BEGIN RSA PRIVATE KEY-----
+ MIIJKAIBAAKCAgEAxxDpleJjMCN5nusfW/AtBAZhx8UVVlhhhIKXvQ+dFODQIdzO
+ oDXybs1zVHWOj31zqbbJnsfsVZ9Uf3p9k6xpJ3WFY9b85WasqTDN1xmSd6swD4N8
+ ...
+ username: github_user_name
+```
+
+### Username & password authentication
+
+Configure the seed job like:
+
+```
+apiVersion: jenkins.io/v1alpha2
+kind: Jenkins
+metadata:
+ name: example
+spec:
+ seedJobs:
+ - id: jenkins-operator-user-pass
+ credentialType: usernamePassword
+ credentialID: k8s-user-pass
+ targets: "cicd/jobs/*.jenkins"
+ description: "Jenkins Operator repository"
+ repositoryBranch: master
+ repositoryUrl: https://github.com/jenkinsci/kubernetes-operator.git
+```
+
+and create a Kubernetes Secret (name of secret should be the same from `credentialID` field):
+
+```
+apiVersion: v1
+kind: Secret
+metadata:
+ name: k8s-user-pass
+stringData:
+ username: github_user_name
+ password: password_or_token
+```
+
+### External authentication
+You can use `external` credential type if you want to configure authentication using Configuration As Code or Groovy Script.
+
+## HTTP Proxy for downloading plugins
+
+To use forwarding proxy with an operator to download plugins you need to add the following environment variable to Jenkins Custom Resource (CR), e.g.:
+
+```yaml
+spec:
+ master:
+ containers:
+ - name: jenkins-master
+ env:
+ - name: CURL_OPTIONS
+ value: -L -x
+```
+
+In `CURL_OPTIONS` var you can set additional arguments to `curl` command.
+
+## Pulling Docker images from private repositories
+
+To pull a Docker Image from private repository you can use `imagePullSecrets`.
+
+Please follow the instructions on [creating a secret with a docker config](https://kubernetes.io/docs/concepts/containers/images/?origin_team=T42NTAGHM#creating-a-secret-with-a-docker-config).
+
+### Docker Hub Configuration
+To use Docker Hub additional steps are required.
+
+Edit the previously created secret:
+```bash
+kubectl -n edit secret
+```
+
+The `.dockerconfigjson` key's value needs to be replaced with a modified version.
+
+After modifications, it needs to be encoded as a Base64 value before setting the `.dockerconfigjson` key.
+
+Example config file to modify and use:
+```
+{
+ "auths":{
+ "https://index.docker.io/v1/":{
+ "username":"user",
+ "password":"password",
+ "email":"yourdockeremail@gmail.com",
+ "auth":"base64 of string user:password"
+ },
+ "auth.docker.io":{
+ "username":"user",
+ "password":"password",
+ "email":"yourdockeremail@gmail.com",
+ "auth":"base64 of string user:password"
+ },
+ "registry.docker.io":{
+ "username":"user",
+ "password":"password",
+ "email":"yourdockeremail@gmail.com",
+ "auth":"base64 of string user:password"
+ },
+ "docker.io":{
+ "username":"user",
+ "password":"password",
+ "email":"yourdockeremail@gmail.com",
+ "auth":"base64 of string user:password"
+ },
+ "https://registry-1.docker.io/v2/": {
+ "username":"user",
+ "password":"password",
+ "email":"yourdockeremail@gmail.com",
+ "auth":"base64 of string user:password"
+ },
+ "registry-1.docker.io/v2/": {
+ "username":"user",
+ "password":"password",
+ "email":"yourdockeremail@gmail.com",
+ "auth":"base64 of string user:password"
+ },
+ "registry-1.docker.io": {
+ "username":"user",
+ "password":"password",
+ "email":"yourdockeremail@gmail.com",
+ "auth":"base64 of string user:password"
+ },
+ "https://registry-1.docker.io": {
+ "username":"user",
+ "password":"password",
+ "email":"yourdockeremail@gmail.com",
+ "auth":"base64 of string user:password"
+ }
+ }
+}
+```
+
+[job-dsl]:https://github.com/jenkinsci/job-dsl-plugin
+[kubernetes-credentials-provider]:https://jenkinsci.github.io/kubernetes-credentials-provider-plugin/
diff --git a/website/content/en/docs/Getting Started/Preview/configure-backup-and-restore.md b/website/content/en/docs/Getting Started/Preview/configure-backup-and-restore.md
new file mode 100644
index 00000000..a5a4135c
--- /dev/null
+++ b/website/content/en/docs/Getting Started/Preview/configure-backup-and-restore.md
@@ -0,0 +1,90 @@
+---
+title: "Configure backup and restore"
+linkTitle: "Configure backup and restore"
+weight: 10
+date: 2021-01-25
+description: >
+ Prevent loss of job history
+---
+
+Backup and restore is done by a container sidecar.
+
+### PVC
+
+#### Create PVC
+
+Save to the file named pvc.yaml:
+```yaml
+apiVersion: v1
+kind: PersistentVolumeClaim
+metadata:
+ name:
+ namespace:
+spec:
+ accessModes:
+ - ReadWriteOnce
+ resources:
+ requests:
+ storage: 500Gi
+```
+
+Run the following command:
+```bash
+$ kubectl -n create -f pvc.yaml
+```
+
+#### Configure Jenkins CR
+
+```yaml
+apiVersion: jenkins.io/v1alpha2
+kind: Jenkins
+metadata:
+ name:
+ namespace:
+spec:
+ master:
+ securityContext:
+ runAsUser: 1000
+ fsGroup: 1000
+ containers:
+ - name: jenkins-master
+ image: jenkins/jenkins:2.263.2-lts-alpine
+ - name: backup # container responsible for the backup and restore
+ env:
+ - name: BACKUP_DIR
+ value: /backup
+ - name: JENKINS_HOME
+ value: /jenkins-home
+ - name: BACKUP_COUNT
+ value: "3" # keep only the 2 most recent backups
+ image: virtuslab/jenkins-operator-backup-pvc:v0.1.0 # look at backup/pvc directory
+ imagePullPolicy: IfNotPresent
+ volumeMounts:
+ - mountPath: /jenkins-home # Jenkins home volume
+ name: jenkins-home
+ - mountPath: /backup # backup volume
+ name: backup
+ volumes:
+ - name: backup # PVC volume where backups will be stored
+ persistentVolumeClaim:
+ claimName:
+ backup:
+ containerName: backup # container name is responsible for backup
+ action:
+ exec:
+ command:
+ - /home/user/bin/backup.sh # this command is invoked on "backup" container to make backup, for example /home/user/bin/backup.sh , is passed by operator
+ getLatestAction:
+ exec:
+ command:
+ - /home/user/bin/get-latest.sh # this command is invoked on "backup" container to get last backup number before pod deletion. If you don't omit it in CR, you can lose data
+ interval: 30 # how often make backup in seconds
+ makeBackupBeforePodDeletion: true # make a backup before pod deletion
+ restore:
+ containerName: backup # container name is responsible for restore backup
+ action:
+ exec:
+ command:
+ - /home/user/bin/restore.sh # this command is invoked on "backup" container to make restore backup, for example /home/user/bin/restore.sh , is passed by operator
+ #recoveryOnce: # if want to restore specific backup configure this field and then Jenkins will be restarted and desired backup will be restored
+```
diff --git a/website/content/en/docs/Getting Started/Preview/custom-backup-and-restore.md b/website/content/en/docs/Getting Started/Preview/custom-backup-and-restore.md
new file mode 100644
index 00000000..f632953a
--- /dev/null
+++ b/website/content/en/docs/Getting Started/Preview/custom-backup-and-restore.md
@@ -0,0 +1,184 @@
+---
+title: "Custom Backup and Restore Providers"
+linkTitle: "Custom Backup and Restore Providers"
+weight: 10
+date: 2021-01-18
+description: >
+ Custom backup and restore provider
+---
+
+With enough effort one can create a custom backup and restore provider
+for the Jenkins Operator.
+
+## Requirements
+
+Two commands (e.g. scripts) are required:
+
+- a backup command, e.g. `backup.sh` that takes one argument, a **backup number**
+- a restore command, e.g. `backup.sh` that takes one argument, a **backup number**
+
+Both scripts need to return an exit code of `0` on success and `1` or greater for failure.
+
+One of those scripts (or the entry point of the container) needs to be responsible
+for backup cleanup or rotation if required, or an external system.
+
+## How it works
+
+The mechanism relies on basic Kubernetes and UNIX functionalities.
+
+The backup (and restore) container runs as a sidecar in the same
+Kubernetes pod as the Jenkins master.
+
+Name of the backup and restore containers can be set as necessary using
+`spec.backup.containerName` and `spec.restore.containerName`.
+In most cases it will be the same container, but we allow for less common use cases.
+
+The operator will call a backup or restore commands inside a sidecar container when necessary:
+
+- backup command (defined in `spec.backup.action.exec.command`)
+ will be called every `N` seconds configurable in: `spec.backup.interval`
+ and on pod shutdown (if enabled in `spec.backup.makeBackupBeforePodDeletion`)
+ with an integer representing the current backup number as first and only argument
+- restore command (defined in `spec.restore.action.exec.command`)
+ will be called at Jenkins startup
+ with an integer representing the backup number to restore as first and only argument
+ (can be overridden using `spec.restore.recoveryOnce`)
+
+## Example AWS S3 backup using the CLI
+
+This example shows abbreviated version of a simple AWS S3 backup implementation
+using: `aws-cli`, `bash` and `kube2iam`.
+
+In addition to your normal `Jenkins` `CustomResource` some additional settings
+for backup and restore are required, e.g.:
+
+```yaml
+kind: Jenkins
+apiVersion: jenkins.io/v1alpha1
+metadata:
+ name: example
+ namespace: jenkins
+spec:
+ master:
+ masterAnnotations:
+ iam.amazonaws.com/role: "my-example-backup-role" # tell kube2iam where the AWS IAM role is
+ containers:
+ - name: jenkins-master
+ ...
+ - name: backup # container responsible for backup and restore
+ image: quay.io/virtuslab/aws-cli:1.16.263-2
+ workingDir: /home/user/bin/
+ command: # our container entry point
+ - sleep
+ - infinity
+ env:
+ - name: BACKUP_BUCKET
+ value: my-example-bucket # the S3 bucket name to use
+ - name: BACKUP_PATH
+ value: my-backup-path # the S3 bucket path prefix to use
+ - name: JENKINS_HOME
+ value: /jenkins-home # the path to mount jenkins home dir in the backup container
+ volumeMounts:
+ - mountPath: /jenkins-home # Jenkins home volume
+ name: jenkins-home
+ - mountPath: /home/user/bin/backup.sh
+ name: backup-scripts
+ subPath: backup.sh
+ readOnly: true
+ - mountPath: /home/user/bin/restore.sh
+ name: backup-scripts
+ subPath: restore.sh
+ readOnly: true
+ volumes:
+ - name: backup-scripts
+ configMap:
+ defaultMode: 0754
+ name: jenkins-operator-backup-s3
+ securityContext: # make sure both containers use the same UID and GUID
+ runAsUser: 1000
+ fsGroup: 1000
+ ...
+ backup:
+ containerName: backup # container name responsible for backup
+ interval: 3600 # how often make a backup in seconds
+ makeBackupBeforePodDeletion: true # trigger backup just before deleting the pod
+ action:
+ exec:
+ command:
+ # this command is invoked on "backup" container to create a backup,
+ # is passed by operator,
+ # for example /home/user/bin/backup.sh
+ - /home/user/bin/backup.sh
+ restore:
+ containerName: backup # container name is responsible for restore backup
+ action:
+ exec:
+ command:
+ # this command is invoked on "backup" container to restore a backup,
+ # is passed by operator
+ # for example /home/user/bin/restore.sh
+ - /home/user/bin/restore.sh
+# recoveryOnce: # if want to restore specific backup configure this field and then Jenkins will be restarted and desired backup will be restored
+```
+
+The actual backup and restore scripts will be provided in a `ConfigMap`:
+
+```yaml
+kind: ConfigMap
+apiVersion: v1
+metadata:
+ name: jenkins-operator-backup-s3
+ namespace: jenkins
+ labels:
+ app: jenkins-operator
+data:
+ backup.sh: |-
+ #!/bin/bash -xeu
+ [[ ! $# -eq 1 ]] && echo "Usage: $0 backup_number" && exit 1;
+ [[ -z "${BACKUP_BUCKET}" ]] && echo "Required 'BACKUP_BUCKET' env not set" && exit 1;
+ [[ -z "${BACKUP_PATH}" ]] && echo "Required 'BACKUP_PATH' env not set" && exit 1;
+ [[ -z "${JENKINS_HOME}" ]] && echo "Required 'JENKINS_HOME' env not set" && exit 1;
+
+ backup_number=$1
+ echo "Running backup #${backup_number}"
+
+ BACKUP_TMP_DIR=$(mktemp -d)
+ tar -C ${JENKINS_HOME} -czf "${BACKUP_TMP_DIR}/${backup_number}.tar.gz" --exclude jobs/*/workspace* -c jobs && \
+
+ aws s3 cp ${BACKUP_TMP_DIR}/${backup_number}.tar.gz s3://${BACKUP_BUCKET}/${BACKUP_PATH}/${backup_number}.tar.gz
+ echo Done
+
+ restore.sh: |-
+ #!/bin/bash -xeu
+ [[ ! $# -eq 1 ]] && echo "Usage: $0 backup_number" && exit 1
+ [[ -z "${BACKUP_BUCKET}" ]] && echo "Required 'BACKUP_BUCKET' env not set" && exit 1;
+ [[ -z "${BACKUP_PATH}" ]] && echo "Required 'BACKUP_PATH' env not set" && exit 1;
+ [[ -z "${JENKINS_HOME}" ]] && echo "Required 'JENKINS_HOME' env not set" && exit 1;
+
+ backup_number=$1
+ echo "Running restore #${backup_number}"
+
+ BACKUP_TMP_DIR=$(mktemp -d)
+ aws s3 cp s3://${BACKUP_BUCKET}/${BACKUP_PATH}/${backup_number}.tar.gz ${BACKUP_TMP_DIR}/${backup_number}.tar.gz
+
+ tar -C ${JENKINS_HOME} -zxf "${BACKUP_TMP_DIR}/${backup_number}.tar.gz"
+ echo Done
+```
+
+In our example we will use S3 bucket lifecycle policy to keep
+the number of backups under control, e.g. Cloud Formation fragment:
+```yaml
+ Type: AWS::S3::Bucket
+ Properties:
+ BucketName: my-example-bucket
+ ...
+ LifecycleConfiguration:
+ Rules:
+ - Id: BackupCleanup
+ Status: Enabled
+ Prefix: my-backup-path
+ ExpirationInDays: 7
+ NoncurrentVersionExpirationInDays: 14
+ AbortIncompleteMultipartUpload:
+ DaysAfterInitiation: 3
+```
diff --git a/website/content/en/docs/Getting Started/Preview/customization.md b/website/content/en/docs/Getting Started/Preview/customization.md
new file mode 100644
index 00000000..76b12e10
--- /dev/null
+++ b/website/content/en/docs/Getting Started/Preview/customization.md
@@ -0,0 +1,202 @@
+---
+title: "Customization"
+linkTitle: "Customization"
+weight: 3
+date: 2021-01-25
+description: >
+ How to customize Jenkins
+---
+
+## How to customize Jenkins
+Jenkins can be customized with plugins.
+Plugin's configuration is applied as groovy scripts or the [configuration as code plugin](https://github.com/jenkinsci/configuration-as-code-plugin).
+Any plugin working for Jenkins can be installed by the Jenkins Operator.
+
+Pre-installed plugins:
+* configuration-as-code v1.47
+* git v4.5.0
+* job-dsl v1.77
+* kubernetes-credentials-provider v0.15
+* kubernetes v1.29.0
+* workflow-aggregator v2.6
+* workflow-job v2.40
+
+Rest of the plugins can be found in [plugins repository](https://plugins.jenkins.io/).
+
+
+#### Install plugins
+
+Edit Custom Resource under `spec.master.plugins`:
+
+```yaml
+apiVersion: jenkins.io/v1alpha2
+kind: Jenkins
+metadata:
+ name: example
+spec:
+ master:
+ plugins:
+ - name: simple-theme-plugin
+ version: "0.6"
+```
+
+Under `spec.master.basePlugins` you can find plugins for a valid **Jenkins Operator**:
+
+```yaml
+apiVersion: jenkins.io/v1alpha2
+kind: Jenkins
+metadata:
+ name: example
+spec:
+ master:
+ basePlugins:
+ - name: kubernetes
+ version: "1.28.6"
+ - name: workflow-job
+ version: "2.40"
+ - name: workflow-aggregator
+ version: "2.6"
+ - name: git
+ version: "4.5.0"
+ - name: job-dsl
+ version: "1.77"
+ - name: configuration-as-code
+ version: "1.46"
+ - name: kubernetes-credentials-provider
+ version: "0.15"
+```
+
+You can change their versions.
+
+The **Jenkins Operator** will then automatically install plugins after the Jenkins master pod restart.
+
+#### Apply plugin's config
+
+By using a [ConfigMap](https://kubernetes.io/docs/tasks/configure-pod-container/configure-pod-configmap/) you can create your own **Jenkins** customized configuration.
+Then you must reference the **`ConfigMap`** in the **Jenkins** pod customization file in `spec.groovyScripts` or `spec.configurationAsCode`
+
+Create a **`ConfigMap`** with specific name (eg. `jenkins-operator-user-configuration`). Then, modify the **Jenkins** manifest:
+
+```yaml
+apiVersion: jenkins.io/v1alpha2
+kind: Jenkins
+metadata:
+ name: example
+spec:
+ configurationAsCode:
+ configurations:
+ - name: jenkins-operator-user-configuration
+ groovyScripts:
+ configurations:
+ - name: jenkins-operator-user-configuration
+```
+
+Here is an example of `jenkins-operator-user-configuration`:
+```yaml
+apiVersion: v1
+kind: ConfigMap
+metadata:
+ name: jenkins-operator-user-configuration
+data:
+ 1-configure-theme.groovy: |
+ import jenkins.*
+ import jenkins.model.*
+ import hudson.*
+ import hudson.model.*
+ import org.jenkinsci.plugins.simpletheme.ThemeElement
+ import org.jenkinsci.plugins.simpletheme.CssTextThemeElement
+ import org.jenkinsci.plugins.simpletheme.CssUrlThemeElement
+
+ Jenkins jenkins = Jenkins.getInstance()
+
+ def decorator = Jenkins.instance.getDescriptorByType(org.codefirst.SimpleThemeDecorator.class)
+
+ List configElements = new ArrayList<>();
+ configElements.add(new CssTextThemeElement("DEFAULT"));
+ configElements.add(new CssUrlThemeElement("https://cdn.rawgit.com/afonsof/jenkins-material-theme/gh-pages/dist/material-light-green.css"));
+ decorator.setElements(configElements);
+ decorator.save();
+
+ jenkins.save()
+ 1-system-message.yaml: |
+ jenkins:
+ systemMessage: "Configuration as Code integration works!!!"
+```
+
+* `*.groovy` is Groovy script configuration
+* `*.yaml is` configuration as code
+
+If you want to correct your configuration you can edit it while the **Jenkins Operator** is running.
+Jenkins will reconcile and apply the new configuration.
+
+## How to use secrets from a Groovy scripts
+
+If you configured `spec.groovyScripts.secret.name`, then this secret is available to use from map Groovy scripts.
+The secrets are loaded to `secrets` map.
+
+Create a [secret](https://kubernetes.io/docs/concepts/configuration/secret/) with for example the name `jenkins-conf-secrets`.
+
+```yaml
+kind: Secret
+apiVersion: v1
+type: Opaque
+metadata:
+ name: jenkins-conf-secrets
+ namespace: default
+data:
+ SYSTEM_MESSAGE: SGVsbG8gd29ybGQ=
+```
+
+Then modify the **Jenkins** pod manifest by changing `spec.groovyScripts.secret.name` to `jenkins-conf-secrets`.
+
+```yaml
+apiVersion: jenkins.io/v1alpha2
+kind: Jenkins
+metadata:
+ name: example
+spec:
+ configurationAsCode:
+ configurations:
+ - name: jenkins-operator-user-configuration
+ secret:
+ name: jenkins-conf-secrets
+ groovyScripts:
+ configurations:
+ - name: jenkins-operator-user-configuration
+ secret:
+ name: jenkins-conf-secrets
+```
+
+Now you can test that the secret is mounted by applying this `ConfigMap` for Groovy script:
+
+```yaml
+apiVersion: v1
+kind: ConfigMap
+metadata:
+ name: jenkins-operator-user-configuration
+data:
+ 1-system-message.groovy: |
+ import jenkins.*
+ import jenkins.model.*
+ import hudson.*
+ import hudson.model.*
+ Jenkins jenkins = Jenkins.getInstance()
+
+ jenkins.setSystemMessage(secrets["SYSTEM_MESSAGE"])
+ jenkins.save()
+```
+
+Or by applying this configuration as code:
+```yaml
+apiVersion: v1
+kind: ConfigMap
+metadata:
+ name: jenkins-operator-user-configuration
+data:
+ 1-system-message.yaml: |
+ jenkins:
+ systemMessage: ${SYSTEM_MESSAGE}
+```
+
+
+After this, you should see the `Hello world` system message from the **Jenkins** homepage.
\ No newline at end of file
diff --git a/website/content/en/docs/Getting Started/Preview/deploy-jenkins.md b/website/content/en/docs/Getting Started/Preview/deploy-jenkins.md
new file mode 100644
index 00000000..29e892f6
--- /dev/null
+++ b/website/content/en/docs/Getting Started/Preview/deploy-jenkins.md
@@ -0,0 +1,90 @@
+---
+title: "Deploy Jenkins"
+linkTitle: "Deploy Jenkins"
+weight: 1
+date: 2021-01-25
+description: >
+ Deploy production ready Jenkins Operator manifest
+---
+
+Once Jenkins Operator is up and running let's deploy actual Jenkins instance.
+Create manifest e.g. **`jenkins_instance.yaml`** with following data and save it on drive.
+
+```yaml
+apiVersion: jenkins.io/v1alpha2
+kind: Jenkins
+metadata:
+ name: example
+spec:
+ master:
+ containers:
+ - name: jenkins-master
+ image: jenkins/jenkins:2.263.2-lts-alpine
+ imagePullPolicy: Always
+ livenessProbe:
+ failureThreshold: 12
+ httpGet:
+ path: /login
+ port: http
+ scheme: HTTP
+ initialDelaySeconds: 80
+ periodSeconds: 10
+ successThreshold: 1
+ timeoutSeconds: 5
+ readinessProbe:
+ failureThreshold: 3
+ httpGet:
+ path: /login
+ port: http
+ scheme: HTTP
+ initialDelaySeconds: 30
+ periodSeconds: 10
+ successThreshold: 1
+ timeoutSeconds: 1
+ 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
+```
+
+Deploy a Jenkins to Kubernetes:
+
+```bash
+kubectl create -f jenkins_instance.yaml
+```
+Watch the Jenkins instance being created:
+
+```bash
+kubectl get pods -w
+```
+
+Get the Jenkins credentials:
+
+```bash
+kubectl get secret jenkins-operator-credentials- -o 'jsonpath={.data.user}' | base64 -d
+kubectl get secret jenkins-operator-credentials- -o 'jsonpath={.data.password}' | base64 -d
+```
+
+Connect to the Jenkins instance (minikube):
+
+```bash
+minikube service jenkins-operator-http- --url
+```
+
+Connect to the Jenkins instance (actual Kubernetes cluster):
+
+```bash
+kubectl port-forward jenkins- 8080:8080
+```
+Then open browser with address `http://localhost:8080`.
+
+
diff --git a/website/content/en/docs/Getting Started/Preview/diagnostics.md b/website/content/en/docs/Getting Started/Preview/diagnostics.md
new file mode 100644
index 00000000..18ecf679
--- /dev/null
+++ b/website/content/en/docs/Getting Started/Preview/diagnostics.md
@@ -0,0 +1,42 @@
+---
+title: "Diagnostics"
+linkTitle: "Diagnostics"
+weight: 40
+date: 2021-01-18
+description: >
+ How to deal with Jenkins Operator problems
+---
+
+
+Turn on debug in **Jenkins Operator** deployment:
+
+```bash
+sed -i 's|\(args:\).*|\1\ ["--debug"\]|' deploy/operator.yaml
+kubectl apply -f deploy/operator.yaml
+```
+
+Watch Kubernetes events:
+
+```bash
+kubectl get events --sort-by='{.lastTimestamp}'
+```
+
+Verify Jenkins master logs:
+
+```bash
+kubectl logs -f jenkins-
+```
+
+Verify the `jenkins-operator` logs:
+
+```bash
+kubectl logs deployment/jenkins-operator
+```
+
+## Troubleshooting
+
+Delete the Jenkins master pod and wait for the new one to come up:
+
+```bash
+kubectl delete pod jenkins-
+```
diff --git a/website/content/en/docs/Getting Started/Preview/notifications.md b/website/content/en/docs/Getting Started/Preview/notifications.md
new file mode 100644
index 00000000..06845e29
--- /dev/null
+++ b/website/content/en/docs/Getting Started/Preview/notifications.md
@@ -0,0 +1,114 @@
+---
+title: "Notifications"
+linkTitle: "Notifications"
+weight: 10
+date: 2021-01-18
+description: >
+ How to setup operator notifications.
+---
+
+## Slack
+
+Please follow [this](https://api.slack.com/incoming-webhooks) instructions to get web hook URL.
+
+Create web hook secret with name `jenkins-operator-notification-data`. Contains key `url` with provided web hook URL.
+
+```bash
+$ kubectl create secret generic jenkins-operator-notification-data --from-literal=url=
+```
+
+Example configuration for Slack:
+
+```yaml
+kind: Jenkins
+spec:
+ master:
+ notifications:
+ - level: info
+ verbose: true
+ name:
+ slack:
+ webHookURLSecretKeySelector:
+ secret:
+ name:
+ key:
+```
+
+## Microsoft Teams
+
+Please follow [this](https://docs.microsoft.com/en-gb/outlook/actionable-messages/send-via-connectors) instructions to get web hook URL.
+
+Example configuration for Microsoft Teams:
+
+```yaml
+kind: Jenkins
+spec:
+ master:
+ notifications:
+ - level: info
+ verbose: true
+ name:
+ teams:
+ webHookURLSecretKeySelector:
+ secret:
+ name:
+ key:
+```
+
+## Mailgun
+
+Example configuration for Mailgun:
+
+```yaml
+kind: Jenkins
+spec:
+ master:
+ notifications:
+ - level: info
+ verbose: true
+ name:
+ mailgun:
+ domain:
+ apiKeySecretKeySelector:
+ secret:
+ name:
+ key:
+ recipient:
+ from:
+```
+
+## Debug options
+
+As you see there is two debugging options:
+
+* `level` (warning/info) - Set level of messages to send.
+
+* `verbose` - Print stacktrace and additional error messages
+
+## Multiple providers
+
+You can use multiple providers to send notification to another communication channels at the same time.
+For example you will send notifications to Slack and Teams.
+
+```yaml
+kind: Jenkins
+spec:
+ master:
+ notifications:
+ - level: info
+ verbose: true
+ name: nslack
+ slack:
+ webHookURLSecretKeySelector:
+ secret:
+ name:
+ key:
+ - level: info
+ verbose: true
+ name: nteams
+ teams:
+ webHookURLSecretKeySelector:
+ secret:
+ name:
+ key:
+```
diff --git a/website/content/en/docs/Getting Started/Preview/openshift.md b/website/content/en/docs/Getting Started/Preview/openshift.md
new file mode 100644
index 00000000..7af146ac
--- /dev/null
+++ b/website/content/en/docs/Getting Started/Preview/openshift.md
@@ -0,0 +1,104 @@
+---
+title: "OpenShift"
+linkTitle: "OpenShift"
+weight: 20
+date: 2020-04-29
+description: >
+ Additional configuration for OpenShift
+---
+
+## SecurityContext
+
+OpenShift enforces Security Constraints Context (scc) when deploying an image.
+By default, container images run in restricted scc which prevents from setting
+a fixed user id to run with. You need to have ensure that you do not provide a
+securityContext with a runAsUser and that your image does not use a hardcoded user.
+
+```yaml
+securityContext: {}
+```
+
+## OpenShift Jenkins image
+
+OpenShift provides a pre-configured Jenkins image containing 3 openshift plugins for
+jenkins (openshift-login-plugin, openshift-sync-plugin and openshift-client-plugin)
+which allows better jenkins integration with kubernetes and OpenShift.
+
+The OpenShift Jenkins image requires additional configuration to be fully enabled.
+
+### Sample OpenShift CR
+The following Custom Resource can be used to create a Jenkins instance using the
+OpenShift Jenkins image and sets values for:
+- `image: 'quay.io/openshift/origin-jenkins:latest' : This is the OpenShift Jenkins image.
+
+- serviceAccount: to allow oauth authentication to work, the service account needs
+a specific annotation pointing to the route exposing the jenkins service. Here,
+the route is named `jenkins-route`
+
+- `OPENSHIFT_ENABLE_OAUTH` environment variable for the master container is set to true.
+
+Here is a complete Jenkins CR allowing the deployment of the Jenkins OpenShift image.
+```yaml
+apiVersion: jenkins.io/v1alpha2
+kind: Jenkins
+metadata:
+ annotations:
+ jenkins.io/openshift-mode: 'true'
+ name: jenkins
+spec:
+ serviceAccount:
+ annotations:
+ serviceaccounts.openshift.io/oauth-redirectreference.jenkins: '{"kind":"OAuthRedirectReference","apiVersion":"v1","reference":{"kind":"Route","name":"jenkins-route"}}'
+ master:
+ containers:
+ - name: jenkins-master
+ image: 'quay.io/openshift/origin-jenkins:latest'
+ command:
+ - /usr/bin/go-init
+ - '-main'
+ - /usr/libexec/s2i/run
+ env:
+ - name: OPENSHIFT_ENABLE_OAUTH
+ value: 'true'
+ - name: OPENSHIFT_ENABLE_REDIRECT_PROMPT
+ value: 'true'
+ - name: DISABLE_ADMINISTRATIVE_MONITORS
+ value: 'false'
+ - name: KUBERNETES_MASTER
+ value: 'https://kubernetes.default:443'
+ - name: KUBERNETES_TRUST_CERTIFICATES
+ value: 'true'
+ - name: JENKINS_SERVICE_NAME
+ value: jenkins-operator-http-jenkins
+ - name: JNLP_SERVICE_NAME
+ value: jenkins-operator-slave-jenkins
+ - name: JENKINS_UC_INSECURE
+ value: 'false'
+ - name: JENKINS_HOME
+ value: /var/lib/jenkins
+ - name: JAVA_OPTS
+ value: >-
+ -XX:+UnlockExperimentalVMOptions -XX:+UnlockExperimentalVMOptions
+ -XX:+UseCGroupMemoryLimitForHeap -XX:MaxRAMFraction=1
+ -Djenkins.install.runSetupWizard=false -Djava.awt.headless=true
+ imagePullPolicy: Always
+ service:
+ port: 8080
+ type: ClusterIP
+ slaveService:
+ port: 50000
+ type: ClusterIP
+```
+
+### OpenShift OAuth integration
+The creation of a Route is required for the integraiton of Jenkins with
+OpenShift oauth authentication. By default, the jenkins http service is named
+`jenkins-operator-http-${jenkins-cr-name}`
+
+```bash
+oc create route edge jenkins-route --service=jenkins-operator-http-jenkins
+```
+Note: the route name (jenkins-route) must match the pointed route on the serviceaccount annotation.
+
+
+After the creation of the Route. It can be used to navigate to the Jenkins Login Page and login with your Openshift Credentials.
diff --git a/website/content/en/docs/Getting Started/Preview/schema.md b/website/content/en/docs/Getting Started/Preview/schema.md
new file mode 100644
index 00000000..2a1444c7
--- /dev/null
+++ b/website/content/en/docs/Getting Started/Preview/schema.md
@@ -0,0 +1,2642 @@
+---
+title: "Schema"
+linkTitle: "Schema"
+weight: 40
+date: 2021-01-18
+description: >
+ API Schema definitions for Jenkins CRD
+---
+
+{{% pageinfo %}}
+This document contains API scheme for `jenkins-operator` Custom Resource Definition manifest
+{{% /pageinfo %}}
+
+Packages:
+
+jenkins.io
+
+
Package v1alpha2 contains API Schema definitions for the jenkins.io v1alpha2 API group
+
+Resource Types:
+
+Jenkins
+
+
+
Jenkins is the Schema for the jenkins API
+
+
+
+
+| Field |
+Description |
+
+
+
+
+
+apiVersion
+string |
+
+
+jenkins.io/v1alpha2
+
+ |
+
+
+
+kind
+string
+ |
+Jenkins |
+
+
+
+metadata
+
+
+Kubernetes meta/v1.ObjectMeta
+
+
+ |
+
+Refer to the Kubernetes API documentation for the fields of the
+metadata field.
+ |
+
+
+
+spec
+
+
+JenkinsSpec
+
+
+ |
+
+ Spec defines the desired state of the Jenkins
+
+
+
+ |
+
+
+
+status
+
+
+JenkinsStatus
+
+
+ |
+
+ Status defines the observed state of Jenkins
+ |
+
+
+
+AppliedGroovyScript
+
+
+(Appears on:
+JenkinsStatus)
+
+
+
AppliedGroovyScript is the applied groovy script in Jenkins by the operator.
+
+
+
+
+| Field |
+Description |
+
+
+
+
+
+configurationType
+
+string
+
+ |
+
+ ConfigurationType is the name of the configuration type(base-groovy, user-groovy, user-casc)
+ |
+
+
+
+source
+
+string
+
+ |
+
+ Source is the name of source where is located groovy script
+ |
+
+
+
+name
+
+string
+
+ |
+
+ Name is the name of the groovy script
+ |
+
+
+
+hash
+
+string
+
+ |
+
+ Hash is the hash of the groovy script and secrets which it uses
+ |
+
+
+
+AuthorizationStrategy
+(string alias)
+
+(Appears on:
+JenkinsAPISettings)
+
+
+
AuthorizationStrategy defines authorization strategy of the operator for the Jenkins API
+
+Backup
+
+
+(Appears on:
+JenkinsSpec)
+
+
+
Backup defines configuration of Jenkins backup.
+
+
+
+
+| Field |
+Description |
+
+
+
+
+
+containerName
+
+string
+
+ |
+
+ ContainerName is the container name responsible for backup operation
+ |
+
+
+
+action
+
+
+Handler
+
+
+ |
+
+ Action defines action which performs backup in backup container sidecar
+ |
+
+
+
+interval
+
+uint64
+
+ |
+
+ Interval tells how often make backup in seconds
+Defaults to 30.
+ |
+
+
+
+makeBackupBeforePodDeletion
+
+bool
+
+ |
+
+ MakeBackupBeforePodDeletion tells operator to make backup before Jenkins master pod deletion
+ |
+
+
+
+ConfigMapRef
+
+
+(Appears on:
+Customization)
+
+
+
ConfigMapRef is reference to Kubernetes ConfigMap.
+
+
+
+
+| Field |
+Description |
+
+
+
+
+
+name
+
+string
+
+ |
+
+ |
+
+
+
+ConfigurationAsCode
+
+
+(Appears on:
+JenkinsSpec)
+
+
+
ConfigurationAsCode defines configuration of Jenkins customization via Configuration as Code Jenkins plugin.
+
+
+
+
+| Field |
+Description |
+
+
+
+
+
+Customization
+
+
+Customization
+
+
+ |
+
+
+(Members of Customization are embedded into this type.)
+
+ |
+
+
+
+Container
+
+
+(Appears on:
+JenkinsMaster)
+
+
+
Container defines Kubernetes container attributes.
+
+
+
+
+| Field |
+Description |
+
+
+
+
+
+name
+
+string
+
+ |
+
+ Name of the container specified as a DNS_LABEL.
+Each container in a pod must have a unique name (DNS_LABEL).
+ |
+
+
+
+image
+
+string
+
+ |
+
+ Docker image name.
+More info: https://kubernetes.io/docs/concepts/containers/images
+ |
+
+
+
+imagePullPolicy
+
+
+Kubernetes core/v1.PullPolicy
+
+
+ |
+
+ Image pull policy.
+One of Always, Never, IfNotPresent.
+Defaults to Always.
+ |
+
+
+
+resources
+
+
+Kubernetes core/v1.ResourceRequirements
+
+
+ |
+
+ Compute Resources required by this container.
+More info: https://kubernetes.io/docs/concepts/configuration/manage-compute-resources-container/
+ |
+
+
+
+command
+
+[]string
+
+ |
+
+(Optional)
+ Entrypoint array. Not executed within a shell.
+The docker image’s ENTRYPOINT is used if this is not provided.
+Variable references $(VAR_NAME) are expanded using the container’s environment. If a variable
+cannot be resolved, the reference in the input string will be unchanged. The $(VAR_NAME) syntax
+can be escaped with a double $$, ie: $$(VAR_NAME). Escaped references will never be expanded,
+regardless of whether the variable exists or not.
+More info: https://kubernetes.io/docs/tasks/inject-data-application/define-command-argument-container/#running-a-command-in-a-shell
+ |
+
+
+
+args
+
+[]string
+
+ |
+
+(Optional)
+ Arguments to the entrypoint.
+The docker image’s CMD is used if this is not provided.
+Variable references $(VAR_NAME) are expanded using the container’s environment. If a variable
+cannot be resolved, the reference in the input string will be unchanged. The $(VAR_NAME) syntax
+can be escaped with a double $$, ie: $$(VAR_NAME). Escaped references will never be expanded,
+regardless of whether the variable exists or not.
+More info: https://kubernetes.io/docs/tasks/inject-data-application/define-command-argument-container/#running-a-command-in-a-shell
+ |
+
+
+
+workingDir
+
+string
+
+ |
+
+(Optional)
+ Container’s working directory.
+If not specified, the container runtime’s default will be used, which
+might be configured in the container image.
+ |
+
+
+
+ports
+
+
+[]Kubernetes core/v1.ContainerPort
+
+
+ |
+
+(Optional)
+ List of ports to expose from the container. Exposing a port here gives
+the system additional information about the network connections a
+container uses, but is primarily informational. Not specifying a port here
+DOES NOT prevent that port from being exposed. Any port which is
+listening on the default “0.0.0.0” address inside a container will be
+accessible from the network.
+ |
+
+
+
+envFrom
+
+
+[]Kubernetes core/v1.EnvFromSource
+
+
+ |
+
+(Optional)
+ List of sources to populate environment variables in the container.
+The keys defined within a source must be a C_IDENTIFIER. All invalid keys
+will be reported as an event when the container is starting. When a key exists in multiple
+sources, the value associated with the last source will take precedence.
+Values defined by an Env with a duplicate key will take precedence.
+ |
+
+
+
+env
+
+
+[]Kubernetes core/v1.EnvVar
+
+
+ |
+
+(Optional)
+ List of environment variables to set in the container.
+ |
+
+
+
+volumeMounts
+
+
+[]Kubernetes core/v1.VolumeMount
+
+
+ |
+
+(Optional)
+ Pod volumes to mount into the container’s filesystem.
+ |
+
+
+
+livenessProbe
+
+
+Kubernetes core/v1.Probe
+
+
+ |
+
+(Optional)
+ Periodic probe of container liveness.
+Container will be restarted if the probe fails.
+ |
+
+
+
+readinessProbe
+
+
+Kubernetes core/v1.Probe
+
+
+ |
+
+(Optional)
+ Periodic probe of container service readiness.
+Container will be removed from service endpoints if the probe fails.
+ |
+
+
+
+lifecycle
+
+
+Kubernetes core/v1.Lifecycle
+
+
+ |
+
+(Optional)
+ Actions that the management system should take in response to container lifecycle events.
+ |
+
+
+
+securityContext
+
+
+Kubernetes core/v1.SecurityContext
+
+
+ |
+
+(Optional)
+ Security options the pod should run with.
+More info: https://kubernetes.io/docs/concepts/policy/security-context/
+More info: https://kubernetes.io/docs/tasks/configure-pod-container/security-context/
+ |
+
+
+
+Customization
+
+
+(Appears on:
+ConfigurationAsCode,
+GroovyScripts)
+
+
+
Customization defines configuration of Jenkins customization.
+
+
+GroovyScripts
+
+
+(Appears on:
+JenkinsSpec)
+
+
+
GroovyScripts defines configuration of Jenkins customization via groovy scripts.
+
+
+
+
+| Field |
+Description |
+
+
+
+
+
+Customization
+
+
+Customization
+
+
+ |
+
+
+(Members of Customization are embedded into this type.)
+
+ |
+
+
+
+Handler
+
+
+(Appears on:
+Backup,
+Restore)
+
+
+
Handler defines a specific action that should be taken.
+
+
+Image
+
+
+(Appears on:
+JenkinsImageSpec)
+
+
+
Defines Jenkins Plugin structure
+
+
+
+
+| Field |
+Description |
+
+
+
+
+
+name
+
+string
+
+ |
+
+ |
+
+
+
+version
+
+string
+
+ |
+
+ |
+
+
+
+JenkinsAPISettings
+
+
+(Appears on:
+JenkinsSpec)
+
+
+
JenkinsAPISettings defines configuration used by the operator to gain admin access to the Jenkins API
+
+
+JenkinsCredentialType
+(string alias)
+
+(Appears on:
+SeedJob)
+
+
+
JenkinsCredentialType defines type of Jenkins credential used to seed job mechanism.
+
+JenkinsImage
+
+
+
JenkinsImage is the Schema for the jenkinsimages API
+
+
+JenkinsImageSpec
+
+
+(Appears on:
+JenkinsImage)
+
+
+
JenkinsImageSpec defines the desired state of JenkinsImage
+
+
+JenkinsImageStatus
+
+
+(Appears on:
+JenkinsImage)
+
+
+
JenkinsImageStatus defines the observed state of JenkinsImage
+
+
+JenkinsMaster
+
+
+(Appears on:
+JenkinsSpec)
+
+
+
JenkinsMaster defines the Jenkins master pod attributes and plugins,
+every single change requires a Jenkins master pod restart.
+
+
+
+
+| Field |
+Description |
+
+
+
+
+
+annotations
+
+map[string]string
+
+ |
+
+(Optional)
+ 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
+ |
+
+
+
+masterAnnotations
+
+map[string]string
+
+ |
+
+(Optional)
+ 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)
+ |
+
+
+
+labels
+
+map[string]string
+
+ |
+
+(Optional)
+ 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.
+More info: http://kubernetes.io/docs/user-guide/labels
+ |
+
+
+
+nodeSelector
+
+map[string]string
+
+ |
+
+(Optional)
+ NodeSelector is a selector which must be true for the pod to fit on a node.
+Selector which must match a node’s labels for the pod to be scheduled on that node.
+More info: https://kubernetes.io/docs/concepts/configuration/assign-pod-node/
+ |
+
+
+
+securityContext
+
+
+Kubernetes core/v1.PodSecurityContext
+
+
+ |
+
+(Optional)
+ SecurityContext that applies to all the containers of the Jenkins
+Master. As per kubernetes specification, it can be overridden
+for each container individually.
+Defaults to:
+runAsUser: 1000
+fsGroup: 1000
+ |
+
+
+
+containers
+
+
+[][]github.com/jenkinsci/kubernetes-operator/pkg/apis/jenkins/v1alpha2.Container
+
+
+ |
+
+(Optional)
+ List of containers belonging to the pod.
+Containers cannot currently be added or removed.
+There must be at least one container in a Pod.
+Defaults to:
+- image: jenkins/jenkins:lts
+imagePullPolicy: Always
+livenessProbe:
+failureThreshold: 12
+httpGet:
+path: /login
+port: http
+scheme: HTTP
+initialDelaySeconds: 80
+periodSeconds: 10
+successThreshold: 1
+timeoutSeconds: 5
+name: jenkins-master
+readinessProbe:
+failureThreshold: 3
+httpGet:
+path: /login
+port: http
+scheme: HTTP
+initialDelaySeconds: 30
+periodSeconds: 10
+successThreshold: 1
+timeoutSeconds: 1
+resources:
+limits:
+cpu: 1500m
+memory: 3Gi
+requests:
+cpu: “1”
+memory: 600Mi
+ |
+
+
+
+imagePullSecrets
+
+
+[]Kubernetes core/v1.LocalObjectReference
+
+
+ |
+
+(Optional)
+ ImagePullSecrets is an optional list of references to secrets in the same namespace to use for pulling any of the images used by this PodSpec.
+If specified, these secrets will be passed to individual puller implementations for them to use. For example,
+in the case of docker, only DockerConfig type secrets are honored.
+More info: https://kubernetes.io/docs/concepts/containers/images#specifying-imagepullsecrets-on-a-pod
+ |
+
+
+
+volumes
+
+
+[]Kubernetes core/v1.Volume
+
+
+ |
+
+(Optional)
+ List of volumes that can be mounted by containers belonging to the pod.
+More info: https://kubernetes.io/docs/concepts/storage/volumes
+ |
+
+
+
+tolerations
+
+
+[]Kubernetes core/v1.Toleration
+
+
+ |
+
+(Optional)
+ If specified, the pod’s tolerations.
+ |
+
+
+
+basePlugins
+
+
+[][]github.com/jenkinsci/kubernetes-operator/pkg/apis/jenkins/v1alpha2.Plugin
+
+
+ |
+
+(Optional)
+ BasePlugins contains plugins required by operator
+Defaults to :
+- name: kubernetes
+version: “1.28.6”
+- name: workflow-job
+version: “2.40”
+- name: workflow-aggregator
+version: “2.6”
+- name: git
+version: “4.5.0”
+- name: job-dsl
+version: “1.77”
+- name: configuration-as-code
+version: “1.46”
+- name: kubernetes-credentials-provider
+version: “0.15”
+ |
+
+
+
+plugins
+
+
+[][]github.com/jenkinsci/kubernetes-operator/pkg/apis/jenkins/v1alpha2.Plugin
+
+
+ |
+
+(Optional)
+ Plugins contains plugins required by user
+ |
+
+
+
+disableCSRFProtection
+
+bool
+
+ |
+
+ DisableCSRFProtection allows you to toggle CSRF Protection on Jenkins
+ |
+
+
+
+priorityClassName
+
+string
+
+ |
+
+(Optional)
+ PriorityClassName for Jenkins master pod
+ |
+
+
+
+JenkinsPlugin
+
+
+(Appears on:
+JenkinsImageSpec,
+JenkinsImageStatus)
+
+
+
Defines Jenkins Plugin structure
+
+
+
+
+| Field |
+Description |
+
+
+
+
+
+name
+
+string
+
+ |
+
+ |
+
+
+
+version
+
+string
+
+ |
+
+ |
+
+
+
+JenkinsSpec
+
+
+(Appears on:
+Jenkins)
+
+
+
JenkinsSpec defines the desired state of the Jenkins.
+
+
+JenkinsStatus
+
+
+(Appears on:
+Jenkins)
+
+
+
JenkinsStatus defines the observed state of Jenkins
+
+
+
+
+| Field |
+Description |
+
+
+
+
+
+operatorVersion
+
+string
+
+ |
+
+(Optional)
+ OperatorVersion is the operator version which manages this CR
+ |
+
+
+
+provisionStartTime
+
+
+Kubernetes meta/v1.Time
+
+
+ |
+
+(Optional)
+ ProvisionStartTime is a time when Jenkins master pod has been created
+ |
+
+
+
+baseConfigurationCompletedTime
+
+
+Kubernetes meta/v1.Time
+
+
+ |
+
+(Optional)
+ BaseConfigurationCompletedTime is a time when Jenkins base configuration phase has been completed
+ |
+
+
+
+userConfigurationCompletedTime
+
+
+Kubernetes meta/v1.Time
+
+
+ |
+
+(Optional)
+ UserConfigurationCompletedTime is a time when Jenkins user configuration phase has been completed
+ |
+
+
+
+restoredBackup
+
+uint64
+
+ |
+
+(Optional)
+ RestoredBackup is the restored backup number after Jenkins master pod restart
+ |
+
+
+
+lastBackup
+
+uint64
+
+ |
+
+(Optional)
+ LastBackup is the latest backup number
+ |
+
+
+
+pendingBackup
+
+uint64
+
+ |
+
+(Optional)
+ PendingBackup is the pending backup number
+ |
+
+
+
+backupDoneBeforePodDeletion
+
+bool
+
+ |
+
+(Optional)
+ BackupDoneBeforePodDeletion tells if backup before pod deletion has been made
+ |
+
+
+
+userAndPasswordHash
+
+string
+
+ |
+
+(Optional)
+ UserAndPasswordHash is a SHA256 hash made from user and password
+ |
+
+
+
+createdSeedJobs
+
+[]string
+
+ |
+
+(Optional)
+ CreatedSeedJobs contains list of seed job id already created in Jenkins
+ |
+
+
+
+appliedGroovyScripts
+
+
+[][]github.com/jenkinsci/kubernetes-operator/pkg/apis/jenkins/v1alpha2.AppliedGroovyScript
+
+
+ |
+
+(Optional)
+ AppliedGroovyScripts is a list with all applied groovy scripts in Jenkins by the operator
+ |
+
+
+
+Mailgun
+
+
+(Appears on:
+Notification)
+
+
+
Mailgun is handler for Mailgun email service notification channel.
+
+
+
+
+| Field |
+Description |
+
+
+
+
+
+domain
+
+string
+
+ |
+
+ |
+
+
+
+apiKeySecretKeySelector
+
+
+SecretKeySelector
+
+
+ |
+
+ |
+
+
+
+recipient
+
+string
+
+ |
+
+ |
+
+
+
+from
+
+string
+
+ |
+
+ |
+
+
+
+MicrosoftTeams
+
+
+(Appears on:
+Notification)
+
+
+
MicrosoftTeams is handler for Microsoft MicrosoftTeams notification channel.
+
+
+
+
+| Field |
+Description |
+
+
+
+
+
+webHookURLSecretKeySelector
+
+
+SecretKeySelector
+
+
+ |
+
+ The web hook URL to MicrosoftTeams App
+ |
+
+
+
+Notification
+
+
+(Appears on:
+JenkinsSpec)
+
+
+
Notification is a service configuration used to send notifications about Jenkins status.
+
+
+NotificationLevel
+(string alias)
+
+(Appears on:
+Notification)
+
+
+
NotificationLevel defines the level of a Notification.
+
+Plugin
+
+
+(Appears on:
+JenkinsMaster)
+
+
+
Plugin defines Jenkins plugin.
+
+
+
+
+| Field |
+Description |
+
+
+
+
+
+name
+
+string
+
+ |
+
+ Name is the name of Jenkins plugin
+ |
+
+
+
+version
+
+string
+
+ |
+
+ Version is the version of Jenkins plugin
+ |
+
+
+
+downloadURL
+
+string
+
+ |
+
+ DownloadURL is the custom url from where plugin has to be downloaded.
+ |
+
+
+
+Restore
+
+
+(Appears on:
+JenkinsSpec)
+
+
+
Restore defines configuration of Jenkins backup restore operation.
+
+
+
+
+| Field |
+Description |
+
+
+
+
+
+containerName
+
+string
+
+ |
+
+ ContainerName is the container name responsible for restore backup operation
+ |
+
+
+
+action
+
+
+Handler
+
+
+ |
+
+ Action defines action which performs restore backup in restore container sidecar
+ |
+
+
+
+getLatestAction
+
+
+Handler
+
+
+ |
+
+(Optional)
+ GetLatestAction defines action which returns the latest backup number. If there is no backup “-1” should be
+returned.
+ |
+
+
+
+recoveryOnce
+
+uint64
+
+ |
+
+(Optional)
+ RecoveryOnce if want to restore specific backup set this field and then Jenkins will be restarted and desired backup will be restored
+ |
+
+
+
+SMTP
+
+
+(Appears on:
+Notification)
+
+
+
SMTP is handler for sending emails via this protocol.
+
+
+
+
+| Field |
+Description |
+
+
+
+
+
+usernameSecretKeySelector
+
+
+SecretKeySelector
+
+
+ |
+
+ |
+
+
+
+passwordSecretKeySelector
+
+
+SecretKeySelector
+
+
+ |
+
+ |
+
+
+
+port
+
+int
+
+ |
+
+ |
+
+
+
+server
+
+string
+
+ |
+
+ |
+
+
+
+tlsInsecureSkipVerify
+
+bool
+
+ |
+
+ |
+
+
+
+from
+
+string
+
+ |
+
+ |
+
+
+
+to
+
+string
+
+ |
+
+ |
+
+
+
+SecretKeySelector
+
+
+(Appears on:
+Mailgun,
+MicrosoftTeams,
+SMTP,
+Slack)
+
+
+
SecretKeySelector selects a key of a Secret.
+
+
+
+
+| Field |
+Description |
+
+
+
+
+
+secret
+
+
+Kubernetes core/v1.LocalObjectReference
+
+
+ |
+
+ The name of the secret in the pod’s namespace to select from.
+ |
+
+
+
+key
+
+string
+
+ |
+
+ The key of the secret to select from. Must be a valid secret key.
+ |
+
+
+
+SecretRef
+
+
+(Appears on:
+Customization)
+
+
+
SecretRef is reference to Kubernetes secret.
+
+
+
+
+| Field |
+Description |
+
+
+
+
+
+name
+
+string
+
+ |
+
+ |
+
+
+
+SeedJob
+
+
+(Appears on:
+JenkinsSpec)
+
+
+
SeedJob defines configuration for seed job
+More info: https://jenkinsci.github.io/kubernetes-operator/docs/getting-started/latest/configuration/#configure-seed-jobs-and-pipelines.
+
+
+
+
+| Field |
+Description |
+
+
+
+
+
+id
+
+string
+
+ |
+
+ ID is the unique seed job name
+ |
+
+
+
+credentialID
+
+string
+
+ |
+
+ CredentialID is the Kubernetes secret name which stores repository access credentials
+ |
+
+
+
+description
+
+string
+
+ |
+
+(Optional)
+ Description is the description of the seed job
+ |
+
+
+
+targets
+
+string
+
+ |
+
+ Targets is the repository path where are seed job definitions
+ |
+
+
+
+repositoryBranch
+
+string
+
+ |
+
+ RepositoryBranch is the repository branch where are seed job definitions
+ |
+
+
+
+repositoryUrl
+
+string
+
+ |
+
+ RepositoryURL is the repository access URL. Can be SSH or HTTPS.
+ |
+
+
+
+credentialType
+
+
+JenkinsCredentialType
+
+
+ |
+
+(Optional)
+ JenkinsCredentialType is the https://jenkinsci.github.io/kubernetes-credentials-provider-plugin/ credential type
+ |
+
+
+
+bitbucketPushTrigger
+
+bool
+
+ |
+
+(Optional)
+ BitbucketPushTrigger is used for Bitbucket web hooks
+ |
+
+
+
+githubPushTrigger
+
+bool
+
+ |
+
+(Optional)
+ GitHubPushTrigger is used for GitHub web hooks
+ |
+
+
+
+buildPeriodically
+
+string
+
+ |
+
+(Optional)
+ BuildPeriodically is setting for scheduled trigger
+ |
+
+
+
+pollSCM
+
+string
+
+ |
+
+(Optional)
+ PollSCM is setting for polling changes in SCM
+ |
+
+
+
+ignoreMissingFiles
+
+bool
+
+ |
+
+(Optional)
+ IgnoreMissingFiles is setting for Job DSL API plugin to ignore files that miss
+ |
+
+
+
+additionalClasspath
+
+string
+
+ |
+
+(Optional)
+ AdditionalClasspath is setting for Job DSL API plugin to set Additional Classpath
+ |
+
+
+
+failOnMissingPlugin
+
+bool
+
+ |
+
+(Optional)
+ FailOnMissingPlugin is setting for Job DSL API plugin that fails job if required plugin is missing
+ |
+
+
+
+unstableOnDeprecation
+
+bool
+
+ |
+
+(Optional)
+ UnstableOnDeprecation is setting for Job DSL API plugin that sets build status as unstable if build using deprecated features
+ |
+
+
+
+Service
+
+
+(Appears on:
+JenkinsSpec)
+
+
+
Service defines Kubernetes service attributes
+
+
+
+
+| Field |
+Description |
+
+
+
+
+
+annotations
+
+map[string]string
+
+ |
+
+(Optional)
+ 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
+ |
+
+
+
+labels
+
+map[string]string
+
+ |
+
+(Optional)
+ Route service traffic to pods with label keys and values matching this
+selector. If empty or not present, the service is assumed to have an
+external process managing its endpoints, which Kubernetes will not
+modify. Only applies to types ClusterIP, NodePort, and LoadBalancer.
+Ignored if type is ExternalName.
+More info: https://kubernetes.io/docs/concepts/services-networking/service/
+ |
+
+
+
+type
+
+
+Kubernetes core/v1.ServiceType
+
+
+ |
+
+(Optional)
+ Type determines how the Service is exposed. Defaults to ClusterIP. Valid
+options are ExternalName, ClusterIP, NodePort, and LoadBalancer.
+“ExternalName” maps to the specified externalName.
+“ClusterIP” allocates a cluster-internal IP address for load-balancing to
+endpoints. Endpoints are determined by the selector or if that is not
+specified, by manual construction of an Endpoints object. If clusterIP is
+“None”, no virtual IP is allocated and the endpoints are published as a
+set of endpoints rather than a stable IP.
+“NodePort” builds on ClusterIP and allocates a port on every node which
+routes to the clusterIP.
+“LoadBalancer” builds on NodePort and creates an
+external load-balancer (if supported in the current cloud) which routes
+to the clusterIP.
+More info: https://kubernetes.io/docs/concepts/services-networking/service/#publishing-services—service-types
+ |
+
+
+
+port
+
+int32
+
+ |
+
+ The port that are exposed by this service.
+More info: https://kubernetes.io/docs/concepts/services-networking/service/#virtual-ips-and-service-proxies
+ |
+
+
+
+nodePort
+
+int32
+
+ |
+
+(Optional)
+ The port on each node on which this service is exposed when type=NodePort or LoadBalancer.
+Usually assigned by the system. If specified, it will be allocated to the service
+if unused or else creation of the service will fail.
+Default is to auto-allocate a port if the ServiceType of this Service requires one.
+More info: https://kubernetes.io/docs/concepts/services-networking/service/#type-nodeport
+ |
+
+
+
+loadBalancerSourceRanges
+
+[]string
+
+ |
+
+(Optional)
+ If specified and supported by the platform, this will restrict traffic through the cloud-provider
+load-balancer will be restricted to the specified client IPs. This field will be ignored if the
+cloud-provider does not support the feature.”
+More info: https://kubernetes.io/docs/tasks/administer-cluster/securing-a-cluster/#restricting-cloud-metadata-api-access
+ |
+
+
+
+loadBalancerIP
+
+string
+
+ |
+
+(Optional)
+ Only applies to Service Type: LoadBalancer
+LoadBalancer will get created with the IP specified in this field.
+This feature depends on whether the underlying cloud-provider supports specifying
+the loadBalancerIP when a load balancer is created.
+This field will be ignored if the cloud-provider does not support the feature.
+ |
+
+
+
+ServiceAccount
+
+
+(Appears on:
+JenkinsSpec)
+
+
+
ServiceAccount defines Kubernetes service account attributes
+
+
+
+
+| Field |
+Description |
+
+
+
+
+
+annotations
+
+map[string]string
+
+ |
+
+(Optional)
+ 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
+ |
+
+
+
+Slack
+
+
+(Appears on:
+Notification)
+
+
+
Slack is handler for Slack notification channel.
+
+
+
+
+| Field |
+Description |
+
+
+
+
+
+webHookURLSecretKeySelector
+
+
+SecretKeySelector
+
+
+ |
+
+ The web hook URL to Slack App
+ |
+
+
+
+
+
+Generated with gen-crd-api-reference-docs
+on git commit fe81e5a.
+
diff --git a/website/content/en/docs/Getting Started/latest/configuration.md b/website/content/en/docs/Getting Started/latest/configuration.md
index 19721edd..fee5aeb9 100644
--- a/website/content/en/docs/Getting Started/latest/configuration.md
+++ b/website/content/en/docs/Getting Started/latest/configuration.md
@@ -89,7 +89,7 @@ podTemplate(label: label,
Jenkins Seed Jobs are configured using `Jenkins.spec.seedJobs` section from your custom resource manifest:
-```
+```yaml
apiVersion: jenkins.io/v1alpha2
kind: Jenkins
metadata:
@@ -150,7 +150,7 @@ If key was generated by `ssh-keygen` the public key content is located in
Jenkins default image details
---
-**Jenkins Operator** is fully compatible with **`jenkins:lts`** Docker image and does not introduce any hidden changes to the upstream Jenkins.
+**Jenkins Operator** is fully compatible with **`jenkins:lts`** Docker image and does not introduce any hidden changes
+to the upstream Jenkins. However due to problems with plugins and images version compatibility we are using specific tags
+in the exemplary Custom Resource, so you know a working configuration.
If needed, the Docker image can be easily changed in custom resource manifest as long as it supports standard Jenkins file system structure.
diff --git a/website/content/en/docs/Installation/Preview/_index.md b/website/content/en/docs/Installation/Preview/_index.md
new file mode 100644
index 00000000..60f42ee0
--- /dev/null
+++ b/website/content/en/docs/Installation/Preview/_index.md
@@ -0,0 +1,881 @@
+---
+title: "Installation - Preview"
+linkTitle: "Installation - Preview"
+weight: 1
+date: 2020-10-05
+description: >
+ How to install Jenkins Operator
+---
+
+{{% pageinfo %}}
+This document describes installation procedure for **Jenkins Operator**.
+All container images can be found at [virtuslab/jenkins-operator](https://hub.docker.com/r/virtuslab/jenkins-operator)
+{{% /pageinfo %}}
+
+## Requirements
+
+To run **Jenkins Operator**, you will need:
+- access to a Kubernetes cluster version `1.17+`
+- `kubectl` version `1.17+`
+
+## Configure Custom Resource Definition
+
+Install Jenkins Custom Resource Definition:
+
+```bash
+kubectl apply -f https://raw.githubusercontent.com/jenkinsci/kubernetes-operator/master/deploy/crds/jenkins_v1alpha2_jenkins_crd.yaml
+```
+
+## Deploy Jenkins Operator
+
+There are two ways to deploy the Jenkins Operator.
+
+### Using YAML's
+
+Apply Service Account and RBAC roles:
+
+```bash
+kubectl apply -f https://raw.githubusercontent.com/jenkinsci/kubernetes-operator/master/config/all-in-one-v1alpha2.yaml
+```
+
+Watch **Jenkins Operator** instance being created:
+
+```bash
+kubectl get pods -w
+```
+
+Now **Jenkins Operator** should be up and running in the `default` namespace.
+
+### Using Helm Chart
+
+There is an option to use Helm to install the operator. It requires the Helm 3+ for deployment.
+
+Create a namespace for the operator:
+
+```bash
+$ kubectl create namespace
+```
+
+To install, you need only to type these commands:
+
+```bash
+$ helm repo add jenkins https://raw.githubusercontent.com/jenkinsci/kubernetes-operator/master/chart
+$ helm install jenkins/jenkins-operator -n
+```
+
+In case you want to use released Chart **v0.4.1**, before installing/upgrading please install additional CRD into the cluster:
+
+```bash
+$ kubectl apply -f https://raw.githubusercontent.com/jenkinsci/kubernetes-operator/master/chart/jenkins-operator/crds/jenkinsimage-crd.yaml
+```
+
+To add custom labels and annotations, you can use `values.yaml` file or pass them into `helm install` command, e.g.:
+
+```bash
+$ helm install jenkins/jenkins-operator -n --set jenkins.labels.LabelKey=LabelValue,jenkins.annotations.AnnotationKey=AnnotationValue
+```
+You can further customize Jenkins using `values.yaml`:
+Jenkins instance configuration
+
+
+
+
+
+ |
+Field |
+Default value |
+Description |
+
+
+
+
+
+
+jenkins
+ |
+
+ operator is section for configuring operator deployment
+
+
+
+enabled
+ |
+
+true
+ |
+
+Enabled can enable or disable the Jenkins instance.
+Set to false if you have configured CR already and/or you want to deploy an operator only.
+ |
+
+
+
+apiVersion
+ |
+jenkins.io/v1alpha2 |
+
+Version of the CR manifest. The recommended and default value is jenkins.io/v1alpha2.
+More info
+ |
+
+
+
+name
+ |
+
+jenkins
+ |
+
+Name of resource. The pod name will be jenkins-<name> (name will be set as suffix).
+ |
+
+
+
+namespace
+ |
+
+default
+ |
+
+Namespace the resources will be deployed to. It's not recommended to use default namespace.
+Create new namespace for jenkins (e.g. kubectl create -n jenkins)
+ |
+
+
+
+labels
+ |
+
+{}
+ |
+
+Labels are injected into metadata labels field.
+ |
+
+
+
+annotations
+ |
+
+{}
+ |
+
+Annotations are injected into metadata annotations field.
+ |
+
+
+
+image
+ |
+
+jenkins/jenkins:lts
+ |
+
+Image is the name (and tag) of the Jenkins instance.
+It's recommended to use LTS (tag: "lts") version.
+ |
+
+
+
+env
+ |
+
+[]
+ |
+
+Env contains jenkins container environment variables.
+ |
+
+
+
+imagePullPolicy
+ |
+
+Always
+ |
+
+Defines policy for pulling images
+ |
+
+
+
+priorityClassName
+ |
+
+""
+ |
+
+PriorityClassName indicates the importance of a Pod relative to other Pods.
+More info
+ |
+
+
+
+disableCSRFProtection
+ |
+
+false
+ |
+
+disableCSRFProtection can enable or disable operator built-in CSRF protection.
+Set it to true if you are using OpenShift Jenkins Plugin.
+More info
+ |
+
+
+
+imagePullSecrets
+ |
+
+[]
+ |
+
+Used if you want to pull images from private repository
+More info
+ |
+
+
+
+notifications
+ |
+
+[]
+ |
+
+Notifications is feature that notify user about Jenkins reconcilation status
+More info
+ |
+
+
+
+basePlugins
+ |
+
+
+- name: kubernetes
+ version: "1.25.2"
+- name: workflow-job
+ version: "2.39"
+- name: workflow-aggregator
+ version: "2.6"
+- name: git
+ version: "4.2.2"
+- name: job-dsl
+ version: "1.77"
+- name: configuration-as-code
+ version: "1.38"
+- name: kubernetes-credentials
+ -provider
+ version: "0.13"
+
+ |
+
+Plugins installed and required by the operator
+shouldn't contain plugins defined by user
+You can change their versions here
+More info
+ |
+
+
+
+plugins
+ |
+
+[]
+ |
+
+Plugins required by the user. You can define plugins here.
+More info
+Example:
+
+plugins:
+ - name: simple-theme-plugin
+ version: 0.5.1
+
+ |
+
+
+
+seedJobs
+ |
+
+[]
+ |
+
+Placeholder for jenkins seed jobs
+For seed job creation tutorial, check: Prepare seed jobs
+ Configure seed jobs
+ Example:
+
+
+seedJobs:
+- id: jenkins-operator
+ targets: "cicd/jobs/*.jenkins"
+ description: "Jenkins Operator repository"
+ repositoryBranch: master
+ repositoryUrl:
+ - https://github.com/jenkinsci/kubernetes-operator.git
+
+
+ |
+
+
+
+resources
+ |
+
+
+limits:
+ cpu: 1500m
+ memory: 3Gi
+requests:
+ cpu: 1
+ memory: 500M
+
+ |
+
+Resource limit/request for Jenkins
+More info
+ |
+
+
+
+volumes
+ |
+
+
+- name: backup
+ persistentVolumeClaim:
+ claimName: jenkins-backup
+
+ |
+
+Volumes used by Jenkins
+By default, we are only using PVC volume for storing backups.
+ |
+
+
+
+volumeMounts
+ |
+
+[]
+ |
+
+volumeMounts are mounts for Jenkins pod.
+ |
+
+
+
+securityContext
+ |
+
+runAsUser: 1000
+fsGroup: 1000
+ |
+
+SecurityContext for pod.
+ |
+
+
+service |
+not implemented |
+Http Jenkins service. See https://jenkinsci.github.io/kubernetes-operator/docs/getting-started/latest/schema/#github.com/jenkinsci/kubernetes-operator/pkg/apis/jenkins/v1alpha2.Service for details. |
+
+
+slaveService |
+not implemented |
+Slave Jenkins service. See https://jenkinsci.github.io/kubernetes-operator/docs/getting-started/latest/schema/#github.com/jenkinsci/kubernetes-operator/pkg/apis/jenkins/v1alpha2.Service for details. |
+
+
+
+livenessProbe
+ |
+
+
+livenessProbe:
+ failureThreshold: 12
+ httpGet:
+ path: /login
+ port: http
+ scheme: HTTP
+ initialDelaySeconds: 80
+ periodSeconds: 10
+ successThreshold: 1
+ timeoutSeconds: 5
+
+ |
+
+livenessProbe for Pod
+ |
+
+
+
+readinessProbe
+ |
+
+
+readinessProbe:
+ failureThreshold: 3
+ httpGet:
+ path: /login
+ port: http
+ scheme: HTTP
+ initialDelaySeconds: 30
+ periodSeconds: 10
+ successThreshold: 1
+ timeoutSeconds: 1
+
+ |
+
+readinessProbe for Pod
+ |
+
+
+
+
+backup
+
+
+
+
+Backup
+
+
+
+ |
+
+ |
+
+Backup is section for configuring operator's backup feature
+By default backup feature is enabled and pre-configured
+This section simplifies the configuration described here: Configure backup and restore
+For customization tips see Custom backup and restore
+ |
+
+
+
+configuration
+
+
+
+Configuration
+
+
+
+ |
+ |
+
+Section where we can configure Jenkins instance.
+See Customization for details
+ |
+
+
+ |
+
+
+
+
+### Configuring operator deployment
+
+
+
+
+ |
+ Field |
+ Default value |
+ Description |
+
+
+
+
+
+
+ operator
+ |
+
+ operator is section for configuring operator deployment
+
+
+
+ replicaCount
+ |
+
+ 1
+ |
+
+ Number of Replicas.
+ |
+
+
+
+ image
+ |
+
+ virtuslab/jenkins-operator:v0.4.0
+ |
+
+ Name (and tag) of the Jenkins Operator image.
+ |
+
+
+
+ imagePullPolicy
+ |
+
+ IfNotPresent
+ |
+
+ Defines policy for pulling images.
+ |
+
+
+
+ imagePullSecrets
+ |
+
+ []
+ |
+
+ Used if you want to pull images from private repository.
+ |
+
+
+
+ nameOverride
+ |
+
+ ""
+ |
+
+ nameOverride overrides the app name.
+ |
+
+
+
+ fullnameOverride
+ |
+
+ ""
+ |
+
+ fullnameOverride overrides the deployment name
+ |
+
+
+
+ resources
+ |
+
+ {}
+ |
+
+ |
+
+
+
+ nodeSelector
+ |
+
+ {}
+ |
+
+ |
+
+
+
+ tolerations
+ |
+
+ {}
+ |
+
+ |
+
+
+
+ affinity
+ |
+
+ {}
+ |
+
+ |
+
+
+ |
+
+
+
+
+
+
+Backup
+
+
+(Appears on:
+JenkinsConfiguration)
+
+
+Backup defines configuration of Jenkins backup.
+
+
+
+
+
+| Field |
+Default value |
+Description |
+
+
+
+
+
+ enabled
+ |
+
+ true
+ |
+
+ Enabled is enable/disable switch for backup feature.
+ |
+
+
+
+ image
+ |
+
+ virtuslab/jenkins-operator-backup-pvc:v0.0.8
+ |
+
+ Image used by backup feature.
+ |
+
+
+
+ containerName
+ |
+
+ backup
+ |
+
+ Backup container name.
+ |
+
+
+
+ interval
+ |
+
+ 30
+ |
+
+ Defines how often make backup in seconds.
+ |
+
+
+
+ makeBackupBeforePodDeletion
+ |
+
+ true
+ |
+
+ When enabled will make backup before pod deletion.
+ |
+
+
+
+ backupCommand
+ |
+
+ /home/user/bin/backup.sh
+ |
+
+ Backup container command.
+ |
+
+
+
+ restoreCommand
+ |
+
+ /home/user/bin/restore.sh
+ |
+
+ Backup restore command.
+ |
+
+
+
+ pvc
+ |
+
+ Persistent Volume Claim Kubernetes resource
+
+
+
+
+
+ enabled
+ |
+
+ true
+ |
+
+ Enable/disable switch for PVC
+ |
+
+
+
+ enabled
+ |
+
+ true
+ |
+
+ Enable/disable switch for PVC
+ |
+
+
+
+ size
+ |
+
+ 5Gi
+ |
+
+ Size of PVC
+ |
+
+
+
+ className
+ |
+
+ ""
+ |
+
+ StorageClassName for PVC
+ More info
+ |
+
+
+
+ |
+
+
+
+ env
+ |
+
+
+- name: BACKUP_DIR
+ value: /backup
+- name: JENKINS_HOME
+ value: /jenkins-home
+- name: BACKUP_COUNT
+ value: "3"
+
+ |
+
+ Contains container environment variables.
+ PVC backup provider handles these variables:
+ BACKUP_DIR - path for storing backup files (default: "/backup")
+ JENKINS_HOME - path to jenkins home (default: "/jenkins-home")
+ BACKUP_COUNT - define how much recent backups will be kept
+ |
+
+
+
+
+ volumeMounts
+ |
+
+
+- name: jenkins-home
+ mountPath: /jenkins-home
+- mountPath: /backup
+ name: backup
+
+ |
+
+ Holds the mount points for volumes.
+ |
+
+
+
+
+ Configuration
+
+
+ (Appears on:
+ Jenkins instance configuration)
+
+
+
+
+
+ | Field |
+ Default value |
+ Description |
+
+
+
+
+
+ configurationAsCode
+ |
+
+ {}
+ |
+
+ ConfigurationAsCode defines configuration of Jenkins customization via Configuration as Code Jenkins plugin.
+Example:
+
+- configMapName: jenkins-casc
+ content: {}
+
+ |
+
+
+
+ groovyScripts
+ |
+
+ {}
+ |
+
+ GroovyScripts defines configuration of Jenkins customization via groovy scripts.
+ Example:
+
+- configMapName: jenkins-gs
+ content: {}
+
+ |
+
+
+
+ secretRefName
+ |
+
+ ""
+ |
+
+ secretRefName of existing secret (previously created).
+ |
+
+
+
+ secretData
+ |
+
+ {}
+ |
+
+ If secretRefName is empty, secretData creates new secret and fills with data provided in secretData.
+ |
+
+
+
+