Merge f6acbfa749 into bf040e1188
				
					
				
			This commit is contained in:
		
						commit
						8536781ac7
					
				| 
						 | 
					@ -73,6 +73,21 @@ type JenkinsSpec struct {
 | 
				
			||||||
 | 
					
 | 
				
			||||||
	// JenkinsAPISettings defines configuration used by the operator to gain admin access to the Jenkins API
 | 
						// JenkinsAPISettings defines configuration used by the operator to gain admin access to the Jenkins API
 | 
				
			||||||
	JenkinsAPISettings JenkinsAPISettings `json:"jenkinsAPISettings"`
 | 
						JenkinsAPISettings JenkinsAPISettings `json:"jenkinsAPISettings"`
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
						// +optional
 | 
				
			||||||
 | 
						Lifecycle JenkinsLifecycle `json:"lifecycle,omitempty"`
 | 
				
			||||||
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					type JenkinsLifecycle struct {
 | 
				
			||||||
 | 
						// +optional
 | 
				
			||||||
 | 
						Ignore JenkinsLifecycleIgnore `json:"ignore,omitempty"`
 | 
				
			||||||
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					type JenkinsLifecycleIgnore struct {
 | 
				
			||||||
 | 
						IgnoredVolumes     []string `json:"volumes,omitempty"`
 | 
				
			||||||
 | 
						IgnoredEnvs        []string `json:"envs,omitempty"`
 | 
				
			||||||
 | 
						IgnoredAnnotations []string `json:"annotations,omitempty"`
 | 
				
			||||||
 | 
						IgnoredLabels      []string `json:"labels,omitempty"`
 | 
				
			||||||
}
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
// AuthorizationStrategy defines authorization strategy of the operator for the Jenkins API
 | 
					// AuthorizationStrategy defines authorization strategy of the operator for the Jenkins API
 | 
				
			||||||
| 
						 | 
					
 | 
				
			||||||
| 
						 | 
					@ -4,6 +4,7 @@ import (
 | 
				
			||||||
	"context"
 | 
						"context"
 | 
				
			||||||
	"fmt"
 | 
						"fmt"
 | 
				
			||||||
	"reflect"
 | 
						"reflect"
 | 
				
			||||||
 | 
						"slices"
 | 
				
			||||||
 | 
					
 | 
				
			||||||
	"github.com/jenkinsci/kubernetes-operator/api/v1alpha2"
 | 
						"github.com/jenkinsci/kubernetes-operator/api/v1alpha2"
 | 
				
			||||||
	"github.com/jenkinsci/kubernetes-operator/pkg/configuration/backuprestore"
 | 
						"github.com/jenkinsci/kubernetes-operator/pkg/configuration/backuprestore"
 | 
				
			||||||
| 
						 | 
					@ -82,7 +83,7 @@ func (r *JenkinsBaseConfigurationReconciler) checkForPodRecreation(currentJenkin
 | 
				
			||||||
			currentJenkinsMasterPod.Labels, r.Configuration.Jenkins.Spec.Master.Labels))
 | 
								currentJenkinsMasterPod.Labels, r.Configuration.Jenkins.Spec.Master.Labels))
 | 
				
			||||||
	}
 | 
						}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
	if !compareMap(r.Configuration.Jenkins.Spec.Master.Annotations, currentJenkinsMasterPod.ObjectMeta.Annotations) {
 | 
						if !r.compareAnnotations(currentJenkinsMasterPod) {
 | 
				
			||||||
		messages = append(messages, "Jenkins pod annotations have changed")
 | 
							messages = append(messages, "Jenkins pod annotations have changed")
 | 
				
			||||||
		verbose = append(verbose, fmt.Sprintf("Jenkins pod annotations have changed, actual '%+v' required '%+v'",
 | 
							verbose = append(verbose, fmt.Sprintf("Jenkins pod annotations have changed, actual '%+v' required '%+v'",
 | 
				
			||||||
			currentJenkinsMasterPod.ObjectMeta.Annotations, r.Configuration.Jenkins.Spec.Master.Annotations))
 | 
								currentJenkinsMasterPod.ObjectMeta.Annotations, r.Configuration.Jenkins.Spec.Master.Annotations))
 | 
				
			||||||
| 
						 | 
					@ -146,6 +147,20 @@ func (r *JenkinsBaseConfigurationReconciler) checkForPodRecreation(currentJenkin
 | 
				
			||||||
	return reason.NewPodRestart(reason.OperatorSource, messages, verbose...)
 | 
						return reason.NewPodRestart(reason.OperatorSource, messages, verbose...)
 | 
				
			||||||
}
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					func (r *JenkinsBaseConfigurationReconciler) compareAnnotations(currentJenkinsMasterPod corev1.Pod) bool {
 | 
				
			||||||
 | 
						ignoredAnnotations := r.Jenkins.Spec.Lifecycle.Ignore.IgnoredAnnotations
 | 
				
			||||||
 | 
						annotations := r.Configuration.Jenkins.Spec.Master.Annotations
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
						res := make(map[string]string)
 | 
				
			||||||
 | 
						for key, val := range annotations {
 | 
				
			||||||
 | 
							if slices.Contains(ignoredAnnotations, key) {
 | 
				
			||||||
 | 
								continue
 | 
				
			||||||
 | 
							}
 | 
				
			||||||
 | 
							res[key] = val
 | 
				
			||||||
 | 
						}
 | 
				
			||||||
 | 
						return compareMap(res, currentJenkinsMasterPod.ObjectMeta.Annotations)
 | 
				
			||||||
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
func (r *JenkinsBaseConfigurationReconciler) ensureJenkinsMasterPod(meta metav1.ObjectMeta) (reconcile.Result, error) {
 | 
					func (r *JenkinsBaseConfigurationReconciler) ensureJenkinsMasterPod(meta metav1.ObjectMeta) (reconcile.Result, error) {
 | 
				
			||||||
	userAndPasswordHash, err := r.calculateUserAndPasswordHash()
 | 
						userAndPasswordHash, err := r.calculateUserAndPasswordHash()
 | 
				
			||||||
	if err != nil {
 | 
						if err != nil {
 | 
				
			||||||
| 
						 | 
					
 | 
				
			||||||
| 
						 | 
					@ -97,6 +97,114 @@ func TestCompareContainerVolumeMounts(t *testing.T) {
 | 
				
			||||||
	})
 | 
						})
 | 
				
			||||||
}
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					func TestCompareAnnotations(t *testing.T) {
 | 
				
			||||||
 | 
						type testCase struct {
 | 
				
			||||||
 | 
							name                string
 | 
				
			||||||
 | 
							jenkinsAnnotations  map[string]string
 | 
				
			||||||
 | 
							ignoredAnnotations  []string
 | 
				
			||||||
 | 
							podAnnotations      map[string]string
 | 
				
			||||||
 | 
							expectedShouldMatch bool
 | 
				
			||||||
 | 
						}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
						runTest := func(t *testing.T, tc testCase) {
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
							t.Run(tc.name, func(t *testing.T) {
 | 
				
			||||||
 | 
								jenkins := &v1alpha2.Jenkins{
 | 
				
			||||||
 | 
									Spec: v1alpha2.JenkinsSpec{
 | 
				
			||||||
 | 
										Master: v1alpha2.JenkinsMaster{
 | 
				
			||||||
 | 
											Annotations: tc.jenkinsAnnotations,
 | 
				
			||||||
 | 
										},
 | 
				
			||||||
 | 
									},
 | 
				
			||||||
 | 
								}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
								if len(tc.ignoredAnnotations) > 0 {
 | 
				
			||||||
 | 
									jenkins.Spec.Lifecycle = v1alpha2.JenkinsLifecycle{
 | 
				
			||||||
 | 
										Ignore: v1alpha2.JenkinsLifecycleIgnore{
 | 
				
			||||||
 | 
											IgnoredAnnotations: tc.ignoredAnnotations,
 | 
				
			||||||
 | 
										},
 | 
				
			||||||
 | 
									}
 | 
				
			||||||
 | 
								}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
								pod := corev1.Pod{
 | 
				
			||||||
 | 
									ObjectMeta: metav1.ObjectMeta{
 | 
				
			||||||
 | 
										Annotations: tc.podAnnotations,
 | 
				
			||||||
 | 
									},
 | 
				
			||||||
 | 
								}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
								reconciler := New(configuration.Configuration{Jenkins: jenkins}, client.JenkinsAPIConnectionSettings{})
 | 
				
			||||||
 | 
								result := reconciler.compareAnnotations(pod)
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
								assert.Equal(t, tc.expectedShouldMatch, result,
 | 
				
			||||||
 | 
									"Expected compareAnnotations to return %v but got %v", tc.expectedShouldMatch, result)
 | 
				
			||||||
 | 
							})
 | 
				
			||||||
 | 
						}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
						testCases := []testCase{
 | 
				
			||||||
 | 
							{
 | 
				
			||||||
 | 
								name:                "no annotation - additional annotations - not different",
 | 
				
			||||||
 | 
								jenkinsAnnotations:  map[string]string{},
 | 
				
			||||||
 | 
								ignoredAnnotations:  nil,
 | 
				
			||||||
 | 
								podAnnotations:      map[string]string{"one": "two"},
 | 
				
			||||||
 | 
								expectedShouldMatch: true,
 | 
				
			||||||
 | 
							},
 | 
				
			||||||
 | 
							{
 | 
				
			||||||
 | 
								name:                "one additional annotation - change not ignored - not different",
 | 
				
			||||||
 | 
								jenkinsAnnotations:  map[string]string{"one": "two"},
 | 
				
			||||||
 | 
								ignoredAnnotations:  nil,
 | 
				
			||||||
 | 
								podAnnotations:      map[string]string{"one": "two", "additional": "annotation"},
 | 
				
			||||||
 | 
								expectedShouldMatch: true,
 | 
				
			||||||
 | 
							},
 | 
				
			||||||
 | 
							{
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
								name:                "annotations different - different",
 | 
				
			||||||
 | 
								jenkinsAnnotations:  map[string]string{"one": "two"},
 | 
				
			||||||
 | 
								ignoredAnnotations:  nil,
 | 
				
			||||||
 | 
								podAnnotations:      map[string]string{"two": "three"},
 | 
				
			||||||
 | 
								expectedShouldMatch: false,
 | 
				
			||||||
 | 
							},
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
							{
 | 
				
			||||||
 | 
								name:                "annotations different - ignored - not different",
 | 
				
			||||||
 | 
								jenkinsAnnotations:  map[string]string{"one": "two"},
 | 
				
			||||||
 | 
								ignoredAnnotations:  []string{"one"},
 | 
				
			||||||
 | 
								podAnnotations:      map[string]string{"two": "three"},
 | 
				
			||||||
 | 
								expectedShouldMatch: true,
 | 
				
			||||||
 | 
							},
 | 
				
			||||||
 | 
							{
 | 
				
			||||||
 | 
								name:                "one annotation different - change not ignored - different",
 | 
				
			||||||
 | 
								jenkinsAnnotations:  map[string]string{"one": "two"},
 | 
				
			||||||
 | 
								ignoredAnnotations:  nil,
 | 
				
			||||||
 | 
								podAnnotations:      map[string]string{"one": "different"},
 | 
				
			||||||
 | 
								expectedShouldMatch: false,
 | 
				
			||||||
 | 
							},
 | 
				
			||||||
 | 
							{
 | 
				
			||||||
 | 
								name:                "one annotation different - change ignored - not different",
 | 
				
			||||||
 | 
								jenkinsAnnotations:  map[string]string{"one": "two"},
 | 
				
			||||||
 | 
								ignoredAnnotations:  []string{"one"},
 | 
				
			||||||
 | 
								podAnnotations:      map[string]string{"one": "different"},
 | 
				
			||||||
 | 
								expectedShouldMatch: true,
 | 
				
			||||||
 | 
							},
 | 
				
			||||||
 | 
							{
 | 
				
			||||||
 | 
								name:                "one additional annotation - different",
 | 
				
			||||||
 | 
								jenkinsAnnotations:  map[string]string{"one": "two", "ignore": "me"},
 | 
				
			||||||
 | 
								ignoredAnnotations:  nil,
 | 
				
			||||||
 | 
								podAnnotations:      map[string]string{"one": "two", "ignore": "this"},
 | 
				
			||||||
 | 
								expectedShouldMatch: false,
 | 
				
			||||||
 | 
							},
 | 
				
			||||||
 | 
							{
 | 
				
			||||||
 | 
								name:                "one additional annotation - change ignored - not different",
 | 
				
			||||||
 | 
								jenkinsAnnotations:  map[string]string{"one": "two", "ignore": "me"},
 | 
				
			||||||
 | 
								ignoredAnnotations:  []string{"ignore"},
 | 
				
			||||||
 | 
								podAnnotations:      map[string]string{"one": "two", "ignore": "this"},
 | 
				
			||||||
 | 
								expectedShouldMatch: true,
 | 
				
			||||||
 | 
							},
 | 
				
			||||||
 | 
						}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
						for _, tc := range testCases {
 | 
				
			||||||
 | 
							runTest(t, tc)
 | 
				
			||||||
 | 
						}
 | 
				
			||||||
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
func TestCompareVolumes(t *testing.T) {
 | 
					func TestCompareVolumes(t *testing.T) {
 | 
				
			||||||
	t.Run("defaults", func(t *testing.T) {
 | 
						t.Run("defaults", func(t *testing.T) {
 | 
				
			||||||
		jenkins := &v1alpha2.Jenkins{}
 | 
							jenkins := &v1alpha2.Jenkins{}
 | 
				
			||||||
| 
						 | 
					@ -160,6 +268,74 @@ func TestCompareVolumes(t *testing.T) {
 | 
				
			||||||
 | 
					
 | 
				
			||||||
		assert.True(t, got)
 | 
							assert.True(t, got)
 | 
				
			||||||
	})
 | 
						})
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
						t.Run("different - additional workspace identity volume", func(t *testing.T) {
 | 
				
			||||||
 | 
							jenkins := &v1alpha2.Jenkins{
 | 
				
			||||||
 | 
								Spec: v1alpha2.JenkinsSpec{
 | 
				
			||||||
 | 
									Master: v1alpha2.JenkinsMaster{},
 | 
				
			||||||
 | 
								},
 | 
				
			||||||
 | 
							}
 | 
				
			||||||
 | 
							pod := corev1.Pod{
 | 
				
			||||||
 | 
								Spec: corev1.PodSpec{
 | 
				
			||||||
 | 
									ServiceAccountName: "service-account-name",
 | 
				
			||||||
 | 
									Volumes:            append(resources.GetJenkinsMasterPodBaseVolumes(jenkins), corev1.Volume{Name: "azure-identity-token"}),
 | 
				
			||||||
 | 
								},
 | 
				
			||||||
 | 
							}
 | 
				
			||||||
 | 
							reconciler := New(configuration.Configuration{Jenkins: jenkins}, client.JenkinsAPIConnectionSettings{})
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
							got := reconciler.compareVolumes(pod)
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
							assert.False(t, got)
 | 
				
			||||||
 | 
						})
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
						t.Run("additional workspace identity volume but ignored", func(t *testing.T) {
 | 
				
			||||||
 | 
							jenkins := &v1alpha2.Jenkins{
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
								Spec: v1alpha2.JenkinsSpec{
 | 
				
			||||||
 | 
									Lifecycle: v1alpha2.JenkinsLifecycle{
 | 
				
			||||||
 | 
										Ignore: v1alpha2.JenkinsLifecycleIgnore{
 | 
				
			||||||
 | 
											IgnoredVolumes: []string{"azure-identity-token"},
 | 
				
			||||||
 | 
										},
 | 
				
			||||||
 | 
									},
 | 
				
			||||||
 | 
								},
 | 
				
			||||||
 | 
							}
 | 
				
			||||||
 | 
							pod := corev1.Pod{
 | 
				
			||||||
 | 
								Spec: corev1.PodSpec{
 | 
				
			||||||
 | 
									ServiceAccountName: "service-account-name",
 | 
				
			||||||
 | 
									Volumes:            append(resources.GetJenkinsMasterPodBaseVolumes(jenkins), corev1.Volume{Name: "azure-identity-token"}),
 | 
				
			||||||
 | 
								},
 | 
				
			||||||
 | 
							}
 | 
				
			||||||
 | 
							reconciler := New(configuration.Configuration{Jenkins: jenkins}, client.JenkinsAPIConnectionSettings{})
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
							got := reconciler.compareVolumes(pod)
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
							assert.True(t, got)
 | 
				
			||||||
 | 
						})
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
						t.Run("additional multiple volumes added but ignored", func(t *testing.T) {
 | 
				
			||||||
 | 
							jenkins := &v1alpha2.Jenkins{
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
								Spec: v1alpha2.JenkinsSpec{
 | 
				
			||||||
 | 
									Lifecycle: v1alpha2.JenkinsLifecycle{
 | 
				
			||||||
 | 
										Ignore: v1alpha2.JenkinsLifecycleIgnore{
 | 
				
			||||||
 | 
											IgnoredVolumes: []string{"volume-present", "volume-absent"},
 | 
				
			||||||
 | 
										},
 | 
				
			||||||
 | 
									},
 | 
				
			||||||
 | 
								},
 | 
				
			||||||
 | 
							}
 | 
				
			||||||
 | 
							pod := corev1.Pod{
 | 
				
			||||||
 | 
								Spec: corev1.PodSpec{
 | 
				
			||||||
 | 
									ServiceAccountName: "service-account-name",
 | 
				
			||||||
 | 
									Volumes:            append(resources.GetJenkinsMasterPodBaseVolumes(jenkins), corev1.Volume{Name: "volume-present"}),
 | 
				
			||||||
 | 
								},
 | 
				
			||||||
 | 
							}
 | 
				
			||||||
 | 
							reconciler := New(configuration.Configuration{Jenkins: jenkins}, client.JenkinsAPIConnectionSettings{})
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
							got := reconciler.compareVolumes(pod)
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
							assert.True(t, got)
 | 
				
			||||||
 | 
						})
 | 
				
			||||||
 | 
					
 | 
				
			||||||
}
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
func TestJenkinsBaseConfigurationReconciler_verifyPlugins(t *testing.T) {
 | 
					func TestJenkinsBaseConfigurationReconciler_verifyPlugins(t *testing.T) {
 | 
				
			||||||
| 
						 | 
					
 | 
				
			||||||
| 
						 | 
					@ -294,6 +294,11 @@ func CompareContainerVolumeMounts(expected corev1.Container, actual corev1.Conta
 | 
				
			||||||
func (r *JenkinsBaseConfigurationReconciler) compareVolumes(actualPod corev1.Pod) bool {
 | 
					func (r *JenkinsBaseConfigurationReconciler) compareVolumes(actualPod corev1.Pod) bool {
 | 
				
			||||||
	var toCompare []corev1.Volume
 | 
						var toCompare []corev1.Volume
 | 
				
			||||||
	for _, volume := range actualPod.Spec.Volumes {
 | 
						for _, volume := range actualPod.Spec.Volumes {
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
							if r.isVolumeIgnored(volume.Name) {
 | 
				
			||||||
 | 
								continue
 | 
				
			||||||
 | 
							}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
		// filter out service account
 | 
							// filter out service account
 | 
				
			||||||
		if strings.HasPrefix(volume.Name, actualPod.Spec.ServiceAccountName) {
 | 
							if strings.HasPrefix(volume.Name, actualPod.Spec.ServiceAccountName) {
 | 
				
			||||||
			continue
 | 
								continue
 | 
				
			||||||
| 
						 | 
					@ -421,3 +426,13 @@ func (r *JenkinsBaseConfigurationReconciler) ensureBaseConfiguration(jenkinsClie
 | 
				
			||||||
	})
 | 
						})
 | 
				
			||||||
	return reconcile.Result{Requeue: requeue}, err
 | 
						return reconcile.Result{Requeue: requeue}, err
 | 
				
			||||||
}
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					// isVolumeIgnored checks if the given volume name is in the list of ignored volumes
 | 
				
			||||||
 | 
					func (r *JenkinsBaseConfigurationReconciler) isVolumeIgnored(volumeName string) bool {
 | 
				
			||||||
 | 
						for _, ignoredVolume := range r.Jenkins.Spec.Lifecycle.Ignore.IgnoredVolumes {
 | 
				
			||||||
 | 
							if ignoredVolume == volumeName {
 | 
				
			||||||
 | 
								return true
 | 
				
			||||||
 | 
							}
 | 
				
			||||||
 | 
						}
 | 
				
			||||||
 | 
						return false
 | 
				
			||||||
 | 
					}
 | 
				
			||||||
| 
						 | 
					
 | 
				
			||||||
		Loading…
	
		Reference in New Issue