Allow configure SecurityContext and Jenkins master container command

This commit is contained in:
Tomasz Sęk 2019-06-29 12:19:17 +02:00
parent 3573b9acd6
commit 3b26e1c5ba
No known key found for this signature in database
GPG Key ID: DC356D23F6A644D0
10 changed files with 64 additions and 24 deletions

View File

@ -211,7 +211,7 @@ run: build ## Run the executable, you can use EXTRA_ARGS
kubectl apply -f deploy/crds/jenkins_$(API_VERSION)_jenkins_crd.yaml
kubectl config use-context $(KUBECTL_CONTEXT)
@echo "Watching '$(WATCH_NAMESPACE)' namespace"
build/_output/bin/jenkins-operator $(EXTRA_ARGS)
build/_output/bin/jenkins-operator --local $(EXTRA_ARGS)
.PHONY: clean
clean: ## Cleanup any build binaries or packages

View File

@ -4,14 +4,9 @@ metadata:
name: example
spec:
master:
securityContext:
runAsUser: 1001
containers:
- name: jenkins-master
image: jenkins/jenkins:lts
command:
- bash
- "/var/jenkins/scripts/init.sh"
imagePullPolicy: Always
livenessProbe:
failureThreshold: 12

View File

@ -156,10 +156,12 @@ type JenkinsMaster struct {
NodeSelector map[string]string `json:"nodeSelector,omitempty"`
// SecurityContext that applies to all the containers of the Jenkins
// Master. As per kubernetes specification, it can be overidden
// Master. As per kubernetes specification, it can be overridden
// for each container individually.
// +optional
// Defaults to: nil
// Defaults to:
// runAsUser: 1000
// fsGroup: 1000
SecurityContext *corev1.PodSecurityContext `json:"securityContext,omitempty"`
// List of containers belonging to the pod.

View File

@ -53,6 +53,7 @@ func (in *Build) DeepCopy() *Build {
// DeepCopyInto is an autogenerated deepcopy function, copying the receiver, writing into out. in must be non-nil.
func (in *Container) DeepCopyInto(out *Container) {
*out = *in
in.Resources.DeepCopyInto(&out.Resources)
if in.Command != nil {
in, out := &in.Command, &out.Command
*out = make([]string, len(*in))
@ -82,7 +83,6 @@ func (in *Container) DeepCopyInto(out *Container) {
(*in)[i].DeepCopyInto(&(*out)[i])
}
}
in.Resources.DeepCopyInto(&out.Resources)
if in.VolumeMounts != nil {
in, out := &in.VolumeMounts, &out.VolumeMounts
*out = make([]v1.VolumeMount, len(*in))
@ -222,6 +222,11 @@ func (in *JenkinsMaster) DeepCopyInto(out *JenkinsMaster) {
(*out)[key] = val
}
}
if in.SecurityContext != nil {
in, out := &in.SecurityContext, &out.SecurityContext
*out = new(v1.PodSecurityContext)
(*in).DeepCopyInto(*out)
}
if in.Containers != nil {
in, out := &in.Containers, &out.Containers
*out = make([]Container, len(*in))

View File

@ -388,6 +388,11 @@ func (r *ReconcileJenkinsBaseConfiguration) ensureJenkinsMasterPod(meta metav1.O
currentJenkinsMasterPod, err := r.getJenkinsMasterPod(meta)
if err != nil && errors.IsNotFound(err) {
jenkinsMasterPod := resources.NewJenkinsMasterPod(meta, r.jenkins)
if !reflect.DeepEqual(jenkinsMasterPod.Spec.Containers[0].Command, resources.GetJenkinsMasterContainerBaseCommand()) {
r.logger.Info(fmt.Sprintf("spec.master.containers[%s].command has been overridden make sure the command looks like: '%v', otherwise the operator won't configure default user and install plugins",
resources.JenkinsMasterContainerName, []string{"bash", "-c", fmt.Sprintf("%s/%s && <custom-command-here> && /sbin/tini -s -- /usr/local/bin/jenkins.sh",
resources.JenkinsScriptsVolumePath, resources.InitScriptName)}))
}
r.logger.Info(fmt.Sprintf("Creating a new Jenkins Master Pod %s/%s", jenkinsMasterPod.Namespace, jenkinsMasterPod.Name))
err = r.createResource(jenkinsMasterPod)
if err != nil {
@ -475,6 +480,12 @@ func (r *ReconcileJenkinsBaseConfiguration) isRecreatePodNeeded(currentJenkinsMa
return true
}
if !reflect.DeepEqual(r.jenkins.Spec.Master.SecurityContext, currentJenkinsMasterPod.Spec.SecurityContext) {
r.logger.Info(fmt.Sprintf("Jenkins pod security context has changed, actual '%+v' required '%+v', recreating pod",
currentJenkinsMasterPod.Spec.SecurityContext, r.jenkins.Spec.Master.SecurityContext))
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))
@ -572,10 +583,10 @@ func (r *ReconcileJenkinsBaseConfiguration) compareContainers(expected corev1.Co
r.logger.Info(fmt.Sprintf("Resources have changed to '%+v' in container '%s', recreating pod", expected.Resources, expected.Name))
return true
}
/* if !reflect.DeepEqual(expected.SecurityContext, actual.SecurityContext) {
if !reflect.DeepEqual(expected.SecurityContext, actual.SecurityContext) {
r.logger.Info(fmt.Sprintf("Security context has changed to '%+v' in container '%s', recreating pod", expected.SecurityContext, expected.Name))
return true
}*/
}
if !reflect.DeepEqual(expected.WorkingDir, actual.WorkingDir) {
r.logger.Info(fmt.Sprintf("Working directory has changed to '%+v' in container '%s', recreating pod", expected.WorkingDir, expected.Name))
return true

View File

@ -19,8 +19,10 @@ const (
jenkinsHomePath = jenkinsPath + "/home"
jenkinsScriptsVolumeName = "scripts"
jenkinsScriptsVolumePath = jenkinsPath + "/scripts"
initScriptName = "init.sh"
// JenkinsScriptsVolumePath is a path where are scripts used to configure Jenkins
JenkinsScriptsVolumePath = jenkinsPath + "/scripts"
// InitScriptName is the init script name which configures init.groovy.d, scripts and install plugins
InitScriptName = "init.sh"
jenkinsOperatorCredentialsVolumeName = "operator-credentials"
jenkinsOperatorCredentialsVolumePath = jenkinsPath + "/operator-credentials"
@ -55,8 +57,18 @@ func buildPodTypeMeta() metav1.TypeMeta {
}
}
// GetJenkinsMasterPodBaseEnvs returns Jenkins master pod envs required by operator
func GetJenkinsMasterPodBaseEnvs() []corev1.EnvVar {
// GetJenkinsMasterContainerBaseCommand returns default Jenkins master container command
func GetJenkinsMasterContainerBaseCommand() []string {
return []string{
"bash",
"-c",
fmt.Sprintf("%s/%s && /sbin/tini -s -- /usr/local/bin/jenkins.sh",
JenkinsScriptsVolumePath, InitScriptName),
}
}
// GetJenkinsMasterContainerBaseEnvs returns Jenkins master pod envs required by operator
func GetJenkinsMasterContainerBaseEnvs() []corev1.EnvVar {
return []corev1.EnvVar{
{
Name: "JENKINS_HOME",
@ -77,6 +89,7 @@ func GetJenkinsMasterPodBaseEnvs() []corev1.EnvVar {
func GetJenkinsMasterPodBaseVolumes(jenkins *v1alpha2.Jenkins) []corev1.Volume {
configMapVolumeSourceDefaultMode := corev1.ConfigMapVolumeSourceDefaultMode
secretVolumeSourceDefaultMode := corev1.SecretVolumeSourceDefaultMode
var scriptsVolumeDefaultMode int32 = 0777
return []corev1.Volume{
{
Name: JenkinsHomeVolumeName,
@ -88,7 +101,7 @@ func GetJenkinsMasterPodBaseVolumes(jenkins *v1alpha2.Jenkins) []corev1.Volume {
Name: jenkinsScriptsVolumeName,
VolumeSource: corev1.VolumeSource{
ConfigMap: &corev1.ConfigMapVolumeSource{
DefaultMode: &configMapVolumeSourceDefaultMode,
DefaultMode: &scriptsVolumeDefaultMode,
LocalObjectReference: corev1.LocalObjectReference{
Name: getScriptsConfigMapName(jenkins),
},
@ -159,7 +172,7 @@ func GetJenkinsMasterContainerBaseVolumeMounts() []corev1.VolumeMount {
},
{
Name: jenkinsScriptsVolumeName,
MountPath: jenkinsScriptsVolumePath,
MountPath: JenkinsScriptsVolumePath,
ReadOnly: true,
},
{
@ -193,7 +206,7 @@ func GetJenkinsMasterContainerBaseVolumeMounts() []corev1.VolumeMount {
// NewJenkinsMasterContainer returns Jenkins master Kubernetes container
func NewJenkinsMasterContainer(jenkins *v1alpha2.Jenkins) corev1.Container {
jenkinsContainer := jenkins.Spec.Master.Containers[0]
envs := GetJenkinsMasterPodBaseEnvs()
envs := GetJenkinsMasterContainerBaseEnvs()
envs = append(envs, jenkinsContainer.Env...)
return corev1.Container{

View File

@ -239,7 +239,7 @@ main() {
main "$@"
`
var initBashTemplate = template.Must(template.New(initScriptName).Parse(`#!/usr/bin/env bash
var initBashTemplate = template.Must(template.New(InitScriptName).Parse(`#!/usr/bin/env bash
set -e
set -x
@ -261,8 +261,6 @@ echo "Installing plugins required by Operator - end"
echo "Installing plugins required by user - begin"
{{ $installPluginsCommand }} {{ range $index, $plugin := .UserPlugins }}{{ $plugin.Name }}:{{ $plugin.Version }} {{ end }}
echo "Installing plugins required by user - end"
/sbin/tini -s -- /usr/local/bin/jenkins.sh
`))
func buildConfigMapTypeMeta() metav1.TypeMeta {
@ -286,7 +284,7 @@ func buildInitBashScript(jenkins *v1alpha2.Jenkins) (*string, error) {
BasePlugins: jenkins.Spec.Master.BasePlugins,
UserPlugins: jenkins.Spec.Master.Plugins,
InstallPluginsCommand: installPluginsCommand,
JenkinsScriptsVolumePath: jenkinsScriptsVolumePath,
JenkinsScriptsVolumePath: JenkinsScriptsVolumePath,
}
output, err := render(initBashTemplate, data)
@ -314,7 +312,7 @@ func NewScriptsConfigMap(meta metav1.ObjectMeta, jenkins *v1alpha2.Jenkins) (*co
TypeMeta: buildConfigMapTypeMeta(),
ObjectMeta: meta,
Data: map[string]string{
initScriptName: *initBashScript,
InitScriptName: *initBashScript,
installPluginsCommand: fmt.Sprintf(installPluginsBashFmt, jenkinsHomePath),
},
}, nil

View File

@ -194,7 +194,7 @@ func (r *ReconcileJenkinsBaseConfiguration) validateContainerVolumeMounts(contai
}
func (r *ReconcileJenkinsBaseConfiguration) validateJenkinsMasterPodEnvs() bool {
baseEnvs := resources.GetJenkinsMasterPodBaseEnvs()
baseEnvs := resources.GetJenkinsMasterContainerBaseEnvs()
baseEnvNames := map[string]string{}
for _, env := range baseEnvs {
baseEnvNames[env.Name] = env.Value

View File

@ -325,6 +325,11 @@ func (r *ReconcileJenkins) setDefaults(jenkins *v1alpha2.Jenkins, logger logr.Lo
FailureThreshold: int32(12),
}
}
if len(jenkinsContainer.Command) == 0 {
logger.Info("Setting default Jenkins container command")
changed = true
jenkinsContainer.Command = resources.GetJenkinsMasterContainerBaseCommand()
}
if len(jenkins.Spec.Master.BasePlugins) == 0 {
logger.Info("Setting default operator plugins")
changed = true
@ -404,6 +409,17 @@ func (r *ReconcileJenkins) setDefaults(jenkins *v1alpha2.Jenkins, logger logr.Lo
jenkins.Spec.Master.Containers = containers
}
if jenkins.Spec.Master.SecurityContext == nil {
logger.Info("Setting default Jenkins master security context")
changed = true
var id int64 = 1000
securityContext := corev1.PodSecurityContext{
RunAsUser: &id,
FSGroup: &id,
}
jenkins.Spec.Master.SecurityContext = &securityContext
}
if changed {
return errors.WithStack(r.client.Update(context.TODO(), jenkins))
}