From 7f39f0c039340a67f5ce3b5d74a13613f34ccfd9 Mon Sep 17 00:00:00 2001 From: Amanda Kaufman Date: Wed, 25 Sep 2019 12:05:38 -0600 Subject: [PATCH] Added error handling for case where SQL server instance not found --- api/v1/sqlaction_types.go | 3 +- .../bases/azure.microsoft.com_sqlactions.yaml | 2 + controllers/sqlaction_controller.go | 120 +++++++++++------- pkg/errhelp/errhelp.go | 5 + 4 files changed, 82 insertions(+), 48 deletions(-) diff --git a/api/v1/sqlaction_types.go b/api/v1/sqlaction_types.go index a9200b8dfed..39321fd9369 100644 --- a/api/v1/sqlaction_types.go +++ b/api/v1/sqlaction_types.go @@ -37,10 +37,11 @@ type SqlActionStatus struct { // Important: Run "make" to regenerate code after modifying this file Provisioning bool `json:"provisioning,omitempty"` Provisioned bool `json:"provisioned,omitempty"` - State string `json:"state,omitempty"` + Message string `json:"state,omitempty"` } // +kubebuilder:object:root=true +// +kubebuilder:subresource:status // SqlAction is the Schema for the sqlactions API type SqlAction struct { diff --git a/config/crd/bases/azure.microsoft.com_sqlactions.yaml b/config/crd/bases/azure.microsoft.com_sqlactions.yaml index d2ee2402090..27d94ae713e 100644 --- a/config/crd/bases/azure.microsoft.com_sqlactions.yaml +++ b/config/crd/bases/azure.microsoft.com_sqlactions.yaml @@ -11,6 +11,8 @@ spec: kind: SqlAction plural: sqlactions scope: "" + subresources: + status: {} validation: openAPIV3Schema: description: SqlAction is the Schema for the sqlactions API diff --git a/controllers/sqlaction_controller.go b/controllers/sqlaction_controller.go index 97fdafff696..508485dc5ab 100644 --- a/controllers/sqlaction_controller.go +++ b/controllers/sqlaction_controller.go @@ -101,6 +101,14 @@ func (r *SqlActionReconciler) Reconcile(req ctrl.Request) (ctrl.Result, error) { Requeue: true, RequeueAfter: time.Second * time.Duration(requeueAfter), }, nil + } else if errhelp.IsResourceNotFound(err) { + log.Info("Not requeueing as a specified resource (such as Sql Server instance) was not found") + instance.Status.Message = "Resource not found error" + // write information back to instance + if updateerr := r.Status().Update(ctx, &instance); updateerr != nil { + r.Recorder.Event(&instance, "Warning", "Failed", "Unable to update instance") + } + return ctrl.Result{}, nil } return ctrl.Result{}, fmt.Errorf("error reconciling sqlaction in azure: %v", err) } @@ -131,6 +139,11 @@ func (r *SqlActionReconciler) reconcileExternal(instance *azurev1.SqlAction) err if instance.Status.Provisioned != true { instance.Status.Provisioning = true + instance.Status.Message = "SqlAction in progress" + // write information back to instance + if updateerr := r.Status().Update(ctx, instance); updateerr != nil { + r.Recorder.Event(instance, "Warning", "Failed", "Unable to update instance") + } sdkClient := sql.GoSDKClient{ Ctx: ctx, @@ -140,7 +153,7 @@ func (r *SqlActionReconciler) reconcileExternal(instance *azurev1.SqlAction) err sqlActionNamespacedName := types.NamespacedName{Name: name, Namespace: namespace} if err := r.Get(ctx, sqlActionNamespacedName, instance); err != nil { - r.Log.Info("Unable to retrieve resource", "err", err.Error()) + r.Log.Info("Unable to retrieve SqlAction resource", "err", err.Error()) // we'll ignore not-found errors, since they can't be fixed by an immediate // requeue (we'll need to wait for a new notification), and we can get them // on deleted requests. @@ -148,69 +161,82 @@ func (r *SqlActionReconciler) reconcileExternal(instance *azurev1.SqlAction) err } // Get the Sql Server instance that corresponds to the Server name in the spec for this action - server, err := sdkClient.GetServer(groupName, serverName) + server, err := sdkClient.GetServer() if err != nil { //log error and kill it, as the parent might not exist in the cluster. It could have been created elsewhere or through the portal directly r.Recorder.Event(instance, "Warning", "Failed", "Unable to get instance of SqlServer") + r.Log.Info("Error", "Sql Server instance not found", err) + instance.Status.Message = "Sql Server instance not found" return err } sdkClient.Location = *server.Location - sqlServerProperties := sql.SQLServerProperties{ - AdministratorLogin: server.ServerProperties.AdministratorLogin, - AdministratorLoginPassword: server.ServerProperties.AdministratorLoginPassword, - } - sqlServerProperties.AdministratorLoginPassword = to.StringPtr(RollCreds(16)) + if instance.Spec.ActionName == "rollcreds" { + sqlServerProperties := sql.SQLServerProperties{ + AdministratorLogin: server.ServerProperties.AdministratorLogin, + AdministratorLoginPassword: server.ServerProperties.AdministratorLoginPassword, + } - //debugging - r.Log.Info("Info", "Username: ", *sqlServerProperties.AdministratorLogin) - r.Log.Info("Info", "New Password: ", *sqlServerProperties.AdministratorLoginPassword) + sqlServerProperties.AdministratorLoginPassword = to.StringPtr(RollCreds(16)) - if _, err := sdkClient.CreateOrUpdateSQLServer(sqlServerProperties); err != nil { - if !strings.Contains(err.Error(), "not complete") { - r.Recorder.Event(instance, "Warning", "Failed", "Unable to provision or update instance") - return errhelp.NewAzureError(err) + //debugging + r.Log.Info("Info", "Username: ", *sqlServerProperties.AdministratorLogin) + r.Log.Info("Info", "New Password: ", *sqlServerProperties.AdministratorLoginPassword) + + if _, err := sdkClient.CreateOrUpdateSQLServer(sqlServerProperties); err != nil { + if !strings.Contains(err.Error(), "not complete") { + r.Recorder.Event(instance, "Warning", "Failed", "Unable to provision or update instance") + return errhelp.NewAzureError(err) + } + } else { + r.Recorder.Event(instance, "Normal", "Provisioned", "resource request successfully submitted to Azure") } - } else { - r.Recorder.Event(instance, "Normal", "Provisioned", "resource request successfully submitted to Azure") - } - // Update the k8s secret - secret := &v1.Secret{ - ObjectMeta: metav1.ObjectMeta{ - Name: serverName, - Namespace: namespace, - }, - Data: map[string][]byte{ - "username": []byte(*sqlServerProperties.AdministratorLogin), - "password": []byte(*sqlServerProperties.AdministratorLoginPassword), - "sqlservernamespace": []byte(instance.Namespace), - "sqlservername": []byte(name), - }, - Type: "Opaque", - } + // Update the k8s secret + secret := &v1.Secret{ + ObjectMeta: metav1.ObjectMeta{ + Name: serverName, + Namespace: namespace, + }, + Data: map[string][]byte{ + "username": []byte(*sqlServerProperties.AdministratorLogin), + "password": []byte(*sqlServerProperties.AdministratorLoginPassword), + "sqlservernamespace": []byte(instance.Namespace), + "sqlservername": []byte(name), + }, + Type: "Opaque", + } - result, createOrUpdateSecretErr := controllerutil.CreateOrUpdate(context.Background(), r.Client, secret, func() error { - r.Log.Info("mutating secret bundle") - secret.Data["password"] = []byte(*sqlServerProperties.AdministratorLoginPassword) - return nil - }) - if createOrUpdateSecretErr != nil { - r.Log.Info("Error", "CreateOrUpdateSecretErr", createOrUpdateSecretErr) - return createOrUpdateSecretErr - } + result, createOrUpdateSecretErr := controllerutil.CreateOrUpdate(context.Background(), r.Client, secret, func() error { + r.Log.Info("mutating secret bundle") + secret.Data["password"] = []byte(*sqlServerProperties.AdministratorLoginPassword) + return nil + }) + if createOrUpdateSecretErr != nil { + r.Log.Info("Error", "CreateOrUpdateSecretErr", createOrUpdateSecretErr) + return createOrUpdateSecretErr + } - // log result for debugging - r.Log.Info("Info", "OperationResult", result) + // log result for debugging + r.Log.Info("Info", "OperationResult", result) - instance.Status.Provisioning = false - instance.Status.Provisioned = true + instance.Status.Provisioning = false + instance.Status.Provisioned = true + instance.Status.Message = "SqlAction completed successfully." - // write information back to instance - if updateerr := r.Update(ctx, instance); updateerr != nil { - r.Recorder.Event(instance, "Warning", "Failed", "Unable to update instance") + // write information back to instance + if updateerr := r.Status().Update(ctx, instance); updateerr != nil { + r.Recorder.Event(instance, "Warning", "Failed", "Unable to update instance") + } + + // write information back to instance + if updateerr := r.Update(ctx, instance); updateerr != nil { + r.Recorder.Event(instance, "Warning", "Failed", "Unable to update instance") + } } + + // Add other actions here (instance.Spec.ActionName) } return nil diff --git a/pkg/errhelp/errhelp.go b/pkg/errhelp/errhelp.go index 5952a219f21..d90e04ffe90 100644 --- a/pkg/errhelp/errhelp.go +++ b/pkg/errhelp/errhelp.go @@ -28,3 +28,8 @@ func IsAsynchronousOperationNotComplete(err error) bool { func IsStatusCode204(err error) bool { return strings.Contains(err.Error(), "StatusCode=204") } + +// IsResourceNotFound checks if error reports that a referenced resource is not found +func IsResourceNotFound(err error) bool { + return strings.Contains(err.Error(), "ResourceNotFound") +}