From d0b02de4292ecc0130c8c761275c4ff7735a5d6c Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Tomasz=20S=C4=99k?= Date: Sun, 30 Jun 2019 23:11:45 +0200 Subject: [PATCH] #28 Mount secrets for groovy and CasC customization --- .../jenkins/configuration/base/reconcile.go | 73 ++++++---- .../configuration/base/resources/pod.go | 135 +++++++++--------- .../configuration/base/resources/service.go | 8 -- .../resources/user_configuration_configmap.go | 58 -------- .../resources/user_configuration_secret.go | 33 ----- 5 files changed, 112 insertions(+), 195 deletions(-) delete mode 100644 pkg/controller/jenkins/configuration/base/resources/user_configuration_configmap.go delete mode 100644 pkg/controller/jenkins/configuration/base/resources/user_configuration_secret.go diff --git a/pkg/controller/jenkins/configuration/base/reconcile.go b/pkg/controller/jenkins/configuration/base/reconcile.go index 5e0ec682..713ebada 100644 --- a/pkg/controller/jenkins/configuration/base/reconcile.go +++ b/pkg/controller/jenkins/configuration/base/reconcile.go @@ -13,7 +13,6 @@ import ( jenkinsclient "github.com/jenkinsci/kubernetes-operator/pkg/controller/jenkins/client" "github.com/jenkinsci/kubernetes-operator/pkg/controller/jenkins/configuration/backuprestore" "github.com/jenkinsci/kubernetes-operator/pkg/controller/jenkins/configuration/base/resources" - "github.com/jenkinsci/kubernetes-operator/pkg/controller/jenkins/constants" "github.com/jenkinsci/kubernetes-operator/pkg/controller/jenkins/groovy" "github.com/jenkinsci/kubernetes-operator/pkg/controller/jenkins/plugins" "github.com/jenkinsci/kubernetes-operator/pkg/log" @@ -149,15 +148,15 @@ func (r *ReconcileJenkinsBaseConfiguration) ensureResourcesRequiredForJenkinsPod } r.logger.V(log.VDebug).Info("Base configuration config map is present") - if err := r.createUserConfigurationConfigMap(metaObject); err != nil { + if err := r.addLabelForWatchesResources(r.jenkins.Spec.GroovyScripts.Customization); err != nil { return err } - r.logger.V(log.VDebug).Info("User configuration config map is present") + r.logger.V(log.VDebug).Info("GroovyScripts Secret and ConfigMap added watched labels") - if err := r.createUserConfigurationSecret(metaObject); err != nil { + if err := r.addLabelForWatchesResources(r.jenkins.Spec.ConfigurationAsCode.Customization); err != nil { return err } - r.logger.V(log.VDebug).Info("User configuration secret is present") + r.logger.V(log.VDebug).Info("ConfigurationAsCode Secret and ConfigMap added watched labels") if err := r.createRBAC(metaObject); err != nil { return err @@ -289,33 +288,49 @@ func (r *ReconcileJenkinsBaseConfiguration) createBaseConfigurationConfigMap(met return stackerr.WithStack(r.createOrUpdateResource(configMap)) } -func (r *ReconcileJenkinsBaseConfiguration) createUserConfigurationConfigMap(meta metav1.ObjectMeta) error { - currentConfigMap := &corev1.ConfigMap{} - err := r.k8sClient.Get(context.TODO(), types.NamespacedName{Name: resources.GetUserConfigurationConfigMapNameFromJenkins(r.jenkins), Namespace: r.jenkins.Namespace}, currentConfigMap) - if err != nil && errors.IsNotFound(err) { - return stackerr.WithStack(r.k8sClient.Create(context.TODO(), resources.NewUserConfigurationConfigMap(r.jenkins))) - } else if err != nil { - return stackerr.WithStack(err) - } - if !resources.VerifyIfLabelsAreSet(currentConfigMap, resources.BuildLabelsForWatchedResources(*r.jenkins)) { - currentConfigMap.ObjectMeta.Labels = resources.BuildLabelsForWatchedResources(*r.jenkins) - return stackerr.WithStack(r.k8sClient.Update(context.TODO(), currentConfigMap)) +func (r *ReconcileJenkinsBaseConfiguration) addLabelForWatchesResources(customization v1alpha2.Customization) error { + labelsForWatchedResources := resources.BuildLabelsForWatchedResources(*r.jenkins) + + if len(customization.Secret.Name) > 0 { + secret := &corev1.Secret{} + err := r.k8sClient.Get(context.TODO(), types.NamespacedName{Name: customization.Secret.Name, Namespace: r.jenkins.Namespace}, secret) + if err != nil { + return stackerr.WithStack(err) + } + + if !resources.VerifyIfLabelsAreSet(secret, labelsForWatchedResources) { + if len(secret.ObjectMeta.Labels) == 0 { + secret.ObjectMeta.Labels = map[string]string{} + } + for key, value := range labelsForWatchedResources { + secret.ObjectMeta.Labels[key] = value + } + + if err = r.k8sClient.Update(context.TODO(), secret); err != nil { + return stackerr.WithStack(r.k8sClient.Update(context.TODO(), secret)) + } + } } - return nil -} + for _, configMapRef := range customization.Configurations { + configMap := &corev1.ConfigMap{} + err := r.k8sClient.Get(context.TODO(), types.NamespacedName{Name: configMapRef.Name, Namespace: r.jenkins.Namespace}, configMap) + if err != nil { + return stackerr.WithStack(err) + } -func (r *ReconcileJenkinsBaseConfiguration) createUserConfigurationSecret(meta metav1.ObjectMeta) error { - currentSecret := &corev1.Secret{} - err := r.k8sClient.Get(context.TODO(), types.NamespacedName{Name: resources.GetUserConfigurationSecretNameFromJenkins(r.jenkins), Namespace: r.jenkins.Namespace}, currentSecret) - if err != nil && errors.IsNotFound(err) { - return stackerr.WithStack(r.k8sClient.Create(context.TODO(), resources.NewUserConfigurationSecret(r.jenkins))) - } else if err != nil { - return stackerr.WithStack(err) - } - if !resources.VerifyIfLabelsAreSet(currentSecret, resources.BuildLabelsForWatchedResources(*r.jenkins)) { - currentSecret.ObjectMeta.Labels = resources.BuildLabelsForWatchedResources(*r.jenkins) - return stackerr.WithStack(r.k8sClient.Update(context.TODO(), currentSecret)) + if !resources.VerifyIfLabelsAreSet(configMap, labelsForWatchedResources) { + if len(configMap.ObjectMeta.Labels) == 0 { + configMap.ObjectMeta.Labels = map[string]string{} + } + for key, value := range labelsForWatchedResources { + configMap.ObjectMeta.Labels[key] = value + } + + if err = r.k8sClient.Update(context.TODO(), configMap); err != nil { + return stackerr.WithStack(r.k8sClient.Update(context.TODO(), configMap)) + } + } } return nil diff --git a/pkg/controller/jenkins/configuration/base/resources/pod.go b/pkg/controller/jenkins/configuration/base/resources/pod.go index 6c9b35d3..1a40a4b9 100644 --- a/pkg/controller/jenkins/configuration/base/resources/pod.go +++ b/pkg/controller/jenkins/configuration/base/resources/pod.go @@ -30,19 +30,12 @@ const ( jenkinsInitConfigurationVolumeName = "init-configuration" jenkinsInitConfigurationVolumePath = jenkinsPath + "/init-configuration" - jenkinsBaseConfigurationVolumeName = "base-configuration" - // JenkinsBaseConfigurationVolumePath is a path where are groovy scripts used to configure Jenkins - // this scripts are provided by jenkins-operator - JenkinsBaseConfigurationVolumePath = jenkinsPath + "/base-configuration" - - jenkinsUserConfigurationVolumeName = "user-configuration" - // JenkinsUserConfigurationVolumePath is a path where are groovy scripts and CasC configs used to configure Jenkins - // this script is provided by user - JenkinsUserConfigurationVolumePath = jenkinsPath + "/user-configuration" - - userConfigurationSecretVolumeName = "user-configuration-secrets" - // UserConfigurationSecretVolumePath is a path where are secrets used for groovy scripts and CasC configs - UserConfigurationSecretVolumePath = jenkinsPath + "/user-configuration-secrets" + // 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" @@ -68,8 +61,8 @@ func GetJenkinsMasterContainerBaseCommand() []string { } // GetJenkinsMasterContainerBaseEnvs returns Jenkins master pod envs required by operator -func GetJenkinsMasterContainerBaseEnvs() []corev1.EnvVar { - return []corev1.EnvVar{ +func GetJenkinsMasterContainerBaseEnvs(jenkins *v1alpha2.Jenkins) []corev1.EnvVar { + envVars := []corev1.EnvVar{ { Name: "JENKINS_HOME", Value: jenkinsHomePath, @@ -78,11 +71,16 @@ func GetJenkinsMasterContainerBaseEnvs() []corev1.EnvVar { 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, - }, } + + 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 @@ -90,7 +88,7 @@ func GetJenkinsMasterPodBaseVolumes(jenkins *v1alpha2.Jenkins) []corev1.Volume { configMapVolumeSourceDefaultMode := corev1.ConfigMapVolumeSourceDefaultMode secretVolumeSourceDefaultMode := corev1.SecretVolumeSourceDefaultMode var scriptsVolumeDefaultMode int32 = 0777 - return []corev1.Volume{ + volumes := []corev1.Volume{ { Name: JenkinsHomeVolumeName, VolumeSource: corev1.VolumeSource{ @@ -119,28 +117,6 @@ func GetJenkinsMasterPodBaseVolumes(jenkins *v1alpha2.Jenkins) []corev1.Volume { }, }, }, - { - Name: jenkinsBaseConfigurationVolumeName, - VolumeSource: corev1.VolumeSource{ - ConfigMap: &corev1.ConfigMapVolumeSource{ - DefaultMode: &configMapVolumeSourceDefaultMode, - LocalObjectReference: corev1.LocalObjectReference{ - Name: GetBaseConfigurationConfigMapName(jenkins), - }, - }, - }, - }, - { - Name: jenkinsUserConfigurationVolumeName, - VolumeSource: corev1.VolumeSource{ - ConfigMap: &corev1.ConfigMapVolumeSource{ - DefaultMode: &configMapVolumeSourceDefaultMode, - LocalObjectReference: corev1.LocalObjectReference{ - Name: GetUserConfigurationConfigMapNameFromJenkins(jenkins), - }, - }, - }, - }, { Name: jenkinsOperatorCredentialsVolumeName, VolumeSource: corev1.VolumeSource{ @@ -150,21 +126,45 @@ func GetJenkinsMasterPodBaseVolumes(jenkins *v1alpha2.Jenkins) []corev1.Volume { }, }, }, - { - Name: userConfigurationSecretVolumeName, + } + + 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: GetUserConfigurationSecretNameFromJenkins(jenkins), + 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() []corev1.VolumeMount { - return []corev1.VolumeMount{ +func GetJenkinsMasterContainerBaseVolumeMounts(jenkins *v1alpha2.Jenkins) []corev1.VolumeMount { + volumeMounts := []corev1.VolumeMount{ { Name: JenkinsHomeVolumeName, MountPath: jenkinsHomePath, @@ -180,33 +180,35 @@ func GetJenkinsMasterContainerBaseVolumeMounts() []corev1.VolumeMount { MountPath: jenkinsInitConfigurationVolumePath, ReadOnly: true, }, - { - Name: jenkinsBaseConfigurationVolumeName, - MountPath: JenkinsBaseConfigurationVolumePath, - ReadOnly: true, - }, - { - Name: jenkinsUserConfigurationVolumeName, - MountPath: JenkinsUserConfigurationVolumePath, - ReadOnly: true, - }, { Name: jenkinsOperatorCredentialsVolumeName, MountPath: jenkinsOperatorCredentialsVolumePath, ReadOnly: true, }, - { - Name: userConfigurationSecretVolumeName, - MountPath: UserConfigurationSecretVolumePath, - 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() + envs := GetJenkinsMasterContainerBaseEnvs(jenkins) envs = append(envs, jenkinsContainer.Env...) return corev1.Container{ @@ -230,7 +232,7 @@ func NewJenkinsMasterContainer(jenkins *v1alpha2.Jenkins) corev1.Container { }, Env: envs, Resources: jenkinsContainer.Resources, - VolumeMounts: append(GetJenkinsMasterContainerBaseVolumeMounts(), jenkinsContainer.VolumeMounts...), + VolumeMounts: append(GetJenkinsMasterContainerBaseVolumeMounts(jenkins), jenkinsContainer.VolumeMounts...), } } @@ -272,7 +274,6 @@ func GetJenkinsMasterPodName(jenkins v1alpha2.Jenkins) string { // 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) @@ -283,10 +284,10 @@ func NewJenkinsMasterPod(objectMeta metav1.ObjectMeta, jenkins *v1alpha2.Jenkins Spec: corev1.PodSpec{ ServiceAccountName: serviceAccountName, RestartPolicy: corev1.RestartPolicyNever, - SecurityContext: jenkins.Spec.Master.SecurityContext, NodeSelector: jenkins.Spec.Master.NodeSelector, Containers: newContainers(jenkins), Volumes: append(GetJenkinsMasterPodBaseVolumes(jenkins), jenkins.Spec.Master.Volumes...), + SecurityContext: jenkins.Spec.Master.SecurityContext, }, } } diff --git a/pkg/controller/jenkins/configuration/base/resources/service.go b/pkg/controller/jenkins/configuration/base/resources/service.go index db3b1bba..b9e2d96c 100644 --- a/pkg/controller/jenkins/configuration/base/resources/service.go +++ b/pkg/controller/jenkins/configuration/base/resources/service.go @@ -7,16 +7,8 @@ import ( "github.com/jenkinsci/kubernetes-operator/pkg/controller/jenkins/constants" corev1 "k8s.io/api/core/v1" - metav1 "k8s.io/apimachinery/pkg/apis/meta/v1" ) -func buildServiceTypeMeta() metav1.TypeMeta { - return metav1.TypeMeta{ - Kind: "Service", - APIVersion: "v1", - } -} - // UpdateService returns new service with override fields from config func UpdateService(actual corev1.Service, config v1alpha2.Service) corev1.Service { actual.ObjectMeta.Annotations = config.Annotations diff --git a/pkg/controller/jenkins/configuration/base/resources/user_configuration_configmap.go b/pkg/controller/jenkins/configuration/base/resources/user_configuration_configmap.go deleted file mode 100644 index 85ad304a..00000000 --- a/pkg/controller/jenkins/configuration/base/resources/user_configuration_configmap.go +++ /dev/null @@ -1,58 +0,0 @@ -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 configureTheme = ` -import jenkins.* -import jenkins.model.* -import hudson.* -import hudson.model.* -import org.jenkinsci.plugins.simpletheme.ThemeElement -import org.jenkinsci.plugins.simpletheme.CssTextThemeElement -import org.jenkinsci.plugins.simpletheme.CssUrlThemeElement - -Jenkins jenkins = Jenkins.getInstance() - -def decorator = Jenkins.instance.getDescriptorByType(org.codefirst.SimpleThemeDecorator.class) - -List configElements = new ArrayList<>(); -configElements.add(new CssTextThemeElement("DEFAULT")); -configElements.add(new CssUrlThemeElement("https://cdn.rawgit.com/afonsof/jenkins-material-theme/gh-pages/dist/material-light-green.css")); -decorator.setElements(configElements); -decorator.save(); - -jenkins.save() -` - -// GetUserConfigurationConfigMapNameFromJenkins returns name of Kubernetes config map used to user configuration -func GetUserConfigurationConfigMapNameFromJenkins(jenkins *v1alpha2.Jenkins) string { - return fmt.Sprintf("%s-user-configuration-%s", constants.OperatorName, jenkins.ObjectMeta.Name) -} - -// GetUserConfigurationConfigMapName returns name of Kubernetes config map used to user configuration -func GetUserConfigurationConfigMapName(jenkinsCRName string) string { - return fmt.Sprintf("%s-user-configuration-%s", constants.OperatorName, jenkinsCRName) -} - -// NewUserConfigurationConfigMap builds Kubernetes config map used to user configuration -func NewUserConfigurationConfigMap(jenkins *v1alpha2.Jenkins) *corev1.ConfigMap { - return &corev1.ConfigMap{ - TypeMeta: buildConfigMapTypeMeta(), - ObjectMeta: metav1.ObjectMeta{ - Name: GetUserConfigurationConfigMapNameFromJenkins(jenkins), - Namespace: jenkins.ObjectMeta.Namespace, - Labels: BuildLabelsForWatchedResources(*jenkins), - }, - Data: map[string]string{ - "1-configure-theme.groovy": configureTheme, - }, - } -} diff --git a/pkg/controller/jenkins/configuration/base/resources/user_configuration_secret.go b/pkg/controller/jenkins/configuration/base/resources/user_configuration_secret.go deleted file mode 100644 index 073c0efd..00000000 --- a/pkg/controller/jenkins/configuration/base/resources/user_configuration_secret.go +++ /dev/null @@ -1,33 +0,0 @@ -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" -) - -// GetUserConfigurationSecretNameFromJenkins returns name of Kubernetes secret used to store jenkins operator credentials -func GetUserConfigurationSecretNameFromJenkins(jenkins *v1alpha2.Jenkins) string { - return fmt.Sprintf("%s-user-configuration-%s", constants.OperatorName, jenkins.Name) -} - -// GetUserConfigurationSecretName returns name of Kubernetes secret used to store jenkins operator credentials -func GetUserConfigurationSecretName(jenkinsCRName string) string { - return fmt.Sprintf("%s-user-configuration-%s", constants.OperatorName, jenkinsCRName) -} - -// NewUserConfigurationSecret builds the Kubernetes secret resource which is used to store user sensitive data for Jenkins configuration -func NewUserConfigurationSecret(jenkins *v1alpha2.Jenkins) *corev1.Secret { - return &corev1.Secret{ - TypeMeta: buildServiceTypeMeta(), - ObjectMeta: metav1.ObjectMeta{ - Name: GetUserConfigurationSecretNameFromJenkins(jenkins), - Namespace: jenkins.ObjectMeta.Namespace, - Labels: BuildLabelsForWatchedResources(*jenkins), - }, - } -}