Allow configure SecurityContext and Jenkins master container command
This commit is contained in:
parent
3573b9acd6
commit
3b26e1c5ba
2
Makefile
2
Makefile
|
|
@ -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 apply -f deploy/crds/jenkins_$(API_VERSION)_jenkins_crd.yaml
|
||||||
kubectl config use-context $(KUBECTL_CONTEXT)
|
kubectl config use-context $(KUBECTL_CONTEXT)
|
||||||
@echo "Watching '$(WATCH_NAMESPACE)' namespace"
|
@echo "Watching '$(WATCH_NAMESPACE)' namespace"
|
||||||
build/_output/bin/jenkins-operator $(EXTRA_ARGS)
|
build/_output/bin/jenkins-operator --local $(EXTRA_ARGS)
|
||||||
|
|
||||||
.PHONY: clean
|
.PHONY: clean
|
||||||
clean: ## Cleanup any build binaries or packages
|
clean: ## Cleanup any build binaries or packages
|
||||||
|
|
|
||||||
|
|
@ -4,14 +4,9 @@ metadata:
|
||||||
name: example
|
name: example
|
||||||
spec:
|
spec:
|
||||||
master:
|
master:
|
||||||
securityContext:
|
|
||||||
runAsUser: 1001
|
|
||||||
containers:
|
containers:
|
||||||
- name: jenkins-master
|
- name: jenkins-master
|
||||||
image: jenkins/jenkins:lts
|
image: jenkins/jenkins:lts
|
||||||
command:
|
|
||||||
- bash
|
|
||||||
- "/var/jenkins/scripts/init.sh"
|
|
||||||
imagePullPolicy: Always
|
imagePullPolicy: Always
|
||||||
livenessProbe:
|
livenessProbe:
|
||||||
failureThreshold: 12
|
failureThreshold: 12
|
||||||
|
|
|
||||||
|
|
@ -156,10 +156,12 @@ type JenkinsMaster struct {
|
||||||
NodeSelector map[string]string `json:"nodeSelector,omitempty"`
|
NodeSelector map[string]string `json:"nodeSelector,omitempty"`
|
||||||
|
|
||||||
// SecurityContext that applies to all the containers of the Jenkins
|
// 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.
|
// for each container individually.
|
||||||
// +optional
|
// +optional
|
||||||
// Defaults to: nil
|
// Defaults to:
|
||||||
|
// runAsUser: 1000
|
||||||
|
// fsGroup: 1000
|
||||||
SecurityContext *corev1.PodSecurityContext `json:"securityContext,omitempty"`
|
SecurityContext *corev1.PodSecurityContext `json:"securityContext,omitempty"`
|
||||||
|
|
||||||
// List of containers belonging to the pod.
|
// List of containers belonging to the pod.
|
||||||
|
|
|
||||||
|
|
@ -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.
|
// DeepCopyInto is an autogenerated deepcopy function, copying the receiver, writing into out. in must be non-nil.
|
||||||
func (in *Container) DeepCopyInto(out *Container) {
|
func (in *Container) DeepCopyInto(out *Container) {
|
||||||
*out = *in
|
*out = *in
|
||||||
|
in.Resources.DeepCopyInto(&out.Resources)
|
||||||
if in.Command != nil {
|
if in.Command != nil {
|
||||||
in, out := &in.Command, &out.Command
|
in, out := &in.Command, &out.Command
|
||||||
*out = make([]string, len(*in))
|
*out = make([]string, len(*in))
|
||||||
|
|
@ -82,7 +83,6 @@ func (in *Container) DeepCopyInto(out *Container) {
|
||||||
(*in)[i].DeepCopyInto(&(*out)[i])
|
(*in)[i].DeepCopyInto(&(*out)[i])
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
in.Resources.DeepCopyInto(&out.Resources)
|
|
||||||
if in.VolumeMounts != nil {
|
if in.VolumeMounts != nil {
|
||||||
in, out := &in.VolumeMounts, &out.VolumeMounts
|
in, out := &in.VolumeMounts, &out.VolumeMounts
|
||||||
*out = make([]v1.VolumeMount, len(*in))
|
*out = make([]v1.VolumeMount, len(*in))
|
||||||
|
|
@ -222,6 +222,11 @@ func (in *JenkinsMaster) DeepCopyInto(out *JenkinsMaster) {
|
||||||
(*out)[key] = val
|
(*out)[key] = val
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
if in.SecurityContext != nil {
|
||||||
|
in, out := &in.SecurityContext, &out.SecurityContext
|
||||||
|
*out = new(v1.PodSecurityContext)
|
||||||
|
(*in).DeepCopyInto(*out)
|
||||||
|
}
|
||||||
if in.Containers != nil {
|
if in.Containers != nil {
|
||||||
in, out := &in.Containers, &out.Containers
|
in, out := &in.Containers, &out.Containers
|
||||||
*out = make([]Container, len(*in))
|
*out = make([]Container, len(*in))
|
||||||
|
|
|
||||||
|
|
@ -388,6 +388,11 @@ func (r *ReconcileJenkinsBaseConfiguration) ensureJenkinsMasterPod(meta metav1.O
|
||||||
currentJenkinsMasterPod, err := r.getJenkinsMasterPod(meta)
|
currentJenkinsMasterPod, err := r.getJenkinsMasterPod(meta)
|
||||||
if err != nil && errors.IsNotFound(err) {
|
if err != nil && errors.IsNotFound(err) {
|
||||||
jenkinsMasterPod := resources.NewJenkinsMasterPod(meta, r.jenkins)
|
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))
|
r.logger.Info(fmt.Sprintf("Creating a new Jenkins Master Pod %s/%s", jenkinsMasterPod.Namespace, jenkinsMasterPod.Name))
|
||||||
err = r.createResource(jenkinsMasterPod)
|
err = r.createResource(jenkinsMasterPod)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
|
|
@ -475,6 +480,12 @@ func (r *ReconcileJenkinsBaseConfiguration) isRecreatePodNeeded(currentJenkinsMa
|
||||||
return true
|
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) {
|
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))
|
||||||
|
|
@ -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))
|
r.logger.Info(fmt.Sprintf("Resources have changed to '%+v' in container '%s', recreating pod", expected.Resources, expected.Name))
|
||||||
return true
|
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))
|
r.logger.Info(fmt.Sprintf("Security context has changed to '%+v' in container '%s', recreating pod", expected.SecurityContext, expected.Name))
|
||||||
return true
|
return true
|
||||||
}*/
|
}
|
||||||
if !reflect.DeepEqual(expected.WorkingDir, actual.WorkingDir) {
|
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))
|
r.logger.Info(fmt.Sprintf("Working directory has changed to '%+v' in container '%s', recreating pod", expected.WorkingDir, expected.Name))
|
||||||
return true
|
return true
|
||||||
|
|
|
||||||
|
|
@ -19,8 +19,10 @@ const (
|
||||||
jenkinsHomePath = jenkinsPath + "/home"
|
jenkinsHomePath = jenkinsPath + "/home"
|
||||||
|
|
||||||
jenkinsScriptsVolumeName = "scripts"
|
jenkinsScriptsVolumeName = "scripts"
|
||||||
jenkinsScriptsVolumePath = jenkinsPath + "/scripts"
|
// JenkinsScriptsVolumePath is a path where are scripts used to configure Jenkins
|
||||||
initScriptName = "init.sh"
|
JenkinsScriptsVolumePath = jenkinsPath + "/scripts"
|
||||||
|
// InitScriptName is the init script name which configures init.groovy.d, scripts and install plugins
|
||||||
|
InitScriptName = "init.sh"
|
||||||
|
|
||||||
jenkinsOperatorCredentialsVolumeName = "operator-credentials"
|
jenkinsOperatorCredentialsVolumeName = "operator-credentials"
|
||||||
jenkinsOperatorCredentialsVolumePath = jenkinsPath + "/operator-credentials"
|
jenkinsOperatorCredentialsVolumePath = jenkinsPath + "/operator-credentials"
|
||||||
|
|
@ -55,8 +57,18 @@ func buildPodTypeMeta() metav1.TypeMeta {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
// GetJenkinsMasterPodBaseEnvs returns Jenkins master pod envs required by operator
|
// GetJenkinsMasterContainerBaseCommand returns default Jenkins master container command
|
||||||
func GetJenkinsMasterPodBaseEnvs() []corev1.EnvVar {
|
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{
|
return []corev1.EnvVar{
|
||||||
{
|
{
|
||||||
Name: "JENKINS_HOME",
|
Name: "JENKINS_HOME",
|
||||||
|
|
@ -77,6 +89,7 @@ func GetJenkinsMasterPodBaseEnvs() []corev1.EnvVar {
|
||||||
func GetJenkinsMasterPodBaseVolumes(jenkins *v1alpha2.Jenkins) []corev1.Volume {
|
func GetJenkinsMasterPodBaseVolumes(jenkins *v1alpha2.Jenkins) []corev1.Volume {
|
||||||
configMapVolumeSourceDefaultMode := corev1.ConfigMapVolumeSourceDefaultMode
|
configMapVolumeSourceDefaultMode := corev1.ConfigMapVolumeSourceDefaultMode
|
||||||
secretVolumeSourceDefaultMode := corev1.SecretVolumeSourceDefaultMode
|
secretVolumeSourceDefaultMode := corev1.SecretVolumeSourceDefaultMode
|
||||||
|
var scriptsVolumeDefaultMode int32 = 0777
|
||||||
return []corev1.Volume{
|
return []corev1.Volume{
|
||||||
{
|
{
|
||||||
Name: JenkinsHomeVolumeName,
|
Name: JenkinsHomeVolumeName,
|
||||||
|
|
@ -88,7 +101,7 @@ func GetJenkinsMasterPodBaseVolumes(jenkins *v1alpha2.Jenkins) []corev1.Volume {
|
||||||
Name: jenkinsScriptsVolumeName,
|
Name: jenkinsScriptsVolumeName,
|
||||||
VolumeSource: corev1.VolumeSource{
|
VolumeSource: corev1.VolumeSource{
|
||||||
ConfigMap: &corev1.ConfigMapVolumeSource{
|
ConfigMap: &corev1.ConfigMapVolumeSource{
|
||||||
DefaultMode: &configMapVolumeSourceDefaultMode,
|
DefaultMode: &scriptsVolumeDefaultMode,
|
||||||
LocalObjectReference: corev1.LocalObjectReference{
|
LocalObjectReference: corev1.LocalObjectReference{
|
||||||
Name: getScriptsConfigMapName(jenkins),
|
Name: getScriptsConfigMapName(jenkins),
|
||||||
},
|
},
|
||||||
|
|
@ -159,7 +172,7 @@ func GetJenkinsMasterContainerBaseVolumeMounts() []corev1.VolumeMount {
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
Name: jenkinsScriptsVolumeName,
|
Name: jenkinsScriptsVolumeName,
|
||||||
MountPath: jenkinsScriptsVolumePath,
|
MountPath: JenkinsScriptsVolumePath,
|
||||||
ReadOnly: true,
|
ReadOnly: true,
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
|
|
@ -193,7 +206,7 @@ func GetJenkinsMasterContainerBaseVolumeMounts() []corev1.VolumeMount {
|
||||||
// NewJenkinsMasterContainer returns Jenkins master Kubernetes container
|
// NewJenkinsMasterContainer returns Jenkins master Kubernetes container
|
||||||
func NewJenkinsMasterContainer(jenkins *v1alpha2.Jenkins) corev1.Container {
|
func NewJenkinsMasterContainer(jenkins *v1alpha2.Jenkins) corev1.Container {
|
||||||
jenkinsContainer := jenkins.Spec.Master.Containers[0]
|
jenkinsContainer := jenkins.Spec.Master.Containers[0]
|
||||||
envs := GetJenkinsMasterPodBaseEnvs()
|
envs := GetJenkinsMasterContainerBaseEnvs()
|
||||||
envs = append(envs, jenkinsContainer.Env...)
|
envs = append(envs, jenkinsContainer.Env...)
|
||||||
|
|
||||||
return corev1.Container{
|
return corev1.Container{
|
||||||
|
|
|
||||||
|
|
@ -239,7 +239,7 @@ main() {
|
||||||
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 -e
|
||||||
set -x
|
set -x
|
||||||
|
|
||||||
|
|
@ -261,8 +261,6 @@ echo "Installing plugins required by Operator - end"
|
||||||
echo "Installing plugins required by user - begin"
|
echo "Installing plugins required by user - begin"
|
||||||
{{ $installPluginsCommand }} {{ range $index, $plugin := .UserPlugins }}{{ $plugin.Name }}:{{ $plugin.Version }} {{ end }}
|
{{ $installPluginsCommand }} {{ range $index, $plugin := .UserPlugins }}{{ $plugin.Name }}:{{ $plugin.Version }} {{ end }}
|
||||||
echo "Installing plugins required by user - end"
|
echo "Installing plugins required by user - end"
|
||||||
|
|
||||||
/sbin/tini -s -- /usr/local/bin/jenkins.sh
|
|
||||||
`))
|
`))
|
||||||
|
|
||||||
func buildConfigMapTypeMeta() metav1.TypeMeta {
|
func buildConfigMapTypeMeta() metav1.TypeMeta {
|
||||||
|
|
@ -286,7 +284,7 @@ func buildInitBashScript(jenkins *v1alpha2.Jenkins) (*string, error) {
|
||||||
BasePlugins: jenkins.Spec.Master.BasePlugins,
|
BasePlugins: jenkins.Spec.Master.BasePlugins,
|
||||||
UserPlugins: jenkins.Spec.Master.Plugins,
|
UserPlugins: jenkins.Spec.Master.Plugins,
|
||||||
InstallPluginsCommand: installPluginsCommand,
|
InstallPluginsCommand: installPluginsCommand,
|
||||||
JenkinsScriptsVolumePath: jenkinsScriptsVolumePath,
|
JenkinsScriptsVolumePath: JenkinsScriptsVolumePath,
|
||||||
}
|
}
|
||||||
|
|
||||||
output, err := render(initBashTemplate, data)
|
output, err := render(initBashTemplate, data)
|
||||||
|
|
@ -314,7 +312,7 @@ func NewScriptsConfigMap(meta metav1.ObjectMeta, jenkins *v1alpha2.Jenkins) (*co
|
||||||
TypeMeta: buildConfigMapTypeMeta(),
|
TypeMeta: buildConfigMapTypeMeta(),
|
||||||
ObjectMeta: meta,
|
ObjectMeta: meta,
|
||||||
Data: map[string]string{
|
Data: map[string]string{
|
||||||
initScriptName: *initBashScript,
|
InitScriptName: *initBashScript,
|
||||||
installPluginsCommand: fmt.Sprintf(installPluginsBashFmt, jenkinsHomePath),
|
installPluginsCommand: fmt.Sprintf(installPluginsBashFmt, jenkinsHomePath),
|
||||||
},
|
},
|
||||||
}, nil
|
}, nil
|
||||||
|
|
|
||||||
|
|
@ -194,7 +194,7 @@ func (r *ReconcileJenkinsBaseConfiguration) validateContainerVolumeMounts(contai
|
||||||
}
|
}
|
||||||
|
|
||||||
func (r *ReconcileJenkinsBaseConfiguration) validateJenkinsMasterPodEnvs() bool {
|
func (r *ReconcileJenkinsBaseConfiguration) validateJenkinsMasterPodEnvs() bool {
|
||||||
baseEnvs := resources.GetJenkinsMasterPodBaseEnvs()
|
baseEnvs := resources.GetJenkinsMasterContainerBaseEnvs()
|
||||||
baseEnvNames := map[string]string{}
|
baseEnvNames := map[string]string{}
|
||||||
for _, env := range baseEnvs {
|
for _, env := range baseEnvs {
|
||||||
baseEnvNames[env.Name] = env.Value
|
baseEnvNames[env.Name] = env.Value
|
||||||
|
|
|
||||||
|
|
@ -325,6 +325,11 @@ func (r *ReconcileJenkins) setDefaults(jenkins *v1alpha2.Jenkins, logger logr.Lo
|
||||||
FailureThreshold: int32(12),
|
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 {
|
if len(jenkins.Spec.Master.BasePlugins) == 0 {
|
||||||
logger.Info("Setting default operator plugins")
|
logger.Info("Setting default operator plugins")
|
||||||
changed = true
|
changed = true
|
||||||
|
|
@ -404,6 +409,17 @@ func (r *ReconcileJenkins) setDefaults(jenkins *v1alpha2.Jenkins, logger logr.Lo
|
||||||
jenkins.Spec.Master.Containers = containers
|
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 {
|
if changed {
|
||||||
return errors.WithStack(r.client.Update(context.TODO(), jenkins))
|
return errors.WithStack(r.client.Update(context.TODO(), jenkins))
|
||||||
}
|
}
|
||||||
|
|
|
||||||
Loading…
Reference in New Issue