1 package grafanadatasource
4 logr "github.com/go-logr/logr"
12 onapv1alpha1 "visualization-operator/pkg/apis/onap/v1alpha1"
13 visualizationutils "visualization-operator/pkg/controller/utils"
15 "k8s.io/apimachinery/pkg/api/errors"
16 "k8s.io/apimachinery/pkg/runtime"
18 "sigs.k8s.io/controller-runtime/pkg/client"
19 "sigs.k8s.io/controller-runtime/pkg/controller"
20 "sigs.k8s.io/controller-runtime/pkg/handler"
21 "sigs.k8s.io/controller-runtime/pkg/manager"
22 "sigs.k8s.io/controller-runtime/pkg/reconcile"
23 logf "sigs.k8s.io/controller-runtime/pkg/runtime/log"
24 "sigs.k8s.io/controller-runtime/pkg/source"
27 var log = logf.Log.WithName("controller_grafanadatasource")
29 // Add creates a new GrafanaDataSource Controller and adds it to the Manager. The Manager will set fields on the Controller
30 // and Start it when the Manager is Started.
31 func Add(mgr manager.Manager) error {
32 return add(mgr, newReconciler(mgr))
35 // newReconciler returns a new reconcile.Reconciler
36 func newReconciler(mgr manager.Manager) reconcile.Reconciler {
37 return &ReconcileGrafanaDataSource{client: mgr.GetClient(), scheme: mgr.GetScheme()}
40 // add adds a new Controller to mgr with r as the reconcile.Reconciler
41 func add(mgr manager.Manager, r reconcile.Reconciler) error {
42 // Create a new controller
43 c, err := controller.New("grafanadatasource-controller", mgr, controller.Options{Reconciler: r})
48 // Watch for changes to primary resource GrafanaDataSource
49 err = c.Watch(&source.Kind{Type: &onapv1alpha1.GrafanaDataSource{}}, &handler.EnqueueRequestForObject{})
57 // blank assignment to verify that ReconcileGrafanaDataSource implements reconcile.Reconciler
58 var _ reconcile.Reconciler = &ReconcileGrafanaDataSource{}
60 // ReconcileGrafanaDataSource reconciles a GrafanaDataSource object
61 type ReconcileGrafanaDataSource struct {
62 // This client, initialized using mgr.Client() above, is a split client
63 // that reads objects from the cache and writes to the apiserver
65 scheme *runtime.Scheme
68 // Reconcile reads that state of the cluster for a GrafanaDataSource object and makes changes based on the state read
69 // and what is in the GrafanaDataSource.Spec
71 // The Controller will requeue the Request to be processed again if the returned error is non-nil or
72 // Result.Requeue is true, otherwise upon completion it will remove the work from the queue.
73 func (r *ReconcileGrafanaDataSource) Reconcile(request reconcile.Request) (reconcile.Result, error) {
74 reqLogger := log.WithValues("Request.Namespace", request.Namespace, "Request.Name", request.Name)
75 reqLogger.Info("Reconciling GrafanaDataSource")
77 // Fetch the GrafanaDataSource instance
78 instance := &onapv1alpha1.GrafanaDataSource{}
79 err := r.client.Get(context.TODO(), request.NamespacedName, instance)
81 if errors.IsNotFound(err) {
82 // Request object not found, could have been deleted after reconcile request.
83 // Owned objects are automatically garbage collected. For additional cleanup logic use finalizers.
84 // Return and don't requeue
85 reqLogger.Info("GrafanaDatasource object not found")
86 return reconcile.Result{}, nil
88 // Error reading the object - requeue the request.
89 reqLogger.Info("Error reading the Grafanadatasource object, Requeing")
90 return reconcile.Result{}, err
93 //Check if deletion timestamp is set. If yes, delete the GrafanaDataSource object
94 isBeingDeleted := checkDeletionTimestamp(reqLogger, instance)
96 //Delete the datasource from grafana
97 err := deleteDatasource(instance)
99 reqLogger.Error(err, "Unable to delete datasource")
100 return reconcile.Result{}, err
102 //remove Finalizer after deletion
103 if visualizationutils.Contains(instance.GetFinalizers(), visualizationutils.VisualizationFinalizer) {
104 if err := removeFinalizer(reqLogger, instance); err != nil {
105 return reconcile.Result{}, err
107 err := r.client.Update(context.TODO(), instance)
109 reqLogger.Error(err, "Unable to update instance")
110 return reconcile.Result{}, err
112 return reconcile.Result{}, nil
116 //Add finalizer for the CR object
117 if !visualizationutils.Contains(instance.GetFinalizers(), visualizationutils.VisualizationFinalizer) {
118 reqLogger.Info("Adding finalizer for GrafanaDatasource")
119 if err := addFinalizer(reqLogger, instance); err != nil {
120 return reconcile.Result{}, err
122 err := r.client.Update(context.TODO(), instance)
124 reqLogger.Error(err, "Unable to update instance")
125 return reconcile.Result{}, err
127 return reconcile.Result{}, nil
130 datasources := instance.Spec.Datasources
131 grafana := instance.Spec.Grafana
133 reqLogger.V(1).Info(" Datasource Name ", "datasources", datasources)
135 //loop through all datasources in the spec
136 for _, datasource := range datasources {
138 //check if datasource exists
139 grafanaURL := grafana["url"] + "/api/datasources/name/" + datasource.Name
140 grafanaUsername := grafana["username"]
141 grafanaPassword := grafana["password"]
143 client := &http.Client{}
144 req, err := http.NewRequest("GET", grafanaURL, nil)
146 reqLogger.Error(err, "GET REQUEST error")
147 return reconcile.Result{}, err
149 req.SetBasicAuth(grafanaUsername, grafanaPassword)
150 getResp, err := client.Do(req)
152 reqLogger.Error(err, "GET RESPONSE error")
153 return reconcile.Result{}, err
156 respBody, err := ioutil.ReadAll(getResp.Body)
158 reqLogger.Error(err, "Response data not read properly")
159 return reconcile.Result{}, err
162 respBodyBytes := []byte(respBody)
163 var respData map[string]interface{}
165 if err := json.Unmarshal(respBodyBytes, &respData); err != nil {
166 reqLogger.Error(err, "JSON unmarshalling error")
167 return reconcile.Result{}, err
170 respURL := fmt.Sprintf("%v", respData["url"])
171 respID := fmt.Sprintf("%v", respData["id"])
172 respIsDefault := respData["isDefault"]
173 respAccess := fmt.Sprintf("%v", respData["access"])
175 defer getResp.Body.Close()
177 //add datasource if datasource does not exist already
178 if getResp.StatusCode == http.StatusNotFound {
179 reqLogger.Info("Datasource does not exist, creating one...")
181 if err := createDataSource(grafana, datasource); err != nil {
182 return reconcile.Result{}, err
184 } else if getResp.StatusCode == http.StatusOK {
185 //if datasource already exists and there is any change in the spec - update it
186 reqLogger.V(1).Info("datasource already exists", "datasource", datasource.Name)
187 if respURL != datasource.URL || respIsDefault.(bool) != datasource.IsDefault || respAccess != datasource.Access {
188 if err := updateDatasource(grafana, datasource, respID); err != nil {
189 return reconcile.Result{}, err
192 reqLogger.Info("No creation/updation of datasource needed")
193 return reconcile.Result{}, nil
197 return reconcile.Result{}, nil
200 func createDataSource(grafana map[string]string, datasource onapv1alpha1.Datasource) error {
201 reqLogger := log.WithValues("Datasource name", datasource.Name, "Datasource URL", datasource.URL)
202 reqLogger.Info("Creating datasource")
204 grafanaURL := grafana["url"] + "/api/datasources"
205 grafanaUsername := grafana["username"]
206 grafanaPassword := grafana["password"]
208 client := &http.Client{}
209 postBody, err := json.Marshal(datasource)
211 reqLogger.Error(err, "JSON Marshalling error")
215 postReq, err := http.NewRequest("POST", grafanaURL, bytes.NewBuffer(postBody))
217 reqLogger.Error(err, "POST REQUEST error")
220 postReq.Header.Set("Content-Type", "application/json")
221 postReq.SetBasicAuth(grafanaUsername, grafanaPassword)
222 postResp, err := client.Do(postReq)
224 reqLogger.Error(err, "POST RESPONSE error")
227 defer postReq.Body.Close()
229 if postResp.StatusCode == http.StatusOK {
230 reqLogger.Info("Datasource created")
236 func updateDatasource(grafana map[string]string, datasource onapv1alpha1.Datasource, datasourceID string) error {
237 reqLogger := log.WithValues("Datasource name", datasource.Name, "Datasource URL", datasource.URL)
238 reqLogger.Info("Updating datasource")
240 grafanaURL := grafana["url"] + "/api/datasources/" + datasourceID
241 grafanaUsername := grafana["username"]
242 grafanaPassword := grafana["password"]
244 client := &http.Client{}
245 putBody, err := json.Marshal(datasource)
247 reqLogger.Error(err, "JSON Marshalling error")
250 putReq, err := http.NewRequest("PUT", grafanaURL, bytes.NewBuffer(putBody))
252 reqLogger.Error(err, "PUT REQUEST error")
255 putReq.Header.Set("Content-Type", "application/json")
256 putReq.Header.Set("Accept", "application/json")
257 putReq.SetBasicAuth(grafanaUsername, grafanaPassword)
259 putResp, err := client.Do(putReq)
261 reqLogger.Error(err, "PUT RESPONSE error")
264 defer putReq.Body.Close()
266 if putResp.StatusCode == http.StatusOK {
267 reqLogger.Info("Datasource updated")
273 func deleteDatasource(instance *onapv1alpha1.GrafanaDataSource) error {
275 datasources := instance.Spec.Datasources
276 grafana := instance.Spec.Grafana
278 for _, datasource := range datasources {
280 reqLogger := log.WithValues("Datasource name", datasource.Name, "Datasource URL", datasource.URL)
281 reqLogger.Info("Deleting datasource")
283 grafanaURL := grafana["url"] + "/api/datasources/name/" + datasource.Name
284 grafanaUsername := grafana["username"]
285 grafanaPassword := grafana["password"]
287 client := &http.Client{}
288 deleteReq, err := http.NewRequest("DELETE", grafanaURL, nil)
290 reqLogger.Error(err, "DELETE request error")
294 deleteReq.SetBasicAuth(grafanaUsername, grafanaPassword)
296 deleteResp, err := client.Do(deleteReq)
298 reqLogger.Error(err, "DELETE RESPONSE error")
302 if deleteResp.StatusCode == http.StatusOK {
303 reqLogger.Info("Datasource deleted")
311 func checkDeletionTimestamp(reqlogger logr.Logger, instance *onapv1alpha1.GrafanaDataSource) bool {
312 isMarkedForDeletion := instance.GetDeletionTimestamp() != nil
313 return isMarkedForDeletion
316 func addFinalizer(reqlogger logr.Logger, instance *onapv1alpha1.GrafanaDataSource) error {
317 reqlogger.Info("Adding finalizer for the GrafanaDatasource")
318 instance.SetFinalizers(append(instance.GetFinalizers(), visualizationutils.VisualizationFinalizer))
322 func removeFinalizer(reqlogger logr.Logger, instance *onapv1alpha1.GrafanaDataSource) error {
323 reqlogger.Info("Removing finalizer for the GrafanaDatasource")
324 instance.SetFinalizers(visualizationutils.Remove(instance.GetFinalizers(), visualizationutils.VisualizationFinalizer))