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 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
|
||||
|
|
|
|||
|
|
@ -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
|
||||
|
|
|
|||
|
|
@ -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.
|
||||
|
|
|
|||
|
|
@ -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))
|
||||
|
|
|
|||
|
|
@ -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
|
||||
|
|
|
|||
|
|
@ -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{
|
||||
|
|
|
|||
|
|
@ -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
|
||||
|
|
|
|||
|
|
@ -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
|
||||
|
|
|
|||
|
|
@ -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))
|
||||
}
|
||||
|
|
|
|||
Loading…
Reference in New Issue