diff --git a/pkg/controller/jenkins/configuration/base/validate.go b/pkg/controller/jenkins/configuration/base/validate.go index 906b2f3a..f3de634a 100644 --- a/pkg/controller/jenkins/configuration/base/validate.go +++ b/pkg/controller/jenkins/configuration/base/validate.go @@ -46,6 +46,17 @@ func (r *ReconcileJenkinsBaseConfiguration) Validate(jenkins *v1alpha2.Jenkins) return false, nil } + if valid, err := r.validateCustomization(r.jenkins.Spec.GroovyScripts.Customization, "spec.groovyScripts"); err != nil { + return false, err + } else if !valid { + return false, nil + } + if valid, err := r.validateCustomization(r.jenkins.Spec.ConfigurationAsCode.Customization, "spec.configurationAsCode"); err != nil { + return false, err + } else if !valid { + return false, nil + } + return true, nil } @@ -194,7 +205,7 @@ func (r *ReconcileJenkinsBaseConfiguration) validateContainerVolumeMounts(contai } func (r *ReconcileJenkinsBaseConfiguration) validateJenkinsMasterPodEnvs() bool { - baseEnvs := resources.GetJenkinsMasterContainerBaseEnvs() + baseEnvs := resources.GetJenkinsMasterContainerBaseEnvs(r.jenkins) baseEnvNames := map[string]string{} for _, env := range baseEnvs { baseEnvNames[env.Name] = env.Value @@ -269,3 +280,45 @@ func (r *ReconcileJenkinsBaseConfiguration) verifyBasePlugins(requiredBasePlugin return valid } + +func (r *ReconcileJenkinsBaseConfiguration) validateCustomization(customization v1alpha2.Customization, name string) (bool, error) { + valid := true + if len(customization.Secret.Name) == 0 && len(customization.Configurations) == 0 { + return true, nil + } + if len(customization.Secret.Name) > 0 && len(customization.Configurations) == 0 { + valid = false + r.logger.V(log.VWarn).Info(fmt.Sprintf("%s.secret.name is set but %s.configurations is empty", name, name)) + } + + if len(customization.Secret.Name) > 0 { + secret := &corev1.Secret{} + err := r.k8sClient.Get(context.TODO(), types.NamespacedName{Name: customization.Secret.Name, Namespace: r.jenkins.ObjectMeta.Namespace}, secret) + if err != nil && apierrors.IsNotFound(err) { + valid = false + r.logger.V(log.VWarn).Info(fmt.Sprintf("Secret '%s' configured in %s.secret.name not found", customization.Secret.Name, name)) + } else if err != nil && !apierrors.IsNotFound(err) { + return false, stackerr.WithStack(err) + } + } + + for index, configMapRef := range customization.Configurations { + if len(configMapRef.Name) == 0 { + r.logger.V(log.VWarn).Info(fmt.Sprintf("%s.configurations[%d] name is empty", name, index)) + valid = false + continue + } + + configMap := &corev1.ConfigMap{} + err := r.k8sClient.Get(context.TODO(), types.NamespacedName{Name: configMapRef.Name, Namespace: r.jenkins.ObjectMeta.Namespace}, configMap) + if err != nil && apierrors.IsNotFound(err) { + valid = false + r.logger.V(log.VWarn).Info(fmt.Sprintf("ConfigMap '%s' configured in %s.configurations[%d] not found", configMapRef.Name, name, index)) + return false, nil + } else if err != nil && !apierrors.IsNotFound(err) { + return false, stackerr.WithStack(err) + } + } + + return valid, nil +} diff --git a/pkg/controller/jenkins/configuration/base/validate_test.go b/pkg/controller/jenkins/configuration/base/validate_test.go index 9c46380b..07b7c208 100644 --- a/pkg/controller/jenkins/configuration/base/validate_test.go +++ b/pkg/controller/jenkins/configuration/base/validate_test.go @@ -10,6 +10,7 @@ import ( "github.com/jenkinsci/kubernetes-operator/pkg/log" "github.com/stretchr/testify/assert" + "github.com/stretchr/testify/require" "k8s.io/api/core/v1" corev1 "k8s.io/api/core/v1" metav1 "k8s.io/apimachinery/pkg/apis/meta/v1" @@ -446,3 +447,122 @@ func TestValidateSecretVolume(t *testing.T) { assert.False(t, got) }) } + +func TestValidateCustomization(t *testing.T) { + namespace := "default" + secretName := "secretName" + configMapName := "configmap-name" + jenkins := &v1alpha2.Jenkins{ + ObjectMeta: metav1.ObjectMeta{ + Namespace: namespace, + }, + } + t.Run("empty", func(t *testing.T) { + customization := v1alpha2.Customization{} + fakeClient := fake.NewFakeClient() + baseReconcileLoop := New(fakeClient, nil, logf.ZapLogger(false), + jenkins, false, false, nil, nil) + + got, err := baseReconcileLoop.validateCustomization(customization, "spec.groovyScripts") + + assert.NoError(t, err) + assert.True(t, got) + }) + t.Run("secret set but configurations is empty", func(t *testing.T) { + customization := v1alpha2.Customization{ + Secret: v1alpha2.SecretRef{Name: secretName}, + Configurations: []v1alpha2.ConfigMapRef{}, + } + secret := &corev1.Secret{ + ObjectMeta: metav1.ObjectMeta{ + Name: secretName, + Namespace: namespace, + }, + } + fakeClient := fake.NewFakeClient() + baseReconcileLoop := New(fakeClient, nil, logf.ZapLogger(false), + jenkins, false, false, nil, nil) + err := fakeClient.Create(context.TODO(), secret) + require.NoError(t, err) + + got, err := baseReconcileLoop.validateCustomization(customization, "spec.groovyScripts") + + assert.NoError(t, err) + assert.False(t, got) + }) + t.Run("secret and configmap exists", func(t *testing.T) { + customization := v1alpha2.Customization{ + Secret: v1alpha2.SecretRef{Name: secretName}, + Configurations: []v1alpha2.ConfigMapRef{{Name: configMapName}}, + } + configMap := &corev1.ConfigMap{ + ObjectMeta: metav1.ObjectMeta{ + Name: configMapName, + Namespace: namespace, + }, + } + secret := &corev1.Secret{ + ObjectMeta: metav1.ObjectMeta{ + Name: secretName, + Namespace: namespace, + }, + } + fakeClient := fake.NewFakeClient() + baseReconcileLoop := New(fakeClient, nil, logf.ZapLogger(false), + jenkins, false, false, nil, nil) + err := fakeClient.Create(context.TODO(), secret) + require.NoError(t, err) + err = fakeClient.Create(context.TODO(), configMap) + require.NoError(t, err) + + got, err := baseReconcileLoop.validateCustomization(customization, "spec.groovyScripts") + + assert.NoError(t, err) + assert.True(t, got) + }) + t.Run("secret not exists and configmap exists", func(t *testing.T) { + configMapName := "configmap-name" + customization := v1alpha2.Customization{ + Secret: v1alpha2.SecretRef{Name: secretName}, + Configurations: []v1alpha2.ConfigMapRef{{Name: configMapName}}, + } + configMap := &corev1.ConfigMap{ + ObjectMeta: metav1.ObjectMeta{ + Name: configMapName, + Namespace: namespace, + }, + } + fakeClient := fake.NewFakeClient() + baseReconcileLoop := New(fakeClient, nil, logf.ZapLogger(false), + jenkins, false, false, nil, nil) + err := fakeClient.Create(context.TODO(), configMap) + require.NoError(t, err) + + got, err := baseReconcileLoop.validateCustomization(customization, "spec.groovyScripts") + + assert.NoError(t, err) + assert.False(t, got) + }) + t.Run("secret exists and configmap not exists", func(t *testing.T) { + customization := v1alpha2.Customization{ + Secret: v1alpha2.SecretRef{Name: secretName}, + Configurations: []v1alpha2.ConfigMapRef{{Name: configMapName}}, + } + secret := &corev1.Secret{ + ObjectMeta: metav1.ObjectMeta{ + Name: secretName, + Namespace: namespace, + }, + } + fakeClient := fake.NewFakeClient() + baseReconcileLoop := New(fakeClient, nil, logf.ZapLogger(false), + jenkins, false, false, nil, nil) + err := fakeClient.Create(context.TODO(), secret) + require.NoError(t, err) + + got, err := baseReconcileLoop.validateCustomization(customization, "spec.groovyScripts") + + assert.NoError(t, err) + assert.False(t, got) + }) +}