#45 Add support for imagePullSecrets parameter
This commit is contained in:
		
							parent
							
								
									164fe30aef
								
							
						
					
					
						commit
						e049218b78
					
				|  | @ -210,6 +210,13 @@ type JenkinsMaster struct { | ||||||
| 	//       memory: 600Mi
 | 	//       memory: 600Mi
 | ||||||
| 	Containers []Container `json:"containers,omitempty"` | 	Containers []Container `json:"containers,omitempty"` | ||||||
| 
 | 
 | ||||||
|  | 	// ImagePullSecrets is an optional list of references to secrets in the same namespace to use for pulling any of the images used by this PodSpec.
 | ||||||
|  | 	// If specified, these secrets will be passed to individual puller implementations for them to use. For example,
 | ||||||
|  | 	// in the case of docker, only DockerConfig type secrets are honored.
 | ||||||
|  | 	// More info: https://kubernetes.io/docs/concepts/containers/images#specifying-imagepullsecrets-on-a-pod
 | ||||||
|  | 	// +optional
 | ||||||
|  | 	ImagePullSecrets []corev1.LocalObjectReference `json:"imagePullSecrets,omitempty"` | ||||||
|  | 
 | ||||||
| 	// List of volumes that can be mounted by containers belonging to the pod.
 | 	// List of volumes that can be mounted by containers belonging to the pod.
 | ||||||
| 	// More info: https://kubernetes.io/docs/concepts/storage/volumes
 | 	// More info: https://kubernetes.io/docs/concepts/storage/volumes
 | ||||||
| 	// +optional
 | 	// +optional
 | ||||||
|  |  | ||||||
|  | @ -322,6 +322,11 @@ func (in *JenkinsMaster) DeepCopyInto(out *JenkinsMaster) { | ||||||
| 			(*in)[i].DeepCopyInto(&(*out)[i]) | 			(*in)[i].DeepCopyInto(&(*out)[i]) | ||||||
| 		} | 		} | ||||||
| 	} | 	} | ||||||
|  | 	if in.ImagePullSecrets != nil { | ||||||
|  | 		in, out := &in.ImagePullSecrets, &out.ImagePullSecrets | ||||||
|  | 		*out = make([]v1.LocalObjectReference, len(*in)) | ||||||
|  | 		copy(*out, *in) | ||||||
|  | 	} | ||||||
| 	if in.Volumes != nil { | 	if in.Volumes != nil { | ||||||
| 		in, out := &in.Volumes, &out.Volumes | 		in, out := &in.Volumes, &out.Volumes | ||||||
| 		*out = make([]v1.Volume, len(*in)) | 		*out = make([]v1.Volume, len(*in)) | ||||||
|  |  | ||||||
|  | @ -527,6 +527,12 @@ func (r *ReconcileJenkinsBaseConfiguration) isRecreatePodNeeded(currentJenkinsMa | ||||||
| 		return true | 		return true | ||||||
| 	} | 	} | ||||||
| 
 | 
 | ||||||
|  | 	if !reflect.DeepEqual(r.jenkins.Spec.Master.ImagePullSecrets, currentJenkinsMasterPod.Spec.ImagePullSecrets) { | ||||||
|  | 		r.logger.Info(fmt.Sprintf("Jenkins Pod ImagePullSecrets has changed, actual '%+v' required '%+v', recreating pod", | ||||||
|  | 			currentJenkinsMasterPod.Spec.ImagePullSecrets, r.jenkins.Spec.Master.ImagePullSecrets)) | ||||||
|  | 		return true | ||||||
|  | 	} | ||||||
|  | 
 | ||||||
| 	if !reflect.DeepEqual(r.jenkins.Spec.Master.NodeSelector, currentJenkinsMasterPod.Spec.NodeSelector) { | 	if !reflect.DeepEqual(r.jenkins.Spec.Master.NodeSelector, currentJenkinsMasterPod.Spec.NodeSelector) { | ||||||
| 		r.logger.Info(fmt.Sprintf("Jenkins pod node selector has changed, actual '%+v' required '%+v', recreating pod", | 		r.logger.Info(fmt.Sprintf("Jenkins pod node selector has changed, actual '%+v' required '%+v', recreating pod", | ||||||
| 			currentJenkinsMasterPod.Spec.NodeSelector, r.jenkins.Spec.Master.NodeSelector)) | 			currentJenkinsMasterPod.Spec.NodeSelector, r.jenkins.Spec.Master.NodeSelector)) | ||||||
|  |  | ||||||
|  | @ -124,6 +124,29 @@ func TestGetJenkinsOpts(t *testing.T) { | ||||||
| 		assert.Contains(t, opts, "httpPort") | 		assert.Contains(t, opts, "httpPort") | ||||||
| 		assert.Equal(t, opts["httpPort"], "8080") | 		assert.Equal(t, opts["httpPort"], "8080") | ||||||
| 	}) | 	}) | ||||||
|  | 
 | ||||||
|  | 	t.Run("JENKINS_OPTS have --httpPort=--8080 argument", func(t *testing.T) { | ||||||
|  | 		jenkins := &v1alpha2.Jenkins{ | ||||||
|  | 			Spec: v1alpha2.JenkinsSpec{ | ||||||
|  | 				Master: v1alpha2.JenkinsMaster{ | ||||||
|  | 					Containers: []v1alpha2.Container{ | ||||||
|  | 						{ | ||||||
|  | 							Env: []corev1.EnvVar{ | ||||||
|  | 								{Name: "JENKINS_OPTS", Value: "--httpPort=--8080"}, | ||||||
|  | 							}, | ||||||
|  | 						}, | ||||||
|  | 					}, | ||||||
|  | 				}, | ||||||
|  | 			}, | ||||||
|  | 		} | ||||||
|  | 
 | ||||||
|  | 		opts := GetJenkinsOpts(jenkins) | ||||||
|  | 
 | ||||||
|  | 		assert.Equal(t, 1, len(opts)) | ||||||
|  | 		assert.NotContains(t, opts, "prefix") | ||||||
|  | 		assert.Contains(t, opts, "httpPort") | ||||||
|  | 		assert.Equal(t, opts["httpPort"], "--8080") | ||||||
|  | 	}) | ||||||
| } | } | ||||||
| 
 | 
 | ||||||
| func TestCompareContainerVolumeMounts(t *testing.T) { | func TestCompareContainerVolumeMounts(t *testing.T) { | ||||||
|  |  | ||||||
|  | @ -288,6 +288,7 @@ func NewJenkinsMasterPod(objectMeta metav1.ObjectMeta, jenkins *v1alpha2.Jenkins | ||||||
| 			Containers:         newContainers(jenkins), | 			Containers:         newContainers(jenkins), | ||||||
| 			Volumes:            append(GetJenkinsMasterPodBaseVolumes(jenkins), jenkins.Spec.Master.Volumes...), | 			Volumes:            append(GetJenkinsMasterPodBaseVolumes(jenkins), jenkins.Spec.Master.Volumes...), | ||||||
| 			SecurityContext:    jenkins.Spec.Master.SecurityContext, | 			SecurityContext:    jenkins.Spec.Master.SecurityContext, | ||||||
|  | 			ImagePullSecrets: jenkins.Spec.Master.ImagePullSecrets, | ||||||
| 		}, | 		}, | ||||||
| 	} | 	} | ||||||
| } | } | ||||||
|  |  | ||||||
|  | @ -2,6 +2,7 @@ package base | ||||||
| 
 | 
 | ||||||
| import ( | import ( | ||||||
| 	"context" | 	"context" | ||||||
|  | 	"errors" | ||||||
| 	"fmt" | 	"fmt" | ||||||
| 	"regexp" | 	"regexp" | ||||||
| 
 | 
 | ||||||
|  | @ -60,6 +61,46 @@ func (r *ReconcileJenkinsBaseConfiguration) Validate(jenkins *v1alpha2.Jenkins) | ||||||
| 	return true, nil | 	return true, nil | ||||||
| } | } | ||||||
| 
 | 
 | ||||||
|  | func (r *ReconcileJenkinsBaseConfiguration) validateImagePullSecrets() (bool, error) { | ||||||
|  | 	var err error | ||||||
|  | 	valid := true | ||||||
|  | 	ips := r.jenkins.Spec.Master.ImagePullSecrets | ||||||
|  | 	for _, sr := range ips { | ||||||
|  | 		valid, err = r.validateImagePullSecret(sr.Name) | ||||||
|  | 		if err != nil || !valid { | ||||||
|  | 			return valid, err | ||||||
|  | 		} | ||||||
|  | 	} | ||||||
|  | 
 | ||||||
|  | 	return valid, err | ||||||
|  | } | ||||||
|  | 
 | ||||||
|  | func (r *ReconcileJenkinsBaseConfiguration) validateImagePullSecret(name string) (bool, error) { | ||||||
|  | 	secret := &corev1.Secret{} | ||||||
|  | 	err := r.k8sClient.Get(context.TODO(), types.NamespacedName{Name: name, Namespace: r.jenkins.ObjectMeta.Namespace}, secret) | ||||||
|  | 	if err != nil && apierrors.IsNotFound(err) { | ||||||
|  | 		r.logger.V(log.VWarn).Info(fmt.Sprintf("Secret '%s' not found", name)) | ||||||
|  | 		return false, nil | ||||||
|  | 	} else if err != nil && !apierrors.IsNotFound(err) { | ||||||
|  | 		return false, stackerr.WithStack(err) | ||||||
|  | 	} | ||||||
|  | 
 | ||||||
|  | 	if secret.Data["docker-server"] == nil { | ||||||
|  | 		return false, errors.New("docker server not set") | ||||||
|  | 	} | ||||||
|  | 	if secret.Data["docker-username"] == nil { | ||||||
|  | 		return false, errors.New("docker username not set") | ||||||
|  | 	} | ||||||
|  | 	if secret.Data["docker-password"] == nil { | ||||||
|  | 		return false, errors.New("docker password not set") | ||||||
|  | 	} | ||||||
|  | 	if secret.Data["docker-email"] == nil { | ||||||
|  | 		return false, errors.New("docker email not set") | ||||||
|  | 	} | ||||||
|  | 
 | ||||||
|  | 	return true, nil | ||||||
|  | } | ||||||
|  | 
 | ||||||
| func (r *ReconcileJenkinsBaseConfiguration) validateVolumes() (bool, error) { | func (r *ReconcileJenkinsBaseConfiguration) validateVolumes() (bool, error) { | ||||||
| 	valid := true | 	valid := true | ||||||
| 	for _, volume := range r.jenkins.Spec.Master.Volumes { | 	for _, volume := range r.jenkins.Spec.Master.Volumes { | ||||||
|  |  | ||||||
|  | @ -132,6 +132,199 @@ func TestValidatePlugins(t *testing.T) { | ||||||
| 	}) | 	}) | ||||||
| } | } | ||||||
| 
 | 
 | ||||||
|  | func TestReconcileJenkinsBaseConfiguration_validateImagePullSecrets(t *testing.T) { | ||||||
|  | 	t.Run("happy", func(t *testing.T) { | ||||||
|  | 		lor := &corev1.Secret{ | ||||||
|  | 			ObjectMeta: metav1.ObjectMeta{ | ||||||
|  | 				Name: "test-ref", | ||||||
|  | 			}, | ||||||
|  | 			Data: map[string][]byte{ | ||||||
|  | 				"docker-server":   []byte("test_server"), | ||||||
|  | 				"docker-username": []byte("test_user"), | ||||||
|  | 				"docker-password": []byte("test_password"), | ||||||
|  | 				"docker-email":    []byte("test_email"), | ||||||
|  | 			}, | ||||||
|  | 		} | ||||||
|  | 
 | ||||||
|  | 		jenkins := v1alpha2.Jenkins{ | ||||||
|  | 			Spec: v1alpha2.JenkinsSpec{ | ||||||
|  | 				Master: v1alpha2.JenkinsMaster{ | ||||||
|  | 					ImagePullSecrets: []corev1.LocalObjectReference{ | ||||||
|  | 						{Name: lor.ObjectMeta.Name}, | ||||||
|  | 					}, | ||||||
|  | 				}, | ||||||
|  | 			}, | ||||||
|  | 		} | ||||||
|  | 
 | ||||||
|  | 		fakeClient := fake.NewFakeClient() | ||||||
|  | 		err := fakeClient.Create(context.TODO(), lor) | ||||||
|  | 		assert.NoError(t, err) | ||||||
|  | 
 | ||||||
|  | 		baseReconcileLoop := New(fakeClient, nil, logf.ZapLogger(false), | ||||||
|  | 			&jenkins, false, false, nil, nil) | ||||||
|  | 
 | ||||||
|  | 		got, err := baseReconcileLoop.validateImagePullSecrets() | ||||||
|  | 		assert.Equal(t, got, true) | ||||||
|  | 		assert.NoError(t, err) | ||||||
|  | 	}) | ||||||
|  | 
 | ||||||
|  | 	t.Run("no secret", func(t *testing.T) { | ||||||
|  | 		jenkins := v1alpha2.Jenkins{ | ||||||
|  | 			Spec: v1alpha2.JenkinsSpec{ | ||||||
|  | 				Master: v1alpha2.JenkinsMaster{ | ||||||
|  | 					ImagePullSecrets: []corev1.LocalObjectReference{ | ||||||
|  | 						{Name: "test-ref"}, | ||||||
|  | 					}, | ||||||
|  | 				}, | ||||||
|  | 			}, | ||||||
|  | 		} | ||||||
|  | 
 | ||||||
|  | 		fakeClient := fake.NewFakeClient() | ||||||
|  | 
 | ||||||
|  | 		baseReconcileLoop := New(fakeClient, nil, logf.ZapLogger(false), | ||||||
|  | 			&jenkins, false, false, nil, nil) | ||||||
|  | 
 | ||||||
|  | 		got, _ := baseReconcileLoop.validateImagePullSecrets() | ||||||
|  | 		assert.Equal(t, got, false) | ||||||
|  | 	}) | ||||||
|  | 
 | ||||||
|  | 	t.Run("no docker email", func(t *testing.T) { | ||||||
|  | 		lor := &corev1.Secret{ | ||||||
|  | 			ObjectMeta: metav1.ObjectMeta{ | ||||||
|  | 				Name: "test-ref", | ||||||
|  | 			}, | ||||||
|  | 			Data: map[string][]byte{ | ||||||
|  | 				"docker-server":   []byte("test_server"), | ||||||
|  | 				"docker-username": []byte("test_user"), | ||||||
|  | 				"docker-password": []byte("test_password"), | ||||||
|  | 			}, | ||||||
|  | 		} | ||||||
|  | 
 | ||||||
|  | 		jenkins := v1alpha2.Jenkins{ | ||||||
|  | 			Spec: v1alpha2.JenkinsSpec{ | ||||||
|  | 				Master: v1alpha2.JenkinsMaster{ | ||||||
|  | 					ImagePullSecrets: []corev1.LocalObjectReference{ | ||||||
|  | 						{Name: lor.ObjectMeta.Name}, | ||||||
|  | 					}, | ||||||
|  | 				}, | ||||||
|  | 			}, | ||||||
|  | 		} | ||||||
|  | 
 | ||||||
|  | 		fakeClient := fake.NewFakeClient() | ||||||
|  | 		err := fakeClient.Create(context.TODO(), lor) | ||||||
|  | 		assert.NoError(t, err) | ||||||
|  | 
 | ||||||
|  | 		baseReconcileLoop := New(fakeClient, nil, logf.ZapLogger(false), | ||||||
|  | 			&jenkins, false, false, nil, nil) | ||||||
|  | 
 | ||||||
|  | 		got, err := baseReconcileLoop.validateImagePullSecrets() | ||||||
|  | 		assert.Equal(t, got, false) | ||||||
|  | 		assert.Error(t, err) | ||||||
|  | 	}) | ||||||
|  | 
 | ||||||
|  | 	t.Run("no docker password", func(t *testing.T) { | ||||||
|  | 		lor := &corev1.Secret{ | ||||||
|  | 			ObjectMeta: metav1.ObjectMeta{ | ||||||
|  | 				Name: "test-ref", | ||||||
|  | 			}, | ||||||
|  | 			Data: map[string][]byte{ | ||||||
|  | 				"docker-server":   []byte("test_server"), | ||||||
|  | 				"docker-username": []byte("test_user"), | ||||||
|  | 				"docker-email":    []byte("test_email"), | ||||||
|  | 			}, | ||||||
|  | 		} | ||||||
|  | 
 | ||||||
|  | 		jenkins := v1alpha2.Jenkins{ | ||||||
|  | 			Spec: v1alpha2.JenkinsSpec{ | ||||||
|  | 				Master: v1alpha2.JenkinsMaster{ | ||||||
|  | 					ImagePullSecrets: []corev1.LocalObjectReference{ | ||||||
|  | 						{Name: lor.ObjectMeta.Name}, | ||||||
|  | 					}, | ||||||
|  | 				}, | ||||||
|  | 			}, | ||||||
|  | 		} | ||||||
|  | 
 | ||||||
|  | 		fakeClient := fake.NewFakeClient() | ||||||
|  | 		err := fakeClient.Create(context.TODO(), lor) | ||||||
|  | 		assert.NoError(t, err) | ||||||
|  | 
 | ||||||
|  | 		baseReconcileLoop := New(fakeClient, nil, logf.ZapLogger(false), | ||||||
|  | 			&jenkins, false, false, nil, nil) | ||||||
|  | 
 | ||||||
|  | 		got, err := baseReconcileLoop.validateImagePullSecrets() | ||||||
|  | 		assert.Equal(t, got, false) | ||||||
|  | 		assert.Error(t, err) | ||||||
|  | 	}) | ||||||
|  | 
 | ||||||
|  | 	t.Run("no docker username", func(t *testing.T) { | ||||||
|  | 		lor := &corev1.Secret{ | ||||||
|  | 			ObjectMeta: metav1.ObjectMeta{ | ||||||
|  | 				Name: "test-ref", | ||||||
|  | 			}, | ||||||
|  | 			Data: map[string][]byte{ | ||||||
|  | 				"docker-server":   []byte("test_server"), | ||||||
|  | 				"docker-password": []byte("test_password"), | ||||||
|  | 				"docker-email":    []byte("test_email"), | ||||||
|  | 			}, | ||||||
|  | 		} | ||||||
|  | 
 | ||||||
|  | 		jenkins := v1alpha2.Jenkins{ | ||||||
|  | 			Spec: v1alpha2.JenkinsSpec{ | ||||||
|  | 				Master: v1alpha2.JenkinsMaster{ | ||||||
|  | 					ImagePullSecrets: []corev1.LocalObjectReference{ | ||||||
|  | 						{Name: lor.ObjectMeta.Name}, | ||||||
|  | 					}, | ||||||
|  | 				}, | ||||||
|  | 			}, | ||||||
|  | 		} | ||||||
|  | 
 | ||||||
|  | 		fakeClient := fake.NewFakeClient() | ||||||
|  | 		err := fakeClient.Create(context.TODO(), lor) | ||||||
|  | 		assert.NoError(t, err) | ||||||
|  | 
 | ||||||
|  | 		baseReconcileLoop := New(fakeClient, nil, logf.ZapLogger(false), | ||||||
|  | 			&jenkins, false, false, nil, nil) | ||||||
|  | 
 | ||||||
|  | 		got, err := baseReconcileLoop.validateImagePullSecrets() | ||||||
|  | 		assert.Equal(t, got, false) | ||||||
|  | 		assert.Error(t, err) | ||||||
|  | 	}) | ||||||
|  | 
 | ||||||
|  | 	t.Run("no docker server", func(t *testing.T) { | ||||||
|  | 		lor := &corev1.Secret{ | ||||||
|  | 			ObjectMeta: metav1.ObjectMeta{ | ||||||
|  | 				Name: "test-ref", | ||||||
|  | 			}, | ||||||
|  | 			Data: map[string][]byte{ | ||||||
|  | 				"docker-username": []byte("test_user"), | ||||||
|  | 				"docker-password": []byte("test_password"), | ||||||
|  | 				"docker-email":    []byte("test_email"), | ||||||
|  | 			}, | ||||||
|  | 		} | ||||||
|  | 
 | ||||||
|  | 		jenkins := v1alpha2.Jenkins{ | ||||||
|  | 			Spec: v1alpha2.JenkinsSpec{ | ||||||
|  | 				Master: v1alpha2.JenkinsMaster{ | ||||||
|  | 					ImagePullSecrets: []corev1.LocalObjectReference{ | ||||||
|  | 						{Name: lor.ObjectMeta.Name}, | ||||||
|  | 					}, | ||||||
|  | 				}, | ||||||
|  | 			}, | ||||||
|  | 		} | ||||||
|  | 
 | ||||||
|  | 		fakeClient := fake.NewFakeClient() | ||||||
|  | 		err := fakeClient.Create(context.TODO(), lor) | ||||||
|  | 		assert.NoError(t, err) | ||||||
|  | 
 | ||||||
|  | 		baseReconcileLoop := New(fakeClient, nil, logf.ZapLogger(false), | ||||||
|  | 			&jenkins, false, false, nil, nil) | ||||||
|  | 
 | ||||||
|  | 		got, err := baseReconcileLoop.validateImagePullSecrets() | ||||||
|  | 		assert.Equal(t, got, false) | ||||||
|  | 		assert.Error(t, err) | ||||||
|  | 	}) | ||||||
|  | } | ||||||
|  | 
 | ||||||
| func TestValidateJenkinsMasterPodEnvs(t *testing.T) { | func TestValidateJenkinsMasterPodEnvs(t *testing.T) { | ||||||
| 	t.Run("happy", func(t *testing.T) { | 	t.Run("happy", func(t *testing.T) { | ||||||
| 		jenkins := v1alpha2.Jenkins{ | 		jenkins := v1alpha2.Jenkins{ | ||||||
|  |  | ||||||
		Loading…
	
		Reference in New Issue