diff --git a/go.mod b/go.mod index f6907e50f2..d071dd790b 100644 --- a/go.mod +++ b/go.mod @@ -25,17 +25,13 @@ require ( cloud.google.com/go/auth v0.4.2 // indirect cloud.google.com/go/auth/oauth2adapt v0.2.2 // indirect cloud.google.com/go/compute/metadata v0.5.2 // indirect - github.com/BurntSushi/toml v1.4.1-0.20240526193622-a339e1f7089c // indirect github.com/ProtonMail/go-crypto v1.1.3 // indirect github.com/agext/levenshtein v1.2.3 // indirect github.com/apparentlymart/go-textseg/v15 v15.0.0 // indirect - github.com/bitfield/gotestdox v0.2.2 // indirect github.com/cloudflare/circl v1.3.7 // indirect github.com/davecgh/go-spew v1.1.1 // indirect - github.com/dnephin/pflag v1.0.7 // indirect github.com/fatih/color v1.17.0 // indirect github.com/felixge/httpsnoop v1.0.4 // indirect - github.com/fsnotify/fsnotify v1.8.0 // indirect github.com/go-logr/logr v1.4.2 // indirect github.com/go-logr/stdr v1.2.2 // indirect github.com/golang/groupcache v0.0.0-20210331224755-41bb18bfe9da // indirect @@ -43,7 +39,6 @@ require ( github.com/google/go-cmp v0.7.0 // indirect github.com/google/go-querystring v1.1.0 // indirect github.com/google/s2a-go v0.1.7 // indirect - github.com/google/shlex v0.0.0-20191202100458-e7afc7fbc510 // indirect github.com/google/uuid v1.6.0 // indirect github.com/googleapis/enterprise-certificate-proxy v0.3.2 // indirect github.com/hashicorp/errwrap v1.1.0 // indirect @@ -81,13 +76,11 @@ require ( go.opentelemetry.io/otel/metric v1.31.0 // indirect go.opentelemetry.io/otel/trace v1.31.0 // indirect golang.org/x/crypto v0.36.0 // indirect - golang.org/x/exp/typeparams v0.0.0-20231108232855-2478ac86f678 // indirect golang.org/x/mod v0.24.0 // indirect golang.org/x/net v0.38.0 // indirect golang.org/x/oauth2 v0.23.0 // indirect golang.org/x/sync v0.12.0 // indirect golang.org/x/sys v0.31.0 // indirect - golang.org/x/term v0.30.0 // indirect golang.org/x/text v0.23.0 // indirect golang.org/x/time v0.5.0 // indirect golang.org/x/tools v0.31.0 // indirect @@ -98,12 +91,10 @@ require ( google.golang.org/protobuf v1.36.3 // indirect gopkg.in/ini.v1 v1.67.0 // indirect gopkg.in/yaml.v3 v3.0.1 // indirect - gotest.tools/gotestsum v1.12.1 // indirect - honnef.co/go/tools v0.6.0 // indirect ) -tool ( - golang.org/x/tools/cmd/goimports - gotest.tools/gotestsum - honnef.co/go/tools/cmd/staticcheck -) +//tool ( +// golang.org/x/tools/cmd/goimports +// gotest.tools/gotestsum +// honnef.co/go/tools/cmd/staticcheck +//) diff --git a/go.sum b/go.sum index 0274499e98..02e8f1622a 100644 --- a/go.sum +++ b/go.sum @@ -8,8 +8,6 @@ cloud.google.com/go/compute/metadata v0.5.2/go.mod h1:C66sj2AluDcIqakBq/M8lw8/yb dario.cat/mergo v1.0.0 h1:AGCNq9Evsj31mOgNPcLyXc+4PNABt905YmuqPYYpBWk= dario.cat/mergo v1.0.0/go.mod h1:uNxQE+84aUszobStD9th8a29P2fMDhsBdgRYvZOxGmk= github.com/BurntSushi/toml v0.3.1/go.mod h1:xHWCNGjB5oqiDr8zfno3MHue2Ht5sIBksp03qcyfWMU= -github.com/BurntSushi/toml v1.4.1-0.20240526193622-a339e1f7089c h1:pxW6RcqyfI9/kWtOwnv/G+AzdKuy2ZrqINhenH4HyNs= -github.com/BurntSushi/toml v1.4.1-0.20240526193622-a339e1f7089c/go.mod h1:ukJfTF/6rtPPRCnwkur4qwRxa8vTRFBF0uk2lLoLwho= github.com/Microsoft/go-winio v0.6.1 h1:9/kr64B9VUZrLm5YYwbGtUJnMgqWVOdUAXu6Migciow= github.com/Microsoft/go-winio v0.6.1/go.mod h1:LRdKpFKfdobln8UmuiYcKPot9D2v6svN5+sAH+4kjUM= github.com/ProtonMail/go-crypto v1.1.3 h1:nRBOetoydLeUb4nHajyO2bKqMLfWQ/ZPwkXqXxPxCFk= @@ -19,8 +17,6 @@ github.com/agext/levenshtein v1.2.3/go.mod h1:JEDfjyjHDjOF/1e4FlBE/PkbqA9OfWu2ki github.com/apparentlymart/go-textseg/v12 v12.0.0/go.mod h1:S/4uRK2UtaQttw1GenVJEynmyUenKwP++x/+DdGV/Ec= github.com/apparentlymart/go-textseg/v15 v15.0.0 h1:uYvfpb3DyLSCGWnctWKGj857c6ew1u1fNQOlOtuGxQY= github.com/apparentlymart/go-textseg/v15 v15.0.0/go.mod h1:K8XmNZdhEBkdlyDdvbmmsvpAG721bKi0joRfFdHIWJ4= -github.com/bitfield/gotestdox v0.2.2 h1:x6RcPAbBbErKLnapz1QeAlf3ospg8efBsedU93CDsnE= -github.com/bitfield/gotestdox v0.2.2/go.mod h1:D+gwtS0urjBrzguAkTM2wodsTQYFHdpx8eqRJ3N+9pY= github.com/bufbuild/protocompile v0.4.0 h1:LbFKd2XowZvQ/kajzguUp2DC9UEIQhIq77fZZlaQsNA= github.com/bufbuild/protocompile v0.4.0/go.mod h1:3v93+mbWn/v3xzN+31nwkJfrEpAUwp+BagBSZWx+TP8= github.com/census-instrumentation/opencensus-proto v0.2.1/go.mod h1:f6KPmirojxKA12rnyqOA5BBL4O983OfeGPqjHWSTneU= @@ -35,8 +31,6 @@ github.com/databricks/databricks-sdk-go v0.63.0/go.mod h1:xBtjeP9nq+6MgTewZW1Ecb github.com/davecgh/go-spew v1.1.0/go.mod h1:J7Y8YcW2NihsgmVo/mv3lAwl/skON4iLHjSsI+c5H38= github.com/davecgh/go-spew v1.1.1 h1:vj9j/u1bqnvCEfJOwUhtlOARqs3+rkHYY13jYWTU97c= github.com/davecgh/go-spew v1.1.1/go.mod h1:J7Y8YcW2NihsgmVo/mv3lAwl/skON4iLHjSsI+c5H38= -github.com/dnephin/pflag v1.0.7 h1:oxONGlWxhmUct0YzKTgrpQv9AUA1wtPBn7zuSjJqptk= -github.com/dnephin/pflag v1.0.7/go.mod h1:uxE91IoWURlOiTUIA8Mq5ZZkAv3dPUfZNaT80Zm7OQE= github.com/emirpasic/gods v1.18.1 h1:FXtiHYKDGKCW2KzwZKx0iC0PQmdlorYgdFG9jPXJ1Bc= github.com/emirpasic/gods v1.18.1/go.mod h1:8tpGGwCnJ5H4r6BWwaV6OrWmMoPhUl5jm/FMNAnJvWQ= github.com/envoyproxy/go-control-plane v0.9.0/go.mod h1:YTl/9mNaCwkRvm6d1a2C3ymFceY/DCBVvsKhRF0iEA4= @@ -48,8 +42,6 @@ github.com/fatih/color v1.17.0 h1:GlRw1BRJxkpqUCBKzKOw098ed57fEsKeNjpTe3cSjK4= github.com/fatih/color v1.17.0/go.mod h1:YZ7TlrGPkiz6ku9fK3TLD/pl3CpsiFyu8N92HLgmosI= github.com/felixge/httpsnoop v1.0.4 h1:NFTV2Zj1bL4mc9sqWACXbQFVBBg2W3GPvqp8/ESS2Wg= github.com/felixge/httpsnoop v1.0.4/go.mod h1:m8KPJKqk1gH5J9DgRY2ASl2lWCfGKXixSwevea8zH2U= -github.com/fsnotify/fsnotify v1.8.0 h1:dAwr6QBTBZIkG8roQaJjGof0pp0EeF+tNV7YBP3F/8M= -github.com/fsnotify/fsnotify v1.8.0/go.mod h1:8jBTzvmWwFyi3Pb8djgCCO5IBqzKJ/Jwo8TRcHyHii0= github.com/go-git/gcfg v1.5.1-0.20230307220236-3a3c6141e376 h1:+zs/tPmkDkHx3U66DAb0lQFJrpS6731Oaa12ikc+DiI= github.com/go-git/gcfg v1.5.1-0.20230307220236-3a3c6141e376/go.mod h1:an3vInlBmSxCcxctByoQdvwPiA7DTK7jaaFDBTtu0ic= github.com/go-git/go-billy/v5 v5.6.0 h1:w2hPNtoehvJIxR00Vb4xX94qHQi/ApZfX+nBE2Cjio8= @@ -98,8 +90,6 @@ github.com/google/go-querystring v1.1.0 h1:AnCroh3fv4ZBgVIf1Iwtovgjaw/GiKJo8M8yD github.com/google/go-querystring v1.1.0/go.mod h1:Kcdr2DB4koayq7X8pmAG4sNG59So17icRSOU623lUBU= github.com/google/s2a-go v0.1.7 h1:60BLSyTrOV4/haCDW4zb1guZItoSq8foHCXrAnjBo/o= github.com/google/s2a-go v0.1.7/go.mod h1:50CgR4k1jNlWBu4UfS4AcfhVe1r6pdZPygJ3R8F0Qdw= -github.com/google/shlex v0.0.0-20191202100458-e7afc7fbc510 h1:El6M4kTTCOh6aBiKaUGG7oYTSPP8MxqL4YI3kZKwcP4= -github.com/google/shlex v0.0.0-20191202100458-e7afc7fbc510/go.mod h1:pupxD2MaaD3pAXIBCelhxNneeOaAeabZDe5s4K6zSpQ= github.com/google/uuid v1.1.2/go.mod h1:TIyPZe4MgqvfeYDBFedMoGGpEw/LqOeaOT+nhxU+yHo= github.com/google/uuid v1.6.0 h1:NIvaJDMOsjHA8n1jAhLSgzrAzy1Hgr+hNrb57e+94F0= github.com/google/uuid v1.6.0/go.mod h1:TIyPZe4MgqvfeYDBFedMoGGpEw/LqOeaOT+nhxU+yHo= @@ -201,8 +191,6 @@ github.com/pkg/browser v0.0.0-20240102092130-5ac0b6a4141c/go.mod h1:7rwL4CYBLnjL github.com/pmezard/go-difflib v1.0.0 h1:4DBwDE0NGyQoBHbLQYPwSUPoCMWR5BEzIk/f1lZbAQM= github.com/pmezard/go-difflib v1.0.0/go.mod h1:iKH77koFhYxTK1pcRnkKkqfTogsbg7gZNVY4sRDYZ/4= github.com/prometheus/client_model v0.0.0-20190812154241-14fe0d1b01d4/go.mod h1:xMI15A0UPsDsEKsMN9yxemIoYk6Tm2C1GtYGdfGttqA= -github.com/rogpeppe/go-internal v1.11.0 h1:cWPaGQEPrBb5/AsnsZesgZZ9yb1OQ+GOISoDNXVBh4M= -github.com/rogpeppe/go-internal v1.11.0/go.mod h1:ddIwULY96R17DhadqLgMfk9H9tvdUzkipdSkR5nkCZA= github.com/sergi/go-diff v1.3.2-0.20230802210424-5b0b94c5c0d3 h1:n661drycOFuPLCN3Uc8sB6B/s6Z4t2xvBgU1htSHuq8= github.com/sergi/go-diff v1.3.2-0.20230802210424-5b0b94c5c0d3/go.mod h1:A0bzQcvG0E7Rwjx0REVgAGH58e96+X0MeOfepqsbeW4= github.com/skeema/knownhosts v1.3.0 h1:AM+y0rI04VksttfwjkSTNQorvGqmwATnvnAHpSgc0LY= @@ -256,8 +244,6 @@ golang.org/x/crypto v0.36.0/go.mod h1:Y4J0ReaxCR1IMaabaSMugxJES1EpwhBHhv2bDHklZv golang.org/x/exp v0.0.0-20190121172915-509febef88a4/go.mod h1:CJ0aWSM057203Lf6IL+f9T1iT9GByDxfZKAQTCR3kQA= golang.org/x/exp v0.0.0-20240222234643-814bf88cf225 h1:LfspQV/FYTatPTr/3HzIcmiUFH7PGP+OQ6mgDYo3yuQ= golang.org/x/exp v0.0.0-20240222234643-814bf88cf225/go.mod h1:CxmFvTBINI24O/j8iY7H1xHzx2i4OsyguNBmN/uPtqc= -golang.org/x/exp/typeparams v0.0.0-20231108232855-2478ac86f678 h1:1P7xPZEwZMoBoz0Yze5Nx2/4pxj6nw9ZqHWXqP0iRgQ= -golang.org/x/exp/typeparams v0.0.0-20231108232855-2478ac86f678/go.mod h1:AbB0pIl9nAr9wVwH+Z2ZpaocVmF5I4GyWCDIsVjR0bk= golang.org/x/lint v0.0.0-20181026193005-c67002cb31c3/go.mod h1:UVdnD1Gm6xHRNCYTkRU2/jEulfH38KcIWyp/GAMgvoE= golang.org/x/lint v0.0.0-20190227174305-5b3e6a55c961/go.mod h1:wehouNa3lNwaWXcvxsM5YxQ5yQlVC4a0KAMCusXpPoU= golang.org/x/lint v0.0.0-20190313153728-d0100b6bd8b3/go.mod h1:6SW0HCj/g11FgYtHlgUYUwCkIfeOF89ocIRzGO/8vkc= @@ -366,11 +352,5 @@ gopkg.in/warnings.v0 v0.1.2/go.mod h1:jksf8JmL6Qr/oQM2OXTHunEvvTAsrWBLb6OOjuVWRN gopkg.in/yaml.v3 v3.0.0-20200313102051-9f266ea9e77c/go.mod h1:K4uyk7z7BCEPqu6E+C64Yfv1cQ7kz7rIZviUmN+EgEM= gopkg.in/yaml.v3 v3.0.1 h1:fxVm/GzAzEWqLHuvctI91KS9hhNmmWOoWu0XTYJS7CA= gopkg.in/yaml.v3 v3.0.1/go.mod h1:K4uyk7z7BCEPqu6E+C64Yfv1cQ7kz7rIZviUmN+EgEM= -gotest.tools/gotestsum v1.12.1 h1:dvcxFBTFR1QsQmrCQa4k/vDXow9altdYz4CjdW+XeBE= -gotest.tools/gotestsum v1.12.1/go.mod h1:mwDmLbx9DIvr09dnAoGgQPLaSXszNpXpWo2bsQge5BE= -gotest.tools/v3 v3.5.1 h1:EENdUnS3pdur5nybKYIh2Vfgc8IUNBjxDPSjtiJcOzU= -gotest.tools/v3 v3.5.1/go.mod h1:isy3WKz7GK6uNw/sbHzfKBLvlvXwUyV06n6brMxxopU= honnef.co/go/tools v0.0.0-20190102054323-c2f93a96b099/go.mod h1:rf3lG4BRIbNafJWhAfAdb/ePZxsR/4RtNHQocxwk9r4= honnef.co/go/tools v0.0.0-20190523083050-ea95bdfd59fc/go.mod h1:rf3lG4BRIbNafJWhAfAdb/ePZxsR/4RtNHQocxwk9r4= -honnef.co/go/tools v0.6.0 h1:TAODvD3knlq75WCp2nyGJtT4LeRV/o7NN9nYPeVJXf8= -honnef.co/go/tools v0.6.0/go.mod h1:3puzxxljPCe8RGJX7BIy1plGbxEOZni5mR2aXe3/uk4= diff --git a/internal/providers/pluginfw/pluginfw_rollout_utils.go b/internal/providers/pluginfw/pluginfw_rollout_utils.go index 32e40996de..b15ab2c65f 100644 --- a/internal/providers/pluginfw/pluginfw_rollout_utils.go +++ b/internal/providers/pluginfw/pluginfw_rollout_utils.go @@ -24,6 +24,7 @@ import ( "github.com/databricks/terraform-provider-databricks/internal/providers/pluginfw/products/serving" "github.com/databricks/terraform-provider-databricks/internal/providers/pluginfw/products/sharing" "github.com/databricks/terraform-provider-databricks/internal/providers/pluginfw/products/volume" + "github.com/databricks/terraform-provider-databricks/internal/providers/pluginfw/products/permission" "github.com/hashicorp/terraform-plugin-framework/datasource" "github.com/hashicorp/terraform-plugin-framework/resource" ) @@ -46,6 +47,7 @@ var migratedDataSources = []func() datasource.DataSource{ var pluginFwOnlyResources = append( []func() resource.Resource{ app.ResourceApp, + permission.NewPermissionResource, sharing.ResourceShare, }, autoGeneratedResources..., @@ -60,6 +62,7 @@ var pluginFwOnlyDataSources = append( catalog.DataSourceFunctions, dashboards.DataSourceDashboards, notificationdestinations.DataSourceNotificationDestinations, + //permission.DataSourcePermission, registered_model.DataSourceRegisteredModel, registered_model.DataSourceRegisteredModelVersions, serving.DataSourceServingEndpoints, diff --git a/internal/providers/pluginfw/products/permission/resource_permission.go b/internal/providers/pluginfw/products/permission/resource_permission.go new file mode 100644 index 0000000000..ed230cc308 --- /dev/null +++ b/internal/providers/pluginfw/products/permission/resource_permission.go @@ -0,0 +1,337 @@ +package permission + +import ( + "context" + "errors" + "fmt" + "strings" + "time" + + "github.com/databricks/databricks-sdk-go" + "github.com/databricks/databricks-sdk-go/apierr" + "github.com/databricks/databricks-sdk-go/service/iam" + "github.com/databricks/terraform-provider-databricks/common" + pluginfwcommon "github.com/databricks/terraform-provider-databricks/internal/providers/pluginfw/common" + pluginfwcontext "github.com/databricks/terraform-provider-databricks/internal/providers/pluginfw/context" + "github.com/hashicorp/terraform-plugin-framework/resource" + "github.com/hashicorp/terraform-plugin-framework/resource/schema" + "github.com/hashicorp/terraform-plugin-framework/schema/validator" + "github.com/hashicorp/terraform-plugin-framework/types" + "github.com/hashicorp/terraform-plugin-framework/path" +) + +const ( + resourceName = "permission" +) + +var ( + _ resource.Resource = &PermissionResource{} + _ resource.ResourceWithConfigure = &PermissionResource{} + _ validator.Object = &accessControlListValidator{} +) + +func NewPermissionResource() resource.Resource { + return &PermissionResource{} +} + +type PermissionResource struct { + client *common.DatabricksClient + workspaceClient databricks.WorkspaceClient + context context.Context +} + +func (r *PermissionResource) Configure(ctx context.Context, req resource.ConfigureRequest, resp *resource.ConfigureResponse) { + if req.ProviderData == nil { + return + } + + workspaceClient, err := req.ProviderData.(*common.DatabricksClient).WorkspaceClient() + if err != nil { + resp.Diagnostics.AddError( + //TODO ADD ERROR MESSAGE IN LINE WITH Provider + "Unable to configure the Databricks client", + fmt.Sprintf("Expected *common.DatabricksClient, got %T", req.ProviderData), + ) + + } else { + r.workspaceClient = *workspaceClient + } +} + +func (r *PermissionResource) Metadata(ctx context.Context, req resource.MetadataRequest, resp *resource.MetadataResponse) { + resp.TypeName = pluginfwcommon.GetDatabricksProductionName(resourceName) +} + +type permissionResourceModel struct { + ObjectID types.String `tfsdk:"object_id"` + ObjectType types.String `tfsdk:"object_type"` + AccessControlList permissionAccessControlModel `tfsdk:"access_control"` + LastUpdated types.String `tfsdk:"last_updated"` +} + +// accessControlListModel is the same as iam.AccessControlRequest +// was originally just called this way in entity.go +type permissionAccessControlModel struct { + ServicePrincipalId types.String `tfsdk:"service_principal_id"` + GroupName types.String `tfsdk:"group_name"` + UserName types.String `tfsdk:"user_name"` + PermissionLevel types.String `tfsdk:"permission_level"` +} + +type accessControlListValidator struct {} + +func (v accessControlListValidator) Description(ctx context.Context) string { + return "Only one of user_name, group_name, or service_principal_id may be set" +} + +func (v accessControlListValidator) MarkdownDescription(ctx context.Context) string { + return v.Description(ctx) +} + + +func (v accessControlListValidator) ValidateObject(ctx context.Context, req validator.ObjectRequest, resp *validator.ObjectResponse) { + var userName, groupName, spID types.String + + if diags := req.Config.GetAttribute(ctx, path.Root("access_control").AtName("user_name"), &userName); diags.HasError() { + resp.Diagnostics.Append(diags...) + return + } + if diags := req.Config.GetAttribute(ctx, path.Root("access_control").AtName("group_name"), &groupName); diags.HasError() { + resp.Diagnostics.Append(diags...) + return + } + if diags := req.Config.GetAttribute(ctx, path.Root("access_control").AtName("service_principal_id"), &spID); diags.HasError() { + resp.Diagnostics.Append(diags...) + return + } + + // Count how many are set + count := 0 + if !userName.IsNull() && userName.ValueString() != "" { + count++ + } + if !groupName.IsNull() && groupName.ValueString() != "" { + count++ + } + if !spID.IsNull() && spID.ValueString() != "" { + count++ + } + + if count != 1 { + resp.Diagnostics.AddError( + "Invalid access control configuration", + "Exactly one of `user_name`, `group_name`, or `service_principal_id` must be set.", + ) + } +} + + + +func (r *PermissionResource) Schema(ctx context.Context, req resource.SchemaRequest, resp *resource.SchemaResponse) { + resp.Schema = schema.Schema{ + Attributes: map[string]schema.Attribute{ + "object_id": schema.StringAttribute{ + Required: true, + }, + "object_type": schema.StringAttribute{ + Required: true, + }, + "last_updated": schema.StringAttribute{ + Computed: true, + }, + }, + Blocks: map[string]schema.Block{ + "access_control": schema.SingleNestedBlock{ + Attributes: map[string]schema.Attribute{ + "service_principal_id": schema.StringAttribute{ + Optional: true, + Description: "The service principal ID of the access control entry.", + }, + "group_name": schema.StringAttribute{ + Optional: true, + Description: "The group name of the access control entry.", + }, + "user_name": schema.StringAttribute{ + Optional: true, + Description: "The user name of the access control entry.", + }, + "permission_level": schema.StringAttribute{ + Optional: true, + Description: "The permission level of the access control entry.", + }, + }, + Validators: []validator.Object{ + accessControlListValidator{}, + }, + }, + }, + } +} + +// TODO set some attributes as optional, required +// see /databricks/permissions/resource_permissions.go line 160 +//func (r *PermissionResource) Schema(ctx context.Context, req resource.SchemaRequest, resp *resource.SchemaResponse) { +// attrs, blocks := tfschema.ResourceStructToSchemaMap(ctx, permissionResourceModel{}, nil) +// resp.Schema = schema.Schema{ +// Attributes: attrs, +// Blocks: blocks, +// } +//} + +func (r *PermissionResource) Create(ctx context.Context, req resource.CreateRequest, resp *resource.CreateResponse) { + ctx = pluginfwcontext.SetUserAgentInResourceContext(ctx, resourceName) + + var plan permissionResourceModel + diags := req.Plan.Get(ctx, &plan) + resp.Diagnostics.Append(diags...) + if resp.Diagnostics.HasError() { + return + } + + + + // generate API request from plan + var acl iam.AccessControlRequest + acl.ServicePrincipalName = plan.AccessControlList.ServicePrincipalId.ValueString() + acl.UserName = plan.AccessControlList.UserName.ValueString() + acl.GroupName = plan.AccessControlList.GroupName.ValueString() + acl.PermissionLevel = iam.PermissionLevel(plan.AccessControlList.PermissionLevel.ValueString()) + + + // create the permission + permission, err := r.workspaceClient.Permissions.Update(ctx, iam.PermissionsRequest{ + RequestObjectId: plan.ObjectID.ValueString(), + RequestObjectType: plan.ObjectType.ValueString(), + AccessControlList: []iam.AccessControlRequest{acl}, + }) + if err != nil { + resp.Diagnostics.AddError( + //TODO ADD ERROR MESSAGE IN LINE WITH Provider + "Unable to Create Permission", + fmt.Sprintf("Error: %s", err.Error()), + ) + } + + //Map response to to schema and populate Computed attribute values + plan.ObjectID = types.StringValue(permission.ObjectId) + plan.ObjectType = types.StringValue(permission.ObjectType) + plan.AccessControlList = permissionAccessControlModel{ + ServicePrincipalId: types.StringValue(permission.AccessControlList[0].ServicePrincipalName), + GroupName: types.StringValue(permission.AccessControlList[0].GroupName), + UserName: types.StringValue(permission.AccessControlList[0].UserName), + PermissionLevel: types.StringValue(string(permission.AccessControlList[0].AllPermissions[0].PermissionLevel)), + } + + + plan.LastUpdated = types.StringValue(time.Now().Format(time.RFC850)) + + // Set state to fully populated data + diags = resp.State.Set(ctx, plan) + resp.Diagnostics.Append(diags...) + if resp.Diagnostics.HasError() { + return + } +} + + +func (r *PermissionResource) Read(ctx context.Context, req resource.ReadRequest, resp *resource.ReadResponse) { + ctx = pluginfwcontext.SetUserAgentInResourceContext(ctx, resourceName) + + // Get Current State + var state permissionResourceModel + diags := req.State.Get(ctx, &state) + resp.Diagnostics.Append(diags...) + if resp.Diagnostics.HasError() { + return + } + + //Get refreshed acls from API + permissions, err := common.RetryOn504(ctx, func(ctx context.Context) (*iam.ObjectPermissions, error) { + return r.workspaceClient.Permissions.Get(ctx, iam.GetPermissionRequest{ + RequestObjectId: state.ObjectID.ValueString(), + RequestObjectType: state.ObjectType.ValueString(), + }) + }) + + var apiErr *apierr.APIError + // https://github.com/databricks/terraform-provider-databricks/issues/1227 + // platform propagates INVALID_STATE error for auto-purged clusters in + // the permissions api. this adds "a logical fix" also here, not to introduce + // cross-package dependency on "clusters". + if errors.As(err, &apiErr) && strings.Contains(apiErr.Message, "Cannot access cluster") && apiErr.StatusCode == 400 { + apiErr.StatusCode = 404 + apiErr.ErrorCode = "RESOURCE_DOES_NOT_EXIST" + err = apiErr + } + if err != nil { + resp.Diagnostics.AddError( + "Failed to get permission", + fmt.Sprintf("Unable to read permission, %s", err.Error()), + ) + return + } + + + //Overwrite data with refreshed state + //TODO: parse getResponse to permissionResourceModel + state.AccessControlList.UserName = types.StringValue(permissions.AccessControlList[0].UserName) + state.AccessControlList.GroupName = types.StringValue(permissions.AccessControlList[0].GroupName) + state.AccessControlList.ServicePrincipalId = types.StringValue(permissions.AccessControlList[0].ServicePrincipalName) + state.AccessControlList.PermissionLevel = types.StringValue(string(permissions.AccessControlList[0].AllPermissions[0].PermissionLevel)) + state.ObjectID = types.StringValue(permissions.ObjectId) + state.ObjectType = types.StringValue(permissions.ObjectType) + + //Set refreshed State + diags = resp.State.Set(ctx, &state) + resp.Diagnostics.Append(diags...) + if resp.Diagnostics.HasError() { + return + } +} + +func (r *PermissionResource) Update(ctx context.Context, req resource.UpdateRequest, resp *resource.UpdateResponse) { + ctx = pluginfwcontext.SetUserAgentInResourceContext(ctx, resourceName) +} + +func (r *PermissionResource) Delete(ctx context.Context, req resource.DeleteRequest, resp *resource.DeleteResponse) { + ctx = pluginfwcontext.SetUserAgentInResourceContext(ctx, resourceName) +} + +// safePutWithOwner is a workaround for the limitation where warehouse without owners cannot have IS_OWNER set +//func (r *PermissionResource) safePutWithOwner(ctx context.Context, objectID string, objectACL []iam.AccessControlRequest, mapping resourcePermissions, ownerOpt string) error { +// w, err := r.client.WorkspaceClient() +// if err != nil { +// return err +// } +// idParts := strings.Split(objectID, "/") +// id := idParts[len(idParts)-1] +// withOwner := mapping.addOwnerPermissionIfNeeded(objectACL, ownerOpt) +// _, err = w.Permissions.Set(ctx, iam.PermissionsRequest{ +// RequestObjectId: id, +// RequestObjectType: mapping.requestObjectType, +// AccessControlList: withOwner, +// }) +// if err != nil { +// if strings.Contains(err.Error(), "with no existing owner must provide a new owner") { +// _, err = w.Permissions.Set(ctx, iam.PermissionsRequest{ +// RequestObjectId: id, +// RequestObjectType: mapping.requestObjectType, +// AccessControlList: objectACL, +// }) +// } +// return err +// } +// return nil +//} + +//func (r *PermissionResource) getCurrentUser(ctx context.Context) (string, error) { +// w, err := r.client.WorkspaceClient() +// if err != nil { +// return "", err +// } +// me, err := w.CurrentUser.Me(ctx) +// if err != nil { +// return "", err +// } +// return me.UserName, nil +//}