Fix calculating hash for user/base configuration

This commit is contained in:
Tomasz Sęk 2019-01-11 07:56:03 +01:00
parent f8190d61ce
commit 516418f97f
No known key found for this signature in database
GPG Key ID: DC356D23F6A644D0
6 changed files with 56 additions and 72 deletions

View File

@ -328,7 +328,7 @@ deepcopy-gen: ## Generate deepcopy golang code
start-minikube: ## Start minikube start-minikube: ## Start minikube
@echo "+ $@" @echo "+ $@"
@minikube status && exit 0 || \ @minikube status && exit 0 || \
minikube start --kubernetes-version $(MINIKUBE_KUBERNETES_VERSION) --vm-driver=$(MINIKUBE_DRIVER) --memory 2048 minikube start --kubernetes-version $(MINIKUBE_KUBERNETES_VERSION) --vm-driver=$(MINIKUBE_DRIVER) --memory 4096
.PHONY: bump-version .PHONY: bump-version
BUMP := patch BUMP := patch

View File

@ -2,11 +2,8 @@ package base
import ( import (
"context" "context"
"crypto/sha256"
"encoding/base64"
"fmt" "fmt"
"reflect" "reflect"
"sort"
"time" "time"
virtuslabv1alpha1 "github.com/VirtusLab/jenkins-operator/pkg/apis/virtuslab/v1alpha1" virtuslabv1alpha1 "github.com/VirtusLab/jenkins-operator/pkg/apis/virtuslab/v1alpha1"
@ -35,7 +32,7 @@ const (
// ReconcileJenkinsBaseConfiguration defines values required for Jenkins base configuration // ReconcileJenkinsBaseConfiguration defines values required for Jenkins base configuration
type ReconcileJenkinsBaseConfiguration struct { type ReconcileJenkinsBaseConfiguration struct {
client client.Client k8sClient client.Client
scheme *runtime.Scheme scheme *runtime.Scheme
logger logr.Logger logger logr.Logger
jenkins *virtuslabv1alpha1.Jenkins jenkins *virtuslabv1alpha1.Jenkins
@ -46,12 +43,12 @@ type ReconcileJenkinsBaseConfiguration struct {
func New(client client.Client, scheme *runtime.Scheme, logger logr.Logger, func New(client client.Client, scheme *runtime.Scheme, logger logr.Logger,
jenkins *virtuslabv1alpha1.Jenkins, local, minikube bool) *ReconcileJenkinsBaseConfiguration { jenkins *virtuslabv1alpha1.Jenkins, local, minikube bool) *ReconcileJenkinsBaseConfiguration {
return &ReconcileJenkinsBaseConfiguration{ return &ReconcileJenkinsBaseConfiguration{
client: client, k8sClient: client,
scheme: scheme, scheme: scheme,
logger: logger, logger: logger,
jenkins: jenkins, jenkins: jenkins,
local: local, local: local,
minikube: minikube, minikube: minikube,
} }
} }
@ -128,7 +125,7 @@ func (r *ReconcileJenkinsBaseConfiguration) Reconcile() (*reconcile.Result, jenk
if err != nil { if err != nil {
return &reconcile.Result{}, nil, err return &reconcile.Result{}, nil, err
} }
if err := r.client.Delete(context.TODO(), currentJenkinsMasterPod); err != nil { if err := r.k8sClient.Delete(context.TODO(), currentJenkinsMasterPod); err != nil {
return &reconcile.Result{}, nil, err return &reconcile.Result{}, nil, err
} }
} }
@ -180,7 +177,7 @@ func isPluginInstalled(plugins *gojenkins.Plugins, requiredPlugin plugin.Plugin)
func (r *ReconcileJenkinsBaseConfiguration) createOperatorCredentialsSecret(meta metav1.ObjectMeta) error { func (r *ReconcileJenkinsBaseConfiguration) createOperatorCredentialsSecret(meta metav1.ObjectMeta) error {
found := &corev1.Secret{} found := &corev1.Secret{}
err := r.client.Get(context.TODO(), types.NamespacedName{Name: resources.GetOperatorCredentialsSecretName(r.jenkins), Namespace: r.jenkins.ObjectMeta.Namespace}, found) err := r.k8sClient.Get(context.TODO(), types.NamespacedName{Name: resources.GetOperatorCredentialsSecretName(r.jenkins), Namespace: r.jenkins.ObjectMeta.Namespace}, found)
if err != nil && apierrors.IsNotFound(err) { if err != nil && apierrors.IsNotFound(err) {
return r.createResource(resources.NewOperatorCredentialsSecret(meta, r.jenkins)) return r.createResource(resources.NewOperatorCredentialsSecret(meta, r.jenkins))
@ -222,9 +219,9 @@ func (r *ReconcileJenkinsBaseConfiguration) createBaseConfigurationConfigMap(met
func (r *ReconcileJenkinsBaseConfiguration) createUserConfigurationConfigMap(meta metav1.ObjectMeta) error { func (r *ReconcileJenkinsBaseConfiguration) createUserConfigurationConfigMap(meta metav1.ObjectMeta) error {
currentConfigMap := &corev1.ConfigMap{} currentConfigMap := &corev1.ConfigMap{}
err := r.client.Get(context.TODO(), types.NamespacedName{Name: resources.GetUserConfigurationConfigMapName(r.jenkins), Namespace: r.jenkins.Namespace}, currentConfigMap) err := r.k8sClient.Get(context.TODO(), types.NamespacedName{Name: resources.GetUserConfigurationConfigMapName(r.jenkins), Namespace: r.jenkins.Namespace}, currentConfigMap)
if err != nil && errors.IsNotFound(err) { if err != nil && errors.IsNotFound(err) {
return r.client.Create(context.TODO(), resources.NewUserConfigurationConfigMap(meta, r.jenkins)) return r.k8sClient.Create(context.TODO(), resources.NewUserConfigurationConfigMap(meta, r.jenkins))
} else if err != nil { } else if err != nil {
return err return err
} }
@ -267,7 +264,7 @@ func (r *ReconcileJenkinsBaseConfiguration) createService(meta metav1.ObjectMeta
func (r *ReconcileJenkinsBaseConfiguration) getJenkinsMasterPod(meta metav1.ObjectMeta) (*corev1.Pod, error) { func (r *ReconcileJenkinsBaseConfiguration) getJenkinsMasterPod(meta metav1.ObjectMeta) (*corev1.Pod, error) {
jenkinsMasterPod := resources.NewJenkinsMasterPod(meta, r.jenkins) jenkinsMasterPod := resources.NewJenkinsMasterPod(meta, r.jenkins)
currentJenkinsMasterPod := &corev1.Pod{} currentJenkinsMasterPod := &corev1.Pod{}
err := r.client.Get(context.TODO(), types.NamespacedName{Name: jenkinsMasterPod.Name, Namespace: jenkinsMasterPod.Namespace}, currentJenkinsMasterPod) err := r.k8sClient.Get(context.TODO(), types.NamespacedName{Name: jenkinsMasterPod.Name, Namespace: jenkinsMasterPod.Namespace}, currentJenkinsMasterPod)
if err != nil { if err != nil {
return nil, err return nil, err
} }
@ -324,7 +321,7 @@ func (r *ReconcileJenkinsBaseConfiguration) createJenkinsMasterPod(meta metav1.O
if currentJenkinsMasterPod != nil && recreatePod && currentJenkinsMasterPod.ObjectMeta.DeletionTimestamp == nil { if currentJenkinsMasterPod != nil && recreatePod && currentJenkinsMasterPod.ObjectMeta.DeletionTimestamp == nil {
r.logger.Info(fmt.Sprintf("Terminating Jenkins Master Pod %s/%s", currentJenkinsMasterPod.Namespace, currentJenkinsMasterPod.Name)) r.logger.Info(fmt.Sprintf("Terminating Jenkins Master Pod %s/%s", currentJenkinsMasterPod.Namespace, currentJenkinsMasterPod.Name))
if err := r.client.Delete(context.TODO(), currentJenkinsMasterPod); err != nil { if err := r.k8sClient.Delete(context.TODO(), currentJenkinsMasterPod); err != nil {
return nil, err return nil, err
} }
return &reconcile.Result{Requeue: true}, nil return &reconcile.Result{Requeue: true}, nil
@ -368,7 +365,7 @@ func (r *ReconcileJenkinsBaseConfiguration) getJenkinsClient(meta metav1.ObjectM
r.logger.V(log.VDebug).Info(fmt.Sprintf("Jenkins API URL %s", jenkinsURL)) r.logger.V(log.VDebug).Info(fmt.Sprintf("Jenkins API URL %s", jenkinsURL))
credentialsSecret := &corev1.Secret{} credentialsSecret := &corev1.Secret{}
err = r.client.Get(context.TODO(), types.NamespacedName{Name: resources.GetOperatorCredentialsSecretName(r.jenkins), Namespace: r.jenkins.ObjectMeta.Namespace}, credentialsSecret) err = r.k8sClient.Get(context.TODO(), types.NamespacedName{Name: resources.GetOperatorCredentialsSecretName(r.jenkins), Namespace: r.jenkins.ObjectMeta.Namespace}, credentialsSecret)
if err != nil { if err != nil {
return nil, err return nil, err
} }
@ -421,7 +418,7 @@ func (r *ReconcileJenkinsBaseConfiguration) getJenkinsClient(meta metav1.ObjectM
} }
func (r *ReconcileJenkinsBaseConfiguration) baseConfiguration(jenkinsClient jenkinsclient.Jenkins) (*reconcile.Result, error) { func (r *ReconcileJenkinsBaseConfiguration) baseConfiguration(jenkinsClient jenkinsclient.Jenkins) (*reconcile.Result, error) {
groovyClient := groovy.New(jenkinsClient, r.client, r.logger, fmt.Sprintf("%s-base-configuration", constants.OperatorName), resources.JenkinsBaseConfigurationVolumePath) groovyClient := groovy.New(jenkinsClient, r.k8sClient, r.logger, fmt.Sprintf("%s-base-configuration", constants.OperatorName), resources.JenkinsBaseConfigurationVolumePath)
err := groovyClient.ConfigureGroovyJob() err := groovyClient.ConfigureGroovyJob()
if err != nil { if err != nil {
@ -429,30 +426,16 @@ func (r *ReconcileJenkinsBaseConfiguration) baseConfiguration(jenkinsClient jenk
} }
configuration := &corev1.ConfigMap{} configuration := &corev1.ConfigMap{}
namespaceName := types.NamespacedName{Namespace: r.jenkins.Namespace, Name: resources.GetUserConfigurationConfigMapName(r.jenkins)} namespaceName := types.NamespacedName{Namespace: r.jenkins.Namespace, Name: resources.GetBaseConfigurationConfigMapName(r.jenkins)}
err = r.client.Get(context.TODO(), namespaceName, configuration) err = r.k8sClient.Get(context.TODO(), namespaceName, configuration)
if err != nil {
return &reconcile.Result{}, err
}
hash := sha256.New()
var keys []string
for key := range configuration.Data {
keys = append(keys, key)
}
sort.Strings(keys)
for _, key := range keys {
hash.Write([]byte(key))
hash.Write([]byte(configuration.Data[key]))
}
encodedHash := base64.URLEncoding.EncodeToString(hash.Sum(nil))
done, err := groovyClient.EnsureGroovyJob(encodedHash, r.jenkins)
if err != nil { if err != nil {
return &reconcile.Result{}, err return &reconcile.Result{}, err
} }
done, err := groovyClient.EnsureGroovyJob(configuration.Data, r.jenkins)
if err != nil {
return &reconcile.Result{}, err
}
if !done { if !done {
return &reconcile.Result{Requeue: true, RequeueAfter: time.Second * 10}, nil return &reconcile.Result{Requeue: true, RequeueAfter: time.Second * 10}, nil
} }

View File

@ -21,7 +21,7 @@ func (r *ReconcileJenkinsBaseConfiguration) createResource(obj metav1.Object) er
return err return err
} }
return r.client.Create(context.TODO(), runtimeObj) return r.k8sClient.Create(context.TODO(), runtimeObj)
} }
func (r *ReconcileJenkinsBaseConfiguration) updateResource(obj metav1.Object) error { func (r *ReconcileJenkinsBaseConfiguration) updateResource(obj metav1.Object) error {
@ -33,7 +33,7 @@ func (r *ReconcileJenkinsBaseConfiguration) updateResource(obj metav1.Object) er
// set Jenkins instance as the owner and controller, don't check error(can be already set) // set Jenkins instance as the owner and controller, don't check error(can be already set)
_ = controllerutil.SetControllerReference(r.jenkins, obj, r.scheme) _ = controllerutil.SetControllerReference(r.jenkins, obj, r.scheme)
return r.client.Update(context.TODO(), runtimeObj) return r.k8sClient.Update(context.TODO(), runtimeObj)
} }
func (r *ReconcileJenkinsBaseConfiguration) createOrUpdateResource(obj metav1.Object) error { func (r *ReconcileJenkinsBaseConfiguration) createOrUpdateResource(obj metav1.Object) error {
@ -45,7 +45,7 @@ func (r *ReconcileJenkinsBaseConfiguration) createOrUpdateResource(obj metav1.Ob
// set Jenkins instance as the owner and controller, don't check error(can be already set) // set Jenkins instance as the owner and controller, don't check error(can be already set)
_ = controllerutil.SetControllerReference(r.jenkins, obj, r.scheme) _ = controllerutil.SetControllerReference(r.jenkins, obj, r.scheme)
err := r.client.Create(context.TODO(), runtimeObj) err := r.k8sClient.Create(context.TODO(), runtimeObj)
if err != nil && errors.IsAlreadyExists(err) { if err != nil && errors.IsAlreadyExists(err) {
return r.updateResource(obj) return r.updateResource(obj)
} else if err != nil && !errors.IsAlreadyExists(err) { } else if err != nil && !errors.IsAlreadyExists(err) {

View File

@ -1,6 +1,7 @@
package user package user
import ( import (
"context"
"fmt" "fmt"
"time" "time"
@ -8,12 +9,13 @@ import (
jenkinsclient "github.com/VirtusLab/jenkins-operator/pkg/controller/jenkins/client" jenkinsclient "github.com/VirtusLab/jenkins-operator/pkg/controller/jenkins/client"
"github.com/VirtusLab/jenkins-operator/pkg/controller/jenkins/configuration/base/resources" "github.com/VirtusLab/jenkins-operator/pkg/controller/jenkins/configuration/base/resources"
"github.com/VirtusLab/jenkins-operator/pkg/controller/jenkins/configuration/user/seedjobs" "github.com/VirtusLab/jenkins-operator/pkg/controller/jenkins/configuration/user/seedjobs"
"github.com/VirtusLab/jenkins-operator/pkg/controller/jenkins/configuration/user/theme"
"github.com/VirtusLab/jenkins-operator/pkg/controller/jenkins/constants" "github.com/VirtusLab/jenkins-operator/pkg/controller/jenkins/constants"
"github.com/VirtusLab/jenkins-operator/pkg/controller/jenkins/groovy" "github.com/VirtusLab/jenkins-operator/pkg/controller/jenkins/groovy"
"github.com/VirtusLab/jenkins-operator/pkg/controller/jenkins/jobs" "github.com/VirtusLab/jenkins-operator/pkg/controller/jenkins/jobs"
"github.com/go-logr/logr" "github.com/go-logr/logr"
corev1 "k8s.io/api/core/v1"
"k8s.io/apimachinery/pkg/types"
k8s "sigs.k8s.io/controller-runtime/pkg/client" k8s "sigs.k8s.io/controller-runtime/pkg/client"
"sigs.k8s.io/controller-runtime/pkg/reconcile" "sigs.k8s.io/controller-runtime/pkg/reconcile"
) )
@ -78,7 +80,14 @@ func (r *ReconcileUserConfiguration) userConfiguration(jenkinsClient jenkinsclie
return &reconcile.Result{}, err return &reconcile.Result{}, err
} }
done, err := groovyClient.EnsureGroovyJob(theme.SetThemeGroovyScript, r.jenkins) configuration := &corev1.ConfigMap{}
namespaceName := types.NamespacedName{Namespace: r.jenkins.Namespace, Name: resources.GetUserConfigurationConfigMapName(r.jenkins)}
err = r.k8sClient.Get(context.TODO(), namespaceName, configuration)
if err != nil {
return &reconcile.Result{}, err
}
done, err := groovyClient.EnsureGroovyJob(configuration.Data, r.jenkins)
if err != nil { if err != nil {
return &reconcile.Result{}, err return &reconcile.Result{}, err
} }

View File

@ -1,25 +0,0 @@
package theme
// SetThemeGroovyScript it's a groovy script which set custom jenkins theme
// TODO move to base configuration
var SetThemeGroovyScript = `
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<ThemeElement> 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()
`

View File

@ -1,7 +1,10 @@
package groovy package groovy
import ( import (
"crypto/sha256"
"encoding/base64"
"fmt" "fmt"
"sort"
virtuslabv1alpha1 "github.com/VirtusLab/jenkins-operator/pkg/apis/virtuslab/v1alpha1" virtuslabv1alpha1 "github.com/VirtusLab/jenkins-operator/pkg/apis/virtuslab/v1alpha1"
jenkinsclient "github.com/VirtusLab/jenkins-operator/pkg/controller/jenkins/client" jenkinsclient "github.com/VirtusLab/jenkins-operator/pkg/controller/jenkins/client"
@ -41,17 +44,31 @@ func (g *Groovy) ConfigureGroovyJob() error {
} }
// EnsureGroovyJob executes groovy script and verifies jenkins job status according to reconciliation loop lifecycle // EnsureGroovyJob executes groovy script and verifies jenkins job status according to reconciliation loop lifecycle
// see https://wiki.jenkins.io/display/JENKINS/Jenkins+Script+Console func (g *Groovy) EnsureGroovyJob(secretOrConfigMapData map[string]string, jenkins *virtuslabv1alpha1.Jenkins) (bool, error) {
func (g *Groovy) EnsureGroovyJob(hash string, jenkins *virtuslabv1alpha1.Jenkins) (bool, error) {
jobsClient := jobs.New(g.jenkinsClient, g.k8sClient, g.logger) jobsClient := jobs.New(g.jenkinsClient, g.k8sClient, g.logger)
done, err := jobsClient.EnsureBuildJob(g.jobName, hash, map[string]string{}, jenkins, true) done, err := jobsClient.EnsureBuildJob(g.jobName, g.calculateHash(secretOrConfigMapData), map[string]string{}, jenkins, true)
if err != nil { if err != nil {
return false, err return false, err
} }
return done, nil return done, nil
} }
func (g *Groovy) calculateHash(secretOrConfigMapData map[string]string) string {
hash := sha256.New()
var keys []string
for key := range secretOrConfigMapData {
keys = append(keys, key)
}
sort.Strings(keys)
for _, key := range keys {
hash.Write([]byte(key))
hash.Write([]byte(secretOrConfigMapData[key]))
}
return base64.URLEncoding.EncodeToString(hash.Sum(nil))
}
const configurationJobXMLFmt = `<?xml version='1.1' encoding='UTF-8'?> const configurationJobXMLFmt = `<?xml version='1.1' encoding='UTF-8'?>
<flow-definition plugin="workflow-job@2.25"> <flow-definition plugin="workflow-job@2.25">
<actions/> <actions/>