#9 Allow set Jenkins master pod env variables

This commit is contained in:
Tomasz Sęk 2019-03-11 16:52:28 +01:00
parent 35f1b318bb
commit 2aa96789b4
No known key found for this signature in database
GPG Key ID: DC356D23F6A644D0
8 changed files with 115 additions and 15 deletions

View File

@ -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

View File

@ -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))

View File

@ -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
} }

View File

@ -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{
{ {

View File

@ -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{}

View File

@ -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)
})
}

View File

@ -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")
} }

View File

@ -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{