diff --git a/api/v1alpha2/jenkins_types.go b/api/v1alpha2/jenkins_types.go index 58a14c03..a64d1adf 100644 --- a/api/v1alpha2/jenkins_types.go +++ b/api/v1alpha2/jenkins_types.go @@ -346,7 +346,7 @@ type JenkinsMaster struct { // Storage settings for the jenkins home directory // Can be tempDir or ephemeral // +optional - StorageSettings StorageSettings `json:"storage,omitempty"` + StorageSettings StorageSettings `json:"storageSettings,omitempty"` // If specified, the pod's tolerations. // +optional @@ -392,11 +392,16 @@ type JenkinsMaster struct { HostAliases []corev1.HostAlias `json:"hostAliases,omitempty"` } -// StorageSettings defines Jenkins master pod persistance attributes. +// StorageSettings defines Jenkins master pod persistence attributes. +// +optional type StorageSettings struct { - UseTempDir bool `json:"useTempDir"` + // UseEmptyDir allows you to create an emptydir as jenkins-home, also this is the default + UseEmptyDir bool `json:"useEmptyDir"` + // UseEphemeralStorage allows you to create kubernetes ephemeral pvc, see "https://kubernetes.io/docs/concepts/storage/ephemeral-volumes/#generic-ephemeral-volumes" UseEphemeralStorage bool `json:"useEphemeralStorage"` + // StorageClassName in case of ephemeral pvc, can be empty StorageClassName string `json:"storageClassName"` + // StorageRequest allows you to specify the storage request, required in case of ephemeral pvc StorageRequest string `json:"storageRequest"` } diff --git a/api/v1alpha2/zz_generated.deepcopy.go b/api/v1alpha2/zz_generated.deepcopy.go index cfd1b305..2002dca0 100644 --- a/api/v1alpha2/zz_generated.deepcopy.go +++ b/api/v1alpha2/zz_generated.deepcopy.go @@ -1,4 +1,3 @@ -//go:build !ignore_autogenerated // +build !ignore_autogenerated /* @@ -23,7 +22,7 @@ package v1alpha2 import ( corev1 "k8s.io/api/core/v1" - rbacv1 "k8s.io/api/rbac/v1" + "k8s.io/api/rbac/v1" "k8s.io/apimachinery/pkg/runtime" ) @@ -340,6 +339,7 @@ func (in *JenkinsMaster) DeepCopyInto(out *JenkinsMaster) { (*in)[i].DeepCopyInto(&(*out)[i]) } } + out.StorageSettings = in.StorageSettings if in.Tolerations != nil { in, out := &in.Tolerations, &out.Tolerations *out = make([]corev1.Toleration, len(*in)) @@ -400,7 +400,7 @@ func (in *JenkinsSpec) DeepCopyInto(out *JenkinsSpec) { in.ConfigurationAsCode.DeepCopyInto(&out.ConfigurationAsCode) if in.Roles != nil { in, out := &in.Roles, &out.Roles - *out = make([]rbacv1.RoleRef, len(*in)) + *out = make([]v1.RoleRef, len(*in)) copy(*out, *in) } in.ServiceAccount.DeepCopyInto(&out.ServiceAccount) @@ -763,6 +763,21 @@ func (in *Slack) DeepCopy() *Slack { return out } +// DeepCopyInto is an autogenerated deepcopy function, copying the receiver, writing into out. in must be non-nil. +func (in *StorageSettings) DeepCopyInto(out *StorageSettings) { + *out = *in +} + +// DeepCopy is an autogenerated deepcopy function, copying the receiver, creating a new StorageSettings. +func (in *StorageSettings) DeepCopy() *StorageSettings { + if in == nil { + return nil + } + out := new(StorageSettings) + in.DeepCopyInto(out) + return out +} + // DeepCopyInto is an autogenerated deepcopy function, copying the receiver, writing into out. in must be non-nil. func (in *Version) DeepCopyInto(out *Version) { *out = *in diff --git a/chart/jenkins-operator/crds/jenkins-crd.yaml b/chart/jenkins-operator/crds/jenkins-crd.yaml index b00bb4f4..a0194bb2 100644 --- a/chart/jenkins-operator/crds/jenkins-crd.yaml +++ b/chart/jenkins-operator/crds/jenkins-crd.yaml @@ -157,11 +157,11 @@ spec: type: object basePlugins: description: 'BasePlugins contains plugins required by operator - Defaults to : - name: kubernetes version: "1.31.3" - name: - workflow-job version: "1145.v7f2433caa07f" - name: workflow-aggregator version: - "2.6" - name: git version: "4.11.3" - name: job-dsl version: - "1.78.1" - name: configuration-as-code version: "1346.ve8cfa_3473c94" - name: - kubernetes-credentials-provider version: "0.20"' + Defaults to : - name: configuration-as-code version: "1625.v27444588cc3d" + - name: git version: "5.0.0" - name: job-dsl version: "1.83" + - name: kubernetes version: "3909.v1f2c633e8590" - name: kubernetes-credentials-provider + version: "1.211.vc236a_f5a_2f3c" - name: workflow-aggregator + version: "596.v8c21c963d92d" - name: workflow-job version: "1289.vd1c337fd5354"' items: description: Plugin defines Jenkins plugin. properties: @@ -1100,11 +1100,6 @@ spec: - resources type: object type: array - latestPlugins: - description: 'Allow to override jenkins-plugin-cli default behavior - while downloading the plugin and dependencies, see: - https://github.com/jenkinsci/plugin-installation-manager-tool#cli-options' - type: boolean disableCSRFProtection: description: DisableCSRFProtection allows you to toggle CSRF Protection on Jenkins @@ -1150,6 +1145,10 @@ spec: selectors of replication controllers and services. More info: http://kubernetes.io/docs/user-guide/labels' type: object + latestPlugins: + description: 'Allow to override jenkins-plugin-cli default behavior + while downloading the plugin and dependencies see: https://github.com/jenkinsci/plugin-installation-manager-tool#cli-options' + type: boolean nodeSelector: additionalProperties: type: string @@ -1331,6 +1330,32 @@ spec: type: string type: object type: object + storageSettings: + description: Storage settings for the jenkins home directory Can + be tempDir or ephemeral + properties: + storageClassName: + description: StorageClassName in case of ephemeral pvc, can + be empty + type: string + storageRequest: + description: StorageRequest allows you to specify the storage + request, required in case of ephemeral pvc + type: string + useEmptyDir: + description: UseEmptyDir allows you to create an emptydir + as jenkins-home, also this is the default + type: boolean + useEphemeralStorage: + description: UseEphemeralStorage allows you to create kubernetes + ephemeral pvc, see "https://kubernetes.io/docs/concepts/storage/ephemeral-volumes/#generic-ephemeral-volumes" + type: boolean + required: + - storageClassName + - storageRequest + - useEmptyDir + - useEphemeralStorage + type: object tolerations: description: If specified, the pod's tolerations. items: @@ -2864,6 +2889,7 @@ spec: type: array required: - disableCSRFProtection + - latestPlugins type: object notifications: description: Notifications defines list of a services which are used @@ -3124,8 +3150,10 @@ spec: type: object type: array seedJobAgentImage: + description: SeedJobAgentImage defines the image that will be used + by the seed job agent. If not defined jenkins/inbound-agent:4.9-1 + will be used. type: string - description: 'SeedJobAgentImage defines the image that will be used by the seed job agent. If not defined jenkins/inbound-agent:4.10-3 will be used.' seedJobs: description: 'SeedJobs defines list of Jenkins Seed Job configurations More info: https://jenkinsci.github.io/kubernetes-operator/docs/getting-started/latest/configuration#configure-seed-jobs-and-pipelines' diff --git a/chart/jenkins-operator/templates/jenkins.yaml b/chart/jenkins-operator/templates/jenkins.yaml index 1ce3b0cc..0fa31605 100644 --- a/chart/jenkins-operator/templates/jenkins.yaml +++ b/chart/jenkins-operator/templates/jenkins.yaml @@ -146,6 +146,9 @@ spec: {{- end }} {{- end }} {{- with .Values.jenkins.volumes }} + storageSettings: {{- toYaml . | nindent 6 }} + {{- end }} + {{- with .Values.jenkins.storageSettings }} volumes: {{- toYaml . | nindent 4 }} {{- end }} {{- with .Values.jenkins.imagePullSecrets }} diff --git a/chart/jenkins-operator/values.yaml b/chart/jenkins-operator/values.yaml index 1ee2335a..eecb86ca 100644 --- a/chart/jenkins-operator/values.yaml +++ b/chart/jenkins-operator/values.yaml @@ -158,6 +158,18 @@ jenkins: # See https://jenkinsci.github.io/kubernetes-operator/docs/installation/#note-on-restricted-jenkins-controller-pod-volumemounts for details volumeMounts: [] + # Storage for Jenkins + # By default emptyDir + # See https://kubernetes.io/docs/concepts/storage/volumes/#emptydir for details + storageSettings: + useEmptyDir: true + # Alternative storage settings + # An ephemeral pvc can be used see https://kubernetes.io/docs/concepts/storage/volumes/#ephemeral-volume + # In case is true we also need to pass the storageRequest and optionally the storageClassName + ephemeralStorage: false + storageClassName: "" + storageRequest: "" + # defines authorization strategy of the operator for the Jenkins API authorizationStrategy: createUser diff --git a/config/crd/bases/jenkins.io_jenkins.yaml b/config/crd/bases/jenkins.io_jenkins.yaml index b00bb4f4..a0194bb2 100644 --- a/config/crd/bases/jenkins.io_jenkins.yaml +++ b/config/crd/bases/jenkins.io_jenkins.yaml @@ -157,11 +157,11 @@ spec: type: object basePlugins: description: 'BasePlugins contains plugins required by operator - Defaults to : - name: kubernetes version: "1.31.3" - name: - workflow-job version: "1145.v7f2433caa07f" - name: workflow-aggregator version: - "2.6" - name: git version: "4.11.3" - name: job-dsl version: - "1.78.1" - name: configuration-as-code version: "1346.ve8cfa_3473c94" - name: - kubernetes-credentials-provider version: "0.20"' + Defaults to : - name: configuration-as-code version: "1625.v27444588cc3d" + - name: git version: "5.0.0" - name: job-dsl version: "1.83" + - name: kubernetes version: "3909.v1f2c633e8590" - name: kubernetes-credentials-provider + version: "1.211.vc236a_f5a_2f3c" - name: workflow-aggregator + version: "596.v8c21c963d92d" - name: workflow-job version: "1289.vd1c337fd5354"' items: description: Plugin defines Jenkins plugin. properties: @@ -1100,11 +1100,6 @@ spec: - resources type: object type: array - latestPlugins: - description: 'Allow to override jenkins-plugin-cli default behavior - while downloading the plugin and dependencies, see: - https://github.com/jenkinsci/plugin-installation-manager-tool#cli-options' - type: boolean disableCSRFProtection: description: DisableCSRFProtection allows you to toggle CSRF Protection on Jenkins @@ -1150,6 +1145,10 @@ spec: selectors of replication controllers and services. More info: http://kubernetes.io/docs/user-guide/labels' type: object + latestPlugins: + description: 'Allow to override jenkins-plugin-cli default behavior + while downloading the plugin and dependencies see: https://github.com/jenkinsci/plugin-installation-manager-tool#cli-options' + type: boolean nodeSelector: additionalProperties: type: string @@ -1331,6 +1330,32 @@ spec: type: string type: object type: object + storageSettings: + description: Storage settings for the jenkins home directory Can + be tempDir or ephemeral + properties: + storageClassName: + description: StorageClassName in case of ephemeral pvc, can + be empty + type: string + storageRequest: + description: StorageRequest allows you to specify the storage + request, required in case of ephemeral pvc + type: string + useEmptyDir: + description: UseEmptyDir allows you to create an emptydir + as jenkins-home, also this is the default + type: boolean + useEphemeralStorage: + description: UseEphemeralStorage allows you to create kubernetes + ephemeral pvc, see "https://kubernetes.io/docs/concepts/storage/ephemeral-volumes/#generic-ephemeral-volumes" + type: boolean + required: + - storageClassName + - storageRequest + - useEmptyDir + - useEphemeralStorage + type: object tolerations: description: If specified, the pod's tolerations. items: @@ -2864,6 +2889,7 @@ spec: type: array required: - disableCSRFProtection + - latestPlugins type: object notifications: description: Notifications defines list of a services which are used @@ -3124,8 +3150,10 @@ spec: type: object type: array seedJobAgentImage: + description: SeedJobAgentImage defines the image that will be used + by the seed job agent. If not defined jenkins/inbound-agent:4.9-1 + will be used. type: string - description: 'SeedJobAgentImage defines the image that will be used by the seed job agent. If not defined jenkins/inbound-agent:4.10-3 will be used.' seedJobs: description: 'SeedJobs defines list of Jenkins Seed Job configurations More info: https://jenkinsci.github.io/kubernetes-operator/docs/getting-started/latest/configuration#configure-seed-jobs-and-pipelines' diff --git a/pkg/configuration/base/resources/pod.go b/pkg/configuration/base/resources/pod.go index 9182d358..574ac22a 100644 --- a/pkg/configuration/base/resources/pod.go +++ b/pkg/configuration/base/resources/pod.go @@ -94,16 +94,11 @@ func validateStorageSize(storageSize string) bool { return false } _, err := resource.ParseQuantity(storageSize) - if err != nil { - return false - } - - return true + return err == nil } // get Jenkins home storage settings from the CRD func getJenkinsHomeStorageSettings(jenkins *v1alpha2.Jenkins) corev1.Volume { - JenkinsHomeVolume := corev1.Volume{} emptyDirVol := corev1.Volume{ Name: JenkinsHomeVolumeName, VolumeSource: corev1.VolumeSource{ @@ -111,7 +106,7 @@ func getJenkinsHomeStorageSettings(jenkins *v1alpha2.Jenkins) corev1.Volume { EmptyDir: &corev1.EmptyDirVolumeSource{}, }, } - + var JenkinsHomeVolume corev1.Volume if jenkins.Spec.Master.StorageSettings.UseEphemeralStorage { if !validateStorageSize(jenkins.Spec.Master.StorageSettings.StorageRequest) { fmt.Println("Invalid storage size %s, falling back to empty dir" + jenkins.Spec.Master.StorageSettings.StorageRequest) diff --git a/pkg/configuration/base/resources/pod_test.go b/pkg/configuration/base/resources/pod_test.go index c2c35013..0df0251d 100644 --- a/pkg/configuration/base/resources/pod_test.go +++ b/pkg/configuration/base/resources/pod_test.go @@ -1,6 +1,7 @@ package resources import ( + "fmt" "testing" "github.com/jenkinsci/kubernetes-operator/api/v1alpha2" @@ -143,22 +144,23 @@ func TestGetJenkinsMasterPodBaseVolumes(t *testing.T) { assert.True(t, groovyExists) assert.True(t, cascExists) }) + // TODO: fix me t.Run("home volume is present and is Tempdir", func(t *testing.T) { jenkins := &v1alpha2.Jenkins{ Spec: v1alpha2.JenkinsSpec{ Master: v1alpha2.JenkinsMaster{ StorageSettings: v1alpha2.StorageSettings{ - UseTempDir: true, + UseEmptyDir: true, }, }, }, } - HomeExist, HomeTempdirExist, HomeEphemeralStorageExist := checkHomeVolumesPresence(jenkins) + homeExist, homeTempdirExist, homeEphemeralStorageExist := checkHomeVolumesPresence(jenkins) - assert.True(t, HomeExist) - assert.True(t, HomeTempdirExist) - assert.False(t, HomeEphemeralStorageExist) + assert.True(t, homeExist) + assert.True(t, homeTempdirExist) + assert.False(t, homeEphemeralStorageExist) }) t.Run("home volume is present and it's ephemeral", func(t *testing.T) { jenkins := &v1alpha2.Jenkins{ @@ -166,18 +168,27 @@ func TestGetJenkinsMasterPodBaseVolumes(t *testing.T) { Master: v1alpha2.JenkinsMaster{ StorageSettings: v1alpha2.StorageSettings{ UseEphemeralStorage: true, - StorageClassName: "", + StorageClassName: "default", StorageRequest: "1Gi", }, }, }, } - HomeExist, HomeTempdirExist, HomeEphemeralStorageExist := checkHomeVolumesPresence(jenkins) + homeExist, homeTempdirExist, homeEphemeralStorageExist := checkHomeVolumesPresence(jenkins) - assert.True(t, HomeExist) - assert.False(t, HomeTempdirExist) - assert.True(t, HomeEphemeralStorageExist) + assert.True(t, homeExist) + assert.False(t, homeTempdirExist) + assert.True(t, homeEphemeralStorageExist) + }) + t.Run("home volume is default and should be an empty dir", func(t *testing.T) { + jenkins := &v1alpha2.Jenkins{} + + homeExist, homeTempdirExist, homeEphemeralStorageExist := checkHomeVolumesPresence(jenkins) + + assert.True(t, homeExist) + assert.True(t, homeTempdirExist) + assert.False(t, homeEphemeralStorageExist) }) } @@ -192,23 +203,23 @@ func checkSecretVolumesPresence(jenkins *v1alpha2.Jenkins) (groovyExists bool, c return groovyExists, cascExists } -func checkHomeVolumesPresence(jenkins *v1alpha2.Jenkins) (HomeExist bool, HomeTempdirExist bool, HomeEphemeralStorageExist bool) { +func checkHomeVolumesPresence(jenkins *v1alpha2.Jenkins) (homeExist bool, homeTempdirExist bool, homeEphemeralStorageExist bool) { for _, volume := range GetJenkinsMasterPodBaseVolumes(jenkins) { if volume.Name == ("jenkins-home") { - HomeExist = true + homeExist = true // check if the volume is an emptyDir if volume.VolumeSource.EmptyDir != nil { - HomeTempdirExist = true + homeTempdirExist = true } else if volume.VolumeSource.Ephemeral != nil { - HomeEphemeralStorageExist = true + homeEphemeralStorageExist = true } } else { - HomeExist = false - HomeTempdirExist = false - HomeEphemeralStorageExist = false + homeExist = false + homeTempdirExist = false + homeEphemeralStorageExist = false } } - return HomeExist, HomeTempdirExist, HomeEphemeralStorageExist + return homeExist, homeTempdirExist, homeEphemeralStorageExist } func Test_validateStorageSize(t *testing.T) { diff --git a/pkg/plugins/base_plugins.go b/pkg/plugins/base_plugins.go index 40f9948a..40c3f1d8 100644 --- a/pkg/plugins/base_plugins.go +++ b/pkg/plugins/base_plugins.go @@ -2,7 +2,7 @@ package plugins const ( configurationAsCodePlugin = "configuration-as-code:1625.v27444588cc3d" - gitPlugin = "git:5.0.0" + gitPlugin = "git:5.0.1" jobDslPlugin = "job-dsl:1.83" kubernetesPlugin = "kubernetes:3909.v1f2c633e8590" kubernetesCredentialsProviderPlugin = "kubernetes-credentials-provider:1.211.vc236a_f5a_2f3c" diff --git a/test/e2e/configuration_test.go b/test/e2e/configuration_test.go index 33d27e3d..ce08c085 100644 --- a/test/e2e/configuration_test.go +++ b/test/e2e/configuration_test.go @@ -25,7 +25,7 @@ const e2e = "e2e" var expectedBasePluginsList = []plugins.Plugin{ plugins.Must(plugins.New("configuration-as-code:1625.v27444588cc3d")), - plugins.Must(plugins.New("git:5.0.0")), + plugins.Must(plugins.New("git:5.0.1")), plugins.Must(plugins.New("kubernetes:3909.v1f2c633e8590")), plugins.Must(plugins.New("kubernetes-credentials-provider:1.211.vc236a_f5a_2f3c")), plugins.Must(plugins.New("job-dsl:1.83")),