From d921ff945baa893bbdb9cba3f9d06c477edf6901 Mon Sep 17 00:00:00 2001 From: IevaVasiljeva Date: Tue, 7 Nov 2023 13:59:42 +0000 Subject: [PATCH 1/3] bring data source permissions up to date with Golang API client changes and add Admin permission --- docs/resources/data_source_permission.md | 4 +- .../resource.tf | 2 +- .../resource_data_source_permission.go | 123 +++++++++--------- 3 files changed, 63 insertions(+), 66 deletions(-) diff --git a/docs/resources/data_source_permission.md b/docs/resources/data_source_permission.md index 0cb247860..439b57d3e 100644 --- a/docs/resources/data_source_permission.md +++ b/docs/resources/data_source_permission.md @@ -48,7 +48,7 @@ resource "grafana_data_source_permission" "fooPermissions" { datasource_id = grafana_data_source.foo.id permissions { team_id = grafana_team.team.id - permission = "Query" + permission = "Admin" } permissions { user_id = grafana_user.user.id @@ -86,7 +86,7 @@ resource "grafana_data_source_permission" "fooPermissions" { Required: -- `permission` (String) Permission to associate with item. Options: `Query` or `Edit` (`Edit` can only be used with Grafana v9.2.3+). +- `permission` (String) Permission to associate with item. Options: `Query`, `Edit` or `Admin` (`Admin` can only be used with Grafana v10.3.0+). Optional: diff --git a/examples/resources/grafana_data_source_permission/resource.tf b/examples/resources/grafana_data_source_permission/resource.tf index 963988e29..2d6009269 100644 --- a/examples/resources/grafana_data_source_permission/resource.tf +++ b/examples/resources/grafana_data_source_permission/resource.tf @@ -33,7 +33,7 @@ resource "grafana_data_source_permission" "fooPermissions" { datasource_id = grafana_data_source.foo.id permissions { team_id = grafana_team.team.id - permission = "Query" + permission = "Admin" } permissions { user_id = grafana_user.user.id diff --git a/internal/resources/grafana/resource_data_source_permission.go b/internal/resources/grafana/resource_data_source_permission.go index 40646a33c..73c89b4d7 100644 --- a/internal/resources/grafana/resource_data_source_permission.go +++ b/internal/resources/grafana/resource_data_source_permission.go @@ -2,7 +2,6 @@ package grafana import ( "context" - "fmt" "strconv" "github.com/hashicorp/terraform-plugin-sdk/v2/diag" @@ -68,8 +67,8 @@ func ResourceDatasourcePermission() *schema.Resource { "permission": { Type: schema.TypeString, Required: true, - ValidateFunc: validation.StringInSlice([]string{"Query", "Edit"}, false), - Description: "Permission to associate with item. Options: `Query` or `Edit` (`Edit` can only be used with Grafana v9.2.3+).", + ValidateFunc: validation.StringInSlice([]string{"Query", "Edit", "Admin"}, false), + Description: "Permission to associate with item. Options: `Query`, `Edit` or `Admin` (`Admin` can only be used with Grafana v10.3.0+).", }, }, }, @@ -89,10 +88,15 @@ func UpdateDatasourcePermissions(ctx context.Context, d *schema.ResourceData, me _, datasourceIDStr := SplitOrgResourceID(d.Get("datasource_id").(string)) datasourceID, _ := strconv.ParseInt(datasourceIDStr, 10, 64) - configuredPermissions := []*gapi.DatasourcePermissionAddPayload{} + dataSource, err := client.DataSource(datasourceID) + if err != nil { + return diag.FromErr(err) + } + + var configuredPermissions []gapi.SetResourcePermissionItem for _, permission := range v.(*schema.Set).List() { permission := permission.(map[string]interface{}) - permissionItem := gapi.DatasourcePermissionAddPayload{} + var permissionItem gapi.SetResourcePermissionItem _, teamIDStr := SplitOrgResourceID(permission["team_id"].(string)) teamID, _ := strconv.ParseInt(teamIDStr, 10, 64) if teamID > 0 { @@ -104,16 +108,13 @@ func UpdateDatasourcePermissions(ctx context.Context, d *schema.ResourceData, me permissionItem.UserID = userID } if permission["built_in_role"].(string) != "" { - permissionItem.BuiltInRole = permission["built_in_role"].(string) + permissionItem.BuiltinRole = permission["built_in_role"].(string) } - var err error - if permissionItem.Permission, err = mapDatasourcePermissionStringToType(permission["permission"].(string)); err != nil { - return diag.FromErr(err) - } - configuredPermissions = append(configuredPermissions, &permissionItem) + permissionItem.Permission = permission["permission"].(string) + configuredPermissions = append(configuredPermissions, permissionItem) } - if err := updateDatasourcePermissions(client, datasourceID, configuredPermissions, true, false); err != nil { + if err := updateDatasourcePermissions(client, dataSource.UID, configuredPermissions); err != nil { return diag.FromErr(err) } @@ -130,23 +131,29 @@ func ReadDatasourcePermissions(ctx context.Context, d *schema.ResourceData, meta return diag.FromErr(err) } - response, err := client.DatasourcePermissions(id) + dataSource, err := client.DataSource(id) + if err != nil { + return diag.FromErr(err) + } + + response, err := client.ListDatasourceResourcePermissions(dataSource.UID) if err, shouldReturn := common.CheckReadError("datasource permissions", d, err); shouldReturn { return err } - permissionItems := make([]interface{}, len(response.Permissions)) - for i, permission := range response.Permissions { + var permissionItems []interface{} + for _, permission := range response { + // Only managed permissions can be provisioned through this resource, so we disregard the permissions obtained through custom and fixed roles here + if !permission.IsManaged { + continue + } permissionItem := make(map[string]interface{}) permissionItem["built_in_role"] = permission.BuiltInRole permissionItem["team_id"] = strconv.FormatInt(permission.TeamID, 10) permissionItem["user_id"] = strconv.FormatInt(permission.UserID, 10) + permissionItem["permission"] = permission.Permission - if permissionItem["permission"], err = mapDatasourcePermissionTypeToString(permission.Permission); err != nil { - return diag.FromErr(err) - } - - permissionItems[i] = permissionItem + permissionItems = append(permissionItems, permissionItem) } d.Set("permissions", permissionItems) @@ -161,80 +168,70 @@ func DeleteDatasourcePermissions(ctx context.Context, d *schema.ResourceData, me if err != nil { return diag.FromErr(err) } - err = updateDatasourcePermissions(client, id, []*gapi.DatasourcePermissionAddPayload{}, false, true) + + dataSource, err := client.DataSource(id) + if err != nil { + return diag.FromErr(err) + } + + err = updateDatasourcePermissions(client, dataSource.UID, []gapi.SetResourcePermissionItem{}) diags, _ := common.CheckReadError("datasource permissions", d, err) return diags } -func updateDatasourcePermissions(client *gapi.Client, id int64, permissions []*gapi.DatasourcePermissionAddPayload, enable, disable bool) error { - areEqual := func(a *gapi.DatasourcePermission, b *gapi.DatasourcePermissionAddPayload) bool { - return a.Permission == b.Permission && a.TeamID == b.TeamID && a.UserID == b.UserID && a.BuiltInRole == b.BuiltInRole +func updateDatasourcePermissions(client *gapi.Client, uid string, permissions []gapi.SetResourcePermissionItem) error { + areEqual := func(a *gapi.ResourcePermission, b gapi.SetResourcePermissionItem) bool { + return a.Permission == b.Permission && a.TeamID == b.TeamID && a.UserID == b.UserID && a.BuiltInRole == b.BuiltinRole } - response, err := client.DatasourcePermissions(id) + response, err := client.ListDatasourceResourcePermissions(uid) if err != nil { return err } - if !response.Enabled && enable { - if err := client.EnableDatasourcePermissions(id); err != nil { - return err - } - } + var permissionList []gapi.SetResourcePermissionItem deleteLoop: - for _, current := range response.Permissions { + for _, current := range response { + // Only managed permissions can be provisioned through this resource, so we disregard the permissions obtained through custom and fixed roles here + if !current.IsManaged { + continue + } for _, new := range permissions { if areEqual(current, new) { continue deleteLoop } } - err := client.RemoveDatasourcePermission(id, current.ID) - if err != nil { - return err + permToRemove := gapi.SetResourcePermissionItem{ + TeamID: current.TeamID, + UserID: current.UserID, + BuiltinRole: current.BuiltInRole, + Permission: "", } + + permissionList = append(permissionList, permToRemove) } addLoop: for _, new := range permissions { - for _, current := range response.Permissions { + for _, current := range response { if areEqual(current, new) { continue addLoop } } - err := client.AddDatasourcePermission(id, new) - if err != nil { - return err + permToAdd := gapi.SetResourcePermissionItem{ + TeamID: new.TeamID, + UserID: new.UserID, + BuiltinRole: new.BuiltinRole, + Permission: new.Permission, } - } - if response.Enabled && disable { - if err := client.DisableDatasourcePermissions(id); err != nil { - return err - } + permissionList = append(permissionList, permToAdd) } - return nil -} + _, err = client.SetDatasourceResourcePermissions(uid, gapi.SetResourcePermissionsBody{Permissions: permissionList}) -func mapDatasourcePermissionStringToType(permission string) (gapi.DatasourcePermissionType, error) { - switch permission { - case "Query": - return gapi.DatasourcePermissionQuery, nil - case "Edit": - return gapi.DatasourcePermissionEdit, nil - } - return 0, fmt.Errorf("unknown datasource permission: %s", permission) -} - -func mapDatasourcePermissionTypeToString(permission gapi.DatasourcePermissionType) (string, error) { - switch permission { - case gapi.DatasourcePermissionQuery: - return "Query", nil - case gapi.DatasourcePermissionEdit: - return "Edit", nil - } - return "", fmt.Errorf("unknown permission type: %d", permission) + return err } From fa315c8a146a612499f0b0dada6c058c50e69b2a Mon Sep 17 00:00:00 2001 From: IevaVasiljeva Date: Wed, 8 Nov 2023 13:37:03 +0000 Subject: [PATCH 2/3] bump golang API version --- go.mod | 2 +- go.sum | 2 ++ 2 files changed, 3 insertions(+), 1 deletion(-) diff --git a/go.mod b/go.mod index a2087a6c3..5af9de9b1 100644 --- a/go.mod +++ b/go.mod @@ -8,7 +8,7 @@ require ( github.com/Masterminds/semver/v3 v3.2.1 github.com/go-openapi/strfmt v0.21.7 github.com/grafana/amixr-api-go-client v0.0.10 - github.com/grafana/grafana-api-golang-client v0.25.0 + github.com/grafana/grafana-api-golang-client v0.26.0 github.com/grafana/grafana-openapi-client-go v0.0.0-20231031181526-6f78415901a3 github.com/grafana/machine-learning-go-client v0.5.0 github.com/grafana/synthetic-monitoring-agent v0.18.3 diff --git a/go.sum b/go.sum index 94532812c..a5e88576c 100644 --- a/go.sum +++ b/go.sum @@ -146,6 +146,8 @@ github.com/grafana/amixr-api-go-client v0.0.10 h1:L2Wc1aETiG7ORqmB+XSCBJdncHM/V0 github.com/grafana/amixr-api-go-client v0.0.10/go.mod h1:N6x26XUrM5zGtK5zL5vNJnAn2JFMxLFPPLTw/6pDkFE= github.com/grafana/grafana-api-golang-client v0.25.0 h1:jDxnR0U5xgIwKzE+IliZJvjMUUTQxGq+c1s+3M46flI= github.com/grafana/grafana-api-golang-client v0.25.0/go.mod h1:24W29gPe9yl0/3A9X624TPkAOR8DpHno490cPwnkv8E= +github.com/grafana/grafana-api-golang-client v0.26.0 h1:Eu2YsfUezYngy8ifvmLybgluIcn/2IS9u1xkzuYstEM= +github.com/grafana/grafana-api-golang-client v0.26.0/go.mod h1:uNLZEmgKtTjHBtCQMwNn3qsx2mpMb8zU+7T4Xv3NR9Y= github.com/grafana/grafana-openapi-client-go v0.0.0-20231031181526-6f78415901a3 h1:jZrhPmoUBL+kwwV5e2AIalKnxV0VW/e0QZ7aHP7Zg3c= github.com/grafana/grafana-openapi-client-go v0.0.0-20231031181526-6f78415901a3/go.mod h1:2vJ8YEgriYoHaNg5eijRU/q7eJTxT078VrGRSTTLeRk= github.com/grafana/machine-learning-go-client v0.5.0 h1:Q1K+MPSy8vfMm2jsk3WQ7O77cGr2fM5hxwtPSoPc5NU= From da9ef1878946e4f476066effbba10f63b0e57922 Mon Sep 17 00:00:00 2001 From: IevaVasiljeva Date: Thu, 9 Nov 2023 15:19:07 +0000 Subject: [PATCH 3/3] test fix --- .../resource_data_source_permission_test.go | 27 +++++++++++++++++++ 1 file changed, 27 insertions(+) diff --git a/internal/resources/grafana/resource_data_source_permission_test.go b/internal/resources/grafana/resource_data_source_permission_test.go index 793a2ed55..5b6af5e60 100644 --- a/internal/resources/grafana/resource_data_source_permission_test.go +++ b/internal/resources/grafana/resource_data_source_permission_test.go @@ -3,6 +3,7 @@ package grafana_test import ( "fmt" "strconv" + "strings" "testing" "github.com/grafana/terraform-provider-grafana/internal/common" @@ -15,6 +16,32 @@ import ( func TestAccDatasourcePermission_basic(t *testing.T) { testutils.CheckEnterpriseTestsEnabled(t) + datasourceID := int64(-1) + // Admin role can only be set from Grafana 10.3.0 onwards + config := testutils.TestAccExample(t, "resources/grafana_data_source_permission/resource.tf") + config = strings.Replace(config, "Admin", "Edit", 1) + + resource.ParallelTest(t, resource.TestCase{ + ProviderFactories: testutils.ProviderFactories, + Steps: []resource.TestStep{ + { + Config: config, + Check: resource.ComposeAggregateTestCheckFunc( + testAccDatasourcePermissionsCheckExists("grafana_data_source_permission.fooPermissions", &datasourceID), + resource.TestCheckResourceAttr("grafana_data_source_permission.fooPermissions", "permissions.#", "4"), + ), + }, + { + Config: testutils.TestAccExample(t, "resources/grafana_data_source_permission/_acc_resource_remove.tf"), + Check: testAccDatasourcePermissionCheckDestroy(&datasourceID), + }, + }, + }) +} + +func TestAccDatasourcePermission_AdminRole(t *testing.T) { + testutils.CheckEnterpriseTestsEnabled(t, ">=10.3.0") + datasourceID := int64(-1) resource.ParallelTest(t, resource.TestCase{