diff --git a/pkg/controller/jenkins/configuration/backuprestore/backuprestore.go b/pkg/controller/jenkins/configuration/backuprestore/backuprestore.go index 85b85d49..bf067de8 100644 --- a/pkg/controller/jenkins/configuration/backuprestore/backuprestore.go +++ b/pkg/controller/jenkins/configuration/backuprestore/backuprestore.go @@ -14,6 +14,7 @@ import ( "github.com/go-logr/logr" "github.com/pkg/errors" corev1 "k8s.io/api/core/v1" + apierrors "k8s.io/apimachinery/pkg/api/errors" "k8s.io/apimachinery/pkg/types" "k8s.io/client-go/kubernetes" "k8s.io/client-go/kubernetes/scheme" @@ -27,7 +28,36 @@ type backupTrigger struct { ticker *time.Ticker } -var backupTriggers = map[string]backupTrigger{} +type backupTriggers struct { + triggers map[string]backupTrigger +} + +func (t *backupTriggers) stop(logger logr.Logger, namespace string, name string) { + key := t.key(namespace, name) + trigger, found := t.triggers[key] + if found { + logger.Info(fmt.Sprintf("Stopping backup trigger for '%s'", key)) + trigger.ticker.Stop() + delete(t.triggers, key) + } else { + logger.V(log.VWarn).Info(fmt.Sprintf("Can't stop backup trigger for '%s', not found, skipping", key)) + } +} + +func (t *backupTriggers) get(namespace, name string) (backupTrigger, bool) { + trigger, found := t.triggers[t.key(namespace, name)] + return trigger, found +} + +func (t *backupTriggers) key(namespace, name string) string { + return namespace + "/" + name +} + +func (t *backupTriggers) add(namespace string, name string, trigger backupTrigger) { + t.triggers[t.key(namespace, name)] = trigger +} + +var triggers = backupTriggers{triggers: make(map[string]backupTrigger)} // BackupAndRestore represents Jenkins backup and restore client type BackupAndRestore struct { @@ -169,7 +199,10 @@ func triggerBackup(ticker *time.Ticker, k8sClient k8s.Client, logger logr.Logger for range ticker.C { jenkins := &v1alpha2.Jenkins{} err := k8sClient.Get(context.TODO(), types.NamespacedName{Namespace: namespace, Name: name}, jenkins) - if err != nil { + if err != nil && apierrors.IsNotFound(err) { + triggers.stop(logger, namespace, name) + return // abort + } else if err != nil { logger.V(log.VWarn).Info(fmt.Sprintf("backup trigger, error when fetching CR: %s", err)) } if jenkins.Status.LastBackup == jenkins.Status.PendingBackup { @@ -184,21 +217,22 @@ func triggerBackup(ticker *time.Ticker, k8sClient k8s.Client, logger logr.Logger // EnsureBackupTrigger creates or update trigger which update CR to make backup func (bar *BackupAndRestore) EnsureBackupTrigger() error { - jenkins := bar.jenkins - trigger, found := backupTriggers[jenkins.Name] - if len(jenkins.Spec.Backup.ContainerName) == 0 || jenkins.Spec.Backup.Interval == 0 { - bar.logger.V(log.VDebug).Info("Skipping create backup trigger") - if found { - bar.stopBackupTrigger(trigger) - } + trigger, found := triggers.get(bar.jenkins.Namespace, bar.jenkins.Name) + + isBackupConfigured := len(bar.jenkins.Spec.Backup.ContainerName) > 0 && bar.jenkins.Spec.Backup.Interval > 0 + if found && !isBackupConfigured { + bar.StopBackupTrigger() return nil } - if !found { + // configured backup has no trigger + if !found && isBackupConfigured { bar.startBackupTrigger() + return nil } - if found && jenkins.Spec.Backup.Interval != trigger.interval { - bar.stopBackupTrigger(trigger) + + if found && isBackupConfigured && bar.jenkins.Spec.Backup.Interval != trigger.interval { + bar.StopBackupTrigger() bar.startBackupTrigger() } @@ -207,28 +241,19 @@ func (bar *BackupAndRestore) EnsureBackupTrigger() error { // StopBackupTrigger stops trigger which update CR to make backup func (bar *BackupAndRestore) StopBackupTrigger() { - trigger, found := backupTriggers[bar.jenkins.Name] - if found { - bar.stopBackupTrigger(trigger) - } + triggers.stop(bar.logger, bar.jenkins.Namespace, bar.jenkins.Name) } func (bar *BackupAndRestore) startBackupTrigger() { bar.logger.Info("Starting backup trigger") ticker := time.NewTicker(time.Duration(bar.jenkins.Spec.Backup.Interval) * time.Second) - backupTriggers[bar.jenkins.Name] = backupTrigger{ + triggers.add(bar.jenkins.Namespace, bar.jenkins.Name, backupTrigger{ interval: bar.jenkins.Spec.Backup.Interval, ticker: ticker, - } + }) go triggerBackup(ticker, bar.k8sClient, bar.logger, bar.jenkins.Namespace, bar.jenkins.Name) } -func (bar *BackupAndRestore) stopBackupTrigger(trigger backupTrigger) { - bar.logger.Info("Stopping backup trigger") - trigger.ticker.Stop() - delete(backupTriggers, bar.jenkins.Name) -} - func (bar *BackupAndRestore) exec(podName, containerName string, command []string) (stdout, stderr bytes.Buffer, err error) { req := bar.clientSet.CoreV1().RESTClient().Post(). Resource("pods"). diff --git a/pkg/controller/jenkins/configuration/base/reconcile.go b/pkg/controller/jenkins/configuration/base/reconcile.go index 3d2627e0..259e0a61 100644 --- a/pkg/controller/jenkins/configuration/base/reconcile.go +++ b/pkg/controller/jenkins/configuration/base/reconcile.go @@ -28,7 +28,6 @@ import ( "k8s.io/apimachinery/pkg/api/errors" apierrors "k8s.io/apimachinery/pkg/api/errors" metav1 "k8s.io/apimachinery/pkg/apis/meta/v1" - "k8s.io/apimachinery/pkg/runtime" "k8s.io/apimachinery/pkg/types" "k8s.io/client-go/rest" "sigs.k8s.io/controller-runtime/pkg/client" @@ -42,17 +41,15 @@ const ( // ReconcileJenkinsBaseConfiguration defines values required for Jenkins base configuration type ReconcileJenkinsBaseConfiguration struct { configuration.Configuration - scheme *runtime.Scheme logger logr.Logger local, minikube bool config *rest.Config } // New create structure which takes care of base configuration -func New(config configuration.Configuration, scheme *runtime.Scheme, logger logr.Logger, local, minikube bool, restConfig *rest.Config) *ReconcileJenkinsBaseConfiguration { +func New(config configuration.Configuration, logger logr.Logger, local, minikube bool, restConfig *rest.Config) *ReconcileJenkinsBaseConfiguration { return &ReconcileJenkinsBaseConfiguration{ Configuration: config, - scheme: scheme, logger: logger, local: local, minikube: minikube, @@ -255,7 +252,7 @@ func (r *ReconcileJenkinsBaseConfiguration) createOperatorCredentialsSecret(meta err := r.Configuration.Client.Get(context.TODO(), types.NamespacedName{Name: resources.GetOperatorCredentialsSecretName(r.Configuration.Jenkins), Namespace: r.Configuration.Jenkins.ObjectMeta.Namespace}, found) if err != nil && apierrors.IsNotFound(err) { - return stackerr.WithStack(r.createResource(resources.NewOperatorCredentialsSecret(meta, r.Configuration.Jenkins))) + return stackerr.WithStack(r.CreateResource(resources.NewOperatorCredentialsSecret(meta, r.Configuration.Jenkins))) } else if err != nil && !apierrors.IsNotFound(err) { return stackerr.WithStack(err) } @@ -265,7 +262,7 @@ func (r *ReconcileJenkinsBaseConfiguration) createOperatorCredentialsSecret(meta return nil } - return stackerr.WithStack(r.updateResource(resources.NewOperatorCredentialsSecret(meta, r.Configuration.Jenkins))) + return stackerr.WithStack(r.UpdateResource(resources.NewOperatorCredentialsSecret(meta, r.Configuration.Jenkins))) } func (r *ReconcileJenkinsBaseConfiguration) createScriptsConfigMap(meta metav1.ObjectMeta) error { @@ -273,7 +270,7 @@ func (r *ReconcileJenkinsBaseConfiguration) createScriptsConfigMap(meta metav1.O if err != nil { return err } - return stackerr.WithStack(r.createOrUpdateResource(configMap)) + return stackerr.WithStack(r.CreateOrUpdateResource(configMap)) } func (r *ReconcileJenkinsBaseConfiguration) createInitConfigurationConfigMap(meta metav1.ObjectMeta) error { @@ -281,12 +278,12 @@ func (r *ReconcileJenkinsBaseConfiguration) createInitConfigurationConfigMap(met if err != nil { return err } - return stackerr.WithStack(r.createOrUpdateResource(configMap)) + return stackerr.WithStack(r.CreateOrUpdateResource(configMap)) } func (r *ReconcileJenkinsBaseConfiguration) createBaseConfigurationConfigMap(meta metav1.ObjectMeta) error { configMap := resources.NewBaseConfigurationConfigMap(meta, r.Configuration.Jenkins) - return stackerr.WithStack(r.createOrUpdateResource(configMap)) + return stackerr.WithStack(r.CreateOrUpdateResource(configMap)) } func (r *ReconcileJenkinsBaseConfiguration) addLabelForWatchesResources(customization v1alpha2.Customization) error { @@ -339,19 +336,19 @@ func (r *ReconcileJenkinsBaseConfiguration) addLabelForWatchesResources(customiz func (r *ReconcileJenkinsBaseConfiguration) createRBAC(meta metav1.ObjectMeta) error { serviceAccount := resources.NewServiceAccount(meta) - err := r.createResource(serviceAccount) + err := r.CreateResource(serviceAccount) if err != nil && !errors.IsAlreadyExists(err) { return stackerr.WithStack(err) } role := resources.NewRole(meta) - err = r.createOrUpdateResource(role) + err = r.CreateOrUpdateResource(role) if err != nil { return stackerr.WithStack(err) } roleBinding := resources.NewRoleBinding(meta) - err = r.createOrUpdateResource(roleBinding) + err = r.CreateOrUpdateResource(roleBinding) if err != nil { return stackerr.WithStack(err) } @@ -373,7 +370,7 @@ func (r *ReconcileJenkinsBaseConfiguration) createService(meta metav1.ObjectMeta Selector: meta.Labels, }, }, config) - if err = r.createResource(&service); err != nil { + if err = r.CreateResource(&service); err != nil { return stackerr.WithStack(err) } } else if err != nil { @@ -381,7 +378,7 @@ func (r *ReconcileJenkinsBaseConfiguration) createService(meta metav1.ObjectMeta } service = resources.UpdateService(service, config) - return stackerr.WithStack(r.updateResource(&service)) + return stackerr.WithStack(r.UpdateResource(&service)) } func (r *ReconcileJenkinsBaseConfiguration) getJenkinsMasterPod() (*corev1.Pod, error) { @@ -416,7 +413,7 @@ func (r *ReconcileJenkinsBaseConfiguration) ensureJenkinsMasterPod(meta metav1.O Reason: reason.NewPodCreation(reason.OperatorSource, []string{"Creating a new Jenkins Master Pod"}), } r.logger.Info(fmt.Sprintf("Creating a new Jenkins Master Pod %s/%s", jenkinsMasterPod.Namespace, jenkinsMasterPod.Name)) - err = r.createResource(jenkinsMasterPod) + err = r.CreateResource(jenkinsMasterPod) if err != nil { return reconcile.Result{}, stackerr.WithStack(err) } @@ -827,7 +824,7 @@ func (r *ReconcileJenkinsBaseConfiguration) ensureJenkinsClient(meta metav1.Obje credentialsSecret.Data[resources.OperatorCredentialsSecretTokenKey] = []byte(token.GetToken()) now, _ := time.Now().UTC().MarshalText() credentialsSecret.Data[resources.OperatorCredentialsSecretTokenCreationKey] = now - err = r.updateResource(credentialsSecret) + err = r.UpdateResource(credentialsSecret) if err != nil { return nil, stackerr.WithStack(err) } diff --git a/pkg/controller/jenkins/configuration/base/reconcile_test.go b/pkg/controller/jenkins/configuration/base/reconcile_test.go index 04b102a6..b129074d 100644 --- a/pkg/controller/jenkins/configuration/base/reconcile_test.go +++ b/pkg/controller/jenkins/configuration/base/reconcile_test.go @@ -234,7 +234,7 @@ func TestCompareVolumes(t *testing.T) { Volumes: resources.GetJenkinsMasterPodBaseVolumes(jenkins), }, } - reconciler := New(configuration.Configuration{Jenkins: jenkins}, nil, nil, false, false, nil) + reconciler := New(configuration.Configuration{Jenkins: jenkins}, nil, false, false, nil) got := reconciler.compareVolumes(pod) @@ -258,7 +258,7 @@ func TestCompareVolumes(t *testing.T) { Volumes: resources.GetJenkinsMasterPodBaseVolumes(jenkins), }, } - reconciler := New(configuration.Configuration{Jenkins: jenkins}, nil, nil, false, false, nil) + reconciler := New(configuration.Configuration{Jenkins: jenkins}, nil, false, false, nil) got := reconciler.compareVolumes(pod) @@ -282,7 +282,7 @@ func TestCompareVolumes(t *testing.T) { Volumes: append(resources.GetJenkinsMasterPodBaseVolumes(jenkins), corev1.Volume{Name: "added"}), }, } - reconciler := New(configuration.Configuration{Jenkins: jenkins}, nil, nil, false, false, nil) + reconciler := New(configuration.Configuration{Jenkins: jenkins}, nil, false, false, nil) got := reconciler.compareVolumes(pod) diff --git a/pkg/controller/jenkins/configuration/base/resources.go b/pkg/controller/jenkins/configuration/base/resources.go deleted file mode 100644 index 290277b7..00000000 --- a/pkg/controller/jenkins/configuration/base/resources.go +++ /dev/null @@ -1,56 +0,0 @@ -package base - -import ( - "context" - - stackerr "github.com/pkg/errors" - "k8s.io/apimachinery/pkg/api/errors" - metav1 "k8s.io/apimachinery/pkg/apis/meta/v1" - "k8s.io/apimachinery/pkg/runtime" - "sigs.k8s.io/controller-runtime/pkg/controller/controllerutil" -) - -func (r *ReconcileJenkinsBaseConfiguration) createResource(obj metav1.Object) error { - runtimeObj, ok := obj.(runtime.Object) - if !ok { - return stackerr.Errorf("is not a %T a runtime.Object", obj) - } - - // Set Jenkins instance as the owner and controller - if err := controllerutil.SetControllerReference(r.Configuration.Jenkins, obj, r.scheme); err != nil { - return stackerr.WithStack(err) - } - - return r.Client.Create(context.TODO(), runtimeObj) // don't wrap error -} - -func (r *ReconcileJenkinsBaseConfiguration) updateResource(obj metav1.Object) error { - runtimeObj, ok := obj.(runtime.Object) - if !ok { - return stackerr.Errorf("is not a %T a runtime.Object", obj) - } - - // set Jenkins instance as the owner and controller, don't check error(can be already set) - _ = controllerutil.SetControllerReference(r.Configuration.Jenkins, obj, r.scheme) - - return r.Client.Update(context.TODO(), runtimeObj) // don't wrap error -} - -func (r *ReconcileJenkinsBaseConfiguration) createOrUpdateResource(obj metav1.Object) error { - runtimeObj, ok := obj.(runtime.Object) - if !ok { - return stackerr.Errorf("is not a %T a runtime.Object", obj) - } - - // set Jenkins instance as the owner and controller, don't check error(can be already set) - _ = controllerutil.SetControllerReference(r.Configuration.Jenkins, obj, r.scheme) - - err := r.Client.Create(context.TODO(), runtimeObj) - if err != nil && errors.IsAlreadyExists(err) { - return r.updateResource(obj) - } else if err != nil && !errors.IsAlreadyExists(err) { - return stackerr.WithStack(err) - } - - return nil -} diff --git a/pkg/controller/jenkins/configuration/base/validate_test.go b/pkg/controller/jenkins/configuration/base/validate_test.go index cc99f892..0bc88cad 100644 --- a/pkg/controller/jenkins/configuration/base/validate_test.go +++ b/pkg/controller/jenkins/configuration/base/validate_test.go @@ -23,7 +23,7 @@ import ( func TestValidatePlugins(t *testing.T) { log.SetupLogger(true) - baseReconcileLoop := New(configuration.Configuration{}, nil, log.Log, false, false, nil) + baseReconcileLoop := New(configuration.Configuration{}, log.Log, false, false, nil) t.Run("empty", func(t *testing.T) { var requiredBasePlugins []plugins.Plugin var basePlugins []v1alpha2.Plugin @@ -166,7 +166,7 @@ func TestReconcileJenkinsBaseConfiguration_validateImagePullSecrets(t *testing.T baseReconcileLoop := New(configuration.Configuration{ Client: fakeClient, Jenkins: &jenkins, - }, nil, logf.ZapLogger(false), false, false, nil) + }, logf.ZapLogger(false), false, false, nil) got, err := baseReconcileLoop.validateImagePullSecrets() fmt.Println(got) @@ -190,7 +190,7 @@ func TestReconcileJenkinsBaseConfiguration_validateImagePullSecrets(t *testing.T baseReconcileLoop := New(configuration.Configuration{ Client: fakeClient, Jenkins: &jenkins, - }, nil, nil, false, false, nil) + }, nil, false, false, nil) got, _ := baseReconcileLoop.validateImagePullSecrets() @@ -226,7 +226,7 @@ func TestReconcileJenkinsBaseConfiguration_validateImagePullSecrets(t *testing.T baseReconcileLoop := New(configuration.Configuration{ Client: fakeClient, Jenkins: &jenkins, - }, nil, nil, false, false, nil) + }, nil, false, false, nil) got, _ := baseReconcileLoop.validateImagePullSecrets() @@ -262,7 +262,7 @@ func TestReconcileJenkinsBaseConfiguration_validateImagePullSecrets(t *testing.T baseReconcileLoop := New(configuration.Configuration{ Client: fakeClient, Jenkins: &jenkins, - }, nil, logf.ZapLogger(false), false, false, nil) + }, logf.ZapLogger(false), false, false, nil) got, _ := baseReconcileLoop.validateImagePullSecrets() @@ -298,7 +298,7 @@ func TestReconcileJenkinsBaseConfiguration_validateImagePullSecrets(t *testing.T baseReconcileLoop := New(configuration.Configuration{ Client: fakeClient, Jenkins: &jenkins, - }, nil, logf.ZapLogger(false), false, false, nil) + }, logf.ZapLogger(false), false, false, nil) got, _ := baseReconcileLoop.validateImagePullSecrets() @@ -334,7 +334,7 @@ func TestReconcileJenkinsBaseConfiguration_validateImagePullSecrets(t *testing.T baseReconcileLoop := New(configuration.Configuration{ Client: fakeClient, Jenkins: &jenkins, - }, nil, logf.ZapLogger(false), false, false, nil) + }, logf.ZapLogger(false), false, false, nil) got, _ := baseReconcileLoop.validateImagePullSecrets() @@ -367,7 +367,7 @@ func TestValidateJenkinsMasterPodEnvs(t *testing.T) { } baseReconcileLoop := New(configuration.Configuration{ Jenkins: &jenkins, - }, nil, logf.ZapLogger(false), false, false, nil) + }, logf.ZapLogger(false), false, false, nil) got := baseReconcileLoop.validateJenkinsMasterPodEnvs() assert.Nil(t, got) }) @@ -394,7 +394,7 @@ func TestValidateJenkinsMasterPodEnvs(t *testing.T) { } baseReconcileLoop := New(configuration.Configuration{ Jenkins: &jenkins, - }, nil, logf.ZapLogger(false), false, false, nil) + }, logf.ZapLogger(false), false, false, nil) got := baseReconcileLoop.validateJenkinsMasterPodEnvs() assert.Equal(t, got, []string{"Jenkins Master container env 'JENKINS_HOME' cannot be overridden"}) @@ -418,7 +418,7 @@ func TestValidateJenkinsMasterPodEnvs(t *testing.T) { } baseReconcileLoop := New(configuration.Configuration{ Jenkins: &jenkins, - }, nil, logf.ZapLogger(false), false, false, nil) + }, logf.ZapLogger(false), false, false, nil) got := baseReconcileLoop.validateJenkinsMasterPodEnvs() assert.Equal(t, got, []string{"Jenkins Master container env 'JAVA_OPTS' doesn't have required flag '-Djava.awt.headless=true'"}) @@ -442,7 +442,7 @@ func TestValidateJenkinsMasterPodEnvs(t *testing.T) { } baseReconcileLoop := New(configuration.Configuration{ Jenkins: &jenkins, - }, nil, logf.ZapLogger(false), false, false, nil) + }, logf.ZapLogger(false), false, false, nil) got := baseReconcileLoop.validateJenkinsMasterPodEnvs() assert.Equal(t, got, []string{"Jenkins Master container env 'JAVA_OPTS' doesn't have required flag '-Djenkins.install.runSetupWizard=false'"}) @@ -464,7 +464,7 @@ func TestValidateReservedVolumes(t *testing.T) { } baseReconcileLoop := New(configuration.Configuration{ Jenkins: &jenkins, - }, nil, logf.ZapLogger(false), false, false, nil) + }, logf.ZapLogger(false), false, false, nil) got := baseReconcileLoop.validateReservedVolumes() assert.Nil(t, got) }) @@ -482,7 +482,7 @@ func TestValidateReservedVolumes(t *testing.T) { } baseReconcileLoop := New(configuration.Configuration{ Jenkins: &jenkins, - }, nil, logf.ZapLogger(false), false, false, nil) + }, logf.ZapLogger(false), false, false, nil) got := baseReconcileLoop.validateReservedVolumes() assert.Equal(t, got, []string{"Jenkins Master pod volume 'jenkins-home' is reserved please choose different one"}) @@ -498,7 +498,7 @@ func TestValidateContainerVolumeMounts(t *testing.T) { } baseReconcileLoop := New(configuration.Configuration{ Jenkins: &jenkins, - }, nil, logf.ZapLogger(false), false, false, nil) + }, logf.ZapLogger(false), false, false, nil) got := baseReconcileLoop.validateContainerVolumeMounts(v1alpha2.Container{}) assert.Nil(t, got) }) @@ -526,7 +526,7 @@ func TestValidateContainerVolumeMounts(t *testing.T) { } baseReconcileLoop := New(configuration.Configuration{ Jenkins: &jenkins, - }, nil, logf.ZapLogger(false), false, false, nil) + }, logf.ZapLogger(false), false, false, nil) got := baseReconcileLoop.validateContainerVolumeMounts(jenkins.Spec.Master.Containers[0]) assert.Nil(t, got) }) @@ -554,7 +554,7 @@ func TestValidateContainerVolumeMounts(t *testing.T) { } baseReconcileLoop := New(configuration.Configuration{ Jenkins: &jenkins, - }, nil, logf.ZapLogger(false), false, false, nil) + }, logf.ZapLogger(false), false, false, nil) got := baseReconcileLoop.validateContainerVolumeMounts(jenkins.Spec.Master.Containers[0]) assert.Equal(t, got, []string{"mountPath not set for 'example' volume mount in container ''"}) }) @@ -577,7 +577,7 @@ func TestValidateContainerVolumeMounts(t *testing.T) { } baseReconcileLoop := New(configuration.Configuration{ Jenkins: &jenkins, - }, nil, logf.ZapLogger(false), false, false, nil) + }, logf.ZapLogger(false), false, false, nil) got := baseReconcileLoop.validateContainerVolumeMounts(jenkins.Spec.Master.Containers[0]) assert.Equal(t, got, []string{"Not found volume for 'missing-volume' volume mount in container ''"}) @@ -599,7 +599,7 @@ func TestValidateConfigMapVolume(t *testing.T) { fakeClient := fake.NewFakeClient() baseReconcileLoop := New(configuration.Configuration{ Client: fakeClient, - }, nil, logf.ZapLogger(false), false, false, nil) + }, logf.ZapLogger(false), false, false, nil) got, err := baseReconcileLoop.validateConfigMapVolume(volume) @@ -627,7 +627,7 @@ func TestValidateConfigMapVolume(t *testing.T) { baseReconcileLoop := New(configuration.Configuration{ Client: fakeClient, Jenkins: jenkins, - }, nil, logf.ZapLogger(false), false, false, nil) + }, logf.ZapLogger(false), false, false, nil) got, err := baseReconcileLoop.validateConfigMapVolume(volume) @@ -653,7 +653,7 @@ func TestValidateConfigMapVolume(t *testing.T) { baseReconcileLoop := New(configuration.Configuration{ Client: fakeClient, Jenkins: jenkins, - }, nil, logf.ZapLogger(false), false, false, nil) + }, logf.ZapLogger(false), false, false, nil) got, err := baseReconcileLoop.validateConfigMapVolume(volume) @@ -678,7 +678,7 @@ func TestValidateSecretVolume(t *testing.T) { fakeClient := fake.NewFakeClient() baseReconcileLoop := New(configuration.Configuration{ Client: fakeClient, - }, nil, logf.ZapLogger(false), false, false, nil) + }, logf.ZapLogger(false), false, false, nil) got, err := baseReconcileLoop.validateSecretVolume(volume) @@ -704,7 +704,7 @@ func TestValidateSecretVolume(t *testing.T) { baseReconcileLoop := New(configuration.Configuration{ Client: fakeClient, Jenkins: jenkins, - }, nil, logf.ZapLogger(false), false, false, nil) + }, logf.ZapLogger(false), false, false, nil) got, err := baseReconcileLoop.validateSecretVolume(volume) @@ -728,7 +728,7 @@ func TestValidateSecretVolume(t *testing.T) { baseReconcileLoop := New(configuration.Configuration{ Client: fakeClient, Jenkins: jenkins, - }, nil, logf.ZapLogger(false), false, false, nil) + }, logf.ZapLogger(false), false, false, nil) got, err := baseReconcileLoop.validateSecretVolume(volume) assert.NoError(t, err) @@ -752,7 +752,7 @@ func TestValidateCustomization(t *testing.T) { baseReconcileLoop := New(configuration.Configuration{ Jenkins: jenkins, Client: fakeClient, - }, nil, logf.ZapLogger(false), false, false, nil) + }, logf.ZapLogger(false), false, false, nil) got, err := baseReconcileLoop.validateCustomization(customization, "spec.groovyScripts") @@ -774,7 +774,7 @@ func TestValidateCustomization(t *testing.T) { baseReconcileLoop := New(configuration.Configuration{ Jenkins: jenkins, Client: fakeClient, - }, nil, logf.ZapLogger(false), false, false, nil) + }, logf.ZapLogger(false), false, false, nil) err := fakeClient.Create(context.TODO(), secret) require.NoError(t, err) @@ -805,7 +805,7 @@ func TestValidateCustomization(t *testing.T) { baseReconcileLoop := New(configuration.Configuration{ Jenkins: jenkins, Client: fakeClient, - }, nil, logf.ZapLogger(false), false, false, nil) + }, logf.ZapLogger(false), false, false, nil) err := fakeClient.Create(context.TODO(), secret) require.NoError(t, err) err = fakeClient.Create(context.TODO(), configMap) @@ -832,7 +832,7 @@ func TestValidateCustomization(t *testing.T) { baseReconcileLoop := New(configuration.Configuration{ Jenkins: jenkins, Client: fakeClient, - }, nil, logf.ZapLogger(false), false, false, nil) + }, logf.ZapLogger(false), false, false, nil) err := fakeClient.Create(context.TODO(), configMap) require.NoError(t, err) @@ -857,7 +857,7 @@ func TestValidateCustomization(t *testing.T) { baseReconcileLoop := New(configuration.Configuration{ Jenkins: jenkins, Client: fakeClient, - }, nil, logf.ZapLogger(false), false, false, nil) + }, logf.ZapLogger(false), false, false, nil) err := fakeClient.Create(context.TODO(), secret) require.NoError(t, err) diff --git a/pkg/controller/jenkins/configuration/configuration.go b/pkg/controller/jenkins/configuration/configuration.go index efa26962..c4155d4b 100644 --- a/pkg/controller/jenkins/configuration/configuration.go +++ b/pkg/controller/jenkins/configuration/configuration.go @@ -10,9 +10,13 @@ import ( stackerr "github.com/pkg/errors" corev1 "k8s.io/api/core/v1" + "k8s.io/apimachinery/pkg/api/errors" + metav1 "k8s.io/apimachinery/pkg/apis/meta/v1" + "k8s.io/apimachinery/pkg/runtime" "k8s.io/apimachinery/pkg/types" "k8s.io/client-go/kubernetes" "sigs.k8s.io/controller-runtime/pkg/client" + "sigs.k8s.io/controller-runtime/pkg/controller/controllerutil" ) // Configuration holds required for Jenkins configuration @@ -21,6 +25,7 @@ type Configuration struct { ClientSet kubernetes.Clientset Notifications *chan event.Event Jenkins *v1alpha2.Jenkins + Scheme *runtime.Scheme } // RestartJenkinsMasterPod terminate Jenkins master pod and notifies about it @@ -49,3 +54,51 @@ func (c *Configuration) getJenkinsMasterPod() (*corev1.Pod, error) { } return currentJenkinsMasterPod, nil } + +// CreateResource is creating kubernetes resource and references it to Jenkins CR +func (c *Configuration) CreateResource(obj metav1.Object) error { + runtimeObj, ok := obj.(runtime.Object) + if !ok { + return stackerr.Errorf("is not a %T a runtime.Object", obj) + } + + // Set Jenkins instance as the owner and controller + if err := controllerutil.SetControllerReference(c.Jenkins, obj, c.Scheme); err != nil { + return stackerr.WithStack(err) + } + + return c.Client.Create(context.TODO(), runtimeObj) // don't wrap error +} + +// UpdateResource is updating kubernetes resource and references it to Jenkins CR +func (c *Configuration) UpdateResource(obj metav1.Object) error { + runtimeObj, ok := obj.(runtime.Object) + if !ok { + return stackerr.Errorf("is not a %T a runtime.Object", obj) + } + + // set Jenkins instance as the owner and controller, don't check error(can be already set) + _ = controllerutil.SetControllerReference(c.Jenkins, obj, c.Scheme) + + return c.Client.Update(context.TODO(), runtimeObj) // don't wrap error +} + +// CreateOrUpdateResource is creating or updating kubernetes resource and references it to Jenkins CR +func (c *Configuration) CreateOrUpdateResource(obj metav1.Object) error { + runtimeObj, ok := obj.(runtime.Object) + if !ok { + return stackerr.Errorf("is not a %T a runtime.Object", obj) + } + + // set Jenkins instance as the owner and controller, don't check error(can be already set) + _ = controllerutil.SetControllerReference(c.Jenkins, obj, c.Scheme) + + err := c.Client.Create(context.TODO(), runtimeObj) + if err != nil && errors.IsAlreadyExists(err) { + return c.UpdateResource(obj) + } else if err != nil && !errors.IsAlreadyExists(err) { + return stackerr.WithStack(err) + } + + return nil +} diff --git a/pkg/controller/jenkins/jenkins_controller.go b/pkg/controller/jenkins/jenkins_controller.go index ce0493a6..5f6016fe 100644 --- a/pkg/controller/jenkins/jenkins_controller.go +++ b/pkg/controller/jenkins/jenkins_controller.go @@ -214,10 +214,11 @@ func (r *ReconcileJenkins) reconcile(request reconcile.Request, logger logr.Logg ClientSet: r.clientSet, Notifications: r.notificationEvents, Jenkins: jenkins, + Scheme: r.scheme, } // Reconcile base configuration - baseConfiguration := base.New(config, r.scheme, logger, r.local, r.minikube, &r.config) + baseConfiguration := base.New(config, logger, r.local, r.minikube, &r.config) baseMessages, err := baseConfiguration.Validate(jenkins) if err != nil { diff --git a/pkg/controller/jenkins/notifications/reason/reason_test.go b/pkg/controller/jenkins/notifications/reason/reason_test.go index 108e65a6..a923daf6 100644 --- a/pkg/controller/jenkins/notifications/reason/reason_test.go +++ b/pkg/controller/jenkins/notifications/reason/reason_test.go @@ -39,12 +39,12 @@ func TestUndefined_HasMessages(t *testing.T) { podRestart := NewUndefined(KubernetesSource, []string{"test", "another-test"}) assert.True(t, podRestart.HasMessages()) }) - + t.Run("verbose full", func(t *testing.T) { podRestart := NewUndefined(KubernetesSource, []string{}, []string{"test", "another-test"}...) assert.True(t, podRestart.HasMessages()) }) - + t.Run("short empty", func(t *testing.T) { podRestart := NewUndefined(KubernetesSource, []string{}) assert.False(t, podRestart.HasMessages())