#45 Add support for imagePullSecrets parameter
This commit is contained in:
		
							parent
							
								
									164fe30aef
								
							
						
					
					
						commit
						e049218b78
					
				|  | @ -210,6 +210,13 @@ type JenkinsMaster struct { | |||
| 	//       memory: 600Mi
 | ||||
| 	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.
 | ||||
| 	// More info: https://kubernetes.io/docs/concepts/storage/volumes
 | ||||
| 	// +optional
 | ||||
|  |  | |||
|  | @ -322,6 +322,11 @@ func (in *JenkinsMaster) DeepCopyInto(out *JenkinsMaster) { | |||
| 			(*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 { | ||||
| 		in, out := &in.Volumes, &out.Volumes | ||||
| 		*out = make([]v1.Volume, len(*in)) | ||||
|  |  | |||
|  | @ -527,6 +527,12 @@ func (r *ReconcileJenkinsBaseConfiguration) isRecreatePodNeeded(currentJenkinsMa | |||
| 		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) { | ||||
| 		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)) | ||||
|  |  | |||
|  | @ -124,6 +124,29 @@ func TestGetJenkinsOpts(t *testing.T) { | |||
| 		assert.Contains(t, opts, "httpPort") | ||||
| 		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) { | ||||
|  |  | |||
|  | @ -288,6 +288,7 @@ func NewJenkinsMasterPod(objectMeta metav1.ObjectMeta, jenkins *v1alpha2.Jenkins | |||
| 			Containers:         newContainers(jenkins), | ||||
| 			Volumes:            append(GetJenkinsMasterPodBaseVolumes(jenkins), jenkins.Spec.Master.Volumes...), | ||||
| 			SecurityContext:    jenkins.Spec.Master.SecurityContext, | ||||
| 			ImagePullSecrets: jenkins.Spec.Master.ImagePullSecrets, | ||||
| 		}, | ||||
| 	} | ||||
| } | ||||
|  |  | |||
|  | @ -2,6 +2,7 @@ package base | |||
| 
 | ||||
| import ( | ||||
| 	"context" | ||||
| 	"errors" | ||||
| 	"fmt" | ||||
| 	"regexp" | ||||
| 
 | ||||
|  | @ -60,6 +61,46 @@ func (r *ReconcileJenkinsBaseConfiguration) Validate(jenkins *v1alpha2.Jenkins) | |||
| 	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) { | ||||
| 	valid := true | ||||
| 	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) { | ||||
| 	t.Run("happy", func(t *testing.T) { | ||||
| 		jenkins := v1alpha2.Jenkins{ | ||||
|  |  | |||
		Loading…
	
		Reference in New Issue