update to go1.24

This commit is contained in:
brokenpip3 2025-12-21 14:35:41 +01:00
parent cf1fd1e02c
commit b9e36ff6e5
No known key found for this signature in database
GPG Key ID: 1D9BDC803797B4B6
38 changed files with 669 additions and 596 deletions

View File

@ -1,41 +1,47 @@
version: "2"
run:
deadline: 5m
allow-parallel-runners: true
skip-files:
- api/v1alpha2/zz_generated.deepcopy.go
issues:
exclude-use-default: false
exclude-rules:
- path: "internal/*"
linters:
- dupl
- path: (.+)_test.go
linters:
- dupl
output:
sort-order:
- file
- severity
- linter
linters:
disable-all: true
default: none
enable:
- dupl
- errcheck
- exportloopref
- goconst
- gocyclo
- gofmt
- goimports
- gosimple
- govet
- ineffassign
- loggercheck
- misspell
- nakedret
- staticcheck
- typecheck
- unconvert
- unparam
- unused
output:
sort-results: true
sort-order:
- file
- severity
- linter
exclusions:
generated: lax
rules:
- linters:
- dupl
path: internal/*
- linters:
- dupl
path: (.+)_test.go
paths:
- third_party$
- builtin$
- examples$
formatters:
enable:
- gofmt
- goimports
exclusions:
generated: lax
paths:
- third_party$
- builtin$
- examples$

View File

@ -70,7 +70,7 @@ HAS_GOLINT := $(shell which $(PROJECT_DIR)/bin/golangci-lint)
lint: ## Verifies `golint` passes
@echo "+ $@"
ifndef HAS_GOLINT
GOBIN=$(PROJECT_DIR)/bin go install github.com/golangci/golangci-lint/cmd/golangci-lint@v1.55.0
GOBIN=$(PROJECT_DIR)/bin go install github.com/golangci/golangci-lint/v2/cmd/golangci-lint@v2.5.0
endif
@bin/golangci-lint run
@ -162,7 +162,7 @@ staticcheck: ## Verifies `staticcheck` passes
@echo "+ $@"
ifndef HAS_STATICCHECK
$(eval TMP_DIR := $(shell mktemp -d))
wget -O $(TMP_DIR)/staticcheck_$(PLATFORM)_amd64.tar.gz https://github.com/dominikh/go-tools/releases/download/2023.1.7/staticcheck_$(PLATFORM)_amd64.tar.gz
wget -O $(TMP_DIR)/staticcheck_$(PLATFORM)_amd64.tar.gz https://github.com/dominikh/go-tools/releases/download/2025.1.1/staticcheck_$(PLATFORM)_amd64.tar.gz
tar zxvf $(TMP_DIR)/staticcheck_$(PLATFORM)_amd64.tar.gz -C $(TMP_DIR)
mkdir -p $(PROJECT_DIR)/bin
mv $(TMP_DIR)/staticcheck/staticcheck $(PROJECT_DIR)/bin
@ -174,7 +174,7 @@ endif
cover: ## Runs go test with coverage
@echo "" > coverage.txt
@for d in $(PACKAGES); do \
ENVTEST_K8S_VERSION = 1.26
ENVTEST_K8S_VERSION = 1.33
KUBEBUILDER_ASSETS="$(shell $(ENVTEST) use $(ENVTEST_K8S_VERSION) --bin-dir $(LOCALBIN) -p path)" IMG_RUNNING_TESTS=1 go test -race -coverprofile=profile.out -covermode=atomic "$$d"; \
if [ -f profile.out ]; then \
cat profile.out >> coverage.txt; \
@ -578,8 +578,8 @@ CONTROLLER_GEN ?= $(LOCALBIN)/controller-gen
ENVTEST ?= $(LOCALBIN)/setup-envtest
## Tool Versions
KUSTOMIZE_VERSION ?= v5.3.0
CONTROLLER_TOOLS_VERSION ?= v0.14.0
KUSTOMIZE_VERSION ?= v5.4.2
CONTROLLER_TOOLS_VERSION ?= v0.18.0
KUSTOMIZE_INSTALL_SCRIPT ?= "https://raw.githubusercontent.com/kubernetes-sigs/kustomize/master/hack/install_kustomize.sh"
.PHONY: kustomize

View File

@ -18,6 +18,7 @@ package v1alpha2
import (
"compress/gzip"
"context"
"encoding/json"
"errors"
"io"
@ -37,10 +38,10 @@ import (
)
var (
jenkinslog = logf.Log.WithName("jenkins-resource") // log is for logging in this package.
SecValidator = *NewSecurityValidator()
_ webhook.Validator = &Jenkins{}
initialSecurityWarningsDownloadSucceded = false
jenkinslog = logf.Log.WithName("jenkins-resource") // log is for logging in this package.
SecValidator = *NewSecurityValidator()
_ webhook.CustomValidator = &Jenkins{}
initialSecurityWarningsDownloadSucceded = false
)
const (
@ -60,28 +61,36 @@ func (in *Jenkins) SetupWebhookWithManager(mgr ctrl.Manager) error {
// TODO(user): change verbs to "verbs=create;update;delete" if you want to enable deletion validation.
// +kubebuilder:webhook:path=/validate-jenkins-io-jenkins-io-v1alpha2-jenkins,mutating=false,failurePolicy=fail,sideEffects=None,groups=jenkins.io.jenkins.io,resources=jenkins,verbs=create;update,versions=v1alpha2,name=vjenkins.kb.io,admissionReviewVersions={v1}
// ValidateCreate implements webhook.Validator so a webhook will be registered for the type
func (in *Jenkins) ValidateCreate() (admission.Warnings, error) {
if in.Spec.ValidateSecurityWarnings {
jenkinslog.Info("validate create", "name", in.Name)
err := Validate(*in)
// ValidateCreate implements webhook.CustomValidator so a webhook will be registered for the type
func (in *Jenkins) ValidateCreate(ctx context.Context, obj runtime.Object) (admission.Warnings, error) {
jenkins, ok := obj.(*Jenkins)
if !ok {
return nil, errors.New("expected a Jenkins object")
}
if jenkins.Spec.ValidateSecurityWarnings {
jenkinslog.Info("validate create", "name", jenkins.Name)
err := Validate(*jenkins)
return nil, err
}
return nil, nil
}
// ValidateUpdate implements webhook.Validator so a webhook will be registered for the type
func (in *Jenkins) ValidateUpdate(old runtime.Object) (admission.Warnings, error) {
if in.Spec.ValidateSecurityWarnings {
jenkinslog.Info("validate update", "name", in.Name)
return nil, Validate(*in)
// ValidateUpdate implements webhook.CustomValidator so a webhook will be registered for the type
func (in *Jenkins) ValidateUpdate(ctx context.Context, oldObj, newObj runtime.Object) (admission.Warnings, error) {
jenkins, ok := newObj.(*Jenkins)
if !ok {
return nil, errors.New("expected a Jenkins object")
}
if jenkins.Spec.ValidateSecurityWarnings {
jenkinslog.Info("validate update", "name", jenkins.Name)
return nil, Validate(*jenkins)
}
return nil, nil
}
func (in *Jenkins) ValidateDelete() (admission.Warnings, error) {
func (in *Jenkins) ValidateDelete(ctx context.Context, obj runtime.Object) (admission.Warnings, error) {
return nil, nil
}

View File

@ -1,6 +1,7 @@
package v1alpha2
import (
"context"
"errors"
"testing"
@ -79,7 +80,7 @@ func TestValidate(t *testing.T) {
t.Run("Validating when plugins data file is not fetched", func(t *testing.T) {
userplugins := []Plugin{{Name: "script-security", Version: "1.77"}, {Name: "git-client", Version: "3.9"}, {Name: "git", Version: "4.8.1"}, {Name: "plain-credentials", Version: "1.7"}}
jenkinscr := *createJenkinsCR(userplugins, true)
_, got := jenkinscr.ValidateCreate()
_, got := jenkinscr.ValidateCreate(context.TODO(), &jenkinscr)
assert.Equal(t, got, errors.New("plugins data has not been fetched"))
})
@ -95,7 +96,7 @@ func TestValidate(t *testing.T) {
{Name: "plain-credentials"}}}
userplugins := []Plugin{{Name: "script-security", Version: "1.77"}, {Name: "git-client", Version: "3.9"}, {Name: "git", Version: "4.8.1"}, {Name: "plain-credentials", Version: "1.7"}}
jenkinscr := *createJenkinsCR(userplugins, true)
_, got := jenkinscr.ValidateCreate()
_, got := jenkinscr.ValidateCreate(context.TODO(), &jenkinscr)
assert.Nil(t, got)
})
@ -113,7 +114,7 @@ func TestValidate(t *testing.T) {
}}
userplugins := []Plugin{{Name: "google-login", Version: "1.2"}, {Name: "mailer", Version: "1.1"}, {Name: "git", Version: "4.8.1"}, {Name: "command-launcher", Version: "1.6"}, {Name: "workflow-cps", Version: "2.59"}}
jenkinscr := *createJenkinsCR(userplugins, true)
_, got := jenkinscr.ValidateCreate()
_, got := jenkinscr.ValidateCreate(context.TODO(), &jenkinscr)
assert.Equal(t, got, errors.New("security vulnerabilities detected in the following user-defined plugins: \nworkflow-cps:2.59\ngoogle-login:1.2\nmailer:1.1"))
})
@ -136,19 +137,19 @@ func TestValidate(t *testing.T) {
userplugins = []Plugin{{Name: "handy-uri-templates-2-api", Version: "2.1.8-1.0"}, {Name: "resource-disposer", Version: "0.8"}, {Name: "jjwt-api", Version: "0.11.2-9.c8b45b8bb173"}, {Name: "blueocean-github-pipeline", Version: "1.2.0-beta-3"}, {Name: "ghprb", Version: "1.39"}}
newjenkinscr := *createJenkinsCR(userplugins, true)
_, got := newjenkinscr.ValidateUpdate(&oldjenkinscr)
_, got := newjenkinscr.ValidateUpdate(context.TODO(), &oldjenkinscr, &newjenkinscr)
assert.Equal(t, got, errors.New("security vulnerabilities detected in the following user-defined plugins: \nhandy-uri-templates-2-api:2.1.8-1.0\nresource-disposer:0.8\nblueocean-github-pipeline:1.2.0-beta-3\nghprb:1.39"))
})
t.Run("Validation is turned off", func(t *testing.T) {
userplugins := []Plugin{{Name: "google-login", Version: "1.2"}, {Name: "mailer", Version: "1.1"}, {Name: "git", Version: "4.8.1"}, {Name: "command-launcher", Version: "1.6"}, {Name: "workflow-cps", Version: "2.59"}}
jenkinscr := *createJenkinsCR(userplugins, false)
_, got := jenkinscr.ValidateCreate()
_, got := jenkinscr.ValidateCreate(context.TODO(), &jenkinscr)
assert.Nil(t, got)
userplugins = []Plugin{{Name: "google-login", Version: "1.2"}, {Name: "mailer", Version: "1.1"}, {Name: "git", Version: "4.8.1"}, {Name: "command-launcher", Version: "1.6"}, {Name: "workflow-cps", Version: "2.59"}}
newjenkinscr := *createJenkinsCR(userplugins, false)
_, got = newjenkinscr.ValidateUpdate(&jenkinscr)
_, got = newjenkinscr.ValidateUpdate(context.TODO(), &jenkinscr, &newjenkinscr)
assert.Nil(t, got)
})
}

View File

@ -3,14 +3,14 @@ API_VERSION_NEXT="v1alpha3"
API_VERSION="v1alpha2"
CLUSTER_DOMAIN="cluster.local"
GEN_CRD_API="gen-crd-api-reference-docs"
GO_VERSION="1.22"
HELM_VERSION="3.12.3"
GO_VERSION="1.24"
HELM_VERSION="3.19.0"
IMAGE_PULL_MODE="local"
KIND_CLUSTER_NAME="jenkins"
LATEST_LTS_VERSION="2.528.3"
NAME="kubernetes-operator"
NAMESPACE="default"
OPERATOR_SDK_VERSION="1.35.0"
OPERATOR_SDK_VERSION="1.41.1"
PKG="github.com/jenkinsci/kubernetes-operator"
QUAY_ORGANIZATION="jenkins-kubernetes-operator"
QUAY_REGISTRY="operator"

View File

@ -3,7 +3,7 @@ apiVersion: apiextensions.k8s.io/v1
kind: CustomResourceDefinition
metadata:
annotations:
controller-gen.kubebuilder.io/version: v0.14.0
controller-gen.kubebuilder.io/version: v0.18.0
name: jenkins.jenkins.io
spec:
group: jenkins.io
@ -301,9 +301,7 @@ spec:
This field is effectively required, but due to backwards compatibility is
allowed to be empty. Instances of this type with an empty value here are
almost certainly wrong.
TODO: Add other useful fields. apiVersion, kind, uid?
More info: https://kubernetes.io/docs/concepts/overview/working-with-objects/names/#names
TODO: Drop `kubebuilder:default` when controller-gen doesn't need it https://github.com/kubernetes-sigs/kubebuilder/issues/3896.
type: string
optional:
description: Specify whether the ConfigMap
@ -369,9 +367,7 @@ spec:
This field is effectively required, but due to backwards compatibility is
allowed to be empty. Instances of this type with an empty value here are
almost certainly wrong.
TODO: Add other useful fields. apiVersion, kind, uid?
More info: https://kubernetes.io/docs/concepts/overview/working-with-objects/names/#names
TODO: Drop `kubebuilder:default` when controller-gen doesn't need it https://github.com/kubernetes-sigs/kubebuilder/issues/3896.
type: string
optional:
description: Specify whether the Secret or
@ -395,7 +391,7 @@ spec:
Values defined by an Env with a duplicate key will take precedence.
items:
description: EnvFromSource represents the source of a
set of ConfigMaps
set of ConfigMaps or Secrets
properties:
configMapRef:
description: The ConfigMap to select from
@ -407,9 +403,7 @@ spec:
This field is effectively required, but due to backwards compatibility is
allowed to be empty. Instances of this type with an empty value here are
almost certainly wrong.
TODO: Add other useful fields. apiVersion, kind, uid?
More info: https://kubernetes.io/docs/concepts/overview/working-with-objects/names/#names
TODO: Drop `kubebuilder:default` when controller-gen doesn't need it https://github.com/kubernetes-sigs/kubebuilder/issues/3896.
type: string
optional:
description: Specify whether the ConfigMap must
@ -418,8 +412,8 @@ spec:
type: object
x-kubernetes-map-type: atomic
prefix:
description: An optional identifier to prepend to
each key in the ConfigMap. Must be a C_IDENTIFIER.
description: Optional text to prepend to the name
of each environment variable. Must be a C_IDENTIFIER.
type: string
secretRef:
description: The Secret to select from
@ -431,9 +425,7 @@ spec:
This field is effectively required, but due to backwards compatibility is
allowed to be empty. Instances of this type with an empty value here are
almost certainly wrong.
TODO: Add other useful fields. apiVersion, kind, uid?
More info: https://kubernetes.io/docs/concepts/overview/working-with-objects/names/#names
TODO: Drop `kubebuilder:default` when controller-gen doesn't need it https://github.com/kubernetes-sigs/kubebuilder/issues/3896.
type: string
optional:
description: Specify whether the Secret must be
@ -466,7 +458,8 @@ spec:
More info: https://kubernetes.io/docs/concepts/containers/container-lifecycle-hooks/#container-hooks
properties:
exec:
description: Exec specifies the action to take.
description: Exec specifies a command to execute
in the container.
properties:
command:
description: |-
@ -481,7 +474,7 @@ spec:
x-kubernetes-list-type: atomic
type: object
httpGet:
description: HTTPGet specifies the http request
description: HTTPGet specifies an HTTP GET request
to perform.
properties:
host:
@ -531,8 +524,8 @@ spec:
- port
type: object
sleep:
description: Sleep represents the duration that
the container should sleep before being terminated.
description: Sleep represents a duration that the
container should sleep.
properties:
seconds:
description: Seconds is the number of seconds
@ -545,8 +538,8 @@ spec:
tcpSocket:
description: |-
Deprecated. TCPSocket is NOT supported as a LifecycleHandler and kept
for the backward compatibility. There are no validation of this field and
lifecycle hooks will fail in runtime when tcp handler is specified.
for backward compatibility. There is no validation of this field and
lifecycle hooks will fail at runtime when it is specified.
properties:
host:
description: 'Optional: Host name to connect
@ -578,7 +571,8 @@ spec:
More info: https://kubernetes.io/docs/concepts/containers/container-lifecycle-hooks/#container-hooks
properties:
exec:
description: Exec specifies the action to take.
description: Exec specifies a command to execute
in the container.
properties:
command:
description: |-
@ -593,7 +587,7 @@ spec:
x-kubernetes-list-type: atomic
type: object
httpGet:
description: HTTPGet specifies the http request
description: HTTPGet specifies an HTTP GET request
to perform.
properties:
host:
@ -643,8 +637,8 @@ spec:
- port
type: object
sleep:
description: Sleep represents the duration that
the container should sleep before being terminated.
description: Sleep represents a duration that the
container should sleep.
properties:
seconds:
description: Seconds is the number of seconds
@ -657,8 +651,8 @@ spec:
tcpSocket:
description: |-
Deprecated. TCPSocket is NOT supported as a LifecycleHandler and kept
for the backward compatibility. There are no validation of this field and
lifecycle hooks will fail in runtime when tcp handler is specified.
for backward compatibility. There is no validation of this field and
lifecycle hooks will fail at runtime when it is specified.
properties:
host:
description: 'Optional: Host name to connect
@ -677,6 +671,12 @@ spec:
- port
type: object
type: object
stopSignal:
description: |-
StopSignal defines which signal will be sent to a container when it is being stopped.
If not specified, the default is defined by the container runtime in use.
StopSignal can only be set for Pods with a non-empty .spec.os.name
type: string
type: object
livenessProbe:
description: |-
@ -684,7 +684,8 @@ spec:
Container will be restarted if the probe fails.
properties:
exec:
description: Exec specifies the action to take.
description: Exec specifies a command to execute in
the container.
properties:
command:
description: |-
@ -705,8 +706,7 @@ spec:
format: int32
type: integer
grpc:
description: GRPC specifies an action involving a GRPC
port.
description: GRPC specifies a GRPC HealthCheckRequest.
properties:
port:
description: Port number of the gRPC service. Number
@ -714,18 +714,19 @@ spec:
format: int32
type: integer
service:
default: ""
description: |-
Service is the name of the service to place in the gRPC HealthCheckRequest
(see https://github.com/grpc/grpc/blob/master/doc/health-checking.md).
If this is not specified, the default behavior is defined by gRPC.
type: string
required:
- port
type: object
httpGet:
description: HTTPGet specifies the http request to perform.
description: HTTPGet specifies an HTTP GET request to
perform.
properties:
host:
description: |-
@ -792,8 +793,8 @@ spec:
format: int32
type: integer
tcpSocket:
description: TCPSocket specifies an action involving
a TCP port.
description: TCPSocket specifies a connection to a TCP
port.
properties:
host:
description: 'Optional: Host name to connect to,
@ -890,7 +891,8 @@ spec:
Container will be removed from service endpoints if the probe fails.
properties:
exec:
description: Exec specifies the action to take.
description: Exec specifies a command to execute in
the container.
properties:
command:
description: |-
@ -911,8 +913,7 @@ spec:
format: int32
type: integer
grpc:
description: GRPC specifies an action involving a GRPC
port.
description: GRPC specifies a GRPC HealthCheckRequest.
properties:
port:
description: Port number of the gRPC service. Number
@ -920,18 +921,19 @@ spec:
format: int32
type: integer
service:
default: ""
description: |-
Service is the name of the service to place in the gRPC HealthCheckRequest
(see https://github.com/grpc/grpc/blob/master/doc/health-checking.md).
If this is not specified, the default behavior is defined by gRPC.
type: string
required:
- port
type: object
httpGet:
description: HTTPGet specifies the http request to perform.
description: HTTPGet specifies an HTTP GET request to
perform.
properties:
host:
description: |-
@ -998,8 +1000,8 @@ spec:
format: int32
type: integer
tcpSocket:
description: TCPSocket specifies an action involving
a TCP port.
description: TCPSocket specifies a connection to a TCP
port.
properties:
host:
description: 'Optional: Host name to connect to,
@ -1049,11 +1051,9 @@ spec:
Claims lists the names of resources, defined in spec.resourceClaims,
that are used by this container.
This is an alpha field and requires enabling the
DynamicResourceAllocation feature gate.
This field is immutable. It can only be set for containers.
items:
description: ResourceClaim references one entry in
@ -1065,6 +1065,12 @@ spec:
the Pod where this field is used. It makes that resource available
inside a container.
type: string
request:
description: |-
Request is the name chosen for a request in the referenced claim.
If empty, everything from the claim is made available, otherwise
only the result of this request.
type: string
required:
- name
type: object
@ -1170,7 +1176,7 @@ spec:
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
The default value is Default which uses the container runtime defaults for
readonly paths and masked paths.
This requires the ProcMountType feature flag to be enabled.
Note that this field cannot be set when spec.os.name is windows.
@ -1252,7 +1258,6 @@ spec:
type indicates which kind of seccomp profile will be applied.
Valid options are:
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.
@ -1326,10 +1331,8 @@ spec:
RecursiveReadOnly specifies whether read-only mounts should be handled
recursively.
If ReadOnly is false, this field has no meaning and must be unspecified.
If ReadOnly is true, and this field is set to Disabled, the mount is not made
recursively read-only. If this field is set to IfPossible, the mount is made
recursively read-only, if it is supported by the container runtime. If this
@ -1337,11 +1340,9 @@ spec:
supported by the container runtime, otherwise the pod will not be started and
an error will be generated to indicate the reason.
If this field is set to IfPossible or Enabled, MountPropagation must be set to
None (or be unspecified, which defaults to None).
If this field is not specified, it is treated as an equivalent of Disabled.
type: string
subPath:
@ -1416,9 +1417,7 @@ spec:
This field is effectively required, but due to backwards compatibility is
allowed to be empty. Instances of this type with an empty value here are
almost certainly wrong.
TODO: Add other useful fields. apiVersion, kind, uid?
More info: https://kubernetes.io/docs/concepts/overview/working-with-objects/names/#names
TODO: Drop `kubebuilder:default` when controller-gen doesn't need it https://github.com/kubernetes-sigs/kubebuilder/issues/3896.
type: string
type: object
x-kubernetes-map-type: atomic
@ -1507,12 +1506,10 @@ spec:
Some volume types allow the Kubelet to change the ownership of that volume
to be owned by the pod:
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----
If unset, the Kubelet will not modify the ownership and permissions of any volume.
Note that this field cannot be set when spec.os.name is windows.
format: int64
@ -1556,6 +1553,32 @@ spec:
Note that this field cannot be set when spec.os.name is windows.
format: int64
type: integer
seLinuxChangePolicy:
description: |-
seLinuxChangePolicy defines how the container's SELinux label is applied to all volumes used by the Pod.
It has no effect on nodes that do not support SELinux or to volumes does not support SELinux.
Valid values are "MountOption" and "Recursive".
"Recursive" means relabeling of all files on all Pod volumes by the container runtime.
This may be slow for large volumes, but allows mixing privileged and unprivileged Pods sharing the same volume on the same node.
"MountOption" mounts all eligible Pod volumes with `-o context` mount option.
This requires all Pods that share the same volume to use the same SELinux label.
It is not possible to share the same volume among privileged and unprivileged Pods.
Eligible volumes are in-tree FibreChannel and iSCSI volumes, and all CSI volumes
whose CSI driver announces SELinux support by setting spec.seLinuxMount: true in their
CSIDriver instance. Other volumes are always re-labelled recursively.
"MountOption" value is allowed only when SELinuxMount feature gate is enabled.
If not specified and SELinuxMount feature gate is enabled, "MountOption" is used.
If not specified and SELinuxMount feature gate is disabled, "MountOption" is used for ReadWriteOncePod volumes
and "Recursive" for all other volumes.
This field affects only Pods that have SELinux label set, either in PodSecurityContext or in SecurityContext of all containers.
All Pods that use the same volume should use the same seLinuxChangePolicy, otherwise some pods can get stuck in ContainerCreating state.
Note that this field cannot be set when spec.os.name is windows.
type: string
seLinuxOptions:
description: |-
The SELinux context to be applied to all containers.
@ -1599,7 +1622,6 @@ spec:
type indicates which kind of seccomp profile will be applied.
Valid options are:
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.
@ -1609,18 +1631,28 @@ spec:
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, the fsGroup (if specified), and group memberships
defined in the container image for the uid of the container process. If unspecified,
no additional groups are added to any container. Note that group memberships
defined in the container image for the uid of the container process are still effective,
even if they are not included in this list.
A list of groups applied to the first process run in each container, in
addition to the container's primary GID and fsGroup (if specified). If
the SupplementalGroupsPolicy feature is enabled, the
supplementalGroupsPolicy field determines whether these are in addition
to or instead of any group memberships defined in the container image.
If unspecified, no additional groups are added, though group memberships
defined in the container image may still be used, depending on the
supplementalGroupsPolicy field.
Note that this field cannot be set when spec.os.name is windows.
items:
format: int64
type: integer
type: array
x-kubernetes-list-type: atomic
supplementalGroupsPolicy:
description: |-
Defines how supplemental groups of the first container processes are calculated.
Valid values are "Merge" and "Strict". If not specified, "Merge" is used.
(Alpha) Using the field requires the SupplementalGroupsPolicy feature gate to be enabled
and the container runtime must implement support for this feature.
Note that this field cannot be set when spec.os.name is windows.
type: string
sysctls:
description: |-
Sysctls hold a list of namespaced sysctls used for the pod. Pods with unsupported
@ -1737,6 +1769,8 @@ spec:
description: |-
awsElasticBlockStore represents an AWS Disk resource that is attached to a
kubelet's host machine and then exposed to the pod.
Deprecated: AWSElasticBlockStore is deprecated. All operations for the in-tree
awsElasticBlockStore type are redirected to the ebs.csi.aws.com CSI driver.
More info: https://kubernetes.io/docs/concepts/storage/volumes#awselasticblockstore
properties:
fsType:
@ -1745,7 +1779,6 @@ spec:
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: |-
@ -1769,8 +1802,10 @@ spec:
- volumeID
type: object
azureDisk:
description: azureDisk represents an Azure Data Disk mount
on the host and bind mount to the pod.
description: |-
azureDisk represents an Azure Data Disk mount on the host and bind mount to the pod.
Deprecated: AzureDisk is deprecated. All operations for the in-tree azureDisk type
are redirected to the disk.csi.azure.com CSI driver.
properties:
cachingMode:
description: 'cachingMode is the Host Caching mode:
@ -1785,6 +1820,7 @@ spec:
blob storage
type: string
fsType:
default: ext4
description: |-
fsType is Filesystem type to mount.
Must be a filesystem type supported by the host operating system.
@ -1798,6 +1834,7 @@ spec:
to shared'
type: string
readOnly:
default: false
description: |-
readOnly Defaults to false (read/write). ReadOnly here will force
the ReadOnly setting in VolumeMounts.
@ -1807,8 +1844,10 @@ spec:
- diskURI
type: object
azureFile:
description: azureFile represents an Azure File Service
mount on the host and bind mount to the pod.
description: |-
azureFile represents an Azure File Service mount on the host and bind mount to the pod.
Deprecated: AzureFile is deprecated. All operations for the in-tree azureFile type
are redirected to the file.csi.azure.com CSI driver.
properties:
readOnly:
description: |-
@ -1827,8 +1866,9 @@ spec:
- shareName
type: object
cephfs:
description: cephFS represents a Ceph FS mount on the host
that shares a pod's lifetime
description: |-
cephFS represents a Ceph FS mount on the host that shares a pod's lifetime.
Deprecated: CephFS is deprecated and the in-tree cephfs type is no longer supported.
properties:
monitors:
description: |-
@ -1865,9 +1905,7 @@ spec:
This field is effectively required, but due to backwards compatibility is
allowed to be empty. Instances of this type with an empty value here are
almost certainly wrong.
TODO: Add other useful fields. apiVersion, kind, uid?
More info: https://kubernetes.io/docs/concepts/overview/working-with-objects/names/#names
TODO: Drop `kubebuilder:default` when controller-gen doesn't need it https://github.com/kubernetes-sigs/kubebuilder/issues/3896.
type: string
type: object
x-kubernetes-map-type: atomic
@ -1882,6 +1920,8 @@ spec:
cinder:
description: |-
cinder represents a cinder volume attached and mounted on kubelets host machine.
Deprecated: Cinder is deprecated. All operations for the in-tree cinder type
are redirected to the cinder.csi.openstack.org CSI driver.
More info: https://examples.k8s.io/mysql-cinder-pd/README.md
properties:
fsType:
@ -1909,9 +1949,7 @@ spec:
This field is effectively required, but due to backwards compatibility is
allowed to be empty. Instances of this type with an empty value here are
almost certainly wrong.
TODO: Add other useful fields. apiVersion, kind, uid?
More info: https://kubernetes.io/docs/concepts/overview/working-with-objects/names/#names
TODO: Drop `kubebuilder:default` when controller-gen doesn't need it https://github.com/kubernetes-sigs/kubebuilder/issues/3896.
type: string
type: object
x-kubernetes-map-type: atomic
@ -1984,9 +2022,7 @@ spec:
This field is effectively required, but due to backwards compatibility is
allowed to be empty. Instances of this type with an empty value here are
almost certainly wrong.
TODO: Add other useful fields. apiVersion, kind, uid?
More info: https://kubernetes.io/docs/concepts/overview/working-with-objects/names/#names
TODO: Drop `kubebuilder:default` when controller-gen doesn't need it https://github.com/kubernetes-sigs/kubebuilder/issues/3896.
type: string
optional:
description: optional specify whether the ConfigMap
@ -1997,7 +2033,7 @@ spec:
csi:
description: csi (Container Storage Interface) represents
ephemeral storage that is handled by certain external
CSI drivers (Beta feature).
CSI drivers.
properties:
driver:
description: |-
@ -2025,9 +2061,7 @@ spec:
This field is effectively required, but due to backwards compatibility is
allowed to be empty. Instances of this type with an empty value here are
almost certainly wrong.
TODO: Add other useful fields. apiVersion, kind, uid?
More info: https://kubernetes.io/docs/concepts/overview/working-with-objects/names/#names
TODO: Drop `kubebuilder:default` when controller-gen doesn't need it https://github.com/kubernetes-sigs/kubebuilder/issues/3896.
type: string
type: object
x-kubernetes-map-type: atomic
@ -2165,7 +2199,6 @@ spec:
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.
Use this if:
a) the volume is only needed while the pod runs,
b) features of normal volumes like restoring from snapshot or capacity
@ -2176,17 +2209,14 @@ spec:
information on the connection between this volume type
and PersistentVolumeClaim).
Use PersistentVolumeClaim or one of the vendor-specific
APIs for volumes that persist for longer than the lifecycle
of an individual pod.
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.
A pod can use both types of ephemeral volumes and
persistent volumes at the same time.
properties:
@ -2200,7 +2230,6 @@ spec:
entry. Pod validation will reject the pod if the concatenated name
is not valid for a PVC (for example, too long).
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
@ -2210,11 +2239,9 @@ spec:
this should not be necessary, but it may be useful when
manually reconstructing a broken cluster.
This field is read-only and no changes will be made by Kubernetes
to the PVC after it has been created.
Required, must not be nil.
properties:
metadata:
@ -2417,7 +2444,7 @@ spec:
set to a Pending state, as reflected by the modifyVolumeStatus field, until such as a resource
exists.
More info: https://kubernetes.io/docs/concepts/storage/volume-attributes-classes/
(Alpha) Using this field requires the VolumeAttributesClass feature gate to be enabled.
(Beta) Using this field requires the VolumeAttributesClass feature gate to be enabled (off by default).
type: string
volumeMode:
description: |-
@ -2443,7 +2470,6 @@ spec:
fsType is the 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: 'lun is Optional: FC target lun number'
@ -2474,6 +2500,7 @@ spec:
description: |-
flexVolume represents a generic volume resource that is
provisioned/attached using an exec based plugin.
Deprecated: FlexVolume is deprecated. Consider using a CSIDriver instead.
properties:
driver:
description: driver is the name of the driver to use
@ -2511,9 +2538,7 @@ spec:
This field is effectively required, but due to backwards compatibility is
allowed to be empty. Instances of this type with an empty value here are
almost certainly wrong.
TODO: Add other useful fields. apiVersion, kind, uid?
More info: https://kubernetes.io/docs/concepts/overview/working-with-objects/names/#names
TODO: Drop `kubebuilder:default` when controller-gen doesn't need it https://github.com/kubernetes-sigs/kubebuilder/issues/3896.
type: string
type: object
x-kubernetes-map-type: atomic
@ -2521,9 +2546,9 @@ spec:
- 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
description: |-
flocker represents a Flocker volume attached to a kubelet's host machine. This depends on the Flocker control service being running.
Deprecated: Flocker is deprecated and the in-tree flocker type is no longer supported.
properties:
datasetName:
description: |-
@ -2539,6 +2564,8 @@ spec:
description: |-
gcePersistentDisk represents a GCE Disk resource that is attached to a
kubelet's host machine and then exposed to the pod.
Deprecated: GCEPersistentDisk is deprecated. All operations for the in-tree
gcePersistentDisk type are redirected to the pd.csi.storage.gke.io CSI driver.
More info: https://kubernetes.io/docs/concepts/storage/volumes#gcepersistentdisk
properties:
fsType:
@ -2547,7 +2574,6 @@ spec:
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: |-
@ -2575,7 +2601,7 @@ spec:
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
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:
@ -2599,6 +2625,7 @@ spec:
glusterfs:
description: |-
glusterfs represents a Glusterfs mount on the host that shares a pod's lifetime.
Deprecated: Glusterfs is deprecated and the in-tree glusterfs type is no longer supported.
More info: https://examples.k8s.io/volumes/glusterfs/README.md
properties:
endpoints:
@ -2628,9 +2655,6 @@ spec:
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: |-
@ -2647,6 +2671,41 @@ spec:
required:
- path
type: object
image:
description: |-
image represents an OCI object (a container image or artifact) pulled and mounted on the kubelet's host machine.
The volume is resolved at pod startup depending on which PullPolicy value is provided:
- Always: the kubelet always attempts to pull the reference. Container creation will fail If the pull fails.
- Never: the kubelet never pulls the reference and only uses a local image or artifact. Container creation will fail if the reference isn't present.
- IfNotPresent: the kubelet pulls if the reference isn't already present on disk. Container creation will fail if the reference isn't present and the pull fails.
The volume gets re-resolved if the pod gets deleted and recreated, which means that new remote content will become available on pod recreation.
A failure to resolve or pull the image during pod startup will block containers from starting and may add significant latency. Failures will be retried using normal volume backoff and will be reported on the pod reason and message.
The types of objects that may be mounted by this volume are defined by the container runtime implementation on a host machine and at minimum must include all valid types supported by the container image field.
The OCI object gets mounted in a single directory (spec.containers[*].volumeMounts.mountPath) by merging the manifest layers in the same way as for container images.
The volume will be mounted read-only (ro) and non-executable files (noexec).
Sub path mounts for containers are not supported (spec.containers[*].volumeMounts.subpath) before 1.33.
The field spec.securityContext.fsGroupChangePolicy has no effect on this volume type.
properties:
pullPolicy:
description: |-
Policy for pulling OCI objects. Possible values are:
Always: the kubelet always attempts to pull the reference. Container creation will fail If the pull fails.
Never: the kubelet never pulls the reference and only uses a local image or artifact. Container creation will fail if the reference isn't present.
IfNotPresent: the kubelet pulls if the reference isn't already present on disk. Container creation will fail if the reference isn't present and the pull fails.
Defaults to Always if :latest tag is specified, or IfNotPresent otherwise.
type: string
reference:
description: |-
Required: Image or artifact reference to be used.
Behaves in the same way as pod.spec.containers[*].image.
Pull secrets will be assembled in the same way as for the container image by looking up node credentials, SA image pull secrets, and pod spec image pull secrets.
More info: https://kubernetes.io/docs/concepts/containers/images
This field is optional to allow higher level config management to default or override
container images in workload controllers like Deployments and StatefulSets.
type: string
type: object
iscsi:
description: |-
iscsi represents an ISCSI Disk resource that is attached to a
@ -2667,7 +2726,6 @@ spec:
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: |-
@ -2679,6 +2737,7 @@ spec:
description: iqn is the target iSCSI Qualified Name.
type: string
iscsiInterface:
default: default
description: |-
iscsiInterface is the interface Name that uses an iSCSI transport.
Defaults to 'default' (tcp).
@ -2711,9 +2770,7 @@ spec:
This field is effectively required, but due to backwards compatibility is
allowed to be empty. Instances of this type with an empty value here are
almost certainly wrong.
TODO: Add other useful fields. apiVersion, kind, uid?
More info: https://kubernetes.io/docs/concepts/overview/working-with-objects/names/#names
TODO: Drop `kubebuilder:default` when controller-gen doesn't need it https://github.com/kubernetes-sigs/kubebuilder/issues/3896.
type: string
type: object
x-kubernetes-map-type: atomic
@ -2778,9 +2835,9 @@ spec:
- claimName
type: object
photonPersistentDisk:
description: photonPersistentDisk represents a PhotonController
persistent disk attached and mounted on kubelets host
machine
description: |-
photonPersistentDisk represents a PhotonController persistent disk attached and mounted on kubelets host machine.
Deprecated: PhotonPersistentDisk is deprecated and the in-tree photonPersistentDisk type is no longer supported.
properties:
fsType:
description: |-
@ -2796,8 +2853,11 @@ spec:
- pdID
type: object
portworxVolume:
description: portworxVolume represents a portworx volume
attached and mounted on kubelets host machine
description: |-
portworxVolume represents a portworx volume attached and mounted on kubelets host machine.
Deprecated: PortworxVolume is deprecated. All operations for the in-tree portworxVolume type
are redirected to the pxd.portworx.com CSI driver when the CSIMigrationPortworx feature-gate
is on.
properties:
fsType:
description: |-
@ -2832,24 +2892,24 @@ spec:
format: int32
type: integer
sources:
description: sources is the list of volume projections
description: |-
sources is the list of volume projections. Each entry in this list
handles one source.
items:
description: Projection that may be projected along
with other supported volume types
description: |-
Projection that may be projected along with other supported volume types.
Exactly one of these fields must be set.
properties:
clusterTrustBundle:
description: |-
ClusterTrustBundle allows a pod to access the `.spec.trustBundle` field
of ClusterTrustBundle objects in an auto-updating file.
Alpha, gated by the ClusterTrustBundleProjection feature gate.
ClusterTrustBundle objects can either be selected by name, or by the
combination of signer name and a label selector.
Kubelet performs aggressive normalization of the PEM contents written
into the pod filesystem. Esoteric PEM features such as inter-block
comments and block headers are stripped. Certificates are deduplicated.
@ -2983,9 +3043,7 @@ spec:
This field is effectively required, but due to backwards compatibility is
allowed to be empty. Instances of this type with an empty value here are
almost certainly wrong.
TODO: Add other useful fields. apiVersion, kind, uid?
More info: https://kubernetes.io/docs/concepts/overview/working-with-objects/names/#names
TODO: Drop `kubebuilder:default` when controller-gen doesn't need it https://github.com/kubernetes-sigs/kubebuilder/issues/3896.
type: string
optional:
description: optional specify whether the
@ -3124,9 +3182,7 @@ spec:
This field is effectively required, but due to backwards compatibility is
allowed to be empty. Instances of this type with an empty value here are
almost certainly wrong.
TODO: Add other useful fields. apiVersion, kind, uid?
More info: https://kubernetes.io/docs/concepts/overview/working-with-objects/names/#names
TODO: Drop `kubebuilder:default` when controller-gen doesn't need it https://github.com/kubernetes-sigs/kubebuilder/issues/3896.
type: string
optional:
description: optional field specify whether
@ -3168,8 +3224,9 @@ spec:
x-kubernetes-list-type: atomic
type: object
quobyte:
description: quobyte represents a Quobyte mount on the host
that shares a pod's lifetime
description: |-
quobyte represents a Quobyte mount on the host that shares a pod's lifetime.
Deprecated: Quobyte is deprecated and the in-tree quobyte type is no longer supported.
properties:
group:
description: |-
@ -3208,6 +3265,7 @@ spec:
rbd:
description: |-
rbd represents a Rados Block Device mount on the host that shares a pod's lifetime.
Deprecated: RBD is deprecated and the in-tree rbd type is no longer supported.
More info: https://examples.k8s.io/volumes/rbd/README.md
properties:
fsType:
@ -3216,7 +3274,6 @@ spec:
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: |-
@ -3224,6 +3281,7 @@ spec:
More info: https://examples.k8s.io/volumes/rbd/README.md#how-to-use-it
type: string
keyring:
default: /etc/ceph/keyring
description: |-
keyring is the path to key ring for RBDUser.
Default is /etc/ceph/keyring.
@ -3238,6 +3296,7 @@ spec:
type: array
x-kubernetes-list-type: atomic
pool:
default: rbd
description: |-
pool is the rados pool name.
Default is rbd.
@ -3263,13 +3322,12 @@ spec:
This field is effectively required, but due to backwards compatibility is
allowed to be empty. Instances of this type with an empty value here are
almost certainly wrong.
TODO: Add other useful fields. apiVersion, kind, uid?
More info: https://kubernetes.io/docs/concepts/overview/working-with-objects/names/#names
TODO: Drop `kubebuilder:default` when controller-gen doesn't need it https://github.com/kubernetes-sigs/kubebuilder/issues/3896.
type: string
type: object
x-kubernetes-map-type: atomic
user:
default: admin
description: |-
user is the rados user name.
Default is admin.
@ -3280,10 +3338,12 @@ spec:
- monitors
type: object
scaleIO:
description: scaleIO represents a ScaleIO persistent volume
attached and mounted on Kubernetes nodes.
description: |-
scaleIO represents a ScaleIO persistent volume attached and mounted on Kubernetes nodes.
Deprecated: ScaleIO is deprecated and the in-tree scaleIO type is no longer supported.
properties:
fsType:
default: xfs
description: |-
fsType is the filesystem type to mount.
Must be a filesystem type supported by the host operating system.
@ -3315,9 +3375,7 @@ spec:
This field is effectively required, but due to backwards compatibility is
allowed to be empty. Instances of this type with an empty value here are
almost certainly wrong.
TODO: Add other useful fields. apiVersion, kind, uid?
More info: https://kubernetes.io/docs/concepts/overview/working-with-objects/names/#names
TODO: Drop `kubebuilder:default` when controller-gen doesn't need it https://github.com/kubernetes-sigs/kubebuilder/issues/3896.
type: string
type: object
x-kubernetes-map-type: atomic
@ -3326,6 +3384,7 @@ spec:
with Gateway, default false
type: boolean
storageMode:
default: ThinProvisioned
description: |-
storageMode indicates whether the storage for a volume should be ThickProvisioned or ThinProvisioned.
Default is ThinProvisioned.
@ -3414,8 +3473,9 @@ spec:
type: string
type: object
storageos:
description: storageOS represents a StorageOS volume attached
and mounted on Kubernetes nodes.
description: |-
storageOS represents a StorageOS volume attached and mounted on Kubernetes nodes.
Deprecated: StorageOS is deprecated and the in-tree storageos type is no longer supported.
properties:
fsType:
description: |-
@ -3440,9 +3500,7 @@ spec:
This field is effectively required, but due to backwards compatibility is
allowed to be empty. Instances of this type with an empty value here are
almost certainly wrong.
TODO: Add other useful fields. apiVersion, kind, uid?
More info: https://kubernetes.io/docs/concepts/overview/working-with-objects/names/#names
TODO: Drop `kubebuilder:default` when controller-gen doesn't need it https://github.com/kubernetes-sigs/kubebuilder/issues/3896.
type: string
type: object
x-kubernetes-map-type: atomic
@ -3462,8 +3520,10 @@ spec:
type: string
type: object
vsphereVolume:
description: vsphereVolume represents a vSphere volume attached
and mounted on kubelets host machine
description: |-
vsphereVolume represents a vSphere volume attached and mounted on kubelets host machine.
Deprecated: VsphereVolume is deprecated. All operations for the in-tree vsphereVolume type
are redirected to the csi.vsphere.vmware.com CSI driver.
properties:
fsType:
description: |-
@ -3526,9 +3586,7 @@ spec:
This field is effectively required, but due to backwards compatibility is
allowed to be empty. Instances of this type with an empty value here are
almost certainly wrong.
TODO: Add other useful fields. apiVersion, kind, uid?
More info: https://kubernetes.io/docs/concepts/overview/working-with-objects/names/#names
TODO: Drop `kubebuilder:default` when controller-gen doesn't need it https://github.com/kubernetes-sigs/kubebuilder/issues/3896.
type: string
type: object
x-kubernetes-map-type: atomic
@ -3571,9 +3629,7 @@ spec:
This field is effectively required, but due to backwards compatibility is
allowed to be empty. Instances of this type with an empty value here are
almost certainly wrong.
TODO: Add other useful fields. apiVersion, kind, uid?
More info: https://kubernetes.io/docs/concepts/overview/working-with-objects/names/#names
TODO: Drop `kubebuilder:default` when controller-gen doesn't need it https://github.com/kubernetes-sigs/kubebuilder/issues/3896.
type: string
type: object
x-kubernetes-map-type: atomic
@ -3607,9 +3663,7 @@ spec:
This field is effectively required, but due to backwards compatibility is
allowed to be empty. Instances of this type with an empty value here are
almost certainly wrong.
TODO: Add other useful fields. apiVersion, kind, uid?
More info: https://kubernetes.io/docs/concepts/overview/working-with-objects/names/#names
TODO: Drop `kubebuilder:default` when controller-gen doesn't need it https://github.com/kubernetes-sigs/kubebuilder/issues/3896.
type: string
type: object
x-kubernetes-map-type: atomic
@ -3643,9 +3697,7 @@ spec:
This field is effectively required, but due to backwards compatibility is
allowed to be empty. Instances of this type with an empty value here are
almost certainly wrong.
TODO: Add other useful fields. apiVersion, kind, uid?
More info: https://kubernetes.io/docs/concepts/overview/working-with-objects/names/#names
TODO: Drop `kubebuilder:default` when controller-gen doesn't need it https://github.com/kubernetes-sigs/kubebuilder/issues/3896.
type: string
type: object
x-kubernetes-map-type: atomic
@ -3683,9 +3735,7 @@ spec:
This field is effectively required, but due to backwards compatibility is
allowed to be empty. Instances of this type with an empty value here are
almost certainly wrong.
TODO: Add other useful fields. apiVersion, kind, uid?
More info: https://kubernetes.io/docs/concepts/overview/working-with-objects/names/#names
TODO: Drop `kubebuilder:default` when controller-gen doesn't need it https://github.com/kubernetes-sigs/kubebuilder/issues/3896.
type: string
type: object
x-kubernetes-map-type: atomic

View File

@ -4,6 +4,56 @@ kind: ClusterRole
metadata:
name: manager-role
rules:
- apiGroups:
- ""
resources:
- configmaps
- secrets
- serviceaccounts
- services
verbs:
- create
- get
- list
- update
- watch
- apiGroups:
- ""
resources:
- events
verbs:
- create
- get
- list
- patch
- watch
- apiGroups:
- ""
resources:
- persistentvolumeclaims
- pods/log
verbs:
- get
- list
- watch
- apiGroups:
- ""
resources:
- pods
verbs:
- '*'
- apiGroups:
- ""
resources:
- pods/exec
verbs:
- '*'
- apiGroups:
- ""
resources:
- pods/portforward
verbs:
- create
- apiGroups:
- apps
resources:
@ -29,79 +79,6 @@ rules:
- 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:

View File

@ -40,9 +40,9 @@
pkgs.helm-docs
pkgs.pre-commit
pkgs.kind
pkgs.golangci-lint
pkgs.go_1_22
rolling.operator-sdk # 1.39.2
rolling.golangci-lint
rolling.go_1_24
pkgs.operator-sdk # 1.39.2
(pkgs.bats.withLibraries (p: [
p.bats-support

82
go.mod
View File

@ -1,29 +1,29 @@
module github.com/jenkinsci/kubernetes-operator
go 1.22.12
go 1.24.0
require (
github.com/bndr/gojenkins v1.1.0
github.com/distribution/reference v0.6.0
github.com/go-logr/logr v1.4.2
github.com/go-logr/logr v1.4.3
github.com/go-logr/zapr v1.3.0
github.com/golang/mock v1.6.0
github.com/mailgun/mailgun-go/v3 v3.6.4
github.com/onsi/ginkgo v1.14.1
github.com/onsi/gomega v1.33.1
github.com/onsi/ginkgo v1.16.5
github.com/onsi/gomega v1.38.2
github.com/openshift/api v3.9.0+incompatible
github.com/pkg/errors v0.9.1
github.com/stretchr/testify v1.9.0
github.com/stretchr/testify v1.11.1
go.uber.org/zap v1.27.0
golang.org/x/crypto v0.31.0
golang.org/x/mod v0.19.0
golang.org/x/crypto v0.41.0
golang.org/x/mod v0.26.0
gopkg.in/gomail.v2 v2.0.0-20160411212932-81ebce5c23df
k8s.io/api v0.30.3
k8s.io/apimachinery v0.30.3
k8s.io/cli-runtime v0.30.3
k8s.io/client-go v0.30.3
k8s.io/utils v0.0.0-20240711033017-18e509b52bc8
sigs.k8s.io/controller-runtime v0.18.4
k8s.io/api v0.33.0
k8s.io/apimachinery v0.33.0
k8s.io/cli-runtime v0.33.0
k8s.io/client-go v0.33.0
k8s.io/utils v0.0.0-20250604170112-4c0f3b243397
sigs.k8s.io/controller-runtime v0.21.0
)
require (
@ -34,32 +34,28 @@ require (
github.com/davecgh/go-spew v1.1.2-0.20180830191138-d8f796af33cc // indirect
github.com/emicklei/go-restful/v3 v3.12.1 // indirect
github.com/evanphx/json-patch v5.9.0+incompatible // indirect
github.com/evanphx/json-patch/v5 v5.9.0 // indirect
github.com/evanphx/json-patch/v5 v5.9.11 // indirect
github.com/fsnotify/fsnotify v1.7.0 // indirect
github.com/fxamacker/cbor/v2 v2.7.0 // indirect
github.com/go-chi/chi v4.1.2+incompatible // indirect
github.com/go-errors/errors v1.5.1 // indirect
github.com/go-openapi/jsonpointer v0.21.0 // indirect
github.com/go-openapi/jsonreference v0.21.0 // indirect
github.com/go-openapi/swag v0.23.0 // indirect
github.com/gogo/protobuf v1.3.2 // indirect
github.com/golang/groupcache v0.0.0-20210331224755-41bb18bfe9da // indirect
github.com/golang/protobuf v1.5.4 // indirect
github.com/google/btree v1.1.2 // indirect
github.com/google/gnostic-models v0.6.8 // indirect
github.com/google/go-cmp v0.6.0 // indirect
github.com/google/gofuzz v1.2.0 // indirect
github.com/google/pprof v0.0.0-20240721033354-7089f98c1d14 // indirect
github.com/google/btree v1.1.3 // indirect
github.com/google/gnostic-models v0.6.9 // indirect
github.com/google/go-cmp v0.7.0 // indirect
github.com/google/shlex v0.0.0-20191202100458-e7afc7fbc510 // indirect
github.com/google/uuid v1.6.0 // indirect
github.com/gorilla/websocket v1.5.3 // indirect
github.com/gorilla/websocket v1.5.4-0.20250319132907-e064f32e3674 // indirect
github.com/gregjones/httpcache v0.0.0-20190611155906-901d90724c79 // indirect
github.com/imdario/mergo v0.3.16 // indirect
github.com/inconshreveable/mousetrap v1.1.0 // indirect
github.com/josharian/intern v1.0.0 // indirect
github.com/json-iterator/go v1.1.12 // indirect
github.com/liggitt/tabwriter v0.0.0-20181228230101-89fcab3d43de // indirect
github.com/mailru/easyjson v0.7.7 // indirect
github.com/moby/spdystream v0.4.0 // indirect
github.com/moby/spdystream v0.5.0 // indirect
github.com/moby/term v0.5.0 // indirect
github.com/modern-go/concurrent v0.0.0-20180306012644-bacd9c7ef1dd // indirect
github.com/modern-go/reflect2 v1.0.2 // indirect
@ -70,37 +66,37 @@ require (
github.com/opencontainers/go-digest v1.0.0 // indirect
github.com/peterbourgon/diskv v2.0.1+incompatible // indirect
github.com/pmezard/go-difflib v1.0.1-0.20181226105442-5d4384ee4fb2 // indirect
github.com/prometheus/client_golang v1.19.1 // indirect
github.com/prometheus/client_golang v1.22.0 // indirect
github.com/prometheus/client_model v0.6.1 // indirect
github.com/prometheus/common v0.55.0 // indirect
github.com/prometheus/common v0.62.0 // indirect
github.com/prometheus/procfs v0.15.1 // indirect
github.com/spf13/cobra v1.8.1 // indirect
github.com/spf13/pflag v1.0.5 // indirect
github.com/x448/float16 v0.8.4 // indirect
github.com/xlab/treeprint v1.2.0 // indirect
go.starlark.net v0.0.0-20240705175910-70002002b310 // indirect
go.uber.org/multierr v1.11.0 // indirect
golang.org/x/exp v0.0.0-20240719175910-8a7402abbf56 // indirect
golang.org/x/net v0.27.0 // indirect
golang.org/x/oauth2 v0.21.0 // indirect
golang.org/x/sync v0.10.0 // indirect
golang.org/x/sys v0.28.0 // indirect
golang.org/x/term v0.27.0 // indirect
golang.org/x/text v0.21.0 // indirect
golang.org/x/time v0.5.0 // indirect
go.yaml.in/yaml/v3 v3.0.4 // indirect
golang.org/x/net v0.43.0 // indirect
golang.org/x/oauth2 v0.27.0 // indirect
golang.org/x/sync v0.16.0 // indirect
golang.org/x/sys v0.35.0 // indirect
golang.org/x/term v0.34.0 // indirect
golang.org/x/text v0.28.0 // indirect
golang.org/x/time v0.9.0 // indirect
gomodules.xyz/jsonpatch/v2 v2.4.0 // indirect
google.golang.org/protobuf v1.34.2 // indirect
google.golang.org/protobuf v1.36.7 // indirect
gopkg.in/alexcesaro/quotedprintable.v3 v3.0.0-20150716171945-2caba252f4dc // indirect
gopkg.in/evanphx/json-patch.v4 v4.12.0 // indirect
gopkg.in/inf.v0 v0.9.1 // indirect
gopkg.in/tomb.v1 v1.0.0-20141024135613-dd632973f1e7 // indirect
gopkg.in/yaml.v2 v2.4.0 // indirect
gopkg.in/yaml.v3 v3.0.1 // indirect
k8s.io/apiextensions-apiserver v0.30.3 // indirect
k8s.io/apiextensions-apiserver v0.33.0 // indirect
k8s.io/klog/v2 v2.130.1 // indirect
k8s.io/kube-openapi v0.0.0-20240709000822-3c01b740850f // indirect
sigs.k8s.io/json v0.0.0-20221116044647-bc3834ca7abd // indirect
sigs.k8s.io/kustomize/api v0.17.3 // indirect
sigs.k8s.io/kustomize/kyaml v0.17.2 // indirect
sigs.k8s.io/structured-merge-diff/v4 v4.4.1 // indirect
k8s.io/kube-openapi v0.0.0-20250318190949-c8a335a9a2ff // indirect
sigs.k8s.io/json v0.0.0-20241010143419-9aa6b5e7a4b3 // indirect
sigs.k8s.io/kustomize/api v0.19.0 // indirect
sigs.k8s.io/kustomize/kyaml v0.19.0 // indirect
sigs.k8s.io/randfill v1.0.0 // indirect
sigs.k8s.io/structured-merge-diff/v4 v4.6.0 // indirect
sigs.k8s.io/yaml v1.4.0 // indirect
)

191
go.sum
View File

@ -1,5 +1,7 @@
github.com/Azure/go-ansiterm v0.0.0-20230124172434-306776ec8161 h1:L/gRVlceqvL25UVaW/CKtUDjefjrs0SPonmDGUVOYP0=
github.com/Azure/go-ansiterm v0.0.0-20230124172434-306776ec8161/go.mod h1:xomTg63KZ2rFqZQzSB4Vz2SUXa1BpHTVz9L5PTmPC4E=
github.com/Masterminds/semver/v3 v3.4.0 h1:Zog+i5UMtVoCU8oKka5P7i9q9HgrJeGzI9SA1Xbatp0=
github.com/Masterminds/semver/v3 v3.4.0/go.mod h1:4V+yj/TJE1HU9XfppCwVMZq3I84lprf4nC11bSS5beM=
github.com/armon/go-socks5 v0.0.0-20160902184237-e75332964ef5 h1:0CwZNZbxp69SHPdPJAN/hZIm0C4OItdklCFmMRWYpio=
github.com/armon/go-socks5 v0.0.0-20160902184237-e75332964ef5/go.mod h1:wHh0iHkYZB8zMSxRWpUBQtwG5a7fFgvEO+odwuTv2gs=
github.com/beorn7/perks v1.0.1 h1:VlbKKnNfV8bJzeqoa4cOKqO6bYr3WgKZxO8Z16+hsOM=
@ -23,8 +25,8 @@ github.com/emicklei/go-restful/v3 v3.12.1 h1:PJMDIM/ak7btuL8Ex0iYET9hxM3CI2sjZtz
github.com/emicklei/go-restful/v3 v3.12.1/go.mod h1:6n3XBCmQQb25CM2LCACGz8ukIrRry+4bhvbpWn3mrbc=
github.com/evanphx/json-patch v5.9.0+incompatible h1:fBXyNpNMuTTDdquAq/uisOr2lShz4oaXpDTX2bLe7ls=
github.com/evanphx/json-patch v5.9.0+incompatible/go.mod h1:50XU6AFN0ol/bzJsmQLiYLvXMP4fmwYFNcr97nuDLSk=
github.com/evanphx/json-patch/v5 v5.9.0 h1:kcBlZQbplgElYIlo/n1hJbls2z/1awpXxpRi0/FOJfg=
github.com/evanphx/json-patch/v5 v5.9.0/go.mod h1:VNkHZ/282BpEyt/tObQO8s5CMPmYYq14uClGH4abBuQ=
github.com/evanphx/json-patch/v5 v5.9.11 h1:/8HVnzMq13/3x9TPvjG08wUGqBTmZBsCWzjTM0wiaDU=
github.com/evanphx/json-patch/v5 v5.9.11/go.mod h1:3j+LviiESTElxA4p3EMKAB9HXj3/XEtnUf6OZxqIQTM=
github.com/facebookgo/ensure v0.0.0-20160127193407-b4ab57deab51 h1:0JZ+dUmQeA8IIVUMzysrX4/AKuQwWhV2dYQuPZdvdSQ=
github.com/facebookgo/ensure v0.0.0-20160127193407-b4ab57deab51/go.mod h1:Yg+htXGokKKdzcwhuNDwVvN+uBxDGXJ7G/VN1d8fa64=
github.com/facebookgo/stack v0.0.0-20160209184415-751773369052 h1:JWuenKqqX8nojtoVVWjGfOF9635RETekkoH6Cc9SX0A=
@ -36,13 +38,15 @@ github.com/fsnotify/fsnotify v1.4.9/go.mod h1:znqG4EE+3YCdAaPaxE2ZRY/06pZUdp0tY4
github.com/fsnotify/fsnotify v1.6.0/go.mod h1:sl3t1tCWJFWoRz9R8WJCbQihKKwmorjAbSClcnxKAGw=
github.com/fsnotify/fsnotify v1.7.0 h1:8JEhPFa5W2WU7YfeZzPNqzMP6Lwt7L2715Ggo0nosvA=
github.com/fsnotify/fsnotify v1.7.0/go.mod h1:40Bi/Hjc2AVfZrqy+aj+yEI+/bRxZnMJyTJwOpGvigM=
github.com/fxamacker/cbor/v2 v2.7.0 h1:iM5WgngdRBanHcxugY4JySA0nk1wZorNOpTgCMedv5E=
github.com/fxamacker/cbor/v2 v2.7.0/go.mod h1:pxXPTn3joSm21Gbwsv0w9OSA2y1HFR9qXEeXQVeNoDQ=
github.com/go-chi/chi v4.0.0+incompatible/go.mod h1:eB3wogJHnLi3x/kFX2A+IbTBlXxmMeXJVKy9tTv1XzQ=
github.com/go-chi/chi v4.1.2+incompatible h1:fGFk2Gmi/YKXk0OmGfBh0WgmN3XB8lVnEyNz34tQRec=
github.com/go-chi/chi v4.1.2+incompatible/go.mod h1:eB3wogJHnLi3x/kFX2A+IbTBlXxmMeXJVKy9tTv1XzQ=
github.com/go-errors/errors v1.5.1 h1:ZwEMSLRCapFLflTpT7NKaAc7ukJ8ZPEjzlxt8rPN8bk=
github.com/go-errors/errors v1.5.1/go.mod h1:sIVyrIiJhuEF+Pj9Ebtd6P/rEYROXFi3BopGUQ5a5Og=
github.com/go-logr/logr v1.4.2 h1:6pFjapn8bFcIbiKo3XT4j/BhANplGihG6tvd+8rYgrY=
github.com/go-logr/logr v1.4.2/go.mod h1:9T104GzyrTigFIr8wt5mBrctHMim0Nb2HLGrmQ40KvY=
github.com/go-logr/logr v1.4.3 h1:CjnDlHq8ikf6E492q6eKboGOC0T8CDaOvkHCIg8idEI=
github.com/go-logr/logr v1.4.3/go.mod h1:9T104GzyrTigFIr8wt5mBrctHMim0Nb2HLGrmQ40KvY=
github.com/go-logr/zapr v1.3.0 h1:XGdV8XW8zdwFiwOA2Dryh1gj2KRQyOOoNmBy4EplIcQ=
github.com/go-logr/zapr v1.3.0/go.mod h1:YKepepNBd1u/oyhd/yQmtjVXmm9uML4IXUgMOwR8/Gg=
github.com/go-openapi/jsonpointer v0.21.0 h1:YgdVicSA9vH5RiHs9TZW5oyafXZFc6+2Vc1rr/O9oNQ=
@ -51,13 +55,12 @@ github.com/go-openapi/jsonreference v0.21.0 h1:Rs+Y7hSXT83Jacb7kFyjn4ijOuVGSvOdF
github.com/go-openapi/jsonreference v0.21.0/go.mod h1:LmZmgsrTkVg9LG4EaHeY8cBDslNPMo06cago5JNLkm4=
github.com/go-openapi/swag v0.23.0 h1:vsEVJDUo2hPJ2tu0/Xc+4noaxyEffXNIs3cOULZ+GrE=
github.com/go-openapi/swag v0.23.0/go.mod h1:esZ8ITTYEsH1V2trKHjAN8Ai7xHb8RV+YSZ577vPjgQ=
github.com/go-task/slim-sprig v0.0.0-20230315185526-52ccab3ef572 h1:tfuBGBXKqDEevZMzYi5KSi8KkcZtzBcTgAUUtapy0OI=
github.com/go-task/slim-sprig v0.0.0-20210107165309-348f09dbbbc0 h1:p104kn46Q8WdvHunIJ9dAyjPVtrBPhSr3KT2yUst43I=
github.com/go-task/slim-sprig v0.0.0-20210107165309-348f09dbbbc0/go.mod h1:fyg7847qk6SyHyPtNmDHnmrv/HOrqktSC+C9fM+CJOE=
github.com/go-task/slim-sprig/v3 v3.0.0 h1:sUs3vkvUymDpBKi3qH1YSqBQk9+9D/8M2mN1vB6EwHI=
github.com/go-task/slim-sprig/v3 v3.0.0/go.mod h1:W848ghGpv3Qj3dhTPRyJypKRiqCdHZiAzKg9hl15HA8=
github.com/gogo/protobuf v1.3.2 h1:Ov1cvc58UF3b5XjBnZv7+opcTcQFZebYjWzi34vdm4Q=
github.com/gogo/protobuf v1.3.2/go.mod h1:P1XiOD3dCwIKUDQYPy72D8LYyHL2YPYrpS2s69NZV8Q=
github.com/golang/groupcache v0.0.0-20210331224755-41bb18bfe9da h1:oI5xCqsCo564l8iNU+DwB5epxmsaqB+rhGL0m5jtYqE=
github.com/golang/groupcache v0.0.0-20210331224755-41bb18bfe9da/go.mod h1:cIg4eruTrX1D+g88fzRXU5OdNfaM+9IcxsU14FzY7Hc=
github.com/golang/mock v1.6.0 h1:ErTB+efbowRARo13NNdxyJji2egdxLGQhRaY+DUumQc=
github.com/golang/mock v1.6.0/go.mod h1:p6yTPP+5HYm5mzsMV8JkE6ZKdX+/wYM6Hr+LicevLPs=
github.com/golang/protobuf v1.2.0/go.mod h1:6lQm79b+lXiMfvg/cZm0SGofjICqVBUtrP5yJMmIC1U=
@ -67,34 +70,30 @@ github.com/golang/protobuf v1.4.0-rc.2/go.mod h1:LlEzMj4AhA7rCAGe4KMBDvJI+AwstrU
github.com/golang/protobuf v1.4.0-rc.4.0.20200313231945-b860323f09d0/go.mod h1:WU3c8KckQ9AFe+yFwt9sWVRKCVIyN9cPHBJSNnbL67w=
github.com/golang/protobuf v1.4.0/go.mod h1:jodUvKwWbYaEsadDk5Fwe5c77LiNKVO9IDvqG2KuDX0=
github.com/golang/protobuf v1.4.2/go.mod h1:oDoupMAO8OvCJWAcko0GGGIgR6R6ocIYbsSw735rRwI=
github.com/golang/protobuf v1.5.4 h1:i7eJL8qZTpSEXOPTxNKhASYpMn+8e5Q6AdndVa1dWek=
github.com/golang/protobuf v1.5.4/go.mod h1:lnTiLA8Wa4RWRcIUkrtSVa5nRhsEGBg48fD6rSs7xps=
github.com/google/btree v1.1.2 h1:xf4v41cLI2Z6FxbKm+8Bu+m8ifhj15JuZ9sa0jZCMUU=
github.com/google/btree v1.1.2/go.mod h1:qOPhT0dTNdNzV6Z/lhRX0YXUafgPLFUh+gZMl761Gm4=
github.com/google/gnostic-models v0.6.8 h1:yo/ABAfM5IMRsS1VnXjTBvUb61tFIHozhlYvRgGre9I=
github.com/google/gnostic-models v0.6.8/go.mod h1:5n7qKqH0f5wFt+aWF8CW6pZLLNOfYuF5OpfBSENuI8U=
github.com/google/btree v1.1.3 h1:CVpQJjYgC4VbzxeGVHfvZrv1ctoYCAI8vbl07Fcxlyg=
github.com/google/btree v1.1.3/go.mod h1:qOPhT0dTNdNzV6Z/lhRX0YXUafgPLFUh+gZMl761Gm4=
github.com/google/gnostic-models v0.6.9 h1:MU/8wDLif2qCXZmzncUQ/BOfxWfthHi63KqpoNbWqVw=
github.com/google/gnostic-models v0.6.9/go.mod h1:CiWsm0s6BSQd1hRn8/QmxqB6BesYcbSZxsz9b0KuDBw=
github.com/google/go-cmp v0.3.0/go.mod h1:8QqcDgzrUqlUb/G2PQTWiueGozuR1884gddMywk6iLU=
github.com/google/go-cmp v0.3.1/go.mod h1:8QqcDgzrUqlUb/G2PQTWiueGozuR1884gddMywk6iLU=
github.com/google/go-cmp v0.4.0/go.mod h1:v8dTdLbMG2kIc/vJvl+f65V22dbkXbowE6jgT/gNBxE=
github.com/google/go-cmp v0.5.9/go.mod h1:17dUlkBOakJ0+DkrSSNjCkIjxS6bF9zb3elmeNGIjoY=
github.com/google/go-cmp v0.6.0 h1:ofyhxvXcZhMsU5ulbFiLKl/XBFqE1GSq7atu8tAmTRI=
github.com/google/go-cmp v0.6.0/go.mod h1:17dUlkBOakJ0+DkrSSNjCkIjxS6bF9zb3elmeNGIjoY=
github.com/google/go-cmp v0.7.0 h1:wk8382ETsv4JYUZwIsn6YpYiWiBsYLSJiTsyBybVuN8=
github.com/google/go-cmp v0.7.0/go.mod h1:pXiqmnSA92OHEEa9HXL2W4E7lf9JzCmGVUdgjX3N/iU=
github.com/google/gofuzz v1.0.0/go.mod h1:dBl0BpW6vV/+mYPU4Po3pmUjxk6FQPldtuIdl/M65Eg=
github.com/google/gofuzz v1.2.0 h1:xRy4A+RhZaiKjJ1bPfwQ8sedCA+YS2YcCHW6ec7JMi0=
github.com/google/gofuzz v1.2.0/go.mod h1:dBl0BpW6vV/+mYPU4Po3pmUjxk6FQPldtuIdl/M65Eg=
github.com/google/pprof v0.0.0-20240721033354-7089f98c1d14 h1:m2fdPWWX/0mdyA1X3XbVTag5NEwmWv0mieoVuRq14A4=
github.com/google/pprof v0.0.0-20240721033354-7089f98c1d14/go.mod h1:K1liHPHnj73Fdn/EKuT8nrFqBihUSKXoLYU0BuatOYo=
github.com/google/pprof v0.0.0-20250403155104-27863c87afa6 h1:BHT72Gu3keYf3ZEu2J0b1vyeLSOYI8bm5wbJM/8yDe8=
github.com/google/pprof v0.0.0-20250403155104-27863c87afa6/go.mod h1:boTsfXsheKC2y+lKOCMpSfarhxDeIzfZG1jqGcPl3cA=
github.com/google/shlex v0.0.0-20191202100458-e7afc7fbc510 h1:El6M4kTTCOh6aBiKaUGG7oYTSPP8MxqL4YI3kZKwcP4=
github.com/google/shlex v0.0.0-20191202100458-e7afc7fbc510/go.mod h1:pupxD2MaaD3pAXIBCelhxNneeOaAeabZDe5s4K6zSpQ=
github.com/google/uuid v1.6.0 h1:NIvaJDMOsjHA8n1jAhLSgzrAzy1Hgr+hNrb57e+94F0=
github.com/google/uuid v1.6.0/go.mod h1:TIyPZe4MgqvfeYDBFedMoGGpEw/LqOeaOT+nhxU+yHo=
github.com/gorilla/websocket v1.5.3 h1:saDtZ6Pbx/0u+bgYQ3q96pZgCzfhKXGPqt7kZ72aNNg=
github.com/gorilla/websocket v1.5.3/go.mod h1:YR8l580nyteQvAITg2hZ9XVh4b55+EU/adAjf1fMHhE=
github.com/gorilla/websocket v1.5.4-0.20250319132907-e064f32e3674 h1:JeSE6pjso5THxAzdVpqr6/geYxZytqFMBCOtn/ujyeo=
github.com/gorilla/websocket v1.5.4-0.20250319132907-e064f32e3674/go.mod h1:r4w70xmWCQKmi1ONH4KIaBptdivuRPyosB9RmPlGEwA=
github.com/gregjones/httpcache v0.0.0-20190611155906-901d90724c79 h1:+ngKgrYPPJrOjhax5N+uePQ0Fh1Z7PheYoUI/0nzkPA=
github.com/gregjones/httpcache v0.0.0-20190611155906-901d90724c79/go.mod h1:FecbI9+v66THATjSRHfNgh1IVFe/9kFxbXtjV0ctIMA=
github.com/hpcloud/tail v1.0.0/go.mod h1:ab1qPbhIpdTxEkNHXyeSf5vhxWSCs/tWer42PpOxQnU=
github.com/imdario/mergo v0.3.16 h1:wwQJbIsHYGMUyLSPrEq1CT16AhnhNJQ51+4fdHUnCl4=
github.com/imdario/mergo v0.3.16/go.mod h1:WBLT9ZmE3lPoWsEzCh9LPo3TiwVN+ZKEjmz+hD27ysY=
github.com/inconshreveable/mousetrap v1.1.0 h1:wN+x4NVGpMsO7ErUn/mUI3vEoE6Jt13X2s0bqwp9tc8=
github.com/inconshreveable/mousetrap v1.1.0/go.mod h1:vpF70FUmC8bwa3OWnCshd2FqLfsEA9PFc4w1p2J65bw=
github.com/josharian/intern v1.0.0 h1:vlS4z54oSdjm0bgjRigI+G1HpF+tI+9rE5LLzOg8HmY=
@ -103,10 +102,14 @@ github.com/json-iterator/go v1.1.12 h1:PV8peI4a0ysnczrg+LtxykD8LfKY9ML6u2jnxaEnr
github.com/json-iterator/go v1.1.12/go.mod h1:e30LSqwooZae/UwlEbR2852Gd8hjQvJoHmT4TnhNGBo=
github.com/kisielk/errcheck v1.5.0/go.mod h1:pFxgyoBC7bSaBwPgfKdkLd5X25qrDl4LWUI2bnpBCr8=
github.com/kisielk/gotool v1.0.0/go.mod h1:XhKaO+MFFWcvkIS/tQcRk01m1F5IRFswLeQ+oQHNcck=
github.com/klauspost/compress v1.18.0 h1:c/Cqfb0r+Yi+JtIEq73FWXVkRonBlf0CRNYc8Zttxdo=
github.com/klauspost/compress v1.18.0/go.mod h1:2Pp+KzxcywXVXMr50+X0Q/Lsb43OQHYWRCY2AiWywWQ=
github.com/kr/pretty v0.3.1 h1:flRD4NNwYAUpkphVc1HcthR4KEIFJ65n8Mw5qdRn3LE=
github.com/kr/pretty v0.3.1/go.mod h1:hoEshYVHaxMs3cyo3Yncou5ZscifuDolrwPKZanG3xk=
github.com/kr/text v0.2.0 h1:5Nx0Ya0ZqY2ygV366QzturHI13Jq95ApcVaJBhpS+AY=
github.com/kr/text v0.2.0/go.mod h1:eLer722TekiGuMkidMxC/pM04lWEeraHUUmBw8l2grE=
github.com/kylelemons/godebug v1.1.0 h1:RPNrshWIDI6G2gRW9EHilWtl7Z6Sb1BR0xunSBf0SNc=
github.com/kylelemons/godebug v1.1.0/go.mod h1:9/0rRGxNHcop5bhtWyNeEfOS8JIWk580+fNqagV/RAw=
github.com/liggitt/tabwriter v0.0.0-20181228230101-89fcab3d43de h1:9TO3cAIGXtEhnIaL+V+BEER86oLrvS+kWobKpbJuye0=
github.com/liggitt/tabwriter v0.0.0-20181228230101-89fcab3d43de/go.mod h1:zAbeS9B/r2mtpb6U+EI2rYA5OAXxsYw6wTamcNW+zcE=
github.com/mailgun/mailgun-go/v3 v3.6.4 h1:+cvbZRgLSHivbz/w1iWLmxVl6Bqf4geD2D7QMj4+8PE=
@ -114,8 +117,8 @@ github.com/mailgun/mailgun-go/v3 v3.6.4/go.mod h1:ZjVnH8S0dR2BLjvkZc/rxwerdcirzl
github.com/mailru/easyjson v0.0.0-20180823135443-60711f1a8329/go.mod h1:C1wdFJiN94OJF2b5HbByQZoLdCWB1Yqtg26g4irojpc=
github.com/mailru/easyjson v0.7.7 h1:UGYAvKxe3sBsEDzO8ZeWOSlIQfWFlxbzLZe7hwFURr0=
github.com/mailru/easyjson v0.7.7/go.mod h1:xzfreul335JAWq5oZzymOObrkdz5UnU4kGfJJLY9Nlc=
github.com/moby/spdystream v0.4.0 h1:Vy79D6mHeJJjiPdFEL2yku1kl0chZpJfZcPpb16BRl8=
github.com/moby/spdystream v0.4.0/go.mod h1:xBAYlnt/ay+11ShkdFKNAG7LsyK/tmNBVvVOwrfMgdI=
github.com/moby/spdystream v0.5.0 h1:7r0J1Si3QO/kjRitvSLVVFUjxMEb/YLj6S9FF62JBCU=
github.com/moby/spdystream v0.5.0/go.mod h1:xBAYlnt/ay+11ShkdFKNAG7LsyK/tmNBVvVOwrfMgdI=
github.com/moby/term v0.5.0 h1:xt8Q1nalod/v7BqbG21f8mQPqH+xAaC9C3N3wfWbVP0=
github.com/moby/term v0.5.0/go.mod h1:8FzsFHVUBGZdbDsJw/ot+X+d5HLUbvklYLJ9uGfcI3Y=
github.com/modern-go/concurrent v0.0.0-20180228061459-e0a39a4cb421/go.mod h1:6dJC0mAP4ikYIbvyc7fijjWJddQyLn8Ig3JB5CqoB9Q=
@ -130,18 +133,19 @@ github.com/munnerz/goautoneg v0.0.0-20191010083416-a7dc8b61c822/go.mod h1:+n7T8m
github.com/mxk/go-flowrate v0.0.0-20140419014527-cca7078d478f h1:y5//uYreIhSUg3J1GEMiLbxo1LJaP8RfCpH6pymGZus=
github.com/mxk/go-flowrate v0.0.0-20140419014527-cca7078d478f/go.mod h1:ZdcZmHo+o7JKHSa8/e818NopupXU1YMK5fe1lsApnBw=
github.com/nxadm/tail v1.4.4/go.mod h1:kenIhsEOeOJmVchQTgglprH7qJGnHDVpk1VPCcaMI8A=
github.com/nxadm/tail v1.4.8/go.mod h1:+ncqLTQzXmGhMZNUePPaPqPvBxHAIsmXswZKocGu+AU=
github.com/nxadm/tail v1.4.11 h1:8feyoE3OzPrcshW5/MJ4sGESc5cqmGkGCWlco4l0bqY=
github.com/nxadm/tail v1.4.11/go.mod h1:OTaG3NK980DZzxbRq6lEuzgU+mug70nY11sMd4JXXHc=
github.com/onsi/ginkgo v1.6.0/go.mod h1:lLunBs/Ym6LB5Z9jYTR76FiuTmxDTDusOGeTQH+WWjE=
github.com/onsi/ginkgo v1.12.1/go.mod h1:zj2OWP4+oCPe1qIXoGWkgMRwljMUYCdkwsT2108oapk=
github.com/onsi/ginkgo v1.14.1 h1:jMU0WaQrP0a/YAEq8eJmJKjBoMs+pClEr1vDMlM/Do4=
github.com/onsi/ginkgo v1.14.1/go.mod h1:iSB4RoI2tjJc9BBv4NKIKWKya62Rps+oPG/Lv9klQyY=
github.com/onsi/ginkgo/v2 v2.17.2 h1:7eMhcy3GimbsA3hEnVKdw/PQM9XN9krpKVXsZdph0/g=
github.com/onsi/ginkgo/v2 v2.17.2/go.mod h1:nP2DPOQoNsQmsVyv5rDA8JkXQoCs6goXIvr/PRJ1eCc=
github.com/onsi/ginkgo v1.16.5 h1:8xi0RTUf59SOSfEtZMvwTvXYMzG4gV23XVHOZiXNtnE=
github.com/onsi/ginkgo v1.16.5/go.mod h1:+E8gABHa3K6zRBolWtd+ROzc/U5bkGt0FwiG042wbpU=
github.com/onsi/ginkgo/v2 v2.25.1 h1:Fwp6crTREKM+oA6Cz4MsO8RhKQzs2/gOIVOUscMAfZY=
github.com/onsi/ginkgo/v2 v2.25.1/go.mod h1:ppTWQ1dh9KM/F1XgpeRqelR+zHVwV81DGRSDnFxK7Sk=
github.com/onsi/gomega v1.7.1/go.mod h1:XdKZgCCFLUoM/7CFJVPcG8C1xQ1AJ0vpAezJrB7JYyY=
github.com/onsi/gomega v1.10.1/go.mod h1:iN09h71vgCQne3DLsj+A5owkum+a2tYe+TOCB1ybHNo=
github.com/onsi/gomega v1.33.1 h1:dsYjIxxSR755MDmKVsaFQTE22ChNBcuuTWgkUDSubOk=
github.com/onsi/gomega v1.33.1/go.mod h1:U4R44UsT+9eLIaYRB2a5qajjtQYn0hauxvRm16AVYg0=
github.com/onsi/gomega v1.38.2 h1:eZCjf2xjZAqe+LeWvKb5weQ+NcPwX84kqJ0cZNxok2A=
github.com/onsi/gomega v1.38.2/go.mod h1:W2MJcYxRGV63b418Ai34Ud0hEdTVXq9NW9+Sx6uXf3k=
github.com/opencontainers/go-digest v1.0.0 h1:apOUWs51W5PlhuyGyz9FCeeBIOUDA/6nW8Oi/yOhh5U=
github.com/opencontainers/go-digest v1.0.0/go.mod h1:0JzlMkj0TRzQZfJkVvzbP0HBR3IKzErnv2BNG4W4MAM=
github.com/openshift/api v3.9.0+incompatible h1:fJ/KsefYuZAjmrr3+5U9yZIZbTOpVkDDLDLFresAeYs=
@ -154,16 +158,16 @@ github.com/pkg/errors v0.9.1/go.mod h1:bwawxfHBFNV+L2hUp1rHADufV3IMtnDRdf1r5NINE
github.com/pmezard/go-difflib v1.0.0/go.mod h1:iKH77koFhYxTK1pcRnkKkqfTogsbg7gZNVY4sRDYZ/4=
github.com/pmezard/go-difflib v1.0.1-0.20181226105442-5d4384ee4fb2 h1:Jamvg5psRIccs7FGNTlIRMkT8wgtp5eCXdBlqhYGL6U=
github.com/pmezard/go-difflib v1.0.1-0.20181226105442-5d4384ee4fb2/go.mod h1:iKH77koFhYxTK1pcRnkKkqfTogsbg7gZNVY4sRDYZ/4=
github.com/prometheus/client_golang v1.19.1 h1:wZWJDwK+NameRJuPGDhlnFgx8e8HN3XHQeLaYJFJBOE=
github.com/prometheus/client_golang v1.19.1/go.mod h1:mP78NwGzrVks5S2H6ab8+ZZGJLZUq1hoULYBAYBw1Ho=
github.com/prometheus/client_golang v1.22.0 h1:rb93p9lokFEsctTys46VnV1kLCDpVZ0a/Y92Vm0Zc6Q=
github.com/prometheus/client_golang v1.22.0/go.mod h1:R7ljNsLXhuQXYZYtw6GAE9AZg8Y7vEW5scdCXrWRXC0=
github.com/prometheus/client_model v0.6.1 h1:ZKSh/rekM+n3CeS952MLRAdFwIKqeY8b62p8ais2e9E=
github.com/prometheus/client_model v0.6.1/go.mod h1:OrxVMOVHjw3lKMa8+x6HeMGkHMQyHDk9E3jmP2AmGiY=
github.com/prometheus/common v0.55.0 h1:KEi6DK7lXW/m7Ig5i47x0vRzuBsHuvJdi5ee6Y3G1dc=
github.com/prometheus/common v0.55.0/go.mod h1:2SECS4xJG1kd8XF9IcM1gMX6510RAEL65zxzNImwdc8=
github.com/prometheus/common v0.62.0 h1:xasJaQlnWAeyHdUBeGjXmutelfJHWMRr+Fg4QszZ2Io=
github.com/prometheus/common v0.62.0/go.mod h1:vyBcEuLSvWos9B1+CyL7JZ2up+uFzXhkqml0W5zIY1I=
github.com/prometheus/procfs v0.15.1 h1:YagwOFzUgYfKKHX6Dr+sHT7km/hxC76UB0learggepc=
github.com/prometheus/procfs v0.15.1/go.mod h1:fB45yRUv8NstnjriLhBQLuOUt+WW4BsoGhij/e3PBqk=
github.com/rogpeppe/go-internal v1.11.0 h1:cWPaGQEPrBb5/AsnsZesgZZ9yb1OQ+GOISoDNXVBh4M=
github.com/rogpeppe/go-internal v1.11.0/go.mod h1:ddIwULY96R17DhadqLgMfk9H9tvdUzkipdSkR5nkCZA=
github.com/rogpeppe/go-internal v1.13.1 h1:KvO1DLK/DRN07sQ1LQKScxyZJuNnedQ5/wKSR38lUII=
github.com/rogpeppe/go-internal v1.13.1/go.mod h1:uMEvuHeurkdAXX61udpOXGD/AzZDWNMNyH2VO9fmH0o=
github.com/russross/blackfriday/v2 v2.1.0/go.mod h1:+Rmxgy9KzJVeS9/2gXHxylqXiyQDYRxCVz55jmeOWTM=
github.com/sergi/go-diff v1.2.0 h1:XU+rvMAioB0UC3q1MFrIQy4Vo5/4VsRDQQXHsEya6xQ=
github.com/sergi/go-diff v1.2.0/go.mod h1:STckp+ISIX8hZLjrqAeVduY0gWCT9IjLuqbuNXdaHfM=
@ -176,34 +180,37 @@ github.com/stretchr/objx v0.5.2 h1:xuMeJ0Sdp5ZMRXx/aWO6RZxdr3beISkG5/G/aIRr3pY=
github.com/stretchr/objx v0.5.2/go.mod h1:FRsXN1f5AsAjCGJKqEizvkpNtU+EGNCLh3NxZ/8L+MA=
github.com/stretchr/testify v1.3.0/go.mod h1:M5WIy9Dh21IEIfnGCwXGc5bZfKNJtfHm1UVUgZn+9EI=
github.com/stretchr/testify v1.4.0/go.mod h1:j7eGeouHqKxXV5pUuKE4zz7dFj8WfuZ+81PSLYec5m4=
github.com/stretchr/testify v1.5.1/go.mod h1:5W2xD1RspED5o8YsWQXVCued0rvSQ+mT+I5cxcmMvtA=
github.com/stretchr/testify v1.7.0/go.mod h1:6Fq8oRcR53rry900zMqJjRRixrwX3KX962/h/Wwjteg=
github.com/stretchr/testify v1.9.0 h1:HtqpIVDClZ4nwg75+f6Lvsy/wHu+3BoSGCbBAcpTsTg=
github.com/stretchr/testify v1.9.0/go.mod h1:r2ic/lqez/lEtzL7wO/rwa5dbSLXVDPFyf8C91i36aY=
github.com/stretchr/testify v1.11.1 h1:7s2iGBzp5EwR7/aIZr8ao5+dra3wiQyKjjFuvgVKu7U=
github.com/stretchr/testify v1.11.1/go.mod h1:wZwfW3scLgRK+23gO65QZefKpKQRnfz6sD981Nm4B6U=
github.com/x448/float16 v0.8.4 h1:qLwI1I70+NjRFUR3zs1JPUCgaCXSh3SW62uAKT1mSBM=
github.com/x448/float16 v0.8.4/go.mod h1:14CWIYCyZA/cWjXOioeEpHeN/83MdbZDRQHoFcYsOfg=
github.com/xlab/treeprint v1.2.0 h1:HzHnuAF1plUN2zGlAFHbSQP2qJ0ZAD3XF5XD7OesXRQ=
github.com/xlab/treeprint v1.2.0/go.mod h1:gj5Gd3gPdKtR1ikdDK6fnFLdmIS0X30kTTuNd/WEJu0=
github.com/yuin/goldmark v1.1.27/go.mod h1:3hX8gzYuyVAZsxl0MRgGTJEmQBFcNTphYh9decYSb74=
github.com/yuin/goldmark v1.2.1/go.mod h1:3hX8gzYuyVAZsxl0MRgGTJEmQBFcNTphYh9decYSb74=
github.com/yuin/goldmark v1.3.5/go.mod h1:mwnBkeHKe2W/ZEtQ+71ViKU8L12m81fl3OWwC1Zlc8k=
go.starlark.net v0.0.0-20240705175910-70002002b310 h1:tEAOMoNmN2MqVNi0MMEWpTtPI4YNCXgxmAGtuv3mST0=
go.starlark.net v0.0.0-20240705175910-70002002b310/go.mod h1:YKMCv9b1WrfWmeqdV5MAuEHWsu5iC+fe6kYl2sQjdI8=
go.uber.org/automaxprocs v1.6.0 h1:O3y2/QNTOdbF+e/dpXNNW7Rx2hZ4sTIPyybbxyNqTUs=
go.uber.org/automaxprocs v1.6.0/go.mod h1:ifeIMSnPZuznNm6jmdzmU3/bfk01Fe2fotchwEFJ8r8=
go.uber.org/goleak v1.3.0 h1:2K3zAYmnTNqV73imy9J1T3WC+gmCePx2hEGkimedGto=
go.uber.org/goleak v1.3.0/go.mod h1:CoHD4mav9JJNrW/WLlf7HGZPjdw8EucARQHekz1X6bE=
go.uber.org/multierr v1.11.0 h1:blXXJkSxSSfBVBlC76pxqeO+LN3aDfLQo+309xJstO0=
go.uber.org/multierr v1.11.0/go.mod h1:20+QtiLqy0Nd6FdQB9TLXag12DsQkrbs3htMFfDN80Y=
go.uber.org/zap v1.27.0 h1:aJMhYGrd5QSmlpLMr2MftRKl7t8J8PTZPA732ud/XR8=
go.uber.org/zap v1.27.0/go.mod h1:GB2qFLM7cTU87MWRP2mPIjqfIDnGu+VIO4V/SdhGo2E=
go.yaml.in/yaml/v3 v3.0.4 h1:tfq32ie2Jv2UxXFdLJdh3jXuOzWiL1fo0bu/FbuKpbc=
go.yaml.in/yaml/v3 v3.0.4/go.mod h1:DhzuOOF2ATzADvBadXxruRBLzYTpT36CKvDb3+aBEFg=
golang.org/x/crypto v0.0.0-20190308221718-c2843e01d9a2/go.mod h1:djNgcEr1/C05ACkg1iLfiJU5Ep61QUkGW8qpdssI0+w=
golang.org/x/crypto v0.0.0-20191011191535-87dc89f01550/go.mod h1:yigFU9vqHzYiE8UmvKecakEJjdnWj3jj499lnFckfCI=
golang.org/x/crypto v0.0.0-20200622213623-75b288015ac9/go.mod h1:LzIPMQfyMNhhGPhUkYOs5KpL4U8rLKemX1yGLhDgUto=
golang.org/x/crypto v0.31.0 h1:ihbySMvVjLAeSH1IbfcRTkD/iNscyz8rGzjF/E5hV6U=
golang.org/x/crypto v0.31.0/go.mod h1:kDsLvtWBEx7MV9tJOj9bnXsPbxwJQ6csT/x4KIN4Ssk=
golang.org/x/exp v0.0.0-20240719175910-8a7402abbf56 h1:2dVuKD2vS7b0QIHQbpyTISPd0LeHDbnYEryqj5Q1ug8=
golang.org/x/exp v0.0.0-20240719175910-8a7402abbf56/go.mod h1:M4RDyNAINzryxdtnbRXRL/OHtkFuWGRjvuhBJpk2IlY=
golang.org/x/crypto v0.41.0 h1:WKYxWedPGCTVVl5+WHSSrOBT0O8lx32+zxmHxijgXp4=
golang.org/x/crypto v0.41.0/go.mod h1:pO5AFd7FA68rFak7rOAGVuygIISepHftHnr8dr6+sUc=
golang.org/x/mod v0.2.0/go.mod h1:s0Qsj1ACt9ePp/hMypM3fl4fZqREWJwdYDEqhRiZZUA=
golang.org/x/mod v0.3.0/go.mod h1:s0Qsj1ACt9ePp/hMypM3fl4fZqREWJwdYDEqhRiZZUA=
golang.org/x/mod v0.4.2/go.mod h1:s0Qsj1ACt9ePp/hMypM3fl4fZqREWJwdYDEqhRiZZUA=
golang.org/x/mod v0.19.0 h1:fEdghXQSo20giMthA7cd28ZC+jts4amQ3YMXiP5oMQ8=
golang.org/x/mod v0.19.0/go.mod h1:hTbmBsO62+eylJbnUtE2MGJUyE7QWk4xUqPFrRgJ+7c=
golang.org/x/mod v0.26.0 h1:EGMPT//Ezu+ylkCijjPc+f4Aih7sZvaAr+O3EHBxvZg=
golang.org/x/mod v0.26.0/go.mod h1:/j6NAhSk8iQ723BGAUyoAcn7SlD7s15Dp9Nd/SfeaFQ=
golang.org/x/net v0.0.0-20180906233101-161cd47e91fd/go.mod h1:mL1N/T3taQHkDXs73rZJwtUhF3w3ftmwwsq0BUmARs4=
golang.org/x/net v0.0.0-20190404232315-eb5bcb51f2a3/go.mod h1:t9HGtf8HONx5eT2rtn7q6eTqICYqUVnKs3thJo3Qplg=
golang.org/x/net v0.0.0-20190620200207-3b0461eec859/go.mod h1:z5CRVTTTmAJ677TzLLGU+0bjPO0LkuOLi4/5GtJWs/s=
@ -212,17 +219,17 @@ golang.org/x/net v0.0.0-20200226121028-0de0cce0169b/go.mod h1:z5CRVTTTmAJ677TzLL
golang.org/x/net v0.0.0-20200520004742-59133d7f0dd7/go.mod h1:qpuaurCH72eLCgpAm/N6yyVIVM9cpaDIP3A8BGJEC5A=
golang.org/x/net v0.0.0-20201021035429-f5854403a974/go.mod h1:sp8m0HH+o8qH0wwXwYZr8TS3Oi6o0r6Gce1SSxlDquU=
golang.org/x/net v0.0.0-20210405180319-a5a99cb37ef4/go.mod h1:p54w0d4576C0XHj96bSt6lcn1PtDYWL6XObtHCRCNQM=
golang.org/x/net v0.27.0 h1:5K3Njcw06/l2y9vpGCSdcxWOYHOUk3dVNGDXN+FvAys=
golang.org/x/net v0.27.0/go.mod h1:dDi0PyhWNoiUOrAS8uXv/vnScO4wnHQO4mj9fn/RytE=
golang.org/x/oauth2 v0.21.0 h1:tsimM75w1tF/uws5rbeHzIWxEqElMehnc+iW793zsZs=
golang.org/x/oauth2 v0.21.0/go.mod h1:XYTD2NtWslqkgxebSiOHnXEap4TF09sJSc7H1sXbhtI=
golang.org/x/net v0.43.0 h1:lat02VYK2j4aLzMzecihNvTlJNQUq316m2Mr9rnM6YE=
golang.org/x/net v0.43.0/go.mod h1:vhO1fvI4dGsIjh73sWfUVjj3N7CA9WkKJNQm2svM6Jg=
golang.org/x/oauth2 v0.27.0 h1:da9Vo7/tDv5RH/7nZDz1eMGS/q1Vv1N/7FCrBhI9I3M=
golang.org/x/oauth2 v0.27.0/go.mod h1:onh5ek6nERTohokkhCD/y2cV4Do3fxFHFuAejCkRWT8=
golang.org/x/sync v0.0.0-20180314180146-1d60e4601c6f/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM=
golang.org/x/sync v0.0.0-20190423024810-112230192c58/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM=
golang.org/x/sync v0.0.0-20190911185100-cd5d95a43a6e/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM=
golang.org/x/sync v0.0.0-20201020160332-67f06af15bc9/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM=
golang.org/x/sync v0.0.0-20210220032951-036812b2e83c/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM=
golang.org/x/sync v0.10.0 h1:3NQrjDixjgGwUOCaF8w2+VYHv0Ve/vGYSbdkTa98gmQ=
golang.org/x/sync v0.10.0/go.mod h1:Czt+wKu1gCyEFDUtn0jG5QVvpJ6rzVqr5aXyt9drQfk=
golang.org/x/sync v0.16.0 h1:ycBJEhp9p4vXvUZNszeOq0kGTPghopOL8q0fq3vstxw=
golang.org/x/sync v0.16.0/go.mod h1:1dzgHSNfp02xaA81J2MS99Qcpr2w7fw1gpm99rleRqA=
golang.org/x/sys v0.0.0-20180909124046-d0be0721c37e/go.mod h1:STP8DvDyc/dI5b8T5hshtkjS+E42TnysNCUPdjciGhY=
golang.org/x/sys v0.0.0-20190215142949-d0b11bdaac8a/go.mod h1:STP8DvDyc/dI5b8T5hshtkjS+E42TnysNCUPdjciGhY=
golang.org/x/sys v0.0.0-20190412213103-97732733099d/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
@ -230,32 +237,32 @@ golang.org/x/sys v0.0.0-20190904154756-749cb33beabd/go.mod h1:h1NjWce9XRLGQEsW7w
golang.org/x/sys v0.0.0-20191005200804-aed5e4c7ecf9/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
golang.org/x/sys v0.0.0-20191120155948-bd437916bb0e/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
golang.org/x/sys v0.0.0-20200323222414-85ca7c5b95cd/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
golang.org/x/sys v0.0.0-20200519105757-fe76b779f299/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
golang.org/x/sys v0.0.0-20200930185726-fdedc70b468f/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
golang.org/x/sys v0.0.0-20201119102817-f84b799fce68/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
golang.org/x/sys v0.0.0-20210112080510-489259a85091/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
golang.org/x/sys v0.0.0-20210330210617-4fbd30eecc44/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
golang.org/x/sys v0.0.0-20210510120138-977fb7262007/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg=
golang.org/x/sys v0.0.0-20210616094352-59db8d763f22/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg=
golang.org/x/sys v0.0.0-20220908164124-27713097b956/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg=
golang.org/x/sys v0.28.0 h1:Fksou7UEQUWlKvIdsqzJmUmCX3cZuD2+P3XyyzwMhlA=
golang.org/x/sys v0.28.0/go.mod h1:/VUhepiaJMQUp4+oa/7Zr1D23ma6VTLIYjOOTFZPUcA=
golang.org/x/sys v0.35.0 h1:vz1N37gP5bs89s7He8XuIYXpyY0+QlsKmzipCbUtyxI=
golang.org/x/sys v0.35.0/go.mod h1:BJP2sWEmIv4KK5OTEluFJCKSidICx8ciO85XgH3Ak8k=
golang.org/x/term v0.0.0-20201126162022-7de9c90e9dd1/go.mod h1:bj7SfCRtBDWHUb9snDiAeCFNEtKQo2Wmx5Cou7ajbmo=
golang.org/x/term v0.27.0 h1:WP60Sv1nlK1T6SupCHbXzSaN0b9wUmsPoRS9b61A23Q=
golang.org/x/term v0.27.0/go.mod h1:iMsnZpn0cago0GOrHO2+Y7u7JPn5AylBrcoWkElMTSM=
golang.org/x/term v0.34.0 h1:O/2T7POpk0ZZ7MAzMeWFSg6S5IpWd/RXDlM9hgM3DR4=
golang.org/x/term v0.34.0/go.mod h1:5jC53AEywhIVebHgPVeg0mj8OD3VO9OzclacVrqpaAw=
golang.org/x/text v0.3.0/go.mod h1:NqM8EUOU14njkJ3fqMW+pc6Ldnwhi/IjpwHt7yyuwOQ=
golang.org/x/text v0.3.2/go.mod h1:bEr9sfX3Q8Zfm5fL9x+3itogRgK3+ptLWKqgva+5dAk=
golang.org/x/text v0.3.3/go.mod h1:5Zoc/QRtKVWzQhOtBMvqHzDpF6irO9z98xDceosuGiQ=
golang.org/x/text v0.21.0 h1:zyQAAkrwaneQ066sspRyJaG9VNi/YJ1NfzcGB3hZ/qo=
golang.org/x/text v0.21.0/go.mod h1:4IBbMaMmOPCJ8SecivzSH54+73PCFmPWxNTLm+vZkEQ=
golang.org/x/time v0.5.0 h1:o7cqy6amK/52YcAKIPlM3a+Fpj35zvRj2TP+e1xFSfk=
golang.org/x/time v0.5.0/go.mod h1:3BpzKBy/shNhVucY/MWOyx10tF3SFh9QdLuxbVysPQM=
golang.org/x/text v0.28.0 h1:rhazDwis8INMIwQ4tpjLDzUhx6RlXqZNPEM0huQojng=
golang.org/x/text v0.28.0/go.mod h1:U8nCwOR8jO/marOQ0QbDiOngZVEBB7MAiitBuMjXiNU=
golang.org/x/time v0.9.0 h1:EsRrnYcQiGH+5FfbgvV4AP7qEZstoyrHB0DzarOQ4ZY=
golang.org/x/time v0.9.0/go.mod h1:3BpzKBy/shNhVucY/MWOyx10tF3SFh9QdLuxbVysPQM=
golang.org/x/tools v0.0.0-20180917221912-90fa682c2a6e/go.mod h1:n7NCudcB/nEzxVGmLbDWY5pfWTLqBcC2KZ6jyYvM4mQ=
golang.org/x/tools v0.0.0-20191119224855-298f0cb1881e/go.mod h1:b+2E5dAYhXwXZwtnZ6UAqBI28+e2cm9otk0dWdXHAEo=
golang.org/x/tools v0.0.0-20200619180055-7c47624df98f/go.mod h1:EkVYQZoAsY45+roYkvgYkIh4xh/qjgUK9TdY2XT94GE=
golang.org/x/tools v0.0.0-20201224043029-2b0845dc783e/go.mod h1:emZCQorbCU4vsT4fOWvOPXz4eW1wZW4PmDk9uLelYpA=
golang.org/x/tools v0.0.0-20210106214847-113979e3529a/go.mod h1:emZCQorbCU4vsT4fOWvOPXz4eW1wZW4PmDk9uLelYpA=
golang.org/x/tools v0.1.1/go.mod h1:o0xws9oXOQQZyjljx8fwUC0k7L1pTE6eaCbjGeHmOkk=
golang.org/x/tools v0.23.0 h1:SGsXPZ+2l4JsgaCKkx+FQ9YZ5XEtA1GZYuoDjenLjvg=
golang.org/x/tools v0.23.0/go.mod h1:pnu6ufv6vQkll6szChhK3C3L/ruaIv5eBeztNG8wtsI=
golang.org/x/tools v0.36.0 h1:kWS0uv/zsvHEle1LbV5LE8QujrxB3wfQyxHfhOk0Qkg=
golang.org/x/tools v0.36.0/go.mod h1:WBDiHKJK8YgLHlcQPYQzNCkUxUypCaa5ZegCVutKm+s=
golang.org/x/xerrors v0.0.0-20190717185122-a985d3407aa7/go.mod h1:I/5z698sn9Ka8TeJc9MKroUUfqBBauWjQqLJ2OPfmY0=
golang.org/x/xerrors v0.0.0-20191011141410-1b5146add898/go.mod h1:I/5z698sn9Ka8TeJc9MKroUUfqBBauWjQqLJ2OPfmY0=
golang.org/x/xerrors v0.0.0-20191204190536-9bdfabe68543/go.mod h1:I/5z698sn9Ka8TeJc9MKroUUfqBBauWjQqLJ2OPfmY0=
@ -268,8 +275,8 @@ google.golang.org/protobuf v0.0.0-20200228230310-ab0ca4ff8a60/go.mod h1:cfTl7dwQ
google.golang.org/protobuf v1.20.1-0.20200309200217-e05f789c0967/go.mod h1:A+miEFZTKqfCUM6K7xSMQL9OKL/b6hQv+e19PK+JZNE=
google.golang.org/protobuf v1.21.0/go.mod h1:47Nbq4nVaFHyn7ilMalzfO3qCViNmqZ2kzikPIcrTAo=
google.golang.org/protobuf v1.23.0/go.mod h1:EGpADcykh3NcUnDUJcl1+ZksZNG86OlYog2l/sGQquU=
google.golang.org/protobuf v1.34.2 h1:6xV6lTsCfpGD21XK49h7MhtcApnLqkfYgPcdHftf6hg=
google.golang.org/protobuf v1.34.2/go.mod h1:qYOHts0dSfpeUzUFpOMr/WGzszTmLH+DiWniOlNbLDw=
google.golang.org/protobuf v1.36.7 h1:IgrO7UwFQGJdRNXH/sQux4R1Dj1WAKcLElzeeRaXV2A=
google.golang.org/protobuf v1.36.7/go.mod h1:jduwjTPXsFjZGTmRluh+L6NjiWu7pchiJ2/5YcXBHnY=
gopkg.in/alexcesaro/quotedprintable.v3 v3.0.0-20150716171945-2caba252f4dc h1:2gGKlE2+asNV9m7xrywl36YYNnBG5ZQ0r/BOOxqPpmk=
gopkg.in/alexcesaro/quotedprintable.v3 v3.0.0-20150716171945-2caba252f4dc/go.mod h1:m7x9LTH6d71AHyAX77c9yqWCCa3UKHcVEj9y7hAtKDk=
gopkg.in/check.v1 v0.0.0-20161208181325-20d25e280405/go.mod h1:Co6ibVJAznAaIkqp8huTwlJQCZ016jof/cbN4VW5Yz0=
@ -286,38 +293,38 @@ gopkg.in/tomb.v1 v1.0.0-20141024135613-dd632973f1e7 h1:uRGJdciOHaEIrze2W8Q3AKkep
gopkg.in/tomb.v1 v1.0.0-20141024135613-dd632973f1e7/go.mod h1:dt/ZhP58zS4L8KSrWDmTeBkI65Dw0HsyUHuEVlX15mw=
gopkg.in/yaml.v2 v2.2.2/go.mod h1:hI93XBmqTisBFMUTm0b8Fm+jr3Dg1NNxqwp+5A1VGuI=
gopkg.in/yaml.v2 v2.2.4/go.mod h1:hI93XBmqTisBFMUTm0b8Fm+jr3Dg1NNxqwp+5A1VGuI=
gopkg.in/yaml.v2 v2.2.8/go.mod h1:hI93XBmqTisBFMUTm0b8Fm+jr3Dg1NNxqwp+5A1VGuI=
gopkg.in/yaml.v2 v2.3.0/go.mod h1:hI93XBmqTisBFMUTm0b8Fm+jr3Dg1NNxqwp+5A1VGuI=
gopkg.in/yaml.v2 v2.4.0 h1:D8xgwECY7CYvx+Y2n4sBz93Jn9JRvxdiyyo8CTfuKaY=
gopkg.in/yaml.v2 v2.4.0/go.mod h1:RDklbk79AGWmwhnvt/jBztapEOGDOx6ZbXqjP6csGnQ=
gopkg.in/yaml.v3 v3.0.0-20200313102051-9f266ea9e77c/go.mod h1:K4uyk7z7BCEPqu6E+C64Yfv1cQ7kz7rIZviUmN+EgEM=
gopkg.in/yaml.v3 v3.0.1 h1:fxVm/GzAzEWqLHuvctI91KS9hhNmmWOoWu0XTYJS7CA=
gopkg.in/yaml.v3 v3.0.1/go.mod h1:K4uyk7z7BCEPqu6E+C64Yfv1cQ7kz7rIZviUmN+EgEM=
k8s.io/api v0.30.3 h1:ImHwK9DCsPA9uoU3rVh4QHAHHK5dTSv1nxJUapx8hoQ=
k8s.io/api v0.30.3/go.mod h1:GPc8jlzoe5JG3pb0KJCSLX5oAFIW3/qNJITlDj8BH04=
k8s.io/apiextensions-apiserver v0.30.3 h1:oChu5li2vsZHx2IvnGP3ah8Nj3KyqG3kRSaKmijhB9U=
k8s.io/apiextensions-apiserver v0.30.3/go.mod h1:uhXxYDkMAvl6CJw4lrDN4CPbONkF3+XL9cacCT44kV4=
k8s.io/apimachinery v0.30.3 h1:q1laaWCmrszyQuSQCfNB8cFgCuDAoPszKY4ucAjDwHc=
k8s.io/apimachinery v0.30.3/go.mod h1:iexa2somDaxdnj7bha06bhb43Zpa6eWH8N8dbqVjTUc=
k8s.io/cli-runtime v0.30.3 h1:aG69oRzJuP2Q4o8dm+f5WJIX4ZBEwrvdID0+MXyUY6k=
k8s.io/cli-runtime v0.30.3/go.mod h1:hwrrRdd9P84CXSKzhHxrOivAR9BRnkMt0OeP5mj7X30=
k8s.io/client-go v0.30.3 h1:bHrJu3xQZNXIi8/MoxYtZBBWQQXwy16zqJwloXXfD3k=
k8s.io/client-go v0.30.3/go.mod h1:8d4pf8vYu665/kUbsxWAQ/JDBNWqfFeZnvFiVdmx89U=
k8s.io/api v0.33.0 h1:yTgZVn1XEe6opVpP1FylmNrIFWuDqe2H0V8CT5gxfIU=
k8s.io/api v0.33.0/go.mod h1:CTO61ECK/KU7haa3qq8sarQ0biLq2ju405IZAd9zsiM=
k8s.io/apiextensions-apiserver v0.33.0 h1:d2qpYL7Mngbsc1taA4IjJPRJ9ilnsXIrndH+r9IimOs=
k8s.io/apiextensions-apiserver v0.33.0/go.mod h1:VeJ8u9dEEN+tbETo+lFkwaaZPg6uFKLGj5vyNEwwSzc=
k8s.io/apimachinery v0.33.0 h1:1a6kHrJxb2hs4t8EE5wuR/WxKDwGN1FKH3JvDtA0CIQ=
k8s.io/apimachinery v0.33.0/go.mod h1:BHW0YOu7n22fFv/JkYOEfkUYNRN0fj0BlvMFWA7b+SM=
k8s.io/cli-runtime v0.33.0 h1:Lbl/pq/1o8BaIuyn+aVLdEPHVN665tBAXUePs8wjX7c=
k8s.io/cli-runtime v0.33.0/go.mod h1:QcA+r43HeUM9jXFJx7A+yiTPfCooau/iCcP1wQh4NFw=
k8s.io/client-go v0.33.0 h1:UASR0sAYVUzs2kYuKn/ZakZlcs2bEHaizrrHUZg0G98=
k8s.io/client-go v0.33.0/go.mod h1:kGkd+l/gNGg8GYWAPr0xF1rRKvVWvzh9vmZAMXtaKOg=
k8s.io/klog/v2 v2.130.1 h1:n9Xl7H1Xvksem4KFG4PYbdQCQxqc/tTUyrgXaOhHSzk=
k8s.io/klog/v2 v2.130.1/go.mod h1:3Jpz1GvMt720eyJH1ckRHK1EDfpxISzJ7I9OYgaDtPE=
k8s.io/kube-openapi v0.0.0-20240709000822-3c01b740850f h1:2sXuKesAYbRHxL3aE2PN6zX/gcJr22cjrsej+W784Tc=
k8s.io/kube-openapi v0.0.0-20240709000822-3c01b740850f/go.mod h1:UxDHUPsUwTOOxSU+oXURfFBcAS6JwiRXTYqYwfuGowc=
k8s.io/utils v0.0.0-20240711033017-18e509b52bc8 h1:pUdcCO1Lk/tbT5ztQWOBi5HBgbBP1J8+AsQnQCKsi8A=
k8s.io/utils v0.0.0-20240711033017-18e509b52bc8/go.mod h1:OLgZIPagt7ERELqWJFomSt595RzquPNLL48iOWgYOg0=
sigs.k8s.io/controller-runtime v0.18.4 h1:87+guW1zhvuPLh1PHybKdYFLU0YJp4FhJRmiHvm5BZw=
sigs.k8s.io/controller-runtime v0.18.4/go.mod h1:TVoGrfdpbA9VRFaRnKgk9P5/atA0pMwq+f+msb9M8Sg=
sigs.k8s.io/json v0.0.0-20221116044647-bc3834ca7abd h1:EDPBXCAspyGV4jQlpZSudPeMmr1bNJefnuqLsRAsHZo=
sigs.k8s.io/json v0.0.0-20221116044647-bc3834ca7abd/go.mod h1:B8JuhiUyNFVKdsE8h686QcCxMaH6HrOAZj4vswFpcB0=
sigs.k8s.io/kustomize/api v0.17.3 h1:6GCuHSsxq7fN5yhF2XrC+AAr8gxQwhexgHflOAD/JJU=
sigs.k8s.io/kustomize/api v0.17.3/go.mod h1:TuDH4mdx7jTfK61SQ/j1QZM/QWR+5rmEiNjvYlhzFhc=
sigs.k8s.io/kustomize/kyaml v0.17.2 h1:+AzvoJUY0kq4QAhH/ydPHHMRLijtUKiyVyh7fOSshr0=
sigs.k8s.io/kustomize/kyaml v0.17.2/go.mod h1:9V0mCjIEYjlXuCdYsSXvyoy2BTsLESH7TlGV81S282U=
sigs.k8s.io/structured-merge-diff/v4 v4.4.1 h1:150L+0vs/8DA78h1u02ooW1/fFq/Lwr+sGiqlzvrtq4=
sigs.k8s.io/structured-merge-diff/v4 v4.4.1/go.mod h1:N8hJocpFajUSSeSJ9bOZ77VzejKZaXsTtZo4/u7Io08=
k8s.io/kube-openapi v0.0.0-20250318190949-c8a335a9a2ff h1:/usPimJzUKKu+m+TE36gUyGcf03XZEP0ZIKgKj35LS4=
k8s.io/kube-openapi v0.0.0-20250318190949-c8a335a9a2ff/go.mod h1:5jIi+8yX4RIb8wk3XwBo5Pq2ccx4FP10ohkbSKCZoK8=
k8s.io/utils v0.0.0-20250604170112-4c0f3b243397 h1:hwvWFiBzdWw1FhfY1FooPn3kzWuJ8tmbZBHi4zVsl1Y=
k8s.io/utils v0.0.0-20250604170112-4c0f3b243397/go.mod h1:OLgZIPagt7ERELqWJFomSt595RzquPNLL48iOWgYOg0=
sigs.k8s.io/controller-runtime v0.21.0 h1:CYfjpEuicjUecRk+KAeyYh+ouUBn4llGyDYytIGcJS8=
sigs.k8s.io/controller-runtime v0.21.0/go.mod h1:OSg14+F65eWqIu4DceX7k/+QRAbTTvxeQSNSOQpukWM=
sigs.k8s.io/json v0.0.0-20241010143419-9aa6b5e7a4b3 h1:/Rv+M11QRah1itp8VhT6HoVx1Ray9eB4DBr+K+/sCJ8=
sigs.k8s.io/json v0.0.0-20241010143419-9aa6b5e7a4b3/go.mod h1:18nIHnGi6636UCz6m8i4DhaJ65T6EruyzmoQqI2BVDo=
sigs.k8s.io/kustomize/api v0.19.0 h1:F+2HB2mU1MSiR9Hp1NEgoU2q9ItNOaBJl0I4Dlus5SQ=
sigs.k8s.io/kustomize/api v0.19.0/go.mod h1:/BbwnivGVcBh1r+8m3tH1VNxJmHSk1PzP5fkP6lbL1o=
sigs.k8s.io/kustomize/kyaml v0.19.0 h1:RFge5qsO1uHhwJsu3ipV7RNolC7Uozc0jUBC/61XSlA=
sigs.k8s.io/kustomize/kyaml v0.19.0/go.mod h1:FeKD5jEOH+FbZPpqUghBP8mrLjJ3+zD3/rf9NNu1cwY=
sigs.k8s.io/randfill v0.0.0-20250304075658-069ef1bbf016/go.mod h1:XeLlZ/jmk4i1HRopwe7/aU3H5n1zNUcX6TM94b3QxOY=
sigs.k8s.io/randfill v1.0.0 h1:JfjMILfT8A6RbawdsK2JXGBR5AQVfd+9TbzrlneTyrU=
sigs.k8s.io/randfill v1.0.0/go.mod h1:XeLlZ/jmk4i1HRopwe7/aU3H5n1zNUcX6TM94b3QxOY=
sigs.k8s.io/structured-merge-diff/v4 v4.6.0 h1:IUA9nvMmnKWcj5jl84xn+T5MnlZKThmUW1TdblaLVAc=
sigs.k8s.io/structured-merge-diff/v4 v4.6.0/go.mod h1:dDy58f92j70zLsuZVuUX5Wp9vtxXpaZnkPGWeqDfCps=
sigs.k8s.io/yaml v1.4.0 h1:Mk1wCc2gy/F0THH0TAp1QYyJNzRm2KCLy3o5ASXVI5E=
sigs.k8s.io/yaml v1.4.0/go.mod h1:Ejl7/uTz7PSA4eKMyQCUTnhZYNmLIl+5c2lQPGR2BPY=

View File

@ -12,6 +12,7 @@ import (
metav1 "k8s.io/apimachinery/pkg/apis/meta/v1"
"k8s.io/apimachinery/pkg/types"
"k8s.io/client-go/util/workqueue"
"sigs.k8s.io/controller-runtime/pkg/client"
"sigs.k8s.io/controller-runtime/pkg/event"
"sigs.k8s.io/controller-runtime/pkg/handler"
"sigs.k8s.io/controller-runtime/pkg/reconcile"
@ -20,13 +21,15 @@ import (
// enqueueRequestForJenkins enqueues a Request for Secrets and ConfigMaps created by jenkins-operator.
type enqueueRequestForJenkins struct{}
func (e *enqueueRequestForJenkins) Create(ctx context.Context, evt event.CreateEvent, q workqueue.RateLimitingInterface) {
var _ handler.TypedEventHandler[client.Object, reconcile.Request] = &enqueueRequestForJenkins{}
func (e *enqueueRequestForJenkins) Create(ctx context.Context, evt event.TypedCreateEvent[client.Object], q workqueue.TypedRateLimitingInterface[reconcile.Request]) {
if req := e.getOwnerReconcileRequests(ctx, evt.Object); req != nil {
q.Add(*req)
}
}
func (e *enqueueRequestForJenkins) Update(ctx context.Context, evt event.UpdateEvent, q workqueue.RateLimitingInterface) {
func (e *enqueueRequestForJenkins) Update(ctx context.Context, evt event.TypedUpdateEvent[client.Object], q workqueue.TypedRateLimitingInterface[reconcile.Request]) {
req1 := e.getOwnerReconcileRequests(ctx, evt.ObjectOld)
req2 := e.getOwnerReconcileRequests(ctx, evt.ObjectNew)
@ -52,13 +55,13 @@ func (e *enqueueRequestForJenkins) Update(ctx context.Context, evt event.UpdateE
}
}
func (e *enqueueRequestForJenkins) Delete(ctx context.Context, evt event.DeleteEvent, q workqueue.RateLimitingInterface) {
func (e *enqueueRequestForJenkins) Delete(ctx context.Context, evt event.TypedDeleteEvent[client.Object], q workqueue.TypedRateLimitingInterface[reconcile.Request]) {
if req := e.getOwnerReconcileRequests(ctx, evt.Object); req != nil {
q.Add(*req)
}
}
func (e *enqueueRequestForJenkins) Generic(ctx context.Context, evt event.GenericEvent, q workqueue.RateLimitingInterface) {
func (e *enqueueRequestForJenkins) Generic(ctx context.Context, evt event.TypedGenericEvent[client.Object], q workqueue.TypedRateLimitingInterface[reconcile.Request]) {
if req := e.getOwnerReconcileRequests(ctx, evt.Object); req != nil {
q.Add(*req)
}
@ -77,15 +80,17 @@ func (e *enqueueRequestForJenkins) getOwnerReconcileRequests(_ context.Context,
}
type jenkinsDecorator struct {
handler handler.EventHandler
handler handler.TypedEventHandler[client.Object, reconcile.Request]
}
func (e *jenkinsDecorator) Create(ctx context.Context, evt event.CreateEvent, q workqueue.RateLimitingInterface) {
var _ handler.TypedEventHandler[client.Object, reconcile.Request] = &jenkinsDecorator{}
func (e *jenkinsDecorator) Create(ctx context.Context, evt event.TypedCreateEvent[client.Object], q workqueue.TypedRateLimitingInterface[reconcile.Request]) {
log.Log.WithValues("cr", evt.Object.GetName()).Info(fmt.Sprintf("%T/%s was created", evt.Object, evt.Object.GetName()))
e.handler.Create(ctx, evt, q)
}
func (e *jenkinsDecorator) Update(ctx context.Context, evt event.UpdateEvent, q workqueue.RateLimitingInterface) {
func (e *jenkinsDecorator) Update(ctx context.Context, evt event.TypedUpdateEvent[client.Object], q workqueue.TypedRateLimitingInterface[reconcile.Request]) {
if !reflect.DeepEqual(evt.ObjectOld.(*v1alpha2.Jenkins).Spec, evt.ObjectNew.(*v1alpha2.Jenkins).Spec) {
log.Log.WithValues("cr", evt.ObjectNew.GetName()).Info(
fmt.Sprintf("%T/%s has been updated", evt.ObjectNew, evt.ObjectNew.GetName()))
@ -93,11 +98,11 @@ func (e *jenkinsDecorator) Update(ctx context.Context, evt event.UpdateEvent, q
e.handler.Update(ctx, evt, q)
}
func (e *jenkinsDecorator) Delete(ctx context.Context, evt event.DeleteEvent, q workqueue.RateLimitingInterface) {
func (e *jenkinsDecorator) Delete(ctx context.Context, evt event.TypedDeleteEvent[client.Object], q workqueue.TypedRateLimitingInterface[reconcile.Request]) {
log.Log.WithValues("cr", evt.Object.GetName()).Info(fmt.Sprintf("%T/%s was deleted", evt.Object, evt.Object.GetName()))
e.handler.Delete(ctx, evt, q)
}
func (e *jenkinsDecorator) Generic(ctx context.Context, evt event.GenericEvent, q workqueue.RateLimitingInterface) {
func (e *jenkinsDecorator) Generic(ctx context.Context, evt event.TypedGenericEvent[client.Object], q workqueue.TypedRateLimitingInterface[reconcile.Request]) {
e.handler.Generic(ctx, evt, q)
}

View File

@ -19,7 +19,6 @@ package controllers
import (
"context"
"fmt"
"math/rand"
"reflect"
"time"
@ -200,13 +199,11 @@ func (r *JenkinsReconciler) Reconcile(_ context.Context, request ctrl.Request) (
[]string{fmt.Sprintf("%s Source '%s' Name '%s' groovy script execution failed, logs: %+v", groovyErr.ConfigurationType, groovyErr.Source, groovyErr.Name, groovyErr.Logs)}...,
),
}
return reconcile.Result{Requeue: false}, nil
return reconcile.Result{}, nil
}
return reconcile.Result{Requeue: true}, nil
}
if result.Requeue && result.RequeueAfter == 0 {
result.RequeueAfter = time.Duration(rand.Intn(10)) * time.Second
return reconcile.Result{RequeueAfter: time.Second}, nil
}
// Random delay logic removed as result.Requeue is deprecated
return result, nil
}
@ -265,11 +262,11 @@ func (r *JenkinsReconciler) reconcile(request reconcile.Request) (reconcile.Resu
if err != nil {
return reconcile.Result{}, jenkins, err
}
if result.Requeue {
if result.RequeueAfter > 0 {
return result, jenkins, nil
}
if jenkinsClient == nil {
return reconcile.Result{Requeue: false}, jenkins, nil
return reconcile.Result{}, jenkins, nil
}
if jenkins.Status.BaseConfigurationCompletedTime == nil {
@ -320,7 +317,7 @@ func (r *JenkinsReconciler) reconcile(request reconcile.Request) (reconcile.Resu
if err != nil {
return reconcile.Result{}, jenkins, err
}
if result.Requeue {
if result.RequeueAfter > 0 {
return result, jenkins, nil
}
@ -329,7 +326,7 @@ func (r *JenkinsReconciler) reconcile(request reconcile.Request) (reconcile.Resu
if err != nil {
return reconcile.Result{}, jenkins, err
}
if result.Requeue {
if result.RequeueAfter > 0 {
return result, jenkins, nil
}

View File

@ -74,11 +74,11 @@ func New(configuration configuration.Configuration, logger logr.Logger) *BackupA
func (bar *BackupAndRestore) Validate() []string {
var messages []string
allContainers := map[string]v1alpha2.Container{}
for _, container := range bar.Configuration.Jenkins.Spec.Master.Containers {
for _, container := range bar.Jenkins.Spec.Master.Containers {
allContainers[container.Name] = container
}
restore := bar.Configuration.Jenkins.Spec.Restore
restore := bar.Jenkins.Spec.Restore
if len(restore.ContainerName) > 0 {
_, found := allContainers[restore.ContainerName]
if !found {
@ -89,7 +89,7 @@ func (bar *BackupAndRestore) Validate() []string {
}
}
backup := bar.Configuration.Jenkins.Spec.Backup
backup := bar.Jenkins.Spec.Backup
if len(backup.ContainerName) > 0 {
_, found := allContainers[backup.ContainerName]
if !found {
@ -118,7 +118,7 @@ const noBackup = "-1"
// Restore performs Jenkins restore backup operation
func (bar *BackupAndRestore) Restore(jenkinsClient jenkinsclient.Jenkins) error {
jenkins := bar.Configuration.Jenkins
jenkins := bar.Jenkins
if len(jenkins.Spec.Restore.ContainerName) == 0 || jenkins.Spec.Restore.Action.Exec == nil {
bar.logger.V(log.VDebug).Info("Skipping restore backup, backup restore not configured")
return nil
@ -195,7 +195,7 @@ func (bar *BackupAndRestore) Restore(jenkinsClient jenkinsclient.Jenkins) error
if err != nil {
return err
}
bar.Configuration.Jenkins = jenkins
bar.Jenkins = jenkins
jenkins.Status.RestoredBackup = backupNumber
jenkins.Status.PendingBackup = backupNumber + 1
@ -207,7 +207,7 @@ func (bar *BackupAndRestore) Restore(jenkinsClient jenkinsclient.Jenkins) error
// Backup performs Jenkins backup operation
func (bar *BackupAndRestore) Backup(setBackupDoneBeforePodDeletion bool) error {
jenkins := bar.Configuration.Jenkins
jenkins := bar.Jenkins
if len(jenkins.Spec.Backup.ContainerName) == 0 || jenkins.Spec.Backup.Action.Exec == nil {
bar.logger.V(log.VDebug).Info("Skipping backup, backup creation not configured")
return nil
@ -268,9 +268,9 @@ func triggerBackup(ticker *time.Ticker, k8sClient k8s.Client, logger logr.Logger
// EnsureBackupTrigger creates or update trigger which update CR to make backup
func (bar *BackupAndRestore) EnsureBackupTrigger() error {
trigger, found := triggers.get(bar.Configuration.Jenkins.Namespace, bar.Configuration.Jenkins.Name)
trigger, found := triggers.get(bar.Jenkins.Namespace, bar.Jenkins.Name)
isBackupConfigured := len(bar.Configuration.Jenkins.Spec.Backup.ContainerName) > 0 && bar.Configuration.Jenkins.Spec.Backup.Interval > 0
isBackupConfigured := len(bar.Jenkins.Spec.Backup.ContainerName) > 0 && bar.Jenkins.Spec.Backup.Interval > 0
if found && !isBackupConfigured {
bar.StopBackupTrigger()
return nil
@ -282,7 +282,7 @@ func (bar *BackupAndRestore) EnsureBackupTrigger() error {
return nil
}
if found && isBackupConfigured && bar.Configuration.Jenkins.Spec.Backup.Interval != trigger.interval {
if found && isBackupConfigured && bar.Jenkins.Spec.Backup.Interval != trigger.interval {
bar.StopBackupTrigger()
bar.startBackupTrigger()
}
@ -292,21 +292,21 @@ func (bar *BackupAndRestore) EnsureBackupTrigger() error {
// StopBackupTrigger stops trigger which update CR to make backup
func (bar *BackupAndRestore) StopBackupTrigger() {
triggers.stop(bar.logger, bar.Configuration.Jenkins.Namespace, bar.Configuration.Jenkins.Name)
triggers.stop(bar.logger, bar.Jenkins.Namespace, bar.Jenkins.Name)
}
// IsBackupTriggerEnabled returns true if the backup trigger is enabled
func (bar *BackupAndRestore) IsBackupTriggerEnabled() bool {
_, enabled := triggers.get(bar.Configuration.Jenkins.Namespace, bar.Configuration.Jenkins.Name)
_, enabled := triggers.get(bar.Jenkins.Namespace, bar.Jenkins.Name)
return enabled
}
func (bar *BackupAndRestore) startBackupTrigger() {
bar.logger.Info("Starting backup trigger")
ticker := time.NewTicker(time.Duration(bar.Configuration.Jenkins.Spec.Backup.Interval) * time.Second)
triggers.add(bar.Configuration.Jenkins.Namespace, bar.Configuration.Jenkins.Name, backupTrigger{
interval: bar.Configuration.Jenkins.Spec.Backup.Interval,
ticker := time.NewTicker(time.Duration(bar.Jenkins.Spec.Backup.Interval) * time.Second)
triggers.add(bar.Jenkins.Namespace, bar.Jenkins.Name, backupTrigger{
interval: bar.Jenkins.Spec.Backup.Interval,
ticker: ticker,
})
go triggerBackup(ticker, bar.Client, bar.logger, bar.Configuration.Jenkins.Namespace, bar.Configuration.Jenkins.Name)
go triggerBackup(ticker, bar.Client, bar.logger, bar.Jenkins.Namespace, bar.Jenkins.Name)
}

View File

@ -8,7 +8,7 @@ import (
)
func (r *JenkinsBaseConfigurationReconciler) createScriptsConfigMap(meta metav1.ObjectMeta) error {
configMap, err := resources.NewScriptsConfigMap(meta, r.Configuration.Jenkins)
configMap, err := resources.NewScriptsConfigMap(meta, r.Jenkins)
if err != nil {
return err
}
@ -16,7 +16,7 @@ func (r *JenkinsBaseConfigurationReconciler) createScriptsConfigMap(meta metav1.
}
func (r *JenkinsBaseConfigurationReconciler) createInitConfigurationConfigMap(meta metav1.ObjectMeta) error {
configMap, err := resources.NewInitConfigurationConfigMap(meta, r.Configuration.Jenkins)
configMap, err := resources.NewInitConfigurationConfigMap(meta, r.Jenkins)
if err != nil {
return err
}
@ -24,7 +24,7 @@ func (r *JenkinsBaseConfigurationReconciler) createInitConfigurationConfigMap(me
}
func (r *JenkinsBaseConfigurationReconciler) createBaseConfigurationConfigMap(meta metav1.ObjectMeta) error {
configMap, err := resources.NewBaseConfigurationConfigMap(meta, r.Configuration.Jenkins, r.KubernetesClusterDomain)
configMap, err := resources.NewBaseConfigurationConfigMap(meta, r.Jenkins, r.KubernetesClusterDomain)
if err != nil {
return err
}

View File

@ -24,9 +24,9 @@ func (r *JenkinsBaseConfigurationReconciler) ensureJenkinsDeployment(meta metav1
_, err = r.GetJenkinsDeployment()
if apierrors.IsNotFound(err) {
jenkinsDeployment := resources.NewJenkinsDeployment(meta, r.Configuration.Jenkins)
jenkinsDeployment := resources.NewJenkinsDeployment(meta, r.Jenkins)
*r.Notifications <- event.Event{
Jenkins: *r.Configuration.Jenkins,
Jenkins: *r.Jenkins,
Phase: event.PhaseBase,
Level: v1alpha2.NotificationLevelInfo,
Reason: reason.NewPodCreation(reason.OperatorSource, []string{"Creating a Jenkins Deployment"}),
@ -39,14 +39,14 @@ func (r *JenkinsBaseConfigurationReconciler) ensureJenkinsDeployment(meta metav1
}
now := metav1.Now()
r.Configuration.Jenkins.Status = v1alpha2.JenkinsStatus{
r.Jenkins.Status = v1alpha2.JenkinsStatus{
OperatorVersion: version.Version,
ProvisionStartTime: &now,
LastBackup: r.Configuration.Jenkins.Status.LastBackup,
PendingBackup: r.Configuration.Jenkins.Status.LastBackup,
LastBackup: r.Jenkins.Status.LastBackup,
PendingBackup: r.Jenkins.Status.LastBackup,
UserAndPasswordHash: userAndPasswordHash,
}
return reconcile.Result{Requeue: true}, r.Client.Update(context.TODO(), r.Configuration.Jenkins)
return reconcile.Result{Requeue: true}, r.Client.Update(context.TODO(), r.Jenkins)
} else if err != nil && !apierrors.IsNotFound(err) {
return reconcile.Result{}, stackerr.WithStack(err)
}

View File

@ -12,21 +12,21 @@ import (
)
func (r *JenkinsBaseConfigurationReconciler) addLabelForWatchesResources(customization v1alpha2.Customization) error {
labelsForWatchedResources := resources.BuildLabelsForWatchedResources(*r.Configuration.Jenkins)
labelsForWatchedResources := resources.BuildLabelsForWatchedResources(*r.Jenkins)
if len(customization.Secret.Name) > 0 {
secret := &corev1.Secret{}
err := r.Client.Get(context.TODO(), types.NamespacedName{Name: customization.Secret.Name, Namespace: r.Configuration.Jenkins.Namespace}, secret)
err := r.Client.Get(context.TODO(), types.NamespacedName{Name: customization.Secret.Name, Namespace: r.Jenkins.Namespace}, secret)
if err != nil {
return stackerr.WithStack(err)
}
if !resources.VerifyIfLabelsAreSet(secret, labelsForWatchedResources) {
if len(secret.ObjectMeta.Labels) == 0 {
secret.ObjectMeta.Labels = map[string]string{}
if len(secret.Labels) == 0 {
secret.Labels = map[string]string{}
}
for key, value := range labelsForWatchedResources {
secret.ObjectMeta.Labels[key] = value
secret.Labels[key] = value
}
if err = r.Client.Update(context.TODO(), secret); err != nil {
@ -37,17 +37,17 @@ func (r *JenkinsBaseConfigurationReconciler) addLabelForWatchesResources(customi
for _, configMapRef := range customization.Configurations {
configMap := &corev1.ConfigMap{}
err := r.Client.Get(context.TODO(), types.NamespacedName{Name: configMapRef.Name, Namespace: r.Configuration.Jenkins.Namespace}, configMap)
err := r.Client.Get(context.TODO(), types.NamespacedName{Name: configMapRef.Name, Namespace: r.Jenkins.Namespace}, configMap)
if err != nil {
return stackerr.WithStack(err)
}
if !resources.VerifyIfLabelsAreSet(configMap, labelsForWatchedResources) {
if len(configMap.ObjectMeta.Labels) == 0 {
configMap.ObjectMeta.Labels = map[string]string{}
if len(configMap.Labels) == 0 {
configMap.Labels = map[string]string{}
}
for key, value := range labelsForWatchedResources {
configMap.ObjectMeta.Labels[key] = value
configMap.Labels[key] = value
}
if err = r.Client.Update(context.TODO(), configMap); err != nil {

View File

@ -13,7 +13,7 @@ import (
)
func (r *JenkinsBaseConfigurationReconciler) verifyPlugins(jenkinsClient jenkinsclient.Jenkins) (bool, error) {
if r.Configuration.Jenkins.Spec.Master.SkipPlugins != nil && *r.Configuration.Jenkins.Spec.Master.SkipPlugins {
if r.Jenkins.Spec.Master.SkipPlugins != nil && *r.Jenkins.Spec.Master.SkipPlugins {
return true, nil
}
allPluginsInJenkins, err := jenkinsClient.GetPlugins(fetchAllPlugins)
@ -30,7 +30,7 @@ func (r *JenkinsBaseConfigurationReconciler) verifyPlugins(jenkinsClient jenkins
r.logger.V(log.VDebug).Info(fmt.Sprintf("Installed plugins '%+v'", installedPlugins))
status := true
allRequiredPlugins := [][]v1alpha2.Plugin{r.Configuration.Jenkins.Spec.Master.BasePlugins, r.Configuration.Jenkins.Spec.Master.Plugins}
allRequiredPlugins := [][]v1alpha2.Plugin{r.Jenkins.Spec.Master.BasePlugins, r.Jenkins.Spec.Master.Plugins}
for _, requiredPlugins := range allRequiredPlugins {
for _, plugin := range requiredPlugins {
if _, ok := isPluginInstalled(allPluginsInJenkins, plugin); !ok {

View File

@ -32,83 +32,83 @@ func (r *JenkinsBaseConfigurationReconciler) checkForPodRecreation(currentJenkin
return reason.NewPodRestart(reason.KubernetesSource, messages, verbose...)
}
userAndPasswordHashIsDifferent := userAndPasswordHash != r.Configuration.Jenkins.Status.UserAndPasswordHash
userAndPasswordHashStatusNotEmpty := r.Configuration.Jenkins.Status.UserAndPasswordHash != ""
userAndPasswordHashIsDifferent := userAndPasswordHash != r.Jenkins.Status.UserAndPasswordHash
userAndPasswordHashStatusNotEmpty := r.Jenkins.Status.UserAndPasswordHash != ""
if userAndPasswordHashIsDifferent && userAndPasswordHashStatusNotEmpty {
messages = append(messages, "User or password have changed")
verbose = append(verbose, "User or password have changed, recreating pod")
}
if r.Configuration.Jenkins.Spec.Restore.RecoveryOnce != 0 && r.Configuration.Jenkins.Status.RestoredBackup != 0 {
if r.Jenkins.Spec.Restore.RecoveryOnce != 0 && r.Jenkins.Status.RestoredBackup != 0 {
messages = append(messages, "spec.restore.recoveryOnce is set")
verbose = append(verbose, "spec.restore.recoveryOnce is set, recreating pod")
}
if version.Version != r.Configuration.Jenkins.Status.OperatorVersion {
if version.Version != r.Jenkins.Status.OperatorVersion {
messages = append(messages, "Jenkins Operator version has changed")
verbose = append(verbose, fmt.Sprintf("Jenkins Operator version has changed, actual '%+v' new '%+v'",
r.Configuration.Jenkins.Status.OperatorVersion, version.Version))
r.Jenkins.Status.OperatorVersion, version.Version))
}
//FIXME too hacky
var jenkinsSecurityContext *corev1.PodSecurityContext
if r.Configuration.Jenkins.Spec.Master.SecurityContext == nil {
if r.Jenkins.Spec.Master.SecurityContext == nil {
jenkinsSecurityContext = &corev1.PodSecurityContext{}
} else {
jenkinsSecurityContext = r.Configuration.Jenkins.Spec.Master.SecurityContext
jenkinsSecurityContext = r.Jenkins.Spec.Master.SecurityContext
}
if !reflect.DeepEqual(jenkinsSecurityContext, currentJenkinsMasterPod.Spec.SecurityContext) {
messages = append(messages, "Jenkins pod security context has changed")
verbose = append(verbose, fmt.Sprintf("Jenkins pod security context has changed, actual '%+v' required '%+v'",
currentJenkinsMasterPod.Spec.SecurityContext, r.Configuration.Jenkins.Spec.Master.SecurityContext))
currentJenkinsMasterPod.Spec.SecurityContext, r.Jenkins.Spec.Master.SecurityContext))
}
if !compareImagePullSecrets(r.Configuration.Jenkins.Spec.Master.ImagePullSecrets, currentJenkinsMasterPod.Spec.ImagePullSecrets) {
if !compareImagePullSecrets(r.Jenkins.Spec.Master.ImagePullSecrets, currentJenkinsMasterPod.Spec.ImagePullSecrets) {
messages = append(messages, "Jenkins Pod ImagePullSecrets has changed")
verbose = append(verbose, fmt.Sprintf("Jenkins Pod ImagePullSecrets has changed, actual '%+v' required '%+v'",
currentJenkinsMasterPod.Spec.ImagePullSecrets, r.Configuration.Jenkins.Spec.Master.ImagePullSecrets))
currentJenkinsMasterPod.Spec.ImagePullSecrets, r.Jenkins.Spec.Master.ImagePullSecrets))
}
if !compareMap(r.Configuration.Jenkins.Spec.Master.NodeSelector, currentJenkinsMasterPod.Spec.NodeSelector) {
if !compareMap(r.Jenkins.Spec.Master.NodeSelector, currentJenkinsMasterPod.Spec.NodeSelector) {
messages = append(messages, "Jenkins pod node selector has changed")
verbose = append(verbose, fmt.Sprintf("Jenkins pod node selector has changed, actual '%+v' required '%+v'",
currentJenkinsMasterPod.Spec.NodeSelector, r.Configuration.Jenkins.Spec.Master.NodeSelector))
currentJenkinsMasterPod.Spec.NodeSelector, r.Jenkins.Spec.Master.NodeSelector))
}
if !compareMap(r.Configuration.Jenkins.Spec.Master.Labels, currentJenkinsMasterPod.Labels) {
if !compareMap(r.Jenkins.Spec.Master.Labels, currentJenkinsMasterPod.Labels) {
messages = append(messages, "Jenkins pod labels have changed")
verbose = append(verbose, fmt.Sprintf("Jenkins pod labels have changed, actual '%+v' required '%+v'",
currentJenkinsMasterPod.Labels, r.Configuration.Jenkins.Spec.Master.Labels))
currentJenkinsMasterPod.Labels, r.Jenkins.Spec.Master.Labels))
}
if !compareMap(r.Configuration.Jenkins.Spec.Master.Annotations, currentJenkinsMasterPod.ObjectMeta.Annotations) {
if !compareMap(r.Jenkins.Spec.Master.Annotations, currentJenkinsMasterPod.Annotations) {
messages = append(messages, "Jenkins pod annotations have changed")
verbose = append(verbose, fmt.Sprintf("Jenkins pod annotations have changed, actual '%+v' required '%+v'",
currentJenkinsMasterPod.ObjectMeta.Annotations, r.Configuration.Jenkins.Spec.Master.Annotations))
currentJenkinsMasterPod.Annotations, r.Jenkins.Spec.Master.Annotations))
}
if !r.compareVolumes(currentJenkinsMasterPod) {
messages = append(messages, "Jenkins pod volumes have changed")
verbose = append(verbose, fmt.Sprintf("Jenkins pod volumes have changed, actual '%v' required '%v'",
currentJenkinsMasterPod.Spec.Volumes, r.Configuration.Jenkins.Spec.Master.Volumes))
currentJenkinsMasterPod.Spec.Volumes, r.Jenkins.Spec.Master.Volumes))
}
if len(r.Configuration.Jenkins.Spec.Master.Containers) != len(currentJenkinsMasterPod.Spec.Containers) {
if len(r.Jenkins.Spec.Master.Containers) != len(currentJenkinsMasterPod.Spec.Containers) {
messages = append(messages, "Jenkins amount of containers has changed")
verbose = append(verbose, fmt.Sprintf("Jenkins amount of containers has changed, actual '%+v' required '%+v'",
len(currentJenkinsMasterPod.Spec.Containers), len(r.Configuration.Jenkins.Spec.Master.Containers)))
len(currentJenkinsMasterPod.Spec.Containers), len(r.Jenkins.Spec.Master.Containers)))
}
if r.Configuration.Jenkins.Spec.Master.PriorityClassName != currentJenkinsMasterPod.Spec.PriorityClassName {
if r.Jenkins.Spec.Master.PriorityClassName != currentJenkinsMasterPod.Spec.PriorityClassName {
messages = append(messages, "Jenkins priorityClassName has changed")
verbose = append(verbose, fmt.Sprintf("Jenkins priorityClassName has changed, actual '%+v' required '%+v'",
currentJenkinsMasterPod.Spec.PriorityClassName, r.Configuration.Jenkins.Spec.Master.PriorityClassName))
currentJenkinsMasterPod.Spec.PriorityClassName, r.Jenkins.Spec.Master.PriorityClassName))
}
customResourceReplaced := (r.Configuration.Jenkins.Status.BaseConfigurationCompletedTime == nil ||
r.Configuration.Jenkins.Status.UserConfigurationCompletedTime == nil) &&
r.Configuration.Jenkins.Status.UserAndPasswordHash == ""
customResourceReplaced := (r.Jenkins.Status.BaseConfigurationCompletedTime == nil ||
r.Jenkins.Status.UserConfigurationCompletedTime == nil) &&
r.Jenkins.Status.UserAndPasswordHash == ""
if customResourceReplaced {
messages = append(messages, "Jenkins CR has been replaced")
@ -117,14 +117,14 @@ func (r *JenkinsBaseConfigurationReconciler) checkForPodRecreation(currentJenkin
for _, actualContainer := range currentJenkinsMasterPod.Spec.Containers {
if actualContainer.Name == resources.JenkinsMasterContainerName {
containerMessages, verboseMessages := r.compareContainers(resources.NewJenkinsMasterContainer(r.Configuration.Jenkins), actualContainer)
containerMessages, verboseMessages := r.compareContainers(resources.NewJenkinsMasterContainer(r.Jenkins), actualContainer)
messages = append(messages, containerMessages...)
verbose = append(verbose, verboseMessages...)
continue
}
var expectedContainer *corev1.Container
for _, jenkinsContainer := range r.Configuration.Jenkins.Spec.Master.Containers {
for _, jenkinsContainer := range r.Jenkins.Spec.Master.Containers {
if jenkinsContainer.Name == actualContainer.Name {
tmp := resources.ConvertJenkinsContainerToKubernetesContainer(jenkinsContainer)
expectedContainer = &tmp
@ -153,11 +153,11 @@ func (r *JenkinsBaseConfigurationReconciler) ensureJenkinsMasterPod(meta metav1.
}
// Check if this Pod already exists
currentJenkinsMasterPod, err := r.Configuration.GetJenkinsMasterPod()
currentJenkinsMasterPod, err := r.GetJenkinsMasterPod()
if err != nil && apierrors.IsNotFound(err) {
jenkinsMasterPod := resources.NewJenkinsMasterPod(meta, r.Configuration.Jenkins)
jenkinsMasterPod := resources.NewJenkinsMasterPod(meta, r.Jenkins)
*r.Notifications <- event.Event{
Jenkins: *r.Configuration.Jenkins,
Jenkins: *r.Jenkins,
Phase: event.PhaseBase,
Level: v1alpha2.NotificationLevelInfo,
Reason: reason.NewPodCreation(reason.OperatorSource, []string{"Creating a new Jenkins Master Pod"}),
@ -169,14 +169,14 @@ func (r *JenkinsBaseConfigurationReconciler) ensureJenkinsMasterPod(meta metav1.
}
now := metav1.Now()
r.Configuration.Jenkins.Status = v1alpha2.JenkinsStatus{
r.Jenkins.Status = v1alpha2.JenkinsStatus{
OperatorVersion: version.Version,
ProvisionStartTime: &now,
LastBackup: r.Configuration.Jenkins.Status.LastBackup,
PendingBackup: r.Configuration.Jenkins.Status.LastBackup,
LastBackup: r.Jenkins.Status.LastBackup,
PendingBackup: r.Jenkins.Status.LastBackup,
UserAndPasswordHash: userAndPasswordHash,
}
return reconcile.Result{Requeue: true}, r.Client.Status().Update(context.TODO(), r.Configuration.Jenkins)
return reconcile.Result{Requeue: true}, r.Client.Status().Update(context.TODO(), r.Jenkins)
} else if err != nil && !apierrors.IsNotFound(err) {
return reconcile.Result{}, stackerr.WithStack(err)
}
@ -185,15 +185,15 @@ func (r *JenkinsBaseConfigurationReconciler) ensureJenkinsMasterPod(meta metav1.
return reconcile.Result{Requeue: true}, nil
}
if r.IsJenkinsTerminating(*currentJenkinsMasterPod) && r.Configuration.Jenkins.Status.UserConfigurationCompletedTime != nil {
if r.IsJenkinsTerminating(*currentJenkinsMasterPod) && r.Jenkins.Status.UserConfigurationCompletedTime != nil {
backupAndRestore := backuprestore.New(r.Configuration, r.logger)
if backupAndRestore.IsBackupTriggerEnabled() {
backupAndRestore.StopBackupTrigger()
return reconcile.Result{Requeue: true}, nil
}
if r.Configuration.Jenkins.Spec.Backup.MakeBackupBeforePodDeletion && !r.Configuration.Jenkins.Status.BackupDoneBeforePodDeletion {
if r.Configuration.Jenkins.Status.LastBackup == r.Configuration.Jenkins.Status.PendingBackup {
r.Configuration.Jenkins.Status.PendingBackup++
if r.Jenkins.Spec.Backup.MakeBackupBeforePodDeletion && !r.Jenkins.Status.BackupDoneBeforePodDeletion {
if r.Jenkins.Status.LastBackup == r.Jenkins.Status.PendingBackup {
r.Jenkins.Status.PendingBackup++
}
if err = backupAndRestore.Backup(true); err != nil {
return reconcile.Result{}, err
@ -209,7 +209,7 @@ func (r *JenkinsBaseConfigurationReconciler) ensureJenkinsMasterPod(meta metav1.
r.logger.Info(msg)
}
return reconcile.Result{Requeue: true}, r.Configuration.RestartJenkinsMasterPod(restartReason)
return reconcile.Result{Requeue: true}, r.RestartJenkinsMasterPod(restartReason)
}
}

View File

@ -42,7 +42,7 @@ func (r *JenkinsBaseConfigurationReconciler) createRBAC(meta metav1.ObjectMeta)
func (r *JenkinsBaseConfigurationReconciler) ensureExtraRBAC(meta metav1.ObjectMeta) error {
var err error
var name string
for _, roleRef := range r.Configuration.Jenkins.Spec.Roles {
for _, roleRef := range r.Jenkins.Spec.Roles {
name = getExtraRoleBindingName(meta.Name, roleRef)
roleBinding := resources.NewRoleBinding(name, meta.Namespace, meta.Name, roleRef)
err := r.Client.Create(context.TODO(), roleBinding)
@ -55,7 +55,7 @@ func (r *JenkinsBaseConfigurationReconciler) ensureExtraRBAC(meta metav1.ObjectM
}
roleBindings := &rbacv1.RoleBindingList{}
err = r.Client.List(context.TODO(), roleBindings, client.InNamespace(r.Configuration.Jenkins.Namespace))
err = r.Client.List(context.TODO(), roleBindings, client.InNamespace(r.Jenkins.Namespace))
if err != nil {
return stackerr.WithStack(err)
}
@ -66,7 +66,7 @@ func (r *JenkinsBaseConfigurationReconciler) ensureExtraRBAC(meta metav1.ObjectM
}
found := false
for _, roleRef := range r.Configuration.Jenkins.Spec.Roles {
for _, roleRef := range r.Jenkins.Spec.Roles {
name = getExtraRoleBindingName(meta.Name, roleRef)
if roleBinding.Name == name {
found = true

View File

@ -50,7 +50,7 @@ func New(config configuration.Configuration, jenkinsAPIConnectionSettings jenkin
// Reconcile takes care of base configuration.
func (r *JenkinsBaseConfigurationReconciler) Reconcile() (reconcile.Result, jenkinsclient.Jenkins, error) {
metaObject := resources.NewResourceObjectMeta(r.Configuration.Jenkins)
metaObject := resources.NewResourceObjectMeta(r.Jenkins)
// Create Necessary Resources
err := r.ensureResourcesRequiredForJenkinsPod(metaObject)
@ -59,12 +59,12 @@ func (r *JenkinsBaseConfigurationReconciler) Reconcile() (reconcile.Result, jenk
}
r.logger.V(log.VDebug).Info("Kubernetes resources are present")
if useDeploymentForJenkinsMaster(r.Configuration.Jenkins) {
if useDeploymentForJenkinsMaster(r.Jenkins) {
result, err := r.ensureJenkinsDeployment(metaObject)
if err != nil {
return reconcile.Result{}, nil, err
}
if result.Requeue {
if result.RequeueAfter > 0 {
return result, nil, nil
}
r.logger.V(log.VDebug).Info("Jenkins Deployment is present")
@ -76,7 +76,7 @@ func (r *JenkinsBaseConfigurationReconciler) Reconcile() (reconcile.Result, jenk
if err != nil {
return reconcile.Result{}, nil, err
}
if result.Requeue {
if result.RequeueAfter > 0 {
return result, nil, nil
}
r.logger.V(log.VDebug).Info("Jenkins master pod is present")
@ -86,19 +86,19 @@ func (r *JenkinsBaseConfigurationReconciler) Reconcile() (reconcile.Result, jenk
return reconcile.Result{}, nil, err
}
if stopReconcileLoop {
return reconcile.Result{Requeue: false}, nil, nil
return reconcile.Result{}, nil, nil
}
result, err = r.waitForJenkins()
if err != nil {
return reconcile.Result{}, nil, err
}
if result.Requeue {
if result.RequeueAfter > 0 {
return result, nil, nil
}
r.logger.V(log.VDebug).Info("Jenkins master pod is ready")
jenkinsClient, err := r.Configuration.GetJenkinsClient()
jenkinsClient, err := r.GetJenkinsClient()
if err != nil {
return reconcile.Result{}, nil, err
}
@ -117,7 +117,7 @@ func (r *JenkinsBaseConfigurationReconciler) Reconcile() (reconcile.Result, jenk
reason.OperatorSource,
[]string{message},
)
return reconcile.Result{Requeue: true}, nil, r.Configuration.RestartJenkinsMasterPod(restartReason)
return reconcile.Result{Requeue: true}, nil, r.RestartJenkinsMasterPod(restartReason)
}
result, err = r.ensureBaseConfiguration(jenkinsClient)
@ -158,12 +158,12 @@ func (r *JenkinsBaseConfigurationReconciler) ensureResourcesRequiredForJenkinsPo
}
r.logger.V(log.VDebug).Info("Base configuration config map is present")
if err := r.addLabelForWatchesResources(r.Configuration.Jenkins.Spec.GroovyScripts.Customization); err != nil {
if err := r.addLabelForWatchesResources(r.Jenkins.Spec.GroovyScripts.Customization); err != nil {
return err
}
r.logger.V(log.VDebug).Info("GroovyScripts Secret and ConfigMap added watched labels")
if err := r.addLabelForWatchesResources(r.Configuration.Jenkins.Spec.ConfigurationAsCode.Customization); err != nil {
if err := r.addLabelForWatchesResources(r.Jenkins.Spec.ConfigurationAsCode.Customization); err != nil {
return err
}
r.logger.V(log.VDebug).Info("ConfigurationAsCode Secret and ConfigMap added watched labels")
@ -178,20 +178,20 @@ func (r *JenkinsBaseConfigurationReconciler) ensureResourcesRequiredForJenkinsPo
}
r.logger.V(log.VDebug).Info("Extra role bindings are present")
httpServiceName := resources.GetJenkinsHTTPServiceName(r.Configuration.Jenkins)
if err := r.createService(metaObject, httpServiceName, r.Configuration.Jenkins.Spec.Service, constants.DefaultHTTPPortInt32); err != nil {
httpServiceName := resources.GetJenkinsHTTPServiceName(r.Jenkins)
if err := r.createService(metaObject, httpServiceName, r.Jenkins.Spec.Service, constants.DefaultHTTPPortInt32); err != nil {
return err
}
r.logger.V(log.VDebug).Info("Jenkins HTTP Service is present")
if err := r.createService(metaObject, resources.GetJenkinsSlavesServiceName(r.Configuration.Jenkins), r.Configuration.Jenkins.Spec.SlaveService, constants.DefaultSlavePortInt32); err != nil {
if err := r.createService(metaObject, resources.GetJenkinsSlavesServiceName(r.Jenkins), r.Jenkins.Spec.SlaveService, constants.DefaultSlavePortInt32); err != nil {
return err
}
r.logger.V(log.VDebug).Info("Jenkins slave Service is present")
if resources.IsRouteAPIAvailable(&r.ClientSet) {
r.logger.V(log.VDebug).Info("Route API is available. Now creating route.")
if err := r.createRoute(metaObject, httpServiceName, r.Configuration.Jenkins); err != nil {
if err := r.createRoute(metaObject, httpServiceName, r.Jenkins); err != nil {
return err
}
r.logger.V(log.VDebug).Info("Jenkins Route is present")
@ -202,10 +202,10 @@ func (r *JenkinsBaseConfigurationReconciler) ensureResourcesRequiredForJenkinsPo
func (r *JenkinsBaseConfigurationReconciler) createOperatorCredentialsSecret(meta metav1.ObjectMeta) error {
found := &corev1.Secret{}
err := r.Configuration.Client.Get(context.TODO(), types.NamespacedName{Name: resources.GetOperatorCredentialsSecretName(r.Configuration.Jenkins), Namespace: r.Configuration.Jenkins.ObjectMeta.Namespace}, found)
err := r.Client.Get(context.TODO(), types.NamespacedName{Name: resources.GetOperatorCredentialsSecretName(r.Jenkins), Namespace: r.Jenkins.Namespace}, found)
if err != nil && apierrors.IsNotFound(err) {
return stackerr.WithStack(r.CreateResource(resources.NewOperatorCredentialsSecret(meta, r.Configuration.Jenkins)))
return stackerr.WithStack(r.CreateResource(resources.NewOperatorCredentialsSecret(meta, r.Jenkins)))
} else if err != nil && !apierrors.IsNotFound(err) {
return stackerr.WithStack(err)
}
@ -214,12 +214,12 @@ func (r *JenkinsBaseConfigurationReconciler) createOperatorCredentialsSecret(met
found.Data[resources.OperatorCredentialsSecretPasswordKey] != nil {
return nil
}
return stackerr.WithStack(r.UpdateResource(resources.NewOperatorCredentialsSecret(meta, r.Configuration.Jenkins)))
return stackerr.WithStack(r.UpdateResource(resources.NewOperatorCredentialsSecret(meta, r.Jenkins)))
}
func (r *JenkinsBaseConfigurationReconciler) calculateUserAndPasswordHash() (string, error) {
credentialsSecret := &corev1.Secret{}
err := r.Client.Get(context.TODO(), types.NamespacedName{Name: resources.GetOperatorCredentialsSecretName(r.Configuration.Jenkins), Namespace: r.Configuration.Jenkins.ObjectMeta.Namespace}, credentialsSecret)
err := r.Client.Get(context.TODO(), types.NamespacedName{Name: resources.GetOperatorCredentialsSecretName(r.Jenkins), Namespace: r.Jenkins.Namespace}, credentialsSecret)
if err != nil {
return "", stackerr.WithStack(err)
}
@ -309,27 +309,27 @@ func (r *JenkinsBaseConfigurationReconciler) compareVolumes(actualPod corev1.Pod
}
return reflect.DeepEqual(
append(resources.GetJenkinsMasterPodBaseVolumes(r.Configuration.Jenkins), r.Configuration.Jenkins.Spec.Master.Volumes...),
append(resources.GetJenkinsMasterPodBaseVolumes(r.Jenkins), r.Jenkins.Spec.Master.Volumes...),
toCompare,
)
}
func (r *JenkinsBaseConfigurationReconciler) detectJenkinsMasterPodStartingIssues() (stopReconcileLoop bool, err error) {
jenkinsMasterPod, err := r.Configuration.GetJenkinsMasterPod()
jenkinsMasterPod, err := r.GetJenkinsMasterPod()
if err != nil {
return false, err
}
if r.Configuration.Jenkins.Status.ProvisionStartTime == nil {
if r.Jenkins.Status.ProvisionStartTime == nil {
return true, nil
}
if jenkinsMasterPod.Status.Phase == corev1.PodPending {
timeout := r.Configuration.Jenkins.Status.ProvisionStartTime.Add(time.Minute * 2).UTC()
timeout := r.Jenkins.Status.ProvisionStartTime.Add(time.Minute * 2).UTC()
now := time.Now().UTC()
if now.After(timeout) {
events := &corev1.EventList{}
err = r.Client.List(context.TODO(), events, client.InNamespace(r.Configuration.Jenkins.Namespace))
err = r.Client.List(context.TODO(), events, client.InNamespace(r.Jenkins.Namespace))
if err != nil {
return false, stackerr.WithStack(err)
}
@ -351,13 +351,13 @@ func (r *JenkinsBaseConfigurationReconciler) detectJenkinsMasterPodStartingIssue
func (r *JenkinsBaseConfigurationReconciler) filterEvents(source corev1.EventList, jenkinsMasterPod corev1.Pod) []string {
events := []string{}
for _, eventItem := range source.Items {
if r.Configuration.Jenkins.Status.ProvisionStartTime.UTC().After(eventItem.LastTimestamp.UTC()) {
if r.Jenkins.Status.ProvisionStartTime.UTC().After(eventItem.LastTimestamp.UTC()) {
continue
}
if eventItem.Type == corev1.EventTypeNormal {
continue
}
if !strings.HasPrefix(eventItem.ObjectMeta.Name, jenkinsMasterPod.Name) {
if !strings.HasPrefix(eventItem.Name, jenkinsMasterPod.Name) {
continue
}
events = append(events, fmt.Sprintf("Message: %s Subobject: %s", eventItem.Message, eventItem.InvolvedObject.FieldPath))
@ -366,7 +366,7 @@ func (r *JenkinsBaseConfigurationReconciler) filterEvents(source corev1.EventLis
}
func (r *JenkinsBaseConfigurationReconciler) waitForJenkins() (reconcile.Result, error) {
jenkinsMasterPod, err := r.Configuration.GetJenkinsMasterPod()
jenkinsMasterPod, err := r.GetJenkinsMasterPod()
if err != nil {
return reconcile.Result{}, err
}
@ -391,7 +391,7 @@ func (r *JenkinsBaseConfigurationReconciler) waitForJenkins() (reconcile.Result,
reason.KubernetesSource,
[]string{message},
)
return reconcile.Result{Requeue: true}, r.Configuration.RestartJenkinsMasterPod(restartReason)
return reconcile.Result{Requeue: true}, r.RestartJenkinsMasterPod(restartReason)
}
if !containerStatus.Ready {
r.logger.V(log.VDebug).Info(fmt.Sprintf("Container '%s' not ready, readiness probe failed", containerStatus.Name))
@ -410,10 +410,10 @@ func (r *JenkinsBaseConfigurationReconciler) ensureBaseConfiguration(jenkinsClie
customization := v1alpha2.GroovyScripts{
Customization: v1alpha2.Customization{
Secret: v1alpha2.SecretRef{Name: ""},
Configurations: []v1alpha2.ConfigMapRef{{Name: resources.GetBaseConfigurationConfigMapName(r.Configuration.Jenkins)}},
Configurations: []v1alpha2.ConfigMapRef{{Name: resources.GetBaseConfigurationConfigMapName(r.Jenkins)}},
},
}
groovyClient := groovy.New(jenkinsClient, r.Client, r.Configuration.Jenkins, "base-groovy", customization.Customization)
groovyClient := groovy.New(jenkinsClient, r.Client, r.Jenkins, "base-groovy", customization.Customization)
requeue, err := groovyClient.Ensure(func(name string) bool {
return strings.HasSuffix(name, ".groovy")
}, func(groovyScript string) string {

View File

@ -154,7 +154,7 @@ GlobalConfiguration.all().get(GlobalJobDslSecurityConfiguration.class).save()
// GetBaseConfigurationConfigMapName returns name of Kubernetes config map used to base configuration.
func GetBaseConfigurationConfigMapName(jenkins *v1alpha2.Jenkins) string {
return fmt.Sprintf("%s-base-configuration-%s", constants.OperatorName, jenkins.ObjectMeta.Name)
return fmt.Sprintf("%s-base-configuration-%s", constants.OperatorName, jenkins.Name)
}
// NewBaseConfigurationConfigMap builds Kubernetes config map used to base configuration.
@ -183,7 +183,7 @@ func NewBaseConfigurationConfigMap(meta metav1.ObjectMeta, jenkins *v1alpha2.Jen
disableInsecureFeaturesGroovyScriptName: disableInsecureFeatures,
configureKubernetesPluginGroovyScriptName: fmt.Sprintf(configureKubernetesPluginFmt,
clusterDomain,
jenkins.ObjectMeta.Namespace,
jenkins.Namespace,
fmt.Sprintf("http://%s:%d%s", jenkinsServiceFQDN, jenkins.Spec.Service.Port, suffix),
fmt.Sprintf("%s:%d", jenkinsSlavesServiceFQDN, jenkins.Spec.SlaveService.Port),
),

View File

@ -63,7 +63,7 @@ func buildCreateJenkinsOperatorUserGroovyScript(jenkins *v1alpha2.Jenkins) (*str
// GetInitConfigurationConfigMapName returns name of Kubernetes config map used to init configuration
func GetInitConfigurationConfigMapName(jenkins *v1alpha2.Jenkins) string {
return fmt.Sprintf("%s-init-configuration-%s", constants.OperatorName, jenkins.ObjectMeta.Name)
return fmt.Sprintf("%s-init-configuration-%s", constants.OperatorName, jenkins.Name)
}
// NewInitConfigurationConfigMap builds Kubernetes config map used to init configuration

View File

@ -13,7 +13,7 @@ import (
func NewResourceObjectMeta(jenkins *v1alpha2.Jenkins) metav1.ObjectMeta {
return metav1.ObjectMeta{
Name: GetResourceName(jenkins),
Namespace: jenkins.ObjectMeta.Namespace,
Namespace: jenkins.Namespace,
Labels: BuildResourceLabels(jenkins),
}
}
@ -39,7 +39,7 @@ func BuildLabelsForWatchedResources(jenkins v1alpha2.Jenkins) map[string]string
// GetResourceName returns name of Kubernetes resource base on Jenkins CR
func GetResourceName(jenkins *v1alpha2.Jenkins) string {
return fmt.Sprintf("%s-%s", constants.LabelAppValue, jenkins.ObjectMeta.Name)
return fmt.Sprintf("%s-%s", constants.LabelAppValue, jenkins.Name)
}
// VerifyIfLabelsAreSet check is selected labels are set for specific resource

View File

@ -148,9 +148,10 @@ func TestGetJenkinsMasterPodBaseVolumes(t *testing.T) {
func checkSecretVolumesPresence(jenkins *v1alpha2.Jenkins) (groovyExists bool, cascExists bool) {
for _, volume := range GetJenkinsMasterPodBaseVolumes(jenkins) {
if volume.Name == ("gs-" + jenkins.Spec.GroovyScripts.Secret.Name) {
switch volume.Name {
case "gs-" + jenkins.Spec.GroovyScripts.Secret.Name:
groovyExists = true
} else if volume.Name == ("casc-" + jenkins.Spec.ConfigurationAsCode.Secret.Name) {
case "casc-" + jenkins.Spec.ConfigurationAsCode.Secret.Name:
cascExists = true
}
}

View File

@ -126,7 +126,7 @@ func buildInitBashScript(jenkins *v1alpha2.Jenkins) (*string, error) {
}
func getScriptsConfigMapName(jenkins *v1alpha2.Jenkins) string {
return fmt.Sprintf("%s-scripts-%s", constants.OperatorName, jenkins.ObjectMeta.Name)
return fmt.Sprintf("%s-scripts-%s", constants.OperatorName, jenkins.Name)
}
// NewScriptsConfigMap builds Kubernetes config map used to store scripts

View File

@ -19,9 +19,9 @@ const ServiceKind = "Service"
// UpdateService returns new service with override fields from config
func UpdateService(actual corev1.Service, config v1alpha2.Service, targetPort int32) corev1.Service {
actual.ObjectMeta.Annotations = config.Annotations
actual.Annotations = config.Annotations
for key, value := range config.Labels {
actual.ObjectMeta.Labels[key] = value
actual.Labels[key] = value
}
actual.Spec.Type = config.Type
actual.Spec.LoadBalancerIP = config.LoadBalancerIP
@ -40,12 +40,12 @@ func UpdateService(actual corev1.Service, config v1alpha2.Service, targetPort in
// GetJenkinsHTTPServiceName returns Kubernetes service name used for expose Jenkins HTTP endpoint
func GetJenkinsHTTPServiceName(jenkins *v1alpha2.Jenkins) string {
return fmt.Sprintf("%s-http-%s", constants.OperatorName, jenkins.ObjectMeta.Name)
return fmt.Sprintf("%s-http-%s", constants.OperatorName, jenkins.Name)
}
// GetJenkinsSlavesServiceName returns Kubernetes service name used for expose Jenkins slave endpoint
func GetJenkinsSlavesServiceName(jenkins *v1alpha2.Jenkins) string {
return fmt.Sprintf("%s-slave-%s", constants.OperatorName, jenkins.ObjectMeta.Name)
return fmt.Sprintf("%s-slave-%s", constants.OperatorName, jenkins.Name)
}
// GetJenkinsHTTPServiceFQDN returns Kubernetes service FQDN used for expose Jenkins HTTP endpoint
@ -55,7 +55,7 @@ func GetJenkinsHTTPServiceFQDN(jenkins *v1alpha2.Jenkins, kubernetesClusterDomai
return "", err
}
return fmt.Sprintf("%s-http-%s.%s.svc.%s", constants.OperatorName, jenkins.ObjectMeta.Name, jenkins.ObjectMeta.Namespace, clusterDomain), nil
return fmt.Sprintf("%s-http-%s.%s.svc.%s", constants.OperatorName, jenkins.Name, jenkins.Namespace, clusterDomain), nil
}
// GetJenkinsSlavesServiceFQDN returns Kubernetes service FQDN used for expose Jenkins slave endpoint
@ -65,7 +65,7 @@ func GetJenkinsSlavesServiceFQDN(jenkins *v1alpha2.Jenkins, kubernetesClusterDom
return "", err
}
return fmt.Sprintf("%s-slave-%s.%s.svc.%s", constants.OperatorName, jenkins.ObjectMeta.Name, jenkins.ObjectMeta.Namespace, clusterDomain), nil
return fmt.Sprintf("%s-slave-%s.%s.svc.%s", constants.OperatorName, jenkins.Name, jenkins.Namespace, clusterDomain), nil
}
// GetClusterDomain returns Kubernetes cluster domain, default to "cluster.local"

View File

@ -18,7 +18,7 @@ import (
// createRoute takes the ServiceName and Creates the Route based on it
func (r *JenkinsBaseConfigurationReconciler) createRoute(meta metav1.ObjectMeta, serviceName string, config *v1alpha2.Jenkins) error {
route := routev1.Route{}
name := fmt.Sprintf("jenkins-%s", config.ObjectMeta.Name)
name := fmt.Sprintf("jenkins-%s", config.Name)
err := r.Client.Get(context.TODO(), types.NamespacedName{Name: name, Namespace: meta.Namespace}, &route)
if err != nil && apierrors.IsNotFound(err) {
port := &routev1.RoutePort{
@ -52,7 +52,7 @@ func (r *JenkinsBaseConfigurationReconciler) createRoute(meta metav1.ObjectMeta,
return stackerr.WithStack(err)
}
route.ObjectMeta.Labels = meta.Labels // make sure that user won't break service by hand
route.Labels = meta.Labels // make sure that user won't break service by hand
route = resources.UpdateRoute(route, config)
return stackerr.WithStack(r.UpdateResource(&route))
}

View File

@ -17,7 +17,7 @@ import (
func (r *JenkinsBaseConfigurationReconciler) createServiceAccount(meta metav1.ObjectMeta) error {
serviceAccount := &corev1.ServiceAccount{}
err := r.Client.Get(context.TODO(), types.NamespacedName{Name: meta.Name, Namespace: meta.Namespace}, serviceAccount)
annotations := r.Configuration.Jenkins.Spec.ServiceAccount.Annotations
annotations := r.Jenkins.Spec.ServiceAccount.Annotations
msg := fmt.Sprintf("createServiceAccount with annotations %v", annotations)
r.logger.V(log.VDebug).Info(msg)
if err != nil && apierrors.IsNotFound(err) {

View File

@ -52,12 +52,12 @@ func (r *JenkinsBaseConfigurationReconciler) Validate(jenkins *v1alpha2.Jenkins)
messages = append(messages, msg...)
}
if msg, err := r.validateCustomization(r.Configuration.Jenkins.Spec.GroovyScripts.Customization, "spec.groovyScripts"); err != nil {
if msg, err := r.validateCustomization(r.Jenkins.Spec.GroovyScripts.Customization, "spec.groovyScripts"); err != nil {
return nil, err
} else if len(msg) > 0 {
messages = append(messages, msg...)
}
if msg, err := r.validateCustomization(r.Configuration.Jenkins.Spec.ConfigurationAsCode.Customization, "spec.configurationAsCode"); err != nil {
if msg, err := r.validateCustomization(r.Jenkins.Spec.ConfigurationAsCode.Customization, "spec.configurationAsCode"); err != nil {
return nil, err
} else if len(msg) > 0 {
messages = append(messages, msg...)
@ -71,7 +71,7 @@ func (r *JenkinsBaseConfigurationReconciler) Validate(jenkins *v1alpha2.Jenkins)
}
func (r *JenkinsBaseConfigurationReconciler) validateJenkinsMasterContainerCommand() []string {
masterContainer := r.Configuration.GetJenkinsMasterContainer()
masterContainer := r.GetJenkinsMasterContainer()
if masterContainer == nil {
return []string{}
}
@ -104,7 +104,7 @@ func (r *JenkinsBaseConfigurationReconciler) validateJenkinsMasterContainerComma
func (r *JenkinsBaseConfigurationReconciler) validateImagePullSecrets() ([]string, error) {
var messages []string
for _, sr := range r.Configuration.Jenkins.Spec.Master.ImagePullSecrets {
for _, sr := range r.Jenkins.Spec.Master.ImagePullSecrets {
msg, err := r.validateImagePullSecret(sr.Name)
if err != nil {
return nil, err
@ -119,7 +119,7 @@ func (r *JenkinsBaseConfigurationReconciler) validateImagePullSecrets() ([]strin
func (r *JenkinsBaseConfigurationReconciler) validateImagePullSecret(secretName string) ([]string, error) {
var messages []string
secret := &corev1.Secret{}
err := r.Client.Get(context.TODO(), types.NamespacedName{Name: secretName, Namespace: r.Configuration.Jenkins.ObjectMeta.Namespace}, secret)
err := r.Client.Get(context.TODO(), types.NamespacedName{Name: secretName, Namespace: r.Jenkins.Namespace}, secret)
if err != nil && apierrors.IsNotFound(err) {
messages = append(messages, fmt.Sprintf("Secret %s not found defined in spec.master.imagePullSecrets", secretName))
} else if err != nil && !apierrors.IsNotFound(err) {
@ -144,7 +144,7 @@ func (r *JenkinsBaseConfigurationReconciler) validateImagePullSecret(secretName
func (r *JenkinsBaseConfigurationReconciler) validateVolumes() ([]string, error) {
var messages []string
for _, volume := range r.Configuration.Jenkins.Spec.Master.Volumes {
for _, volume := range r.Jenkins.Spec.Master.Volumes {
switch {
case volume.ConfigMap != nil:
if msg, err := r.validateConfigMapVolume(volume); err != nil {
@ -174,7 +174,7 @@ func (r *JenkinsBaseConfigurationReconciler) validatePersistentVolumeClaim(volum
var messages []string
pvc := &corev1.PersistentVolumeClaim{}
err := r.Client.Get(context.TODO(), types.NamespacedName{Name: volume.PersistentVolumeClaim.ClaimName, Namespace: r.Configuration.Jenkins.ObjectMeta.Namespace}, pvc)
err := r.Client.Get(context.TODO(), types.NamespacedName{Name: volume.PersistentVolumeClaim.ClaimName, Namespace: r.Jenkins.Namespace}, pvc)
if err != nil && apierrors.IsNotFound(err) {
messages = append(messages, fmt.Sprintf("PersistentVolumeClaim '%s' not found for volume '%v'", volume.PersistentVolumeClaim.ClaimName, volume))
} else if err != nil && !apierrors.IsNotFound(err) {
@ -191,7 +191,7 @@ func (r *JenkinsBaseConfigurationReconciler) validateConfigMapVolume(volume core
}
configMap := &corev1.ConfigMap{}
err := r.Client.Get(context.TODO(), types.NamespacedName{Name: volume.ConfigMap.Name, Namespace: r.Configuration.Jenkins.ObjectMeta.Namespace}, configMap)
err := r.Client.Get(context.TODO(), types.NamespacedName{Name: volume.ConfigMap.Name, Namespace: r.Jenkins.Namespace}, configMap)
if err != nil && apierrors.IsNotFound(err) {
messages = append(messages, fmt.Sprintf("ConfigMap '%s' not found for volume '%v'", volume.ConfigMap.Name, volume))
} else if err != nil && !apierrors.IsNotFound(err) {
@ -208,7 +208,7 @@ func (r *JenkinsBaseConfigurationReconciler) validateSecretVolume(volume corev1.
}
secret := &corev1.Secret{}
err := r.Client.Get(context.TODO(), types.NamespacedName{Name: volume.Secret.SecretName, Namespace: r.Configuration.Jenkins.ObjectMeta.Namespace}, secret)
err := r.Client.Get(context.TODO(), types.NamespacedName{Name: volume.Secret.SecretName, Namespace: r.Jenkins.Namespace}, secret)
if err != nil && apierrors.IsNotFound(err) {
messages = append(messages, fmt.Sprintf("Secret '%s' not found for volume '%v'", volume.Secret.SecretName, volume))
} else if err != nil && !apierrors.IsNotFound(err) {
@ -221,8 +221,8 @@ func (r *JenkinsBaseConfigurationReconciler) validateSecretVolume(volume corev1.
func (r *JenkinsBaseConfigurationReconciler) validateReservedVolumes() []string {
var messages []string
for _, baseVolume := range resources.GetJenkinsMasterPodBaseVolumes(r.Configuration.Jenkins) {
for _, volume := range r.Configuration.Jenkins.Spec.Master.Volumes {
for _, baseVolume := range resources.GetJenkinsMasterPodBaseVolumes(r.Jenkins) {
for _, volume := range r.Jenkins.Spec.Master.Volumes {
if baseVolume.Name == volume.Name {
messages = append(messages, fmt.Sprintf("Jenkins Master pod volume '%s' is reserved please choose different one", volume.Name))
}
@ -255,7 +255,7 @@ func (r *JenkinsBaseConfigurationReconciler) validateContainer(container v1alpha
func (r *JenkinsBaseConfigurationReconciler) validateContainerVolumeMounts(container v1alpha2.Container) []string {
var messages []string
allVolumes := append(resources.GetJenkinsMasterPodBaseVolumes(r.Configuration.Jenkins), r.Configuration.Jenkins.Spec.Master.Volumes...)
allVolumes := append(resources.GetJenkinsMasterPodBaseVolumes(r.Jenkins), r.Jenkins.Spec.Master.Volumes...)
for _, volumeMount := range container.VolumeMounts {
if len(volumeMount.MountPath) == 0 {
@ -279,14 +279,14 @@ func (r *JenkinsBaseConfigurationReconciler) validateContainerVolumeMounts(conta
func (r *JenkinsBaseConfigurationReconciler) validateJenkinsMasterPodEnvs() []string {
var messages []string
baseEnvs := resources.GetJenkinsMasterContainerBaseEnvs(r.Configuration.Jenkins)
baseEnvs := resources.GetJenkinsMasterContainerBaseEnvs(r.Jenkins)
baseEnvNames := map[string]string{}
for _, env := range baseEnvs {
baseEnvNames[env.Name] = env.Value
}
javaOpts := corev1.EnvVar{}
for _, userEnv := range r.Configuration.Jenkins.Spec.Master.Containers[0].Env {
for _, userEnv := range r.Jenkins.Spec.Master.Containers[0].Env {
if userEnv.Name == constants.JavaOpsVariableName {
javaOpts = userEnv
}
@ -383,7 +383,7 @@ func (r *JenkinsBaseConfigurationReconciler) validateCustomization(customization
if len(customization.Secret.Name) > 0 {
secret := &corev1.Secret{}
err := r.Client.Get(context.TODO(), types.NamespacedName{Name: customization.Secret.Name, Namespace: r.Configuration.Jenkins.ObjectMeta.Namespace}, secret)
err := r.Client.Get(context.TODO(), types.NamespacedName{Name: customization.Secret.Name, Namespace: r.Jenkins.Namespace}, secret)
if err != nil && apierrors.IsNotFound(err) {
messages = append(messages, fmt.Sprintf("Secret '%s' configured in %s.secret.name not found", customization.Secret.Name, name))
} else if err != nil && !apierrors.IsNotFound(err) {
@ -398,7 +398,7 @@ func (r *JenkinsBaseConfigurationReconciler) validateCustomization(customization
}
configMap := &corev1.ConfigMap{}
err := r.Client.Get(context.TODO(), types.NamespacedName{Name: configMapRef.Name, Namespace: r.Configuration.Jenkins.ObjectMeta.Namespace}, configMap)
err := r.Client.Get(context.TODO(), types.NamespacedName{Name: configMapRef.Name, Namespace: r.Jenkins.Namespace}, configMap)
if err != nil && apierrors.IsNotFound(err) {
messages = append(messages, fmt.Sprintf("ConfigMap '%s' configured in %s.configurations[%d] not found", configMapRef.Name, name, index))
} else if err != nil && !apierrors.IsNotFound(err) {

View File

@ -137,7 +137,7 @@ func TestReconcileJenkinsBaseConfiguration_validateImagePullSecrets(t *testing.T
Spec: v1alpha2.JenkinsSpec{
Master: v1alpha2.JenkinsMaster{
ImagePullSecrets: []corev1.LocalObjectReference{
{Name: secret.ObjectMeta.Name},
{Name: secret.Name},
},
},
},
@ -197,7 +197,7 @@ func TestReconcileJenkinsBaseConfiguration_validateImagePullSecrets(t *testing.T
Spec: v1alpha2.JenkinsSpec{
Master: v1alpha2.JenkinsMaster{
ImagePullSecrets: []corev1.LocalObjectReference{
{Name: secret.ObjectMeta.Name},
{Name: secret.Name},
},
},
},
@ -233,7 +233,7 @@ func TestReconcileJenkinsBaseConfiguration_validateImagePullSecrets(t *testing.T
Spec: v1alpha2.JenkinsSpec{
Master: v1alpha2.JenkinsMaster{
ImagePullSecrets: []corev1.LocalObjectReference{
{Name: secret.ObjectMeta.Name},
{Name: secret.Name},
},
},
},
@ -269,7 +269,7 @@ func TestReconcileJenkinsBaseConfiguration_validateImagePullSecrets(t *testing.T
Spec: v1alpha2.JenkinsSpec{
Master: v1alpha2.JenkinsMaster{
ImagePullSecrets: []corev1.LocalObjectReference{
{Name: secret.ObjectMeta.Name},
{Name: secret.Name},
},
},
},
@ -305,7 +305,7 @@ func TestReconcileJenkinsBaseConfiguration_validateImagePullSecrets(t *testing.T
Spec: v1alpha2.JenkinsSpec{
Master: v1alpha2.JenkinsMaster{
ImagePullSecrets: []corev1.LocalObjectReference{
{Name: secret.ObjectMeta.Name},
{Name: secret.Name},
},
},
},
@ -616,7 +616,8 @@ func TestValidateConfigMapVolume(t *testing.T) {
assert.NoError(t, err)
assert.Equal(t, got, []string{"ConfigMap 'configmap-name' not found for volume '{volume-name {nil nil nil nil nil nil nil nil nil nil nil nil nil nil nil nil nil nil &ConfigMapVolumeSource{LocalObjectReference:LocalObjectReference{Name:configmap-name,},Items:[]KeyToPath{},DefaultMode:nil,Optional:*false,} nil nil nil nil nil nil nil nil nil nil}}'"})
assert.Len(t, got, 1)
assert.Contains(t, got[0], "ConfigMap 'configmap-name' not found for volume")
})
}
@ -689,8 +690,8 @@ func TestValidateSecretVolume(t *testing.T) {
got, err := baseReconcileLoop.validateSecretVolume(volume)
assert.NoError(t, err)
assert.Equal(t, got, []string{"Secret 'secret-name' not found for volume '{volume-name {nil nil nil nil nil &SecretVolumeSource{SecretName:secret-name,Items:[]KeyToPath{},DefaultMode:nil,Optional:*false,} nil nil nil nil nil nil nil nil nil nil nil nil nil nil nil nil nil nil nil nil nil nil nil}}'"})
assert.Len(t, got, 1)
assert.Contains(t, got[0], "Secret 'secret-name' not found for volume")
})
}

View File

@ -83,7 +83,7 @@ func (c *Configuration) GetJenkinsDeployment() (*appsv1.Deployment, error) {
// IsJenkinsTerminating returns true if the Jenkins pod is terminating.
func (c *Configuration) IsJenkinsTerminating(pod corev1.Pod) bool {
return pod.ObjectMeta.DeletionTimestamp != nil
return pod.DeletionTimestamp != nil
}
// CreateResource is creating kubernetes resource and references it to Jenkins CR
@ -193,7 +193,7 @@ func (c *Configuration) getJenkinsAPIUrl() (string, error) {
var service corev1.Service
err := c.Client.Get(context.TODO(), types.NamespacedName{
Namespace: c.Jenkins.ObjectMeta.Namespace,
Namespace: c.Jenkins.Namespace,
Name: resources.GetJenkinsHTTPServiceName(c.Jenkins),
}, &service)
@ -230,7 +230,7 @@ func (c *Configuration) GetJenkinsClientFromSecret() (jenkinsclient.Jenkins, err
return nil, err
}
credentialsSecret := &corev1.Secret{}
err = c.Client.Get(context.TODO(), types.NamespacedName{Name: resources.GetOperatorCredentialsSecretName(c.Jenkins), Namespace: c.Jenkins.ObjectMeta.Namespace}, credentialsSecret)
err = c.Client.Get(context.TODO(), types.NamespacedName{Name: resources.GetOperatorCredentialsSecretName(c.Jenkins), Namespace: c.Jenkins.Namespace}, credentialsSecret)
if err != nil {
return nil, stackerr.WithStack(err)
}

View File

@ -2,6 +2,7 @@ package user
import (
"strings"
"time"
"github.com/jenkinsci/kubernetes-operator/api/v1alpha2"
jenkinsclient "github.com/jenkinsci/kubernetes-operator/pkg/client"
@ -45,7 +46,7 @@ func (r *reconcileUserConfiguration) ReconcileCasc() (reconcile.Result, error) {
if err != nil {
return reconcile.Result{}, err
}
if result.Requeue {
if result.RequeueAfter > 0 {
return result, nil
}
@ -72,7 +73,7 @@ func (r *reconcileUserConfiguration) ReconcileOthers() (reconcile.Result, error)
if err != nil {
return reconcile.Result{}, err
}
if result.Requeue {
if result.RequeueAfter > 0 {
return result, nil
}
@ -81,19 +82,19 @@ func (r *reconcileUserConfiguration) ReconcileOthers() (reconcile.Result, error)
func (r *reconcileUserConfiguration) ensureSeedJobs() (reconcile.Result, error) {
seedJobs := seedjobs.New(r.jenkinsClient, r.Configuration)
done, err := seedJobs.EnsureSeedJobs(r.Configuration.Jenkins)
done, err := seedJobs.EnsureSeedJobs(r.Jenkins)
if err != nil {
return reconcile.Result{}, err
}
if !done {
return reconcile.Result{Requeue: true}, nil
return reconcile.Result{RequeueAfter: time.Second * 30}, nil
}
return reconcile.Result{}, nil
}
func (r *reconcileUserConfiguration) ensureCasc(jenkinsClient jenkinsclient.Jenkins) (reconcile.Result, error) {
configurationAsCodeClient := casc.New(jenkinsClient, r.Client, r.Configuration.Jenkins)
requeue, err := configurationAsCodeClient.Ensure(r.Configuration.Jenkins)
configurationAsCodeClient := casc.New(jenkinsClient, r.Client, r.Jenkins)
requeue, err := configurationAsCodeClient.Ensure(r.Jenkins)
if err != nil {
return reconcile.Result{}, err
}
@ -101,7 +102,7 @@ func (r *reconcileUserConfiguration) ensureCasc(jenkinsClient jenkinsclient.Jenk
return reconcile.Result{Requeue: true}, nil
}
groovyClient := groovy.New(jenkinsClient, r.Client, r.Configuration.Jenkins, "user-groovy", r.Configuration.Jenkins.Spec.GroovyScripts.Customization)
groovyClient := groovy.New(jenkinsClient, r.Client, r.Jenkins, "user-groovy", r.Jenkins.Spec.GroovyScripts.Customization)
requeue, err = groovyClient.WaitForSecretSynchronization(resources.GroovyScriptsSecretVolumePath)
if err != nil {
return reconcile.Result{}, err

View File

@ -311,14 +311,14 @@ func (s *seedJobs) ensureLabelsForSecrets(jenkins v1alpha2.Jenkins) error {
requiredLabels[JenkinsCredentialTypeLabelName] = string(seedJob.JenkinsCredentialType)
secret := &corev1.Secret{}
namespaceName := types.NamespacedName{Namespace: jenkins.ObjectMeta.Namespace, Name: seedJob.CredentialID}
namespaceName := types.NamespacedName{Namespace: jenkins.Namespace, Name: seedJob.CredentialID}
err := s.Client.Get(context.TODO(), namespaceName, secret)
if err != nil {
return stackerr.WithStack(err)
}
if !resources.VerifyIfLabelsAreSet(secret, requiredLabels) {
secret.ObjectMeta.Labels = requiredLabels
secret.Labels = requiredLabels
if err = s.Client.Update(context.TODO(), secret); err != nil {
return stackerr.WithStack(err)
}

View File

@ -166,21 +166,21 @@ func validateBasicSSHSecret(secret v1.Secret) []string {
var messages []string
username, exists := secret.Data[UsernameSecretKey]
if !exists {
messages = append(messages, fmt.Sprintf("required data '%s' not found in secret '%s'", UsernameSecretKey, secret.ObjectMeta.Name))
messages = append(messages, fmt.Sprintf("required data '%s' not found in secret '%s'", UsernameSecretKey, secret.Name))
}
if len(username) == 0 {
messages = append(messages, fmt.Sprintf("required data '%s' is empty in secret '%s'", UsernameSecretKey, secret.ObjectMeta.Name))
messages = append(messages, fmt.Sprintf("required data '%s' is empty in secret '%s'", UsernameSecretKey, secret.Name))
}
privateKey, exists := secret.Data[PrivateKeySecretKey]
if !exists {
messages = append(messages, fmt.Sprintf("required data '%s' not found in secret '%s'", PrivateKeySecretKey, secret.ObjectMeta.Name))
messages = append(messages, fmt.Sprintf("required data '%s' not found in secret '%s'", PrivateKeySecretKey, secret.Name))
}
if len(string(privateKey)) == 0 {
messages = append(messages, fmt.Sprintf("required data '%s' not found in secret '%s'", PrivateKeySecretKey, secret.ObjectMeta.Name))
messages = append(messages, fmt.Sprintf("required data '%s' not found in secret '%s'", PrivateKeySecretKey, secret.Name))
}
if err := validatePrivateKey(string(privateKey)); err != nil {
messages = append(messages, fmt.Sprintf("private key '%s' invalid in secret '%s': %s", PrivateKeySecretKey, secret.ObjectMeta.Name, err))
messages = append(messages, fmt.Sprintf("private key '%s' invalid in secret '%s': %s", PrivateKeySecretKey, secret.Name, err))
}
return messages
@ -190,17 +190,17 @@ func validateUsernamePasswordSecret(secret v1.Secret) []string {
var messages []string
username, exists := secret.Data[UsernameSecretKey]
if !exists {
messages = append(messages, fmt.Sprintf("required data '%s' not found in secret '%s'", UsernameSecretKey, secret.ObjectMeta.Name))
messages = append(messages, fmt.Sprintf("required data '%s' not found in secret '%s'", UsernameSecretKey, secret.Name))
}
if len(username) == 0 {
messages = append(messages, fmt.Sprintf("required data '%s' is empty in secret '%s'", UsernameSecretKey, secret.ObjectMeta.Name))
messages = append(messages, fmt.Sprintf("required data '%s' is empty in secret '%s'", UsernameSecretKey, secret.Name))
}
password, exists := secret.Data[PasswordSecretKey]
if !exists {
messages = append(messages, fmt.Sprintf("required data '%s' not found in secret '%s'", PasswordSecretKey, secret.ObjectMeta.Name))
messages = append(messages, fmt.Sprintf("required data '%s' not found in secret '%s'", PasswordSecretKey, secret.Name))
}
if len(password) == 0 {
messages = append(messages, fmt.Sprintf("required data '%s' is empty in secret '%s'", PasswordSecretKey, secret.ObjectMeta.Name))
messages = append(messages, fmt.Sprintf("required data '%s' is empty in secret '%s'", PasswordSecretKey, secret.Name))
}
return messages
@ -210,17 +210,17 @@ func validateGithubAppSecret(secret v1.Secret) []string {
var messages []string
appid, exists := secret.Data[AppIDSecretKey]
if !exists {
messages = append(messages, fmt.Sprintf("required data '%s' not found in secret '%s'", AppIDSecretKey, secret.ObjectMeta.Name))
messages = append(messages, fmt.Sprintf("required data '%s' not found in secret '%s'", AppIDSecretKey, secret.Name))
}
if len(appid) == 0 {
messages = append(messages, fmt.Sprintf("required data '%s' is empty in secret '%s'", AppIDSecretKey, secret.ObjectMeta.Name))
messages = append(messages, fmt.Sprintf("required data '%s' is empty in secret '%s'", AppIDSecretKey, secret.Name))
}
pkey, exists := secret.Data[PrivateKeySecretKey]
if !exists {
messages = append(messages, fmt.Sprintf("required data '%s' not found in secret '%s'", PrivateKeySecretKey, secret.ObjectMeta.Name))
messages = append(messages, fmt.Sprintf("required data '%s' not found in secret '%s'", PrivateKeySecretKey, secret.Name))
}
if len(pkey) == 0 {
messages = append(messages, fmt.Sprintf("required data '%s' is empty in secret '%s'", PrivateKeySecretKey, secret.ObjectMeta.Name))
messages = append(messages, fmt.Sprintf("required data '%s' is empty in secret '%s'", PrivateKeySecretKey, secret.Name))
}
return messages

View File

@ -87,7 +87,7 @@ func (g *Groovy) WaitForSecretSynchronization(secretsPath string) (requeue bool,
}
secret := &corev1.Secret{}
err = g.k8sClient.Get(context.TODO(), types.NamespacedName{Name: g.customization.Secret.Name, Namespace: g.jenkins.ObjectMeta.Namespace}, secret)
err = g.k8sClient.Get(context.TODO(), types.NamespacedName{Name: g.customization.Secret.Name, Namespace: g.jenkins.Namespace}, secret)
if err != nil {
return true, errors.WithStack(err)
}
@ -116,7 +116,7 @@ func (g *Groovy) Ensure(filter func(name string) bool, updateGroovyScript func(g
if len(g.customization.Secret.Name) > 0 {
err := g.k8sClient.Get(context.TODO(), types.NamespacedName{
Name: g.customization.Secret.Name,
Namespace: g.jenkins.ObjectMeta.Namespace,
Namespace: g.jenkins.Namespace,
}, secret)
if err != nil {
return true, err
@ -125,7 +125,7 @@ func (g *Groovy) Ensure(filter func(name string) bool, updateGroovyScript func(g
for _, configMapRef := range g.customization.Configurations {
configMap := &corev1.ConfigMap{}
err := g.k8sClient.Get(context.TODO(), types.NamespacedName{Name: configMapRef.Name, Namespace: g.jenkins.ObjectMeta.Namespace}, configMap)
err := g.k8sClient.Get(context.TODO(), types.NamespacedName{Name: configMapRef.Name, Namespace: g.jenkins.Namespace}, configMap)
if err != nil {
return true, errors.WithStack(err)
}

View File

@ -125,7 +125,7 @@ func verifyJenkinsMasterPodAttributes(jenkins *v1alpha2.Jenkins) {
defaultGracePeriod := constants.DefaultTerminationGracePeriodSeconds
assertMapContainsElementsFromAnotherMap(jenkins.Spec.Master.Annotations, jenkinsPod.ObjectMeta.Annotations)
assertMapContainsElementsFromAnotherMap(jenkins.Spec.Master.Annotations, jenkinsPod.Annotations)
Expect(jenkinsPod.Spec.NodeSelector).Should(Equal(jenkins.Spec.Master.NodeSelector))
Expect(jenkinsPod.Spec.Containers[0].Name).Should(Equal(resources.JenkinsMasterContainerName))

View File

@ -175,7 +175,7 @@ func createJenkinsAPIClientFromServiceAccount(jenkins *v1alpha2.Jenkins, jenkins
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"})
token, _, err := r.Exec(podName, resources.JenkinsMasterContainerName, []string{"cat", "/var/run/secrets/kubernetes.io/serviceaccount/token"})
if err != nil {
return nil, err
}
@ -255,7 +255,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
serviceName := constants.OperatorName + "-" + serviceKind + "-" + jenkins.Name
Expect(K8sClient.Get(context.TODO(), client.ObjectKey{Name: serviceName, Namespace: jenkins.Namespace}, service)).Should(Succeed())
return service

View File

@ -1,9 +1,11 @@
package e2e
import (
"context"
"flag"
"path/filepath"
"testing"
"time"
"github.com/jenkinsci/kubernetes-operator/api/v1alpha2"
controllers "github.com/jenkinsci/kubernetes-operator/internal/controller"
@ -21,9 +23,12 @@ import (
"sigs.k8s.io/controller-runtime/pkg/envtest"
logf "sigs.k8s.io/controller-runtime/pkg/log"
"sigs.k8s.io/controller-runtime/pkg/log/zap"
metricsserver "sigs.k8s.io/controller-runtime/pkg/metrics/server"
// +kubebuilder:scaffold:imports
)
var managerCancel context.CancelFunc
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.")
@ -60,6 +65,9 @@ var _ = BeforeSuite(func(done Done) {
// setup manager
k8sManager, err := ctrl.NewManager(Cfg, ctrl.Options{
Scheme: scheme.Scheme,
Metrics: metricsserver.Options{
BindAddress: "0", // Disable metrics server to avoid port conflicts
},
})
Expect(err).NotTo(HaveOccurred())
@ -90,18 +98,32 @@ var _ = BeforeSuite(func(done Done) {
}).SetupWithManager(k8sManager)
Expect(err).NotTo(HaveOccurred())
ctx, cancel := context.WithCancel(context.Background())
go func() {
err = k8sManager.Start(ctrl.SetupSignalHandler())
defer GinkgoRecover()
err = k8sManager.Start(ctx)
Expect(err).NotTo(HaveOccurred())
}()
// Give the manager time to start up
time.Sleep(2 * time.Second)
K8sClient = k8sManager.GetClient()
Expect(K8sClient).NotTo(BeNil())
// Store cancel function for cleanup in AfterSuite
managerCancel = cancel
// Store cancel function for cleanup in AfterSuite
// The cancel function will be called in AfterSuite
close(done)
}, 60)
var _ = AfterSuite(func() {
By("tearing down the test environment")
if managerCancel != nil {
managerCancel()
}
err := testEnv.Stop()
Expect(err).NotTo(HaveOccurred())
})