Merge f6acbfa749 into 08f0a74771
This commit is contained in:
commit
4d1ecf97ee
|
|
@ -73,6 +73,21 @@ type JenkinsSpec struct {
|
|||
|
||||
// JenkinsAPISettings defines configuration used by the operator to gain admin access to the Jenkins API
|
||||
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
|
||||
|
|
|
|||
|
|
@ -4,6 +4,7 @@ import (
|
|||
"context"
|
||||
"fmt"
|
||||
"reflect"
|
||||
"slices"
|
||||
|
||||
"github.com/jenkinsci/kubernetes-operator/api/v1alpha2"
|
||||
"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))
|
||||
}
|
||||
|
||||
if !compareMap(r.Configuration.Jenkins.Spec.Master.Annotations, currentJenkinsMasterPod.ObjectMeta.Annotations) {
|
||||
if !r.compareAnnotations(currentJenkinsMasterPod) {
|
||||
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))
|
||||
|
|
@ -146,6 +147,20 @@ func (r *JenkinsBaseConfigurationReconciler) checkForPodRecreation(currentJenkin
|
|||
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) {
|
||||
userAndPasswordHash, err := r.calculateUserAndPasswordHash()
|
||||
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) {
|
||||
t.Run("defaults", func(t *testing.T) {
|
||||
jenkins := &v1alpha2.Jenkins{}
|
||||
|
|
@ -160,6 +268,74 @@ func TestCompareVolumes(t *testing.T) {
|
|||
|
||||
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) {
|
||||
|
|
|
|||
|
|
@ -294,6 +294,11 @@ func CompareContainerVolumeMounts(expected corev1.Container, actual corev1.Conta
|
|||
func (r *JenkinsBaseConfigurationReconciler) compareVolumes(actualPod corev1.Pod) bool {
|
||||
var toCompare []corev1.Volume
|
||||
for _, volume := range actualPod.Spec.Volumes {
|
||||
|
||||
if r.isVolumeIgnored(volume.Name) {
|
||||
continue
|
||||
}
|
||||
|
||||
// filter out service account
|
||||
if strings.HasPrefix(volume.Name, actualPod.Spec.ServiceAccountName) {
|
||||
continue
|
||||
|
|
@ -421,3 +426,13 @@ func (r *JenkinsBaseConfigurationReconciler) ensureBaseConfiguration(jenkinsClie
|
|||
})
|
||||
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