Add permissions block to each resource (#264)

Example:

```yaml
resources:
  jobs:
    my_job:
      name: "[${bundle.environment}] My job"
      permissions:
        - level: CAN_VIEW
          group_name: users
```
This commit is contained in:
Pieter Noordhuis 2023-03-21 10:58:16 +01:00 committed by GitHub
parent 58563b1ea9
commit 66ca9ec266
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
7 changed files with 218 additions and 34 deletions

View File

@ -4,6 +4,7 @@ import "github.com/databricks/databricks-sdk-go/service/jobs"
type Job struct {
ID string `json:"id,omitempty"`
Permissions []Permission `json:"permissions,omitempty"`
*jobs.JobSettings
}

View File

@ -3,5 +3,7 @@ package resources
import "github.com/databricks/databricks-sdk-go/service/mlflow"
type MlflowExperiment struct {
Permissions []Permission `json:"permissions,omitempty"`
*mlflow.Experiment
}

View File

@ -3,5 +3,7 @@ package resources
import "github.com/databricks/databricks-sdk-go/service/mlflow"
type MlflowModel struct {
Permissions []Permission `json:"permissions,omitempty"`
*mlflow.RegisteredModel
}

View File

@ -0,0 +1,11 @@
package resources
// Permission holds the permission level setting for a single principal.
// Multiple of these can be defined on any resource.
type Permission struct {
Level string `json:"level"`
UserName string `json:"user_name,omitempty"`
ServicePrincipalName string `json:"service_principal_name,omitempty"`
GroupName string `json:"group_name,omitempty"`
}

View File

@ -4,6 +4,7 @@ import "github.com/databricks/databricks-sdk-go/service/pipelines"
type Pipeline struct {
ID string `json:"id,omitempty"`
Permissions []Permission `json:"permissions,omitempty"`
*pipelines.PipelineSpec
}

View File

@ -5,6 +5,7 @@ import (
"fmt"
"github.com/databricks/bricks/bundle/config"
"github.com/databricks/bricks/bundle/config/resources"
"github.com/databricks/bricks/bundle/internal/tf/schema"
tfjson "github.com/hashicorp/terraform-json"
)
@ -14,6 +15,35 @@ func conv(from any, to any) {
json.Unmarshal(buf, &to)
}
func convPermissions(acl []resources.Permission) *schema.ResourcePermissions {
if len(acl) == 0 {
return nil
}
resource := schema.ResourcePermissions{}
for _, ac := range acl {
resource.AccessControl = append(resource.AccessControl, convPermission(ac))
}
return &resource
}
func convPermission(ac resources.Permission) schema.ResourcePermissionsAccessControl {
dst := schema.ResourcePermissionsAccessControl{
PermissionLevel: ac.Level,
}
if ac.UserName != "" {
dst.UserName = ac.UserName
}
if ac.GroupName != "" {
dst.GroupName = ac.GroupName
}
if ac.ServicePrincipalName != "" {
dst.ServicePrincipalName = ac.ServicePrincipalName
}
return dst
}
// BundleToTerraform converts resources in a bundle configuration
// to the equivalent Terraform JSON representation.
//
@ -29,6 +59,7 @@ func BundleToTerraform(config *config.Root) *schema.Root {
var dst schema.ResourceJob
conv(src, &dst)
if src.JobSettings != nil {
for _, v := range src.Tasks {
var t schema.ResourceJobTask
conv(v, &t)
@ -58,14 +89,22 @@ func BundleToTerraform(config *config.Root) *schema.Root {
Tag: git.GitTag,
}
}
}
tfroot.Resource.Job[k] = &dst
// Configure permissions for this resource.
if rp := convPermissions(src.Permissions); rp != nil {
rp.JobId = fmt.Sprintf("${databricks_job.%s.id}", k)
tfroot.Resource.Permissions["job_"+k] = rp
}
}
for k, src := range config.Resources.Pipelines {
var dst schema.ResourcePipeline
conv(src, &dst)
if src.PipelineSpec != nil {
for _, v := range src.Libraries {
var l schema.ResourcePipelineLibrary
conv(v, &l)
@ -77,20 +116,39 @@ func BundleToTerraform(config *config.Root) *schema.Root {
conv(v, &l)
dst.Cluster = append(dst.Cluster, l)
}
}
tfroot.Resource.Pipeline[k] = &dst
// Configure permissions for this resource.
if rp := convPermissions(src.Permissions); rp != nil {
rp.PipelineId = fmt.Sprintf("${databricks_pipeline.%s.id}", k)
tfroot.Resource.Permissions["pipeline_"+k] = rp
}
}
for k, src := range config.Resources.Models {
var dst schema.ResourceMlflowModel
conv(src, &dst)
tfroot.Resource.MlflowModel[k] = &dst
// Configure permissions for this resource.
if rp := convPermissions(src.Permissions); rp != nil {
rp.RegisteredModelId = fmt.Sprintf("${databricks_mlflow_model.%s.registered_model_id}", k)
tfroot.Resource.Permissions["mlflow_model_"+k] = rp
}
}
for k, src := range config.Resources.Experiments {
var dst schema.ResourceMlflowExperiment
conv(src, &dst)
tfroot.Resource.MlflowExperiment[k] = &dst
// Configure permissions for this resource.
if rp := convPermissions(src.Permissions); rp != nil {
rp.ExperimentId = fmt.Sprintf("${databricks_mlflow_experiment.%s.id}", k)
tfroot.Resource.Permissions["mlflow_experiment_"+k] = rp
}
}
return tfroot

View File

@ -47,6 +47,33 @@ func TestConvertJob(t *testing.T) {
assert.Nil(t, out.Data)
}
func TestConvertJobPermissions(t *testing.T) {
var src = resources.Job{
Permissions: []resources.Permission{
{
Level: "CAN_VIEW",
UserName: "jane@doe.com",
},
},
}
var config = config.Root{
Resources: config.Resources{
Jobs: map[string]*resources.Job{
"my_job": &src,
},
},
}
out := BundleToTerraform(&config)
assert.NotEmpty(t, out.Resource.Permissions["job_my_job"].JobId)
assert.Len(t, out.Resource.Permissions["job_my_job"].AccessControl, 1)
p := out.Resource.Permissions["job_my_job"].AccessControl[0]
assert.Equal(t, "jane@doe.com", p.UserName)
assert.Equal(t, "CAN_VIEW", p.PermissionLevel)
}
func TestConvertJobTaskLibraries(t *testing.T) {
var src = resources.Job{
JobSettings: &jobs.JobSettings{
@ -81,6 +108,33 @@ func TestConvertJobTaskLibraries(t *testing.T) {
assert.Equal(t, "mlflow", out.Resource.Job["my_job"].Task[0].Library[0].Pypi.Package)
}
func TestConvertPipelinePermissions(t *testing.T) {
var src = resources.Pipeline{
Permissions: []resources.Permission{
{
Level: "CAN_VIEW",
UserName: "jane@doe.com",
},
},
}
var config = config.Root{
Resources: config.Resources{
Pipelines: map[string]*resources.Pipeline{
"my_pipeline": &src,
},
},
}
out := BundleToTerraform(&config)
assert.NotEmpty(t, out.Resource.Permissions["pipeline_my_pipeline"].PipelineId)
assert.Len(t, out.Resource.Permissions["pipeline_my_pipeline"].AccessControl, 1)
p := out.Resource.Permissions["pipeline_my_pipeline"].AccessControl[0]
assert.Equal(t, "jane@doe.com", p.UserName)
assert.Equal(t, "CAN_VIEW", p.PermissionLevel)
}
func TestConvertModel(t *testing.T) {
var src = resources.MlflowModel{
RegisteredModel: &mlflow.RegisteredModel{
@ -118,6 +172,33 @@ func TestConvertModel(t *testing.T) {
assert.Nil(t, out.Data)
}
func TestConvertModelPermissions(t *testing.T) {
var src = resources.MlflowModel{
Permissions: []resources.Permission{
{
Level: "CAN_READ",
UserName: "jane@doe.com",
},
},
}
var config = config.Root{
Resources: config.Resources{
Models: map[string]*resources.MlflowModel{
"my_model": &src,
},
},
}
out := BundleToTerraform(&config)
assert.NotEmpty(t, out.Resource.Permissions["mlflow_model_my_model"].RegisteredModelId)
assert.Len(t, out.Resource.Permissions["mlflow_model_my_model"].AccessControl, 1)
p := out.Resource.Permissions["mlflow_model_my_model"].AccessControl[0]
assert.Equal(t, "jane@doe.com", p.UserName)
assert.Equal(t, "CAN_READ", p.PermissionLevel)
}
func TestConvertExperiment(t *testing.T) {
var src = resources.MlflowExperiment{
Experiment: &mlflow.Experiment{
@ -137,3 +218,31 @@ func TestConvertExperiment(t *testing.T) {
assert.Equal(t, "name", out.Resource.MlflowExperiment["my_experiment"].Name)
assert.Nil(t, out.Data)
}
func TestConvertExperimentPermissions(t *testing.T) {
var src = resources.MlflowExperiment{
Permissions: []resources.Permission{
{
Level: "CAN_READ",
UserName: "jane@doe.com",
},
},
}
var config = config.Root{
Resources: config.Resources{
Experiments: map[string]*resources.MlflowExperiment{
"my_experiment": &src,
},
},
}
out := BundleToTerraform(&config)
assert.NotEmpty(t, out.Resource.Permissions["mlflow_experiment_my_experiment"].ExperimentId)
assert.Len(t, out.Resource.Permissions["mlflow_experiment_my_experiment"].AccessControl, 1)
p := out.Resource.Permissions["mlflow_experiment_my_experiment"].AccessControl[0]
assert.Equal(t, "jane@doe.com", p.UserName)
assert.Equal(t, "CAN_READ", p.PermissionLevel)
}