#9 Allow set Jenkins master pod env variables
This commit is contained in:
		
							parent
							
								
									35f1b318bb
								
							
						
					
					
						commit
						2aa96789b4
					
				|  | @ -25,6 +25,7 @@ type JenkinsMaster struct { | ||||||
| 	NodeSelector map[string]string           `json:"nodeSelector,omitempty"` | 	NodeSelector map[string]string           `json:"nodeSelector,omitempty"` | ||||||
| 	Annotations  map[string]string           `json:"masterAnnotations,omitempty"` | 	Annotations  map[string]string           `json:"masterAnnotations,omitempty"` | ||||||
| 	Resources    corev1.ResourceRequirements `json:"resources,omitempty"` | 	Resources    corev1.ResourceRequirements `json:"resources,omitempty"` | ||||||
|  | 	Env          []corev1.EnvVar             `json:"env,omitempty"` | ||||||
| 	// OperatorPlugins contains plugins required by operator
 | 	// OperatorPlugins contains plugins required by operator
 | ||||||
| 	OperatorPlugins map[string][]string `json:"basePlugins,omitempty"` | 	OperatorPlugins map[string][]string `json:"basePlugins,omitempty"` | ||||||
| 	// Plugins contains plugins required by user
 | 	// Plugins contains plugins required by user
 | ||||||
|  |  | ||||||
|  | @ -128,6 +128,13 @@ func (in *JenkinsMaster) DeepCopyInto(out *JenkinsMaster) { | ||||||
| 		} | 		} | ||||||
| 	} | 	} | ||||||
| 	in.Resources.DeepCopyInto(&out.Resources) | 	in.Resources.DeepCopyInto(&out.Resources) | ||||||
|  | 	if in.Env != nil { | ||||||
|  | 		in, out := &in.Env, &out.Env | ||||||
|  | 		*out = make([]v1.EnvVar, len(*in)) | ||||||
|  | 		for i := range *in { | ||||||
|  | 			(*in)[i].DeepCopyInto(&(*out)[i]) | ||||||
|  | 		} | ||||||
|  | 	} | ||||||
| 	if in.OperatorPlugins != nil { | 	if in.OperatorPlugins != nil { | ||||||
| 		in, out := &in.OperatorPlugins, &out.OperatorPlugins | 		in, out := &in.OperatorPlugins, &out.OperatorPlugins | ||||||
| 		*out = make(map[string][]string, len(*in)) | 		*out = make(map[string][]string, len(*in)) | ||||||
|  |  | ||||||
|  | @ -401,7 +401,15 @@ func (r *ReconcileJenkinsBaseConfiguration) isRecreatePodNeeded(currentJenkinsMa | ||||||
| 
 | 
 | ||||||
| 	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", | ||||||
| 			r.jenkins.Spec.Master.NodeSelector, currentJenkinsMasterPod.Spec.NodeSelector)) | 			currentJenkinsMasterPod.Spec.NodeSelector, r.jenkins.Spec.Master.NodeSelector)) | ||||||
|  | 		recreatePod = true | ||||||
|  | 	} | ||||||
|  | 
 | ||||||
|  | 	requiredEnvs := resources.GetJenkinsMasterPodBaseEnvs() | ||||||
|  | 	requiredEnvs = append(requiredEnvs, r.jenkins.Spec.Master.Env...) | ||||||
|  | 	if !reflect.DeepEqual(requiredEnvs, currentJenkinsMasterPod.Spec.Containers[0].Env) { | ||||||
|  | 		r.logger.Info(fmt.Sprintf("Jenkins env have changed, actual '%+v' required '%+v' - recreating pod", | ||||||
|  | 			currentJenkinsMasterPod.Spec.Containers[0].Env, requiredEnvs)) | ||||||
| 		recreatePod = true | 		recreatePod = true | ||||||
| 	} | 	} | ||||||
| 
 | 
 | ||||||
|  |  | ||||||
|  | @ -55,6 +55,24 @@ func buildPodTypeMeta() metav1.TypeMeta { | ||||||
| 	} | 	} | ||||||
| } | } | ||||||
| 
 | 
 | ||||||
|  | // GetJenkinsMasterPodBaseEnvs returns Jenkins master pod envs required by operator
 | ||||||
|  | func GetJenkinsMasterPodBaseEnvs() []corev1.EnvVar { | ||||||
|  | 	return []corev1.EnvVar{ | ||||||
|  | 		{ | ||||||
|  | 			Name:  "JENKINS_HOME", | ||||||
|  | 			Value: jenkinsHomePath, | ||||||
|  | 		}, | ||||||
|  | 		{ | ||||||
|  | 			Name:  "JAVA_OPTS", | ||||||
|  | 			Value: "-XX:+UnlockExperimentalVMOptions -XX:+UseCGroupMemoryLimitForHeap -XX:MaxRAMFraction=1 -Djenkins.install.runSetupWizard=false -Djava.awt.headless=true", | ||||||
|  | 		}, | ||||||
|  | 		{ | ||||||
|  | 			Name:  "SECRETS", // https://github.com/jenkinsci/configuration-as-code-plugin/blob/master/demos/kubernetes-secrets/README.md
 | ||||||
|  | 			Value: UserConfigurationSecretVolumePath, | ||||||
|  | 		}, | ||||||
|  | 	} | ||||||
|  | } | ||||||
|  | 
 | ||||||
| // NewJenkinsMasterPod builds Jenkins Master Kubernetes Pod resource
 | // NewJenkinsMasterPod builds Jenkins Master Kubernetes Pod resource
 | ||||||
| func NewJenkinsMasterPod(objectMeta metav1.ObjectMeta, jenkins *v1alpha1.Jenkins) *corev1.Pod { | func NewJenkinsMasterPod(objectMeta metav1.ObjectMeta, jenkins *v1alpha1.Jenkins) *corev1.Pod { | ||||||
| 	initialDelaySeconds := int32(30) | 	initialDelaySeconds := int32(30) | ||||||
|  | @ -63,6 +81,8 @@ func NewJenkinsMasterPod(objectMeta metav1.ObjectMeta, jenkins *v1alpha1.Jenkins | ||||||
| 	runAsUser := jenkinsUserUID | 	runAsUser := jenkinsUserUID | ||||||
| 
 | 
 | ||||||
| 	objectMeta.Annotations = jenkins.Spec.Master.Annotations | 	objectMeta.Annotations = jenkins.Spec.Master.Annotations | ||||||
|  | 	envs := GetJenkinsMasterPodBaseEnvs() | ||||||
|  | 	envs = append(envs, jenkins.Spec.Master.Env...) | ||||||
| 
 | 
 | ||||||
| 	return &corev1.Pod{ | 	return &corev1.Pod{ | ||||||
| 		TypeMeta:   buildPodTypeMeta(), | 		TypeMeta:   buildPodTypeMeta(), | ||||||
|  | @ -115,20 +135,7 @@ func NewJenkinsMasterPod(objectMeta metav1.ObjectMeta, jenkins *v1alpha1.Jenkins | ||||||
| 							ContainerPort: constants.DefaultSlavePortInt32, | 							ContainerPort: constants.DefaultSlavePortInt32, | ||||||
| 						}, | 						}, | ||||||
| 					}, | 					}, | ||||||
| 					Env: []corev1.EnvVar{ | 					Env:       envs, | ||||||
| 						{ |  | ||||||
| 							Name:  "JENKINS_HOME", |  | ||||||
| 							Value: jenkinsHomePath, |  | ||||||
| 						}, |  | ||||||
| 						{ |  | ||||||
| 							Name:  "JAVA_OPTS", |  | ||||||
| 							Value: "-XX:+UnlockExperimentalVMOptions -XX:+UseCGroupMemoryLimitForHeap -XX:MaxRAMFraction=1 -Djenkins.install.runSetupWizard=false -Djava.awt.headless=true", |  | ||||||
| 						}, |  | ||||||
| 						{ |  | ||||||
| 							Name:  "SECRETS", // https://github.com/jenkinsci/configuration-as-code-plugin/blob/master/demos/kubernetes-secrets/README.md
 |  | ||||||
| 							Value: UserConfigurationSecretVolumePath, |  | ||||||
| 						}, |  | ||||||
| 					}, |  | ||||||
| 					Resources: jenkins.Spec.Master.Resources, | 					Resources: jenkins.Spec.Master.Resources, | ||||||
| 					VolumeMounts: []corev1.VolumeMount{ | 					VolumeMounts: []corev1.VolumeMount{ | ||||||
| 						{ | 						{ | ||||||
|  |  | ||||||
|  | @ -5,6 +5,7 @@ import ( | ||||||
| 	"regexp" | 	"regexp" | ||||||
| 
 | 
 | ||||||
| 	"github.com/jenkinsci/kubernetes-operator/pkg/apis/jenkinsio/v1alpha1" | 	"github.com/jenkinsci/kubernetes-operator/pkg/apis/jenkinsio/v1alpha1" | ||||||
|  | 	"github.com/jenkinsci/kubernetes-operator/pkg/controller/jenkins/configuration/base/resources" | ||||||
| 	"github.com/jenkinsci/kubernetes-operator/pkg/controller/jenkins/plugins" | 	"github.com/jenkinsci/kubernetes-operator/pkg/controller/jenkins/plugins" | ||||||
| 	"github.com/jenkinsci/kubernetes-operator/pkg/log" | 	"github.com/jenkinsci/kubernetes-operator/pkg/log" | ||||||
| 
 | 
 | ||||||
|  | @ -32,9 +33,31 @@ func (r *ReconcileJenkinsBaseConfiguration) Validate(jenkins *v1alpha1.Jenkins) | ||||||
| 		return false, nil | 		return false, nil | ||||||
| 	} | 	} | ||||||
| 
 | 
 | ||||||
|  | 	if !r.validateJenkinsMasterPodEnvs() { | ||||||
|  | 		return false, nil | ||||||
|  | 	} | ||||||
|  | 
 | ||||||
| 	return true, nil | 	return true, nil | ||||||
| } | } | ||||||
| 
 | 
 | ||||||
|  | func (r *ReconcileJenkinsBaseConfiguration) validateJenkinsMasterPodEnvs() bool { | ||||||
|  | 	baseEnvs := resources.GetJenkinsMasterPodBaseEnvs() | ||||||
|  | 	baseEnvNames := map[string]string{} | ||||||
|  | 	for _, env := range baseEnvs { | ||||||
|  | 		baseEnvNames[env.Name] = env.Value | ||||||
|  | 	} | ||||||
|  | 
 | ||||||
|  | 	valid := true | ||||||
|  | 	for _, userEnv := range r.jenkins.Spec.Master.Env { | ||||||
|  | 		if _, overriding := baseEnvNames[userEnv.Name]; overriding { | ||||||
|  | 			r.logger.V(log.VWarn).Info(fmt.Sprintf("Jenkins Master pod env '%s' cannot be overridden", userEnv.Name)) | ||||||
|  | 			valid = false | ||||||
|  | 		} | ||||||
|  | 	} | ||||||
|  | 
 | ||||||
|  | 	return valid | ||||||
|  | } | ||||||
|  | 
 | ||||||
| func (r *ReconcileJenkinsBaseConfiguration) validatePlugins(pluginsWithVersionSlice ...map[string][]string) bool { | func (r *ReconcileJenkinsBaseConfiguration) validatePlugins(pluginsWithVersionSlice ...map[string][]string) bool { | ||||||
| 	valid := true | 	valid := true | ||||||
| 	allPlugins := map[plugins.Plugin][]plugins.Plugin{} | 	allPlugins := map[plugins.Plugin][]plugins.Plugin{} | ||||||
|  |  | ||||||
|  | @ -3,7 +3,10 @@ package base | ||||||
| import ( | import ( | ||||||
| 	"testing" | 	"testing" | ||||||
| 
 | 
 | ||||||
|  | 	"github.com/jenkinsci/kubernetes-operator/pkg/apis/jenkinsio/v1alpha1" | ||||||
|  | 
 | ||||||
| 	"github.com/stretchr/testify/assert" | 	"github.com/stretchr/testify/assert" | ||||||
|  | 	"k8s.io/api/core/v1" | ||||||
| 	logf "sigs.k8s.io/controller-runtime/pkg/runtime/log" | 	logf "sigs.k8s.io/controller-runtime/pkg/runtime/log" | ||||||
| ) | ) | ||||||
| 
 | 
 | ||||||
|  | @ -65,3 +68,42 @@ func TestValidatePlugins(t *testing.T) { | ||||||
| 	}) | 	}) | ||||||
| 
 | 
 | ||||||
| } | } | ||||||
|  | 
 | ||||||
|  | func TestValidateJenkinsMasterPodEnvs(t *testing.T) { | ||||||
|  | 	t.Run("happy", func(t *testing.T) { | ||||||
|  | 		jenkins := v1alpha1.Jenkins{ | ||||||
|  | 			Spec: v1alpha1.JenkinsSpec{ | ||||||
|  | 				Master: v1alpha1.JenkinsMaster{ | ||||||
|  | 					Env: []v1.EnvVar{ | ||||||
|  | 						{ | ||||||
|  | 							Name:  "SOME_VALUE", | ||||||
|  | 							Value: "", | ||||||
|  | 						}, | ||||||
|  | 					}, | ||||||
|  | 				}, | ||||||
|  | 			}, | ||||||
|  | 		} | ||||||
|  | 		baseReconcileLoop := New(nil, nil, logf.ZapLogger(false), | ||||||
|  | 			&jenkins, false, false) | ||||||
|  | 		got := baseReconcileLoop.validateJenkinsMasterPodEnvs() | ||||||
|  | 		assert.Equal(t, true, got) | ||||||
|  | 	}) | ||||||
|  | 	t.Run("override JENKINS_HOME env", func(t *testing.T) { | ||||||
|  | 		jenkins := v1alpha1.Jenkins{ | ||||||
|  | 			Spec: v1alpha1.JenkinsSpec{ | ||||||
|  | 				Master: v1alpha1.JenkinsMaster{ | ||||||
|  | 					Env: []v1.EnvVar{ | ||||||
|  | 						{ | ||||||
|  | 							Name:  "JENKINS_HOME", | ||||||
|  | 							Value: "", | ||||||
|  | 						}, | ||||||
|  | 					}, | ||||||
|  | 				}, | ||||||
|  | 			}, | ||||||
|  | 		} | ||||||
|  | 		baseReconcileLoop := New(nil, nil, logf.ZapLogger(false), | ||||||
|  | 			&jenkins, false, false) | ||||||
|  | 		got := baseReconcileLoop.validateJenkinsMasterPodEnvs() | ||||||
|  | 		assert.Equal(t, false, got) | ||||||
|  | 	}) | ||||||
|  | } | ||||||
|  |  | ||||||
|  | @ -143,6 +143,12 @@ func verifyJenkinsMasterPodAttributes(t *testing.T, jenkins *v1alpha1.Jenkins) { | ||||||
| 		t.Fatalf("Invalid jenkins pod node selector expected '%+v', actual '%+v'", jenkins.Spec.Master.NodeSelector, jenkinsPod.Spec.NodeSelector) | 		t.Fatalf("Invalid jenkins pod node selector expected '%+v', actual '%+v'", jenkins.Spec.Master.NodeSelector, jenkinsPod.Spec.NodeSelector) | ||||||
| 	} | 	} | ||||||
| 
 | 
 | ||||||
|  | 	requiredEnvs := resources.GetJenkinsMasterPodBaseEnvs() | ||||||
|  | 	requiredEnvs = append(requiredEnvs, jenkins.Spec.Master.Env...) | ||||||
|  | 	if !reflect.DeepEqual(jenkinsPod.Spec.Containers[0].Env, requiredEnvs) { | ||||||
|  | 		t.Fatalf("Invalid jenkins pod continer resources expected '%+v', actual '%+v'", requiredEnvs, jenkinsPod.Spec.Containers[0].Env) | ||||||
|  | 	} | ||||||
|  | 
 | ||||||
| 	t.Log("Jenkins pod attributes are valid") | 	t.Log("Jenkins pod attributes are valid") | ||||||
| } | } | ||||||
| 
 | 
 | ||||||
|  |  | ||||||
|  | @ -73,6 +73,12 @@ func createJenkinsCR(t *testing.T, name, namespace string) *v1alpha1.Jenkins { | ||||||
| 					"simple-theme-plugin:0.5.1": {}, | 					"simple-theme-plugin:0.5.1": {}, | ||||||
| 				}, | 				}, | ||||||
| 				NodeSelector: map[string]string{"kubernetes.io/hostname": "minikube"}, | 				NodeSelector: map[string]string{"kubernetes.io/hostname": "minikube"}, | ||||||
|  | 				Env: []v1.EnvVar{ | ||||||
|  | 					{ | ||||||
|  | 						Name:  "TEST_ENV", | ||||||
|  | 						Value: "test_env_value", | ||||||
|  | 					}, | ||||||
|  | 				}, | ||||||
| 			}, | 			}, | ||||||
| 			//TODO(bantoniak) add seed job with private key
 | 			//TODO(bantoniak) add seed job with private key
 | ||||||
| 			SeedJobs: []v1alpha1.SeedJob{ | 			SeedJobs: []v1alpha1.SeedJob{ | ||||||
|  |  | ||||||
		Loading…
	
		Reference in New Issue