Skip to content

Commit

Permalink
patch - adding grafana_folder resource and support folder id and uuid… (
Browse files Browse the repository at this point in the history
#154)

* patch - adding grafana_folder resource and support folder id and uuid for hosted-grafana-dashboard
  • Loading branch information
OrNovo authored Oct 5, 2023
1 parent 9e08319 commit 93a31e6
Show file tree
Hide file tree
Showing 15 changed files with 645 additions and 94 deletions.
8 changes: 4 additions & 4 deletions coralogix/clientset/clientset.go
Original file line number Diff line number Diff line change
Expand Up @@ -6,7 +6,7 @@ type ClientSet struct {
enrichments *EnrichmentsClient
dataSet *DataSetClient
dashboards *DashboardsClient
grafanaDashboards *GrafanaDashboardClient
grafana *GrafanaClient
actions *ActionsClient
recordingRuleGroups *RecordingRulesGroupsSetsClient
tcoPolicies *TCOPoliciesClient
Expand Down Expand Up @@ -36,8 +36,8 @@ func (c *ClientSet) Dashboards() *DashboardsClient {
return c.dashboards
}

func (c *ClientSet) GrafanaDashboards() *GrafanaDashboardClient {
return c.grafanaDashboards
func (c *ClientSet) Grafana() *GrafanaClient {
return c.grafana
}

func (c *ClientSet) Actions() *ActionsClient {
Expand Down Expand Up @@ -79,7 +79,7 @@ func NewClientSet(targetUrl, apiKey, teamsApiKey string) *ClientSet {
enrichments: NewEnrichmentClient(apikeyCPC),
dataSet: NewDataSetClient(apikeyCPC),
dashboards: NewDashboardsClient(apikeyCPC),
grafanaDashboards: NewGrafanaClient(apikeyCPC),
grafana: NewGrafanaClient(apikeyCPC),
actions: NewActionsClient(apikeyCPC),
recordingRuleGroups: NewRecordingRuleGroupsClient(apikeyCPC),
tcoPolicies: NewTCOPoliciesClient(apikeyCPC),
Expand Down
76 changes: 68 additions & 8 deletions coralogix/clientset/grafana-client.go
Original file line number Diff line number Diff line change
Expand Up @@ -11,12 +11,12 @@ import (
gapi "github.com/grafana/grafana-api-golang-client"
)

type GrafanaDashboardClient struct {
type GrafanaClient struct {
targetUrl string
client *rest.Client
}

func (g GrafanaDashboardClient) CreateGrafanaDashboard(ctx context.Context, dashboard gapi.Dashboard) (*gapi.DashboardSaveResponse, error) {
func (g GrafanaClient) CreateGrafanaDashboard(ctx context.Context, dashboard gapi.Dashboard) (*gapi.DashboardSaveResponse, error) {
body, err := json.Marshal(dashboard)
if err != nil {
return nil, err
Expand All @@ -36,7 +36,7 @@ func (g GrafanaDashboardClient) CreateGrafanaDashboard(ctx context.Context, dash
return &dashboardResp, nil
}

func (g GrafanaDashboardClient) GetGrafanaDashboard(ctx context.Context, uid string) (*gapi.Dashboard, error) {
func (g GrafanaClient) GetGrafanaDashboard(ctx context.Context, uid string) (*gapi.Dashboard, error) {
bodyResp, err := g.client.Get(ctx, fmt.Sprintf("/grafana/api/dashboards/uid/%s", uid))
if err != nil {
return nil, err
Expand All @@ -51,24 +51,84 @@ func (g GrafanaDashboardClient) GetGrafanaDashboard(ctx context.Context, uid str
return &dashboardResp, nil
}

func (g GrafanaDashboardClient) UpdateGrafanaDashboard(ctx context.Context, dashboard gapi.Dashboard) (*gapi.DashboardSaveResponse, error) {
func (g GrafanaClient) UpdateGrafanaDashboard(ctx context.Context, dashboard gapi.Dashboard) (*gapi.DashboardSaveResponse, error) {
dashboard.Overwrite = true
return g.CreateGrafanaDashboard(ctx, dashboard)
}

func (g GrafanaDashboardClient) DeleteGrafanaDashboard(ctx context.Context, uid string) error {
func (g GrafanaClient) DeleteGrafanaDashboard(ctx context.Context, uid string) error {
_, err := g.client.Delete(ctx, fmt.Sprintf("/grafana/api/dashboards/uid/%s", uid))
return err

}

func (g GrafanaDashboardClient) GetTargetURL() string {
func (g GrafanaClient) CreateGrafanaFolder(ctx context.Context, folder gapi.Folder) (*gapi.Folder, error) {
body, err := json.Marshal(folder)
if err != nil {
return nil, err
}

bodyResp, err := g.client.Post(ctx, "/grafana/api/folders", "application/json", string(body))
if err != nil {
return nil, err
}

var folderResp gapi.Folder
err = json.Unmarshal([]byte(bodyResp), &folderResp)
if err != nil {
return nil, err
}

return &folderResp, nil
}

func (g GrafanaClient) GetGrafanaFolder(ctx context.Context, uid string) (*gapi.Folder, error) {
bodyResp, err := g.client.Get(ctx, fmt.Sprintf("/grafana/api/folders/id/%s", uid))
if err != nil {
return nil, err
}

var folderResp gapi.Folder
err = json.Unmarshal([]byte(bodyResp), &folderResp)
if err != nil {
return nil, err
}

return &folderResp, nil
}

func (g GrafanaClient) UpdateGrafanaFolder(ctx context.Context, folder gapi.Folder) (*gapi.Folder, error) {
body, err := json.Marshal(folder)
if err != nil {
return nil, err
}

bodyResp, err := g.client.Put(ctx, fmt.Sprintf("/grafana/api/folders/%s", folder.UID), "application/json", string(body))
if err != nil {
return nil, err
}

var folderResp gapi.Folder
err = json.Unmarshal([]byte(bodyResp), &folderResp)
if err != nil {
return nil, err
}

return &folderResp, nil
}

func (g GrafanaClient) DeleteGrafanaFolder(ctx context.Context, uid string) error {
_, err := g.client.Delete(ctx, fmt.Sprintf("/grafana/api/folders/%s", uid))
return err
}

func (g GrafanaClient) GetTargetURL() string {
return g.targetUrl

}

func NewGrafanaClient(c *CallPropertiesCreator) *GrafanaDashboardClient {
func NewGrafanaClient(c *CallPropertiesCreator) *GrafanaClient {
targetUrl := "https://" + strings.Replace(c.targetUrl, "grpc", "http", 1)
client := rest.NewRestClient(targetUrl, c.apiKey)
return &GrafanaDashboardClient{client: client, targetUrl: targetUrl}
return &GrafanaClient{client: client, targetUrl: targetUrl}
}
4 changes: 2 additions & 2 deletions coralogix/data_source_hosted_dashboard.go
Original file line number Diff line number Diff line change
Expand Up @@ -93,7 +93,7 @@ func extractDashboardTypeAndUIDFromID(uid string) (string, string) {
}

func dataSourceHostedGrafanaDashboardRead(ctx context.Context, d *schema.ResourceData, meta interface{}, uid string) diag.Diagnostics {
dashboard, err := meta.(*clientset.ClientSet).GrafanaDashboards().GetGrafanaDashboard(ctx, uid)
dashboard, err := meta.(*clientset.ClientSet).Grafana().GetGrafanaDashboard(ctx, uid)
if err != nil {
return diag.FromErr(err)
}
Expand All @@ -111,7 +111,7 @@ func dataSourceHostedGrafanaDashboardRead(ctx context.Context, d *schema.Resourc
hostedGrafanaNewSchema["title"] = dashboard.Model["title"].(string)
hostedGrafanaNewSchema["folder"] = dashboard.FolderID
hostedGrafanaNewSchema["is_starred"] = dashboard.Meta.IsStarred
hostedGrafanaNewSchema["url"] = strings.TrimRight(meta.(*clientset.ClientSet).GrafanaDashboards().GetTargetURL(), "/") + dashboard.Meta.URL
hostedGrafanaNewSchema["url"] = strings.TrimRight(meta.(*clientset.ClientSet).Grafana().GetTargetURL(), "/") + dashboard.Meta.URL

if err = d.Set("grafana", []interface{}{hostedGrafanaNewSchema}); err != nil {
return diag.FromErr(err)
Expand Down
1 change: 1 addition & 0 deletions coralogix/provider.go
Original file line number Diff line number Diff line change
Expand Up @@ -79,6 +79,7 @@ func OldProvider() *oldSchema.Provider {
"coralogix_hosted_dashboard": resourceCoralogixHostedDashboard(),
"coralogix_recording_rules_groups_set": resourceCoralogixRecordingRulesGroupsSet(),
"coralogix_webhook": resourceCoralogixWebhook(),
"coralogix_grafana_folder": resourceGrafanaFolder(),
},

ConfigureContextFunc: func(context context.Context, d *oldSchema.ResourceData) (interface{}, diag.Diagnostics) {
Expand Down
165 changes: 165 additions & 0 deletions coralogix/resource_grafana_folder.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,165 @@
package coralogix

import (
"context"
"fmt"
"log"
"strconv"
"strings"

gapi "github.com/grafana/grafana-api-golang-client"
"github.com/hashicorp/terraform-plugin-sdk/v2/diag"
"github.com/hashicorp/terraform-plugin-sdk/v2/helper/schema"
"google.golang.org/grpc/codes"
"google.golang.org/grpc/status"
"terraform-provider-coralogix/coralogix/clientset"
)

func resourceGrafanaFolder() *schema.Resource {
return &schema.Resource{

Description: `
* [Official documentation](https://grafana.com/docs/grafana/latest/dashboards/manage-dashboards/)
* [HTTP API](https://grafana.com/docs/grafana/latest/developers/http_api/folder/)
`,

CreateContext: CreateFolder,
DeleteContext: DeleteFolder,
ReadContext: ReadFolder,
UpdateContext: UpdateFolder,
Importer: &schema.ResourceImporter{
StateContext: schema.ImportStatePassthroughContext,
},

Schema: map[string]*schema.Schema{
"uid": {
Type: schema.TypeString,
Computed: true,
Optional: true,
Description: "Unique identifier.",
},
"title": {
Type: schema.TypeString,
Required: true,
Description: "The title of the folder.",
},
"url": {
Type: schema.TypeString,
Computed: true,
Description: "The full URL of the folder.",
},
"prevent_destroy_if_not_empty": {
Type: schema.TypeBool,
Optional: true,
Default: false,
Description: "Prevent deletion of the folder if it is not empty (contains dashboards or alert rules).",
},
},
}
}

func CreateFolder(ctx context.Context, d *schema.ResourceData, meta interface{}) diag.Diagnostics {
var err error
var folder gapi.Folder
folder.Title = d.Get("title").(string)
if uid, ok := d.GetOk("uid"); ok {
folder.UID = uid.(string)
}

log.Printf("[INFO] Creating grafana-folder: %#v", folder)
resp, err := meta.(*clientset.ClientSet).Grafana().CreateGrafanaFolder(ctx, folder)
if err != nil {
return diag.Errorf("failed to create folder: %s", err)
}
log.Printf("[INFO] Received grafana-folder: %#v", resp)

flattenGrafanaFolder(*resp, d, meta)

return ReadFolder(ctx, d, meta)
}

func expandGrafanaFolder(d *schema.ResourceData) gapi.Folder {
var folder gapi.Folder
if v, ok := d.GetOk("id"); ok {
folder.ID = v.(int64)
}
if v, ok := d.GetOk("title"); ok {
folder.Title = v.(string)
}
if v, ok := d.GetOk("uid"); ok {
folder.UID = v.(string)
}
if v, ok := d.GetOk("url"); ok {
folder.URL = v.(string)
}
return folder
}

func flattenGrafanaFolder(folder gapi.Folder, d *schema.ResourceData, meta interface{}) diag.Diagnostics {
d.SetId(fmt.Sprintf("%d", folder.ID))

if err := d.Set("title", folder.Title); err != nil {
return diag.FromErr(err)
}
if err := d.Set("uid", folder.UID); err != nil {
return diag.FromErr(err)
}
if err := d.Set("url", strings.TrimRight(meta.(*clientset.ClientSet).Grafana().GetTargetURL(), "/")+folder.URL); err != nil {
return diag.FromErr(err)
}
return nil
}

func UpdateFolder(ctx context.Context, d *schema.ResourceData, meta interface{}) diag.Diagnostics {
folder := expandGrafanaFolder(d)
resp, err := meta.(*clientset.ClientSet).Grafana().UpdateGrafanaFolder(ctx, folder)
if err != nil {
log.Printf("[ERROR] Received error: %#v", err)
return diag.FromErr(err)
}
flattenGrafanaFolder(*resp, d, meta)

return ReadFolder(ctx, d, meta)
}

// SplitOrgResourceID splits into two parts (org ID and resource ID) the ID of an org-scoped resource
func SplitOrgResourceID(id string) (int64, string) {
if strings.ContainsRune(id, ':') {
parts := strings.SplitN(id, ":", 2)
orgID, _ := strconv.ParseInt(parts[0], 10, 64)
return orgID, parts[1]
}

return 0, id
}

func ReadFolder(ctx context.Context, d *schema.ResourceData, meta interface{}) diag.Diagnostics {
folder, err := meta.(*clientset.ClientSet).Grafana().GetGrafanaFolder(ctx, d.Id())
if err != nil {
log.Printf("[ERROR] Received error: %#v", err)
return diag.FromErr(err)
}
log.Printf("[INFO] Received grafana-folder: %#v", folder)

return flattenGrafanaFolder(*folder, d, meta)
}

func DeleteFolder(ctx context.Context, d *schema.ResourceData, meta interface{}) diag.Diagnostics {
folder := expandGrafanaFolder(d)
err := meta.(*clientset.ClientSet).Grafana().DeleteGrafanaFolder(ctx, folder.UID)
if err != nil {
log.Printf("[ERROR] Received error: %#v", err)
if status.Code(err) == codes.NotFound {
d.SetId("")
return diag.Diagnostics{diag.Diagnostic{
Severity: diag.Warning,
Summary: fmt.Sprintf("grafana-dashboard %q is in state, but no longer exists in Coralogix backend", d.Id()),
Detail: fmt.Sprintf("%s will be recreated when you apply", d.Id()),
}}
}
return handleRpcError(err, "grafana-folder")
}

d.SetId("")
return nil
}
Loading

0 comments on commit 93a31e6

Please sign in to comment.