diff --git a/cmd/manager/main.go b/cmd/manager/main.go index 8cf7faeb..21c33918 100644 --- a/cmd/manager/main.go +++ b/cmd/manager/main.go @@ -9,6 +9,7 @@ import ( "github.com/jenkinsci/kubernetes-operator/pkg/apis" "github.com/jenkinsci/kubernetes-operator/pkg/controller/jenkins" + "github.com/jenkinsci/kubernetes-operator/pkg/controller/jenkins/constants" "github.com/jenkinsci/kubernetes-operator/pkg/event" "github.com/jenkinsci/kubernetes-operator/pkg/log" "github.com/jenkinsci/kubernetes-operator/version" @@ -17,6 +18,7 @@ import ( "github.com/operator-framework/operator-sdk/pkg/leader" "github.com/operator-framework/operator-sdk/pkg/ready" sdkVersion "github.com/operator-framework/operator-sdk/version" + "github.com/pkg/errors" _ "k8s.io/client-go/plugin/pkg/client/auth/gcp" "sigs.k8s.io/controller-runtime/pkg/client/config" "sigs.k8s.io/controller-runtime/pkg/manager" @@ -37,31 +39,31 @@ func main() { debug := flag.Bool("debug", false, "Set log level to debug") flag.Parse() - log.SetupLogger(debug) + log.SetupLogger(*debug) printInfo() namespace, err := k8sutil.GetWatchNamespace() if err != nil { - fatal(err, "failed to get watch namespace") + fatal(errors.Wrap(err, "failed to get watch namespace"), *debug) } log.Log.Info(fmt.Sprintf("watch namespace: %v", namespace)) // get a config to talk to the apiserver cfg, err := config.GetConfig() if err != nil { - fatal(err, "failed to get config") + fatal(errors.Wrap(err, "failed to get config"), *debug) } // become the leader before proceeding err = leader.Become(context.TODO(), "jenkins-operator-lock") if err != nil { - fatal(err, "failed to become leader") + fatal(errors.Wrap(err, "failed to become leader"), *debug) } r := ready.NewFileReady() err = r.Set() if err != nil { - fatal(err, "failed to get ready.NewFileReady") + fatal(errors.Wrap(err, "failed to get ready.NewFileReady"), *debug) } defer func() { _ = r.Unset() @@ -70,36 +72,40 @@ func main() { // create a new Cmd to provide shared dependencies and start components mgr, err := manager.New(cfg, manager.Options{Namespace: namespace}) if err != nil { - fatal(err, "failed to create manager") + fatal(errors.Wrap(err, "failed to create manager"), *debug) } log.Log.Info("Registering Components.") // setup Scheme for all resources if err := apis.AddToScheme(mgr.GetScheme()); err != nil { - fatal(err, "failed to setup scheme") + fatal(errors.Wrap(err, "failed to setup scheme"), *debug) } // setup events - events, err := event.New(cfg) + events, err := event.New(cfg, constants.OperatorName) if err != nil { - fatal(err, "failed to create manager") + fatal(errors.Wrap(err, "failed to create manager"), *debug) } // setup Jenkins controller if err := jenkins.Add(mgr, *local, *minikube, events); err != nil { - fatal(err, "failed to setup controllers") + fatal(errors.Wrap(err, "failed to setup controllers"), *debug) } log.Log.Info("Starting the Cmd.") // start the Cmd if err := mgr.Start(signals.SetupSignalHandler()); err != nil { - fatal(err, "failed to start cmd") + fatal(errors.Wrap(err, "failed to start cmd"), *debug) } } -func fatal(err error, message string) { - log.Log.Error(err, message) +func fatal(err error, debug bool) { + if debug { + log.Log.Error(nil, fmt.Sprintf("%+v", err)) + } else { + log.Log.Error(nil, fmt.Sprintf("%s", err)) + } os.Exit(-1) } diff --git a/pkg/controller/jenkins/client/jenkins.go b/pkg/controller/jenkins/client/jenkins.go index 79561c42..68808f5b 100644 --- a/pkg/controller/jenkins/client/jenkins.go +++ b/pkg/controller/jenkins/client/jenkins.go @@ -64,20 +64,13 @@ func (jenkins *jenkins) CreateOrUpdateJob(config, jobName string) (job *gojenkin if isNotFoundError(err) { job, err = jenkins.CreateJob(config, jobName) created = true - return + return job, true, errors.WithStack(err) } else if err != nil { - return + return job, false, errors.WithStack(err) } err = job.UpdateConfig(config) - return -} - -func isNotFoundError(err error) bool { - if err != nil { - return err.Error() == errorNotFound.Error() - } - return false + return job, false, errors.WithStack(err) } // BuildJenkinsAPIUrl returns Jenkins API URL @@ -89,7 +82,7 @@ func BuildJenkinsAPIUrl(namespace, serviceName string, portNumber int, local, mi cmd.Stdout = &out err := cmd.Run() if err != nil { - return "", err + return "", errors.WithStack(err) } lines := strings.Split(out.String(), "\n") // First is for http, the second one is for Jenkins slaves communication @@ -135,3 +128,10 @@ func New(url, user, passwordOrToken string) (Jenkins, error) { return jenkinsClient, nil } + +func isNotFoundError(err error) bool { + if err != nil { + return err.Error() == errorNotFound.Error() + } + return false +} diff --git a/pkg/controller/jenkins/configuration/base/reconcile.go b/pkg/controller/jenkins/configuration/base/reconcile.go index b08bdb22..5fdb5020 100644 --- a/pkg/controller/jenkins/configuration/base/reconcile.go +++ b/pkg/controller/jenkins/configuration/base/reconcile.go @@ -16,6 +16,7 @@ import ( "github.com/bndr/gojenkins" "github.com/go-logr/logr" + stackerr "github.com/pkg/errors" corev1 "k8s.io/api/core/v1" "k8s.io/apimachinery/pkg/api/errors" apierrors "k8s.io/apimachinery/pkg/api/errors" @@ -141,7 +142,7 @@ func (r *ReconcileJenkinsBaseConfiguration) ensureResourcesRequiredForJenkinsPod func (r *ReconcileJenkinsBaseConfiguration) verifyPlugins(jenkinsClient jenkinsclient.Jenkins) (bool, error) { allPluginsInJenkins, err := jenkinsClient.GetPlugins(fetchAllPlugins) if err != nil { - return false, err + return false, stackerr.WithStack(err) } var installedPlugins []string @@ -200,9 +201,9 @@ func (r *ReconcileJenkinsBaseConfiguration) createOperatorCredentialsSecret(meta 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) { - return r.createResource(resources.NewOperatorCredentialsSecret(meta, r.jenkins)) + return stackerr.WithStack(r.createResource(resources.NewOperatorCredentialsSecret(meta, r.jenkins))) } else if err != nil && !apierrors.IsNotFound(err) { - return err + return stackerr.WithStack(err) } if found.Data[resources.OperatorCredentialsSecretUserNameKey] != nil && @@ -210,7 +211,7 @@ func (r *ReconcileJenkinsBaseConfiguration) createOperatorCredentialsSecret(meta return nil } - return r.updateResource(resources.NewOperatorCredentialsSecret(meta, r.jenkins)) + return stackerr.WithStack(r.updateResource(resources.NewOperatorCredentialsSecret(meta, r.jenkins))) } func (r *ReconcileJenkinsBaseConfiguration) createScriptsConfigMap(meta metav1.ObjectMeta) error { @@ -218,7 +219,7 @@ func (r *ReconcileJenkinsBaseConfiguration) createScriptsConfigMap(meta metav1.O if err != nil { return err } - return r.createOrUpdateResource(configMap) + return stackerr.WithStack(r.createOrUpdateResource(configMap)) } func (r *ReconcileJenkinsBaseConfiguration) createInitConfigurationConfigMap(meta metav1.ObjectMeta) error { @@ -226,29 +227,26 @@ func (r *ReconcileJenkinsBaseConfiguration) createInitConfigurationConfigMap(met if err != nil { return err } - return r.createOrUpdateResource(configMap) + return stackerr.WithStack(r.createOrUpdateResource(configMap)) } func (r *ReconcileJenkinsBaseConfiguration) createBaseConfigurationConfigMap(meta metav1.ObjectMeta) error { - configMap, err := resources.NewBaseConfigurationConfigMap(meta, r.jenkins) - if err != nil { - return err - } - return r.createOrUpdateResource(configMap) + configMap := resources.NewBaseConfigurationConfigMap(meta, r.jenkins) + 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.GetUserConfigurationConfigMapName(r.jenkins), Namespace: r.jenkins.Namespace}, currentConfigMap) if err != nil && errors.IsNotFound(err) { - return r.k8sClient.Create(context.TODO(), resources.NewUserConfigurationConfigMap(r.jenkins)) + return stackerr.WithStack(r.k8sClient.Create(context.TODO(), resources.NewUserConfigurationConfigMap(r.jenkins))) } else if err != nil { - return err + return stackerr.WithStack(err) } valid := r.verifyLabelsForWatchedResource(currentConfigMap) if !valid { currentConfigMap.ObjectMeta.Labels = resources.BuildLabelsForWatchedResources(r.jenkins) - return r.k8sClient.Update(context.TODO(), currentConfigMap) + return stackerr.WithStack(r.k8sClient.Update(context.TODO(), currentConfigMap)) } return nil @@ -258,19 +256,19 @@ func (r *ReconcileJenkinsBaseConfiguration) createRBAC(meta metav1.ObjectMeta) e serviceAccount := resources.NewServiceAccount(meta) err := r.createResource(serviceAccount) if err != nil && !errors.IsAlreadyExists(err) { - return err + return stackerr.WithStack(err) } role := resources.NewRole(meta) err = r.createOrUpdateResource(role) if err != nil { - return err + return stackerr.WithStack(err) } roleBinding := resources.NewRoleBinding(meta) err = r.createOrUpdateResource(roleBinding) if err != nil { - return err + return stackerr.WithStack(err) } return nil @@ -279,7 +277,7 @@ func (r *ReconcileJenkinsBaseConfiguration) createRBAC(meta metav1.ObjectMeta) e func (r *ReconcileJenkinsBaseConfiguration) createService(meta metav1.ObjectMeta) error { err := r.createResource(resources.NewService(meta, r.minikube)) if err != nil && !apierrors.IsAlreadyExists(err) { - return err + return stackerr.WithStack(err) } return nil @@ -290,7 +288,7 @@ func (r *ReconcileJenkinsBaseConfiguration) getJenkinsMasterPod(meta metav1.Obje currentJenkinsMasterPod := &corev1.Pod{} err := r.k8sClient.Get(context.TODO(), types.NamespacedName{Name: jenkinsMasterPod.Name, Namespace: jenkinsMasterPod.Namespace}, currentJenkinsMasterPod) if err != nil { - return nil, err + return nil, err // don't wrap error } return currentJenkinsMasterPod, nil } @@ -303,16 +301,16 @@ func (r *ReconcileJenkinsBaseConfiguration) ensureJenkinsMasterPod(meta metav1.O r.logger.Info(fmt.Sprintf("Creating a new Jenkins Master Pod %s/%s", jenkinsMasterPod.Namespace, jenkinsMasterPod.Name)) err = r.createResource(jenkinsMasterPod) if err != nil { - return reconcile.Result{}, err + return reconcile.Result{}, stackerr.WithStack(err) } r.jenkins.Status = v1alpha1.JenkinsStatus{} err = r.updateResource(r.jenkins) if err != nil { - return reconcile.Result{}, err + return reconcile.Result{}, err // don't wrap error } return reconcile.Result{}, nil } else if err != nil && !errors.IsNotFound(err) { - return reconcile.Result{}, err + return reconcile.Result{}, stackerr.WithStack(err) } // Recreate pod @@ -357,7 +355,7 @@ func (r *ReconcileJenkinsBaseConfiguration) restartJenkinsMasterPod(meta metav1. if err != nil { return err } - return r.k8sClient.Delete(context.TODO(), currentJenkinsMasterPod) + return stackerr.WithStack(r.k8sClient.Delete(context.TODO(), currentJenkinsMasterPod)) } func (r *ReconcileJenkinsBaseConfiguration) waitForJenkins(meta metav1.ObjectMeta) (reconcile.Result, error) { @@ -397,7 +395,7 @@ func (r *ReconcileJenkinsBaseConfiguration) ensureJenkinsClient(meta metav1.Obje credentialsSecret := &corev1.Secret{} err = r.k8sClient.Get(context.TODO(), types.NamespacedName{Name: resources.GetOperatorCredentialsSecretName(r.jenkins), Namespace: r.jenkins.ObjectMeta.Namespace}, credentialsSecret) if err != nil { - return nil, err + return nil, stackerr.WithStack(err) } currentJenkinsMasterPod, err := r.getJenkinsMasterPod(meta) if err != nil { @@ -437,7 +435,7 @@ func (r *ReconcileJenkinsBaseConfiguration) ensureJenkinsClient(meta metav1.Obje credentialsSecret.Data[resources.OperatorCredentialsSecretTokenCreationKey] = now err = r.updateResource(credentialsSecret) if err != nil { - return nil, err + return nil, stackerr.WithStack(err) } } @@ -459,7 +457,7 @@ func (r *ReconcileJenkinsBaseConfiguration) ensureBaseConfiguration(jenkinsClien namespaceName := types.NamespacedName{Namespace: r.jenkins.Namespace, Name: resources.GetBaseConfigurationConfigMapName(r.jenkins)} err = r.k8sClient.Get(context.TODO(), namespaceName, configuration) if err != nil { - return reconcile.Result{}, err + return reconcile.Result{}, stackerr.WithStack(err) } done, err := groovyClient.EnsureGroovyJob(configuration.Data, r.jenkins) diff --git a/pkg/controller/jenkins/configuration/base/resources.go b/pkg/controller/jenkins/configuration/base/resources.go index 8168bfe2..3d65489d 100644 --- a/pkg/controller/jenkins/configuration/base/resources.go +++ b/pkg/controller/jenkins/configuration/base/resources.go @@ -2,8 +2,8 @@ package base import ( "context" - "fmt" + stackerr "github.com/pkg/errors" "k8s.io/apimachinery/pkg/api/errors" metav1 "k8s.io/apimachinery/pkg/apis/meta/v1" "k8s.io/apimachinery/pkg/runtime" @@ -13,33 +13,33 @@ import ( func (r *ReconcileJenkinsBaseConfiguration) createResource(obj metav1.Object) error { runtimeObj, ok := obj.(runtime.Object) if !ok { - return fmt.Errorf("is not a %T a runtime.Object", obj) + return stackerr.Errorf("is not a %T a runtime.Object", obj) } // Set Jenkins instance as the owner and controller if err := controllerutil.SetControllerReference(r.jenkins, obj, r.scheme); err != nil { - return err + return stackerr.WithStack(err) } - return r.k8sClient.Create(context.TODO(), runtimeObj) + return r.k8sClient.Create(context.TODO(), runtimeObj) // don't wrap error } func (r *ReconcileJenkinsBaseConfiguration) updateResource(obj metav1.Object) error { runtimeObj, ok := obj.(runtime.Object) if !ok { - return fmt.Errorf("is not a %T a runtime.Object", obj) + 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.jenkins, obj, r.scheme) - return r.k8sClient.Update(context.TODO(), runtimeObj) + return r.k8sClient.Update(context.TODO(), runtimeObj) // don't wrap error } func (r *ReconcileJenkinsBaseConfiguration) createOrUpdateResource(obj metav1.Object) error { runtimeObj, ok := obj.(runtime.Object) if !ok { - return fmt.Errorf("is not a %T a runtime.Object", obj) + 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) @@ -49,7 +49,7 @@ func (r *ReconcileJenkinsBaseConfiguration) createOrUpdateResource(obj metav1.Ob if err != nil && errors.IsAlreadyExists(err) { return r.updateResource(obj) } else if err != nil && !errors.IsAlreadyExists(err) { - return err + return stackerr.WithStack(err) } return nil diff --git a/pkg/controller/jenkins/configuration/base/resources/base_configuration_configmap.go b/pkg/controller/jenkins/configuration/base/resources/base_configuration_configmap.go index 9e40defa..e889ae1d 100644 --- a/pkg/controller/jenkins/configuration/base/resources/base_configuration_configmap.go +++ b/pkg/controller/jenkins/configuration/base/resources/base_configuration_configmap.go @@ -167,7 +167,7 @@ func GetBaseConfigurationConfigMapName(jenkins *v1alpha1.Jenkins) string { } // NewBaseConfigurationConfigMap builds Kubernetes config map used to base configuration -func NewBaseConfigurationConfigMap(meta metav1.ObjectMeta, jenkins *v1alpha1.Jenkins) (*corev1.ConfigMap, error) { +func NewBaseConfigurationConfigMap(meta metav1.ObjectMeta, jenkins *v1alpha1.Jenkins) *corev1.ConfigMap { meta.Name = GetBaseConfigurationConfigMapName(jenkins) return &corev1.ConfigMap{ @@ -183,5 +183,5 @@ func NewBaseConfigurationConfigMap(meta metav1.ObjectMeta, jenkins *v1alpha1.Jen jenkins.ObjectMeta.Namespace, GetResourceName(jenkins), HTTPPortInt), "7-configure-views.groovy": configureViews, }, - }, nil + } } diff --git a/pkg/controller/jenkins/configuration/base/resources/render.go b/pkg/controller/jenkins/configuration/base/resources/render.go index 51da4503..ea2cd665 100644 --- a/pkg/controller/jenkins/configuration/base/resources/render.go +++ b/pkg/controller/jenkins/configuration/base/resources/render.go @@ -3,13 +3,15 @@ package resources import ( "bytes" "text/template" + + "github.com/pkg/errors" ) // render executes a parsed template (go-template) with configuration from data func render(template *template.Template, data interface{}) (string, error) { var buffer bytes.Buffer if err := template.Execute(&buffer, data); err != nil { - return "", err + return "", errors.WithStack(err) } return buffer.String(), nil diff --git a/pkg/controller/jenkins/configuration/user/reconcile.go b/pkg/controller/jenkins/configuration/user/reconcile.go index e2fedc87..c9b333dc 100644 --- a/pkg/controller/jenkins/configuration/user/reconcile.go +++ b/pkg/controller/jenkins/configuration/user/reconcile.go @@ -13,6 +13,7 @@ import ( "github.com/jenkinsci/kubernetes-operator/pkg/controller/jenkins/jobs" "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" @@ -73,7 +74,7 @@ func (r *ReconcileUserConfiguration) ensureSeedJobs() (reconcile.Result, error) return reconcile.Result{}, nil } // unexpected error - requeue reconciliation loop - return reconcile.Result{}, err + return reconcile.Result{}, errors.WithStack(err) } // build not finished yet - requeue reconciliation loop with timeout if !done { @@ -94,7 +95,7 @@ func (r *ReconcileUserConfiguration) ensureUserConfiguration(jenkinsClient jenki 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 + return reconcile.Result{}, errors.WithStack(err) } done, err := groovyClient.EnsureGroovyJob(configuration.Data, r.jenkins) diff --git a/pkg/controller/jenkins/configuration/user/validate.go b/pkg/controller/jenkins/configuration/user/validate.go index cdabf729..224d9764 100644 --- a/pkg/controller/jenkins/configuration/user/validate.go +++ b/pkg/controller/jenkins/configuration/user/validate.go @@ -4,13 +4,13 @@ import ( "context" "crypto/x509" "encoding/pem" - "errors" "fmt" "strings" "github.com/jenkinsci/kubernetes-operator/pkg/apis/jenkinsio/v1alpha1" "github.com/jenkinsci/kubernetes-operator/pkg/log" + stackerr "github.com/pkg/errors" "k8s.io/api/core/v1" apierrors "k8s.io/apimachinery/pkg/api/errors" "k8s.io/apimachinery/pkg/types" @@ -55,7 +55,7 @@ func (r *ReconcileUserConfiguration) validateSeedJobs(jenkins *v1alpha1.Jenkins) logger.Info("secret not found") valid = false } else if err != nil { - return false, err + return false, stackerr.WithStack(err) } privateKey := string(deployKeySecret.Data[seedJob.PrivateKey.SecretKeyRef.Key]) @@ -77,17 +77,17 @@ func (r *ReconcileUserConfiguration) validateSeedJobs(jenkins *v1alpha1.Jenkins) func validatePrivateKey(privateKey string) error { block, _ := pem.Decode([]byte(privateKey)) if block == nil { - return errors.New("failed to decode PEM block") + return stackerr.New("failed to decode PEM block") } priv, err := x509.ParsePKCS1PrivateKey(block.Bytes) if err != nil { - return err + return stackerr.WithStack(err) } err = priv.Validate() if err != nil { - return err + return stackerr.WithStack(err) } return nil diff --git a/pkg/controller/jenkins/jenkins_controller.go b/pkg/controller/jenkins/jenkins_controller.go index 09398bf1..6fb2fbca 100644 --- a/pkg/controller/jenkins/jenkins_controller.go +++ b/pkg/controller/jenkins/jenkins_controller.go @@ -13,6 +13,7 @@ import ( "github.com/jenkinsci/kubernetes-operator/pkg/log" "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/api/resource" @@ -57,13 +58,13 @@ func add(mgr manager.Manager, r reconcile.Reconciler) error { // Create a new controller c, err := controller.New("jenkins-controller", mgr, controller.Options{Reconciler: r}) if err != nil { - return err + return errors.WithStack(err) } // Watch for changes to primary resource Jenkins err = c.Watch(&source.Kind{Type: &v1alpha1.Jenkins{}}, &handler.EnqueueRequestForObject{}) if err != nil { - return err + return errors.WithStack(err) } // Watch for changes to secondary resource Pods and requeue the owner Jenkins @@ -72,18 +73,18 @@ func add(mgr manager.Manager, r reconcile.Reconciler) error { OwnerType: &v1alpha1.Jenkins{}, }) if err != nil { - return err + return errors.WithStack(err) } jenkinsHandler := &enqueueRequestForJenkins{} err = c.Watch(&source.Kind{Type: &corev1.Secret{}}, jenkinsHandler) if err != nil { - return err + return errors.WithStack(err) } err = c.Watch(&source.Kind{Type: &corev1.ConfigMap{}}, jenkinsHandler) if err != nil { - return err + return errors.WithStack(err) } return nil @@ -109,7 +110,11 @@ func (r *ReconcileJenkins) Reconcile(request reconcile.Request) (reconcile.Resul logger.V(log.VWarn).Info(err.Error()) return reconcile.Result{Requeue: true}, nil } else if err != nil { - logger.V(log.VWarn).Info(fmt.Sprintf("Reconcile loop failed: %+v", err)) + if log.Debug { + logger.V(log.VWarn).Info(fmt.Sprintf("Reconcile loop failed: %+v", err)) + } else { + logger.V(log.VWarn).Info(fmt.Sprintf("Reconcile loop failed: %s", err)) + } return reconcile.Result{Requeue: true}, nil } return result, nil @@ -127,7 +132,7 @@ func (r *ReconcileJenkins) reconcile(request reconcile.Request, logger logr.Logg return reconcile.Result{}, nil } // Error reading the object - requeue the request. - return reconcile.Result{}, err + return reconcile.Result{}, errors.WithStack(err) } err = r.setDefaults(jenkins, logger) @@ -161,7 +166,7 @@ func (r *ReconcileJenkins) reconcile(request reconcile.Request, logger logr.Logg jenkins.Status.BaseConfigurationCompletedTime = &now err = r.client.Update(context.TODO(), jenkins) if err != nil { - return reconcile.Result{}, err + return reconcile.Result{}, errors.WithStack(err) } logger.Info("Base configuration phase is complete") r.events.Emit(jenkins, event.TypeNormal, reasonBaseConfigurationSuccess, "Base configuration completed") @@ -192,7 +197,7 @@ func (r *ReconcileJenkins) reconcile(request reconcile.Request, logger logr.Logg jenkins.Status.UserConfigurationCompletedTime = &now err = r.client.Update(context.TODO(), jenkins) if err != nil { - return reconcile.Result{}, err + return reconcile.Result{}, errors.WithStack(err) } logger.Info("User configuration phase is complete") r.events.Emit(jenkins, event.TypeNormal, reasonUserConfigurationSuccess, "User configuration completed") @@ -241,7 +246,7 @@ func (r *ReconcileJenkins) setDefaults(jenkins *v1alpha1.Jenkins, logger logr.Lo } if changed { - return r.client.Update(context.TODO(), jenkins) + return errors.WithStack(r.client.Update(context.TODO(), jenkins)) } return nil } diff --git a/pkg/controller/jenkins/jobs/jobs.go b/pkg/controller/jenkins/jobs/jobs.go index e8cc124a..4795680d 100644 --- a/pkg/controller/jenkins/jobs/jobs.go +++ b/pkg/controller/jenkins/jobs/jobs.go @@ -2,7 +2,6 @@ package jobs import ( "context" - "errors" "fmt" "strings" @@ -11,21 +10,22 @@ import ( "github.com/jenkinsci/kubernetes-operator/pkg/log" "github.com/go-logr/logr" + "github.com/pkg/errors" metav1 "k8s.io/apimachinery/pkg/apis/meta/v1" k8s "sigs.k8s.io/controller-runtime/pkg/client" ) var ( // ErrorUnexpectedBuildStatus - this is custom error returned when jenkins build has unexpected status - ErrorUnexpectedBuildStatus = errors.New("unexpected build status") + ErrorUnexpectedBuildStatus = fmt.Errorf("unexpected build status") // ErrorBuildFailed - this is custom error returned when jenkins build has failed - ErrorBuildFailed = errors.New("build failed") + ErrorBuildFailed = fmt.Errorf("build failed") // ErrorAbortBuildFailed - this is custom error returned when jenkins build couldn't be aborted - ErrorAbortBuildFailed = errors.New("build abort failed") + ErrorAbortBuildFailed = fmt.Errorf("build abort failed") // ErrorUnrecoverableBuildFailed - this is custom error returned when jenkins build has failed and cannot be recovered - ErrorUnrecoverableBuildFailed = errors.New("build failed and cannot be recovered") + ErrorUnrecoverableBuildFailed = fmt.Errorf("build failed and cannot be recovered") // ErrorNotFound - this is error returned when jenkins build couldn't be found - ErrorNotFound = errors.New("404") + ErrorNotFound = fmt.Errorf("404") // BuildRetires - determines max amount of retires for failed build BuildRetires = 3 ) @@ -54,11 +54,7 @@ func New(jenkinsClient client.Jenkins, k8sClient k8s.Client, logger logr.Logger) func (jobs *Jobs) EnsureBuildJob(jobName, hash string, parameters map[string]string, jenkins *v1alpha1.Jenkins, preserveStatus bool) (done bool, err error) { jobs.logger.V(log.VDebug).Info(fmt.Sprintf("Ensuring build, name:'%s' hash:'%s'", jobName, hash)) - build, err := jobs.getBuildFromStatus(jobName, hash, jenkins) - if err != nil { - return false, err - } - + build := jobs.getBuildFromStatus(jobName, hash, jenkins) if build != nil { jobs.logger.V(log.VDebug).Info(fmt.Sprintf("Build exists in status, %+v", build)) switch build.Status { @@ -86,16 +82,16 @@ func (jobs *Jobs) EnsureBuildJob(jobName, hash string, parameters map[string]str return jobs.buildJob(newBuild, parameters, jenkins) } -func (jobs *Jobs) getBuildFromStatus(jobName string, hash string, jenkins *v1alpha1.Jenkins) (*v1alpha1.Build, error) { +func (jobs *Jobs) getBuildFromStatus(jobName string, hash string, jenkins *v1alpha1.Jenkins) *v1alpha1.Build { if jenkins != nil { builds := jenkins.Status.Builds for _, build := range builds { if build.JobName == jobName && build.Hash == hash { - return &build, nil + return &build } } } - return nil, nil + return nil } func (jobs *Jobs) ensureSuccessBuild(build v1alpha1.Build, jenkins *v1alpha1.Jenkins, preserveStatus bool) (bool, error) { @@ -103,7 +99,6 @@ func (jobs *Jobs) ensureSuccessBuild(build v1alpha1.Build, jenkins *v1alpha1.Jen if !preserveStatus { err := jobs.removeBuildFromStatus(build, jenkins) - jobs.logger.V(log.VDebug).Info(fmt.Sprintf("Removing build from status, %+v", build)) if err != nil { jobs.logger.V(log.VWarn).Info(fmt.Sprintf("Couldn't remove build from status, %+v", build)) return false, err @@ -122,7 +117,7 @@ func (jobs *Jobs) ensureRunningBuild(build v1alpha1.Build, jenkins *v1alpha1.Jen return false, nil } else if err != nil { jobs.logger.V(log.VWarn).Info(fmt.Sprintf("Couldn't get jenkins build, %+v", build)) - return false, err + return false, errors.WithStack(err) } if jenkinsBuild.GetResult() != "" { @@ -166,7 +161,6 @@ func (jobs *Jobs) ensureFailedBuild(build v1alpha1.Build, jenkins *v1alpha1.Jenk jobs.logger.V(log.VWarn).Info(fmt.Sprintf("The retries limit was reached , %+v", build)) if !preserveStatus { - jobs.logger.V(log.VDebug).Info(fmt.Sprintf("Removing build from status, %+v", build)) err := jobs.removeBuildFromStatus(build, jenkins) if err != nil { jobs.logger.V(log.VWarn).Info(fmt.Sprintf("Couldn't remove build from status, %+v", build)) @@ -181,17 +175,17 @@ func (jobs *Jobs) ensureExpiredBuild(build v1alpha1.Build, jenkins *v1alpha1.Jen jenkinsBuild, err := jobs.jenkinsClient.GetBuild(build.JobName, build.Number) if err != nil { - return false, err + return false, errors.WithStack(err) } _, err = jenkinsBuild.Stop() if err != nil { - return false, err + return false, errors.WithStack(err) } jenkinsBuild, err = jobs.jenkinsClient.GetBuild(build.JobName, build.Number) if err != nil { - return false, err + return false, errors.WithStack(err) } if v1alpha1.BuildStatus(jenkinsBuild.GetResult()) != v1alpha1.BuildAbortedStatus { @@ -206,7 +200,6 @@ func (jobs *Jobs) ensureExpiredBuild(build v1alpha1.Build, jenkins *v1alpha1.Jen // TODO(antoniaklja) clean up k8s resources if !preserveStatus { - jobs.logger.V(log.VDebug).Info(fmt.Sprintf("Removing build from status, %+v", build)) err = jobs.removeBuildFromStatus(build, jenkins) if err != nil { jobs.logger.V(log.VWarn).Info(fmt.Sprintf("Couldn't remove build from status, %+v", build)) @@ -218,6 +211,7 @@ func (jobs *Jobs) ensureExpiredBuild(build v1alpha1.Build, jenkins *v1alpha1.Jen } func (jobs *Jobs) removeBuildFromStatus(build v1alpha1.Build, jenkins *v1alpha1.Jenkins) error { + jobs.logger.V(log.VDebug).Info(fmt.Sprintf("Removing build from status, %+v", build)) builds := make([]v1alpha1.Build, len(jenkins.Status.Builds)) for _, existingBuild := range jenkins.Status.Builds { if existingBuild.JobName != build.JobName && existingBuild.Hash != build.Hash { @@ -227,7 +221,7 @@ func (jobs *Jobs) removeBuildFromStatus(build v1alpha1.Build, jenkins *v1alpha1. jenkins.Status.Builds = builds err := jobs.k8sClient.Update(context.TODO(), jenkins) if err != nil { - return err + return err // don't wrap because apierrors.IsConflict(err) won't work in jenkins_controller } return nil @@ -238,7 +232,7 @@ func (jobs *Jobs) buildJob(build v1alpha1.Build, parameters map[string]string, j job, err := jobs.jenkinsClient.GetJob(build.JobName) if err != nil { jobs.logger.V(log.VWarn).Info(fmt.Sprintf("Couldn't find jenkins job, %+v", build)) - return false, err + return false, errors.WithStack(err) } nextBuildNumber := job.GetDetails().NextBuildNumber @@ -246,7 +240,7 @@ func (jobs *Jobs) buildJob(build v1alpha1.Build, parameters map[string]string, j _, err = jobs.jenkinsClient.BuildJob(build.JobName, parameters) if err != nil { jobs.logger.V(log.VWarn).Info(fmt.Sprintf("Couldn't run build, %+v", build)) - return false, err + return false, errors.WithStack(err) } build.Status = v1alpha1.BuildRunningStatus @@ -281,7 +275,7 @@ func (jobs *Jobs) updateBuildStatus(build v1alpha1.Build, jenkins *v1alpha1.Jenk } err := jobs.k8sClient.Update(context.TODO(), jenkins) if err != nil { - return err + return err // don't wrap because apierrors.IsConflict(err) won't work in jenkins_controller } return nil diff --git a/pkg/controller/jenkins/plugins/plugin.go b/pkg/controller/jenkins/plugins/plugin.go index 6f56e098..12d7368a 100644 --- a/pkg/controller/jenkins/plugins/plugin.go +++ b/pkg/controller/jenkins/plugins/plugin.go @@ -5,6 +5,8 @@ import ( "strings" "github.com/jenkinsci/kubernetes-operator/pkg/log" + + "github.com/pkg/errors" ) // Plugin represents jenkins plugin @@ -22,7 +24,7 @@ func (p Plugin) String() string { func New(nameWithVersion string) (*Plugin, error) { val := strings.SplitN(nameWithVersion, ":", 2) if val == nil || len(val) != 2 { - return nil, fmt.Errorf("invalid plugin format '%s'", nameWithVersion) + return nil, errors.Errorf("invalid plugin format '%s'", nameWithVersion) } return &Plugin{ Name: val[0], diff --git a/pkg/controller/jenkins/plugins/plugin_test.go b/pkg/controller/jenkins/plugins/plugin_test.go index d56915da..0809acc5 100644 --- a/pkg/controller/jenkins/plugins/plugin_test.go +++ b/pkg/controller/jenkins/plugins/plugin_test.go @@ -9,8 +9,7 @@ import ( ) func TestVerifyDependencies(t *testing.T) { - debug := false - log.SetupLogger(&debug) + log.SetupLogger(false) t.Run("happy, single root plugin with one dependent plugin", func(t *testing.T) { basePlugins := map[Plugin][]Plugin{ diff --git a/pkg/event/event.go b/pkg/event/event.go index 768890fc..6e544251 100644 --- a/pkg/event/event.go +++ b/pkg/event/event.go @@ -3,8 +3,7 @@ package event import ( "fmt" - "github.com/jenkinsci/kubernetes-operator/pkg/controller/jenkins/constants" - + "github.com/pkg/errors" "k8s.io/api/core/v1" "k8s.io/apimachinery/pkg/runtime" "k8s.io/client-go/kubernetes" @@ -38,8 +37,8 @@ type recorder struct { } // New returns recorder used to emit events -func New(config *rest.Config) (Recorder, error) { - eventRecorder, err := initializeEventRecorder(config) +func New(config *rest.Config, component string) (Recorder, error) { + eventRecorder, err := initializeEventRecorder(config, component) if err != nil { return nil, err } @@ -49,10 +48,10 @@ func New(config *rest.Config) (Recorder, error) { }, nil } -func initializeEventRecorder(config *rest.Config) (record.EventRecorder, error) { +func initializeEventRecorder(config *rest.Config, component string) (record.EventRecorder, error) { client, err := kubernetes.NewForConfig(config) if err != nil { - return nil, err + return nil, errors.WithStack(err) } eventBroadcaster := record.NewBroadcaster() //eventBroadcaster.StartLogging(glog.Infof) TODO integrate with proper logger @@ -61,8 +60,8 @@ func initializeEventRecorder(config *rest.Config) (record.EventRecorder, error) Interface: client.CoreV1().Events("")}) eventRecorder := eventBroadcaster.NewRecorder( scheme.Scheme, - v1.EventSource{ - Component: constants.OperatorName}) + v1.EventSource{Component: component}, + ) return eventRecorder, nil } diff --git a/pkg/log/log.go b/pkg/log/log.go index 3f16e90e..db4a4f09 100644 --- a/pkg/log/log.go +++ b/pkg/log/log.go @@ -1,12 +1,19 @@ package log import ( - "sigs.k8s.io/controller-runtime/pkg/runtime/log" + "log" + + "github.com/go-logr/logr" + "github.com/go-logr/zapr" + "go.uber.org/zap" logf "sigs.k8s.io/controller-runtime/pkg/runtime/log" ) // Log represents global logger -var Log = log.Log.WithName("controller-jenkins") +var Log = logf.Log.WithName("controller-jenkins") + +// Debug indicates that debug level is set +var Debug bool const ( // VWarn defines warning log level @@ -15,8 +22,30 @@ const ( VDebug = 1 ) -// SetupLogger setups global logger -func SetupLogger(development *bool) { - logf.SetLogger(logf.ZapLogger(*development)) - Log = log.Log.WithName("controller-jenkins") +func zapLogger(debug bool) logr.Logger { + var zapLog *zap.Logger + var err error + zapLogCfg := zap.NewDevelopmentConfig() + if debug { + zapLogCfg.Level = zap.NewAtomicLevelAt(zap.DebugLevel) + } else { + zapLogCfg.Level = zap.NewAtomicLevelAt(zap.InfoLevel) + } + zapLog, err = zapLogCfg.Build(zap.AddStacktrace(zap.DPanicLevel), zap.AddCallerSkip(1)) + // who watches the watchmen? + fatalIfErr(err, log.Fatalf) + return zapr.NewLogger(zapLog) +} + +func fatalIfErr(err error, f func(format string, v ...interface{})) { + if err != nil { + f("unable to construct the logger: %v", err) + } +} + +// SetupLogger setups global logger +func SetupLogger(debug bool) { + Debug = debug + logf.SetLogger(zapLogger(debug)) + Log = logf.Log.WithName("controller-jenkins") }