From 87fcc5f8a5a17fa22058bf6e7a9d0cdba93c8677 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Tomasz=20S=C4=99k?= Date: Sun, 30 Jun 2019 23:16:03 +0200 Subject: [PATCH] #28 Change groovy client implementation --- pkg/controller/jenkins/groovy/groovy.go | 282 +++++++---- pkg/controller/jenkins/groovy/groovy_test.go | 507 +++++++++++++++++++ 2 files changed, 690 insertions(+), 99 deletions(-) create mode 100644 pkg/controller/jenkins/groovy/groovy_test.go diff --git a/pkg/controller/jenkins/groovy/groovy.go b/pkg/controller/jenkins/groovy/groovy.go index da919c2a..675392ed 100644 --- a/pkg/controller/jenkins/groovy/groovy.go +++ b/pkg/controller/jenkins/groovy/groovy.go @@ -1,6 +1,7 @@ package groovy import ( + "context" "crypto/sha256" "encoding/base64" "fmt" @@ -9,144 +10,227 @@ import ( "github.com/jenkinsci/kubernetes-operator/pkg/apis/jenkins/v1alpha2" jenkinsclient "github.com/jenkinsci/kubernetes-operator/pkg/controller/jenkins/client" - "github.com/jenkinsci/kubernetes-operator/pkg/controller/jenkins/jobs" + "github.com/jenkinsci/kubernetes-operator/pkg/log" "github.com/go-logr/logr" + "github.com/pkg/errors" + corev1 "k8s.io/api/core/v1" + "k8s.io/apimachinery/pkg/types" k8s "sigs.k8s.io/controller-runtime/pkg/client" ) -const ( - jobHashParameterName = "hash" -) - -// Groovy defines API for groovy scripts execution via jenkins job +// Groovy defines API for groovy secrets execution via jenkins job type Groovy struct { - jenkinsClient jenkinsclient.Jenkins - k8sClient k8s.Client - logger logr.Logger - jobName string - scriptsPath string + k8sClient k8s.Client + logger logr.Logger + jenkins *v1alpha2.Jenkins + jenkinsClient jenkinsclient.Jenkins + configurationType string + customization v1alpha2.Customization } // New creates new instance of Groovy -func New(jenkinsClient jenkinsclient.Jenkins, k8sClient k8s.Client, logger logr.Logger, jobName, scriptsPath string) *Groovy { +func New(jenkinsClient jenkinsclient.Jenkins, k8sClient k8s.Client, logger logr.Logger, jenkins *v1alpha2.Jenkins, configurationType string, customization v1alpha2.Customization) *Groovy { return &Groovy{ - jenkinsClient: jenkinsClient, - k8sClient: k8sClient, - logger: logger, - jobName: jobName, - scriptsPath: scriptsPath, + jenkinsClient: jenkinsClient, + k8sClient: k8sClient, + logger: logger, + jenkins: jenkins, + configurationType: configurationType, + customization: customization, } } -// ConfigureJob configures jenkins job for executing groovy scripts -func (g *Groovy) ConfigureJob() error { - _, created, err := g.jenkinsClient.CreateOrUpdateJob(fmt.Sprintf(configurationJobXMLFmt, g.scriptsPath), g.jobName) +// EnsureSingle runs single groovy script +func (g *Groovy) EnsureSingle(source, name, hash, groovyScript string) (requeue bool, err error) { + if g.isGroovyScriptAlreadyApplied(source, name, hash) { + return false, nil + } + + logs, err := g.jenkinsClient.ExecuteScript(groovyScript) if err != nil { - return err + if _, ok := err.(*jenkinsclient.GroovyScriptExecutionFailed); ok { + g.logger.V(log.VWarn).Info(fmt.Sprintf("%s Source '%s' Name '%s' groovy script execution failed, logs :\n%s", g.configurationType, source, name, logs)) + } + return true, err } - if created { - g.logger.Info(fmt.Sprintf("'%s' job has been created", g.jobName)) - } - return nil + + g.jenkins.Status.AppliedGroovyScripts = append(g.jenkins.Status.AppliedGroovyScripts, v1alpha2.AppliedGroovyScript{ + ConfigurationType: g.configurationType, + Source: source, + Name: name, + Hash: hash, + }) + return true, g.k8sClient.Update(context.TODO(), g.jenkins) } -// Ensure executes groovy script and verifies jenkins job status according to reconciliation loop lifecycle -func (g *Groovy) Ensure(secretOrConfigMapData map[string]string, jenkins *v1alpha2.Jenkins) (bool, error) { - jobsClient := jobs.New(g.jenkinsClient, g.k8sClient, g.logger) +// WaitForSecretSynchronization runs groovy script which waits to synchronize secrets in pod by k8s +func (g *Groovy) WaitForSecretSynchronization(secretsPath string) (requeue bool, err error) { + if len(g.customization.Secret.Name) == 0 { + return false, nil + } - hash := g.calculateHash(secretOrConfigMapData) - done, err := jobsClient.EnsureBuildJob(g.jobName, hash, map[string]string{jobHashParameterName: hash}, jenkins, true) + secret := &corev1.Secret{} + err = g.k8sClient.Get(context.TODO(), types.NamespacedName{Name: g.customization.Secret.Name, Namespace: g.jenkins.ObjectMeta.Namespace}, secret) if err != nil { - return false, err + return true, errors.WithStack(err) } - return done, nil + + toCalculate := map[string]string{} + for secretKey, secretValue := range secret.Data { + toCalculate[secretKey] = string(secretValue) + } + hash := g.calculateHash(toCalculate) + + name := "synchronizing-secret.groovy" + if g.isGroovyScriptAlreadyApplied(g.customization.Secret.Name, name, hash) { + return false, nil + } + + g.logger.Info(fmt.Sprintf("%s Secret '%s' running synchronization", g.configurationType, secret.Name)) + return g.EnsureSingle(g.customization.Secret.Name, name, hash, fmt.Sprintf(synchronizeSecretsGroovyScriptFmt, secretsPath, hash)) } -func (g *Groovy) calculateHash(secretOrConfigMapData map[string]string) string { +// Ensure runs all groovy scripts configured in customization structure +func (g *Groovy) Ensure(filter func(name string) bool, updateGroovyScript func(groovyScript string) string) (requeue bool, err error) { + secret := &corev1.Secret{} + if len(g.customization.Secret.Name) > 0 { + err := g.k8sClient.Get(context.TODO(), types.NamespacedName{Name: g.customization.Secret.Name, Namespace: g.jenkins.ObjectMeta.Namespace}, secret) + if err != nil { + return true, err + } + } + + for _, configMapRef := range g.customization.Configurations { + configMap := &corev1.ConfigMap{} + err := g.k8sClient.Get(context.TODO(), types.NamespacedName{Name: configMapRef.Name, Namespace: g.jenkins.ObjectMeta.Namespace}, configMap) + if err != nil { + return true, errors.WithStack(err) + } + + var names []string + for name := range configMap.Data { + names = append(names, name) + } + sort.Strings(names) + + for _, name := range names { + groovyScript := updateGroovyScript(configMap.Data[name]) + if !filter(name) { + g.logger.V(log.VDebug).Info(fmt.Sprintf("Skipping %s ConfigMap '%s' name '%s'", g.configurationType, configMap.Name, name)) + continue + } + + hash := g.calculateCustomizationHash(*secret, name, groovyScript) + if g.isGroovyScriptAlreadyApplied(configMap.Name, name, hash) { + continue + } + + g.logger.Info(fmt.Sprintf("%s ConfigMap '%s' name '%s' running groovy script", g.configurationType, configMap.Name, name)) + requeue, err := g.EnsureSingle(configMap.Name, name, hash, groovyScript) + if err != nil || requeue { + return requeue, err + } + } + } + + return false, nil +} + +func (g *Groovy) calculateCustomizationHash(secret corev1.Secret, key, groovyScript string) string { + toCalculate := map[string]string{} + for secretKey, secretValue := range secret.Data { + toCalculate[secretKey] = string(secretValue) + } + toCalculate[key] = groovyScript + return g.calculateHash(toCalculate) +} + +func (g *Groovy) isGroovyScriptAlreadyApplied(source, name, hash string) bool { + for _, appliedGroovyScript := range g.jenkins.Status.AppliedGroovyScripts { + if appliedGroovyScript.ConfigurationType == g.configurationType && appliedGroovyScript.Hash == hash && + appliedGroovyScript.Name == name && appliedGroovyScript.Source == source { + return true + } + } + + return false +} + +func (g *Groovy) calculateHash(data map[string]string) string { hash := sha256.New() var keys []string - for key := range secretOrConfigMapData { + for key := range data { keys = append(keys, key) } sort.Strings(keys) for _, key := range keys { - if strings.HasSuffix(key, ".groovy") { - hash.Write([]byte(key)) - hash.Write([]byte(secretOrConfigMapData[key])) - } + hash.Write([]byte(key)) + hash.Write([]byte(data[key])) } return base64.StdEncoding.EncodeToString(hash.Sum(nil)) } -const configurationJobXMLFmt = ` - - - - false - - - - - - ` + jobHashParameterName + ` - - - false - - - - - - - false - - - false - +} ` diff --git a/pkg/controller/jenkins/groovy/groovy_test.go b/pkg/controller/jenkins/groovy/groovy_test.go new file mode 100644 index 00000000..14286764 --- /dev/null +++ b/pkg/controller/jenkins/groovy/groovy_test.go @@ -0,0 +1,507 @@ +package groovy + +import ( + "context" + "fmt" + "strings" + "testing" + + "github.com/jenkinsci/kubernetes-operator/pkg/apis/jenkins/v1alpha2" + jenkinsclient "github.com/jenkinsci/kubernetes-operator/pkg/controller/jenkins/client" + + "github.com/golang/mock/gomock" + "github.com/jenkinsci/kubernetes-operator/pkg/log" + "github.com/stretchr/testify/assert" + "github.com/stretchr/testify/require" + corev1 "k8s.io/api/core/v1" + metav1 "k8s.io/apimachinery/pkg/apis/meta/v1" + "k8s.io/apimachinery/pkg/types" + "k8s.io/client-go/kubernetes/scheme" + "sigs.k8s.io/controller-runtime/pkg/client/fake" +) + +func TestGroovy_EnsureSingle(t *testing.T) { + log.SetupLogger(true) + configurationType := "test-conf-type" + emptyCustomization := v1alpha2.Customization{} + hash := "hash" + groovyScript := "groovy-script" + groovyScriptName := "groovy-script-name" + source := "source" + ctx := context.TODO() + jenkinsName := "jenkins" + namespace := "default" + + t.Run("execute script and save status", func(t *testing.T) { + // given + jenkins := &v1alpha2.Jenkins{ + ObjectMeta: metav1.ObjectMeta{ + Name: jenkinsName, + Namespace: namespace, + }, + } + err := v1alpha2.SchemeBuilder.AddToScheme(scheme.Scheme) + require.NoError(t, err) + fakeClient := fake.NewFakeClient() + err = fakeClient.Create(ctx, jenkins) + require.NoError(t, err) + + ctrl := gomock.NewController(t) + defer ctrl.Finish() + jenkinsClient := jenkinsclient.NewMockJenkins(ctrl) + jenkinsClient.EXPECT().ExecuteScript(groovyScript).Return("logs", nil) + + groovyClient := New(jenkinsClient, fakeClient, log.Log, jenkins, configurationType, emptyCustomization) + + // when + requeue, err := groovyClient.EnsureSingle(source, groovyScriptName, hash, groovyScript) + + // then + require.NoError(t, err) + assert.True(t, requeue) + + err = fakeClient.Get(ctx, types.NamespacedName{Name: jenkins.Name, Namespace: jenkins.Namespace}, jenkins) + require.NoError(t, err) + assert.Equal(t, 1, len(jenkins.Status.AppliedGroovyScripts)) + assert.Equal(t, configurationType, jenkins.Status.AppliedGroovyScripts[0].ConfigurationType) + assert.Equal(t, hash, jenkins.Status.AppliedGroovyScripts[0].Hash) + assert.Equal(t, source, jenkins.Status.AppliedGroovyScripts[0].Source) + assert.Equal(t, groovyScriptName, jenkins.Status.AppliedGroovyScripts[0].Name) + }) + t.Run("no execute script", func(t *testing.T) { + // given + jenkins := &v1alpha2.Jenkins{ + ObjectMeta: metav1.ObjectMeta{ + Name: jenkinsName, + Namespace: namespace, + }, + Status: v1alpha2.JenkinsStatus{ + AppliedGroovyScripts: []v1alpha2.AppliedGroovyScript{ + { + ConfigurationType: configurationType, + Source: source, + Name: groovyScriptName, + Hash: hash, + }, + }, + }, + } + err := v1alpha2.SchemeBuilder.AddToScheme(scheme.Scheme) + require.NoError(t, err) + fakeClient := fake.NewFakeClient() + err = fakeClient.Create(ctx, jenkins) + require.NoError(t, err) + + ctrl := gomock.NewController(t) + defer ctrl.Finish() + jenkinsClient := jenkinsclient.NewMockJenkins(ctrl) + + groovyClient := New(jenkinsClient, fakeClient, log.Log, jenkins, configurationType, emptyCustomization) + + // when + requeue, err := groovyClient.EnsureSingle(source, groovyScriptName, hash, groovyScript) + + // then + require.NoError(t, err) + assert.False(t, requeue) + + err = fakeClient.Get(ctx, types.NamespacedName{Name: jenkins.Name, Namespace: jenkins.Namespace}, jenkins) + require.NoError(t, err) + assert.Equal(t, 1, len(jenkins.Status.AppliedGroovyScripts)) + assert.Equal(t, configurationType, jenkins.Status.AppliedGroovyScripts[0].ConfigurationType) + assert.Equal(t, hash, jenkins.Status.AppliedGroovyScripts[0].Hash) + assert.Equal(t, source, jenkins.Status.AppliedGroovyScripts[0].Source) + assert.Equal(t, groovyScriptName, jenkins.Status.AppliedGroovyScripts[0].Name) + }) + t.Run("execute script fails", func(t *testing.T) { + // given + jenkins := &v1alpha2.Jenkins{ + ObjectMeta: metav1.ObjectMeta{ + Name: jenkinsName, + Namespace: namespace, + }, + } + err := v1alpha2.SchemeBuilder.AddToScheme(scheme.Scheme) + require.NoError(t, err) + fakeClient := fake.NewFakeClient() + err = fakeClient.Create(ctx, jenkins) + require.NoError(t, err) + + ctrl := gomock.NewController(t) + defer ctrl.Finish() + jenkinsClient := jenkinsclient.NewMockJenkins(ctrl) + jenkinsClient.EXPECT().ExecuteScript(groovyScript).Return("fail logs", &jenkinsclient.GroovyScriptExecutionFailed{}) + + groovyClient := New(jenkinsClient, fakeClient, log.Log, jenkins, configurationType, emptyCustomization) + + // when + requeue, err := groovyClient.EnsureSingle(source, groovyScriptName, hash, groovyScript) + + // then + require.Error(t, err) + assert.True(t, requeue) + + err = fakeClient.Get(ctx, types.NamespacedName{Name: jenkins.Name, Namespace: jenkins.Namespace}, jenkins) + require.NoError(t, err) + assert.Equal(t, 0, len(jenkins.Status.AppliedGroovyScripts)) + }) +} + +func TestGroovy_Ensure(t *testing.T) { + log.SetupLogger(true) + configurationType := "test-conf-type" + groovyScript := "groovy-script" + groovyScriptName := "groovy-script-name.groovy" + ctx := context.TODO() + jenkinsName := "jenkins" + namespace := "default" + configMapName := "config-map-name" + secretName := "secret-name" + + allGroovyScriptsFunc := func(name string) bool { + return true + } + noUpdateGroovyScript := func(groovyScript string) string { + return groovyScript + } + + t.Run("select groovy files with .groovy extension", func(t *testing.T) { + // given + groovyScriptExtension := ".groovy" + jenkins := &v1alpha2.Jenkins{ + ObjectMeta: metav1.ObjectMeta{ + Name: jenkinsName, + Namespace: namespace, + }, + } + customization := v1alpha2.Customization{ + Configurations: []v1alpha2.ConfigMapRef{ + { + Name: configMapName, + }, + }, + } + configMap := &corev1.ConfigMap{ + ObjectMeta: metav1.ObjectMeta{ + Name: configMapName, + Namespace: namespace, + }, + Data: map[string]string{ + groovyScriptName: groovyScript, + "to-ommit": "to-ommit", + }, + } + err := v1alpha2.SchemeBuilder.AddToScheme(scheme.Scheme) + require.NoError(t, err) + fakeClient := fake.NewFakeClient() + err = fakeClient.Create(ctx, jenkins) + require.NoError(t, err) + err = fakeClient.Create(ctx, configMap) + require.NoError(t, err) + + ctrl := gomock.NewController(t) + defer ctrl.Finish() + jenkinsClient := jenkinsclient.NewMockJenkins(ctrl) + jenkinsClient.EXPECT().ExecuteScript(groovyScript).Return("logs", nil) + + groovyClient := New(jenkinsClient, fakeClient, log.Log, jenkins, configurationType, customization) + onlyGroovyFilesFunc := func(name string) bool { + return strings.HasSuffix(name, groovyScriptExtension) + } + + // when + requeue, err := groovyClient.Ensure(onlyGroovyFilesFunc, noUpdateGroovyScript) + require.NoError(t, err) + assert.True(t, requeue) + requeue, err = groovyClient.Ensure(onlyGroovyFilesFunc, noUpdateGroovyScript) + require.NoError(t, err) + assert.False(t, requeue) + + // then + err = fakeClient.Get(ctx, types.NamespacedName{Name: jenkins.Name, Namespace: jenkins.Namespace}, jenkins) + require.NoError(t, err) + assert.Equal(t, 1, len(jenkins.Status.AppliedGroovyScripts)) + assert.Equal(t, configurationType, jenkins.Status.AppliedGroovyScripts[0].ConfigurationType) + assert.Equal(t, "qoXeeh4ia+KXhT01lYNxe+oxByDf8dfT2npP9fgzjbk=", jenkins.Status.AppliedGroovyScripts[0].Hash) + assert.Equal(t, configMapName, jenkins.Status.AppliedGroovyScripts[0].Source) + assert.Equal(t, groovyScriptName, jenkins.Status.AppliedGroovyScripts[0].Name) + }) + t.Run("change groovy script", func(t *testing.T) { + // given + groovyScriptSuffix := "suffix" + jenkins := &v1alpha2.Jenkins{ + ObjectMeta: metav1.ObjectMeta{ + Name: jenkinsName, + Namespace: namespace, + }, + } + customization := v1alpha2.Customization{ + Configurations: []v1alpha2.ConfigMapRef{ + { + Name: configMapName, + }, + }, + } + configMap := &corev1.ConfigMap{ + ObjectMeta: metav1.ObjectMeta{ + Name: configMapName, + Namespace: namespace, + }, + Data: map[string]string{ + groovyScriptName: groovyScript, + }, + } + err := v1alpha2.SchemeBuilder.AddToScheme(scheme.Scheme) + require.NoError(t, err) + fakeClient := fake.NewFakeClient() + err = fakeClient.Create(ctx, jenkins) + require.NoError(t, err) + err = fakeClient.Create(ctx, configMap) + require.NoError(t, err) + + ctrl := gomock.NewController(t) + defer ctrl.Finish() + jenkinsClient := jenkinsclient.NewMockJenkins(ctrl) + jenkinsClient.EXPECT().ExecuteScript(groovyScript+groovyScriptSuffix).Return("logs", nil) + + groovyClient := New(jenkinsClient, fakeClient, log.Log, jenkins, configurationType, customization) + updateGroovyFunc := func(groovyScript string) string { + return groovyScript + groovyScriptSuffix + } + + // when + requeue, err := groovyClient.Ensure(allGroovyScriptsFunc, updateGroovyFunc) + require.NoError(t, err) + assert.True(t, requeue) + requeue, err = groovyClient.Ensure(allGroovyScriptsFunc, updateGroovyFunc) + require.NoError(t, err) + assert.False(t, requeue) + + // then + err = fakeClient.Get(ctx, types.NamespacedName{Name: jenkins.Name, Namespace: jenkins.Namespace}, jenkins) + require.NoError(t, err) + assert.Equal(t, 1, len(jenkins.Status.AppliedGroovyScripts)) + assert.Equal(t, configurationType, jenkins.Status.AppliedGroovyScripts[0].ConfigurationType) + assert.Equal(t, "TgTpV3nDxMNMM93t6jgni0UHa7C+uL+D+BLcW3a7b6M=", jenkins.Status.AppliedGroovyScripts[0].Hash) + assert.Equal(t, configMapName, jenkins.Status.AppliedGroovyScripts[0].Source) + assert.Equal(t, groovyScriptName, jenkins.Status.AppliedGroovyScripts[0].Name) + }) + t.Run("execute script without secret and save status", func(t *testing.T) { + // given + jenkins := &v1alpha2.Jenkins{ + ObjectMeta: metav1.ObjectMeta{ + Name: jenkinsName, + Namespace: namespace, + }, + } + customization := v1alpha2.Customization{ + Configurations: []v1alpha2.ConfigMapRef{ + { + Name: configMapName, + }, + }, + } + configMap := &corev1.ConfigMap{ + ObjectMeta: metav1.ObjectMeta{ + Name: configMapName, + Namespace: namespace, + }, + Data: map[string]string{ + groovyScriptName: groovyScript, + }, + } + err := v1alpha2.SchemeBuilder.AddToScheme(scheme.Scheme) + require.NoError(t, err) + fakeClient := fake.NewFakeClient() + err = fakeClient.Create(ctx, jenkins) + require.NoError(t, err) + err = fakeClient.Create(ctx, configMap) + require.NoError(t, err) + + ctrl := gomock.NewController(t) + defer ctrl.Finish() + jenkinsClient := jenkinsclient.NewMockJenkins(ctrl) + jenkinsClient.EXPECT().ExecuteScript(groovyScript).Return("logs", nil) + + groovyClient := New(jenkinsClient, fakeClient, log.Log, jenkins, configurationType, customization) + + // when + requeue, err := groovyClient.Ensure(allGroovyScriptsFunc, noUpdateGroovyScript) + require.NoError(t, err) + assert.True(t, requeue) + requeue, err = groovyClient.Ensure(allGroovyScriptsFunc, noUpdateGroovyScript) + require.NoError(t, err) + assert.False(t, requeue) + + // then + err = fakeClient.Get(ctx, types.NamespacedName{Name: jenkins.Name, Namespace: jenkins.Namespace}, jenkins) + require.NoError(t, err) + assert.Equal(t, 1, len(jenkins.Status.AppliedGroovyScripts)) + assert.Equal(t, configurationType, jenkins.Status.AppliedGroovyScripts[0].ConfigurationType) + assert.Equal(t, "qoXeeh4ia+KXhT01lYNxe+oxByDf8dfT2npP9fgzjbk=", jenkins.Status.AppliedGroovyScripts[0].Hash) + assert.Equal(t, configMapName, jenkins.Status.AppliedGroovyScripts[0].Source) + assert.Equal(t, groovyScriptName, jenkins.Status.AppliedGroovyScripts[0].Name) + }) + t.Run("execute script with secret and save status", func(t *testing.T) { + // given + jenkins := &v1alpha2.Jenkins{ + ObjectMeta: metav1.ObjectMeta{ + Name: jenkinsName, + Namespace: namespace, + }, + } + customization := v1alpha2.Customization{ + Secret: v1alpha2.SecretRef{Name: secretName}, + Configurations: []v1alpha2.ConfigMapRef{ + { + Name: configMapName, + }, + }, + } + configMap := &corev1.ConfigMap{ + ObjectMeta: metav1.ObjectMeta{ + Name: configMapName, + Namespace: namespace, + }, + Data: map[string]string{ + groovyScriptName: groovyScript, + }, + } + secret := &corev1.Secret{ + ObjectMeta: metav1.ObjectMeta{ + Name: secretName, + Namespace: namespace, + }, + Data: map[string][]byte{ + "SECRET_KEY": []byte("secret-value"), + }, + } + err := v1alpha2.SchemeBuilder.AddToScheme(scheme.Scheme) + require.NoError(t, err) + fakeClient := fake.NewFakeClient() + err = fakeClient.Create(ctx, jenkins) + require.NoError(t, err) + err = fakeClient.Create(ctx, secret) + require.NoError(t, err) + err = fakeClient.Create(ctx, configMap) + require.NoError(t, err) + + ctrl := gomock.NewController(t) + defer ctrl.Finish() + jenkinsClient := jenkinsclient.NewMockJenkins(ctrl) + jenkinsClient.EXPECT().ExecuteScript(groovyScript).Return("logs", nil) + + groovyClient := New(jenkinsClient, fakeClient, log.Log, jenkins, configurationType, customization) + + // when + requeue, err := groovyClient.Ensure(allGroovyScriptsFunc, noUpdateGroovyScript) + require.NoError(t, err) + assert.True(t, requeue) + requeue, err = groovyClient.Ensure(allGroovyScriptsFunc, noUpdateGroovyScript) + require.NoError(t, err) + assert.False(t, requeue) + + // then + err = fakeClient.Get(ctx, types.NamespacedName{Name: jenkins.Name, Namespace: jenkins.Namespace}, jenkins) + require.NoError(t, err) + assert.Equal(t, 1, len(jenkins.Status.AppliedGroovyScripts)) + assert.Equal(t, configurationType, jenkins.Status.AppliedGroovyScripts[0].ConfigurationType) + assert.Equal(t, "em9pjw9mUheUpPRCJWD2Dww+80YQPoHCZbzzKZZw4lo=", jenkins.Status.AppliedGroovyScripts[0].Hash) + assert.Equal(t, configMapName, jenkins.Status.AppliedGroovyScripts[0].Source) + assert.Equal(t, groovyScriptName, jenkins.Status.AppliedGroovyScripts[0].Name) + }) +} + +func TestGroovy_isGroovyScriptAlreadyApplied(t *testing.T) { + log.SetupLogger(true) + emptyCustomization := v1alpha2.Customization{} + configurationType := "test-conf-type" + + t.Run("found", func(t *testing.T) { + jenkins := &v1alpha2.Jenkins{ + Status: v1alpha2.JenkinsStatus{ + AppliedGroovyScripts: []v1alpha2.AppliedGroovyScript{ + { + ConfigurationType: configurationType, + Source: "source", + Name: "name", + Hash: "hash", + }, + }, + }, + } + groovyClient := New(nil, nil, log.Log, jenkins, configurationType, emptyCustomization) + + got := groovyClient.isGroovyScriptAlreadyApplied("source", "name", "hash") + + assert.True(t, got) + }) + t.Run("not found", func(t *testing.T) { + jenkins := &v1alpha2.Jenkins{ + Status: v1alpha2.JenkinsStatus{ + AppliedGroovyScripts: []v1alpha2.AppliedGroovyScript{ + { + ConfigurationType: configurationType, + Source: "source", + Name: "name", + Hash: "hash", + }, + }, + }, + } + groovyClient := New(nil, nil, log.Log, jenkins, configurationType, emptyCustomization) + + got := groovyClient.isGroovyScriptAlreadyApplied("source", "not-exist", "hash") + + assert.False(t, got) + }) + t.Run("empty Jenkins status", func(t *testing.T) { + jenkins := &v1alpha2.Jenkins{} + groovyClient := New(nil, nil, log.Log, jenkins, configurationType, emptyCustomization) + + got := groovyClient.isGroovyScriptAlreadyApplied("source", "name", "hash") + + assert.False(t, got) + }) +} + +func TestAddSecretsLoaderToGroovyScript(t *testing.T) { + secretsPath := "/var/jenkins/groovy-scripts-secrets" + secretsLoader := fmt.Sprintf(secretsLoaderGroovyScriptFmt, secretsPath) + + t.Run("without imports", func(t *testing.T) { + groovyScript := "println 'Simple groovy script" + updater := AddSecretsLoaderToGroovyScript(secretsPath) + + got := updater(groovyScript) + + assert.Equal(t, secretsLoader+groovyScript, got) + }) + t.Run("with imports", func(t *testing.T) { + groovyScript := `import com.foo.bar +import com.foo.bar2 +println 'Simple groovy script'` + imports := `import com.foo.bar +import com.foo.bar2` + tail := `println 'Simple groovy script'` + update := AddSecretsLoaderToGroovyScript(secretsPath) + + got := update(groovyScript) + + assert.Equal(t, imports+"\n\n"+secretsLoader+"\n\n"+tail, got) + }) + t.Run("with imports and separate section", func(t *testing.T) { + groovyScript := `import com.foo.bar +import com.foo.bar2 + +println 'Simple groovy script'` + imports := `import com.foo.bar +import com.foo.bar2` + tail := `println 'Simple groovy script'` + update := AddSecretsLoaderToGroovyScript(secretsPath) + + got := update(groovyScript) + + assert.Equal(t, imports+"\n\n"+secretsLoader+"\n\n\n"+tail, got) + }) +}