diff --git a/.github/workflows/auto-tests.yaml b/.github/workflows/auto-tests.yaml index 6231e0d4e..400a3d5f4 100644 --- a/.github/workflows/auto-tests.yaml +++ b/.github/workflows/auto-tests.yaml @@ -46,7 +46,10 @@ jobs: make minikube-start MINIKUBE_DRIVER='docker' CPUS_NUMBER=2 - name: Jenkins Operator - e2e - run: | - make e2e E2E_TEST_ARGS='-ginkgo.v' + run: make e2e E2E_TEST_ARGS='-ginkgo.v' -#TODO Helm e2e test \ No newline at end of file + - name: Jenkins Operator - Helm Chart tests + run: | + make helm-lint + eval $(bin/minikube docker-env) + make helm-e2e E2E_TEST_ARGS='-ginkgo.v' diff --git a/Makefile b/Makefile index 20438f214..295ef6aa8 100644 --- a/Makefile +++ b/Makefile @@ -89,11 +89,17 @@ test: ## Runs the go tests @RUNNING_TESTS=1 go test -tags "$(BUILDTAGS) cgo" $(PACKAGES_FOR_UNIT_TESTS) .PHONY: e2e -e2e: deepcopy-gen ## Runs e2e tests, you can use EXTRA_ARGS +e2e: deepcopy-gen manifests ## Runs e2e tests, you can use EXTRA_ARGS @echo "+ $@" - RUNNING_TESTS=1 go test -parallel=1 "./test/e2e/" -tags "$(BUILDTAGS) cgo" -v -timeout 60m -run "$(E2E_TEST_SELECTOR)" \ + RUNNING_TESTS=1 go test -parallel=1 "./test/e2e/" -ginkgo.v -tags "$(BUILDTAGS) cgo" -v -timeout 60m -run "$(E2E_TEST_SELECTOR)" \ -jenkins-api-hostname=$(JENKINS_API_HOSTNAME) -jenkins-api-port=$(JENKINS_API_PORT) -jenkins-api-use-nodeport=$(JENKINS_API_USE_NODEPORT) $(E2E_TEST_ARGS) +.PHONY: helm-e2e +IMAGE_NAME := $(DOCKER_REGISTRY):$(GITCOMMIT) +helm-e2e: helm container-runtime-build ## Runs helm e2e tests, you can use EXTRA_ARGS + @echo "+ $@" + RUNNING_TESTS=1 go test -parallel=1 "./test/helm/" -ginkgo.v -tags "$(BUILDTAGS) cgo" -v -timeout 60m -run "$(E2E_TEST_SELECTOR)" -image-name=$(IMAGE_NAME) $(E2E_TEST_ARGS) + .PHONY: vet vet: ## Verifies `go vet` passes @echo "+ $@" @@ -136,7 +142,7 @@ install: ## Installs the executable .PHONY: run run: export WATCH_NAMESPACE = $(NAMESPACE) run: export OPERATOR_NAME = $(NAME) -run: fmt vet manifests install-crds build ## Run the executable, you can use EXTRA_ARGS +run: fmt vet install-crds build ## Run the executable, you can use EXTRA_ARGS @echo "+ $@" ifeq ($(KUBERNETES_PROVIDER),minikube) kubectl config use-context $(KUBECTL_CONTEXT) @@ -301,6 +307,18 @@ ifneq ($(KUBERNETES_PROVIDER),crc) $(error KUBERNETES_PROVIDER not set to 'crc') endif +.PHONY: helm +HAS_HELM := $(shell which $(PROJECT_DIR)/bin/helm) +helm: ## Download helm if it's not present + @echo "+ $@" +ifndef HAS_HELM + mkdir -p $(PROJECT_DIR)/bin + curl -Lo bin/helm.tar.gz https://get.helm.sh/helm-v$(HELM_VERSION)-$(PLATFORM)-amd64.tar.gz && tar xzfv bin/helm.tar.gz -C $(PROJECT_DIR)/bin + mv $(PROJECT_DIR)/bin/$(PLATFORM)-amd64/helm $(PROJECT_DIR)/bin/helm + rm -rf $(PROJECT_DIR)/bin/$(PLATFORM)-amd64 + rm -rf $(PROJECT_DIR)/bin/helm.tar.gz +endif + .PHONY: minikube HAS_MINIKUBE := $(shell which $(PROJECT_DIR)/bin/minikube) minikube: ## Download minikube if it's not present @@ -342,11 +360,13 @@ bump-version: sembump ## Bump the version in the version file. Set BUMP to [ pat sed -i s/$(VERSION)/$(NEW_VERSION)/g README.md sed -i s/$(VERSION)/$(NEW_VERSION)/g deploy/operator.yaml sed -i s/$(VERSION)/$(NEW_VERSION)/g deploy/$(ALL_IN_ONE_DEPLOY_FILE_PREFIX)-$(API_VERSION).yaml - cp deploy/service_account.yaml deploy/$(ALL_IN_ONE_DEPLOY_FILE_PREFIX)-$(API_VERSION).yaml - cat deploy/role.yaml >> deploy/$(ALL_IN_ONE_DEPLOY_FILE_PREFIX)-$(API_VERSION).yaml - cat deploy/role_binding.yaml >> deploy/$(ALL_IN_ONE_DEPLOY_FILE_PREFIX)-$(API_VERSION).yaml - cat deploy/operator.yaml >> deploy/$(ALL_IN_ONE_DEPLOY_FILE_PREFIX)-$(API_VERSION).yaml - git add VERSION.txt README.md deploy/operator.yaml deploy/$(ALL_IN_ONE_DEPLOY_FILE_PREFIX)-$(API_VERSION).yaml + cp config/service_account.yaml deploy/$(ALL_IN_ONE_DEPLOY_FILE_PREFIX)-$(API_VERSION).yaml + cat config/rbac/leader_election_role.yaml >> config/$(ALL_IN_ONE_DEPLOY_FILE_PREFIX)-$(API_VERSION).yaml + cat config/rbac/leader_election_role_binding.yaml >> config/$(ALL_IN_ONE_DEPLOY_FILE_PREFIX)=$(API_VERSION).yaml + cat config/rbac/role.yaml >> config/$(ALL_IN_ONE_DEPLOY_FILE_PREFIX)=$(API_VERSION).yaml + cat config/rbac/role_binding.yaml >> config/$(ALL_IN_ONE_DEPLOY_FILE_PREFIX)-$(API_VERSION).yaml + cat config/manager/manager.yaml >> config/$(ALL_IN_ONE_DEPLOY_FILE_PREFIX)-$(API_VERSION).yaml + git add VERSION.txt README.md config/manager/manager.yaml config/$(ALL_IN_ONE_DEPLOY_FILE_PREFIX)-$(API_VERSION).yaml git commit -vaem "Bump version to $(NEW_VERSION)" @echo "Run make tag to create and push the tag for new version $(NEW_VERSION)" @@ -379,19 +399,24 @@ endif go mod vendor -v @echo +.PHONY: helm-lint +helm-lint: helm + @echo "+ $@" + bin/helm lint chart/jenkins-operator + .PHONY: helm-package -helm-package: +helm-package: helm @echo "+ $@" mkdir -p /tmp/jenkins-operator-charts mv chart/jenkins-operator/*.tgz /tmp/jenkins-operator-charts - cd chart && helm package jenkins-operator + cd chart && bin/helm package jenkins-operator mv /tmp/jenkins-operator-charts/*.tgz chart/jenkins-operator/ rm -rf /tmp/jenkins-operator-charts/ .PHONY: helm-deploy helm-deploy: helm-package @echo "+ $@" - helm repo index chart/ --url https://raw.githubusercontent.com/jenkinsci/kubernetes-operator/master/chart/jenkins-operator/ + bin/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 and build hugo extended locally if necessary @@ -418,6 +443,7 @@ FILENAME := config/all_in_one_$(API_VERSION).yaml all-in-one-build: ## Re-generate all-in-one yaml @echo "+ $@" > $(FILENAME) + cat config/service_account.yaml >> $(FILENAME) cat config/rbac/leader_election_role.yaml >> $(FILENAME) cat config/rbac/leader_election_role_binding.yaml >> $(FILENAME) cat config/rbac/role.yaml >> $(FILENAME) diff --git a/chart/jenkins-operator/crds/jenkins-crd.yaml b/chart/jenkins-operator/crds/jenkins-crd.yaml index b254ecaeb..f32021644 100644 --- a/chart/jenkins-operator/crds/jenkins-crd.yaml +++ b/chart/jenkins-operator/crds/jenkins-crd.yaml @@ -1,6 +1,11 @@ -apiVersion: apiextensions.k8s.io/v1beta1 + +--- +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 @@ -11,9 +16,3397 @@ spec: singular: jenkins scope: Namespaced versions: - - name : v1alpha2 + - 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 - - name : v1alpha1 - served: true - storage: false + subresources: + status: {} +status: + acceptedNames: + kind: "" + plural: "" + conditions: [] + storedVersions: [] diff --git a/chart/jenkins-operator/crds/jenkinsimage-crd.yaml b/chart/jenkins-operator/crds/jenkinsimage-crd.yaml deleted file mode 100644 index 127530a48..000000000 --- a/chart/jenkins-operator/crds/jenkinsimage-crd.yaml +++ /dev/null @@ -1,19 +0,0 @@ -apiVersion: apiextensions.k8s.io/v1beta1 -kind: CustomResourceDefinition -metadata: - name: jenkinsimages.jenkins.io -spec: - group: jenkins.io - names: - kind: JenkinsImage - listKind: JenkinsImageList - plural: jenkinsimages - singular: jenkinsimage - scope: Namespaced - versions: - - name : v1alpha2 - served: true - storage: true - - name : v1alpha1 - served: true - storage: false \ No newline at end of file diff --git a/chart/jenkins-operator/templates/_role.yaml b/chart/jenkins-operator/templates/_role.yaml index ed34150b5..9ff460642 100644 --- a/chart/jenkins-operator/templates/_role.yaml +++ b/chart/jenkins-operator/templates/_role.yaml @@ -7,54 +7,80 @@ metadata: name: jenkins-operator namespace: {{ $namespace }} rules: + - apiGroups: + - apps + resources: + - daemonsets + - deployments + - replicasets + - statefulsets + verbs: + - '*' + - apiGroups: + - apps + - jenkins-operator + resources: + - deployments/finalizers + verbs: + - update + - apiGroups: + - build.openshift.io + resources: + - buildconfigs + - builds + verbs: + - get + - list + - watch - apiGroups: - "" resources: - - services - configmaps - secrets + - services verbs: - - get - create - - update + - get - list + - update - watch - apiGroups: - - apps + - "" resources: - - deployments - - daemonsets - - replicasets - - statefulsets + - events verbs: - - '*' + - create + - get + - list + - patch + - watch - apiGroups: - "" resources: - - serviceaccounts + - persistentvolumeclaims verbs: - get - - create - - update - list - watch - apiGroups: - - rbac.authorization.k8s.io + - "" resources: - - roles - - rolebindings + - pods verbs: - - get - create - - update + - delete + - get - list + - patch + - update - watch - apiGroups: - "" resources: - - pods/portforward + - pods + - pods/exec verbs: - - create + - '*' - apiGroups: - "" resources: @@ -66,28 +92,41 @@ rules: - apiGroups: - "" resources: - - pods - - pods/exec + - pods/portforward verbs: - - "*" + - create - apiGroups: - "" resources: - - events + - serviceaccounts verbs: + - create - get + - list + - update - watch + - apiGroups: + - image.openshift.io + resources: + - imagestreams + verbs: + - get - list - - create - - patch + - watch - apiGroups: - - apps - resourceNames: - - jenkins-operator + - jenkins.io resources: - - deployments/finalizers + - jenkins/finalizers verbs: - update + - apiGroups: + - jenkins.io + resources: + - jenkins/status + verbs: + - get + - patch + - update - apiGroups: - jenkins.io resources: @@ -95,23 +134,26 @@ rules: verbs: - '*' - apiGroups: - - "" + - rbac.authorization.k8s.io resources: - - persistentvolumeclaims + - rolebindings + - roles verbs: + - create - get - list + - update - watch - apiGroups: - "route.openshift.io" resources: - routes verbs: + - create - get - list - - watch - - create - update + - watch - apiGroups: - "image.openshift.io" resources: diff --git a/chart/jenkins-operator/templates/jenkins.yaml b/chart/jenkins-operator/templates/jenkins.yaml index 7d070fdbc..3952b3b1a 100644 --- a/chart/jenkins-operator/templates/jenkins.yaml +++ b/chart/jenkins-operator/templates/jenkins.yaml @@ -9,31 +9,41 @@ metadata: {{- end }} spec: configurationAsCode: - configurations: - {{- range .Values.jenkins.configuration.configurationAsCode }} - - name: {{ .configMapName }} - {{- end }} {{- if .Values.jenkins.configuration.configurationAsCode }} + configurations: + {{ range .Values.jenkins.configuration.configurationAsCode }} + - name: {{ .configMapName }} + {{- end }} secret: {{- if .Values.jenkins.configuration.secretRefName }} name: {{ .Values.jenkins.configuration.secretRefName }} {{- else if .Values.jenkins.configuration.secretData }} name: jenkins-{{ .Values.jenkins.name }} {{- end }} + {{- else }} + configurations: [] + secret: + name: "" {{- end }} groovyScripts: - configurations: - {{- range .Values.jenkins.configuration.groovyScripts }} - - name: {{ .configMapName }} - {{- end }} {{- if .Values.jenkins.configuration.groovyScripts }} + configurations: + {{- range .Values.jenkins.configuration.groovyScripts }} + - name: {{ .configMapName }} + {{- end }} secret: {{- if .Values.jenkins.configuration.secretRefName }} name: {{ .Values.jenkins.configuration.secretRefName }} {{- else if .Values.jenkins.configuration.secretData }} name: jenkins-{{ .Values.jenkins.name }} {{- end }} + {{- else }} + configurations: [] + secret: + name: "" {{- end }} + jenkinsAPISettings: + authorizationStrategy: {{ .Values.jenkins.authorizationStrategy }} {{- if .Values.jenkins.backup.enabled }} backup: containerName: {{ .Values.jenkins.backup.containerName }} @@ -84,7 +94,11 @@ spec: {{- with .Values.jenkins.plugins }} plugins: {{ toYaml . | nindent 4 }} {{- end }} - priorityClassName: {{ .Values.jenkins.priorityClassName }} + {{- if .Values.jenkins.priorityClassName }} + priorityClassName: {{- .Values.jenkins.priorityClassName }} + {{- else }} + priorityClassName: "" + {{- end }} disableCSRFProtection: {{ .Values.jenkins.disableCSRFProtection }} containers: - name: jenkins-master @@ -112,6 +126,9 @@ spec: - name: {{ .Values.jenkins.backup.containerName }} image: {{ .Values.jenkins.backup.image }} imagePullPolicy: IfNotPresent + {{- with .Values.jenkins.backup.resources }} + resources: {{ toYaml . | nindent 10 }} + {{- end }} {{- with .Values.jenkins.backup.env }} env: {{- toYaml . | nindent 8 }} {{- end }} diff --git a/chart/jenkins-operator/templates/leader_election_role.yaml b/chart/jenkins-operator/templates/leader_election_role.yaml new file mode 100644 index 000000000..96f0afa7c --- /dev/null +++ b/chart/jenkins-operator/templates/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/chart/jenkins-operator/templates/leader_election_role_binding.yaml b/chart/jenkins-operator/templates/leader_election_role_binding.yaml new file mode 100644 index 000000000..2f0a21324 --- /dev/null +++ b/chart/jenkins-operator/templates/leader_election_role_binding.yaml @@ -0,0 +1,12 @@ +--- +apiVersion: rbac.authorization.k8s.io/v1 +kind: RoleBinding +metadata: + name: leader-election-rolebinding +roleRef: + apiGroup: rbac.authorization.k8s.io + kind: Role + name: leader-election-role +subjects: +- kind: ServiceAccount + name: jenkins-operator diff --git a/chart/jenkins-operator/templates/operator.yaml b/chart/jenkins-operator/templates/operator.yaml index ffc1c7cf0..c00c2b390 100644 --- a/chart/jenkins-operator/templates/operator.yaml +++ b/chart/jenkins-operator/templates/operator.yaml @@ -30,7 +30,7 @@ spec: containerPort: 80 protocol: TCP command: - - jenkins-operator + - /manager args: [] env: - name: WATCH_NAMESPACE diff --git a/chart/jenkins-operator/values.yaml b/chart/jenkins-operator/values.yaml index 363296c11..9670c5f23 100644 --- a/chart/jenkins-operator/values.yaml +++ b/chart/jenkins-operator/values.yaml @@ -63,7 +63,7 @@ jenkins: # # basePlugins: # - name: kubernetes - # version: 1.28.6 + # version: 1.29.2 # - name: workflow-job # version: "2.40" # - name: workflow-aggregator @@ -136,6 +136,9 @@ jenkins: # volumeMounts are mounts for Jenkins pod volumeMounts: [] + # defines authorization strategy of the operator for the Jenkins API + authorizationStrategy: createUser + # securityContext for pod securityContext: runAsUser: 1000 @@ -216,6 +219,15 @@ jenkins: # See https://kubernetes.io/docs/concepts/storage/persistent-volumes/#class-1 for more details className: "" + # resources used by backup container + resources: + limits: + cpu: 1500m + memory: 1Gi + requests: + cpu: 100m + memory: 256Mi + # env contains container environment variables # PVC backup provider handles these variables: # BACKUP_DIR - path for storing backup files (default: "/backup") diff --git a/config/all_in_one_v1alpha2.yaml b/config/all_in_one_v1alpha2.yaml index 0a70e35b9..8ec36d885 100644 --- a/config/all_in_one_v1alpha2.yaml +++ b/config/all_in_one_v1alpha2.yaml @@ -1,4 +1,9 @@ --- +apiVersion: v1 +kind: ServiceAccount +metadata: + name: jenkins-operator +--- # permissions to do leader election. apiVersion: rbac.authorization.k8s.io/v1 kind: Role @@ -37,45 +42,95 @@ roleRef: name: leader-election-role subjects: - kind: ServiceAccount - name: default - namespace: default + name: jenkins-operator + --- apiVersion: rbac.authorization.k8s.io/v1 -kind: ClusterRole +kind: Role metadata: creationTimestamp: null - name: manager-role + name: jenkins-operator rules: +- apiGroups: + - apps + resources: + - daemonsets + - deployments + - replicasets + - statefulsets + verbs: + - '*' +- apiGroups: + - apps + - jenkins-operator + resources: + - deployments/finalizers + verbs: + - update +- apiGroups: + - build.openshift.io + resources: + - buildconfigs + - builds + verbs: + - get + - list + - watch - apiGroups: - "" resources: - - services - configmaps - secrets - - serviceaccounts + - services verbs: - - get - create + - get + - list - update + - watch +- apiGroups: + - "" + resources: + - events + verbs: + - create + - get - list + - patch - watch - apiGroups: - - apps + - "" resources: - - deployments - - daemonsets - - replicasets - - statefulsets + - persistentvolumeclaims verbs: - - '*' + - get + - list + - watch - apiGroups: - - rbac.authorization.k8s.io + - "" resources: - - roles - - rolebindings + - pods verbs: - create + - delete + - get + - list + - patch - update + - watch +- apiGroups: + - "" + resources: + - pods + - pods/exec + verbs: + - '*' +- apiGroups: + - "" + resources: + - pods/log + verbs: + - get - list - watch - apiGroups: @@ -83,100 +138,100 @@ rules: resources: - pods/portforward verbs: - - create + - create - apiGroups: - - "" + - "" resources: - - pods/log + - serviceaccounts verbs: - - get - - list - - watch + - create + - get + - list + - update + - watch - apiGroups: - - "" + - image.openshift.io resources: - - pods - - pods/exec + - imagestreams verbs: - - "*" + - get + - list + - watch - apiGroups: - - "" + - jenkins.io resources: - - events + - jenkins/finalizers verbs: - - get - - watch - - list - - create - - patch + - update - apiGroups: - - apps - resourceNames: - - jenkins-operator + - jenkins.io resources: - - deployments/finalizers + - jenkins/status verbs: - - update + - get + - patch + - update - apiGroups: - - jenkins.io + - jenkins.io resources: - - '*' + - '*' verbs: - - '*' + - '*' - apiGroups: - - "" + - rbac.authorization.k8s.io resources: - - persistentvolumeclaims + - rolebindings + - roles verbs: - - get - - list - - watch + - create + - get + - list + - update + - watch - apiGroups: - - "route.openshift.io" + - "route.openshift.io" resources: - - routes + - routes verbs: - - get - - list - - watch - - create - - update + - create + - get + - list + - update + - watch - apiGroups: - - "image.openshift.io" + - "image.openshift.io" resources: - - imagestreams + - imagestreams verbs: - - get - - list - - watch + - get + - list + - watch - apiGroups: - - "build.openshift.io" + - "build.openshift.io" resources: - - builds - - buildconfigs + - builds + - buildconfigs verbs: - - get - - list - - watch + - get + - list + - watch --- apiVersion: rbac.authorization.k8s.io/v1 -kind: ClusterRoleBinding +kind: RoleBinding metadata: - name: manager-rolebinding + name: jenkins-operator roleRef: apiGroup: rbac.authorization.k8s.io - kind: ClusterRole - name: manager-role + kind: Role + name: jenkins-operator subjects: - kind: ServiceAccount - name: default - namespace: default + name: jenkins-operator --- apiVersion: apps/v1 kind: Deployment metadata: name: jenkins-operator - namespace: default labels: control-plane: controller-manager spec: @@ -189,6 +244,7 @@ spec: labels: control-plane: controller-manager spec: + serviceAccountName: jenkins-operator securityContext: runAsUser: 65532 containers: diff --git a/config/manager/manager.yaml b/config/manager/manager.yaml index e0c319273..1a631d33f 100644 --- a/config/manager/manager.yaml +++ b/config/manager/manager.yaml @@ -16,6 +16,7 @@ spec: labels: control-plane: controller-manager spec: + serviceAccountName: jenkins-operator securityContext: runAsUser: 65532 containers: diff --git a/config/rbac/role.yaml b/config/rbac/role.yaml index c6364d23b..096d1926a 100644 --- a/config/rbac/role.yaml +++ b/config/rbac/role.yaml @@ -1,119 +1,172 @@ + --- apiVersion: rbac.authorization.k8s.io/v1 -kind: ClusterRole +kind: Role metadata: creationTimestamp: null - name: manager-role + name: jenkins-operator 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: - - get - - 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 + - apiGroups: + - apps + resources: + - daemonsets + - deployments + - replicasets + - statefulsets + verbs: + - '*' + - apiGroups: + - apps + - jenkins-operator + resources: + - deployments/finalizers + verbs: + - update + - apiGroups: + - build.openshift.io + resources: + - buildconfigs + - builds + verbs: + - get + - list + - watch + - apiGroups: + - "" + resources: + - configmaps + - secrets + - services + verbs: + - create + - get + - list + - update + - watch + - apiGroups: + - "" + resources: + - events + verbs: + - create + - get + - list + - patch + - watch + - apiGroups: + - "" + resources: + - persistentvolumeclaims + verbs: + - get + - list + - watch + - apiGroups: + - "" + resources: + - pods + verbs: + - create + - delete + - get + - list + - patch + - update + - watch + - apiGroups: + - "" + resources: + - pods + - pods/exec + verbs: + - '*' + - apiGroups: + - "" + resources: + - pods/log + verbs: + - get + - list + - watch + - apiGroups: + - "" + resources: + - pods/portforward + verbs: + - create + - apiGroups: + - "" + resources: + - serviceaccounts + verbs: + - create + - get + - list + - update + - watch + - apiGroups: + - image.openshift.io + resources: + - imagestreams + verbs: + - get + - list + - watch + - apiGroups: + - jenkins.io + resources: + - jenkins/finalizers + verbs: + - update + - apiGroups: + - jenkins.io + resources: + - jenkins/status + verbs: + - get + - patch + - update + - apiGroups: + - jenkins.io + resources: + - '*' + verbs: + - '*' + - apiGroups: + - rbac.authorization.k8s.io + resources: + - rolebindings + - roles + verbs: + - create + - get + - list + - update + - watch + - apiGroups: + - "route.openshift.io" + resources: + - routes + verbs: + - create + - get + - list + - update + - watch + - apiGroups: + - "image.openshift.io" + resources: + - imagestreams + verbs: + - get + - list + - watch + - apiGroups: + - "build.openshift.io" + resources: + - builds + - buildconfigs + verbs: + - get + - list + - watch \ No newline at end of file diff --git a/config/rbac/role_binding.yaml b/config/rbac/role_binding.yaml index 1d8f4c445..149040eec 100644 --- a/config/rbac/role_binding.yaml +++ b/config/rbac/role_binding.yaml @@ -1,13 +1,12 @@ --- apiVersion: rbac.authorization.k8s.io/v1 -kind: ClusterRoleBinding +kind: RoleBinding metadata: name: manager-rolebinding roleRef: apiGroup: rbac.authorization.k8s.io - kind: ClusterRole - name: manager-role + kind: Role + name: jenkins-operator subjects: - kind: ServiceAccount - name: default - namespace: default + name: jenkins-operator diff --git a/config/service_account.yaml b/config/service_account.yaml new file mode 100644 index 000000000..7d1e1842b --- /dev/null +++ b/config/service_account.yaml @@ -0,0 +1,6 @@ +--- +apiVersion: v1 +kind: ServiceAccount +metadata: + name: jenkins-operator + namespace: default diff --git a/controllers/jenkins_controller.go b/controllers/jenkins_controller.go index 875250f2c..00528b725 100644 --- a/controllers/jenkins_controller.go +++ b/controllers/jenkins_controller.go @@ -111,8 +111,20 @@ func (r *JenkinsReconciler) newJenkinsReconcilier(jenkins *v1alpha2.Jenkins) con // +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 +// +kubebuilder:rbac:groups=core,resources=services;configmaps;secrets,verbs=get;list;watch;create;update +// +kubebuilder:rbac:groups=apps,resources=deployments;daemonsets;replicasets;statefulsets,verbs=* +// +kubebuilder:rbac:groups=core,resources=serviceaccounts,verbs=get;list;watch;create;update +// +kubebuilder:rbac:groups=rbac.authorization.k8s.io,resources=roles;rolebindings,verbs=get;list;watch;create;update +// +kubebuilder:rbac:groups=core,resources=pods/portforward,verbs=create +// +kubebuilder:rbac:groups=core,resources=pods/log,verbs=get;list;watch +// +kubebuilder:rbac:groups=core,resources=pods;pods/exec,verbs=* +// +kubebuilder:rbac:groups=core,resources=events,verbs=get;watch;list;create;patch +// +kubebuilder:rbac:groups=apps;jenkins-operator,resources=deployments/finalizers,verbs=update +// +kubebuilder:rbac:groups=jenkins.io,resources=*,verbs=* +// +kubebuilder:rbac:groups=core,resources=persistentvolumeclaims,verbs=get;list;watch +// +kubebuilder:rbac:groups=route.openshift.io,resources=routes,verbs=get;list;watch;create;update +// +kubebuilder:rbac:groups=image.openshift.io,resources=imagestreams,verbs=get;list;watch +// +kubebuilder:rbac:groups=build.openshift.io,resources=builds;buildconfigs,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 diff --git a/main.go b/main.go index 89beb2be5..82e0cc97c 100644 --- a/main.go +++ b/main.go @@ -122,6 +122,7 @@ func main() { HealthProbeBindAddress: probeAddr, LeaderElection: enableLeaderElection, LeaderElectionID: "c674355f.jenkins.io", + Namespace: namespace, }) if err != nil { fatal(errors.Wrap(err, "unable to start manager"), *debug) diff --git a/test/e2e/configuration_test.go b/test/e2e/configuration_test.go index aa759670f..509a2ca01 100644 --- a/test/e2e/configuration_test.go +++ b/test/e2e/configuration_test.go @@ -35,7 +35,7 @@ func createUserConfigurationSecret(namespace string, stringData map[string]strin } _, _ = fmt.Fprintf(GinkgoWriter, "User configuration secret %+v\n", *userConfiguration) - Expect(k8sClient.Create(context.TODO(), userConfiguration)).Should(Succeed()) + Expect(K8sClient.Create(context.TODO(), userConfiguration)).Should(Succeed()) } func createUserConfigurationConfigMap(namespace string, numberOfExecutorsSecretKeyName string, systemMessage string) { @@ -63,7 +63,7 @@ unclassified: } _, _ = fmt.Fprintf(GinkgoWriter, "User configuration %+v\n", *userConfiguration) - Expect(k8sClient.Create(context.TODO(), userConfiguration)).Should(Succeed()) + Expect(K8sClient.Create(context.TODO(), userConfiguration)).Should(Succeed()) } func createDefaultLimitsForContainersInNamespace(namespace string) { @@ -92,7 +92,7 @@ func createDefaultLimitsForContainersInNamespace(namespace string) { } _, _ = fmt.Fprintf(GinkgoWriter, "LimitRange %+v\n", *limitRange) - Expect(k8sClient.Create(context.TODO(), limitRange)).Should(Succeed()) + Expect(K8sClient.Create(context.TODO(), limitRange)).Should(Succeed()) } func verifyJenkinsMasterPodAttributes(jenkins *v1alpha2.Jenkins) { diff --git a/test/e2e/helm_test.go b/test/e2e/helm_test.go deleted file mode 100644 index dd9150449..000000000 --- a/test/e2e/helm_test.go +++ /dev/null @@ -1,71 +0,0 @@ -// +build Helm - -package e2e - -// TODO -/* -import ( - "fmt" - "os/exec" - "testing" - - "github.com/jenkinsci/kubernetes-operator/pkg/apis" - "github.com/jenkinsci/kubernetes-operator/pkg/apis/jenkins/v1alpha2" - - framework "github.com/operator-framework/operator-sdk/pkg/test" - "github.com/stretchr/testify/require" - metav1 "k8s.io/apimachinery/pkg/apis/meta/v1" -) - -func TestLintHelmChart(t *testing.T) { - t.Parallel() - - cmd := exec.Command("helm", "lint", "./chart/jenkins-operator") - output, err := cmd.CombinedOutput() - require.NoError(t, err, string(output)) -} - -func TestDeployHelmChart(t *testing.T) { - // Given - t.Parallel() - ctx := framework.NewContext(t) - defer ctx.Cleanup() - - namespace, err := ctx.GetNamespace() - require.NoError(t, err) - - jenkinsServiceList := &v1alpha2.JenkinsList{ - TypeMeta: metav1.TypeMeta{ - Kind: v1alpha2.Kind, - APIVersion: v1alpha2.SchemeGroupVersion.String(), - }, - } - err = framework.AddToFrameworkScheme(apis.AddToScheme, jenkinsServiceList) - require.NoError(t, err) - - jenkins := &v1alpha2.Jenkins{ - TypeMeta: v1alpha2.JenkinsTypeMeta(), - ObjectMeta: metav1.ObjectMeta{ - Name: "jenkins", - Namespace: namespace, - }, - } - - cmd := exec.Command("helm", "upgrade", "jenkins", "./chart/jenkins-operator", "--namespace", namespace, "--debug", - "--set-string", fmt.Sprintf("jenkins.namespace=%s", namespace), "--install") - output, err := cmd.CombinedOutput() - require.NoError(t, err, string(output)) - - waitForJenkinsBaseConfigurationToComplete(t, jenkins) - waitForJenkinsUserConfigurationToComplete(t, jenkins) - - cmd = exec.Command("helm", "upgrade", "jenkins", "./chart/jenkins-operator", "--namespace", namespace, "--debug", - "--set-string", fmt.Sprintf("jenkins.namespace=%s", namespace), "--install") - output, err = cmd.CombinedOutput() - require.NoError(t, err, string(output)) - - // Then - waitForJenkinsBaseConfigurationToComplete(t, jenkins) - waitForJenkinsUserConfigurationToComplete(t, jenkins) -} -*/ diff --git a/test/e2e/jenkins_configuration_test.go b/test/e2e/jenkins_configuration_test.go index dbaa9aeb4..9222a1175 100644 --- a/test/e2e/jenkins_configuration_test.go +++ b/test/e2e/jenkins_configuration_test.go @@ -73,7 +73,7 @@ var _ = Describe("Jenkins controller configuration", func() { ) BeforeEach(func() { - namespace = createNamespace() + namespace = CreateNamespace() createUserConfigurationSecret(namespace.Name, userConfigurationSecretData) createUserConfigurationConfigMap(namespace.Name, numberOfExecutorsEnvName, fmt.Sprintf("${%s}", systemMessageEnvName)) @@ -83,18 +83,18 @@ var _ = Describe("Jenkins controller configuration", func() { }) AfterEach(func() { - destroyNamespace(namespace) + DestroyNamespace(namespace) }) Context("when deploying CR to cluster", func() { It("creates Jenkins instance and configures it", func() { - waitForJenkinsBaseConfigurationToComplete(jenkins) + WaitForJenkinsBaseConfigurationToComplete(jenkins) verifyJenkinsMasterPodAttributes(jenkins) verifyServices(jenkins) jenkinsClient, cleanUpFunc := verifyJenkinsAPIConnection(jenkins, namespace.Name) defer cleanUpFunc() verifyPlugins(jenkinsClient, jenkins) - waitForJenkinsUserConfigurationToComplete(jenkins) + WaitForJenkinsUserConfigurationToComplete(jenkins) verifyUserConfiguration(jenkinsClient, numberOfExecutors, systemMessage) verifyJenkinsSeedJobs(jenkinsClient, []seedJobConfig{mySeedJob}) }) @@ -124,17 +124,17 @@ var _ = Describe("Jenkins controller priority class", func() { ) BeforeEach(func() { - namespace = createNamespace() + namespace = CreateNamespace() jenkins = createJenkinsCR(jenkinsCRName, namespace.Name, nil, groovyScripts, casc, priorityClassName) }) AfterEach(func() { - destroyNamespace(namespace) + DestroyNamespace(namespace) }) Context("when deploying CR with priority class to cluster", func() { It("creates Jenkins instance and configures it", func() { - waitForJenkinsBaseConfigurationToComplete(jenkins) + WaitForJenkinsBaseConfigurationToComplete(jenkins) verifyJenkinsMasterPodAttributes(jenkins) }) }) @@ -175,17 +175,17 @@ var _ = Describe("Jenkins controller plugins test", func() { ) BeforeEach(func() { - namespace = createNamespace() + namespace = CreateNamespace() jenkins = createJenkinsCR(jenkinsCRName, namespace.Name, &[]v1alpha2.SeedJob{mySeedJob.SeedJob}, groovyScripts, casc, priorityClassName) }) AfterEach(func() { - destroyNamespace(namespace) + DestroyNamespace(namespace) }) Context("when deploying CR with a SeedJob to cluster", func() { It("runs kubernetes plugin job successfully", func() { - waitForJenkinsUserConfigurationToComplete(jenkins) + WaitForJenkinsUserConfigurationToComplete(jenkins) jenkinsClient, cleanUpFunc := verifyJenkinsAPIConnection(jenkins, namespace.Name) defer cleanUpFunc() waitForJobCreation(jenkinsClient, jobID) diff --git a/test/e2e/jenkins_pod_restart_test.go b/test/e2e/jenkins_pod_restart_test.go index 46cda3a29..2e7a0fcda 100644 --- a/test/e2e/jenkins_pod_restart_test.go +++ b/test/e2e/jenkins_pod_restart_test.go @@ -32,23 +32,23 @@ var _ = Describe("Jenkins controller", func() { ) BeforeEach(func() { - namespace = createNamespace() + namespace = CreateNamespace() configureAuthorizationToUnSecure(namespace.Name, userConfigurationConfigMapName) jenkins = createJenkinsCR(jenkinsCRName, namespace.Name, nil, groovyScripts, casc, priorityClassName) }) AfterEach(func() { - destroyNamespace(namespace) + DestroyNamespace(namespace) }) Context("when restarting Jenkins master pod", func() { It("new Jenkins Master pod should be created", func() { - waitForJenkinsBaseConfigurationToComplete(jenkins) + WaitForJenkinsBaseConfigurationToComplete(jenkins) restartJenkinsMasterPod(jenkins) waitForRecreateJenkinsMasterPod(jenkins) checkBaseConfigurationCompleteTimeIsNotSet(jenkins) - waitForJenkinsBaseConfigurationToComplete(jenkins) + WaitForJenkinsBaseConfigurationToComplete(jenkins) }) }) }) @@ -80,20 +80,20 @@ var _ = Describe("Jenkins controller", func() { ) BeforeEach(func() { - namespace = createNamespace() + namespace = CreateNamespace() configureAuthorizationToUnSecure(namespace.Name, userConfigurationConfigMapName) jenkins = createJenkinsCRSafeRestart(jenkinsCRName, namespace.Name, nil, groovyScripts, casc, priorityClassName) }) AfterEach(func() { - destroyNamespace(namespace) + DestroyNamespace(namespace) }) Context("when running Jenkins safe restart", func() { It("authorization strategy is not overwritten", func() { - waitForJenkinsBaseConfigurationToComplete(jenkins) - waitForJenkinsUserConfigurationToComplete(jenkins) + WaitForJenkinsBaseConfigurationToComplete(jenkins) + WaitForJenkinsUserConfigurationToComplete(jenkins) jenkinsClient, cleanUpFunc := verifyJenkinsAPIConnection(jenkins, namespace.Name) defer cleanUpFunc() checkIfAuthorizationStrategyUnsecuredIsSet(jenkinsClient) diff --git a/test/e2e/jenkins_restore_backup_test.go b/test/e2e/jenkins_restore_backup_test.go index eeb474d84..8c1f0b841 100644 --- a/test/e2e/jenkins_restore_backup_test.go +++ b/test/e2e/jenkins_restore_backup_test.go @@ -21,19 +21,19 @@ var _ = Describe("Jenkins controller backup and restore", func() { ) BeforeEach(func() { - namespace = createNamespace() + namespace = CreateNamespace() createPVC(namespace.Name) jenkins = createJenkinsWithBackupAndRestoreConfigured(jenkinsCRName, namespace.Name) }) AfterEach(func() { - destroyNamespace(namespace) + 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) + WaitForJenkinsUserConfigurationToComplete(jenkins) jenkinsClient, cleanUpFunc := verifyJenkinsAPIConnection(jenkins, namespace.Name) defer cleanUpFunc() waitForJobCreation(jenkinsClient, jobID) @@ -42,7 +42,7 @@ var _ = Describe("Jenkins controller backup and restore", func() { jenkins = getJenkins(jenkins.Namespace, jenkins.Name) restartJenkinsMasterPod(jenkins) waitForRecreateJenkinsMasterPod(jenkins) - waitForJenkinsUserConfigurationToComplete(jenkins) + WaitForJenkinsUserConfigurationToComplete(jenkins) jenkinsClient2, cleanUpFunc2 := verifyJenkinsAPIConnection(jenkins, namespace.Name) defer cleanUpFunc2() waitForJobCreation(jenkinsClient2, jobID) @@ -51,7 +51,7 @@ var _ = Describe("Jenkins controller backup and restore", func() { resetJenkinsStatus(jenkins) jenkins = getJenkins(jenkins.Namespace, jenkins.Name) checkBaseConfigurationCompleteTimeIsNotSet(jenkins) - waitForJenkinsUserConfigurationToComplete(jenkins) + WaitForJenkinsUserConfigurationToComplete(jenkins) jenkinsClient3, cleanUpFunc3 := verifyJenkinsAPIConnection(jenkins, namespace.Name) defer cleanUpFunc3() waitForJobCreation(jenkinsClient3, jobID) diff --git a/test/e2e/jenkins_test.go b/test/e2e/jenkins_test.go index 725925b77..bfeb59501 100644 --- a/test/e2e/jenkins_test.go +++ b/test/e2e/jenkins_test.go @@ -31,7 +31,7 @@ const ( 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()) + Expect(K8sClient.Get(context.TODO(), namespaceName, jenkins)).Should(Succeed()) return jenkins } @@ -41,7 +41,7 @@ func getJenkinsMasterPod(jenkins *v1alpha2.Jenkins) *corev1.Pod { Namespace: jenkins.Namespace, } pods := &corev1.PodList{} - Expect(k8sClient.List(context.TODO(), pods, lo)).Should(Succeed()) + 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] } @@ -147,7 +147,7 @@ func createJenkinsCR(name, namespace string, seedJob *[]v1alpha2.SeedJob, groovy _, _ = fmt.Fprintf(GinkgoWriter, "Jenkins CR %+v\n", *jenkins) - Expect(k8sClient.Create(context.TODO(), jenkins)).Should(Succeed()) + Expect(K8sClient.Create(context.TODO(), jenkins)).Should(Succeed()) return jenkins } @@ -253,7 +253,7 @@ func createJenkinsCRSafeRestart(name, namespace string, seedJob *[]v1alpha2.Seed _, _ = fmt.Fprintf(GinkgoWriter, "Jenkins CR %+v\n", *jenkins) - Expect(k8sClient.Create(context.TODO(), jenkins)).Should(Succeed()) + Expect(K8sClient.Create(context.TODO(), jenkins)).Should(Succeed()) return jenkins } @@ -261,11 +261,11 @@ func createJenkinsCRSafeRestart(name, namespace string, seedJob *[]v1alpha2.Seed func createJenkinsAPIClientFromServiceAccount(jenkins *v1alpha2.Jenkins, jenkinsAPIURL string) (jenkinsclient.Jenkins, error) { podName := resources.GetJenkinsMasterPodName(jenkins) - clientSet, err := kubernetes.NewForConfig(cfg) + clientSet, err := kubernetes.NewForConfig(Cfg) if err != nil { return nil, err } - config := configuration.Configuration{Jenkins: jenkins, ClientSet: *clientSet, Config: cfg} + 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"}) @@ -281,7 +281,7 @@ func createJenkinsAPIClientFromSecret(jenkins *v1alpha2.Jenkins, jenkinsAPIURL s adminSecret := &corev1.Secret{} namespaceName := types.NamespacedName{Namespace: jenkins.Namespace, Name: resources.GetOperatorCredentialsSecretName(jenkins)} - if err := k8sClient.Get(context.TODO(), namespaceName, adminSecret); err != nil { + if err := K8sClient.Get(context.TODO(), namespaceName, adminSecret); err != nil { return nil, err } @@ -296,7 +296,7 @@ func verifyJenkinsAPIConnection(jenkins *v1alpha2.Jenkins, namespace string) (je By("establishing Jenkins API connection") var service corev1.Service - err := k8sClient.Get(context.TODO(), types.NamespacedName{ + err := K8sClient.Get(context.TODO(), types.NamespacedName{ Namespace: jenkins.Namespace, Name: resources.GetJenkinsHTTPServiceName(jenkins), }, &service) @@ -333,7 +333,7 @@ func verifyJenkinsAPIConnection(jenkins *v1alpha2.Jenkins, namespace string) (je func restartJenkinsMasterPod(jenkins *v1alpha2.Jenkins) { _, _ = fmt.Fprintf(GinkgoWriter, "Restarting Jenkins master pod\n") jenkinsPod := getJenkinsMasterPod(jenkins) - Expect(k8sClient.Delete(context.TODO(), jenkinsPod)).Should(Succeed()) + Expect(K8sClient.Delete(context.TODO(), jenkinsPod)).Should(Succeed()) Eventually(func() (bool, error) { jenkinsPod = getJenkinsMasterPod(jenkins) @@ -346,7 +346,7 @@ func restartJenkinsMasterPod(jenkins *v1alpha2.Jenkins) { 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()) + Expect(K8sClient.Get(context.TODO(), client.ObjectKey{Name: serviceName, Namespace: jenkins.Namespace}, service)).Should(Succeed()) return service } diff --git a/test/e2e/port_forward_test.go b/test/e2e/port_forward_test.go index 12f318afe..a719399e7 100644 --- a/test/e2e/port_forward_test.go +++ b/test/e2e/port_forward_test.go @@ -66,7 +66,7 @@ func setupPortForwardToPod(namespace, podName string, podPort int) (port int, cl readyCh := make(chan struct{}) req := portForwardToPodRequest{ - config: cfg, + config: Cfg, pod: v1.Pod{ ObjectMeta: metav1.ObjectMeta{ Name: podName, diff --git a/test/e2e/restart_test.go b/test/e2e/restart_test.go index f5eb145b2..d666d9174 100644 --- a/test/e2e/restart_test.go +++ b/test/e2e/restart_test.go @@ -33,7 +33,7 @@ jenkins.save() }, } - Expect(k8sClient.Create(context.TODO(), limitRange)).Should(Succeed()) + Expect(K8sClient.Create(context.TODO(), limitRange)).Should(Succeed()) } func checkIfAuthorizationStrategyUnsecuredIsSet(jenkinsClient jenkinsclient.Jenkins) { @@ -56,7 +56,7 @@ func checkBaseConfigurationCompleteTimeIsNotSet(jenkins *v1alpha2.Jenkins) { Eventually(func() (bool, error) { actualJenkins := &v1alpha2.Jenkins{} - err := k8sClient.Get(context.TODO(), types.NamespacedName{Name: jenkins.Name, Namespace: jenkins.Namespace}, actualJenkins) + err := K8sClient.Get(context.TODO(), types.NamespacedName{Name: jenkins.Name, Namespace: jenkins.Namespace}, actualJenkins) if err != nil { return false, err } diff --git a/test/e2e/restorebackup_test.go b/test/e2e/restorebackup_test.go index 46d4b5248..fc0be5d24 100644 --- a/test/e2e/restorebackup_test.go +++ b/test/e2e/restorebackup_test.go @@ -61,7 +61,7 @@ func createPVC(namespace string) { }, } - Expect(k8sClient.Create(context.TODO(), pvc)).Should(Succeed()) + Expect(K8sClient.Create(context.TODO(), pvc)).Should(Succeed()) } func createJenkinsWithBackupAndRestoreConfigured(name, namespace string) *v1alpha2.Jenkins { @@ -180,7 +180,7 @@ func createJenkinsWithBackupAndRestoreConfigured(name, namespace string) *v1alph _, _ = fmt.Fprintf(GinkgoWriter, "Jenkins CR %+v\n", *jenkins) - Expect(k8sClient.Create(context.TODO(), jenkins)).Should(Succeed()) + Expect(K8sClient.Create(context.TODO(), jenkins)).Should(Succeed()) return jenkins } @@ -190,5 +190,5 @@ func resetJenkinsStatus(jenkins *v1alpha2.Jenkins) { jenkins = getJenkins(jenkins.Namespace, jenkins.Name) jenkins.Status = v1alpha2.JenkinsStatus{} - Expect(k8sClient.Status().Update(context.TODO(), jenkins)).Should(Succeed()) + Expect(K8sClient.Status().Update(context.TODO(), jenkins)).Should(Succeed()) } diff --git a/test/e2e/seedjobs_test.go b/test/e2e/seedjobs_test.go index 5c1766806..b7b2b3512 100644 --- a/test/e2e/seedjobs_test.go +++ b/test/e2e/seedjobs_test.go @@ -104,7 +104,7 @@ func createKubernetesCredentialsProviderSecret(namespace string, config seedJobC }, } - Expect(k8sClient.Create(context.TODO(), secret)).Should(Succeed()) + Expect(K8sClient.Create(context.TODO(), secret)).Should(Succeed()) } func verifyJenkinsSeedJobs(jenkinsClient jenkinsclient.Jenkins, seedJobs []seedJobConfig) { diff --git a/test/e2e/suite_test.go b/test/e2e/suite_test.go index eca535e42..99155d395 100644 --- a/test/e2e/suite_test.go +++ b/test/e2e/suite_test.go @@ -1,12 +1,9 @@ package e2e import ( - "context" "flag" - "fmt" "path/filepath" "testing" - "time" "github.com/jenkinsci/kubernetes-operator/api/v1alpha2" "github.com/jenkinsci/kubernetes-operator/controllers" @@ -18,13 +15,9 @@ import ( . "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" @@ -32,16 +25,6 @@ import ( // +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.") @@ -68,9 +51,9 @@ var _ = BeforeSuite(func(done Done) { } var err error - cfg, err = testEnv.Start() + Cfg, err = testEnv.Start() Expect(err).NotTo(HaveOccurred()) - Expect(cfg).NotTo(BeNil()) + Expect(Cfg).NotTo(BeNil()) err = v1alpha2.AddToScheme(scheme.Scheme) Expect(err).NotTo(HaveOccurred()) @@ -78,20 +61,20 @@ var _ = BeforeSuite(func(done Done) { // +kubebuilder:scaffold:scheme // setup manager - k8sManager, err := ctrl.NewManager(cfg, ctrl.Options{ + k8sManager, err := ctrl.NewManager(Cfg, ctrl.Options{ Scheme: scheme.Scheme, }) Expect(err).NotTo(HaveOccurred()) // setup controller - clientSet, err := kubernetes.NewForConfig(cfg) + clientSet, err := kubernetes.NewForConfig(Cfg) Expect(err).NotTo(HaveOccurred()) // setup events - events, err := event.New(cfg, constants.OperatorName) + events, err := event.New(Cfg, constants.OperatorName) Expect(err).NotTo(HaveOccurred()) notificationEvents := make(chan e.Event) - go notifications.Listen(notificationEvents, events, k8sClient) + go notifications.Listen(notificationEvents, events, K8sClient) jenkinsAPIConnectionSettings := jenkinsClient.JenkinsAPIConnectionSettings{ Hostname: *hostname, @@ -104,7 +87,7 @@ var _ = BeforeSuite(func(done Done) { Scheme: k8sManager.GetScheme(), JenkinsAPIConnectionSettings: jenkinsAPIConnectionSettings, ClientSet: *clientSet, - Config: *cfg, + Config: *Cfg, NotificationEvents: ¬ificationEvents, KubernetesClusterDomain: "cluster.local", }).SetupWithManager(k8sManager) @@ -115,8 +98,8 @@ var _ = BeforeSuite(func(done Done) { Expect(err).NotTo(HaveOccurred()) }() - k8sClient = k8sManager.GetClient() - Expect(k8sClient).NotTo(BeNil()) + K8sClient = k8sManager.GetClient() + Expect(K8sClient).NotTo(BeNil()) close(done) }, 60) @@ -125,39 +108,3 @@ var _ = AfterSuite(func() { 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/test_utility.go b/test/e2e/test_utility.go new file mode 100644 index 000000000..4006b7911 --- /dev/null +++ b/test/e2e/test_utility.go @@ -0,0 +1,61 @@ +package e2e + +import ( + "context" + "fmt" + "time" + + "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/rest" + "sigs.k8s.io/controller-runtime/pkg/client" + "sigs.k8s.io/controller-runtime/pkg/envtest" +) + +var ( + Cfg *rest.Config + K8sClient client.Client + testEnv *envtest.Environment + + hostname *string + port *int + useNodePort *bool +) + +func CreateNamespace() *corev1.Namespace { + ginkgo.By("creating temporary namespace") + + namespace := &corev1.Namespace{ + ObjectMeta: metav1.ObjectMeta{ + Name: fmt.Sprintf("ns%d", time.Now().Unix()), + }, + } + gomega.Expect(K8sClient.Create(context.TODO(), namespace)).Should(gomega.Succeed()) + return namespace +} + +func DestroyNamespace(namespace *corev1.Namespace) { + ginkgo.By("deleting temporary namespace") + + gomega.Expect(K8sClient.Delete(context.TODO(), namespace)).Should(gomega.Succeed()) + + gomega.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(gomega.BeTrue()) +} diff --git a/test/e2e/wait_test.go b/test/e2e/wait.go similarity index 53% rename from test/e2e/wait_test.go rename to test/e2e/wait.go index 9d511c791..d5495ff37 100644 --- a/test/e2e/wait_test.go +++ b/test/e2e/wait.go @@ -10,8 +10,8 @@ import ( 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" + "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" @@ -23,36 +23,36 @@ var ( retryInterval = time.Second * 5 ) -func waitForJenkinsBaseConfigurationToComplete(jenkins *v1alpha2.Jenkins) { - By("waiting for Jenkins base configuration phase to complete") +func WaitForJenkinsBaseConfigurationToComplete(jenkins *v1alpha2.Jenkins) { + ginkgo.By("waiting for Jenkins base configuration phase to complete") - Eventually(func() (*metav1.Time, error) { + gomega.Eventually(func() (*metav1.Time, error) { actualJenkins := &v1alpha2.Jenkins{} - err := k8sClient.Get(context.TODO(), types.NamespacedName{Name: jenkins.Name, Namespace: jenkins.Namespace}, actualJenkins) + 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())) + }, time.Duration(170)*retryInterval, retryInterval).Should(gomega.Not(gomega.BeNil())) - _, _ = fmt.Fprintf(GinkgoWriter, "Jenkins pod is running\n") + _, _ = fmt.Fprintf(ginkgo.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()) + gomega.Expect(K8sClient.Get(context.TODO(), namespacedName, jenkins)).Should(gomega.Succeed()) } func waitForRecreateJenkinsMasterPod(jenkins *v1alpha2.Jenkins) { - By("waiting for Jenkins Master Pod recreation") + ginkgo.By("waiting for Jenkins Master Pod recreation") - Eventually(func() (bool, error) { + gomega.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) + err := K8sClient.List(context.TODO(), pods, lo) if err != nil { return false, err } @@ -61,31 +61,31 @@ func waitForRecreateJenkinsMasterPod(jenkins *v1alpha2.Jenkins) { } return pods.Items[0].DeletionTimestamp == nil, nil - }, 30*retryInterval, retryInterval).Should(BeTrue()) + }, 30*retryInterval, retryInterval).Should(gomega.BeTrue()) - _, _ = fmt.Fprintf(GinkgoWriter, "Jenkins pod has been recreated\n") + _, _ = fmt.Fprintf(ginkgo.GinkgoWriter, "Jenkins pod has been recreated\n") } -func waitForJenkinsUserConfigurationToComplete(jenkins *v1alpha2.Jenkins) { - By("waiting for Jenkins user configuration phase to complete") +func WaitForJenkinsUserConfigurationToComplete(jenkins *v1alpha2.Jenkins) { + ginkgo.By("waiting for Jenkins user configuration phase to complete") - Eventually(func() (*metav1.Time, error) { + gomega.Eventually(func() (*metav1.Time, error) { actualJenkins := &v1alpha2.Jenkins{} - err := k8sClient.Get(context.TODO(), types.NamespacedName{Name: jenkins.Name, Namespace: jenkins.Namespace}, actualJenkins) + 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") + }, time.Duration(110)*retryInterval, retryInterval).Should(gomega.Not(gomega.BeNil())) + _, _ = fmt.Fprintf(ginkgo.GinkgoWriter, "Jenkins instance is up and ready\n") } func waitForJenkinsSafeRestart(jenkinsClient jenkinsclient.Jenkins) { - By("waiting for Jenkins safe restart") + ginkgo.By("waiting for Jenkins safe restart") - Eventually(func() (bool, error) { + gomega.Eventually(func() (bool, error) { status, err := jenkinsClient.Poll() - _, _ = fmt.Fprintf(GinkgoWriter, "Safe restart status: %+v, err: %s\n", status, err) + _, _ = fmt.Fprintf(ginkgo.GinkgoWriter, "Safe restart status: %+v, err: %s\n", status, err) if err != nil { return false, err } @@ -93,5 +93,5 @@ func waitForJenkinsSafeRestart(jenkinsClient jenkinsclient.Jenkins) { return false, err } return true, nil - }, time.Second*200, time.Second*5).Should(BeTrue()) + }, time.Second*200, time.Second*5).Should(gomega.BeTrue()) } diff --git a/test/helm/helm_test.go b/test/helm/helm_test.go new file mode 100644 index 000000000..d2fb06668 --- /dev/null +++ b/test/helm/helm_test.go @@ -0,0 +1,60 @@ +package helm + +import ( + "fmt" + "os/exec" + + "github.com/jenkinsci/kubernetes-operator/api/v1alpha2" + "github.com/jenkinsci/kubernetes-operator/test/e2e" + + . "github.com/onsi/ginkgo" + . "github.com/onsi/gomega" + corev1 "k8s.io/api/core/v1" + metav1 "k8s.io/apimachinery/pkg/apis/meta/v1" + // +kubebuilder:scaffold:imports +) + +var _ = Describe("Jenkins controller", func() { + var ( + namespace *corev1.Namespace + ) + + BeforeEach(func() { + namespace = e2e.CreateNamespace() + }) + AfterEach(func() { + showLogsIfTestHasFailed(CurrentGinkgoTestDescription().Failed, namespace.Name) + e2e.DestroyNamespace(namespace) + }) + Context("when deploying Helm Chart to cluster", func() { + It("creates Jenkins instance and configures it", func() { + + jenkins := &v1alpha2.Jenkins{ + TypeMeta: v1alpha2.JenkinsTypeMeta(), + ObjectMeta: metav1.ObjectMeta{ + Name: "jenkins", + Namespace: namespace.Name, + }, + } + + cmd := exec.Command("../../bin/helm", "upgrade", "jenkins", "../../chart/jenkins-operator", "--namespace", namespace.Name, "--debug", + "--set-string", fmt.Sprintf("jenkins.namespace=%s", namespace.Name), + "--set-string", fmt.Sprintf("operator.image=%s", *imageName), "--install") + output, err := cmd.CombinedOutput() + Expect(err).NotTo(HaveOccurred(), string(output)) + + e2e.WaitForJenkinsBaseConfigurationToComplete(jenkins) + e2e.WaitForJenkinsUserConfigurationToComplete(jenkins) + + cmd = exec.Command("../../bin/helm", "upgrade", "jenkins", "../../chart/jenkins-operator", "--namespace", namespace.Name, "--debug", + "--set-string", fmt.Sprintf("jenkins.namespace=%s", namespace.Name), + "--set-string", fmt.Sprintf("operator.image=%s", *imageName), "--install") + output, err = cmd.CombinedOutput() + + Expect(err).NotTo(HaveOccurred(), string(output)) + + e2e.WaitForJenkinsBaseConfigurationToComplete(jenkins) + e2e.WaitForJenkinsUserConfigurationToComplete(jenkins) + }) + }) +}) diff --git a/test/helm/logging_test.go b/test/helm/logging_test.go new file mode 100644 index 000000000..88474e8cd --- /dev/null +++ b/test/helm/logging_test.go @@ -0,0 +1,135 @@ +package helm + +import ( + "bytes" + "context" + "fmt" + "io" + "sort" + + "github.com/jenkinsci/kubernetes-operator/test/e2e" + "github.com/onsi/ginkgo" + + corev1 "k8s.io/api/core/v1" + "k8s.io/api/events/v1beta1" + "k8s.io/apimachinery/pkg/labels" + "k8s.io/client-go/kubernetes" + "sigs.k8s.io/controller-runtime/pkg/client" +) + +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) (*corev1.Pod, error) { + lo := &client.ListOptions{ + LabelSelector: labels.SelectorFromSet(operatorPodLabels), + Namespace: namespace, + } + pods := &corev1.PodList{} + err := e2e.K8sClient.List(context.TODO(), pods, lo) + if err != nil { + return nil, err + } + return &pods.Items[0], nil +} + +func getOperatorLogs(namespace string) (string, error) { + pod, err := getOperatorPod(namespace) + if err != nil { + return "Operator pod doesn't exist", err + } + logOptions := corev1.PodLogOptions{TailLines: &podLogTailLimit} + + // creates the clientset + clientset, err := kubernetes.NewForConfig(e2e.Cfg) + if err != nil { + return "", err + } + req := clientset.CoreV1().Pods(pod.Namespace).GetLogs(pod.Name, &logOptions) + podLogs, err := req.Stream(context.TODO()) + 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(namespace string) { + _, _ = fmt.Fprintf(ginkgo.GinkgoWriter, "Operator logs in '%s' namespace:\n", namespace) + logs, err := getOperatorLogs(namespace) + if err != nil { + _, _ = fmt.Fprintf(ginkgo.GinkgoWriter, "Couldn't get the operator pod logs: %s", err) + } else { + _, _ = fmt.Fprintf(ginkgo.GinkgoWriter, "Last %d lines of log from operator:\n %s", podLogTailLimit, logs) + } +} + +func getKubernetesEvents(namespace string) ([]v1beta1.Event, error) { + listOptions := &client.ListOptions{ + Limit: kubernetesEventsLimit, + Namespace: namespace, + } + + events := &v1beta1.EventList{} + err := e2e.K8sClient.List(context.TODO(), events, 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(namespace string) { + _, _ = fmt.Fprintf(ginkgo.GinkgoWriter, "Kubernetes events in '%s' namespace:\n", namespace) + events, err := getKubernetesEvents(namespace) + if err != nil { + _, _ = fmt.Fprintf(ginkgo.GinkgoWriter, "Couldn't get kubernetes events: %s", err) + } else { + _, _ = fmt.Fprintf(ginkgo.GinkgoWriter, "Last %d events from kubernetes:\n", kubernetesEventsLimit) + + for _, event := range events { + _, _ = fmt.Fprintf(ginkgo.GinkgoWriter, "%+v\n\n", event) + } + } +} + +func printKubernetesPods(namespace string) { + _, _ = fmt.Fprintf(ginkgo.GinkgoWriter, "All pods in '%s' namespace:\n", namespace) + + pod, err := getOperatorPod(namespace) + if err == nil { + _, _ = fmt.Fprintf(ginkgo.GinkgoWriter, "%+v\n\n", pod) + } +} + +func showLogsIfTestHasFailed(failed bool, namespace string) { + if failed { + _, _ = fmt.Fprintf(ginkgo.GinkgoWriter, "Test failed. Bellow here you can check logs:") + + printKubernetesEvents(namespace) + printKubernetesPods(namespace) + printOperatorLogs(namespace) + } +} diff --git a/test/helm/suite_test.go b/test/helm/suite_test.go new file mode 100644 index 000000000..59dff0256 --- /dev/null +++ b/test/helm/suite_test.go @@ -0,0 +1,76 @@ +package helm + +import ( + "flag" + "testing" + + "github.com/jenkinsci/kubernetes-operator/api/v1alpha2" + "github.com/jenkinsci/kubernetes-operator/test/e2e" + + . "github.com/onsi/ginkgo" + . "github.com/onsi/gomega" + "k8s.io/client-go/kubernetes/scheme" + ctrl "sigs.k8s.io/controller-runtime" + "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 ( + testEnv *envtest.Environment + imageName *string +) + +func init() { + imageName = flag.String("image-name", "", "Name of the locally built for testing Jenkins Operator Image.") +} + +func TestHelm(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{ + UseExistingCluster: &useExistingCluster, + } + + 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()) + + go func() { + err = k8sManager.Start(ctrl.SetupSignalHandler()) + Expect(err).NotTo(HaveOccurred()) + }() + + e2e.K8sClient = k8sManager.GetClient() + Expect(e2e.K8sClient).NotTo(BeNil()) + close(done) +}, 60) + +var _ = AfterSuite(func() { + By("tearing down the test environment") + err := testEnv.Stop() + Expect(err).NotTo(HaveOccurred()) +}) diff --git a/variables.mk b/variables.mk index 0c310b537..94968b3c5 100644 --- a/variables.mk +++ b/variables.mk @@ -59,7 +59,7 @@ GO_LDFLAGS_STATIC=-ldflags "-w $(CTIMEVAR) -extldflags -static" 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) +PACKAGES_FOR_UNIT_TESTS = $(shell go list -f '{{.ImportPath}}/' ./... | grep -v vendor | grep -v e2e | grep -v helm) # Run all the e2e tests by default E2E_TEST_SELECTOR ?= .*