295 lines
		
	
	
		
			9.6 KiB
		
	
	
	
		
			Go
		
	
	
	
			
		
		
	
	
			295 lines
		
	
	
		
			9.6 KiB
		
	
	
	
		
			Go
		
	
	
	
| package resources
 | |
| 
 | |
| import (
 | |
| 	"fmt"
 | |
| 
 | |
| 	"github.com/jenkinsci/kubernetes-operator/pkg/apis/jenkins/v1alpha2"
 | |
| 	"github.com/jenkinsci/kubernetes-operator/pkg/controller/jenkins/constants"
 | |
| 
 | |
| 	corev1 "k8s.io/api/core/v1"
 | |
| 	metav1 "k8s.io/apimachinery/pkg/apis/meta/v1"
 | |
| )
 | |
| 
 | |
| const (
 | |
| 	// JenkinsMasterContainerName is the Jenkins master container name in pod
 | |
| 	JenkinsMasterContainerName = "jenkins-master"
 | |
| 	// JenkinsHomeVolumeName is the Jenkins home volume name
 | |
| 	JenkinsHomeVolumeName = "jenkins-home"
 | |
| 	jenkinsPath           = "/var/jenkins"
 | |
| 	jenkinsHomePath       = jenkinsPath + "/home"
 | |
| 
 | |
| 	jenkinsScriptsVolumeName = "scripts"
 | |
| 	// 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"
 | |
| 
 | |
| 	jenkinsInitConfigurationVolumeName = "init-configuration"
 | |
| 	jenkinsInitConfigurationVolumePath = jenkinsPath + "/init-configuration"
 | |
| 
 | |
| 	// GroovyScriptsSecretVolumePath is a path where are groovy scripts used to configure Jenkins
 | |
| 	// This script is provided by user
 | |
| 	GroovyScriptsSecretVolumePath = jenkinsPath + "/groovy-scripts-secrets"
 | |
| 	// ConfigurationAsCodeSecretVolumePath is a path where are CasC configs used to configure Jenkins
 | |
| 	// This script is provided by user
 | |
| 	ConfigurationAsCodeSecretVolumePath = jenkinsPath + "/configuration-as-code-secrets"
 | |
| 
 | |
| 	httpPortName  = "http"
 | |
| 	slavePortName = "slavelistener"
 | |
| 	// HTTPPortInt defines Jenkins master HTTP port
 | |
| 	HTTPPortInt = 8080
 | |
| )
 | |
| 
 | |
| func buildPodTypeMeta() metav1.TypeMeta {
 | |
| 	return metav1.TypeMeta{
 | |
| 		Kind:       "Pod",
 | |
| 		APIVersion: "v1",
 | |
| 	}
 | |
| }
 | |
| 
 | |
| // 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(jenkins *v1alpha2.Jenkins) []corev1.EnvVar {
 | |
| 	envVars := []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",
 | |
| 		},
 | |
| 	}
 | |
| 
 | |
| 	if len(jenkins.Spec.ConfigurationAsCode.Secret.Name) > 0 {
 | |
| 		envVars = append(envVars, corev1.EnvVar{
 | |
| 			Name:  "SECRETS", // https://github.com/jenkinsci/configuration-as-code-plugin/blob/master/demos/kubernetes-secrets/README.md
 | |
| 			Value: ConfigurationAsCodeSecretVolumePath,
 | |
| 		})
 | |
| 	}
 | |
| 
 | |
| 	return envVars
 | |
| }
 | |
| 
 | |
| // GetJenkinsMasterPodBaseVolumes returns Jenkins master pod volumes required by operator
 | |
| func GetJenkinsMasterPodBaseVolumes(jenkins *v1alpha2.Jenkins) []corev1.Volume {
 | |
| 	configMapVolumeSourceDefaultMode := corev1.ConfigMapVolumeSourceDefaultMode
 | |
| 	secretVolumeSourceDefaultMode := corev1.SecretVolumeSourceDefaultMode
 | |
| 	var scriptsVolumeDefaultMode int32 = 0777
 | |
| 	volumes := []corev1.Volume{
 | |
| 		{
 | |
| 			Name: JenkinsHomeVolumeName,
 | |
| 			VolumeSource: corev1.VolumeSource{
 | |
| 				EmptyDir: &corev1.EmptyDirVolumeSource{},
 | |
| 			},
 | |
| 		},
 | |
| 		{
 | |
| 			Name: jenkinsScriptsVolumeName,
 | |
| 			VolumeSource: corev1.VolumeSource{
 | |
| 				ConfigMap: &corev1.ConfigMapVolumeSource{
 | |
| 					DefaultMode: &scriptsVolumeDefaultMode,
 | |
| 					LocalObjectReference: corev1.LocalObjectReference{
 | |
| 						Name: getScriptsConfigMapName(jenkins),
 | |
| 					},
 | |
| 				},
 | |
| 			},
 | |
| 		},
 | |
| 		{
 | |
| 			Name: jenkinsInitConfigurationVolumeName,
 | |
| 			VolumeSource: corev1.VolumeSource{
 | |
| 				ConfigMap: &corev1.ConfigMapVolumeSource{
 | |
| 					DefaultMode: &configMapVolumeSourceDefaultMode,
 | |
| 					LocalObjectReference: corev1.LocalObjectReference{
 | |
| 						Name: GetInitConfigurationConfigMapName(jenkins),
 | |
| 					},
 | |
| 				},
 | |
| 			},
 | |
| 		},
 | |
| 		{
 | |
| 			Name: jenkinsOperatorCredentialsVolumeName,
 | |
| 			VolumeSource: corev1.VolumeSource{
 | |
| 				Secret: &corev1.SecretVolumeSource{
 | |
| 					DefaultMode: &secretVolumeSourceDefaultMode,
 | |
| 					SecretName:  GetOperatorCredentialsSecretName(jenkins),
 | |
| 				},
 | |
| 			},
 | |
| 		},
 | |
| 	}
 | |
| 
 | |
| 	if len(jenkins.Spec.GroovyScripts.Secret.Name) > 0 {
 | |
| 		volumes = append(volumes, corev1.Volume{
 | |
| 			Name: getGroovyScriptsSecretVolumeName(jenkins),
 | |
| 			VolumeSource: corev1.VolumeSource{
 | |
| 				Secret: &corev1.SecretVolumeSource{
 | |
| 					DefaultMode: &secretVolumeSourceDefaultMode,
 | |
| 					SecretName:  jenkins.Spec.GroovyScripts.Secret.Name,
 | |
| 				},
 | |
| 			},
 | |
| 		})
 | |
| 	}
 | |
| 	if len(jenkins.Spec.ConfigurationAsCode.Secret.Name) > 0 {
 | |
| 		volumes = append(volumes, corev1.Volume{
 | |
| 			Name: getConfigurationAsCodeSecretVolumeName(jenkins),
 | |
| 			VolumeSource: corev1.VolumeSource{
 | |
| 				Secret: &corev1.SecretVolumeSource{
 | |
| 					DefaultMode: &secretVolumeSourceDefaultMode,
 | |
| 					SecretName:  jenkins.Spec.ConfigurationAsCode.Secret.Name,
 | |
| 				},
 | |
| 			},
 | |
| 		})
 | |
| 	}
 | |
| 
 | |
| 	return volumes
 | |
| }
 | |
| 
 | |
| func getGroovyScriptsSecretVolumeName(jenkins *v1alpha2.Jenkins) string {
 | |
| 	return "gs-" + jenkins.Spec.GroovyScripts.Secret.Name
 | |
| }
 | |
| 
 | |
| func getConfigurationAsCodeSecretVolumeName(jenkins *v1alpha2.Jenkins) string {
 | |
| 	return "casc-" + jenkins.Spec.GroovyScripts.Secret.Name
 | |
| }
 | |
| 
 | |
| // GetJenkinsMasterContainerBaseVolumeMounts returns Jenkins master pod volume mounts required by operator
 | |
| func GetJenkinsMasterContainerBaseVolumeMounts(jenkins *v1alpha2.Jenkins) []corev1.VolumeMount {
 | |
| 	volumeMounts := []corev1.VolumeMount{
 | |
| 		{
 | |
| 			Name:      JenkinsHomeVolumeName,
 | |
| 			MountPath: jenkinsHomePath,
 | |
| 			ReadOnly:  false,
 | |
| 		},
 | |
| 		{
 | |
| 			Name:      jenkinsScriptsVolumeName,
 | |
| 			MountPath: JenkinsScriptsVolumePath,
 | |
| 			ReadOnly:  true,
 | |
| 		},
 | |
| 		{
 | |
| 			Name:      jenkinsInitConfigurationVolumeName,
 | |
| 			MountPath: jenkinsInitConfigurationVolumePath,
 | |
| 			ReadOnly:  true,
 | |
| 		},
 | |
| 		{
 | |
| 			Name:      jenkinsOperatorCredentialsVolumeName,
 | |
| 			MountPath: jenkinsOperatorCredentialsVolumePath,
 | |
| 			ReadOnly:  true,
 | |
| 		},
 | |
| 	}
 | |
| 
 | |
| 	if len(jenkins.Spec.GroovyScripts.Secret.Name) > 0 {
 | |
| 		volumeMounts = append(volumeMounts, corev1.VolumeMount{
 | |
| 			Name:      getGroovyScriptsSecretVolumeName(jenkins),
 | |
| 			MountPath: GroovyScriptsSecretVolumePath,
 | |
| 			ReadOnly:  true,
 | |
| 		})
 | |
| 	}
 | |
| 	if len(jenkins.Spec.ConfigurationAsCode.Secret.Name) > 0 {
 | |
| 		volumeMounts = append(volumeMounts, corev1.VolumeMount{
 | |
| 			Name:      getConfigurationAsCodeSecretVolumeName(jenkins),
 | |
| 			MountPath: ConfigurationAsCodeSecretVolumePath,
 | |
| 			ReadOnly:  true,
 | |
| 		})
 | |
| 	}
 | |
| 
 | |
| 	return volumeMounts
 | |
| }
 | |
| 
 | |
| // NewJenkinsMasterContainer returns Jenkins master Kubernetes container
 | |
| func NewJenkinsMasterContainer(jenkins *v1alpha2.Jenkins) corev1.Container {
 | |
| 	jenkinsContainer := jenkins.Spec.Master.Containers[0]
 | |
| 	envs := GetJenkinsMasterContainerBaseEnvs(jenkins)
 | |
| 	envs = append(envs, jenkinsContainer.Env...)
 | |
| 
 | |
| 	return corev1.Container{
 | |
| 		Name:            JenkinsMasterContainerName,
 | |
| 		Image:           jenkinsContainer.Image,
 | |
| 		ImagePullPolicy: jenkinsContainer.ImagePullPolicy,
 | |
| 		Command:         jenkinsContainer.Command,
 | |
| 		LivenessProbe:   jenkinsContainer.LivenessProbe,
 | |
| 		ReadinessProbe:  jenkinsContainer.ReadinessProbe,
 | |
| 		Ports: []corev1.ContainerPort{
 | |
| 			{
 | |
| 				Name:          httpPortName,
 | |
| 				ContainerPort: constants.DefaultHTTPPortInt32,
 | |
| 				Protocol:      corev1.ProtocolTCP,
 | |
| 			},
 | |
| 			{
 | |
| 				Name:          slavePortName,
 | |
| 				ContainerPort: constants.DefaultSlavePortInt32,
 | |
| 				Protocol:      corev1.ProtocolTCP,
 | |
| 			},
 | |
| 		},
 | |
| 		Env:          envs,
 | |
| 		Resources:    jenkinsContainer.Resources,
 | |
| 		VolumeMounts: append(GetJenkinsMasterContainerBaseVolumeMounts(jenkins), jenkinsContainer.VolumeMounts...),
 | |
| 	}
 | |
| }
 | |
| 
 | |
| // ConvertJenkinsContainerToKubernetesContainer converts Jenkins container to Kubernetes container
 | |
| func ConvertJenkinsContainerToKubernetesContainer(container v1alpha2.Container) corev1.Container {
 | |
| 	return corev1.Container{
 | |
| 		Name:            container.Name,
 | |
| 		Image:           container.Image,
 | |
| 		Command:         container.Command,
 | |
| 		Args:            container.Args,
 | |
| 		WorkingDir:      container.WorkingDir,
 | |
| 		Ports:           container.Ports,
 | |
| 		EnvFrom:         container.EnvFrom,
 | |
| 		Env:             container.Env,
 | |
| 		Resources:       container.Resources,
 | |
| 		VolumeMounts:    container.VolumeMounts,
 | |
| 		LivenessProbe:   container.LivenessProbe,
 | |
| 		ReadinessProbe:  container.ReadinessProbe,
 | |
| 		Lifecycle:       container.Lifecycle,
 | |
| 		ImagePullPolicy: container.ImagePullPolicy,
 | |
| 		SecurityContext: container.SecurityContext,
 | |
| 	}
 | |
| }
 | |
| 
 | |
| func newContainers(jenkins *v1alpha2.Jenkins) (containers []corev1.Container) {
 | |
| 	containers = append(containers, NewJenkinsMasterContainer(jenkins))
 | |
| 
 | |
| 	for _, container := range jenkins.Spec.Master.Containers[1:] {
 | |
| 		containers = append(containers, ConvertJenkinsContainerToKubernetesContainer(container))
 | |
| 	}
 | |
| 
 | |
| 	return
 | |
| }
 | |
| 
 | |
| // GetJenkinsMasterPodName returns Jenkins pod name for given CR
 | |
| func GetJenkinsMasterPodName(jenkins v1alpha2.Jenkins) string {
 | |
| 	return fmt.Sprintf("jenkins-%s", jenkins.Name)
 | |
| }
 | |
| 
 | |
| // NewJenkinsMasterPod builds Jenkins Master Kubernetes Pod resource
 | |
| func NewJenkinsMasterPod(objectMeta metav1.ObjectMeta, jenkins *v1alpha2.Jenkins) *corev1.Pod {
 | |
| 	serviceAccountName := objectMeta.Name
 | |
| 	objectMeta.Annotations = jenkins.Spec.Master.Annotations
 | |
| 	objectMeta.Name = GetJenkinsMasterPodName(*jenkins)
 | |
| 
 | |
| 	return &corev1.Pod{
 | |
| 		TypeMeta:   buildPodTypeMeta(),
 | |
| 		ObjectMeta: objectMeta,
 | |
| 		Spec: corev1.PodSpec{
 | |
| 			ServiceAccountName: serviceAccountName,
 | |
| 			RestartPolicy:      corev1.RestartPolicyNever,
 | |
| 			NodeSelector:       jenkins.Spec.Master.NodeSelector,
 | |
| 			Containers:         newContainers(jenkins),
 | |
| 			Volumes:            append(GetJenkinsMasterPodBaseVolumes(jenkins), jenkins.Spec.Master.Volumes...),
 | |
| 			SecurityContext:    jenkins.Spec.Master.SecurityContext,
 | |
| 			ImagePullSecrets: jenkins.Spec.Master.ImagePullSecrets,
 | |
| 		},
 | |
| 	}
 | |
| }
 |