Visualization operator - update/delete datasource
[demo.git] / vnfs / DAaaS / microservices / visualization-operator / pkg / controller / grafanadatasource / grafanadatasource_controller.go
index f46cf1b..88b8a92 100644 (file)
@@ -1,11 +1,16 @@
 package grafanadatasource
 
 import (
+       logr "github.com/go-logr/logr"
+
        "bytes"
        "context"
        "encoding/json"
+       "fmt"
+       "io/ioutil"
 
        onapv1alpha1 "visualization-operator/pkg/apis/onap/v1alpha1"
+       visualizationutils "visualization-operator/pkg/controller/utils"
 
        "k8s.io/apimachinery/pkg/api/errors"
        "k8s.io/apimachinery/pkg/runtime"
@@ -77,13 +82,51 @@ func (r *ReconcileGrafanaDataSource) Reconcile(request reconcile.Request) (recon
                        // Request object not found, could have been deleted after reconcile request.
                        // Owned objects are automatically garbage collected. For additional cleanup logic use finalizers.
                        // Return and don't requeue
+                       reqLogger.Info("GrafanaDatasource object not found")
                        return reconcile.Result{}, nil
                }
                // Error reading the object - requeue the request.
-
+               reqLogger.Info("Error reading the Grafanadatasource object, Requeing")
                return reconcile.Result{}, err
        }
 
+       //Check if deletion timestamp is set. If yes, delete the GrafanaDataSource object
+       isBeingDeleted := checkDeletionTimestamp(reqLogger, instance)
+       if isBeingDeleted {
+               //Delete the datasource from grafana
+               err := deleteDatasource(instance)
+               if err != nil {
+                       reqLogger.Error(err, "Unable to delete datasource")
+                       return reconcile.Result{}, err
+               }
+               //remove Finalizer after deletion
+               if visualizationutils.Contains(instance.GetFinalizers(), visualizationutils.VisualizationFinalizer) {
+                       if err := removeFinalizer(reqLogger, instance); err != nil {
+                               return reconcile.Result{}, err
+                       }
+                       err := r.client.Update(context.TODO(), instance)
+                       if err != nil {
+                               reqLogger.Error(err, "Unable to update instance")
+                               return reconcile.Result{}, err
+                       }
+                       return reconcile.Result{}, nil
+               }
+       }
+
+       //Add finalizer for the CR object
+       if !visualizationutils.Contains(instance.GetFinalizers(), visualizationutils.VisualizationFinalizer) {
+               reqLogger.Info("Adding finalizer for GrafanaDatasource")
+               if err := addFinalizer(reqLogger, instance); err != nil {
+                       return reconcile.Result{}, err
+               }
+               err := r.client.Update(context.TODO(), instance)
+               if err != nil {
+                       reqLogger.Error(err, "Unable to update instance")
+                       return reconcile.Result{}, err
+               }
+               return reconcile.Result{}, nil
+       }
+
        datasources := instance.Spec.Datasources
        grafana := instance.Spec.Grafana
 
@@ -110,6 +153,25 @@ func (r *ReconcileGrafanaDataSource) Reconcile(request reconcile.Request) (recon
                        return reconcile.Result{}, err
                }
 
+               respBody, err := ioutil.ReadAll(getResp.Body)
+               if err != nil {
+                       reqLogger.Error(err, "Response data not read properly")
+                       return reconcile.Result{}, err
+               }
+
+               respBodyBytes := []byte(respBody)
+               var respData map[string]interface{}
+
+               if err := json.Unmarshal(respBodyBytes, &respData); err != nil {
+                       reqLogger.Error(err, "JSON unmarshalling error")
+                       return reconcile.Result{}, err
+               }
+
+               respURL := fmt.Sprintf("%v", respData["url"])
+               respID := fmt.Sprintf("%v", respData["id"])
+               respIsDefault := respData["isDefault"]
+               respAccess := fmt.Sprintf("%v", respData["access"])
+
                defer getResp.Body.Close()
 
                //add datasource if datasource does not exist already
@@ -120,19 +182,24 @@ func (r *ReconcileGrafanaDataSource) Reconcile(request reconcile.Request) (recon
                                return reconcile.Result{}, err
                        }
                } else if getResp.StatusCode == http.StatusOK {
-                       //if datasource already exists
+                       //if datasource already exists and there is any change in the spec - update it
                        reqLogger.V(1).Info("datasource already exists", "datasource", datasource.Name)
-               } else {
-                       reqLogger.Error(err, "unknown error", datasource.Name)
+                       if respURL != datasource.URL || respIsDefault.(bool) != datasource.IsDefault || respAccess != datasource.Access {
+                               if err := updateDatasource(grafana, datasource, respID); err != nil {
+                                       return reconcile.Result{}, err
+                               }
+                       } else {
+                               reqLogger.Info("No creation/updation of datasource needed")
+                               return reconcile.Result{}, nil
+                       }
                }
-
        }
        return reconcile.Result{}, nil
 }
 
 func createDataSource(grafana map[string]string, datasource onapv1alpha1.Datasource) error {
        reqLogger := log.WithValues("Datasource name", datasource.Name, "Datasource URL", datasource.URL)
-       reqLogger.Info("creating datasource")
+       reqLogger.Info("Creating datasource")
 
        grafanaURL := grafana["url"] + "/api/datasources"
        grafanaUsername := grafana["username"]
@@ -145,19 +212,19 @@ func createDataSource(grafana map[string]string, datasource onapv1alpha1.Datasou
                return err
        }
 
-       req, err := http.NewRequest("POST", grafanaURL, bytes.NewBuffer(postBody))
+       postReq, err := http.NewRequest("POST", grafanaURL, bytes.NewBuffer(postBody))
        if err != nil {
                reqLogger.Error(err, "POST REQUEST error")
                return err
        }
-       req.Header.Set("Content-Type", "application/json")
-       req.SetBasicAuth(grafanaUsername, grafanaPassword)
-       postResp, err := client.Do(req)
+       postReq.Header.Set("Content-Type", "application/json")
+       postReq.SetBasicAuth(grafanaUsername, grafanaPassword)
+       postResp, err := client.Do(postReq)
        if err != nil {
                reqLogger.Error(err, "POST RESPONSE error")
                return err
        }
-       defer req.Body.Close()
+       defer postReq.Body.Close()
 
        if postResp.StatusCode == http.StatusOK {
                reqLogger.Info("Datasource created")
@@ -165,3 +232,95 @@ func createDataSource(grafana map[string]string, datasource onapv1alpha1.Datasou
        }
        return err
 }
+
+func updateDatasource(grafana map[string]string, datasource onapv1alpha1.Datasource, datasourceID string) error {
+       reqLogger := log.WithValues("Datasource name", datasource.Name, "Datasource URL", datasource.URL)
+       reqLogger.Info("Updating datasource")
+
+       grafanaURL := grafana["url"] + "/api/datasources/" + datasourceID
+       grafanaUsername := grafana["username"]
+       grafanaPassword := grafana["password"]
+
+       client := &http.Client{}
+       putBody, err := json.Marshal(datasource)
+       if err != nil {
+               reqLogger.Error(err, "JSON Marshalling error")
+               return err
+       }
+       putReq, err := http.NewRequest("PUT", grafanaURL, bytes.NewBuffer(putBody))
+       if err != nil {
+               reqLogger.Error(err, "PUT REQUEST error")
+               return err
+       }
+       putReq.Header.Set("Content-Type", "application/json")
+       putReq.Header.Set("Accept", "application/json")
+       putReq.SetBasicAuth(grafanaUsername, grafanaPassword)
+
+       putResp, err := client.Do(putReq)
+       if err != nil {
+               reqLogger.Error(err, "PUT RESPONSE error")
+               return err
+       }
+       defer putReq.Body.Close()
+
+       if putResp.StatusCode == http.StatusOK {
+               reqLogger.Info("Datasource updated")
+               return nil
+       }
+       return err
+}
+
+func deleteDatasource(instance *onapv1alpha1.GrafanaDataSource) error {
+
+       datasources := instance.Spec.Datasources
+       grafana := instance.Spec.Grafana
+
+       for _, datasource := range datasources {
+
+               reqLogger := log.WithValues("Datasource name", datasource.Name, "Datasource URL", datasource.URL)
+               reqLogger.Info("Deleting datasource")
+
+               grafanaURL := grafana["url"] + "/api/datasources/name/" + datasource.Name
+               grafanaUsername := grafana["username"]
+               grafanaPassword := grafana["password"]
+
+               client := &http.Client{}
+               deleteReq, err := http.NewRequest("DELETE", grafanaURL, nil)
+               if err != nil {
+                       reqLogger.Error(err, "DELETE request error")
+                       return err
+               }
+
+               deleteReq.SetBasicAuth(grafanaUsername, grafanaPassword)
+
+               deleteResp, err := client.Do(deleteReq)
+               if err != nil {
+                       reqLogger.Error(err, "DELETE RESPONSE error")
+                       return err
+               }
+
+               if deleteResp.StatusCode == http.StatusOK {
+                       reqLogger.Info("Datasource deleted")
+                       return nil
+               }
+               return err
+       }
+       return nil
+}
+
+func checkDeletionTimestamp(reqlogger logr.Logger, instance *onapv1alpha1.GrafanaDataSource) bool {
+       isMarkedForDeletion := instance.GetDeletionTimestamp() != nil
+       return isMarkedForDeletion
+}
+
+func addFinalizer(reqlogger logr.Logger, instance *onapv1alpha1.GrafanaDataSource) error {
+       reqlogger.Info("Adding finalizer for the GrafanaDatasource")
+       instance.SetFinalizers(append(instance.GetFinalizers(), visualizationutils.VisualizationFinalizer))
+       return nil
+}
+
+func removeFinalizer(reqlogger logr.Logger, instance *onapv1alpha1.GrafanaDataSource) error {
+       reqlogger.Info("Removing finalizer for the GrafanaDatasource")
+       instance.SetFinalizers(visualizationutils.Remove(instance.GetFinalizers(), visualizationutils.VisualizationFinalizer))
+       return nil
+}