From 27fe4799560292ecfebb8d4a240cb674b2fc1f52 Mon Sep 17 00:00:00 2001 From: Denis Bilenko Date: Fri, 6 Mar 2026 10:30:28 +0100 Subject: [PATCH 01/28] Add plan for simplifying permissions types Analogous to simpler-grant: replace 12 resource-specific permission types with iam.AccessControlRequest from the SDK directly. Co-Authored-By: Claude Sonnet 4.6 --- PLAN_PERMISSIONS.md | 215 ++++++++++++++++++++++++++++++++++++++++++++ 1 file changed, 215 insertions(+) create mode 100644 PLAN_PERMISSIONS.md diff --git a/PLAN_PERMISSIONS.md b/PLAN_PERMISSIONS.md new file mode 100644 index 0000000000..3f3287ca5c --- /dev/null +++ b/PLAN_PERMISSIONS.md @@ -0,0 +1,215 @@ +# Plan: Simplify Permissions (analogous to simpler-grant) + +## What Was Done in simpler-grant + +### Problem +Each resource type had its own custom Grant type with a resource-specific privilege enum: +- `Grant` (generic, `Privileges []string`) for `registered_model` +- `SchemaGrant` with `SchemaGrantPrivilege` enum for `schema` +- `CatalogGrant` with `CatalogGrantPrivilege` enum for `catalog` +- `ExternalLocationGrant` with `ExternalLocationGrantPrivilege` enum for `external_location` +- `VolumeGrant` with `VolumeGrantPrivilege` enum for `volume` + +The `PrepareGrantsInputConfig` used reflection to convert any of these custom types into `[]catalog.PrivilegeAssignment` for the direct engine. + +The SDK already had `catalog.PrivilegeAssignment` (principal + `[]catalog.Privilege`) which covers all UC privileges. + +### What Was Changed +1. **Go**: Deleted `grant.go` and removed 5 custom Grant types + their privilege enums from `catalog.go`, `schema.go`, `volume.go`, `external_location.go`, `registered_model.go`. Replaced all `Grants` fields with `[]catalog.PrivilegeAssignment`. +2. **Go**: Replaced the reflection-based `PrepareGrantsInputConfig` with a simple type assertion `inputConfig.(*[]catalog.PrivilegeAssignment)`. +3. **Go**: Deleted `TestSchemaGrantPrivilegesExhaustive` test (which kept the old enum in sync with the SDK). +4. **Schema**: Updated `annotations.yml` (removed custom type annotations), `annotations_openapi_overrides.yml` (removed overrides for custom types), regenerated `jsonschema.json`. +5. **Python**: Removed per-resource grant/privilege model files (`schema_grant.py`, `schema_grant_privilege.py`, `catalog_grant.py`, etc.), added shared `privilege.py` and `privilege_assignment.py` under each resource package. +6. **Test data bug**: `testdata/pass/ml.yml` used `INSERT` as a privilege, which was accepted with the old `[]string` type but invalid with `catalog.Privilege` enum. Fixed by replacing with `MODIFY`. + +### Net Result +- Deleted ~950 lines, added ~111 lines +- No behavioral change — the SDK's `catalog.Privilege` enum is a superset of the old per-resource enums (which were manually maintained subsets) + +### Issues Encountered +1. **Schema testdata used invalid privilege value** (`INSERT` instead of a valid UC privilege like `MODIFY`). The old `Grant` type used `[]string` with no enum validation, hiding this. Only surfaced after switching to the typed SDK enum. Caught by CI (`validate-generated-is-up-to-date`). + - **Lesson**: After switching from `[]string`/untyped to a typed enum, check all testdata/acceptance files for values that are now invalid. + +--- + +## Plan: Simplify Permissions + +### Current State (analogous to the old grants situation) + +`bundle/config/resources/permission.go` defines: +- 12 resource-specific permission types: `AlertPermission`, `AppPermission`, `ClusterPermission`, `DashboardPermission`, `DatabaseInstancePermission`, `DatabaseProjectPermission`, `JobPermission`, `MlflowExperimentPermission`, `MlflowModelPermission`, `ModelServingEndpointPermission`, `PipelinePermission`, `SqlWarehousePermission` +- 12 corresponding level types: `AlertPermissionLevel`, `AppPermissionLevel`, etc. — all declared as `string` with no enum values (only `AlertPermissionLevel` has a `Values()` method) +- `IPermission` interface with 4 methods: `GetLevel()`, `GetUserName()`, `GetServicePrincipalName()`, `GetGroupName()`, `GetAPIRequestObjectType()` +- ~180 lines of boilerplate: `GetLevel`/`GetUserName`/`GetServicePrincipalName`/`GetGroupName` implementations repeated 12 times +- `GetAPIRequestObjectType()` per type (maps to API path prefix like `/jobs/`, `/pipelines/`, etc.) + +`bundle/direct/dresources/permissions.go` uses reflection to iterate over the slice, casts each element to `IPermission`, extracts level/principal fields, and converts to `[]iam.AccessControlRequest`. + +The SDK's `iam.AccessControlRequest` already has: `PermissionLevel iam.PermissionLevel`, `UserName`, `GroupName`, `ServicePrincipalName`. + +### Goal + +Replace all 12 resource-specific permission types with `iam.AccessControlRequest` directly from the SDK, analogous to how `catalog.PrivilegeAssignment` replaced the custom grant types. + +The object type prefix (e.g. `/jobs/`) needs to come from somewhere — currently embedded in each type via `GetAPIRequestObjectType()`. This must be handled differently (see Step 2 below). + +### Steps + +#### Step 1: Understand how `GetAPIRequestObjectType` is used + +`PreparePermissionsInputConfig` uses it to build the `objectIdRef`: +```go +prefix := zeroValueInterface.GetAPIRequestObjectType() +objectIdRef := prefix + "${" + baseNode + ".id}" +``` + +The mapping is: +| Type | Prefix | +|------|--------| +| AlertPermission | `/alertsv2/` | +| AppPermission | `/apps/` | +| ClusterPermission | `/clusters/` | +| DashboardPermission | `/dashboards/` | +| DatabaseInstancePermission | `/database-instances/` | +| DatabaseProjectPermission | `/database-projects/` | +| JobPermission | `/jobs/` | +| MlflowExperimentPermission | `/experiments/` | +| MlflowModelPermission | `/registered-models/` | +| ModelServingEndpointPermission | `/serving-endpoints/` | +| PipelinePermission | `/pipelines/` | +| SqlWarehousePermission | `/sql/warehouses/` | + +This mapping is from resource type name (in the bundle node path) to object type prefix. It can be extracted from the node path (`baseNode`) directly, e.g. `resources.jobs.myjob.permissions` → `/jobs/`. + +#### Step 2: Replace IPermission dispatch with a static map + +Add a map in `permissions.go` (analogous to `grantResourceToSecurableType` in `grants.go`): +```go +var permissionResourceToObjectType = map[string]string{ + "alerts": "/alertsv2/", + "apps": "/apps/", + "clusters": "/clusters/", + "dashboards": "/dashboards/", + "database_instances": "/database-instances/", + "postgres_projects": "/database-projects/", + "jobs": "/jobs/", + "experiments": "/experiments/", + "models": "/registered-models/", + "model_serving_endpoints": "/serving-endpoints/", + "pipelines": "/pipelines/", + "sql_warehouses": "/sql/warehouses/", +} +``` + +Then extract resource type from `baseNode` (e.g. `resources.jobs.myjob` → `jobs`) and look it up. + +Note: The existing special cases in `PreparePermissionsInputConfig` for `model_serving_endpoints` (uses `.endpoint_id` not `.id`) and `postgres_projects` (uses `.project_id`) are already handled via string prefix checks and can remain. + +#### Step 3: Change resource structs (Go) + +In each resource file, change: +```go +Permissions []JobPermission `json:"permissions,omitempty"` +``` +to: +```go +Permissions []iam.AccessControlRequest `json:"permissions,omitempty"` +``` + +Resources affected: `alerts.go`, `apps.go`, `clusters.go`, `dashboard.go`, `database_instance.go`, `job.go`, `mlflow_experiment.go`, `mlflow_model.go`, `model_serving_endpoint.go`, `pipeline.go`, `postgres_project.go`, `sql_warehouses.go`. + +Note: `secret_scope.go` uses `SecretScopePermission` (different mechanism — secret ACLs, not the Permissions API). Leave it unchanged. + +#### Step 4: Simplify `PreparePermissionsInputConfig` (direct engine) + +Replace the reflection-based conversion with a simple type assertion: +```go +permissionsPtr, ok := inputConfig.(*[]iam.AccessControlRequest) +if !ok { + return nil, fmt.Errorf("expected *[]iam.AccessControlRequest, got %T", inputConfig) +} +permissions := *permissionsPtr +``` + +Remove the `reflect` import, remove the `IPermission` cast loop. + +#### Step 5: Delete `permission.go` content + +Remove from `bundle/config/resources/permission.go`: +- All 12 `*PermissionLevel` type declarations +- All 12 `*Permission` struct types +- All `GetAPIRequestObjectType()` methods +- All `GetLevel()`/`GetUserName()`/`GetServicePrincipalName()`/`GetGroupName()` boilerplate +- `IPermission` interface +- The unused `Permission` struct (the generic one at top — check if still used anywhere) + +Keep in `permission.go`: +- `PermissionOrder`, `GetLevelScore`, `CompareLevels`, `GetMaxLevel` (used by workspace permissions logic) + +#### Step 6: Update schema annotations + +In `bundle/internal/schema/annotations.yml`, remove the per-type permission annotations (e.g. `github.com/databricks/cli/bundle/config/resources.AlertPermission:`, etc.) since those types are gone. The `iam.AccessControlRequest` type from the SDK will be used instead, annotated via `annotations_openapi.yml`. + +Check `annotations_openapi_overrides.yml` for any overrides on the old permission types — remove those too. + +#### Step 7: Regenerate schema and validate + +```bash +make schema +``` + +Verify `bundle/schema/jsonschema.json` and `bundle/schema/jsonschema_for_docs.json` look correct — the `permissions` field on each resource should reference `iam.AccessControlRequest` schema from SDK, which will have `permission_level` (enum of `iam.PermissionLevel` values), `user_name`, `group_name`, `service_principal_name`. + +#### Step 8: Update Python models + +Remove per-resource permission model files: +- `python/databricks/bundles/jobs/_models/job_permission.py` +- `python/databricks/bundles/jobs/_models/job_permission_level.py` +- `python/databricks/bundles/pipelines/_models/pipeline_permission.py` +- `python/databricks/bundles/pipelines/_models/pipeline_permission_level.py` +- Any other `*_permission.py` / `*_permission_level.py` files + +Add shared `access_control_request.py` and `permission_level.py` under affected packages (jobs, pipelines, etc.), generated from SDK types. + +Update `__init__.py` files accordingly. + +Run: `cd python && make codegen && make test && make lint && make docs` + +#### Step 9: Check testdata/acceptance for now-invalid permission level values + +**Lesson learned from simpler-grant**: The old `*PermissionLevel` types are declared as `string` with no actual enum validation in the schema (only `AlertPermissionLevel` has `Values()`). After switching to `iam.PermissionLevel`, the schema will enforce the enum. Scan all testdata and acceptance files: + +```bash +grep -rn "level:" bundle/internal/schema/testdata/ acceptance/ --include="*.yml" +``` + +Ensure all `level:` values are valid `iam.PermissionLevel` values: +`CAN_ATTACH_TO`, `CAN_BIND`, `CAN_CREATE`, `CAN_EDIT`, `CAN_EDIT_METADATA`, `CAN_MANAGE`, `CAN_MANAGE_PRODUCTION_VERSIONS`, `CAN_MANAGE_RUN`, `CAN_MANAGE_STAGING_VERSIONS`, `CAN_MONITOR`, `CAN_MONITOR_ONLY`, `CAN_QUERY`, `CAN_READ`, `CAN_RESTART`, `CAN_RUN`, `CAN_USE`, `CAN_VIEW`, `CAN_VIEW_METADATA`, `IS_OWNER`. + +Also check Python test files and acceptance scripts. + +#### Step 10: Run tests + +```bash +make test +make schema # if schema-related code changed +cd python && make codegen && make test && make lint && make docs +``` + +--- + +## Key Differences vs simpler-grant + +| Aspect | simpler-grant | simpler-permissions | +|--------|--------------|---------------------| +| Custom types deleted | 5 (one per resource) | ~12 (one per resource type) | +| API object type dispatch | N/A (securable type from node name) | Must map resource type → object type prefix | +| SDK replacement type | `catalog.PrivilegeAssignment` | `iam.AccessControlRequest` | +| Field name change | `privileges []string` → `[]catalog.Privilege` (enum enforced) | `level string` → `iam.PermissionLevel` (enum enforced) | +| Reflection removed | Yes (grants.go) | Yes (permissions.go) | +| Exceptions | None | `secret_scope` (ACL, not Permissions API); `model_serving_endpoints` (uses `.endpoint_id`); `postgres_projects` (uses `.project_id`) | +| Schema enum impact | `INSERT` was invalid (caught by CI) | Any invalid `CAN_*` values will be caught — scan testdata first | + +## Risk: `iam.PermissionLevel` is a shared enum + +`iam.PermissionLevel` covers ALL resource types' permission levels combined. Some levels are only valid for specific resources (e.g. `CAN_ATTACH_TO` is only for clusters). This is acceptable — same as with `catalog.Privilege` being a superset. The schema will allow any valid `iam.PermissionLevel` on any resource, which is slightly more permissive than the current per-resource enums (but those had no per-resource values anyway — all defined as bare `string`). From 858a559c882457fac4be1d8f938f98d0642f90f8 Mon Sep 17 00:00:00 2001 From: Denis Bilenko Date: Fri, 6 Mar 2026 16:34:28 +0100 Subject: [PATCH 02/28] Replace per-resource permission types with shared Permission type - Remove 12 per-resource permission structs (JobPermission, PipelinePermission, etc.) and corresponding PermissionLevel enum types in favor of shared resources.Permission - Remove IPermission interface and reflection-based dispatch in dresources/permissions.go - Replace with static permissionResourceToObjectType map and direct type assertion - Update annotations.yml and annotations_openapi_overrides.yml to remove obsolete entries - Regenerate JSON schema - Add backward-compat aliases (JobPermission, PipelinePermission, etc.) in Python __init__.py Co-Authored-By: Claude Sonnet 4.6 --- .claude/settings.local.json | 16 + .../apply_bundle_permissions_test.go | 74 +- .../config/mutator/resourcemutator/run_as.go | 4 +- .../validate_target_mode_test.go | 12 +- bundle/config/resources/alerts.go | 2 +- bundle/config/resources/apps.go | 2 +- bundle/config/resources/clusters.go | 2 +- bundle/config/resources/dashboard.go | 2 +- bundle/config/resources/database_instance.go | 2 +- bundle/config/resources/job.go | 2 +- bundle/config/resources/mlflow_experiment.go | 2 +- bundle/config/resources/mlflow_model.go | 2 +- .../resources/model_serving_endpoint.go | 2 +- bundle/config/resources/permission.go | 210 ----- bundle/config/resources/pipeline.go | 2 +- bundle/config/resources/postgres_project.go | 2 +- bundle/config/resources/sql_warehouses.go | 2 +- bundle/config/resources_types_test.go | 2 +- bundle/deploy/terraform/convert_test.go | 10 +- .../terraform/tfdyn/convert_alert_test.go | 2 +- .../terraform/tfdyn/convert_app_test.go | 2 +- .../terraform/tfdyn/convert_cluster_test.go | 2 +- .../terraform/tfdyn/convert_dashboard_test.go | 2 +- .../tfdyn/convert_database_instance_test.go | 2 +- .../tfdyn/convert_experiment_test.go | 2 +- .../terraform/tfdyn/convert_job_test.go | 2 +- .../convert_model_serving_endpoint_test.go | 2 +- .../terraform/tfdyn/convert_model_test.go | 2 +- .../tfdyn/convert_permissions_test.go | 4 +- .../terraform/tfdyn/convert_pipeline_test.go | 2 +- .../tfdyn/convert_postgres_project_test.go | 2 +- bundle/direct/dresources/permissions.go | 55 +- bundle/internal/schema/annotations.yml | 156 ---- .../schema/annotations_openapi_overrides.yml | 118 --- bundle/schema/jsonschema.json | 767 +----------------- bundle/tests/bundle_permissions_test.go | 32 +- bundle/tests/model_serving_endpoint_test.go | 2 +- python/codegen/codegen/aliases_patch.py | 10 + python/databricks/bundles/jobs/__init__.py | 24 +- python/databricks/bundles/jobs/_models/job.py | 9 +- .../bundles/jobs/_models/job_permission.py | 48 -- .../jobs/_models/job_permission_level.py | 14 - .../bundles/jobs/_models/permission.py | 68 ++ .../databricks/bundles/pipelines/__init__.py | 24 +- .../bundles/pipelines/_models/permission.py | 68 ++ .../bundles/pipelines/_models/pipeline.py | 9 +- .../pipelines/_models/pipeline_permission.py | 48 -- .../_models/pipeline_permission_level.py | 14 - 48 files changed, 348 insertions(+), 1496 deletions(-) create mode 100644 .claude/settings.local.json delete mode 100644 python/databricks/bundles/jobs/_models/job_permission.py delete mode 100644 python/databricks/bundles/jobs/_models/job_permission_level.py create mode 100644 python/databricks/bundles/jobs/_models/permission.py create mode 100644 python/databricks/bundles/pipelines/_models/permission.py delete mode 100644 python/databricks/bundles/pipelines/_models/pipeline_permission.py delete mode 100644 python/databricks/bundles/pipelines/_models/pipeline_permission_level.py diff --git a/.claude/settings.local.json b/.claude/settings.local.json new file mode 100644 index 0000000000..823f03a07a --- /dev/null +++ b/.claude/settings.local.json @@ -0,0 +1,16 @@ +{ + "permissions": { + "allow": [ + "Bash(go run:*)", + "Bash(python3:*)", + "Bash(make fmtfull:*)", + "Bash(make codegen:*)", + "Bash(git:*)", + "Bash(GIT_EDITOR=true GIT_SEQUENCE_EDITOR=true VISUAL=true GIT_PAGER=cat git rebase:*)", + "Read(//Users/denis.bilenko/work/cli/.git/worktrees/cli-main-simpler-grant/rebase-merge/**)", + "Read(//Users/denis.bilenko/work/cli/**)", + "Bash(GIT_EDITOR=true GIT_PAGER=cat git diff:*)", + "Bash(GIT_PAGER=cat git rebase:*)" + ] + } +} diff --git a/bundle/config/mutator/resourcemutator/apply_bundle_permissions_test.go b/bundle/config/mutator/resourcemutator/apply_bundle_permissions_test.go index 4923f84f22..28ee4bdaae 100644 --- a/bundle/config/mutator/resourcemutator/apply_bundle_permissions_test.go +++ b/bundle/config/mutator/resourcemutator/apply_bundle_permissions_test.go @@ -86,58 +86,58 @@ func TestApplyBundlePermissions(t *testing.T) { require.NoError(t, diags.Error()) require.Len(t, b.Config.Resources.Jobs["job_1"].Permissions, 3) - require.Contains(t, b.Config.Resources.Jobs["job_1"].Permissions, resources.JobPermission{Level: "CAN_MANAGE", UserName: "TestUser"}) - require.Contains(t, b.Config.Resources.Jobs["job_1"].Permissions, resources.JobPermission{Level: "CAN_VIEW", GroupName: "TestGroup"}) - require.Contains(t, b.Config.Resources.Jobs["job_1"].Permissions, resources.JobPermission{Level: "CAN_MANAGE_RUN", ServicePrincipalName: "TestServicePrincipal"}) + require.Contains(t, b.Config.Resources.Jobs["job_1"].Permissions, resources.Permission{Level: "CAN_MANAGE", UserName: "TestUser"}) + require.Contains(t, b.Config.Resources.Jobs["job_1"].Permissions, resources.Permission{Level: "CAN_VIEW", GroupName: "TestGroup"}) + require.Contains(t, b.Config.Resources.Jobs["job_1"].Permissions, resources.Permission{Level: "CAN_MANAGE_RUN", ServicePrincipalName: "TestServicePrincipal"}) require.Len(t, b.Config.Resources.Jobs["job_2"].Permissions, 3) - require.Contains(t, b.Config.Resources.Jobs["job_2"].Permissions, resources.JobPermission{Level: "CAN_MANAGE", UserName: "TestUser"}) - require.Contains(t, b.Config.Resources.Jobs["job_2"].Permissions, resources.JobPermission{Level: "CAN_VIEW", GroupName: "TestGroup"}) - require.Contains(t, b.Config.Resources.Jobs["job_2"].Permissions, resources.JobPermission{Level: "CAN_MANAGE_RUN", ServicePrincipalName: "TestServicePrincipal"}) + require.Contains(t, b.Config.Resources.Jobs["job_2"].Permissions, resources.Permission{Level: "CAN_MANAGE", UserName: "TestUser"}) + require.Contains(t, b.Config.Resources.Jobs["job_2"].Permissions, resources.Permission{Level: "CAN_VIEW", GroupName: "TestGroup"}) + require.Contains(t, b.Config.Resources.Jobs["job_2"].Permissions, resources.Permission{Level: "CAN_MANAGE_RUN", ServicePrincipalName: "TestServicePrincipal"}) require.Len(t, b.Config.Resources.Pipelines["pipeline_1"].Permissions, 3) - require.Contains(t, b.Config.Resources.Pipelines["pipeline_1"].Permissions, resources.PipelinePermission{Level: "CAN_MANAGE", UserName: "TestUser"}) - require.Contains(t, b.Config.Resources.Pipelines["pipeline_1"].Permissions, resources.PipelinePermission{Level: "CAN_VIEW", GroupName: "TestGroup"}) - require.Contains(t, b.Config.Resources.Pipelines["pipeline_1"].Permissions, resources.PipelinePermission{Level: "CAN_RUN", ServicePrincipalName: "TestServicePrincipal"}) + require.Contains(t, b.Config.Resources.Pipelines["pipeline_1"].Permissions, resources.Permission{Level: "CAN_MANAGE", UserName: "TestUser"}) + require.Contains(t, b.Config.Resources.Pipelines["pipeline_1"].Permissions, resources.Permission{Level: "CAN_VIEW", GroupName: "TestGroup"}) + require.Contains(t, b.Config.Resources.Pipelines["pipeline_1"].Permissions, resources.Permission{Level: "CAN_RUN", ServicePrincipalName: "TestServicePrincipal"}) require.Len(t, b.Config.Resources.Pipelines["pipeline_2"].Permissions, 3) - require.Contains(t, b.Config.Resources.Pipelines["pipeline_2"].Permissions, resources.PipelinePermission{Level: "CAN_MANAGE", UserName: "TestUser"}) - require.Contains(t, b.Config.Resources.Pipelines["pipeline_2"].Permissions, resources.PipelinePermission{Level: "CAN_VIEW", GroupName: "TestGroup"}) - require.Contains(t, b.Config.Resources.Pipelines["pipeline_2"].Permissions, resources.PipelinePermission{Level: "CAN_RUN", ServicePrincipalName: "TestServicePrincipal"}) + require.Contains(t, b.Config.Resources.Pipelines["pipeline_2"].Permissions, resources.Permission{Level: "CAN_MANAGE", UserName: "TestUser"}) + require.Contains(t, b.Config.Resources.Pipelines["pipeline_2"].Permissions, resources.Permission{Level: "CAN_VIEW", GroupName: "TestGroup"}) + require.Contains(t, b.Config.Resources.Pipelines["pipeline_2"].Permissions, resources.Permission{Level: "CAN_RUN", ServicePrincipalName: "TestServicePrincipal"}) require.Len(t, b.Config.Resources.Models["model_1"].Permissions, 2) - require.Contains(t, b.Config.Resources.Models["model_1"].Permissions, resources.MlflowModelPermission{Level: "CAN_MANAGE", UserName: "TestUser"}) - require.Contains(t, b.Config.Resources.Models["model_1"].Permissions, resources.MlflowModelPermission{Level: "CAN_READ", GroupName: "TestGroup"}) + require.Contains(t, b.Config.Resources.Models["model_1"].Permissions, resources.Permission{Level: "CAN_MANAGE", UserName: "TestUser"}) + require.Contains(t, b.Config.Resources.Models["model_1"].Permissions, resources.Permission{Level: "CAN_READ", GroupName: "TestGroup"}) require.Len(t, b.Config.Resources.Models["model_2"].Permissions, 2) - require.Contains(t, b.Config.Resources.Models["model_2"].Permissions, resources.MlflowModelPermission{Level: "CAN_MANAGE", UserName: "TestUser"}) - require.Contains(t, b.Config.Resources.Models["model_2"].Permissions, resources.MlflowModelPermission{Level: "CAN_READ", GroupName: "TestGroup"}) + require.Contains(t, b.Config.Resources.Models["model_2"].Permissions, resources.Permission{Level: "CAN_MANAGE", UserName: "TestUser"}) + require.Contains(t, b.Config.Resources.Models["model_2"].Permissions, resources.Permission{Level: "CAN_READ", GroupName: "TestGroup"}) require.Len(t, b.Config.Resources.Experiments["experiment_1"].Permissions, 2) - require.Contains(t, b.Config.Resources.Experiments["experiment_1"].Permissions, resources.MlflowExperimentPermission{Level: "CAN_MANAGE", UserName: "TestUser"}) - require.Contains(t, b.Config.Resources.Experiments["experiment_1"].Permissions, resources.MlflowExperimentPermission{Level: "CAN_READ", GroupName: "TestGroup"}) + require.Contains(t, b.Config.Resources.Experiments["experiment_1"].Permissions, resources.Permission{Level: "CAN_MANAGE", UserName: "TestUser"}) + require.Contains(t, b.Config.Resources.Experiments["experiment_1"].Permissions, resources.Permission{Level: "CAN_READ", GroupName: "TestGroup"}) require.Len(t, b.Config.Resources.Experiments["experiment_2"].Permissions, 2) - require.Contains(t, b.Config.Resources.Experiments["experiment_2"].Permissions, resources.MlflowExperimentPermission{Level: "CAN_MANAGE", UserName: "TestUser"}) - require.Contains(t, b.Config.Resources.Experiments["experiment_2"].Permissions, resources.MlflowExperimentPermission{Level: "CAN_READ", GroupName: "TestGroup"}) + require.Contains(t, b.Config.Resources.Experiments["experiment_2"].Permissions, resources.Permission{Level: "CAN_MANAGE", UserName: "TestUser"}) + require.Contains(t, b.Config.Resources.Experiments["experiment_2"].Permissions, resources.Permission{Level: "CAN_READ", GroupName: "TestGroup"}) require.Len(t, b.Config.Resources.ModelServingEndpoints["endpoint_1"].Permissions, 3) - require.Contains(t, b.Config.Resources.ModelServingEndpoints["endpoint_1"].Permissions, resources.ModelServingEndpointPermission{Level: "CAN_MANAGE", UserName: "TestUser"}) - require.Contains(t, b.Config.Resources.ModelServingEndpoints["endpoint_1"].Permissions, resources.ModelServingEndpointPermission{Level: "CAN_VIEW", GroupName: "TestGroup"}) - require.Contains(t, b.Config.Resources.ModelServingEndpoints["endpoint_1"].Permissions, resources.ModelServingEndpointPermission{Level: "CAN_QUERY", ServicePrincipalName: "TestServicePrincipal"}) + require.Contains(t, b.Config.Resources.ModelServingEndpoints["endpoint_1"].Permissions, resources.Permission{Level: "CAN_MANAGE", UserName: "TestUser"}) + require.Contains(t, b.Config.Resources.ModelServingEndpoints["endpoint_1"].Permissions, resources.Permission{Level: "CAN_VIEW", GroupName: "TestGroup"}) + require.Contains(t, b.Config.Resources.ModelServingEndpoints["endpoint_1"].Permissions, resources.Permission{Level: "CAN_QUERY", ServicePrincipalName: "TestServicePrincipal"}) require.Len(t, b.Config.Resources.ModelServingEndpoints["endpoint_2"].Permissions, 3) - require.Contains(t, b.Config.Resources.ModelServingEndpoints["endpoint_2"].Permissions, resources.ModelServingEndpointPermission{Level: "CAN_MANAGE", UserName: "TestUser"}) - require.Contains(t, b.Config.Resources.ModelServingEndpoints["endpoint_2"].Permissions, resources.ModelServingEndpointPermission{Level: "CAN_VIEW", GroupName: "TestGroup"}) - require.Contains(t, b.Config.Resources.ModelServingEndpoints["endpoint_2"].Permissions, resources.ModelServingEndpointPermission{Level: "CAN_QUERY", ServicePrincipalName: "TestServicePrincipal"}) + require.Contains(t, b.Config.Resources.ModelServingEndpoints["endpoint_2"].Permissions, resources.Permission{Level: "CAN_MANAGE", UserName: "TestUser"}) + require.Contains(t, b.Config.Resources.ModelServingEndpoints["endpoint_2"].Permissions, resources.Permission{Level: "CAN_VIEW", GroupName: "TestGroup"}) + require.Contains(t, b.Config.Resources.ModelServingEndpoints["endpoint_2"].Permissions, resources.Permission{Level: "CAN_QUERY", ServicePrincipalName: "TestServicePrincipal"}) require.Len(t, b.Config.Resources.Dashboards["dashboard_1"].Permissions, 2) - require.Contains(t, b.Config.Resources.Dashboards["dashboard_1"].Permissions, resources.DashboardPermission{Level: "CAN_MANAGE", UserName: "TestUser"}) - require.Contains(t, b.Config.Resources.Dashboards["dashboard_1"].Permissions, resources.DashboardPermission{Level: "CAN_READ", GroupName: "TestGroup"}) + require.Contains(t, b.Config.Resources.Dashboards["dashboard_1"].Permissions, resources.Permission{Level: "CAN_MANAGE", UserName: "TestUser"}) + require.Contains(t, b.Config.Resources.Dashboards["dashboard_1"].Permissions, resources.Permission{Level: "CAN_READ", GroupName: "TestGroup"}) require.Len(t, b.Config.Resources.Apps["app_1"].Permissions, 2) - require.Contains(t, b.Config.Resources.Apps["app_1"].Permissions, resources.AppPermission{Level: "CAN_MANAGE", UserName: "TestUser"}) - require.Contains(t, b.Config.Resources.Apps["app_1"].Permissions, resources.AppPermission{Level: "CAN_USE", GroupName: "TestGroup"}) + require.Contains(t, b.Config.Resources.Apps["app_1"].Permissions, resources.Permission{Level: "CAN_MANAGE", UserName: "TestUser"}) + require.Contains(t, b.Config.Resources.Apps["app_1"].Permissions, resources.Permission{Level: "CAN_USE", GroupName: "TestGroup"}) } func TestWarningOnOverlapPermission(t *testing.T) { @@ -156,7 +156,7 @@ func TestWarningOnOverlapPermission(t *testing.T) { JobSettings: jobs.JobSettings{ Name: "job_1", }, - Permissions: []resources.JobPermission{ + Permissions: []resources.Permission{ {Level: "CAN_VIEW", UserName: "TestUser"}, }, }, @@ -164,7 +164,7 @@ func TestWarningOnOverlapPermission(t *testing.T) { JobSettings: jobs.JobSettings{ Name: "job_2", }, - Permissions: []resources.JobPermission{ + Permissions: []resources.Permission{ {Level: "CAN_VIEW", UserName: "TestUser2"}, }, }, @@ -176,11 +176,11 @@ func TestWarningOnOverlapPermission(t *testing.T) { diags := bundle.Apply(t.Context(), b, ApplyBundlePermissions()) require.NoError(t, diags.Error()) - require.Contains(t, b.Config.Resources.Jobs["job_1"].Permissions, resources.JobPermission{Level: "CAN_VIEW", UserName: "TestUser"}) - require.Contains(t, b.Config.Resources.Jobs["job_1"].Permissions, resources.JobPermission{Level: "CAN_VIEW", GroupName: "TestGroup"}) - require.Contains(t, b.Config.Resources.Jobs["job_2"].Permissions, resources.JobPermission{Level: "CAN_VIEW", UserName: "TestUser2"}) - require.Contains(t, b.Config.Resources.Jobs["job_2"].Permissions, resources.JobPermission{Level: "CAN_MANAGE", UserName: "TestUser"}) - require.Contains(t, b.Config.Resources.Jobs["job_2"].Permissions, resources.JobPermission{Level: "CAN_VIEW", GroupName: "TestGroup"}) + require.Contains(t, b.Config.Resources.Jobs["job_1"].Permissions, resources.Permission{Level: "CAN_VIEW", UserName: "TestUser"}) + require.Contains(t, b.Config.Resources.Jobs["job_1"].Permissions, resources.Permission{Level: "CAN_VIEW", GroupName: "TestGroup"}) + require.Contains(t, b.Config.Resources.Jobs["job_2"].Permissions, resources.Permission{Level: "CAN_VIEW", UserName: "TestUser2"}) + require.Contains(t, b.Config.Resources.Jobs["job_2"].Permissions, resources.Permission{Level: "CAN_MANAGE", UserName: "TestUser"}) + require.Contains(t, b.Config.Resources.Jobs["job_2"].Permissions, resources.Permission{Level: "CAN_VIEW", GroupName: "TestGroup"}) } func TestAllResourcesExplicitlyDefinedForPermissionsSupport(t *testing.T) { diff --git a/bundle/config/mutator/resourcemutator/run_as.go b/bundle/config/mutator/resourcemutator/run_as.go index d53d05ed13..96c2e52d89 100644 --- a/bundle/config/mutator/resourcemutator/run_as.go +++ b/bundle/config/mutator/resourcemutator/run_as.go @@ -196,11 +196,11 @@ func setPipelineOwnersToRunAsIdentity(b *bundle.Bundle) { for i := range b.Config.Resources.Pipelines { pipeline := b.Config.Resources.Pipelines[i] - pipeline.Permissions = slices.DeleteFunc(pipeline.Permissions, func(p resources.PipelinePermission) bool { + pipeline.Permissions = slices.DeleteFunc(pipeline.Permissions, func(p resources.Permission) bool { return (runAs.ServicePrincipalName != "" && p.ServicePrincipalName == runAs.ServicePrincipalName) || (runAs.UserName != "" && p.UserName == runAs.UserName) }) - pipeline.Permissions = append(pipeline.Permissions, resources.PipelinePermission{ + pipeline.Permissions = append(pipeline.Permissions, resources.Permission{ Level: "IS_OWNER", ServicePrincipalName: runAs.ServicePrincipalName, UserName: runAs.UserName, diff --git a/bundle/config/mutator/resourcemutator/validate_target_mode_test.go b/bundle/config/mutator/resourcemutator/validate_target_mode_test.go index bf82773d9e..f2bf0bad0c 100644 --- a/bundle/config/mutator/resourcemutator/validate_target_mode_test.go +++ b/bundle/config/mutator/resourcemutator/validate_target_mode_test.go @@ -48,37 +48,37 @@ func TestProcessTargetModeProduction(t *testing.T) { diags = validateProductionMode(b, false) require.ErrorContains(t, diags.Error(), "A common practice is to use a username or principal name in this path, i.e. use\n\n root_path: /Workspace/Users/lennart@company.com/.bundle/${bundle.name}/${bundle.target}") - jobPermissions := []resources.JobPermission{ + jobPermissions := []resources.Permission{ { Level: "CAN_MANAGE", UserName: "user@company.com", }, } - pipelinePermissions := []resources.PipelinePermission{ + pipelinePermissions := []resources.Permission{ { Level: "CAN_MANAGE", UserName: "user@company.com", }, } - experimentPermissions := []resources.MlflowExperimentPermission{ + experimentPermissions := []resources.Permission{ { Level: "CAN_MANAGE", UserName: "user@company.com", }, } - modelPermissions := []resources.MlflowModelPermission{ + modelPermissions := []resources.Permission{ { Level: "CAN_MANAGE", UserName: "user@company.com", }, } - endpointPermissions := []resources.ModelServingEndpointPermission{ + endpointPermissions := []resources.Permission{ { Level: "CAN_MANAGE", UserName: "user@company.com", }, } - clusterPermissions := []resources.ClusterPermission{ + clusterPermissions := []resources.Permission{ { Level: "CAN_MANAGE", UserName: "user@company.com", diff --git a/bundle/config/resources/alerts.go b/bundle/config/resources/alerts.go index 628a9acdf1..bfdd64e900 100644 --- a/bundle/config/resources/alerts.go +++ b/bundle/config/resources/alerts.go @@ -14,7 +14,7 @@ type Alert struct { BaseResource sql.AlertV2 //nolint AlertV2 also defines Id and URL field with the same json tag "id" and "url" - Permissions []AlertPermission `json:"permissions,omitempty"` + Permissions []Permission `json:"permissions,omitempty"` // Filepath points to the local .dbalert.json file containing the alert definition. // If specified, any fields that are part of the .dbalert.json file schema will not be allowed in diff --git a/bundle/config/resources/apps.go b/bundle/config/resources/apps.go index e48f6e7dae..7375ab6c2d 100644 --- a/bundle/config/resources/apps.go +++ b/bundle/config/resources/apps.go @@ -50,7 +50,7 @@ type App struct { // This is used in conjunction with GitRepository (from apps.App) and is passed to the Deploy API. GitSource *apps.GitSource `json:"git_source,omitempty"` - Permissions []AppPermission `json:"permissions,omitempty"` + Permissions []Permission `json:"permissions,omitempty"` } func (a *App) UnmarshalJSON(b []byte) error { diff --git a/bundle/config/resources/clusters.go b/bundle/config/resources/clusters.go index c549ac4a6b..deba448647 100644 --- a/bundle/config/resources/clusters.go +++ b/bundle/config/resources/clusters.go @@ -14,7 +14,7 @@ type Cluster struct { BaseResource compute.ClusterSpec - Permissions []ClusterPermission `json:"permissions,omitempty"` + Permissions []Permission `json:"permissions,omitempty"` } func (s *Cluster) UnmarshalJSON(b []byte) error { diff --git a/bundle/config/resources/dashboard.go b/bundle/config/resources/dashboard.go index 6f2ac7b8c8..c108ac8abe 100644 --- a/bundle/config/resources/dashboard.go +++ b/bundle/config/resources/dashboard.go @@ -80,7 +80,7 @@ type Dashboard struct { BaseResource DashboardConfig - Permissions []DashboardPermission `json:"permissions,omitempty"` + Permissions []Permission `json:"permissions,omitempty"` // FilePath points to the local `.lvdash.json` file containing the dashboard definition. // This is inlined into serialized_dashboard during deployment. The file_path is kept around diff --git a/bundle/config/resources/database_instance.go b/bundle/config/resources/database_instance.go index 6734dfd07d..2ba19b84f4 100644 --- a/bundle/config/resources/database_instance.go +++ b/bundle/config/resources/database_instance.go @@ -14,7 +14,7 @@ type DatabaseInstance struct { BaseResource database.DatabaseInstance - Permissions []DatabaseInstancePermission `json:"permissions,omitempty"` + Permissions []Permission `json:"permissions,omitempty"` } func (d *DatabaseInstance) Exists(ctx context.Context, w *databricks.WorkspaceClient, name string) (bool, error) { diff --git a/bundle/config/resources/job.go b/bundle/config/resources/job.go index b2b7ff15f1..5c3eb8b29d 100644 --- a/bundle/config/resources/job.go +++ b/bundle/config/resources/job.go @@ -15,7 +15,7 @@ type Job struct { BaseResource jobs.JobSettings - Permissions []JobPermission `json:"permissions,omitempty"` + Permissions []Permission `json:"permissions,omitempty"` } func (j *Job) UnmarshalJSON(b []byte) error { diff --git a/bundle/config/resources/mlflow_experiment.go b/bundle/config/resources/mlflow_experiment.go index 0a2a36b840..e80ee26250 100644 --- a/bundle/config/resources/mlflow_experiment.go +++ b/bundle/config/resources/mlflow_experiment.go @@ -14,7 +14,7 @@ type MlflowExperiment struct { BaseResource ml.CreateExperiment - Permissions []MlflowExperimentPermission `json:"permissions,omitempty"` + Permissions []Permission `json:"permissions,omitempty"` } func (s *MlflowExperiment) UnmarshalJSON(b []byte) error { diff --git a/bundle/config/resources/mlflow_model.go b/bundle/config/resources/mlflow_model.go index a867f55a0e..1fa2cac8e8 100644 --- a/bundle/config/resources/mlflow_model.go +++ b/bundle/config/resources/mlflow_model.go @@ -14,7 +14,7 @@ type MlflowModel struct { BaseResource ml.CreateModelRequest - Permissions []MlflowModelPermission `json:"permissions,omitempty"` + Permissions []Permission `json:"permissions,omitempty"` } func (s *MlflowModel) UnmarshalJSON(b []byte) error { diff --git a/bundle/config/resources/model_serving_endpoint.go b/bundle/config/resources/model_serving_endpoint.go index d3e390596d..9d98c15094 100644 --- a/bundle/config/resources/model_serving_endpoint.go +++ b/bundle/config/resources/model_serving_endpoint.go @@ -19,7 +19,7 @@ type ModelServingEndpoint struct { // This is a resource agnostic implementation of permissions for ACLs. // Implementation could be different based on the resource type. - Permissions []ModelServingEndpointPermission `json:"permissions,omitempty"` + Permissions []Permission `json:"permissions,omitempty"` } func (s *ModelServingEndpoint) UnmarshalJSON(b []byte) error { diff --git a/bundle/config/resources/permission.go b/bundle/config/resources/permission.go index 8964af5f0c..8104313883 100644 --- a/bundle/config/resources/permission.go +++ b/bundle/config/resources/permission.go @@ -30,216 +30,6 @@ func (p Permission) String() string { return "level: " + p.Level } -type IPermission interface { - GetLevel() string - GetUserName() string - GetServicePrincipalName() string - GetGroupName() string - GetAPIRequestObjectType() string -} - -// Permission level types -type ( - AlertPermissionLevel string - AppPermissionLevel string - ClusterPermissionLevel string - DashboardPermissionLevel string - DatabaseInstancePermissionLevel string - DatabaseProjectPermissionLevel string - JobPermissionLevel string - MlflowExperimentPermissionLevel string - MlflowModelPermissionLevel string - ModelServingEndpointPermissionLevel string - PipelinePermissionLevel string - SqlWarehousePermissionLevel string -) - -func (l AlertPermissionLevel) Values() []string { - return []string{ - "CAN_EDIT", - "CAN_MANAGE", - "CAN_READ", - "CAN_RUN", - } -} - -type AlertPermission struct { - Level AlertPermissionLevel `json:"level"` - - UserName string `json:"user_name,omitempty"` - ServicePrincipalName string `json:"service_principal_name,omitempty"` - GroupName string `json:"group_name,omitempty"` -} - -type AppPermission struct { - Level AppPermissionLevel `json:"level"` - - UserName string `json:"user_name,omitempty"` - ServicePrincipalName string `json:"service_principal_name,omitempty"` - GroupName string `json:"group_name,omitempty"` -} - -type ClusterPermission struct { - Level ClusterPermissionLevel `json:"level"` - - UserName string `json:"user_name,omitempty"` - ServicePrincipalName string `json:"service_principal_name,omitempty"` - GroupName string `json:"group_name,omitempty"` -} - -type DashboardPermission struct { - Level DashboardPermissionLevel `json:"level"` - - UserName string `json:"user_name,omitempty"` - ServicePrincipalName string `json:"service_principal_name,omitempty"` - GroupName string `json:"group_name,omitempty"` -} - -type DatabaseInstancePermission struct { - Level DatabaseInstancePermissionLevel `json:"level"` - - UserName string `json:"user_name,omitempty"` - ServicePrincipalName string `json:"service_principal_name,omitempty"` - GroupName string `json:"group_name,omitempty"` -} - -type DatabaseProjectPermission struct { - Level DatabaseProjectPermissionLevel `json:"level"` - - UserName string `json:"user_name,omitempty"` - ServicePrincipalName string `json:"service_principal_name,omitempty"` - GroupName string `json:"group_name,omitempty"` -} - -type JobPermission struct { - Level JobPermissionLevel `json:"level"` - - UserName string `json:"user_name,omitempty"` - ServicePrincipalName string `json:"service_principal_name,omitempty"` - GroupName string `json:"group_name,omitempty"` -} - -type MlflowExperimentPermission struct { - Level MlflowExperimentPermissionLevel `json:"level"` - - UserName string `json:"user_name,omitempty"` - ServicePrincipalName string `json:"service_principal_name,omitempty"` - GroupName string `json:"group_name,omitempty"` -} - -type MlflowModelPermission struct { - Level MlflowModelPermissionLevel `json:"level"` - - UserName string `json:"user_name,omitempty"` - ServicePrincipalName string `json:"service_principal_name,omitempty"` - GroupName string `json:"group_name,omitempty"` -} - -type ModelServingEndpointPermission struct { - Level ModelServingEndpointPermissionLevel `json:"level"` - - UserName string `json:"user_name,omitempty"` - ServicePrincipalName string `json:"service_principal_name,omitempty"` - GroupName string `json:"group_name,omitempty"` -} - -type PipelinePermission struct { - Level PipelinePermissionLevel `json:"level"` - - UserName string `json:"user_name,omitempty"` - ServicePrincipalName string `json:"service_principal_name,omitempty"` - GroupName string `json:"group_name,omitempty"` -} - -type SqlWarehousePermission struct { - Level SqlWarehousePermissionLevel `json:"level"` - - UserName string `json:"user_name,omitempty"` - ServicePrincipalName string `json:"service_principal_name,omitempty"` - GroupName string `json:"group_name,omitempty"` -} - -// GetAPIRequestObjectType is used by direct to construct a request to permissions API: -// https://github.com/databricks/terraform-provider-databricks/blob/430902d/permissions/permission_definitions.go#L775C24-L775C32 -func (p AlertPermission) GetAPIRequestObjectType() string { return "/alertsv2/" } -func (p AppPermission) GetAPIRequestObjectType() string { return "/apps/" } -func (p ClusterPermission) GetAPIRequestObjectType() string { return "/clusters/" } -func (p DashboardPermission) GetAPIRequestObjectType() string { return "/dashboards/" } -func (p DatabaseInstancePermission) GetAPIRequestObjectType() string { return "/database-instances/" } -func (p DatabaseProjectPermission) GetAPIRequestObjectType() string { return "/database-projects/" } -func (p JobPermission) GetAPIRequestObjectType() string { return "/jobs/" } -func (p MlflowExperimentPermission) GetAPIRequestObjectType() string { return "/experiments/" } -func (p MlflowModelPermission) GetAPIRequestObjectType() string { return "/registered-models/" } -func (p ModelServingEndpointPermission) GetAPIRequestObjectType() string { - return "/serving-endpoints/" -} -func (p PipelinePermission) GetAPIRequestObjectType() string { return "/pipelines/" } -func (p SqlWarehousePermission) GetAPIRequestObjectType() string { return "/sql/warehouses/" } - -// IPermission interface implementations boilerplate - -func (p AlertPermission) GetLevel() string { return string(p.Level) } -func (p AlertPermission) GetUserName() string { return p.UserName } -func (p AlertPermission) GetServicePrincipalName() string { return p.ServicePrincipalName } -func (p AlertPermission) GetGroupName() string { return p.GroupName } - -func (p AppPermission) GetLevel() string { return string(p.Level) } -func (p AppPermission) GetUserName() string { return p.UserName } -func (p AppPermission) GetServicePrincipalName() string { return p.ServicePrincipalName } -func (p AppPermission) GetGroupName() string { return p.GroupName } - -func (p ClusterPermission) GetLevel() string { return string(p.Level) } -func (p ClusterPermission) GetUserName() string { return p.UserName } -func (p ClusterPermission) GetServicePrincipalName() string { return p.ServicePrincipalName } -func (p ClusterPermission) GetGroupName() string { return p.GroupName } - -func (p DashboardPermission) GetLevel() string { return string(p.Level) } -func (p DashboardPermission) GetUserName() string { return p.UserName } -func (p DashboardPermission) GetServicePrincipalName() string { return p.ServicePrincipalName } -func (p DashboardPermission) GetGroupName() string { return p.GroupName } - -func (p DatabaseInstancePermission) GetLevel() string { return string(p.Level) } -func (p DatabaseInstancePermission) GetUserName() string { return p.UserName } -func (p DatabaseInstancePermission) GetServicePrincipalName() string { return p.ServicePrincipalName } -func (p DatabaseInstancePermission) GetGroupName() string { return p.GroupName } - -func (p DatabaseProjectPermission) GetLevel() string { return string(p.Level) } -func (p DatabaseProjectPermission) GetUserName() string { return p.UserName } -func (p DatabaseProjectPermission) GetServicePrincipalName() string { return p.ServicePrincipalName } -func (p DatabaseProjectPermission) GetGroupName() string { return p.GroupName } - -func (p JobPermission) GetLevel() string { return string(p.Level) } -func (p JobPermission) GetUserName() string { return p.UserName } -func (p JobPermission) GetServicePrincipalName() string { return p.ServicePrincipalName } -func (p JobPermission) GetGroupName() string { return p.GroupName } - -func (p MlflowExperimentPermission) GetLevel() string { return string(p.Level) } -func (p MlflowExperimentPermission) GetUserName() string { return p.UserName } -func (p MlflowExperimentPermission) GetServicePrincipalName() string { return p.ServicePrincipalName } -func (p MlflowExperimentPermission) GetGroupName() string { return p.GroupName } - -func (p MlflowModelPermission) GetLevel() string { return string(p.Level) } -func (p MlflowModelPermission) GetUserName() string { return p.UserName } -func (p MlflowModelPermission) GetServicePrincipalName() string { return p.ServicePrincipalName } -func (p MlflowModelPermission) GetGroupName() string { return p.GroupName } - -func (p ModelServingEndpointPermission) GetLevel() string { return string(p.Level) } -func (p ModelServingEndpointPermission) GetUserName() string { return p.UserName } -func (p ModelServingEndpointPermission) GetServicePrincipalName() string { - return p.ServicePrincipalName -} -func (p ModelServingEndpointPermission) GetGroupName() string { return p.GroupName } - -func (p PipelinePermission) GetLevel() string { return string(p.Level) } -func (p PipelinePermission) GetUserName() string { return p.UserName } -func (p PipelinePermission) GetServicePrincipalName() string { return p.ServicePrincipalName } -func (p PipelinePermission) GetGroupName() string { return p.GroupName } - -func (p SqlWarehousePermission) GetLevel() string { return string(p.Level) } -func (p SqlWarehousePermission) GetUserName() string { return p.UserName } -func (p SqlWarehousePermission) GetServicePrincipalName() string { return p.ServicePrincipalName } -func (p SqlWarehousePermission) GetGroupName() string { return p.GroupName } - // PermissionOrder defines the hierarchy of permission levels. // Higher numbers mean more permissive access. // Based on https://docs.databricks.com/aws/en/security/auth/access-control diff --git a/bundle/config/resources/pipeline.go b/bundle/config/resources/pipeline.go index e213ff9c00..f530374120 100644 --- a/bundle/config/resources/pipeline.go +++ b/bundle/config/resources/pipeline.go @@ -14,7 +14,7 @@ type Pipeline struct { BaseResource pipelines.CreatePipeline //nolint CreatePipeline also defines Id field with the same json tag "id" - Permissions []PipelinePermission `json:"permissions,omitempty"` + Permissions []Permission `json:"permissions,omitempty"` } func (p *Pipeline) UnmarshalJSON(b []byte) error { diff --git a/bundle/config/resources/postgres_project.go b/bundle/config/resources/postgres_project.go index a1c26d00ed..d76a6ee135 100644 --- a/bundle/config/resources/postgres_project.go +++ b/bundle/config/resources/postgres_project.go @@ -30,7 +30,7 @@ type PostgresProject struct { BaseResource PostgresProjectConfig - Permissions []DatabaseProjectPermission `json:"permissions,omitempty"` + Permissions []Permission `json:"permissions,omitempty"` } func (p *PostgresProject) Exists(ctx context.Context, w *databricks.WorkspaceClient, name string) (bool, error) { diff --git a/bundle/config/resources/sql_warehouses.go b/bundle/config/resources/sql_warehouses.go index bed567b805..b9b072e67d 100644 --- a/bundle/config/resources/sql_warehouses.go +++ b/bundle/config/resources/sql_warehouses.go @@ -14,7 +14,7 @@ type SqlWarehouse struct { BaseResource sql.CreateWarehouseRequest - Permissions []SqlWarehousePermission `json:"permissions,omitempty"` + Permissions []Permission `json:"permissions,omitempty"` } func (sw *SqlWarehouse) UnmarshalJSON(b []byte) error { diff --git a/bundle/config/resources_types_test.go b/bundle/config/resources_types_test.go index 577f1dd003..244e9dfa67 100644 --- a/bundle/config/resources_types_test.go +++ b/bundle/config/resources_types_test.go @@ -18,5 +18,5 @@ func TestResourcesTypesMap(t *testing.T) { typ, ok = ResourcesTypes["jobs.permissions"] assert.True(t, ok, "resources type for 'jobs.permissions' not found in ResourcesTypes map") - assert.Equal(t, reflect.TypeOf([]resources.JobPermission{}), typ, "resources type for 'jobs.permissions' mismatch") + assert.Equal(t, reflect.TypeOf([]resources.Permission{}), typ, "resources type for 'jobs.permissions' mismatch") } diff --git a/bundle/deploy/terraform/convert_test.go b/bundle/deploy/terraform/convert_test.go index 564340190d..0b583e2b4e 100644 --- a/bundle/deploy/terraform/convert_test.go +++ b/bundle/deploy/terraform/convert_test.go @@ -91,7 +91,7 @@ func TestBundleToTerraformJob(t *testing.T) { func TestBundleToTerraformJobPermissions(t *testing.T) { src := resources.Job{ - Permissions: []resources.JobPermission{ + Permissions: []resources.Permission{ { Level: "CAN_VIEW", UserName: "jane@doe.com", @@ -260,7 +260,7 @@ func TestBundleToTerraformPipeline(t *testing.T) { func TestBundleToTerraformPipelinePermissions(t *testing.T) { src := resources.Pipeline{ - Permissions: []resources.PipelinePermission{ + Permissions: []resources.Permission{ { Level: "CAN_VIEW", UserName: "jane@doe.com", @@ -331,7 +331,7 @@ func TestBundleToTerraformModelPermissions(t *testing.T) { CreateModelRequest: ml.CreateModelRequest{ Name: "name", }, - Permissions: []resources.MlflowModelPermission{ + Permissions: []resources.Permission{ { Level: "CAN_READ", UserName: "jane@doe.com", @@ -385,7 +385,7 @@ func TestBundleToTerraformExperimentPermissions(t *testing.T) { CreateExperiment: ml.CreateExperiment{ Name: "name", }, - Permissions: []resources.MlflowExperimentPermission{ + Permissions: []resources.Permission{ { Level: "CAN_READ", UserName: "jane@doe.com", @@ -481,7 +481,7 @@ func TestBundleToTerraformModelServingPermissions(t *testing.T) { }, }, }, - Permissions: []resources.ModelServingEndpointPermission{ + Permissions: []resources.Permission{ { Level: "CAN_VIEW", UserName: "jane@doe.com", diff --git a/bundle/deploy/terraform/tfdyn/convert_alert_test.go b/bundle/deploy/terraform/tfdyn/convert_alert_test.go index 94c3ca518d..25d5942519 100644 --- a/bundle/deploy/terraform/tfdyn/convert_alert_test.go +++ b/bundle/deploy/terraform/tfdyn/convert_alert_test.go @@ -21,7 +21,7 @@ func TestConvertAlert(t *testing.T) { CustomSummary: "Test alert summary", CustomDescription: "Test alert description", }, - Permissions: []resources.AlertPermission{ + Permissions: []resources.Permission{ { Level: "CAN_VIEW", UserName: "jane@doe.com", diff --git a/bundle/deploy/terraform/tfdyn/convert_app_test.go b/bundle/deploy/terraform/tfdyn/convert_app_test.go index 896d0e52d5..6eb2ce7e23 100644 --- a/bundle/deploy/terraform/tfdyn/convert_app_test.go +++ b/bundle/deploy/terraform/tfdyn/convert_app_test.go @@ -35,7 +35,7 @@ func TestConvertApp(t *testing.T) { }, }, }, - Permissions: []resources.AppPermission{ + Permissions: []resources.Permission{ { Level: "CAN_RUN", UserName: "jack@gmail.com", diff --git a/bundle/deploy/terraform/tfdyn/convert_cluster_test.go b/bundle/deploy/terraform/tfdyn/convert_cluster_test.go index c28e874413..ae676ada99 100644 --- a/bundle/deploy/terraform/tfdyn/convert_cluster_test.go +++ b/bundle/deploy/terraform/tfdyn/convert_cluster_test.go @@ -35,7 +35,7 @@ func TestConvertCluster(t *testing.T) { }, }, - Permissions: []resources.ClusterPermission{ + Permissions: []resources.Permission{ { Level: "CAN_RUN", UserName: "jack@gmail.com", diff --git a/bundle/deploy/terraform/tfdyn/convert_dashboard_test.go b/bundle/deploy/terraform/tfdyn/convert_dashboard_test.go index 847a609365..d8f05dc76c 100644 --- a/bundle/deploy/terraform/tfdyn/convert_dashboard_test.go +++ b/bundle/deploy/terraform/tfdyn/convert_dashboard_test.go @@ -20,7 +20,7 @@ func TestConvertDashboard(t *testing.T) { EmbedCredentials: true, }, - Permissions: []resources.DashboardPermission{ + Permissions: []resources.Permission{ { Level: "CAN_VIEW", UserName: "jane@doe.com", diff --git a/bundle/deploy/terraform/tfdyn/convert_database_instance_test.go b/bundle/deploy/terraform/tfdyn/convert_database_instance_test.go index d29386a24d..533c69abed 100644 --- a/bundle/deploy/terraform/tfdyn/convert_database_instance_test.go +++ b/bundle/deploy/terraform/tfdyn/convert_database_instance_test.go @@ -73,7 +73,7 @@ func TestConvertDatabaseInstanceWithPermissions(t *testing.T) { Name: "db-instance-with-permissions", Capacity: "CU_2", }, - Permissions: []resources.DatabaseInstancePermission{ + Permissions: []resources.Permission{ { Level: "CAN_USE", UserName: "user@example.com", diff --git a/bundle/deploy/terraform/tfdyn/convert_experiment_test.go b/bundle/deploy/terraform/tfdyn/convert_experiment_test.go index 9140ef13ac..6d2e6ea2d0 100644 --- a/bundle/deploy/terraform/tfdyn/convert_experiment_test.go +++ b/bundle/deploy/terraform/tfdyn/convert_experiment_test.go @@ -17,7 +17,7 @@ func TestConvertExperiment(t *testing.T) { CreateExperiment: ml.CreateExperiment{ Name: "name", }, - Permissions: []resources.MlflowExperimentPermission{ + Permissions: []resources.Permission{ { Level: "CAN_READ", UserName: "jane@doe.com", diff --git a/bundle/deploy/terraform/tfdyn/convert_job_test.go b/bundle/deploy/terraform/tfdyn/convert_job_test.go index 782075fc7f..0a84fbd20b 100644 --- a/bundle/deploy/terraform/tfdyn/convert_job_test.go +++ b/bundle/deploy/terraform/tfdyn/convert_job_test.go @@ -71,7 +71,7 @@ func TestConvertJob(t *testing.T) { }, }, }, - Permissions: []resources.JobPermission{ + Permissions: []resources.Permission{ { Level: "CAN_VIEW", UserName: "jane@doe.com", diff --git a/bundle/deploy/terraform/tfdyn/convert_model_serving_endpoint_test.go b/bundle/deploy/terraform/tfdyn/convert_model_serving_endpoint_test.go index fbb3d39a72..6aa3c079e6 100644 --- a/bundle/deploy/terraform/tfdyn/convert_model_serving_endpoint_test.go +++ b/bundle/deploy/terraform/tfdyn/convert_model_serving_endpoint_test.go @@ -35,7 +35,7 @@ func TestConvertModelServingEndpoint(t *testing.T) { }, }, }, - Permissions: []resources.ModelServingEndpointPermission{ + Permissions: []resources.Permission{ { Level: "CAN_VIEW", UserName: "jane@doe.com", diff --git a/bundle/deploy/terraform/tfdyn/convert_model_test.go b/bundle/deploy/terraform/tfdyn/convert_model_test.go index 3d7e8b22c4..999d22bccc 100644 --- a/bundle/deploy/terraform/tfdyn/convert_model_test.go +++ b/bundle/deploy/terraform/tfdyn/convert_model_test.go @@ -28,7 +28,7 @@ func TestConvertModel(t *testing.T) { }, }, }, - Permissions: []resources.MlflowModelPermission{ + Permissions: []resources.Permission{ { Level: "CAN_READ", UserName: "jane@doe.com", diff --git a/bundle/deploy/terraform/tfdyn/convert_permissions_test.go b/bundle/deploy/terraform/tfdyn/convert_permissions_test.go index 4d0d2d7481..9efe3e9d94 100644 --- a/bundle/deploy/terraform/tfdyn/convert_permissions_test.go +++ b/bundle/deploy/terraform/tfdyn/convert_permissions_test.go @@ -13,7 +13,7 @@ import ( func TestConvertPermissions(t *testing.T) { src := resources.Job{ - Permissions: []resources.JobPermission{ + Permissions: []resources.Permission{ { Level: "CAN_VIEW", UserName: "jane@doe.com", @@ -72,7 +72,7 @@ func TestConvertPermissionsNil(t *testing.T) { func TestConvertPermissionsEmpty(t *testing.T) { src := resources.Job{ - Permissions: []resources.JobPermission{}, + Permissions: []resources.Permission{}, } vin, err := convert.FromTyped(src, dyn.NilValue) diff --git a/bundle/deploy/terraform/tfdyn/convert_pipeline_test.go b/bundle/deploy/terraform/tfdyn/convert_pipeline_test.go index f16b6b8595..98c2e832bd 100644 --- a/bundle/deploy/terraform/tfdyn/convert_pipeline_test.go +++ b/bundle/deploy/terraform/tfdyn/convert_pipeline_test.go @@ -64,7 +64,7 @@ func TestConvertPipeline(t *testing.T) { }, }, }, - Permissions: []resources.PipelinePermission{ + Permissions: []resources.Permission{ { Level: "CAN_VIEW", UserName: "jane@doe.com", diff --git a/bundle/deploy/terraform/tfdyn/convert_postgres_project_test.go b/bundle/deploy/terraform/tfdyn/convert_postgres_project_test.go index 271ac2f985..696a6e76c2 100644 --- a/bundle/deploy/terraform/tfdyn/convert_postgres_project_test.go +++ b/bundle/deploy/terraform/tfdyn/convert_postgres_project_test.go @@ -64,7 +64,7 @@ func TestConvertPostgresProjectWithPermissions(t *testing.T) { PgVersion: 17, }, }, - Permissions: []resources.DatabaseProjectPermission{ + Permissions: []resources.Permission{ { Level: "CAN_USE", UserName: "user@example.com", diff --git a/bundle/direct/dresources/permissions.go b/bundle/direct/dresources/permissions.go index 7c47e11c73..adb090ac5c 100644 --- a/bundle/direct/dresources/permissions.go +++ b/bundle/direct/dresources/permissions.go @@ -3,7 +3,6 @@ package dresources import ( "context" "fmt" - "reflect" "strings" "github.com/databricks/cli/bundle/config/resources" @@ -12,6 +11,23 @@ import ( "github.com/databricks/databricks-sdk-go/service/iam" ) +// GetAPIRequestObjectType is used by direct to construct a request to permissions API: +// https://github.com/databricks/terraform-provider-databricks/blob/430902d/permissions/permission_definitions.go#L775C24-L775C32 +var permissionResourceToObjectType = map[string]string{ + "alerts": "/alertsv2/", + "apps": "/apps/", + "clusters": "/clusters/", + "dashboards": "/dashboards/", + "database_instances": "/database-instances/", + "postgres_projects": "/database-projects/", + "jobs": "/jobs/", + "experiments": "/experiments/", + "models": "/registered-models/", + "model_serving_endpoints": "/serving-endpoints/", + "pipelines": "/pipelines/", + "sql_warehouses": "/sql/warehouses/", +} + type ResourcePermissions struct { client *databricks.WorkspaceClient } @@ -27,38 +43,35 @@ func PreparePermissionsInputConfig(inputConfig any, node string) (*structvar.Str return nil, fmt.Errorf("internal error: node %q does not end with .permissions", node) } - // Use reflection to get the slice from the pointer - rv := reflect.ValueOf(inputConfig) - if rv.Kind() != reflect.Ptr || rv.Elem().Kind() != reflect.Slice { - return nil, fmt.Errorf("inputConfig must be a pointer to a slice, got: %T", inputConfig) + parts := strings.Split(baseNode, ".") + if len(parts) < 2 { + return nil, fmt.Errorf("internal error: unexpected node format %q", baseNode) } + resourceType := parts[1] - sliceValue := rv.Elem() + prefix, ok := permissionResourceToObjectType[resourceType] + if !ok { + return nil, fmt.Errorf("unsupported permissions resource type: %s", resourceType) + } - // Get the element type from the slice type and create zero value to get the object type - elemType := sliceValue.Type().Elem() - zeroValue := reflect.Zero(elemType) - zeroValueInterface, ok := zeroValue.Interface().(resources.IPermission) + permissionsPtr, ok := inputConfig.(*[]resources.Permission) if !ok { - return nil, fmt.Errorf("slice elements do not implement IPermission interface: %v", elemType) + return nil, fmt.Errorf("expected *[]resources.Permission, got %T", inputConfig) } - prefix := zeroValueInterface.GetAPIRequestObjectType() - // Convert slice to []resources.IPermission - permissions := make([]iam.AccessControlRequest, 0, sliceValue.Len()) - for i := range sliceValue.Len() { - elem := sliceValue.Index(i).Interface().(resources.IPermission) + permissions := make([]iam.AccessControlRequest, 0, len(*permissionsPtr)) + for _, p := range *permissionsPtr { permissions = append(permissions, iam.AccessControlRequest{ - PermissionLevel: iam.PermissionLevel(elem.GetLevel()), - GroupName: elem.GetGroupName(), - ServicePrincipalName: elem.GetServicePrincipalName(), - UserName: elem.GetUserName(), + PermissionLevel: iam.PermissionLevel(p.Level), + GroupName: p.GroupName, + ServicePrincipalName: p.ServicePrincipalName, + UserName: p.UserName, ForceSendFields: nil, }) } objectIdRef := prefix + "${" + baseNode + ".id}" - // For permissions, model serving endpoint uses it's internal ID, which is different + // For permissions, model serving endpoint uses its internal ID, which is different // from its CRUD APIs which use the name. // We have a wrapper struct [RefreshOutput] from which we read the internal ID // in order to set the appropriate permissions. diff --git a/bundle/internal/schema/annotations.yml b/bundle/internal/schema/annotations.yml index 2eb01cd85a..a9a9a7fd2c 100644 --- a/bundle/internal/schema/annotations.yml +++ b/bundle/internal/schema/annotations.yml @@ -509,19 +509,6 @@ github.com/databricks/cli/bundle/config/resources.Alert: "warehouse_id": "description": |- PLACEHOLDER -github.com/databricks/cli/bundle/config/resources.AlertPermission: - "group_name": - "description": |- - PLACEHOLDER - "level": - "description": |- - PLACEHOLDER - "service_principal_name": - "description": |- - PLACEHOLDER - "user_name": - "description": |- - PLACEHOLDER github.com/databricks/cli/bundle/config/resources.App: "git_source": "description": |- @@ -545,19 +532,6 @@ github.com/databricks/cli/bundle/config/resources.AppEnvVar: "value_from": "description": |- PLACEHOLDER -github.com/databricks/cli/bundle/config/resources.AppPermission: - "group_name": - "description": |- - PLACEHOLDER - "level": - "description": |- - PLACEHOLDER - "service_principal_name": - "description": |- - PLACEHOLDER - "user_name": - "description": |- - PLACEHOLDER github.com/databricks/cli/bundle/config/resources.Catalog: "comment": "description": |- @@ -583,19 +557,6 @@ github.com/databricks/cli/bundle/config/resources.Catalog: "storage_root": "description": |- PLACEHOLDER -github.com/databricks/cli/bundle/config/resources.ClusterPermission: - "group_name": - "description": |- - PLACEHOLDER - "level": - "description": |- - PLACEHOLDER - "service_principal_name": - "description": |- - PLACEHOLDER - "user_name": - "description": |- - PLACEHOLDER github.com/databricks/cli/bundle/config/resources.Dashboard: "dataset_catalog": "description": |- @@ -603,49 +564,10 @@ github.com/databricks/cli/bundle/config/resources.Dashboard: "dataset_schema": "description": |- Sets the default schema for all datasets in this dashboard. When set, this overrides the schema specified in individual dataset definitions. -github.com/databricks/cli/bundle/config/resources.DashboardPermission: - "group_name": - "description": |- - PLACEHOLDER - "level": - "description": |- - PLACEHOLDER - "service_principal_name": - "description": |- - PLACEHOLDER - "user_name": - "description": |- - PLACEHOLDER github.com/databricks/cli/bundle/config/resources.DatabaseInstance: "effective_capacity": "description": |- PLACEHOLDER -github.com/databricks/cli/bundle/config/resources.DatabaseInstancePermission: - "group_name": - "description": |- - PLACEHOLDER - "level": - "description": |- - PLACEHOLDER - "service_principal_name": - "description": |- - PLACEHOLDER - "user_name": - "description": |- - PLACEHOLDER -github.com/databricks/cli/bundle/config/resources.DatabaseProjectPermission: - "group_name": - "description": |- - PLACEHOLDER - "level": - "description": |- - PLACEHOLDER - "service_principal_name": - "description": |- - PLACEHOLDER - "user_name": - "description": |- - PLACEHOLDER github.com/databricks/cli/bundle/config/resources.ExternalLocation: "comment": "description": |- @@ -683,62 +605,10 @@ github.com/databricks/cli/bundle/config/resources.ExternalLocation: "url": "description": |- PLACEHOLDER -github.com/databricks/cli/bundle/config/resources.JobPermission: - "group_name": - "description": |- - PLACEHOLDER - "level": - "description": |- - PLACEHOLDER - "service_principal_name": - "description": |- - PLACEHOLDER - "user_name": - "description": |- - PLACEHOLDER github.com/databricks/cli/bundle/config/resources.Lifecycle: "prevent_destroy": "description": |- Lifecycle setting to prevent the resource from being destroyed. -github.com/databricks/cli/bundle/config/resources.MlflowExperimentPermission: - "group_name": - "description": |- - PLACEHOLDER - "level": - "description": |- - PLACEHOLDER - "service_principal_name": - "description": |- - PLACEHOLDER - "user_name": - "description": |- - PLACEHOLDER -github.com/databricks/cli/bundle/config/resources.MlflowModelPermission: - "group_name": - "description": |- - PLACEHOLDER - "level": - "description": |- - PLACEHOLDER - "service_principal_name": - "description": |- - PLACEHOLDER - "user_name": - "description": |- - PLACEHOLDER -github.com/databricks/cli/bundle/config/resources.ModelServingEndpointPermission: - "group_name": - "description": |- - PLACEHOLDER - "level": - "description": |- - PLACEHOLDER - "service_principal_name": - "description": |- - PLACEHOLDER - "user_name": - "description": |- - PLACEHOLDER github.com/databricks/cli/bundle/config/resources.Permission: "-": "description": |- @@ -757,19 +627,6 @@ github.com/databricks/cli/bundle/config/resources.Permission: "user_name": "description": |- The name of the user that has the permission set in level. -github.com/databricks/cli/bundle/config/resources.PipelinePermission: - "group_name": - "description": |- - PLACEHOLDER - "level": - "description": |- - PLACEHOLDER - "service_principal_name": - "description": |- - PLACEHOLDER - "user_name": - "description": |- - PLACEHOLDER github.com/databricks/cli/bundle/config/resources.PostgresBranch: "branch_id": "description": |- @@ -949,19 +806,6 @@ github.com/databricks/cli/bundle/config/resources.SecretScopePermission: "user_name": "description": |- The name of the user that has the permission set in level. This field translates to a `principal` field in secret scope ACL. -github.com/databricks/cli/bundle/config/resources.SqlWarehousePermission: - "group_name": - "description": |- - PLACEHOLDER - "level": - "description": |- - PLACEHOLDER - "service_principal_name": - "description": |- - PLACEHOLDER - "user_name": - "description": |- - PLACEHOLDER github.com/databricks/cli/bundle/config/resources.SyncedDatabaseTable: "data_synchronization_status": "description": |- diff --git a/bundle/internal/schema/annotations_openapi_overrides.yml b/bundle/internal/schema/annotations_openapi_overrides.yml index 4496dd37f1..3a83d7a0d2 100644 --- a/bundle/internal/schema/annotations_openapi_overrides.yml +++ b/bundle/internal/schema/annotations_openapi_overrides.yml @@ -14,17 +14,6 @@ github.com/databricks/cli/bundle/config/resources.Alert: "schedule": "description": |- PLACEHOLDER -github.com/databricks/cli/bundle/config/resources.AlertPermissionLevel: - "_": - "enum": - - |- - CAN_EDIT - - |- - CAN_MANAGE - - |- - CAN_READ - - |- - CAN_RUN github.com/databricks/cli/bundle/config/resources.App: "app_status": "description": |- @@ -80,13 +69,6 @@ github.com/databricks/cli/bundle/config/resources.App: "user_api_scopes": "description": |- PLACEHOLDER -github.com/databricks/cli/bundle/config/resources.AppPermissionLevel: - "_": - "enum": - - |- - CAN_MANAGE - - |- - CAN_USE github.com/databricks/cli/bundle/config/resources.Catalog: "grants": "description": |- @@ -145,15 +127,6 @@ github.com/databricks/cli/bundle/config/resources.Cluster: "workload_type": "description": |- PLACEHOLDER -github.com/databricks/cli/bundle/config/resources.ClusterPermissionLevel: - "_": - "enum": - - |- - CAN_MANAGE - - |- - CAN_RESTART - - |- - CAN_ATTACH_TO github.com/databricks/cli/bundle/config/resources.Dashboard: "_": "markdown_description": |- @@ -226,17 +199,6 @@ github.com/databricks/cli/bundle/config/resources.Dashboard: "warehouse_id": "description": |- The warehouse ID used to run the dashboard. -github.com/databricks/cli/bundle/config/resources.DashboardPermissionLevel: - "_": - "enum": - - |- - CAN_READ - - |- - CAN_RUN - - |- - CAN_EDIT - - |- - CAN_MANAGE github.com/databricks/cli/bundle/config/resources.DatabaseCatalog: "create_database_if_not_exists": "description": |- @@ -254,22 +216,6 @@ github.com/databricks/cli/bundle/config/resources.DatabaseInstance: "permissions": "description": |- PLACEHOLDER -github.com/databricks/cli/bundle/config/resources.DatabaseInstancePermissionLevel: - "_": - "enum": - - |- - CAN_CREATE - - |- - CAN_USE - - |- - CAN_MANAGE -github.com/databricks/cli/bundle/config/resources.DatabaseProjectPermissionLevel: - "_": - "enum": - - |- - CAN_USE - - |- - CAN_MANAGE github.com/databricks/cli/bundle/config/resources.ExternalLocation: "grants": "description": |- @@ -308,17 +254,6 @@ github.com/databricks/cli/bundle/config/resources.Job: "run_as": "description": |- PLACEHOLDER -github.com/databricks/cli/bundle/config/resources.JobPermissionLevel: - "_": - "enum": - - |- - CAN_MANAGE - - |- - CAN_MANAGE_RUN - - |- - CAN_VIEW - - |- - IS_OWNER github.com/databricks/cli/bundle/config/resources.MlflowExperiment: "_": "markdown_description": |- @@ -342,15 +277,6 @@ github.com/databricks/cli/bundle/config/resources.MlflowExperiment: "permissions": "description": |- PLACEHOLDER -github.com/databricks/cli/bundle/config/resources.MlflowExperimentPermissionLevel: - "_": - "enum": - - |- - CAN_MANAGE - - |- - CAN_EDIT - - |- - CAN_READ github.com/databricks/cli/bundle/config/resources.MlflowModel: "_": "markdown_description": |- @@ -361,19 +287,6 @@ github.com/databricks/cli/bundle/config/resources.MlflowModel: "permissions": "description": |- PLACEHOLDER -github.com/databricks/cli/bundle/config/resources.MlflowModelPermissionLevel: - "_": - "enum": - - |- - CAN_EDIT - - |- - CAN_MANAGE - - |- - CAN_MANAGE_STAGING_VERSIONS - - |- - CAN_MANAGE_PRODUCTION_VERSIONS - - |- - CAN_READ github.com/databricks/cli/bundle/config/resources.ModelServingEndpoint: "_": "markdown_description": |- @@ -409,15 +322,6 @@ github.com/databricks/cli/bundle/config/resources.ModelServingEndpoint: "permissions": "description": |- PLACEHOLDER -github.com/databricks/cli/bundle/config/resources.ModelServingEndpointPermissionLevel: - "_": - "enum": - - |- - CAN_MANAGE - - |- - CAN_QUERY - - |- - CAN_VIEW github.com/databricks/cli/bundle/config/resources.Pipeline: "_": "markdown_description": |- @@ -457,17 +361,6 @@ github.com/databricks/cli/bundle/config/resources.Pipeline: "trigger": "deprecation_message": |- Use continuous instead -github.com/databricks/cli/bundle/config/resources.PipelinePermissionLevel: - "_": - "enum": - - |- - CAN_MANAGE - - |- - IS_OWNER - - |- - CAN_RUN - - |- - CAN_VIEW github.com/databricks/cli/bundle/config/resources.QualityMonitor: "_": "markdown_description": |- @@ -638,17 +531,6 @@ github.com/databricks/cli/bundle/config/resources.SqlWarehouse: "warehouse_type": "description": |- PLACEHOLDER -github.com/databricks/cli/bundle/config/resources.SqlWarehousePermissionLevel: - "_": - "enum": - - |- - CAN_MANAGE - - |- - CAN_USE - - |- - CAN_MONITOR - - |- - CAN_VIEW github.com/databricks/cli/bundle/config/resources.SyncedDatabaseTable: "lifecycle": "description": |- diff --git a/bundle/schema/jsonschema.json b/bundle/schema/jsonschema.json index a74fbb6013..7445d4d48a 100644 --- a/bundle/schema/jsonschema.json +++ b/bundle/schema/jsonschema.json @@ -86,7 +86,7 @@ "$ref": "#/$defs/string" }, "permissions": { - "$ref": "#/$defs/slice/github.com/databricks/cli/bundle/config/resources.AlertPermission" + "$ref": "#/$defs/slice/github.com/databricks/cli/bundle/config/resources.Permission" }, "query_text": { "$ref": "#/$defs/string" @@ -121,52 +121,6 @@ } ] }, - "resources.AlertPermission": { - "oneOf": [ - { - "type": "object", - "properties": { - "group_name": { - "$ref": "#/$defs/string" - }, - "level": { - "$ref": "#/$defs/github.com/databricks/cli/bundle/config/resources.AlertPermissionLevel" - }, - "service_principal_name": { - "$ref": "#/$defs/string" - }, - "user_name": { - "$ref": "#/$defs/string" - } - }, - "additionalProperties": false, - "required": [ - "level" - ] - }, - { - "type": "string", - "pattern": "\\$\\{(var(\\.[a-zA-Z]+([-_]?[a-zA-Z0-9]+)*(\\[[0-9]+\\])*)+)\\}" - } - ] - }, - "resources.AlertPermissionLevel": { - "oneOf": [ - { - "type": "string", - "enum": [ - "CAN_EDIT", - "CAN_MANAGE", - "CAN_READ", - "CAN_RUN" - ] - }, - { - "type": "string", - "pattern": "\\$\\{(var(\\.[a-zA-Z]+([-_]?[a-zA-Z0-9]+)*(\\[[0-9]+\\])*)+)\\}" - } - ] - }, "resources.App": { "oneOf": [ { @@ -204,7 +158,7 @@ "$ref": "#/$defs/string" }, "permissions": { - "$ref": "#/$defs/slice/github.com/databricks/cli/bundle/config/resources.AppPermission" + "$ref": "#/$defs/slice/github.com/databricks/cli/bundle/config/resources.Permission" }, "resources": { "description": "Resources for the app.", @@ -283,50 +237,6 @@ } ] }, - "resources.AppPermission": { - "oneOf": [ - { - "type": "object", - "properties": { - "group_name": { - "$ref": "#/$defs/string" - }, - "level": { - "$ref": "#/$defs/github.com/databricks/cli/bundle/config/resources.AppPermissionLevel" - }, - "service_principal_name": { - "$ref": "#/$defs/string" - }, - "user_name": { - "$ref": "#/$defs/string" - } - }, - "additionalProperties": false, - "required": [ - "level" - ] - }, - { - "type": "string", - "pattern": "\\$\\{(var(\\.[a-zA-Z]+([-_]?[a-zA-Z0-9]+)*(\\[[0-9]+\\])*)+)\\}" - } - ] - }, - "resources.AppPermissionLevel": { - "oneOf": [ - { - "type": "string", - "enum": [ - "CAN_MANAGE", - "CAN_USE" - ] - }, - { - "type": "string", - "pattern": "\\$\\{(var(\\.[a-zA-Z]+([-_]?[a-zA-Z0-9]+)*(\\[[0-9]+\\])*)+)\\}" - } - ] - }, "resources.Catalog": { "oneOf": [ { @@ -470,7 +380,7 @@ "$ref": "#/$defs/int" }, "permissions": { - "$ref": "#/$defs/slice/github.com/databricks/cli/bundle/config/resources.ClusterPermission" + "$ref": "#/$defs/slice/github.com/databricks/cli/bundle/config/resources.Permission" }, "policy_id": { "description": "The ID of the cluster policy used to create the cluster if applicable.", @@ -528,51 +438,6 @@ } ] }, - "resources.ClusterPermission": { - "oneOf": [ - { - "type": "object", - "properties": { - "group_name": { - "$ref": "#/$defs/string" - }, - "level": { - "$ref": "#/$defs/github.com/databricks/cli/bundle/config/resources.ClusterPermissionLevel" - }, - "service_principal_name": { - "$ref": "#/$defs/string" - }, - "user_name": { - "$ref": "#/$defs/string" - } - }, - "additionalProperties": false, - "required": [ - "level" - ] - }, - { - "type": "string", - "pattern": "\\$\\{(var(\\.[a-zA-Z]+([-_]?[a-zA-Z0-9]+)*(\\[[0-9]+\\])*)+)\\}" - } - ] - }, - "resources.ClusterPermissionLevel": { - "oneOf": [ - { - "type": "string", - "enum": [ - "CAN_MANAGE", - "CAN_RESTART", - "CAN_ATTACH_TO" - ] - }, - { - "type": "string", - "pattern": "\\$\\{(var(\\.[a-zA-Z]+([-_]?[a-zA-Z0-9]+)*(\\[[0-9]+\\])*)+)\\}" - } - ] - }, "resources.Dashboard": { "oneOf": [ { @@ -625,7 +490,7 @@ "$ref": "#/$defs/string" }, "permissions": { - "$ref": "#/$defs/slice/github.com/databricks/cli/bundle/config/resources.DashboardPermission" + "$ref": "#/$defs/slice/github.com/databricks/cli/bundle/config/resources.Permission" }, "serialized_dashboard": { "description": "The contents of the dashboard in serialized string form.\nThis field is excluded in List Dashboards responses.\nUse the [get dashboard API](https://docs.databricks.com/api/workspace/lakeview/get)\nto retrieve an example response, which includes the `serialized_dashboard` field.\nThis field provides the structure of the JSON string that represents the dashboard's\nlayout and components.", @@ -649,52 +514,6 @@ } ] }, - "resources.DashboardPermission": { - "oneOf": [ - { - "type": "object", - "properties": { - "group_name": { - "$ref": "#/$defs/string" - }, - "level": { - "$ref": "#/$defs/github.com/databricks/cli/bundle/config/resources.DashboardPermissionLevel" - }, - "service_principal_name": { - "$ref": "#/$defs/string" - }, - "user_name": { - "$ref": "#/$defs/string" - } - }, - "additionalProperties": false, - "required": [ - "level" - ] - }, - { - "type": "string", - "pattern": "\\$\\{(var(\\.[a-zA-Z]+([-_]?[a-zA-Z0-9]+)*(\\[[0-9]+\\])*)+)\\}" - } - ] - }, - "resources.DashboardPermissionLevel": { - "oneOf": [ - { - "type": "string", - "enum": [ - "CAN_READ", - "CAN_RUN", - "CAN_EDIT", - "CAN_MANAGE" - ] - }, - { - "type": "string", - "pattern": "\\$\\{(var(\\.[a-zA-Z]+([-_]?[a-zA-Z0-9]+)*(\\[[0-9]+\\])*)+)\\}" - } - ] - }, "resources.DatabaseCatalog": { "oneOf": [ { @@ -772,7 +591,7 @@ "$ref": "#/$defs/github.com/databricks/databricks-sdk-go/service/database.DatabaseInstanceRef" }, "permissions": { - "$ref": "#/$defs/slice/github.com/databricks/cli/bundle/config/resources.DatabaseInstancePermission" + "$ref": "#/$defs/slice/github.com/databricks/cli/bundle/config/resources.Permission" }, "retention_window_in_days": { "description": "The retention window for the instance. This is the time window in days\nfor which the historical data is retained. The default value is 7 days.\nValid values are 2 to 35 days.", @@ -798,95 +617,6 @@ } ] }, - "resources.DatabaseInstancePermission": { - "oneOf": [ - { - "type": "object", - "properties": { - "group_name": { - "$ref": "#/$defs/string" - }, - "level": { - "$ref": "#/$defs/github.com/databricks/cli/bundle/config/resources.DatabaseInstancePermissionLevel" - }, - "service_principal_name": { - "$ref": "#/$defs/string" - }, - "user_name": { - "$ref": "#/$defs/string" - } - }, - "additionalProperties": false, - "required": [ - "level" - ] - }, - { - "type": "string", - "pattern": "\\$\\{(var(\\.[a-zA-Z]+([-_]?[a-zA-Z0-9]+)*(\\[[0-9]+\\])*)+)\\}" - } - ] - }, - "resources.DatabaseInstancePermissionLevel": { - "oneOf": [ - { - "type": "string", - "enum": [ - "CAN_CREATE", - "CAN_USE", - "CAN_MANAGE" - ] - }, - { - "type": "string", - "pattern": "\\$\\{(var(\\.[a-zA-Z]+([-_]?[a-zA-Z0-9]+)*(\\[[0-9]+\\])*)+)\\}" - } - ] - }, - "resources.DatabaseProjectPermission": { - "oneOf": [ - { - "type": "object", - "properties": { - "group_name": { - "$ref": "#/$defs/string" - }, - "level": { - "$ref": "#/$defs/github.com/databricks/cli/bundle/config/resources.DatabaseProjectPermissionLevel" - }, - "service_principal_name": { - "$ref": "#/$defs/string" - }, - "user_name": { - "$ref": "#/$defs/string" - } - }, - "additionalProperties": false, - "required": [ - "level" - ] - }, - { - "type": "string", - "pattern": "\\$\\{(var(\\.[a-zA-Z]+([-_]?[a-zA-Z0-9]+)*(\\[[0-9]+\\])*)+)\\}" - } - ] - }, - "resources.DatabaseProjectPermissionLevel": { - "oneOf": [ - { - "type": "string", - "enum": [ - "CAN_USE", - "CAN_MANAGE" - ] - }, - { - "type": "string", - "pattern": "\\$\\{(var(\\.[a-zA-Z]+([-_]?[a-zA-Z0-9]+)*(\\[[0-9]+\\])*)+)\\}" - } - ] - }, "resources.ExternalLocation": { "oneOf": [ { @@ -1003,7 +733,7 @@ "$ref": "#/$defs/github.com/databricks/databricks-sdk-go/service/jobs.PerformanceTarget" }, "permissions": { - "$ref": "#/$defs/slice/github.com/databricks/cli/bundle/config/resources.JobPermission" + "$ref": "#/$defs/slice/github.com/databricks/cli/bundle/config/resources.Permission" }, "queue": { "description": "The queue settings of the job.", @@ -1052,52 +782,6 @@ } ] }, - "resources.JobPermission": { - "oneOf": [ - { - "type": "object", - "properties": { - "group_name": { - "$ref": "#/$defs/string" - }, - "level": { - "$ref": "#/$defs/github.com/databricks/cli/bundle/config/resources.JobPermissionLevel" - }, - "service_principal_name": { - "$ref": "#/$defs/string" - }, - "user_name": { - "$ref": "#/$defs/string" - } - }, - "additionalProperties": false, - "required": [ - "level" - ] - }, - { - "type": "string", - "pattern": "\\$\\{(var(\\.[a-zA-Z]+([-_]?[a-zA-Z0-9]+)*(\\[[0-9]+\\])*)+)\\}" - } - ] - }, - "resources.JobPermissionLevel": { - "oneOf": [ - { - "type": "string", - "enum": [ - "CAN_MANAGE", - "CAN_MANAGE_RUN", - "CAN_VIEW", - "IS_OWNER" - ] - }, - { - "type": "string", - "pattern": "\\$\\{(var(\\.[a-zA-Z]+([-_]?[a-zA-Z0-9]+)*(\\[[0-9]+\\])*)+)\\}" - } - ] - }, "resources.Lifecycle": { "oneOf": [ { @@ -1134,7 +818,7 @@ "$ref": "#/$defs/string" }, "permissions": { - "$ref": "#/$defs/slice/github.com/databricks/cli/bundle/config/resources.MlflowExperimentPermission" + "$ref": "#/$defs/slice/github.com/databricks/cli/bundle/config/resources.Permission" }, "tags": { "description": "A collection of tags to set on the experiment. Maximum tag size and number of tags per request\ndepends on the storage backend. All storage backends are guaranteed to support tag keys up\nto 250 bytes in size and tag values up to 5000 bytes in size. All storage backends are also\nguaranteed to support up to 20 tags per request.", @@ -1153,51 +837,6 @@ } ] }, - "resources.MlflowExperimentPermission": { - "oneOf": [ - { - "type": "object", - "properties": { - "group_name": { - "$ref": "#/$defs/string" - }, - "level": { - "$ref": "#/$defs/github.com/databricks/cli/bundle/config/resources.MlflowExperimentPermissionLevel" - }, - "service_principal_name": { - "$ref": "#/$defs/string" - }, - "user_name": { - "$ref": "#/$defs/string" - } - }, - "additionalProperties": false, - "required": [ - "level" - ] - }, - { - "type": "string", - "pattern": "\\$\\{(var(\\.[a-zA-Z]+([-_]?[a-zA-Z0-9]+)*(\\[[0-9]+\\])*)+)\\}" - } - ] - }, - "resources.MlflowExperimentPermissionLevel": { - "oneOf": [ - { - "type": "string", - "enum": [ - "CAN_MANAGE", - "CAN_EDIT", - "CAN_READ" - ] - }, - { - "type": "string", - "pattern": "\\$\\{(var(\\.[a-zA-Z]+([-_]?[a-zA-Z0-9]+)*(\\[[0-9]+\\])*)+)\\}" - } - ] - }, "resources.MlflowModel": { "oneOf": [ { @@ -1216,7 +855,7 @@ "$ref": "#/$defs/string" }, "permissions": { - "$ref": "#/$defs/slice/github.com/databricks/cli/bundle/config/resources.MlflowModelPermission" + "$ref": "#/$defs/slice/github.com/databricks/cli/bundle/config/resources.Permission" }, "tags": { "description": "Additional metadata for registered model.", @@ -1235,53 +874,6 @@ } ] }, - "resources.MlflowModelPermission": { - "oneOf": [ - { - "type": "object", - "properties": { - "group_name": { - "$ref": "#/$defs/string" - }, - "level": { - "$ref": "#/$defs/github.com/databricks/cli/bundle/config/resources.MlflowModelPermissionLevel" - }, - "service_principal_name": { - "$ref": "#/$defs/string" - }, - "user_name": { - "$ref": "#/$defs/string" - } - }, - "additionalProperties": false, - "required": [ - "level" - ] - }, - { - "type": "string", - "pattern": "\\$\\{(var(\\.[a-zA-Z]+([-_]?[a-zA-Z0-9]+)*(\\[[0-9]+\\])*)+)\\}" - } - ] - }, - "resources.MlflowModelPermissionLevel": { - "oneOf": [ - { - "type": "string", - "enum": [ - "CAN_EDIT", - "CAN_MANAGE", - "CAN_MANAGE_STAGING_VERSIONS", - "CAN_MANAGE_PRODUCTION_VERSIONS", - "CAN_READ" - ] - }, - { - "type": "string", - "pattern": "\\$\\{(var(\\.[a-zA-Z]+([-_]?[a-zA-Z0-9]+)*(\\[[0-9]+\\])*)+)\\}" - } - ] - }, "resources.ModelServingEndpoint": { "oneOf": [ { @@ -1314,74 +906,29 @@ "description": "The name of the serving endpoint. This field is required and must be unique across a Databricks workspace.\nAn endpoint name can consist of alphanumeric characters, dashes, and underscores.", "$ref": "#/$defs/string" }, - "permissions": { - "$ref": "#/$defs/slice/github.com/databricks/cli/bundle/config/resources.ModelServingEndpointPermission" - }, - "rate_limits": { - "description": "Rate limits to be applied to the serving endpoint. NOTE: this field is deprecated, please use AI Gateway to manage rate limits.", - "$ref": "#/$defs/slice/github.com/databricks/databricks-sdk-go/service/serving.RateLimit", - "deprecationMessage": "This field is deprecated", - "deprecated": true - }, - "route_optimized": { - "description": "Enable route optimization for the serving endpoint.", - "$ref": "#/$defs/bool" - }, - "tags": { - "description": "Tags to be attached to the serving endpoint and automatically propagated to billing logs.", - "$ref": "#/$defs/slice/github.com/databricks/databricks-sdk-go/service/serving.EndpointTag" - } - }, - "additionalProperties": false, - "required": [ - "name" - ], - "markdownDescription": "The model_serving_endpoint resource allows you to define [model serving endpoints](https://docs.databricks.com/api/workspace/servingendpoints/create). See [link](https://docs.databricks.com/machine-learning/model-serving/manage-serving-endpoints.html)." - }, - { - "type": "string", - "pattern": "\\$\\{(var(\\.[a-zA-Z]+([-_]?[a-zA-Z0-9]+)*(\\[[0-9]+\\])*)+)\\}" - } - ] - }, - "resources.ModelServingEndpointPermission": { - "oneOf": [ - { - "type": "object", - "properties": { - "group_name": { - "$ref": "#/$defs/string" + "permissions": { + "$ref": "#/$defs/slice/github.com/databricks/cli/bundle/config/resources.Permission" }, - "level": { - "$ref": "#/$defs/github.com/databricks/cli/bundle/config/resources.ModelServingEndpointPermissionLevel" + "rate_limits": { + "description": "Rate limits to be applied to the serving endpoint. NOTE: this field is deprecated, please use AI Gateway to manage rate limits.", + "$ref": "#/$defs/slice/github.com/databricks/databricks-sdk-go/service/serving.RateLimit", + "deprecationMessage": "This field is deprecated", + "deprecated": true }, - "service_principal_name": { - "$ref": "#/$defs/string" + "route_optimized": { + "description": "Enable route optimization for the serving endpoint.", + "$ref": "#/$defs/bool" }, - "user_name": { - "$ref": "#/$defs/string" + "tags": { + "description": "Tags to be attached to the serving endpoint and automatically propagated to billing logs.", + "$ref": "#/$defs/slice/github.com/databricks/databricks-sdk-go/service/serving.EndpointTag" } }, "additionalProperties": false, "required": [ - "level" - ] - }, - { - "type": "string", - "pattern": "\\$\\{(var(\\.[a-zA-Z]+([-_]?[a-zA-Z0-9]+)*(\\[[0-9]+\\])*)+)\\}" - } - ] - }, - "resources.ModelServingEndpointPermissionLevel": { - "oneOf": [ - { - "type": "string", - "enum": [ - "CAN_MANAGE", - "CAN_QUERY", - "CAN_VIEW" - ] + "name" + ], + "markdownDescription": "The model_serving_endpoint resource allows you to define [model serving endpoints](https://docs.databricks.com/api/workspace/servingendpoints/create). See [link](https://docs.databricks.com/machine-learning/model-serving/manage-serving-endpoints.html)." }, { "type": "string", @@ -1506,7 +1053,7 @@ "$ref": "#/$defs/slice/github.com/databricks/databricks-sdk-go/service/pipelines.Notifications" }, "permissions": { - "$ref": "#/$defs/slice/github.com/databricks/cli/bundle/config/resources.PipelinePermission" + "$ref": "#/$defs/slice/github.com/databricks/cli/bundle/config/resources.Permission" }, "photon": { "description": "Whether Photon is enabled for this pipeline.", @@ -1569,52 +1116,6 @@ } ] }, - "resources.PipelinePermission": { - "oneOf": [ - { - "type": "object", - "properties": { - "group_name": { - "$ref": "#/$defs/string" - }, - "level": { - "$ref": "#/$defs/github.com/databricks/cli/bundle/config/resources.PipelinePermissionLevel" - }, - "service_principal_name": { - "$ref": "#/$defs/string" - }, - "user_name": { - "$ref": "#/$defs/string" - } - }, - "additionalProperties": false, - "required": [ - "level" - ] - }, - { - "type": "string", - "pattern": "\\$\\{(var(\\.[a-zA-Z]+([-_]?[a-zA-Z0-9]+)*(\\[[0-9]+\\])*)+)\\}" - } - ] - }, - "resources.PipelinePermissionLevel": { - "oneOf": [ - { - "type": "string", - "enum": [ - "CAN_MANAGE", - "IS_OWNER", - "CAN_RUN", - "CAN_VIEW" - ] - }, - { - "type": "string", - "pattern": "\\$\\{(var(\\.[a-zA-Z]+([-_]?[a-zA-Z0-9]+)*(\\[[0-9]+\\])*)+)\\}" - } - ] - }, "resources.PostgresBranch": { "oneOf": [ { @@ -1742,7 +1243,7 @@ "$ref": "#/$defs/github.com/databricks/cli/bundle/config/resources.Lifecycle" }, "permissions": { - "$ref": "#/$defs/slice/github.com/databricks/cli/bundle/config/resources.DatabaseProjectPermission" + "$ref": "#/$defs/slice/github.com/databricks/cli/bundle/config/resources.Permission" }, "pg_version": { "$ref": "#/$defs/int" @@ -2099,7 +1600,7 @@ "$ref": "#/$defs/string" }, "permissions": { - "$ref": "#/$defs/slice/github.com/databricks/cli/bundle/config/resources.SqlWarehousePermission" + "$ref": "#/$defs/slice/github.com/databricks/cli/bundle/config/resources.Permission" }, "spot_instance_policy": { "$ref": "#/$defs/github.com/databricks/databricks-sdk-go/service/sql.SpotInstancePolicy" @@ -2120,52 +1621,6 @@ } ] }, - "resources.SqlWarehousePermission": { - "oneOf": [ - { - "type": "object", - "properties": { - "group_name": { - "$ref": "#/$defs/string" - }, - "level": { - "$ref": "#/$defs/github.com/databricks/cli/bundle/config/resources.SqlWarehousePermissionLevel" - }, - "service_principal_name": { - "$ref": "#/$defs/string" - }, - "user_name": { - "$ref": "#/$defs/string" - } - }, - "additionalProperties": false, - "required": [ - "level" - ] - }, - { - "type": "string", - "pattern": "\\$\\{(var(\\.[a-zA-Z]+([-_]?[a-zA-Z0-9]+)*(\\[[0-9]+\\])*)+)\\}" - } - ] - }, - "resources.SqlWarehousePermissionLevel": { - "oneOf": [ - { - "type": "string", - "enum": [ - "CAN_MANAGE", - "CAN_USE", - "CAN_MONITOR", - "CAN_VIEW" - ] - }, - { - "type": "string", - "pattern": "\\$\\{(var(\\.[a-zA-Z]+([-_]?[a-zA-Z0-9]+)*(\\[[0-9]+\\])*)+)\\}" - } - ] - }, "resources.SyncedDatabaseTable": { "oneOf": [ { @@ -11033,20 +10488,6 @@ "cli": { "bundle": { "config": { - "resources.AlertPermission": { - "oneOf": [ - { - "type": "array", - "items": { - "$ref": "#/$defs/github.com/databricks/cli/bundle/config/resources.AlertPermission" - } - }, - { - "type": "string", - "pattern": "\\$\\{(var(\\.[a-zA-Z]+([-_]?[a-zA-Z0-9]+)*(\\[[0-9]+\\])*)+)\\}" - } - ] - }, "resources.AppEnvVar": { "oneOf": [ { @@ -11061,132 +10502,6 @@ } ] }, - "resources.AppPermission": { - "oneOf": [ - { - "type": "array", - "items": { - "$ref": "#/$defs/github.com/databricks/cli/bundle/config/resources.AppPermission" - } - }, - { - "type": "string", - "pattern": "\\$\\{(var(\\.[a-zA-Z]+([-_]?[a-zA-Z0-9]+)*(\\[[0-9]+\\])*)+)\\}" - } - ] - }, - "resources.ClusterPermission": { - "oneOf": [ - { - "type": "array", - "items": { - "$ref": "#/$defs/github.com/databricks/cli/bundle/config/resources.ClusterPermission" - } - }, - { - "type": "string", - "pattern": "\\$\\{(var(\\.[a-zA-Z]+([-_]?[a-zA-Z0-9]+)*(\\[[0-9]+\\])*)+)\\}" - } - ] - }, - "resources.DashboardPermission": { - "oneOf": [ - { - "type": "array", - "items": { - "$ref": "#/$defs/github.com/databricks/cli/bundle/config/resources.DashboardPermission" - } - }, - { - "type": "string", - "pattern": "\\$\\{(var(\\.[a-zA-Z]+([-_]?[a-zA-Z0-9]+)*(\\[[0-9]+\\])*)+)\\}" - } - ] - }, - "resources.DatabaseInstancePermission": { - "oneOf": [ - { - "type": "array", - "items": { - "$ref": "#/$defs/github.com/databricks/cli/bundle/config/resources.DatabaseInstancePermission" - } - }, - { - "type": "string", - "pattern": "\\$\\{(var(\\.[a-zA-Z]+([-_]?[a-zA-Z0-9]+)*(\\[[0-9]+\\])*)+)\\}" - } - ] - }, - "resources.DatabaseProjectPermission": { - "oneOf": [ - { - "type": "array", - "items": { - "$ref": "#/$defs/github.com/databricks/cli/bundle/config/resources.DatabaseProjectPermission" - } - }, - { - "type": "string", - "pattern": "\\$\\{(var(\\.[a-zA-Z]+([-_]?[a-zA-Z0-9]+)*(\\[[0-9]+\\])*)+)\\}" - } - ] - }, - "resources.JobPermission": { - "oneOf": [ - { - "type": "array", - "items": { - "$ref": "#/$defs/github.com/databricks/cli/bundle/config/resources.JobPermission" - } - }, - { - "type": "string", - "pattern": "\\$\\{(var(\\.[a-zA-Z]+([-_]?[a-zA-Z0-9]+)*(\\[[0-9]+\\])*)+)\\}" - } - ] - }, - "resources.MlflowExperimentPermission": { - "oneOf": [ - { - "type": "array", - "items": { - "$ref": "#/$defs/github.com/databricks/cli/bundle/config/resources.MlflowExperimentPermission" - } - }, - { - "type": "string", - "pattern": "\\$\\{(var(\\.[a-zA-Z]+([-_]?[a-zA-Z0-9]+)*(\\[[0-9]+\\])*)+)\\}" - } - ] - }, - "resources.MlflowModelPermission": { - "oneOf": [ - { - "type": "array", - "items": { - "$ref": "#/$defs/github.com/databricks/cli/bundle/config/resources.MlflowModelPermission" - } - }, - { - "type": "string", - "pattern": "\\$\\{(var(\\.[a-zA-Z]+([-_]?[a-zA-Z0-9]+)*(\\[[0-9]+\\])*)+)\\}" - } - ] - }, - "resources.ModelServingEndpointPermission": { - "oneOf": [ - { - "type": "array", - "items": { - "$ref": "#/$defs/github.com/databricks/cli/bundle/config/resources.ModelServingEndpointPermission" - } - }, - { - "type": "string", - "pattern": "\\$\\{(var(\\.[a-zA-Z]+([-_]?[a-zA-Z0-9]+)*(\\[[0-9]+\\])*)+)\\}" - } - ] - }, "resources.Permission": { "oneOf": [ { @@ -11201,20 +10516,6 @@ } ] }, - "resources.PipelinePermission": { - "oneOf": [ - { - "type": "array", - "items": { - "$ref": "#/$defs/github.com/databricks/cli/bundle/config/resources.PipelinePermission" - } - }, - { - "type": "string", - "pattern": "\\$\\{(var(\\.[a-zA-Z]+([-_]?[a-zA-Z0-9]+)*(\\[[0-9]+\\])*)+)\\}" - } - ] - }, "resources.SecretScopePermission": { "oneOf": [ { @@ -11228,20 +10529,6 @@ "pattern": "\\$\\{(var(\\.[a-zA-Z]+([-_]?[a-zA-Z0-9]+)*(\\[[0-9]+\\])*)+)\\}" } ] - }, - "resources.SqlWarehousePermission": { - "oneOf": [ - { - "type": "array", - "items": { - "$ref": "#/$defs/github.com/databricks/cli/bundle/config/resources.SqlWarehousePermission" - } - }, - { - "type": "string", - "pattern": "\\$\\{(var(\\.[a-zA-Z]+([-_]?[a-zA-Z0-9]+)*(\\[[0-9]+\\])*)+)\\}" - } - ] } }, "config.ArtifactFile": { diff --git a/bundle/tests/bundle_permissions_test.go b/bundle/tests/bundle_permissions_test.go index 44eed0c153..9365208246 100644 --- a/bundle/tests/bundle_permissions_test.go +++ b/bundle/tests/bundle_permissions_test.go @@ -22,16 +22,16 @@ func TestBundlePermissions(t *testing.T) { require.NoError(t, diags.Error()) pipelinePermissions := b.Config.Resources.Pipelines["nyc_taxi_pipeline"].Permissions - assert.Contains(t, pipelinePermissions, resources.PipelinePermission{Level: "CAN_RUN", UserName: "test@company.com"}) - assert.NotContains(t, pipelinePermissions, resources.PipelinePermission{Level: "CAN_MANAGE", GroupName: "devs"}) - assert.NotContains(t, pipelinePermissions, resources.PipelinePermission{Level: "CAN_VIEW", ServicePrincipalName: "1234-abcd"}) - assert.NotContains(t, pipelinePermissions, resources.PipelinePermission{Level: "CAN_RUN", UserName: "bot@company.com"}) + assert.Contains(t, pipelinePermissions, resources.Permission{Level: "CAN_RUN", UserName: "test@company.com"}) + assert.NotContains(t, pipelinePermissions, resources.Permission{Level: "CAN_MANAGE", GroupName: "devs"}) + assert.NotContains(t, pipelinePermissions, resources.Permission{Level: "CAN_VIEW", ServicePrincipalName: "1234-abcd"}) + assert.NotContains(t, pipelinePermissions, resources.Permission{Level: "CAN_RUN", UserName: "bot@company.com"}) jobsPermissions := b.Config.Resources.Jobs["pipeline_schedule"].Permissions - assert.Contains(t, jobsPermissions, resources.JobPermission{Level: "CAN_MANAGE_RUN", UserName: "test@company.com"}) - assert.NotContains(t, jobsPermissions, resources.JobPermission{Level: "CAN_MANAGE", GroupName: "devs"}) - assert.NotContains(t, jobsPermissions, resources.JobPermission{Level: "CAN_VIEW", ServicePrincipalName: "1234-abcd"}) - assert.NotContains(t, jobsPermissions, resources.JobPermission{Level: "CAN_MANAGE_RUN", UserName: "bot@company.com"}) + assert.Contains(t, jobsPermissions, resources.Permission{Level: "CAN_MANAGE_RUN", UserName: "test@company.com"}) + assert.NotContains(t, jobsPermissions, resources.Permission{Level: "CAN_MANAGE", GroupName: "devs"}) + assert.NotContains(t, jobsPermissions, resources.Permission{Level: "CAN_VIEW", ServicePrincipalName: "1234-abcd"}) + assert.NotContains(t, jobsPermissions, resources.Permission{Level: "CAN_MANAGE_RUN", UserName: "bot@company.com"}) } func TestBundlePermissionsDevTarget(t *testing.T) { @@ -45,14 +45,14 @@ func TestBundlePermissionsDevTarget(t *testing.T) { require.NoError(t, diags.Error()) pipelinePermissions := b.Config.Resources.Pipelines["nyc_taxi_pipeline"].Permissions - assert.Contains(t, pipelinePermissions, resources.PipelinePermission{Level: "CAN_RUN", UserName: "test@company.com"}) - assert.Contains(t, pipelinePermissions, resources.PipelinePermission{Level: "CAN_MANAGE", GroupName: "devs"}) - assert.Contains(t, pipelinePermissions, resources.PipelinePermission{Level: "CAN_VIEW", ServicePrincipalName: "1234-abcd"}) - assert.Contains(t, pipelinePermissions, resources.PipelinePermission{Level: "CAN_RUN", UserName: "bot@company.com"}) + assert.Contains(t, pipelinePermissions, resources.Permission{Level: "CAN_RUN", UserName: "test@company.com"}) + assert.Contains(t, pipelinePermissions, resources.Permission{Level: "CAN_MANAGE", GroupName: "devs"}) + assert.Contains(t, pipelinePermissions, resources.Permission{Level: "CAN_VIEW", ServicePrincipalName: "1234-abcd"}) + assert.Contains(t, pipelinePermissions, resources.Permission{Level: "CAN_RUN", UserName: "bot@company.com"}) jobsPermissions := b.Config.Resources.Jobs["pipeline_schedule"].Permissions - assert.Contains(t, jobsPermissions, resources.JobPermission{Level: "CAN_MANAGE_RUN", UserName: "test@company.com"}) - assert.Contains(t, jobsPermissions, resources.JobPermission{Level: "CAN_MANAGE", GroupName: "devs"}) - assert.Contains(t, jobsPermissions, resources.JobPermission{Level: "CAN_VIEW", ServicePrincipalName: "1234-abcd"}) - assert.Contains(t, jobsPermissions, resources.JobPermission{Level: "CAN_MANAGE_RUN", UserName: "bot@company.com"}) + assert.Contains(t, jobsPermissions, resources.Permission{Level: "CAN_MANAGE_RUN", UserName: "test@company.com"}) + assert.Contains(t, jobsPermissions, resources.Permission{Level: "CAN_MANAGE", GroupName: "devs"}) + assert.Contains(t, jobsPermissions, resources.Permission{Level: "CAN_VIEW", ServicePrincipalName: "1234-abcd"}) + assert.Contains(t, jobsPermissions, resources.Permission{Level: "CAN_MANAGE_RUN", UserName: "bot@company.com"}) } diff --git a/bundle/tests/model_serving_endpoint_test.go b/bundle/tests/model_serving_endpoint_test.go index c0997c0f9a..900e93fa25 100644 --- a/bundle/tests/model_serving_endpoint_test.go +++ b/bundle/tests/model_serving_endpoint_test.go @@ -17,7 +17,7 @@ func assertExpected(t *testing.T, p *resources.ModelServingEndpoint) { assert.Equal(t, "model-name-1", p.Config.TrafficConfig.Routes[0].ServedModelName) assert.Equal(t, 100, p.Config.TrafficConfig.Routes[0].TrafficPercentage) - assert.Equal(t, resources.ModelServingEndpointPermission{ + assert.Equal(t, resources.Permission{ GroupName: "users", Level: "CAN_QUERY", }, p.Permissions[0]) diff --git a/python/codegen/codegen/aliases_patch.py b/python/codegen/codegen/aliases_patch.py index 864f70afb9..eed4ceb4c4 100644 --- a/python/codegen/codegen/aliases_patch.py +++ b/python/codegen/codegen/aliases_patch.py @@ -22,4 +22,14 @@ "VolumeGrantPrivilege": "Privilege", "VolumeGrantPrivilegeParam": "PrivilegeParam", }, + "jobs": { + "JobPermission": "Permission", + "JobPermissionDict": "PermissionDict", + "JobPermissionParam": "PermissionParam", + }, + "pipelines": { + "PipelinePermission": "Permission", + "PipelinePermissionDict": "PermissionDict", + "PipelinePermissionParam": "PermissionParam", + }, } diff --git a/python/databricks/bundles/jobs/__init__.py b/python/databricks/bundles/jobs/__init__.py index 3e98d2acd6..7e2caf34ed 100644 --- a/python/databricks/bundles/jobs/__init__.py +++ b/python/databricks/bundles/jobs/__init__.py @@ -120,8 +120,6 @@ "JobParameterDefinitionParam", "JobPermission", "JobPermissionDict", - "JobPermissionLevel", - "JobPermissionLevelParam", "JobPermissionParam", "JobRunAs", "JobRunAsDict", @@ -173,6 +171,9 @@ "PeriodicTriggerConfigurationParam", "PeriodicTriggerConfigurationTimeUnit", "PeriodicTriggerConfigurationTimeUnitParam", + "Permission", + "PermissionDict", + "PermissionParam", "PipelineParams", "PipelineParamsDict", "PipelineParamsParam", @@ -475,15 +476,6 @@ JobParameterDefinitionDict, JobParameterDefinitionParam, ) -from databricks.bundles.jobs._models.job_permission import ( - JobPermission, - JobPermissionDict, - JobPermissionParam, -) -from databricks.bundles.jobs._models.job_permission_level import ( - JobPermissionLevel, - JobPermissionLevelParam, -) from databricks.bundles.jobs._models.job_run_as import ( JobRunAs, JobRunAsDict, @@ -562,6 +554,11 @@ PeriodicTriggerConfigurationTimeUnit, PeriodicTriggerConfigurationTimeUnitParam, ) +from databricks.bundles.jobs._models.permission import ( + Permission, + PermissionDict, + PermissionParam, +) from databricks.bundles.jobs._models.pipeline_params import ( PipelineParams, PipelineParamsDict, @@ -732,6 +729,11 @@ WorkspaceStorageInfoParam, ) +# Backward-compatibility aliases +JobPermission = Permission +JobPermissionDict = PermissionDict +JobPermissionParam = PermissionParam + def _resolve_recursive_imports(): import typing diff --git a/python/databricks/bundles/jobs/_models/job.py b/python/databricks/bundles/jobs/_models/job.py index e836d4c9a8..44d3967e15 100644 --- a/python/databricks/bundles/jobs/_models/job.py +++ b/python/databricks/bundles/jobs/_models/job.py @@ -35,10 +35,6 @@ JobParameterDefinition, JobParameterDefinitionParam, ) -from databricks.bundles.jobs._models.job_permission import ( - JobPermission, - JobPermissionParam, -) from databricks.bundles.jobs._models.job_run_as import JobRunAs, JobRunAsParam from databricks.bundles.jobs._models.jobs_health_rules import ( JobsHealthRules, @@ -49,6 +45,7 @@ PerformanceTarget, PerformanceTargetParam, ) +from databricks.bundles.jobs._models.permission import Permission, PermissionParam from databricks.bundles.jobs._models.queue_settings import ( QueueSettings, QueueSettingsParam, @@ -155,7 +152,7 @@ class Job(Resource): * `PERFORMANCE_OPTIMIZED`: Prioritizes fast startup and execution times through rapid scaling and optimized cluster performance. """ - permissions: VariableOrList[JobPermission] = field(default_factory=list) + permissions: VariableOrList[Permission] = field(default_factory=list) queue: VariableOrOptional[QueueSettings] = None """ @@ -300,7 +297,7 @@ class JobDict(TypedDict, total=False): * `PERFORMANCE_OPTIMIZED`: Prioritizes fast startup and execution times through rapid scaling and optimized cluster performance. """ - permissions: VariableOrList[JobPermissionParam] + permissions: VariableOrList[PermissionParam] queue: VariableOrOptional[QueueSettingsParam] """ diff --git a/python/databricks/bundles/jobs/_models/job_permission.py b/python/databricks/bundles/jobs/_models/job_permission.py deleted file mode 100644 index b2ab73cee0..0000000000 --- a/python/databricks/bundles/jobs/_models/job_permission.py +++ /dev/null @@ -1,48 +0,0 @@ -from dataclasses import dataclass -from typing import TYPE_CHECKING, TypedDict - -from databricks.bundles.core._transform import _transform -from databricks.bundles.core._transform_to_json import _transform_to_json_value -from databricks.bundles.core._variable import VariableOr, VariableOrOptional -from databricks.bundles.jobs._models.job_permission_level import ( - JobPermissionLevel, - JobPermissionLevelParam, -) - -if TYPE_CHECKING: - from typing_extensions import Self - - -@dataclass(kw_only=True) -class JobPermission: - """""" - - level: VariableOr[JobPermissionLevel] - - group_name: VariableOrOptional[str] = None - - service_principal_name: VariableOrOptional[str] = None - - user_name: VariableOrOptional[str] = None - - @classmethod - def from_dict(cls, value: "JobPermissionDict") -> "Self": - return _transform(cls, value) - - def as_dict(self) -> "JobPermissionDict": - return _transform_to_json_value(self) # type:ignore - - -class JobPermissionDict(TypedDict, total=False): - """""" - - level: VariableOr[JobPermissionLevelParam] - - group_name: VariableOrOptional[str] - - service_principal_name: VariableOrOptional[str] - - user_name: VariableOrOptional[str] - - -JobPermissionParam = JobPermissionDict | JobPermission diff --git a/python/databricks/bundles/jobs/_models/job_permission_level.py b/python/databricks/bundles/jobs/_models/job_permission_level.py deleted file mode 100644 index fc4e5ebac7..0000000000 --- a/python/databricks/bundles/jobs/_models/job_permission_level.py +++ /dev/null @@ -1,14 +0,0 @@ -from enum import Enum -from typing import Literal - - -class JobPermissionLevel(Enum): - CAN_MANAGE = "CAN_MANAGE" - CAN_MANAGE_RUN = "CAN_MANAGE_RUN" - CAN_VIEW = "CAN_VIEW" - IS_OWNER = "IS_OWNER" - - -JobPermissionLevelParam = ( - Literal["CAN_MANAGE", "CAN_MANAGE_RUN", "CAN_VIEW", "IS_OWNER"] | JobPermissionLevel -) diff --git a/python/databricks/bundles/jobs/_models/permission.py b/python/databricks/bundles/jobs/_models/permission.py new file mode 100644 index 0000000000..493137a3a9 --- /dev/null +++ b/python/databricks/bundles/jobs/_models/permission.py @@ -0,0 +1,68 @@ +from dataclasses import dataclass +from typing import TYPE_CHECKING, TypedDict + +from databricks.bundles.core._transform import _transform +from databricks.bundles.core._transform_to_json import _transform_to_json_value +from databricks.bundles.core._variable import VariableOr, VariableOrOptional + +if TYPE_CHECKING: + from typing_extensions import Self + + +@dataclass(kw_only=True) +class Permission: + """""" + + level: VariableOr[str] + """ + The allowed permission for user, group, service principal defined for this permission. + """ + + group_name: VariableOrOptional[str] = None + """ + The name of the group that has the permission set in level. + """ + + service_principal_name: VariableOrOptional[str] = None + """ + The name of the service principal that has the permission set in level. + """ + + user_name: VariableOrOptional[str] = None + """ + The name of the user that has the permission set in level. + """ + + @classmethod + def from_dict(cls, value: "PermissionDict") -> "Self": + return _transform(cls, value) + + def as_dict(self) -> "PermissionDict": + return _transform_to_json_value(self) # type:ignore + + +class PermissionDict(TypedDict, total=False): + """""" + + level: VariableOr[str] + """ + The allowed permission for user, group, service principal defined for this permission. + """ + + group_name: VariableOrOptional[str] + """ + The name of the group that has the permission set in level. + """ + + service_principal_name: VariableOrOptional[str] + """ + The name of the service principal that has the permission set in level. + """ + + user_name: VariableOrOptional[str] + """ + The name of the user that has the permission set in level. + """ + + +PermissionParam = PermissionDict | Permission diff --git a/python/databricks/bundles/pipelines/__init__.py b/python/databricks/bundles/pipelines/__init__.py index 2aef912fa7..9458231fd8 100644 --- a/python/databricks/bundles/pipelines/__init__.py +++ b/python/databricks/bundles/pipelines/__init__.py @@ -90,6 +90,9 @@ "PathPattern", "PathPatternDict", "PathPatternParam", + "Permission", + "PermissionDict", + "PermissionParam", "Pipeline", "PipelineCluster", "PipelineClusterAutoscale", @@ -106,8 +109,6 @@ "PipelineParam", "PipelinePermission", "PipelinePermissionDict", - "PipelinePermissionLevel", - "PipelinePermissionLevelParam", "PipelinePermissionParam", "PipelinesEnvironment", "PipelinesEnvironmentDict", @@ -308,6 +309,11 @@ PathPatternDict, PathPatternParam, ) +from databricks.bundles.pipelines._models.permission import ( + Permission, + PermissionDict, + PermissionParam, +) from databricks.bundles.pipelines._models.pipeline import ( Pipeline, PipelineDict, @@ -332,15 +338,6 @@ PipelineLibraryDict, PipelineLibraryParam, ) -from databricks.bundles.pipelines._models.pipeline_permission import ( - PipelinePermission, - PipelinePermissionDict, - PipelinePermissionParam, -) -from databricks.bundles.pipelines._models.pipeline_permission_level import ( - PipelinePermissionLevel, - PipelinePermissionLevelParam, -) from databricks.bundles.pipelines._models.pipelines_environment import ( PipelinesEnvironment, PipelinesEnvironmentDict, @@ -411,3 +408,8 @@ WorkspaceStorageInfoDict, WorkspaceStorageInfoParam, ) + +# Backward-compatibility aliases +PipelinePermission = Permission +PipelinePermissionDict = PermissionDict +PipelinePermissionParam = PermissionParam diff --git a/python/databricks/bundles/pipelines/_models/permission.py b/python/databricks/bundles/pipelines/_models/permission.py new file mode 100644 index 0000000000..493137a3a9 --- /dev/null +++ b/python/databricks/bundles/pipelines/_models/permission.py @@ -0,0 +1,68 @@ +from dataclasses import dataclass +from typing import TYPE_CHECKING, TypedDict + +from databricks.bundles.core._transform import _transform +from databricks.bundles.core._transform_to_json import _transform_to_json_value +from databricks.bundles.core._variable import VariableOr, VariableOrOptional + +if TYPE_CHECKING: + from typing_extensions import Self + + +@dataclass(kw_only=True) +class Permission: + """""" + + level: VariableOr[str] + """ + The allowed permission for user, group, service principal defined for this permission. + """ + + group_name: VariableOrOptional[str] = None + """ + The name of the group that has the permission set in level. + """ + + service_principal_name: VariableOrOptional[str] = None + """ + The name of the service principal that has the permission set in level. + """ + + user_name: VariableOrOptional[str] = None + """ + The name of the user that has the permission set in level. + """ + + @classmethod + def from_dict(cls, value: "PermissionDict") -> "Self": + return _transform(cls, value) + + def as_dict(self) -> "PermissionDict": + return _transform_to_json_value(self) # type:ignore + + +class PermissionDict(TypedDict, total=False): + """""" + + level: VariableOr[str] + """ + The allowed permission for user, group, service principal defined for this permission. + """ + + group_name: VariableOrOptional[str] + """ + The name of the group that has the permission set in level. + """ + + service_principal_name: VariableOrOptional[str] + """ + The name of the service principal that has the permission set in level. + """ + + user_name: VariableOrOptional[str] + """ + The name of the user that has the permission set in level. + """ + + +PermissionParam = PermissionDict | Permission diff --git a/python/databricks/bundles/pipelines/_models/pipeline.py b/python/databricks/bundles/pipelines/_models/pipeline.py index 2b4bbed23b..1002c7c9eb 100644 --- a/python/databricks/bundles/pipelines/_models/pipeline.py +++ b/python/databricks/bundles/pipelines/_models/pipeline.py @@ -33,6 +33,7 @@ Notifications, NotificationsParam, ) +from databricks.bundles.pipelines._models.permission import Permission, PermissionParam from databricks.bundles.pipelines._models.pipeline_cluster import ( PipelineCluster, PipelineClusterParam, @@ -41,10 +42,6 @@ PipelineLibrary, PipelineLibraryParam, ) -from databricks.bundles.pipelines._models.pipeline_permission import ( - PipelinePermission, - PipelinePermissionParam, -) from databricks.bundles.pipelines._models.pipelines_environment import ( PipelinesEnvironment, PipelinesEnvironmentParam, @@ -160,7 +157,7 @@ class Pipeline(Resource): List of notification settings for this pipeline. """ - permissions: VariableOrList[PipelinePermission] = field(default_factory=list) + permissions: VariableOrList[Permission] = field(default_factory=list) photon: VariableOrOptional[bool] = None """ @@ -325,7 +322,7 @@ class PipelineDict(TypedDict, total=False): List of notification settings for this pipeline. """ - permissions: VariableOrList[PipelinePermissionParam] + permissions: VariableOrList[PermissionParam] photon: VariableOrOptional[bool] """ diff --git a/python/databricks/bundles/pipelines/_models/pipeline_permission.py b/python/databricks/bundles/pipelines/_models/pipeline_permission.py deleted file mode 100644 index 0a3ed95538..0000000000 --- a/python/databricks/bundles/pipelines/_models/pipeline_permission.py +++ /dev/null @@ -1,48 +0,0 @@ -from dataclasses import dataclass -from typing import TYPE_CHECKING, TypedDict - -from databricks.bundles.core._transform import _transform -from databricks.bundles.core._transform_to_json import _transform_to_json_value -from databricks.bundles.core._variable import VariableOr, VariableOrOptional -from databricks.bundles.pipelines._models.pipeline_permission_level import ( - PipelinePermissionLevel, - PipelinePermissionLevelParam, -) - -if TYPE_CHECKING: - from typing_extensions import Self - - -@dataclass(kw_only=True) -class PipelinePermission: - """""" - - level: VariableOr[PipelinePermissionLevel] - - group_name: VariableOrOptional[str] = None - - service_principal_name: VariableOrOptional[str] = None - - user_name: VariableOrOptional[str] = None - - @classmethod - def from_dict(cls, value: "PipelinePermissionDict") -> "Self": - return _transform(cls, value) - - def as_dict(self) -> "PipelinePermissionDict": - return _transform_to_json_value(self) # type:ignore - - -class PipelinePermissionDict(TypedDict, total=False): - """""" - - level: VariableOr[PipelinePermissionLevelParam] - - group_name: VariableOrOptional[str] - - service_principal_name: VariableOrOptional[str] - - user_name: VariableOrOptional[str] - - -PipelinePermissionParam = PipelinePermissionDict | PipelinePermission diff --git a/python/databricks/bundles/pipelines/_models/pipeline_permission_level.py b/python/databricks/bundles/pipelines/_models/pipeline_permission_level.py deleted file mode 100644 index 3e6edf3080..0000000000 --- a/python/databricks/bundles/pipelines/_models/pipeline_permission_level.py +++ /dev/null @@ -1,14 +0,0 @@ -from enum import Enum -from typing import Literal - - -class PipelinePermissionLevel(Enum): - CAN_MANAGE = "CAN_MANAGE" - IS_OWNER = "IS_OWNER" - CAN_RUN = "CAN_RUN" - CAN_VIEW = "CAN_VIEW" - - -PipelinePermissionLevelParam = ( - Literal["CAN_MANAGE", "IS_OWNER", "CAN_RUN", "CAN_VIEW"] | PipelinePermissionLevel -) From 26c9f1d62098115ad48d4a0a3a8cb146bac5743c Mon Sep 17 00:00:00 2001 From: Denis Bilenko Date: Fri, 6 Mar 2026 17:16:32 +0100 Subject: [PATCH 03/28] Use aliases_patch.py for backward-compat permission aliases Move jobs/pipelines permission aliases from packages.EXTRA_ALIASES to aliases_patch.ALIASES, consistent with how grant aliases are handled. Co-Authored-By: Claude Sonnet 4.6 --- .claude/settings.local.json | 10 +++++----- python/databricks/bundles/jobs/__init__.py | 9 ++++----- python/databricks/bundles/pipelines/__init__.py | 1 - 3 files changed, 9 insertions(+), 11 deletions(-) diff --git a/.claude/settings.local.json b/.claude/settings.local.json index 823f03a07a..1bfdee9e24 100644 --- a/.claude/settings.local.json +++ b/.claude/settings.local.json @@ -6,11 +6,11 @@ "Bash(make fmtfull:*)", "Bash(make codegen:*)", "Bash(git:*)", - "Bash(GIT_EDITOR=true GIT_SEQUENCE_EDITOR=true VISUAL=true GIT_PAGER=cat git rebase:*)", - "Read(//Users/denis.bilenko/work/cli/.git/worktrees/cli-main-simpler-grant/rebase-merge/**)", - "Read(//Users/denis.bilenko/work/cli/**)", - "Bash(GIT_EDITOR=true GIT_PAGER=cat git diff:*)", - "Bash(GIT_PAGER=cat git rebase:*)" + "Bash(lefthook version:*)", + "Read(//Users/denis.bilenko/.config/git/**)", + "Bash(git config core.hooksPath)", + "Bash(git config --global core.hooksPath)", + "Bash(GIT_EDITOR=true GIT_SEQUENCE_EDITOR=true VISUAL=true GIT_PAGER=cat git:*)" ] } } diff --git a/python/databricks/bundles/jobs/__init__.py b/python/databricks/bundles/jobs/__init__.py index 7e2caf34ed..5831b253ed 100644 --- a/python/databricks/bundles/jobs/__init__.py +++ b/python/databricks/bundles/jobs/__init__.py @@ -729,11 +729,6 @@ WorkspaceStorageInfoParam, ) -# Backward-compatibility aliases -JobPermission = Permission -JobPermissionDict = PermissionDict -JobPermissionParam = PermissionParam - def _resolve_recursive_imports(): import typing @@ -748,3 +743,7 @@ def _resolve_recursive_imports(): _resolve_recursive_imports() + +JobPermission = Permission +JobPermissionDict = PermissionDict +JobPermissionParam = PermissionParam diff --git a/python/databricks/bundles/pipelines/__init__.py b/python/databricks/bundles/pipelines/__init__.py index 9458231fd8..643f59bd24 100644 --- a/python/databricks/bundles/pipelines/__init__.py +++ b/python/databricks/bundles/pipelines/__init__.py @@ -409,7 +409,6 @@ WorkspaceStorageInfoParam, ) -# Backward-compatibility aliases PipelinePermission = Permission PipelinePermissionDict = PermissionDict PipelinePermissionParam = PermissionParam From 4b198721e0e1c9e8e87d7771a5c3576672ae195e Mon Sep 17 00:00:00 2001 From: Denis Bilenko Date: Mon, 9 Mar 2026 15:36:56 +0100 Subject: [PATCH 04/28] rm settings.local --- .claude/settings.local.json | 16 ---------------- 1 file changed, 16 deletions(-) delete mode 100644 .claude/settings.local.json diff --git a/.claude/settings.local.json b/.claude/settings.local.json deleted file mode 100644 index 1bfdee9e24..0000000000 --- a/.claude/settings.local.json +++ /dev/null @@ -1,16 +0,0 @@ -{ - "permissions": { - "allow": [ - "Bash(go run:*)", - "Bash(python3:*)", - "Bash(make fmtfull:*)", - "Bash(make codegen:*)", - "Bash(git:*)", - "Bash(lefthook version:*)", - "Read(//Users/denis.bilenko/.config/git/**)", - "Bash(git config core.hooksPath)", - "Bash(git config --global core.hooksPath)", - "Bash(GIT_EDITOR=true GIT_SEQUENCE_EDITOR=true VISUAL=true GIT_PAGER=cat git:*)" - ] - } -} From a8221a8975a41c749ff17955d66286f3dfef27e3 Mon Sep 17 00:00:00 2001 From: Denis Bilenko Date: Mon, 9 Mar 2026 15:44:09 +0100 Subject: [PATCH 05/28] update jsonschema_for_docs.json --- bundle/schema/jsonschema_for_docs.json | 497 +------------------------ 1 file changed, 12 insertions(+), 485 deletions(-) diff --git a/bundle/schema/jsonschema_for_docs.json b/bundle/schema/jsonschema_for_docs.json index fb2312a96e..811c957784 100644 --- a/bundle/schema/jsonschema_for_docs.json +++ b/bundle/schema/jsonschema_for_docs.json @@ -43,7 +43,7 @@ "x-since-version": "v0.279.0" }, "permissions": { - "$ref": "#/$defs/slice/github.com/databricks/cli/bundle/config/resources.AlertPermission", + "$ref": "#/$defs/slice/github.com/databricks/cli/bundle/config/resources.Permission", "x-since-version": "v0.279.0" }, "query_text": { @@ -78,40 +78,6 @@ "warehouse_id" ] }, - "resources.AlertPermission": { - "type": "object", - "properties": { - "group_name": { - "$ref": "#/$defs/string", - "x-since-version": "v0.279.0" - }, - "level": { - "$ref": "#/$defs/github.com/databricks/cli/bundle/config/resources.AlertPermissionLevel", - "x-since-version": "v0.279.0" - }, - "service_principal_name": { - "$ref": "#/$defs/string", - "x-since-version": "v0.279.0" - }, - "user_name": { - "$ref": "#/$defs/string", - "x-since-version": "v0.279.0" - } - }, - "additionalProperties": false, - "required": [ - "level" - ] - }, - "resources.AlertPermissionLevel": { - "type": "string", - "enum": [ - "CAN_EDIT", - "CAN_MANAGE", - "CAN_READ", - "CAN_RUN" - ] - }, "resources.App": { "type": "object", "properties": { @@ -155,7 +121,7 @@ "x-since-version": "v0.239.0" }, "permissions": { - "$ref": "#/$defs/slice/github.com/databricks/cli/bundle/config/resources.AppPermission", + "$ref": "#/$defs/slice/github.com/databricks/cli/bundle/config/resources.Permission", "x-since-version": "v0.239.0" }, "resources": { @@ -223,38 +189,6 @@ "name" ] }, - "resources.AppPermission": { - "type": "object", - "properties": { - "group_name": { - "$ref": "#/$defs/string", - "x-since-version": "v0.247.0" - }, - "level": { - "$ref": "#/$defs/github.com/databricks/cli/bundle/config/resources.AppPermissionLevel", - "x-since-version": "v0.247.0" - }, - "service_principal_name": { - "$ref": "#/$defs/string", - "x-since-version": "v0.247.0" - }, - "user_name": { - "$ref": "#/$defs/string", - "x-since-version": "v0.247.0" - } - }, - "additionalProperties": false, - "required": [ - "level" - ] - }, - "resources.AppPermissionLevel": { - "type": "string", - "enum": [ - "CAN_MANAGE", - "CAN_USE" - ] - }, "resources.Catalog": { "type": "object", "properties": { @@ -421,7 +355,7 @@ "x-since-version": "v0.229.0" }, "permissions": { - "$ref": "#/$defs/slice/github.com/databricks/cli/bundle/config/resources.ClusterPermission", + "$ref": "#/$defs/slice/github.com/databricks/cli/bundle/config/resources.Permission", "x-since-version": "v0.229.0" }, "policy_id": { @@ -486,39 +420,6 @@ "additionalProperties": false, "markdownDescription": "The cluster resource defines an [all-purpose cluster](https://docs.databricks.com/api/workspace/clusters/create)." }, - "resources.ClusterPermission": { - "type": "object", - "properties": { - "group_name": { - "$ref": "#/$defs/string", - "x-since-version": "v0.247.0" - }, - "level": { - "$ref": "#/$defs/github.com/databricks/cli/bundle/config/resources.ClusterPermissionLevel", - "x-since-version": "v0.247.0" - }, - "service_principal_name": { - "$ref": "#/$defs/string", - "x-since-version": "v0.247.0" - }, - "user_name": { - "$ref": "#/$defs/string", - "x-since-version": "v0.247.0" - } - }, - "additionalProperties": false, - "required": [ - "level" - ] - }, - "resources.ClusterPermissionLevel": { - "type": "string", - "enum": [ - "CAN_MANAGE", - "CAN_RESTART", - "CAN_ATTACH_TO" - ] - }, "resources.Dashboard": { "type": "object", "properties": { @@ -581,7 +482,7 @@ "x-since-version": "v0.234.0" }, "permissions": { - "$ref": "#/$defs/slice/github.com/databricks/cli/bundle/config/resources.DashboardPermission", + "$ref": "#/$defs/slice/github.com/databricks/cli/bundle/config/resources.Permission", "x-since-version": "v0.232.0" }, "serialized_dashboard": { @@ -603,40 +504,6 @@ "additionalProperties": false, "markdownDescription": "The dashboard resource allows you to manage [AI/BI dashboards](https://docs.databricks.com/api/workspace/lakeview/create) in a bundle. For information about AI/BI dashboards, see [link](https://docs.databricks.com/dashboards/index.html)." }, - "resources.DashboardPermission": { - "type": "object", - "properties": { - "group_name": { - "$ref": "#/$defs/string", - "x-since-version": "v0.247.0" - }, - "level": { - "$ref": "#/$defs/github.com/databricks/cli/bundle/config/resources.DashboardPermissionLevel", - "x-since-version": "v0.247.0" - }, - "service_principal_name": { - "$ref": "#/$defs/string", - "x-since-version": "v0.247.0" - }, - "user_name": { - "$ref": "#/$defs/string", - "x-since-version": "v0.247.0" - } - }, - "additionalProperties": false, - "required": [ - "level" - ] - }, - "resources.DashboardPermissionLevel": { - "type": "string", - "enum": [ - "CAN_READ", - "CAN_RUN", - "CAN_EDIT", - "CAN_MANAGE" - ] - }, "resources.DatabaseCatalog": { "type": "object", "properties": { @@ -717,7 +584,7 @@ "x-since-version": "v0.265.0" }, "permissions": { - "$ref": "#/$defs/slice/github.com/databricks/cli/bundle/config/resources.DatabaseInstancePermission", + "$ref": "#/$defs/slice/github.com/databricks/cli/bundle/config/resources.Permission", "x-since-version": "v0.265.0" }, "retention_window_in_days": { @@ -741,71 +608,6 @@ "name" ] }, - "resources.DatabaseInstancePermission": { - "type": "object", - "properties": { - "group_name": { - "$ref": "#/$defs/string", - "x-since-version": "v0.265.0" - }, - "level": { - "$ref": "#/$defs/github.com/databricks/cli/bundle/config/resources.DatabaseInstancePermissionLevel", - "x-since-version": "v0.265.0" - }, - "service_principal_name": { - "$ref": "#/$defs/string", - "x-since-version": "v0.265.0" - }, - "user_name": { - "$ref": "#/$defs/string", - "x-since-version": "v0.265.0" - } - }, - "additionalProperties": false, - "required": [ - "level" - ] - }, - "resources.DatabaseInstancePermissionLevel": { - "type": "string", - "enum": [ - "CAN_CREATE", - "CAN_USE", - "CAN_MANAGE" - ] - }, - "resources.DatabaseProjectPermission": { - "type": "object", - "properties": { - "group_name": { - "$ref": "#/$defs/string", - "x-since-version": "v0.292.0" - }, - "level": { - "$ref": "#/$defs/github.com/databricks/cli/bundle/config/resources.DatabaseProjectPermissionLevel", - "x-since-version": "v0.292.0" - }, - "service_principal_name": { - "$ref": "#/$defs/string", - "x-since-version": "v0.292.0" - }, - "user_name": { - "$ref": "#/$defs/string", - "x-since-version": "v0.292.0" - } - }, - "additionalProperties": false, - "required": [ - "level" - ] - }, - "resources.DatabaseProjectPermissionLevel": { - "type": "string", - "enum": [ - "CAN_USE", - "CAN_MANAGE" - ] - }, "resources.ExternalLocation": { "type": "object", "properties": { @@ -938,7 +740,7 @@ "x-since-version": "v0.241.0" }, "permissions": { - "$ref": "#/$defs/slice/github.com/databricks/cli/bundle/config/resources.JobPermission", + "$ref": "#/$defs/slice/github.com/databricks/cli/bundle/config/resources.Permission", "x-since-version": "v0.229.0" }, "queue": { @@ -991,40 +793,6 @@ "additionalProperties": false, "markdownDescription": "The job resource allows you to define [jobs and their corresponding tasks](https://docs.databricks.com/api/workspace/jobs/create) in your bundle. For information about jobs, see [link](https://docs.databricks.com/jobs/index.html). For a tutorial that uses a Databricks Asset Bundles template to create a job, see [link](https://docs.databricks.com/dev-tools/bundles/jobs-tutorial.html)." }, - "resources.JobPermission": { - "type": "object", - "properties": { - "group_name": { - "$ref": "#/$defs/string", - "x-since-version": "v0.247.0" - }, - "level": { - "$ref": "#/$defs/github.com/databricks/cli/bundle/config/resources.JobPermissionLevel", - "x-since-version": "v0.247.0" - }, - "service_principal_name": { - "$ref": "#/$defs/string", - "x-since-version": "v0.247.0" - }, - "user_name": { - "$ref": "#/$defs/string", - "x-since-version": "v0.247.0" - } - }, - "additionalProperties": false, - "required": [ - "level" - ] - }, - "resources.JobPermissionLevel": { - "type": "string", - "enum": [ - "CAN_MANAGE", - "CAN_MANAGE_RUN", - "CAN_VIEW", - "IS_OWNER" - ] - }, "resources.Lifecycle": { "type": "object", "properties": { @@ -1055,7 +823,7 @@ "x-since-version": "v0.229.0" }, "permissions": { - "$ref": "#/$defs/slice/github.com/databricks/cli/bundle/config/resources.MlflowExperimentPermission", + "$ref": "#/$defs/slice/github.com/databricks/cli/bundle/config/resources.Permission", "x-since-version": "v0.229.0" }, "tags": { @@ -1070,39 +838,6 @@ ], "markdownDescription": "The experiment resource allows you to define [MLflow experiments](https://docs.databricks.com/api/workspace/experiments/createexperiment) in a bundle. For information about MLflow experiments, see [link](https://docs.databricks.com/mlflow/experiments.html)." }, - "resources.MlflowExperimentPermission": { - "type": "object", - "properties": { - "group_name": { - "$ref": "#/$defs/string", - "x-since-version": "v0.247.0" - }, - "level": { - "$ref": "#/$defs/github.com/databricks/cli/bundle/config/resources.MlflowExperimentPermissionLevel", - "x-since-version": "v0.247.0" - }, - "service_principal_name": { - "$ref": "#/$defs/string", - "x-since-version": "v0.247.0" - }, - "user_name": { - "$ref": "#/$defs/string", - "x-since-version": "v0.247.0" - } - }, - "additionalProperties": false, - "required": [ - "level" - ] - }, - "resources.MlflowExperimentPermissionLevel": { - "type": "string", - "enum": [ - "CAN_MANAGE", - "CAN_EDIT", - "CAN_READ" - ] - }, "resources.MlflowModel": { "type": "object", "properties": { @@ -1122,7 +857,7 @@ "x-since-version": "v0.229.0" }, "permissions": { - "$ref": "#/$defs/slice/github.com/databricks/cli/bundle/config/resources.MlflowModelPermission", + "$ref": "#/$defs/slice/github.com/databricks/cli/bundle/config/resources.Permission", "x-since-version": "v0.229.0" }, "tags": { @@ -1137,41 +872,6 @@ ], "markdownDescription": "The model resource allows you to define [legacy models](https://docs.databricks.com/api/workspace/modelregistry/createmodel) in bundles. Databricks recommends you use Unity Catalog [registered models](https://docs.databricks.com/dev-tools/bundles/reference.html#registered-model) instead." }, - "resources.MlflowModelPermission": { - "type": "object", - "properties": { - "group_name": { - "$ref": "#/$defs/string", - "x-since-version": "v0.247.0" - }, - "level": { - "$ref": "#/$defs/github.com/databricks/cli/bundle/config/resources.MlflowModelPermissionLevel", - "x-since-version": "v0.247.0" - }, - "service_principal_name": { - "$ref": "#/$defs/string", - "x-since-version": "v0.247.0" - }, - "user_name": { - "$ref": "#/$defs/string", - "x-since-version": "v0.247.0" - } - }, - "additionalProperties": false, - "required": [ - "level" - ] - }, - "resources.MlflowModelPermissionLevel": { - "type": "string", - "enum": [ - "CAN_EDIT", - "CAN_MANAGE", - "CAN_MANAGE_STAGING_VERSIONS", - "CAN_MANAGE_PRODUCTION_VERSIONS", - "CAN_READ" - ] - }, "resources.ModelServingEndpoint": { "type": "object", "properties": { @@ -1210,7 +910,7 @@ "x-since-version": "v0.229.0" }, "permissions": { - "$ref": "#/$defs/slice/github.com/databricks/cli/bundle/config/resources.ModelServingEndpointPermission", + "$ref": "#/$defs/slice/github.com/databricks/cli/bundle/config/resources.Permission", "x-since-version": "v0.229.0" }, "rate_limits": { @@ -1237,39 +937,6 @@ ], "markdownDescription": "The model_serving_endpoint resource allows you to define [model serving endpoints](https://docs.databricks.com/api/workspace/servingendpoints/create). See [link](https://docs.databricks.com/machine-learning/model-serving/manage-serving-endpoints.html)." }, - "resources.ModelServingEndpointPermission": { - "type": "object", - "properties": { - "group_name": { - "$ref": "#/$defs/string", - "x-since-version": "v0.247.0" - }, - "level": { - "$ref": "#/$defs/github.com/databricks/cli/bundle/config/resources.ModelServingEndpointPermissionLevel", - "x-since-version": "v0.247.0" - }, - "service_principal_name": { - "$ref": "#/$defs/string", - "x-since-version": "v0.247.0" - }, - "user_name": { - "$ref": "#/$defs/string", - "x-since-version": "v0.247.0" - } - }, - "additionalProperties": false, - "required": [ - "level" - ] - }, - "resources.ModelServingEndpointPermissionLevel": { - "type": "string", - "enum": [ - "CAN_MANAGE", - "CAN_QUERY", - "CAN_VIEW" - ] - }, "resources.Permission": { "type": "object", "properties": { @@ -1400,7 +1067,7 @@ "x-since-version": "v0.229.0" }, "permissions": { - "$ref": "#/$defs/slice/github.com/databricks/cli/bundle/config/resources.PipelinePermission", + "$ref": "#/$defs/slice/github.com/databricks/cli/bundle/config/resources.Permission", "x-since-version": "v0.229.0" }, "photon": { @@ -1469,40 +1136,6 @@ "additionalProperties": false, "markdownDescription": "The pipeline resource allows you to create Delta Live Tables [pipelines](https://docs.databricks.com/api/workspace/pipelines/create). For information about pipelines, see [link](https://docs.databricks.com/dlt/index.html). For a tutorial that uses the Databricks Asset Bundles template to create a pipeline, see [link](https://docs.databricks.com/dev-tools/bundles/pipelines-tutorial.html)." }, - "resources.PipelinePermission": { - "type": "object", - "properties": { - "group_name": { - "$ref": "#/$defs/string", - "x-since-version": "v0.247.0" - }, - "level": { - "$ref": "#/$defs/github.com/databricks/cli/bundle/config/resources.PipelinePermissionLevel", - "x-since-version": "v0.247.0" - }, - "service_principal_name": { - "$ref": "#/$defs/string", - "x-since-version": "v0.247.0" - }, - "user_name": { - "$ref": "#/$defs/string", - "x-since-version": "v0.247.0" - } - }, - "additionalProperties": false, - "required": [ - "level" - ] - }, - "resources.PipelinePermissionLevel": { - "type": "string", - "enum": [ - "CAN_MANAGE", - "IS_OWNER", - "CAN_RUN", - "CAN_VIEW" - ] - }, "resources.PostgresBranch": { "type": "object", "properties": { @@ -1639,7 +1272,7 @@ "x-since-version": "v0.287.0" }, "permissions": { - "$ref": "#/$defs/slice/github.com/databricks/cli/bundle/config/resources.DatabaseProjectPermission", + "$ref": "#/$defs/slice/github.com/databricks/cli/bundle/config/resources.Permission", "x-since-version": "v0.292.0" }, "pg_version": { @@ -2002,7 +1635,7 @@ "x-since-version": "v0.260.0" }, "permissions": { - "$ref": "#/$defs/slice/github.com/databricks/cli/bundle/config/resources.SqlWarehousePermission", + "$ref": "#/$defs/slice/github.com/databricks/cli/bundle/config/resources.Permission", "x-since-version": "v0.260.0" }, "spot_instance_policy": { @@ -2021,40 +1654,6 @@ }, "additionalProperties": false }, - "resources.SqlWarehousePermission": { - "type": "object", - "properties": { - "group_name": { - "$ref": "#/$defs/string", - "x-since-version": "v0.260.0" - }, - "level": { - "$ref": "#/$defs/github.com/databricks/cli/bundle/config/resources.SqlWarehousePermissionLevel", - "x-since-version": "v0.260.0" - }, - "service_principal_name": { - "$ref": "#/$defs/string", - "x-since-version": "v0.260.0" - }, - "user_name": { - "$ref": "#/$defs/string", - "x-since-version": "v0.260.0" - } - }, - "additionalProperties": false, - "required": [ - "level" - ] - }, - "resources.SqlWarehousePermissionLevel": { - "type": "string", - "enum": [ - "CAN_MANAGE", - "CAN_USE", - "CAN_MONITOR", - "CAN_VIEW" - ] - }, "resources.SyncedDatabaseTable": { "type": "object", "description": "Next field marker: 18", @@ -9043,95 +8642,23 @@ "cli": { "bundle": { "config": { - "resources.AlertPermission": { - "type": "array", - "items": { - "$ref": "#/$defs/github.com/databricks/cli/bundle/config/resources.AlertPermission" - } - }, "resources.AppEnvVar": { "type": "array", "items": { "$ref": "#/$defs/github.com/databricks/cli/bundle/config/resources.AppEnvVar" } }, - "resources.AppPermission": { - "type": "array", - "items": { - "$ref": "#/$defs/github.com/databricks/cli/bundle/config/resources.AppPermission" - } - }, - "resources.ClusterPermission": { - "type": "array", - "items": { - "$ref": "#/$defs/github.com/databricks/cli/bundle/config/resources.ClusterPermission" - } - }, - "resources.DashboardPermission": { - "type": "array", - "items": { - "$ref": "#/$defs/github.com/databricks/cli/bundle/config/resources.DashboardPermission" - } - }, - "resources.DatabaseInstancePermission": { - "type": "array", - "items": { - "$ref": "#/$defs/github.com/databricks/cli/bundle/config/resources.DatabaseInstancePermission" - } - }, - "resources.DatabaseProjectPermission": { - "type": "array", - "items": { - "$ref": "#/$defs/github.com/databricks/cli/bundle/config/resources.DatabaseProjectPermission" - } - }, - "resources.JobPermission": { - "type": "array", - "items": { - "$ref": "#/$defs/github.com/databricks/cli/bundle/config/resources.JobPermission" - } - }, - "resources.MlflowExperimentPermission": { - "type": "array", - "items": { - "$ref": "#/$defs/github.com/databricks/cli/bundle/config/resources.MlflowExperimentPermission" - } - }, - "resources.MlflowModelPermission": { - "type": "array", - "items": { - "$ref": "#/$defs/github.com/databricks/cli/bundle/config/resources.MlflowModelPermission" - } - }, - "resources.ModelServingEndpointPermission": { - "type": "array", - "items": { - "$ref": "#/$defs/github.com/databricks/cli/bundle/config/resources.ModelServingEndpointPermission" - } - }, "resources.Permission": { "type": "array", "items": { "$ref": "#/$defs/github.com/databricks/cli/bundle/config/resources.Permission" } }, - "resources.PipelinePermission": { - "type": "array", - "items": { - "$ref": "#/$defs/github.com/databricks/cli/bundle/config/resources.PipelinePermission" - } - }, "resources.SecretScopePermission": { "type": "array", "items": { "$ref": "#/$defs/github.com/databricks/cli/bundle/config/resources.SecretScopePermission" } - }, - "resources.SqlWarehousePermission": { - "type": "array", - "items": { - "$ref": "#/$defs/github.com/databricks/cli/bundle/config/resources.SqlWarehousePermission" - } } }, "config.ArtifactFile": { From d980f7a73a5c235d891287b01bc49f9a32fa5ea9 Mon Sep 17 00:00:00 2001 From: Denis Bilenko Date: Mon, 9 Mar 2026 15:58:58 +0100 Subject: [PATCH 06/28] rm PLAN --- PLAN_PERMISSIONS.md | 215 -------------------------------------------- 1 file changed, 215 deletions(-) delete mode 100644 PLAN_PERMISSIONS.md diff --git a/PLAN_PERMISSIONS.md b/PLAN_PERMISSIONS.md deleted file mode 100644 index 3f3287ca5c..0000000000 --- a/PLAN_PERMISSIONS.md +++ /dev/null @@ -1,215 +0,0 @@ -# Plan: Simplify Permissions (analogous to simpler-grant) - -## What Was Done in simpler-grant - -### Problem -Each resource type had its own custom Grant type with a resource-specific privilege enum: -- `Grant` (generic, `Privileges []string`) for `registered_model` -- `SchemaGrant` with `SchemaGrantPrivilege` enum for `schema` -- `CatalogGrant` with `CatalogGrantPrivilege` enum for `catalog` -- `ExternalLocationGrant` with `ExternalLocationGrantPrivilege` enum for `external_location` -- `VolumeGrant` with `VolumeGrantPrivilege` enum for `volume` - -The `PrepareGrantsInputConfig` used reflection to convert any of these custom types into `[]catalog.PrivilegeAssignment` for the direct engine. - -The SDK already had `catalog.PrivilegeAssignment` (principal + `[]catalog.Privilege`) which covers all UC privileges. - -### What Was Changed -1. **Go**: Deleted `grant.go` and removed 5 custom Grant types + their privilege enums from `catalog.go`, `schema.go`, `volume.go`, `external_location.go`, `registered_model.go`. Replaced all `Grants` fields with `[]catalog.PrivilegeAssignment`. -2. **Go**: Replaced the reflection-based `PrepareGrantsInputConfig` with a simple type assertion `inputConfig.(*[]catalog.PrivilegeAssignment)`. -3. **Go**: Deleted `TestSchemaGrantPrivilegesExhaustive` test (which kept the old enum in sync with the SDK). -4. **Schema**: Updated `annotations.yml` (removed custom type annotations), `annotations_openapi_overrides.yml` (removed overrides for custom types), regenerated `jsonschema.json`. -5. **Python**: Removed per-resource grant/privilege model files (`schema_grant.py`, `schema_grant_privilege.py`, `catalog_grant.py`, etc.), added shared `privilege.py` and `privilege_assignment.py` under each resource package. -6. **Test data bug**: `testdata/pass/ml.yml` used `INSERT` as a privilege, which was accepted with the old `[]string` type but invalid with `catalog.Privilege` enum. Fixed by replacing with `MODIFY`. - -### Net Result -- Deleted ~950 lines, added ~111 lines -- No behavioral change — the SDK's `catalog.Privilege` enum is a superset of the old per-resource enums (which were manually maintained subsets) - -### Issues Encountered -1. **Schema testdata used invalid privilege value** (`INSERT` instead of a valid UC privilege like `MODIFY`). The old `Grant` type used `[]string` with no enum validation, hiding this. Only surfaced after switching to the typed SDK enum. Caught by CI (`validate-generated-is-up-to-date`). - - **Lesson**: After switching from `[]string`/untyped to a typed enum, check all testdata/acceptance files for values that are now invalid. - ---- - -## Plan: Simplify Permissions - -### Current State (analogous to the old grants situation) - -`bundle/config/resources/permission.go` defines: -- 12 resource-specific permission types: `AlertPermission`, `AppPermission`, `ClusterPermission`, `DashboardPermission`, `DatabaseInstancePermission`, `DatabaseProjectPermission`, `JobPermission`, `MlflowExperimentPermission`, `MlflowModelPermission`, `ModelServingEndpointPermission`, `PipelinePermission`, `SqlWarehousePermission` -- 12 corresponding level types: `AlertPermissionLevel`, `AppPermissionLevel`, etc. — all declared as `string` with no enum values (only `AlertPermissionLevel` has a `Values()` method) -- `IPermission` interface with 4 methods: `GetLevel()`, `GetUserName()`, `GetServicePrincipalName()`, `GetGroupName()`, `GetAPIRequestObjectType()` -- ~180 lines of boilerplate: `GetLevel`/`GetUserName`/`GetServicePrincipalName`/`GetGroupName` implementations repeated 12 times -- `GetAPIRequestObjectType()` per type (maps to API path prefix like `/jobs/`, `/pipelines/`, etc.) - -`bundle/direct/dresources/permissions.go` uses reflection to iterate over the slice, casts each element to `IPermission`, extracts level/principal fields, and converts to `[]iam.AccessControlRequest`. - -The SDK's `iam.AccessControlRequest` already has: `PermissionLevel iam.PermissionLevel`, `UserName`, `GroupName`, `ServicePrincipalName`. - -### Goal - -Replace all 12 resource-specific permission types with `iam.AccessControlRequest` directly from the SDK, analogous to how `catalog.PrivilegeAssignment` replaced the custom grant types. - -The object type prefix (e.g. `/jobs/`) needs to come from somewhere — currently embedded in each type via `GetAPIRequestObjectType()`. This must be handled differently (see Step 2 below). - -### Steps - -#### Step 1: Understand how `GetAPIRequestObjectType` is used - -`PreparePermissionsInputConfig` uses it to build the `objectIdRef`: -```go -prefix := zeroValueInterface.GetAPIRequestObjectType() -objectIdRef := prefix + "${" + baseNode + ".id}" -``` - -The mapping is: -| Type | Prefix | -|------|--------| -| AlertPermission | `/alertsv2/` | -| AppPermission | `/apps/` | -| ClusterPermission | `/clusters/` | -| DashboardPermission | `/dashboards/` | -| DatabaseInstancePermission | `/database-instances/` | -| DatabaseProjectPermission | `/database-projects/` | -| JobPermission | `/jobs/` | -| MlflowExperimentPermission | `/experiments/` | -| MlflowModelPermission | `/registered-models/` | -| ModelServingEndpointPermission | `/serving-endpoints/` | -| PipelinePermission | `/pipelines/` | -| SqlWarehousePermission | `/sql/warehouses/` | - -This mapping is from resource type name (in the bundle node path) to object type prefix. It can be extracted from the node path (`baseNode`) directly, e.g. `resources.jobs.myjob.permissions` → `/jobs/`. - -#### Step 2: Replace IPermission dispatch with a static map - -Add a map in `permissions.go` (analogous to `grantResourceToSecurableType` in `grants.go`): -```go -var permissionResourceToObjectType = map[string]string{ - "alerts": "/alertsv2/", - "apps": "/apps/", - "clusters": "/clusters/", - "dashboards": "/dashboards/", - "database_instances": "/database-instances/", - "postgres_projects": "/database-projects/", - "jobs": "/jobs/", - "experiments": "/experiments/", - "models": "/registered-models/", - "model_serving_endpoints": "/serving-endpoints/", - "pipelines": "/pipelines/", - "sql_warehouses": "/sql/warehouses/", -} -``` - -Then extract resource type from `baseNode` (e.g. `resources.jobs.myjob` → `jobs`) and look it up. - -Note: The existing special cases in `PreparePermissionsInputConfig` for `model_serving_endpoints` (uses `.endpoint_id` not `.id`) and `postgres_projects` (uses `.project_id`) are already handled via string prefix checks and can remain. - -#### Step 3: Change resource structs (Go) - -In each resource file, change: -```go -Permissions []JobPermission `json:"permissions,omitempty"` -``` -to: -```go -Permissions []iam.AccessControlRequest `json:"permissions,omitempty"` -``` - -Resources affected: `alerts.go`, `apps.go`, `clusters.go`, `dashboard.go`, `database_instance.go`, `job.go`, `mlflow_experiment.go`, `mlflow_model.go`, `model_serving_endpoint.go`, `pipeline.go`, `postgres_project.go`, `sql_warehouses.go`. - -Note: `secret_scope.go` uses `SecretScopePermission` (different mechanism — secret ACLs, not the Permissions API). Leave it unchanged. - -#### Step 4: Simplify `PreparePermissionsInputConfig` (direct engine) - -Replace the reflection-based conversion with a simple type assertion: -```go -permissionsPtr, ok := inputConfig.(*[]iam.AccessControlRequest) -if !ok { - return nil, fmt.Errorf("expected *[]iam.AccessControlRequest, got %T", inputConfig) -} -permissions := *permissionsPtr -``` - -Remove the `reflect` import, remove the `IPermission` cast loop. - -#### Step 5: Delete `permission.go` content - -Remove from `bundle/config/resources/permission.go`: -- All 12 `*PermissionLevel` type declarations -- All 12 `*Permission` struct types -- All `GetAPIRequestObjectType()` methods -- All `GetLevel()`/`GetUserName()`/`GetServicePrincipalName()`/`GetGroupName()` boilerplate -- `IPermission` interface -- The unused `Permission` struct (the generic one at top — check if still used anywhere) - -Keep in `permission.go`: -- `PermissionOrder`, `GetLevelScore`, `CompareLevels`, `GetMaxLevel` (used by workspace permissions logic) - -#### Step 6: Update schema annotations - -In `bundle/internal/schema/annotations.yml`, remove the per-type permission annotations (e.g. `github.com/databricks/cli/bundle/config/resources.AlertPermission:`, etc.) since those types are gone. The `iam.AccessControlRequest` type from the SDK will be used instead, annotated via `annotations_openapi.yml`. - -Check `annotations_openapi_overrides.yml` for any overrides on the old permission types — remove those too. - -#### Step 7: Regenerate schema and validate - -```bash -make schema -``` - -Verify `bundle/schema/jsonschema.json` and `bundle/schema/jsonschema_for_docs.json` look correct — the `permissions` field on each resource should reference `iam.AccessControlRequest` schema from SDK, which will have `permission_level` (enum of `iam.PermissionLevel` values), `user_name`, `group_name`, `service_principal_name`. - -#### Step 8: Update Python models - -Remove per-resource permission model files: -- `python/databricks/bundles/jobs/_models/job_permission.py` -- `python/databricks/bundles/jobs/_models/job_permission_level.py` -- `python/databricks/bundles/pipelines/_models/pipeline_permission.py` -- `python/databricks/bundles/pipelines/_models/pipeline_permission_level.py` -- Any other `*_permission.py` / `*_permission_level.py` files - -Add shared `access_control_request.py` and `permission_level.py` under affected packages (jobs, pipelines, etc.), generated from SDK types. - -Update `__init__.py` files accordingly. - -Run: `cd python && make codegen && make test && make lint && make docs` - -#### Step 9: Check testdata/acceptance for now-invalid permission level values - -**Lesson learned from simpler-grant**: The old `*PermissionLevel` types are declared as `string` with no actual enum validation in the schema (only `AlertPermissionLevel` has `Values()`). After switching to `iam.PermissionLevel`, the schema will enforce the enum. Scan all testdata and acceptance files: - -```bash -grep -rn "level:" bundle/internal/schema/testdata/ acceptance/ --include="*.yml" -``` - -Ensure all `level:` values are valid `iam.PermissionLevel` values: -`CAN_ATTACH_TO`, `CAN_BIND`, `CAN_CREATE`, `CAN_EDIT`, `CAN_EDIT_METADATA`, `CAN_MANAGE`, `CAN_MANAGE_PRODUCTION_VERSIONS`, `CAN_MANAGE_RUN`, `CAN_MANAGE_STAGING_VERSIONS`, `CAN_MONITOR`, `CAN_MONITOR_ONLY`, `CAN_QUERY`, `CAN_READ`, `CAN_RESTART`, `CAN_RUN`, `CAN_USE`, `CAN_VIEW`, `CAN_VIEW_METADATA`, `IS_OWNER`. - -Also check Python test files and acceptance scripts. - -#### Step 10: Run tests - -```bash -make test -make schema # if schema-related code changed -cd python && make codegen && make test && make lint && make docs -``` - ---- - -## Key Differences vs simpler-grant - -| Aspect | simpler-grant | simpler-permissions | -|--------|--------------|---------------------| -| Custom types deleted | 5 (one per resource) | ~12 (one per resource type) | -| API object type dispatch | N/A (securable type from node name) | Must map resource type → object type prefix | -| SDK replacement type | `catalog.PrivilegeAssignment` | `iam.AccessControlRequest` | -| Field name change | `privileges []string` → `[]catalog.Privilege` (enum enforced) | `level string` → `iam.PermissionLevel` (enum enforced) | -| Reflection removed | Yes (grants.go) | Yes (permissions.go) | -| Exceptions | None | `secret_scope` (ACL, not Permissions API); `model_serving_endpoints` (uses `.endpoint_id`); `postgres_projects` (uses `.project_id`) | -| Schema enum impact | `INSERT` was invalid (caught by CI) | Any invalid `CAN_*` values will be caught — scan testdata first | - -## Risk: `iam.PermissionLevel` is a shared enum - -`iam.PermissionLevel` covers ALL resource types' permission levels combined. Some levels are only valid for specific resources (e.g. `CAN_ATTACH_TO` is only for clusters). This is acceptable — same as with `catalog.Privilege` being a superset. The schema will allow any valid `iam.PermissionLevel` on any resource, which is slightly more permissive than the current per-resource enums (but those had no per-resource values anyway — all defined as bare `string`). From 9314dddf1b79a60fadbd5b8d526cfd20dc7c3183 Mon Sep 17 00:00:00 2001 From: Denis Bilenko Date: Mon, 9 Mar 2026 16:01:38 +0100 Subject: [PATCH 07/28] Add backward-compat aliases for *PermissionLevel types in Python JobPermissionLevel = str and PipelinePermissionLevel = str preserve the exported names while reflecting that levels are now plain strings. Co-Authored-By: Claude Sonnet 4.6 --- python/codegen/codegen/aliases_patch.py | 4 ++++ python/databricks/bundles/jobs/__init__.py | 4 ++++ python/databricks/bundles/pipelines/__init__.py | 4 ++++ 3 files changed, 12 insertions(+) diff --git a/python/codegen/codegen/aliases_patch.py b/python/codegen/codegen/aliases_patch.py index eed4ceb4c4..2af4710080 100644 --- a/python/codegen/codegen/aliases_patch.py +++ b/python/codegen/codegen/aliases_patch.py @@ -26,10 +26,14 @@ "JobPermission": "Permission", "JobPermissionDict": "PermissionDict", "JobPermissionParam": "PermissionParam", + "JobPermissionLevel": "str", + "JobPermissionLevelParam": "str", }, "pipelines": { "PipelinePermission": "Permission", "PipelinePermissionDict": "PermissionDict", "PipelinePermissionParam": "PermissionParam", + "PipelinePermissionLevel": "str", + "PipelinePermissionLevelParam": "str", }, } diff --git a/python/databricks/bundles/jobs/__init__.py b/python/databricks/bundles/jobs/__init__.py index 5831b253ed..a114ca84a2 100644 --- a/python/databricks/bundles/jobs/__init__.py +++ b/python/databricks/bundles/jobs/__init__.py @@ -120,6 +120,8 @@ "JobParameterDefinitionParam", "JobPermission", "JobPermissionDict", + "JobPermissionLevel", + "JobPermissionLevelParam", "JobPermissionParam", "JobRunAs", "JobRunAsDict", @@ -746,4 +748,6 @@ def _resolve_recursive_imports(): JobPermission = Permission JobPermissionDict = PermissionDict +JobPermissionLevel = str +JobPermissionLevelParam = str JobPermissionParam = PermissionParam diff --git a/python/databricks/bundles/pipelines/__init__.py b/python/databricks/bundles/pipelines/__init__.py index 643f59bd24..4050db989c 100644 --- a/python/databricks/bundles/pipelines/__init__.py +++ b/python/databricks/bundles/pipelines/__init__.py @@ -109,6 +109,8 @@ "PipelineParam", "PipelinePermission", "PipelinePermissionDict", + "PipelinePermissionLevel", + "PipelinePermissionLevelParam", "PipelinePermissionParam", "PipelinesEnvironment", "PipelinesEnvironmentDict", @@ -411,4 +413,6 @@ PipelinePermission = Permission PipelinePermissionDict = PermissionDict +PipelinePermissionLevel = str +PipelinePermissionLevelParam = str PipelinePermissionParam = PermissionParam From 61b08e8872c8e3c70fc2f8c6d9c6687e6f3d75be Mon Sep 17 00:00:00 2001 From: Denis Bilenko Date: Mon, 9 Mar 2026 16:07:09 +0100 Subject: [PATCH 08/28] Add NEXT_CHANGELOG entry for #4686 Co-Authored-By: Claude Sonnet 4.6 --- NEXT_CHANGELOG.md | 1 + 1 file changed, 1 insertion(+) diff --git a/NEXT_CHANGELOG.md b/NEXT_CHANGELOG.md index ed11712f51..6bf2f63608 100644 --- a/NEXT_CHANGELOG.md +++ b/NEXT_CHANGELOG.md @@ -6,6 +6,7 @@ ### Bundles * Modify grants to use SDK types ([#4666](https://github.com/databricks/cli/pull/4666)) +* Modify permissions to use a shared Permission type ([#4686](https://github.com/databricks/cli/pull/4686)) ### Dependency updates - Bump databricks-sdk-go from v0.112.0 to v0.117.0. From 0b214a44e197599aecbf41804a0f8a3b4247932b Mon Sep 17 00:00:00 2001 From: Denis Bilenko Date: Mon, 9 Mar 2026 21:39:57 +0100 Subject: [PATCH 09/28] Use resource-specific SDK permission level types for bundle resources Replace the generic `[]resources.Permission[iam.PermissionLevel]` with per-resource typed `resources.Permissions[L]` using the SDK's own enum types (e.g. `jobs.JobPermissionLevel`, `pipelines.PipelinePermissionLevel`, etc.). This provides type safety and IDE autocomplete for valid permission levels per resource. A `PermissionsSlice` interface enables type-agnostic conversion to `iam.AccessControlRequest` without reflection. Co-Authored-By: Claude Sonnet 4.6 --- .claude/settings.local.json | 10 + .../apply_bundle_permissions.go | 27 +- .../apply_bundle_permissions_test.go | 83 +-- .../config/mutator/resourcemutator/run_as.go | 6 +- .../validate_target_mode_test.go | 47 +- bundle/config/resources/alerts.go | 3 +- bundle/config/resources/apps.go | 2 +- bundle/config/resources/clusters.go | 2 +- bundle/config/resources/dashboard.go | 3 +- bundle/config/resources/database_instance.go | 3 +- bundle/config/resources/job.go | 2 +- bundle/config/resources/mlflow_experiment.go | 2 +- bundle/config/resources/mlflow_model.go | 2 +- .../resources/model_serving_endpoint.go | 2 +- bundle/config/resources/permission.go | 38 +- bundle/config/resources/pipeline.go | 2 +- bundle/config/resources/postgres_project.go | 3 +- bundle/config/resources/sql_warehouses.go | 2 +- bundle/config/resources_types_test.go | 3 +- bundle/config/root.go | 3 +- bundle/config/target.go | 3 +- .../validate/folder_permissions_test.go | 9 +- bundle/deploy/terraform/convert_test.go | 10 +- .../terraform/tfdyn/convert_alert_test.go | 3 +- .../terraform/tfdyn/convert_app_test.go | 20 +- .../terraform/tfdyn/convert_cluster_test.go | 2 +- .../terraform/tfdyn/convert_dashboard_test.go | 3 +- .../tfdyn/convert_database_instance_test.go | 3 +- .../tfdyn/convert_experiment_test.go | 2 +- .../terraform/tfdyn/convert_job_test.go | 2 +- .../convert_model_serving_endpoint_test.go | 2 +- .../terraform/tfdyn/convert_model_test.go | 2 +- .../tfdyn/convert_permissions_test.go | 5 +- .../terraform/tfdyn/convert_pipeline_test.go | 2 +- .../tfdyn/convert_postgres_project_test.go | 3 +- bundle/direct/dresources/permissions.go | 15 +- bundle/internal/schema/annotations.yml | 148 ++++- .../permission_diagnostics_test.go | 6 +- bundle/permissions/permission_report_test.go | 9 +- bundle/permissions/terraform_errors_test.go | 9 +- bundle/permissions/validate_test.go | 5 +- .../permissions/workspace_path_permissions.go | 25 +- .../workspace_path_permissions_test.go | 23 +- bundle/permissions/workspace_root.go | 2 +- bundle/permissions/workspace_root_test.go | 5 +- bundle/schema/jsonschema.json | 529 ++++++++++++++++-- bundle/tests/bundle_permissions_test.go | 51 +- bundle/tests/model_serving_endpoint_test.go | 3 +- 48 files changed, 877 insertions(+), 269 deletions(-) create mode 100644 .claude/settings.local.json diff --git a/.claude/settings.local.json b/.claude/settings.local.json new file mode 100644 index 0000000000..34ef364164 --- /dev/null +++ b/.claude/settings.local.json @@ -0,0 +1,10 @@ +{ + "permissions": { + "allow": [ + "Bash(perl:*)", + "Bash(do:*)", + "Bash(sed:*)", + "Bash(python3:*)" + ] + } +} diff --git a/bundle/config/mutator/resourcemutator/apply_bundle_permissions.go b/bundle/config/mutator/resourcemutator/apply_bundle_permissions.go index e99281b27a..f859badd8d 100644 --- a/bundle/config/mutator/resourcemutator/apply_bundle_permissions.go +++ b/bundle/config/mutator/resourcemutator/apply_bundle_permissions.go @@ -13,6 +13,7 @@ import ( "github.com/databricks/cli/libs/diag" "github.com/databricks/cli/libs/dyn" "github.com/databricks/cli/libs/dyn/convert" + "github.com/databricks/databricks-sdk-go/service/iam" ) var ( @@ -104,7 +105,7 @@ func (m *bundlePermissions) Apply(ctx context.Context, b *bundle.Bundle) diag.Di err = b.Config.Mutate(func(v dyn.Value) (dyn.Value, error) { for key, pattern := range patterns { v, err = dyn.MapByPattern(v, pattern, func(p dyn.Path, v dyn.Value) (dyn.Value, error) { - var permissions []resources.Permission + var permissions []resources.Permission[iam.PermissionLevel] pv, err := dyn.Get(v, "permissions") // If the permissions field is not found, we set to an empty array if err != nil { @@ -151,7 +152,7 @@ func (m *bundlePermissions) Apply(ctx context.Context, b *bundle.Bundle) diag.Di func validatePermissions(b *bundle.Bundle) error { for _, p := range b.Config.Permissions { - if !slices.Contains(allowedLevels, p.Level) { + if !slices.Contains(allowedLevels, string(p.Level)) { return fmt.Errorf("invalid permission level: %s, allowed values: [%s]", p.Level, strings.Join(allowedLevels, ", ")) } } @@ -165,14 +166,14 @@ func (m *bundlePermissions) Name() string { func convertPermissions( ctx context.Context, - bundlePermissions []resources.Permission, - resourcePermissions []resources.Permission, + bundlePermissions []resources.Permission[iam.PermissionLevel], + resourcePermissions []resources.Permission[iam.PermissionLevel], resourceName string, lm map[string]string, -) []resources.Permission { - var permissions []resources.Permission +) []resources.Permission[iam.PermissionLevel] { + var permissions []resources.Permission[iam.PermissionLevel] for _, p := range bundlePermissions { - level, ok := lm[p.Level] + level, ok := lm[string(p.Level)] // If there is no bundle permission level defined in the map, it means // it's not applicable for the resource, therefore skipping if !ok { @@ -183,8 +184,8 @@ func convertPermissions( continue } - permissions = append(permissions, resources.Permission{ - Level: level, + permissions = append(permissions, resources.Permission[iam.PermissionLevel]{ + Level: iam.PermissionLevel(level), UserName: p.UserName, GroupName: p.GroupName, ServicePrincipalName: p.ServicePrincipalName, @@ -195,8 +196,8 @@ func convertPermissions( } func isPermissionOverlap( - permission resources.Permission, - resourcePermissions []resources.Permission, + permission resources.Permission[iam.PermissionLevel], + resourcePermissions []resources.Permission[iam.PermissionLevel], resourceName string, ) (bool, diag.Diagnostics) { var diagnostics diag.Diagnostics @@ -225,8 +226,8 @@ func isPermissionOverlap( func notifyForPermissionOverlap( ctx context.Context, - permission resources.Permission, - resourcePermissions []resources.Permission, + permission resources.Permission[iam.PermissionLevel], + resourcePermissions []resources.Permission[iam.PermissionLevel], resourceName string, ) bool { isOverlap, _ := isPermissionOverlap(permission, resourcePermissions, resourceName) diff --git a/bundle/config/mutator/resourcemutator/apply_bundle_permissions_test.go b/bundle/config/mutator/resourcemutator/apply_bundle_permissions_test.go index 28ee4bdaae..be57b60b31 100644 --- a/bundle/config/mutator/resourcemutator/apply_bundle_permissions_test.go +++ b/bundle/config/mutator/resourcemutator/apply_bundle_permissions_test.go @@ -10,7 +10,12 @@ import ( "github.com/databricks/cli/bundle" "github.com/databricks/cli/bundle/config" "github.com/databricks/cli/bundle/config/resources" + appssdk "github.com/databricks/databricks-sdk-go/service/apps" + "github.com/databricks/databricks-sdk-go/service/iam" "github.com/databricks/databricks-sdk-go/service/jobs" + "github.com/databricks/databricks-sdk-go/service/ml" + "github.com/databricks/databricks-sdk-go/service/pipelines" + "github.com/databricks/databricks-sdk-go/service/serving" "github.com/stretchr/testify/assert" "github.com/stretchr/testify/require" ) @@ -36,7 +41,7 @@ func TestApplyBundlePermissions(t *testing.T) { Workspace: config.Workspace{ RootPath: "/Users/foo@bar.com", }, - Permissions: []resources.Permission{ + Permissions: []resources.Permission[iam.PermissionLevel]{ {Level: permissions.CAN_MANAGE, UserName: "TestUser"}, {Level: permissions.CAN_VIEW, GroupName: "TestGroup"}, {Level: permissions.CAN_RUN, ServicePrincipalName: "TestServicePrincipal"}, @@ -86,58 +91,58 @@ func TestApplyBundlePermissions(t *testing.T) { require.NoError(t, diags.Error()) require.Len(t, b.Config.Resources.Jobs["job_1"].Permissions, 3) - require.Contains(t, b.Config.Resources.Jobs["job_1"].Permissions, resources.Permission{Level: "CAN_MANAGE", UserName: "TestUser"}) - require.Contains(t, b.Config.Resources.Jobs["job_1"].Permissions, resources.Permission{Level: "CAN_VIEW", GroupName: "TestGroup"}) - require.Contains(t, b.Config.Resources.Jobs["job_1"].Permissions, resources.Permission{Level: "CAN_MANAGE_RUN", ServicePrincipalName: "TestServicePrincipal"}) + require.Contains(t, b.Config.Resources.Jobs["job_1"].Permissions, resources.Permission[jobs.JobPermissionLevel]{Level: "CAN_MANAGE", UserName: "TestUser"}) + require.Contains(t, b.Config.Resources.Jobs["job_1"].Permissions, resources.Permission[jobs.JobPermissionLevel]{Level: "CAN_VIEW", GroupName: "TestGroup"}) + require.Contains(t, b.Config.Resources.Jobs["job_1"].Permissions, resources.Permission[jobs.JobPermissionLevel]{Level: "CAN_MANAGE_RUN", ServicePrincipalName: "TestServicePrincipal"}) require.Len(t, b.Config.Resources.Jobs["job_2"].Permissions, 3) - require.Contains(t, b.Config.Resources.Jobs["job_2"].Permissions, resources.Permission{Level: "CAN_MANAGE", UserName: "TestUser"}) - require.Contains(t, b.Config.Resources.Jobs["job_2"].Permissions, resources.Permission{Level: "CAN_VIEW", GroupName: "TestGroup"}) - require.Contains(t, b.Config.Resources.Jobs["job_2"].Permissions, resources.Permission{Level: "CAN_MANAGE_RUN", ServicePrincipalName: "TestServicePrincipal"}) + require.Contains(t, b.Config.Resources.Jobs["job_2"].Permissions, resources.Permission[jobs.JobPermissionLevel]{Level: "CAN_MANAGE", UserName: "TestUser"}) + require.Contains(t, b.Config.Resources.Jobs["job_2"].Permissions, resources.Permission[jobs.JobPermissionLevel]{Level: "CAN_VIEW", GroupName: "TestGroup"}) + require.Contains(t, b.Config.Resources.Jobs["job_2"].Permissions, resources.Permission[jobs.JobPermissionLevel]{Level: "CAN_MANAGE_RUN", ServicePrincipalName: "TestServicePrincipal"}) require.Len(t, b.Config.Resources.Pipelines["pipeline_1"].Permissions, 3) - require.Contains(t, b.Config.Resources.Pipelines["pipeline_1"].Permissions, resources.Permission{Level: "CAN_MANAGE", UserName: "TestUser"}) - require.Contains(t, b.Config.Resources.Pipelines["pipeline_1"].Permissions, resources.Permission{Level: "CAN_VIEW", GroupName: "TestGroup"}) - require.Contains(t, b.Config.Resources.Pipelines["pipeline_1"].Permissions, resources.Permission{Level: "CAN_RUN", ServicePrincipalName: "TestServicePrincipal"}) + require.Contains(t, b.Config.Resources.Pipelines["pipeline_1"].Permissions, resources.Permission[pipelines.PipelinePermissionLevel]{Level: "CAN_MANAGE", UserName: "TestUser"}) + require.Contains(t, b.Config.Resources.Pipelines["pipeline_1"].Permissions, resources.Permission[pipelines.PipelinePermissionLevel]{Level: "CAN_VIEW", GroupName: "TestGroup"}) + require.Contains(t, b.Config.Resources.Pipelines["pipeline_1"].Permissions, resources.Permission[pipelines.PipelinePermissionLevel]{Level: "CAN_RUN", ServicePrincipalName: "TestServicePrincipal"}) require.Len(t, b.Config.Resources.Pipelines["pipeline_2"].Permissions, 3) - require.Contains(t, b.Config.Resources.Pipelines["pipeline_2"].Permissions, resources.Permission{Level: "CAN_MANAGE", UserName: "TestUser"}) - require.Contains(t, b.Config.Resources.Pipelines["pipeline_2"].Permissions, resources.Permission{Level: "CAN_VIEW", GroupName: "TestGroup"}) - require.Contains(t, b.Config.Resources.Pipelines["pipeline_2"].Permissions, resources.Permission{Level: "CAN_RUN", ServicePrincipalName: "TestServicePrincipal"}) + require.Contains(t, b.Config.Resources.Pipelines["pipeline_2"].Permissions, resources.Permission[pipelines.PipelinePermissionLevel]{Level: "CAN_MANAGE", UserName: "TestUser"}) + require.Contains(t, b.Config.Resources.Pipelines["pipeline_2"].Permissions, resources.Permission[pipelines.PipelinePermissionLevel]{Level: "CAN_VIEW", GroupName: "TestGroup"}) + require.Contains(t, b.Config.Resources.Pipelines["pipeline_2"].Permissions, resources.Permission[pipelines.PipelinePermissionLevel]{Level: "CAN_RUN", ServicePrincipalName: "TestServicePrincipal"}) require.Len(t, b.Config.Resources.Models["model_1"].Permissions, 2) - require.Contains(t, b.Config.Resources.Models["model_1"].Permissions, resources.Permission{Level: "CAN_MANAGE", UserName: "TestUser"}) - require.Contains(t, b.Config.Resources.Models["model_1"].Permissions, resources.Permission{Level: "CAN_READ", GroupName: "TestGroup"}) + require.Contains(t, b.Config.Resources.Models["model_1"].Permissions, resources.Permission[ml.RegisteredModelPermissionLevel]{Level: "CAN_MANAGE", UserName: "TestUser"}) + require.Contains(t, b.Config.Resources.Models["model_1"].Permissions, resources.Permission[ml.RegisteredModelPermissionLevel]{Level: "CAN_READ", GroupName: "TestGroup"}) require.Len(t, b.Config.Resources.Models["model_2"].Permissions, 2) - require.Contains(t, b.Config.Resources.Models["model_2"].Permissions, resources.Permission{Level: "CAN_MANAGE", UserName: "TestUser"}) - require.Contains(t, b.Config.Resources.Models["model_2"].Permissions, resources.Permission{Level: "CAN_READ", GroupName: "TestGroup"}) + require.Contains(t, b.Config.Resources.Models["model_2"].Permissions, resources.Permission[ml.RegisteredModelPermissionLevel]{Level: "CAN_MANAGE", UserName: "TestUser"}) + require.Contains(t, b.Config.Resources.Models["model_2"].Permissions, resources.Permission[ml.RegisteredModelPermissionLevel]{Level: "CAN_READ", GroupName: "TestGroup"}) require.Len(t, b.Config.Resources.Experiments["experiment_1"].Permissions, 2) - require.Contains(t, b.Config.Resources.Experiments["experiment_1"].Permissions, resources.Permission{Level: "CAN_MANAGE", UserName: "TestUser"}) - require.Contains(t, b.Config.Resources.Experiments["experiment_1"].Permissions, resources.Permission{Level: "CAN_READ", GroupName: "TestGroup"}) + require.Contains(t, b.Config.Resources.Experiments["experiment_1"].Permissions, resources.Permission[ml.ExperimentPermissionLevel]{Level: "CAN_MANAGE", UserName: "TestUser"}) + require.Contains(t, b.Config.Resources.Experiments["experiment_1"].Permissions, resources.Permission[ml.ExperimentPermissionLevel]{Level: "CAN_READ", GroupName: "TestGroup"}) require.Len(t, b.Config.Resources.Experiments["experiment_2"].Permissions, 2) - require.Contains(t, b.Config.Resources.Experiments["experiment_2"].Permissions, resources.Permission{Level: "CAN_MANAGE", UserName: "TestUser"}) - require.Contains(t, b.Config.Resources.Experiments["experiment_2"].Permissions, resources.Permission{Level: "CAN_READ", GroupName: "TestGroup"}) + require.Contains(t, b.Config.Resources.Experiments["experiment_2"].Permissions, resources.Permission[ml.ExperimentPermissionLevel]{Level: "CAN_MANAGE", UserName: "TestUser"}) + require.Contains(t, b.Config.Resources.Experiments["experiment_2"].Permissions, resources.Permission[ml.ExperimentPermissionLevel]{Level: "CAN_READ", GroupName: "TestGroup"}) require.Len(t, b.Config.Resources.ModelServingEndpoints["endpoint_1"].Permissions, 3) - require.Contains(t, b.Config.Resources.ModelServingEndpoints["endpoint_1"].Permissions, resources.Permission{Level: "CAN_MANAGE", UserName: "TestUser"}) - require.Contains(t, b.Config.Resources.ModelServingEndpoints["endpoint_1"].Permissions, resources.Permission{Level: "CAN_VIEW", GroupName: "TestGroup"}) - require.Contains(t, b.Config.Resources.ModelServingEndpoints["endpoint_1"].Permissions, resources.Permission{Level: "CAN_QUERY", ServicePrincipalName: "TestServicePrincipal"}) + require.Contains(t, b.Config.Resources.ModelServingEndpoints["endpoint_1"].Permissions, resources.Permission[serving.ServingEndpointPermissionLevel]{Level: "CAN_MANAGE", UserName: "TestUser"}) + require.Contains(t, b.Config.Resources.ModelServingEndpoints["endpoint_1"].Permissions, resources.Permission[serving.ServingEndpointPermissionLevel]{Level: "CAN_VIEW", GroupName: "TestGroup"}) + require.Contains(t, b.Config.Resources.ModelServingEndpoints["endpoint_1"].Permissions, resources.Permission[serving.ServingEndpointPermissionLevel]{Level: "CAN_QUERY", ServicePrincipalName: "TestServicePrincipal"}) require.Len(t, b.Config.Resources.ModelServingEndpoints["endpoint_2"].Permissions, 3) - require.Contains(t, b.Config.Resources.ModelServingEndpoints["endpoint_2"].Permissions, resources.Permission{Level: "CAN_MANAGE", UserName: "TestUser"}) - require.Contains(t, b.Config.Resources.ModelServingEndpoints["endpoint_2"].Permissions, resources.Permission{Level: "CAN_VIEW", GroupName: "TestGroup"}) - require.Contains(t, b.Config.Resources.ModelServingEndpoints["endpoint_2"].Permissions, resources.Permission{Level: "CAN_QUERY", ServicePrincipalName: "TestServicePrincipal"}) + require.Contains(t, b.Config.Resources.ModelServingEndpoints["endpoint_2"].Permissions, resources.Permission[serving.ServingEndpointPermissionLevel]{Level: "CAN_MANAGE", UserName: "TestUser"}) + require.Contains(t, b.Config.Resources.ModelServingEndpoints["endpoint_2"].Permissions, resources.Permission[serving.ServingEndpointPermissionLevel]{Level: "CAN_VIEW", GroupName: "TestGroup"}) + require.Contains(t, b.Config.Resources.ModelServingEndpoints["endpoint_2"].Permissions, resources.Permission[serving.ServingEndpointPermissionLevel]{Level: "CAN_QUERY", ServicePrincipalName: "TestServicePrincipal"}) require.Len(t, b.Config.Resources.Dashboards["dashboard_1"].Permissions, 2) - require.Contains(t, b.Config.Resources.Dashboards["dashboard_1"].Permissions, resources.Permission{Level: "CAN_MANAGE", UserName: "TestUser"}) - require.Contains(t, b.Config.Resources.Dashboards["dashboard_1"].Permissions, resources.Permission{Level: "CAN_READ", GroupName: "TestGroup"}) + require.Contains(t, b.Config.Resources.Dashboards["dashboard_1"].Permissions, resources.Permission[iam.PermissionLevel]{Level: "CAN_MANAGE", UserName: "TestUser"}) + require.Contains(t, b.Config.Resources.Dashboards["dashboard_1"].Permissions, resources.Permission[iam.PermissionLevel]{Level: "CAN_READ", GroupName: "TestGroup"}) require.Len(t, b.Config.Resources.Apps["app_1"].Permissions, 2) - require.Contains(t, b.Config.Resources.Apps["app_1"].Permissions, resources.Permission{Level: "CAN_MANAGE", UserName: "TestUser"}) - require.Contains(t, b.Config.Resources.Apps["app_1"].Permissions, resources.Permission{Level: "CAN_USE", GroupName: "TestGroup"}) + require.Contains(t, b.Config.Resources.Apps["app_1"].Permissions, resources.Permission[appssdk.AppPermissionLevel]{Level: "CAN_MANAGE", UserName: "TestUser"}) + require.Contains(t, b.Config.Resources.Apps["app_1"].Permissions, resources.Permission[appssdk.AppPermissionLevel]{Level: "CAN_USE", GroupName: "TestGroup"}) } func TestWarningOnOverlapPermission(t *testing.T) { @@ -146,7 +151,7 @@ func TestWarningOnOverlapPermission(t *testing.T) { Workspace: config.Workspace{ RootPath: "/Users/foo@bar.com", }, - Permissions: []resources.Permission{ + Permissions: []resources.Permission[iam.PermissionLevel]{ {Level: permissions.CAN_MANAGE, UserName: "TestUser"}, {Level: permissions.CAN_VIEW, GroupName: "TestGroup"}, }, @@ -156,7 +161,7 @@ func TestWarningOnOverlapPermission(t *testing.T) { JobSettings: jobs.JobSettings{ Name: "job_1", }, - Permissions: []resources.Permission{ + Permissions: resources.Permissions[jobs.JobPermissionLevel]{ {Level: "CAN_VIEW", UserName: "TestUser"}, }, }, @@ -164,7 +169,7 @@ func TestWarningOnOverlapPermission(t *testing.T) { JobSettings: jobs.JobSettings{ Name: "job_2", }, - Permissions: []resources.Permission{ + Permissions: resources.Permissions[jobs.JobPermissionLevel]{ {Level: "CAN_VIEW", UserName: "TestUser2"}, }, }, @@ -176,11 +181,11 @@ func TestWarningOnOverlapPermission(t *testing.T) { diags := bundle.Apply(t.Context(), b, ApplyBundlePermissions()) require.NoError(t, diags.Error()) - require.Contains(t, b.Config.Resources.Jobs["job_1"].Permissions, resources.Permission{Level: "CAN_VIEW", UserName: "TestUser"}) - require.Contains(t, b.Config.Resources.Jobs["job_1"].Permissions, resources.Permission{Level: "CAN_VIEW", GroupName: "TestGroup"}) - require.Contains(t, b.Config.Resources.Jobs["job_2"].Permissions, resources.Permission{Level: "CAN_VIEW", UserName: "TestUser2"}) - require.Contains(t, b.Config.Resources.Jobs["job_2"].Permissions, resources.Permission{Level: "CAN_MANAGE", UserName: "TestUser"}) - require.Contains(t, b.Config.Resources.Jobs["job_2"].Permissions, resources.Permission{Level: "CAN_VIEW", GroupName: "TestGroup"}) + require.Contains(t, b.Config.Resources.Jobs["job_1"].Permissions, resources.Permission[jobs.JobPermissionLevel]{Level: "CAN_VIEW", UserName: "TestUser"}) + require.Contains(t, b.Config.Resources.Jobs["job_1"].Permissions, resources.Permission[jobs.JobPermissionLevel]{Level: "CAN_VIEW", GroupName: "TestGroup"}) + require.Contains(t, b.Config.Resources.Jobs["job_2"].Permissions, resources.Permission[jobs.JobPermissionLevel]{Level: "CAN_VIEW", UserName: "TestUser2"}) + require.Contains(t, b.Config.Resources.Jobs["job_2"].Permissions, resources.Permission[jobs.JobPermissionLevel]{Level: "CAN_MANAGE", UserName: "TestUser"}) + require.Contains(t, b.Config.Resources.Jobs["job_2"].Permissions, resources.Permission[jobs.JobPermissionLevel]{Level: "CAN_VIEW", GroupName: "TestGroup"}) } func TestAllResourcesExplicitlyDefinedForPermissionsSupport(t *testing.T) { diff --git a/bundle/config/mutator/resourcemutator/run_as.go b/bundle/config/mutator/resourcemutator/run_as.go index 96c2e52d89..07efbb4592 100644 --- a/bundle/config/mutator/resourcemutator/run_as.go +++ b/bundle/config/mutator/resourcemutator/run_as.go @@ -196,12 +196,12 @@ func setPipelineOwnersToRunAsIdentity(b *bundle.Bundle) { for i := range b.Config.Resources.Pipelines { pipeline := b.Config.Resources.Pipelines[i] - pipeline.Permissions = slices.DeleteFunc(pipeline.Permissions, func(p resources.Permission) bool { + pipeline.Permissions = slices.DeleteFunc([]resources.Permission[pipelines.PipelinePermissionLevel](pipeline.Permissions), func(p resources.Permission[pipelines.PipelinePermissionLevel]) bool { return (runAs.ServicePrincipalName != "" && p.ServicePrincipalName == runAs.ServicePrincipalName) || (runAs.UserName != "" && p.UserName == runAs.UserName) }) - pipeline.Permissions = append(pipeline.Permissions, resources.Permission{ - Level: "IS_OWNER", + pipeline.Permissions = append(pipeline.Permissions, resources.Permission[pipelines.PipelinePermissionLevel]{ + Level: pipelines.PipelinePermissionLevelIsOwner, ServicePrincipalName: runAs.ServicePrincipalName, UserName: runAs.UserName, }) diff --git a/bundle/config/mutator/resourcemutator/validate_target_mode_test.go b/bundle/config/mutator/resourcemutator/validate_target_mode_test.go index f2bf0bad0c..5a2eac818a 100644 --- a/bundle/config/mutator/resourcemutator/validate_target_mode_test.go +++ b/bundle/config/mutator/resourcemutator/validate_target_mode_test.go @@ -4,13 +4,16 @@ import ( "testing" "github.com/databricks/cli/libs/diag" + "github.com/databricks/databricks-sdk-go/service/compute" "github.com/databricks/databricks-sdk-go/service/jobs" + "github.com/databricks/databricks-sdk-go/service/ml" + "github.com/databricks/databricks-sdk-go/service/pipelines" + "github.com/databricks/databricks-sdk-go/service/serving" "github.com/stretchr/testify/assert" "github.com/databricks/cli/bundle" "github.com/databricks/cli/bundle/config" "github.com/databricks/cli/bundle/config/resources" - "github.com/databricks/databricks-sdk-go/service/pipelines" "github.com/stretchr/testify/require" ) @@ -48,41 +51,23 @@ func TestProcessTargetModeProduction(t *testing.T) { diags = validateProductionMode(b, false) require.ErrorContains(t, diags.Error(), "A common practice is to use a username or principal name in this path, i.e. use\n\n root_path: /Workspace/Users/lennart@company.com/.bundle/${bundle.name}/${bundle.target}") - jobPermissions := []resources.Permission{ - { - Level: "CAN_MANAGE", - UserName: "user@company.com", - }, + jobPermissions := resources.Permissions[jobs.JobPermissionLevel]{ + {Level: "CAN_MANAGE", UserName: "user@company.com"}, } - pipelinePermissions := []resources.Permission{ - { - Level: "CAN_MANAGE", - UserName: "user@company.com", - }, + pipelinePermissions := resources.Permissions[pipelines.PipelinePermissionLevel]{ + {Level: "CAN_MANAGE", UserName: "user@company.com"}, } - experimentPermissions := []resources.Permission{ - { - Level: "CAN_MANAGE", - UserName: "user@company.com", - }, + experimentPermissions := resources.Permissions[ml.ExperimentPermissionLevel]{ + {Level: "CAN_MANAGE", UserName: "user@company.com"}, } - modelPermissions := []resources.Permission{ - { - Level: "CAN_MANAGE", - UserName: "user@company.com", - }, + modelPermissions := resources.Permissions[ml.RegisteredModelPermissionLevel]{ + {Level: "CAN_MANAGE", UserName: "user@company.com"}, } - endpointPermissions := []resources.Permission{ - { - Level: "CAN_MANAGE", - UserName: "user@company.com", - }, + endpointPermissions := resources.Permissions[serving.ServingEndpointPermissionLevel]{ + {Level: "CAN_MANAGE", UserName: "user@company.com"}, } - clusterPermissions := []resources.Permission{ - { - Level: "CAN_MANAGE", - UserName: "user@company.com", - }, + clusterPermissions := resources.Permissions[compute.ClusterPermissionLevel]{ + {Level: "CAN_MANAGE", UserName: "user@company.com"}, } b.Config.Resources.Jobs["job1"].Permissions = jobPermissions b.Config.Resources.Jobs["job1"].RunAs = &jobs.JobRunAs{UserName: "user@company.com"} diff --git a/bundle/config/resources/alerts.go b/bundle/config/resources/alerts.go index bfdd64e900..be34e0061c 100644 --- a/bundle/config/resources/alerts.go +++ b/bundle/config/resources/alerts.go @@ -7,6 +7,7 @@ import ( "github.com/databricks/cli/libs/log" "github.com/databricks/databricks-sdk-go" "github.com/databricks/databricks-sdk-go/marshal" + "github.com/databricks/databricks-sdk-go/service/iam" "github.com/databricks/databricks-sdk-go/service/sql" ) @@ -14,7 +15,7 @@ type Alert struct { BaseResource sql.AlertV2 //nolint AlertV2 also defines Id and URL field with the same json tag "id" and "url" - Permissions []Permission `json:"permissions,omitempty"` + Permissions Permissions[iam.PermissionLevel] `json:"permissions,omitempty"` // Filepath points to the local .dbalert.json file containing the alert definition. // If specified, any fields that are part of the .dbalert.json file schema will not be allowed in diff --git a/bundle/config/resources/apps.go b/bundle/config/resources/apps.go index 7375ab6c2d..a11c67810a 100644 --- a/bundle/config/resources/apps.go +++ b/bundle/config/resources/apps.go @@ -50,7 +50,7 @@ type App struct { // This is used in conjunction with GitRepository (from apps.App) and is passed to the Deploy API. GitSource *apps.GitSource `json:"git_source,omitempty"` - Permissions []Permission `json:"permissions,omitempty"` + Permissions Permissions[apps.AppPermissionLevel] `json:"permissions,omitempty"` } func (a *App) UnmarshalJSON(b []byte) error { diff --git a/bundle/config/resources/clusters.go b/bundle/config/resources/clusters.go index deba448647..dff7e92426 100644 --- a/bundle/config/resources/clusters.go +++ b/bundle/config/resources/clusters.go @@ -14,7 +14,7 @@ type Cluster struct { BaseResource compute.ClusterSpec - Permissions []Permission `json:"permissions,omitempty"` + Permissions Permissions[compute.ClusterPermissionLevel] `json:"permissions,omitempty"` } func (s *Cluster) UnmarshalJSON(b []byte) error { diff --git a/bundle/config/resources/dashboard.go b/bundle/config/resources/dashboard.go index c108ac8abe..b52d3e527b 100644 --- a/bundle/config/resources/dashboard.go +++ b/bundle/config/resources/dashboard.go @@ -9,6 +9,7 @@ import ( "github.com/databricks/databricks-sdk-go" "github.com/databricks/databricks-sdk-go/marshal" "github.com/databricks/databricks-sdk-go/service/dashboards" + "github.com/databricks/databricks-sdk-go/service/iam" ) type DashboardConfig struct { @@ -80,7 +81,7 @@ type Dashboard struct { BaseResource DashboardConfig - Permissions []Permission `json:"permissions,omitempty"` + Permissions Permissions[iam.PermissionLevel] `json:"permissions,omitempty"` // FilePath points to the local `.lvdash.json` file containing the dashboard definition. // This is inlined into serialized_dashboard during deployment. The file_path is kept around diff --git a/bundle/config/resources/database_instance.go b/bundle/config/resources/database_instance.go index 2ba19b84f4..6ffaa36b61 100644 --- a/bundle/config/resources/database_instance.go +++ b/bundle/config/resources/database_instance.go @@ -8,13 +8,14 @@ import ( "github.com/databricks/databricks-sdk-go" "github.com/databricks/databricks-sdk-go/service/database" + "github.com/databricks/databricks-sdk-go/service/iam" ) type DatabaseInstance struct { BaseResource database.DatabaseInstance - Permissions []Permission `json:"permissions,omitempty"` + Permissions Permissions[iam.PermissionLevel] `json:"permissions,omitempty"` } func (d *DatabaseInstance) Exists(ctx context.Context, w *databricks.WorkspaceClient, name string) (bool, error) { diff --git a/bundle/config/resources/job.go b/bundle/config/resources/job.go index 5c3eb8b29d..430c270d84 100644 --- a/bundle/config/resources/job.go +++ b/bundle/config/resources/job.go @@ -15,7 +15,7 @@ type Job struct { BaseResource jobs.JobSettings - Permissions []Permission `json:"permissions,omitempty"` + Permissions Permissions[jobs.JobPermissionLevel] `json:"permissions,omitempty"` } func (j *Job) UnmarshalJSON(b []byte) error { diff --git a/bundle/config/resources/mlflow_experiment.go b/bundle/config/resources/mlflow_experiment.go index e80ee26250..a1f07aea9a 100644 --- a/bundle/config/resources/mlflow_experiment.go +++ b/bundle/config/resources/mlflow_experiment.go @@ -14,7 +14,7 @@ type MlflowExperiment struct { BaseResource ml.CreateExperiment - Permissions []Permission `json:"permissions,omitempty"` + Permissions Permissions[ml.ExperimentPermissionLevel] `json:"permissions,omitempty"` } func (s *MlflowExperiment) UnmarshalJSON(b []byte) error { diff --git a/bundle/config/resources/mlflow_model.go b/bundle/config/resources/mlflow_model.go index 1fa2cac8e8..8935d07372 100644 --- a/bundle/config/resources/mlflow_model.go +++ b/bundle/config/resources/mlflow_model.go @@ -14,7 +14,7 @@ type MlflowModel struct { BaseResource ml.CreateModelRequest - Permissions []Permission `json:"permissions,omitempty"` + Permissions Permissions[ml.RegisteredModelPermissionLevel] `json:"permissions,omitempty"` } func (s *MlflowModel) UnmarshalJSON(b []byte) error { diff --git a/bundle/config/resources/model_serving_endpoint.go b/bundle/config/resources/model_serving_endpoint.go index 9d98c15094..a3a8fed6b1 100644 --- a/bundle/config/resources/model_serving_endpoint.go +++ b/bundle/config/resources/model_serving_endpoint.go @@ -19,7 +19,7 @@ type ModelServingEndpoint struct { // This is a resource agnostic implementation of permissions for ACLs. // Implementation could be different based on the resource type. - Permissions []Permission `json:"permissions,omitempty"` + Permissions Permissions[serving.ServingEndpointPermissionLevel] `json:"permissions,omitempty"` } func (s *ModelServingEndpoint) UnmarshalJSON(b []byte) error { diff --git a/bundle/config/resources/permission.go b/bundle/config/resources/permission.go index 8104313883..219e354f3a 100644 --- a/bundle/config/resources/permission.go +++ b/bundle/config/resources/permission.go @@ -3,18 +3,48 @@ package resources import ( "fmt" "strings" + + "github.com/databricks/databricks-sdk-go/service/iam" ) // Permission holds the permission level setting for a single principal. -type Permission struct { - Level string `json:"level"` +type Permission[L ~string] struct { + Level L `json:"level"` UserName string `json:"user_name,omitempty"` ServicePrincipalName string `json:"service_principal_name,omitempty"` GroupName string `json:"group_name,omitempty"` } -func (p Permission) String() string { +// ToAccessControlRequest converts to the SDK type used by the permissions API. +func (p Permission[L]) ToAccessControlRequest() iam.AccessControlRequest { + return iam.AccessControlRequest{ + PermissionLevel: iam.PermissionLevel(p.Level), + UserName: p.UserName, + ServicePrincipalName: p.ServicePrincipalName, + GroupName: p.GroupName, + } +} + +// Permissions is a named slice of Permission[L] that implements PermissionsSlice, +// allowing generic code to work with any instantiation via the interface. +type Permissions[L ~string] []Permission[L] + +// PermissionsSlice is implemented by any Permissions[L], enabling type-agnostic +// conversion to iam.AccessControlRequest without reflection or type switches. +type PermissionsSlice interface { + ToAccessControlRequests() []iam.AccessControlRequest +} + +func (ps Permissions[L]) ToAccessControlRequests() []iam.AccessControlRequest { + result := make([]iam.AccessControlRequest, len(ps)) + for i, p := range ps { + result[i] = p.ToAccessControlRequest() + } + return result +} + +func (p Permission[L]) String() string { if p.UserName != "" { return fmt.Sprintf("level: %s, user_name: %s", p.Level, p.UserName) } @@ -27,7 +57,7 @@ func (p Permission) String() string { return fmt.Sprintf("level: %s, group_name: %s", p.Level, p.GroupName) } - return "level: " + p.Level + return "level: " + string(p.Level) } // PermissionOrder defines the hierarchy of permission levels. diff --git a/bundle/config/resources/pipeline.go b/bundle/config/resources/pipeline.go index f530374120..2ab2c16f24 100644 --- a/bundle/config/resources/pipeline.go +++ b/bundle/config/resources/pipeline.go @@ -14,7 +14,7 @@ type Pipeline struct { BaseResource pipelines.CreatePipeline //nolint CreatePipeline also defines Id field with the same json tag "id" - Permissions []Permission `json:"permissions,omitempty"` + Permissions Permissions[pipelines.PipelinePermissionLevel] `json:"permissions,omitempty"` } func (p *Pipeline) UnmarshalJSON(b []byte) error { diff --git a/bundle/config/resources/postgres_project.go b/bundle/config/resources/postgres_project.go index d76a6ee135..8211c61dbb 100644 --- a/bundle/config/resources/postgres_project.go +++ b/bundle/config/resources/postgres_project.go @@ -7,6 +7,7 @@ import ( "github.com/databricks/cli/libs/log" "github.com/databricks/databricks-sdk-go" "github.com/databricks/databricks-sdk-go/marshal" + "github.com/databricks/databricks-sdk-go/service/iam" "github.com/databricks/databricks-sdk-go/service/postgres" ) @@ -30,7 +31,7 @@ type PostgresProject struct { BaseResource PostgresProjectConfig - Permissions []Permission `json:"permissions,omitempty"` + Permissions Permissions[iam.PermissionLevel] `json:"permissions,omitempty"` } func (p *PostgresProject) Exists(ctx context.Context, w *databricks.WorkspaceClient, name string) (bool, error) { diff --git a/bundle/config/resources/sql_warehouses.go b/bundle/config/resources/sql_warehouses.go index b9b072e67d..7f52b5822c 100644 --- a/bundle/config/resources/sql_warehouses.go +++ b/bundle/config/resources/sql_warehouses.go @@ -14,7 +14,7 @@ type SqlWarehouse struct { BaseResource sql.CreateWarehouseRequest - Permissions []Permission `json:"permissions,omitempty"` + Permissions Permissions[sql.WarehousePermissionLevel] `json:"permissions,omitempty"` } func (sw *SqlWarehouse) UnmarshalJSON(b []byte) error { diff --git a/bundle/config/resources_types_test.go b/bundle/config/resources_types_test.go index 244e9dfa67..2d5b45b9b8 100644 --- a/bundle/config/resources_types_test.go +++ b/bundle/config/resources_types_test.go @@ -7,6 +7,7 @@ import ( "github.com/stretchr/testify/assert" "github.com/databricks/cli/bundle/config/resources" + "github.com/databricks/databricks-sdk-go/service/jobs" ) func TestResourcesTypesMap(t *testing.T) { @@ -18,5 +19,5 @@ func TestResourcesTypesMap(t *testing.T) { typ, ok = ResourcesTypes["jobs.permissions"] assert.True(t, ok, "resources type for 'jobs.permissions' not found in ResourcesTypes map") - assert.Equal(t, reflect.TypeOf([]resources.Permission{}), typ, "resources type for 'jobs.permissions' mismatch") + assert.Equal(t, reflect.TypeOf(resources.Permissions[jobs.JobPermissionLevel]{}), typ, "resources type for 'jobs.permissions' mismatch") } diff --git a/bundle/config/root.go b/bundle/config/root.go index 861791fc34..ab8e7512d2 100644 --- a/bundle/config/root.go +++ b/bundle/config/root.go @@ -17,6 +17,7 @@ import ( "github.com/databricks/cli/libs/dyn/merge" "github.com/databricks/cli/libs/dyn/yamlloader" "github.com/databricks/cli/libs/log" + "github.com/databricks/databricks-sdk-go/service/iam" "github.com/databricks/databricks-sdk-go/service/jobs" ) @@ -75,7 +76,7 @@ type Root struct { // Permissions section allows to define permissions which will be // applied to all resources defined in bundle - Permissions []resources.Permission `json:"permissions,omitempty"` + Permissions []resources.Permission[iam.PermissionLevel] `json:"permissions,omitempty"` // Locations is an output-only field that holds configuration location // information for every path in the configuration tree. diff --git a/bundle/config/target.go b/bundle/config/target.go index fae9c940b3..d5f1f2d052 100644 --- a/bundle/config/target.go +++ b/bundle/config/target.go @@ -3,6 +3,7 @@ package config import ( "github.com/databricks/cli/bundle/config/resources" "github.com/databricks/cli/bundle/config/variable" + "github.com/databricks/databricks-sdk-go/service/iam" "github.com/databricks/databricks-sdk-go/service/jobs" ) @@ -68,7 +69,7 @@ type Target struct { Sync *Sync `json:"sync,omitempty"` - Permissions []resources.Permission `json:"permissions,omitempty"` + Permissions []resources.Permission[iam.PermissionLevel] `json:"permissions,omitempty"` } const ( diff --git a/bundle/config/validate/folder_permissions_test.go b/bundle/config/validate/folder_permissions_test.go index 394ffee4e2..dc699ba2a5 100644 --- a/bundle/config/validate/folder_permissions_test.go +++ b/bundle/config/validate/folder_permissions_test.go @@ -10,6 +10,7 @@ import ( "github.com/databricks/cli/libs/diag" "github.com/databricks/databricks-sdk-go/apierr" "github.com/databricks/databricks-sdk-go/experimental/mocks" + "github.com/databricks/databricks-sdk-go/service/iam" "github.com/databricks/databricks-sdk-go/service/workspace" "github.com/stretchr/testify/mock" "github.com/stretchr/testify/require" @@ -25,7 +26,7 @@ func TestFolderPermissionsInheritedWhenRootPathDoesNotExist(t *testing.T) { StatePath: "/Workspace/Users/foo@bar.com/state", ResourcePath: "/Workspace/Users/foo@bar.com/resources", }, - Permissions: []resources.Permission{ + Permissions: []resources.Permission[iam.PermissionLevel]{ {Level: permissions.CAN_MANAGE, UserName: "foo@bar.com"}, }, }, @@ -82,7 +83,7 @@ func TestValidateFolderPermissionsFailsOnMissingBundlePermission(t *testing.T) { StatePath: "/Workspace/Users/foo@bar.com/state", ResourcePath: "/Workspace/Users/foo@bar.com/resources", }, - Permissions: []resources.Permission{ + Permissions: []resources.Permission[iam.PermissionLevel]{ {Level: permissions.CAN_MANAGE, UserName: "foo@bar.com"}, }, }, @@ -136,7 +137,7 @@ func TestValidateFolderPermissionsFailsOnPermissionMismatch(t *testing.T) { StatePath: "/Workspace/Users/foo@bar.com/state", ResourcePath: "/Workspace/Users/foo@bar.com/resources", }, - Permissions: []resources.Permission{ + Permissions: []resources.Permission[iam.PermissionLevel]{ {Level: permissions.CAN_MANAGE, UserName: "foo@bar.com"}, }, }, @@ -179,7 +180,7 @@ func TestValidateFolderPermissionsFailsOnNoRootFolder(t *testing.T) { StatePath: "/NotExisting/state", ResourcePath: "/NotExisting/resources", }, - Permissions: []resources.Permission{ + Permissions: []resources.Permission[iam.PermissionLevel]{ {Level: permissions.CAN_MANAGE, UserName: "foo@bar.com"}, }, }, diff --git a/bundle/deploy/terraform/convert_test.go b/bundle/deploy/terraform/convert_test.go index 0b583e2b4e..dfb315eb9e 100644 --- a/bundle/deploy/terraform/convert_test.go +++ b/bundle/deploy/terraform/convert_test.go @@ -91,7 +91,7 @@ func TestBundleToTerraformJob(t *testing.T) { func TestBundleToTerraformJobPermissions(t *testing.T) { src := resources.Job{ - Permissions: []resources.Permission{ + Permissions: resources.Permissions[jobs.JobPermissionLevel]{ { Level: "CAN_VIEW", UserName: "jane@doe.com", @@ -260,7 +260,7 @@ func TestBundleToTerraformPipeline(t *testing.T) { func TestBundleToTerraformPipelinePermissions(t *testing.T) { src := resources.Pipeline{ - Permissions: []resources.Permission{ + Permissions: resources.Permissions[pipelines.PipelinePermissionLevel]{ { Level: "CAN_VIEW", UserName: "jane@doe.com", @@ -331,7 +331,7 @@ func TestBundleToTerraformModelPermissions(t *testing.T) { CreateModelRequest: ml.CreateModelRequest{ Name: "name", }, - Permissions: []resources.Permission{ + Permissions: resources.Permissions[ml.RegisteredModelPermissionLevel]{ { Level: "CAN_READ", UserName: "jane@doe.com", @@ -385,7 +385,7 @@ func TestBundleToTerraformExperimentPermissions(t *testing.T) { CreateExperiment: ml.CreateExperiment{ Name: "name", }, - Permissions: []resources.Permission{ + Permissions: resources.Permissions[ml.ExperimentPermissionLevel]{ { Level: "CAN_READ", UserName: "jane@doe.com", @@ -481,7 +481,7 @@ func TestBundleToTerraformModelServingPermissions(t *testing.T) { }, }, }, - Permissions: []resources.Permission{ + Permissions: resources.Permissions[serving.ServingEndpointPermissionLevel]{ { Level: "CAN_VIEW", UserName: "jane@doe.com", diff --git a/bundle/deploy/terraform/tfdyn/convert_alert_test.go b/bundle/deploy/terraform/tfdyn/convert_alert_test.go index 25d5942519..1b76bbf593 100644 --- a/bundle/deploy/terraform/tfdyn/convert_alert_test.go +++ b/bundle/deploy/terraform/tfdyn/convert_alert_test.go @@ -7,6 +7,7 @@ import ( "github.com/databricks/cli/bundle/internal/tf/schema" "github.com/databricks/cli/libs/dyn" "github.com/databricks/cli/libs/dyn/convert" + "github.com/databricks/databricks-sdk-go/service/iam" "github.com/databricks/databricks-sdk-go/service/sql" "github.com/stretchr/testify/assert" "github.com/stretchr/testify/require" @@ -21,7 +22,7 @@ func TestConvertAlert(t *testing.T) { CustomSummary: "Test alert summary", CustomDescription: "Test alert description", }, - Permissions: []resources.Permission{ + Permissions: resources.Permissions[iam.PermissionLevel]{ { Level: "CAN_VIEW", UserName: "jane@doe.com", diff --git a/bundle/deploy/terraform/tfdyn/convert_app_test.go b/bundle/deploy/terraform/tfdyn/convert_app_test.go index 6eb2ce7e23..8073280b5e 100644 --- a/bundle/deploy/terraform/tfdyn/convert_app_test.go +++ b/bundle/deploy/terraform/tfdyn/convert_app_test.go @@ -7,7 +7,7 @@ import ( "github.com/databricks/cli/bundle/internal/tf/schema" "github.com/databricks/cli/libs/dyn" "github.com/databricks/cli/libs/dyn/convert" - "github.com/databricks/databricks-sdk-go/service/apps" + appssdk "github.com/databricks/databricks-sdk-go/service/apps" "github.com/stretchr/testify/assert" "github.com/stretchr/testify/require" ) @@ -15,27 +15,27 @@ import ( func TestConvertApp(t *testing.T) { src := resources.App{ SourceCodePath: "./app", - App: apps.App{ + App: appssdk.App{ Name: "app_id", Description: "app description", - Resources: []apps.AppResource{ + Resources: []appssdk.AppResource{ { Name: "job1", - Job: &apps.AppResourceJob{ + Job: &appssdk.AppResourceJob{ Id: "1234", Permission: "CAN_MANAGE_RUN", }, }, { Name: "sql1", - SqlWarehouse: &apps.AppResourceSqlWarehouse{ + SqlWarehouse: &appssdk.AppResourceSqlWarehouse{ Id: "5678", Permission: "CAN_USE", }, }, }, }, - Permissions: []resources.Permission{ + Permissions: resources.Permissions[appssdk.AppPermissionLevel]{ { Level: "CAN_RUN", UserName: "jack@gmail.com", @@ -97,19 +97,19 @@ func TestConvertApp(t *testing.T) { func TestConvertAppWithNoDescription(t *testing.T) { src := resources.App{ SourceCodePath: "./app", - App: apps.App{ + App: appssdk.App{ Name: "app_id", - Resources: []apps.AppResource{ + Resources: []appssdk.AppResource{ { Name: "job1", - Job: &apps.AppResourceJob{ + Job: &appssdk.AppResourceJob{ Id: "1234", Permission: "CAN_MANAGE_RUN", }, }, { Name: "sql1", - SqlWarehouse: &apps.AppResourceSqlWarehouse{ + SqlWarehouse: &appssdk.AppResourceSqlWarehouse{ Id: "5678", Permission: "CAN_USE", }, diff --git a/bundle/deploy/terraform/tfdyn/convert_cluster_test.go b/bundle/deploy/terraform/tfdyn/convert_cluster_test.go index ae676ada99..ee8a82abe6 100644 --- a/bundle/deploy/terraform/tfdyn/convert_cluster_test.go +++ b/bundle/deploy/terraform/tfdyn/convert_cluster_test.go @@ -35,7 +35,7 @@ func TestConvertCluster(t *testing.T) { }, }, - Permissions: []resources.Permission{ + Permissions: resources.Permissions[compute.ClusterPermissionLevel]{ { Level: "CAN_RUN", UserName: "jack@gmail.com", diff --git a/bundle/deploy/terraform/tfdyn/convert_dashboard_test.go b/bundle/deploy/terraform/tfdyn/convert_dashboard_test.go index d8f05dc76c..c8cca19769 100644 --- a/bundle/deploy/terraform/tfdyn/convert_dashboard_test.go +++ b/bundle/deploy/terraform/tfdyn/convert_dashboard_test.go @@ -7,6 +7,7 @@ import ( "github.com/databricks/cli/bundle/internal/tf/schema" "github.com/databricks/cli/libs/dyn" "github.com/databricks/cli/libs/dyn/convert" + "github.com/databricks/databricks-sdk-go/service/iam" "github.com/stretchr/testify/assert" "github.com/stretchr/testify/require" ) @@ -20,7 +21,7 @@ func TestConvertDashboard(t *testing.T) { EmbedCredentials: true, }, - Permissions: []resources.Permission{ + Permissions: resources.Permissions[iam.PermissionLevel]{ { Level: "CAN_VIEW", UserName: "jane@doe.com", diff --git a/bundle/deploy/terraform/tfdyn/convert_database_instance_test.go b/bundle/deploy/terraform/tfdyn/convert_database_instance_test.go index 533c69abed..925a5e0480 100644 --- a/bundle/deploy/terraform/tfdyn/convert_database_instance_test.go +++ b/bundle/deploy/terraform/tfdyn/convert_database_instance_test.go @@ -8,6 +8,7 @@ import ( "github.com/databricks/cli/libs/dyn" "github.com/databricks/cli/libs/dyn/convert" "github.com/databricks/databricks-sdk-go/service/database" + "github.com/databricks/databricks-sdk-go/service/iam" "github.com/stretchr/testify/assert" "github.com/stretchr/testify/require" ) @@ -73,7 +74,7 @@ func TestConvertDatabaseInstanceWithPermissions(t *testing.T) { Name: "db-instance-with-permissions", Capacity: "CU_2", }, - Permissions: []resources.Permission{ + Permissions: resources.Permissions[iam.PermissionLevel]{ { Level: "CAN_USE", UserName: "user@example.com", diff --git a/bundle/deploy/terraform/tfdyn/convert_experiment_test.go b/bundle/deploy/terraform/tfdyn/convert_experiment_test.go index 6d2e6ea2d0..d86bc3dfba 100644 --- a/bundle/deploy/terraform/tfdyn/convert_experiment_test.go +++ b/bundle/deploy/terraform/tfdyn/convert_experiment_test.go @@ -17,7 +17,7 @@ func TestConvertExperiment(t *testing.T) { CreateExperiment: ml.CreateExperiment{ Name: "name", }, - Permissions: []resources.Permission{ + Permissions: resources.Permissions[ml.ExperimentPermissionLevel]{ { Level: "CAN_READ", UserName: "jane@doe.com", diff --git a/bundle/deploy/terraform/tfdyn/convert_job_test.go b/bundle/deploy/terraform/tfdyn/convert_job_test.go index 0a84fbd20b..cf27d11a9d 100644 --- a/bundle/deploy/terraform/tfdyn/convert_job_test.go +++ b/bundle/deploy/terraform/tfdyn/convert_job_test.go @@ -71,7 +71,7 @@ func TestConvertJob(t *testing.T) { }, }, }, - Permissions: []resources.Permission{ + Permissions: resources.Permissions[jobs.JobPermissionLevel]{ { Level: "CAN_VIEW", UserName: "jane@doe.com", diff --git a/bundle/deploy/terraform/tfdyn/convert_model_serving_endpoint_test.go b/bundle/deploy/terraform/tfdyn/convert_model_serving_endpoint_test.go index 6aa3c079e6..c6ac5b1556 100644 --- a/bundle/deploy/terraform/tfdyn/convert_model_serving_endpoint_test.go +++ b/bundle/deploy/terraform/tfdyn/convert_model_serving_endpoint_test.go @@ -35,7 +35,7 @@ func TestConvertModelServingEndpoint(t *testing.T) { }, }, }, - Permissions: []resources.Permission{ + Permissions: resources.Permissions[serving.ServingEndpointPermissionLevel]{ { Level: "CAN_VIEW", UserName: "jane@doe.com", diff --git a/bundle/deploy/terraform/tfdyn/convert_model_test.go b/bundle/deploy/terraform/tfdyn/convert_model_test.go index 999d22bccc..93544b4707 100644 --- a/bundle/deploy/terraform/tfdyn/convert_model_test.go +++ b/bundle/deploy/terraform/tfdyn/convert_model_test.go @@ -28,7 +28,7 @@ func TestConvertModel(t *testing.T) { }, }, }, - Permissions: []resources.Permission{ + Permissions: resources.Permissions[ml.RegisteredModelPermissionLevel]{ { Level: "CAN_READ", UserName: "jane@doe.com", diff --git a/bundle/deploy/terraform/tfdyn/convert_permissions_test.go b/bundle/deploy/terraform/tfdyn/convert_permissions_test.go index 9efe3e9d94..3143681d12 100644 --- a/bundle/deploy/terraform/tfdyn/convert_permissions_test.go +++ b/bundle/deploy/terraform/tfdyn/convert_permissions_test.go @@ -7,13 +7,14 @@ import ( "github.com/databricks/cli/bundle/internal/tf/schema" "github.com/databricks/cli/libs/dyn" "github.com/databricks/cli/libs/dyn/convert" + "github.com/databricks/databricks-sdk-go/service/jobs" "github.com/stretchr/testify/assert" "github.com/stretchr/testify/require" ) func TestConvertPermissions(t *testing.T) { src := resources.Job{ - Permissions: []resources.Permission{ + Permissions: resources.Permissions[jobs.JobPermissionLevel]{ { Level: "CAN_VIEW", UserName: "jane@doe.com", @@ -72,7 +73,7 @@ func TestConvertPermissionsNil(t *testing.T) { func TestConvertPermissionsEmpty(t *testing.T) { src := resources.Job{ - Permissions: []resources.Permission{}, + Permissions: resources.Permissions[jobs.JobPermissionLevel]{}, } vin, err := convert.FromTyped(src, dyn.NilValue) diff --git a/bundle/deploy/terraform/tfdyn/convert_pipeline_test.go b/bundle/deploy/terraform/tfdyn/convert_pipeline_test.go index 98c2e832bd..e6239dd341 100644 --- a/bundle/deploy/terraform/tfdyn/convert_pipeline_test.go +++ b/bundle/deploy/terraform/tfdyn/convert_pipeline_test.go @@ -64,7 +64,7 @@ func TestConvertPipeline(t *testing.T) { }, }, }, - Permissions: []resources.Permission{ + Permissions: resources.Permissions[pipelines.PipelinePermissionLevel]{ { Level: "CAN_VIEW", UserName: "jane@doe.com", diff --git a/bundle/deploy/terraform/tfdyn/convert_postgres_project_test.go b/bundle/deploy/terraform/tfdyn/convert_postgres_project_test.go index 696a6e76c2..26f0b44998 100644 --- a/bundle/deploy/terraform/tfdyn/convert_postgres_project_test.go +++ b/bundle/deploy/terraform/tfdyn/convert_postgres_project_test.go @@ -9,6 +9,7 @@ import ( "github.com/databricks/cli/libs/dyn" "github.com/databricks/cli/libs/dyn/convert" "github.com/databricks/databricks-sdk-go/common/types/duration" + "github.com/databricks/databricks-sdk-go/service/iam" "github.com/databricks/databricks-sdk-go/service/postgres" "github.com/stretchr/testify/assert" "github.com/stretchr/testify/require" @@ -64,7 +65,7 @@ func TestConvertPostgresProjectWithPermissions(t *testing.T) { PgVersion: 17, }, }, - Permissions: []resources.Permission{ + Permissions: resources.Permissions[iam.PermissionLevel]{ { Level: "CAN_USE", UserName: "user@example.com", diff --git a/bundle/direct/dresources/permissions.go b/bundle/direct/dresources/permissions.go index adb090ac5c..32c941394b 100644 --- a/bundle/direct/dresources/permissions.go +++ b/bundle/direct/dresources/permissions.go @@ -54,21 +54,12 @@ func PreparePermissionsInputConfig(inputConfig any, node string) (*structvar.Str return nil, fmt.Errorf("unsupported permissions resource type: %s", resourceType) } - permissionsPtr, ok := inputConfig.(*[]resources.Permission) + permsSlice, ok := inputConfig.(resources.PermissionsSlice) if !ok { - return nil, fmt.Errorf("expected *[]resources.Permission, got %T", inputConfig) + return nil, fmt.Errorf("expected resources.PermissionsSlice, got %T", inputConfig) } - permissions := make([]iam.AccessControlRequest, 0, len(*permissionsPtr)) - for _, p := range *permissionsPtr { - permissions = append(permissions, iam.AccessControlRequest{ - PermissionLevel: iam.PermissionLevel(p.Level), - GroupName: p.GroupName, - ServicePrincipalName: p.ServicePrincipalName, - UserName: p.UserName, - ForceSendFields: nil, - }) - } + permissions := permsSlice.ToAccessControlRequests() objectIdRef := prefix + "${" + baseNode + ".id}" // For permissions, model serving endpoint uses its internal ID, which is different diff --git a/bundle/internal/schema/annotations.yml b/bundle/internal/schema/annotations.yml index a9a9a7fd2c..24f952741c 100644 --- a/bundle/internal/schema/annotations.yml +++ b/bundle/internal/schema/annotations.yml @@ -609,8 +609,152 @@ github.com/databricks/cli/bundle/config/resources.Lifecycle: "prevent_destroy": "description": |- Lifecycle setting to prevent the resource from being destroyed. -github.com/databricks/cli/bundle/config/resources.Permission: - "-": +? github.com/databricks/cli/bundle/config/resources.Permission[github.com/databricks/databricks-sdk-go/service/apps.AppPermissionLevel] +: "-": + "description": |- + Defines a permission for a specific entity. + "markdown_description": |- + Defines a permission for a specific entity. See [\_](/dev-tools/bundles/settings.md#permissions) and [\_](/dev-tools/bundles/permissions.md). + "group_name": + "description": |- + The name of the group that has the permission set in level. + "level": + "description": |- + The allowed permission for user, group, service principal defined for this permission. + "service_principal_name": + "description": |- + The name of the service principal that has the permission set in level. + "user_name": + "description": |- + The name of the user that has the permission set in level. +? github.com/databricks/cli/bundle/config/resources.Permission[github.com/databricks/databricks-sdk-go/service/compute.ClusterPermissionLevel] +: "-": + "description": |- + Defines a permission for a specific entity. + "markdown_description": |- + Defines a permission for a specific entity. See [\_](/dev-tools/bundles/settings.md#permissions) and [\_](/dev-tools/bundles/permissions.md). + "group_name": + "description": |- + The name of the group that has the permission set in level. + "level": + "description": |- + The allowed permission for user, group, service principal defined for this permission. + "service_principal_name": + "description": |- + The name of the service principal that has the permission set in level. + "user_name": + "description": |- + The name of the user that has the permission set in level. +? github.com/databricks/cli/bundle/config/resources.Permission[github.com/databricks/databricks-sdk-go/service/iam.PermissionLevel] +: "-": + "description": |- + Defines a permission for a specific entity. + "markdown_description": |- + Defines a permission for a specific entity. See [\_](/dev-tools/bundles/settings.md#permissions) and [\_](/dev-tools/bundles/permissions.md). + "group_name": + "description": |- + The name of the group that has the permission set in level. + "level": + "description": |- + The allowed permission for user, group, service principal defined for this permission. + "service_principal_name": + "description": |- + The name of the service principal that has the permission set in level. + "user_name": + "description": |- + The name of the user that has the permission set in level. +? github.com/databricks/cli/bundle/config/resources.Permission[github.com/databricks/databricks-sdk-go/service/jobs.JobPermissionLevel] +: "-": + "description": |- + Defines a permission for a specific entity. + "markdown_description": |- + Defines a permission for a specific entity. See [\_](/dev-tools/bundles/settings.md#permissions) and [\_](/dev-tools/bundles/permissions.md). + "group_name": + "description": |- + The name of the group that has the permission set in level. + "level": + "description": |- + The allowed permission for user, group, service principal defined for this permission. + "service_principal_name": + "description": |- + The name of the service principal that has the permission set in level. + "user_name": + "description": |- + The name of the user that has the permission set in level. +? github.com/databricks/cli/bundle/config/resources.Permission[github.com/databricks/databricks-sdk-go/service/ml.ExperimentPermissionLevel] +: "-": + "description": |- + Defines a permission for a specific entity. + "markdown_description": |- + Defines a permission for a specific entity. See [\_](/dev-tools/bundles/settings.md#permissions) and [\_](/dev-tools/bundles/permissions.md). + "group_name": + "description": |- + The name of the group that has the permission set in level. + "level": + "description": |- + The allowed permission for user, group, service principal defined for this permission. + "service_principal_name": + "description": |- + The name of the service principal that has the permission set in level. + "user_name": + "description": |- + The name of the user that has the permission set in level. +? github.com/databricks/cli/bundle/config/resources.Permission[github.com/databricks/databricks-sdk-go/service/ml.RegisteredModelPermissionLevel] +: "-": + "description": |- + Defines a permission for a specific entity. + "markdown_description": |- + Defines a permission for a specific entity. See [\_](/dev-tools/bundles/settings.md#permissions) and [\_](/dev-tools/bundles/permissions.md). + "group_name": + "description": |- + The name of the group that has the permission set in level. + "level": + "description": |- + The allowed permission for user, group, service principal defined for this permission. + "service_principal_name": + "description": |- + The name of the service principal that has the permission set in level. + "user_name": + "description": |- + The name of the user that has the permission set in level. +? github.com/databricks/cli/bundle/config/resources.Permission[github.com/databricks/databricks-sdk-go/service/pipelines.PipelinePermissionLevel] +: "-": + "description": |- + Defines a permission for a specific entity. + "markdown_description": |- + Defines a permission for a specific entity. See [\_](/dev-tools/bundles/settings.md#permissions) and [\_](/dev-tools/bundles/permissions.md). + "group_name": + "description": |- + The name of the group that has the permission set in level. + "level": + "description": |- + The allowed permission for user, group, service principal defined for this permission. + "service_principal_name": + "description": |- + The name of the service principal that has the permission set in level. + "user_name": + "description": |- + The name of the user that has the permission set in level. +? github.com/databricks/cli/bundle/config/resources.Permission[github.com/databricks/databricks-sdk-go/service/serving.ServingEndpointPermissionLevel] +: "-": + "description": |- + Defines a permission for a specific entity. + "markdown_description": |- + Defines a permission for a specific entity. See [\_](/dev-tools/bundles/settings.md#permissions) and [\_](/dev-tools/bundles/permissions.md). + "group_name": + "description": |- + The name of the group that has the permission set in level. + "level": + "description": |- + The allowed permission for user, group, service principal defined for this permission. + "service_principal_name": + "description": |- + The name of the service principal that has the permission set in level. + "user_name": + "description": |- + The name of the user that has the permission set in level. +? github.com/databricks/cli/bundle/config/resources.Permission[github.com/databricks/databricks-sdk-go/service/sql.WarehousePermissionLevel] +: "-": "description": |- Defines a permission for a specific entity. "markdown_description": |- diff --git a/bundle/permissions/permission_diagnostics_test.go b/bundle/permissions/permission_diagnostics_test.go index e5a9214f43..c206fd14ac 100644 --- a/bundle/permissions/permission_diagnostics_test.go +++ b/bundle/permissions/permission_diagnostics_test.go @@ -13,7 +13,7 @@ import ( ) func TestPermissionDiagnosticsApplySuccess(t *testing.T) { - b := mockBundle([]resources.Permission{ + b := mockBundle([]resources.Permission[iam.PermissionLevel]{ {Level: "CAN_MANAGE", UserName: "testuser@databricks.com"}, }) @@ -29,7 +29,7 @@ func TestPermissionDiagnosticsEmpty(t *testing.T) { } func TestPermissionDiagnosticsApplyFail(t *testing.T) { - b := mockBundle([]resources.Permission{ + b := mockBundle([]resources.Permission[iam.PermissionLevel]{ {Level: "CAN_VIEW", UserName: "testuser@databricks.com"}, }) @@ -48,7 +48,7 @@ func TestPermissionDiagnosticsApplyFail(t *testing.T) { require.Contains(t, diags[0].Summary, expectedMsg) } -func mockBundle(permissions []resources.Permission) *bundle.Bundle { +func mockBundle(permissions []resources.Permission[iam.PermissionLevel]) *bundle.Bundle { return &bundle.Bundle{ Config: config.Root{ Workspace: config.Workspace{ diff --git a/bundle/permissions/permission_report_test.go b/bundle/permissions/permission_report_test.go index 52437aacdf..b3810dae51 100644 --- a/bundle/permissions/permission_report_test.go +++ b/bundle/permissions/permission_report_test.go @@ -5,11 +5,12 @@ import ( "github.com/databricks/cli/bundle/config/resources" "github.com/databricks/cli/bundle/permissions" + "github.com/databricks/databricks-sdk-go/service/iam" "github.com/stretchr/testify/require" ) func TestPermissionsReportPermissionDeniedWithGroup(t *testing.T) { - b := mockBundle([]resources.Permission{ + b := mockBundle([]resources.Permission[iam.PermissionLevel]{ {Level: "CAN_MANAGE", GroupName: "testgroup"}, }) @@ -22,7 +23,7 @@ func TestPermissionsReportPermissionDeniedWithGroup(t *testing.T) { } func TestPermissionsReportPermissionDeniedWithOtherGroup(t *testing.T) { - b := mockBundle([]resources.Permission{ + b := mockBundle([]resources.Permission[iam.PermissionLevel]{ {Level: "CAN_MANAGE", GroupName: "othergroup"}, }) @@ -36,7 +37,7 @@ func TestPermissionsReportPermissionDeniedWithOtherGroup(t *testing.T) { } func TestPermissionsReportPermissionDeniedWithoutPermission(t *testing.T) { - b := mockBundle([]resources.Permission{ + b := mockBundle([]resources.Permission[iam.PermissionLevel]{ {Level: "CAN_VIEW", UserName: "testuser@databricks.com"}, }) @@ -62,7 +63,7 @@ func TestPermissionsReportPermissionDeniedNilPermission(t *testing.T) { } func TestPermissionsReportFindOtherOwners(t *testing.T) { - b := mockBundle([]resources.Permission{ + b := mockBundle([]resources.Permission[iam.PermissionLevel]{ {Level: "CAN_MANAGE", GroupName: "testgroup"}, {Level: "CAN_MANAGE", UserName: "alice@databricks.com"}, }) diff --git a/bundle/permissions/terraform_errors_test.go b/bundle/permissions/terraform_errors_test.go index 1ad008e251..bf5b136a5f 100644 --- a/bundle/permissions/terraform_errors_test.go +++ b/bundle/permissions/terraform_errors_test.go @@ -6,13 +6,14 @@ import ( "github.com/databricks/cli/bundle/config/resources" "github.com/databricks/cli/bundle/permissions" + "github.com/databricks/databricks-sdk-go/service/iam" "github.com/databricks/databricks-sdk-go/service/jobs" "github.com/stretchr/testify/require" ) func TestTryExtendTerraformPermissionError1(t *testing.T) { ctx := t.Context() - b := mockBundle([]resources.Permission{ + b := mockBundle([]resources.Permission[iam.PermissionLevel]{ {Level: "CAN_MANAGE", UserName: "alice@databricks.com"}, }) err := permissions.TryExtendTerraformPermissionError(ctx, b, errors.New("Error: terraform apply: exit status 1\n"+ @@ -33,7 +34,7 @@ func TestTryExtendTerraformPermissionError1(t *testing.T) { func TestTryExtendTerraformPermissionError2(t *testing.T) { ctx := t.Context() - b := mockBundle([]resources.Permission{ + b := mockBundle([]resources.Permission[iam.PermissionLevel]{ {Level: "CAN_MANAGE", UserName: "alice@databricks.com"}, {Level: "CAN_MANAGE", UserName: "bob@databricks.com"}, }) @@ -54,7 +55,7 @@ func TestTryExtendTerraformPermissionError2(t *testing.T) { func TestTryExtendTerraformPermissionError3(t *testing.T) { ctx := t.Context() - b := mockBundle([]resources.Permission{ + b := mockBundle([]resources.Permission[iam.PermissionLevel]{ {Level: "CAN_MANAGE", UserName: "testuser@databricks.com"}, }) err := permissions.TryExtendTerraformPermissionError(ctx, b, errors.New("Error: terraform apply: exit status 1\n"+ @@ -74,7 +75,7 @@ func TestTryExtendTerraformPermissionError3(t *testing.T) { func TestTryExtendTerraformPermissionErrorNotOwner(t *testing.T) { ctx := t.Context() - b := mockBundle([]resources.Permission{ + b := mockBundle([]resources.Permission[iam.PermissionLevel]{ {Level: "CAN_MANAGE", GroupName: "data_team@databricks.com"}, }) b.Config.RunAs = &jobs.JobRunAs{ diff --git a/bundle/permissions/validate_test.go b/bundle/permissions/validate_test.go index 5cd3f05104..b5406df950 100644 --- a/bundle/permissions/validate_test.go +++ b/bundle/permissions/validate_test.go @@ -8,6 +8,7 @@ import ( "github.com/databricks/cli/bundle/config/resources" "github.com/databricks/cli/libs/diag" "github.com/databricks/databricks-sdk-go/experimental/mocks" + "github.com/databricks/databricks-sdk-go/service/iam" "github.com/databricks/databricks-sdk-go/service/jobs" "github.com/stretchr/testify/require" ) @@ -18,7 +19,7 @@ func TestValidateSharedRootPermissionsForShared(t *testing.T) { Workspace: config.Workspace{ RootPath: "/Workspace/Shared/foo/bar", }, - Permissions: []resources.Permission{ + Permissions: []resources.Permission[iam.PermissionLevel]{ {Level: CAN_MANAGE, GroupName: "users"}, }, Resources: config.Resources{ @@ -43,7 +44,7 @@ func TestValidateSharedRootPermissionsForSharedError(t *testing.T) { Workspace: config.Workspace{ RootPath: "/Workspace/Shared/foo/bar", }, - Permissions: []resources.Permission{ + Permissions: []resources.Permission[iam.PermissionLevel]{ {Level: CAN_MANAGE, UserName: "foo@bar.com"}, }, Resources: config.Resources{ diff --git a/bundle/permissions/workspace_path_permissions.go b/bundle/permissions/workspace_path_permissions.go index cabf4117f6..94d2eb1e43 100644 --- a/bundle/permissions/workspace_path_permissions.go +++ b/bundle/permissions/workspace_path_permissions.go @@ -6,16 +6,17 @@ import ( "github.com/databricks/cli/bundle/config/resources" "github.com/databricks/cli/libs/diag" + "github.com/databricks/databricks-sdk-go/service/iam" "github.com/databricks/databricks-sdk-go/service/workspace" ) type WorkspacePathPermissions struct { Path string - Permissions []resources.Permission + Permissions []resources.Permission[iam.PermissionLevel] } func ObjectAclToResourcePermissions(path string, acl []workspace.WorkspaceObjectAccessControlResponse) *WorkspacePathPermissions { - var permissions []resources.Permission + var permissions []resources.Permission[iam.PermissionLevel] for _, a := range acl { // Skip the admin group because it's added to all resources by default. if a.GroupName == "admins" { @@ -23,16 +24,16 @@ func ObjectAclToResourcePermissions(path string, acl []workspace.WorkspaceObject } // Find the highest permission level for this principal (handles inherited + explicit permissions) - var highestLevel string + var highestLevel iam.PermissionLevel for _, pl := range a.AllPermissions { - level := convertWorkspaceObjectPermissionLevel(pl.PermissionLevel) - if resources.GetLevelScore(level) > resources.GetLevelScore(highestLevel) { + level := iam.PermissionLevel(convertWorkspaceObjectPermissionLevel(pl.PermissionLevel)) + if resources.GetLevelScore(string(level)) > resources.GetLevelScore(string(highestLevel)) { highestLevel = level } } if highestLevel != "" { - permissions = append(permissions, resources.Permission{ + permissions = append(permissions, resources.Permission[iam.PermissionLevel]{ Level: highestLevel, GroupName: a.GroupName, UserName: a.UserName, @@ -44,7 +45,7 @@ func ObjectAclToResourcePermissions(path string, acl []workspace.WorkspaceObject return &WorkspacePathPermissions{Permissions: permissions, Path: path} } -func (p WorkspacePathPermissions) Compare(perms []resources.Permission) diag.Diagnostics { +func (p WorkspacePathPermissions) Compare(perms []resources.Permission[iam.PermissionLevel]) diag.Diagnostics { var diags diag.Diagnostics // Check the permissions in the workspace and see if they are all set in the bundle. @@ -66,7 +67,7 @@ func (p WorkspacePathPermissions) Compare(perms []resources.Permission) diag.Dia } // samePrincipal checks if two permissions refer to the same user/group/service principal. -func samePrincipal(a, b resources.Permission) bool { +func samePrincipal(a, b resources.Permission[iam.PermissionLevel]) bool { return a.UserName == b.UserName && a.GroupName == b.GroupName && a.ServicePrincipalName == b.ServicePrincipalName @@ -75,12 +76,12 @@ func samePrincipal(a, b resources.Permission) bool { // containsAll checks if all permissions in permA (workspace) are accounted for in permB (bundle). // A workspace permission is considered accounted for if the bundle has the same principal // with an equal or higher permission level. -func containsAll(permA, permB []resources.Permission) (bool, []resources.Permission) { - var missing []resources.Permission +func containsAll(permA, permB []resources.Permission[iam.PermissionLevel]) (bool, []resources.Permission[iam.PermissionLevel]) { + var missing []resources.Permission[iam.PermissionLevel] for _, a := range permA { found := false for _, b := range permB { - if samePrincipal(a, b) && resources.GetLevelScore(b.Level) >= resources.GetLevelScore(a.Level) { + if samePrincipal(a, b) && resources.GetLevelScore(string(b.Level)) >= resources.GetLevelScore(string(a.Level)) { found = true break } @@ -103,7 +104,7 @@ func convertWorkspaceObjectPermissionLevel(level workspace.WorkspaceObjectPermis } } -func toString(p []resources.Permission) string { +func toString(p []resources.Permission[iam.PermissionLevel]) string { var sb strings.Builder for _, perm := range p { sb.WriteString(fmt.Sprintf("- %s\n", perm.String())) diff --git a/bundle/permissions/workspace_path_permissions_test.go b/bundle/permissions/workspace_path_permissions_test.go index df2f86a8a8..d34a9b6dd0 100644 --- a/bundle/permissions/workspace_path_permissions_test.go +++ b/bundle/permissions/workspace_path_permissions_test.go @@ -5,18 +5,19 @@ import ( "github.com/databricks/cli/bundle/config/resources" "github.com/databricks/cli/libs/diag" + "github.com/databricks/databricks-sdk-go/service/iam" "github.com/databricks/databricks-sdk-go/service/workspace" "github.com/stretchr/testify/require" ) func TestWorkspacePathPermissionsCompare(t *testing.T) { testCases := []struct { - perms []resources.Permission + perms []resources.Permission[iam.PermissionLevel] acl []workspace.WorkspaceObjectAccessControlResponse expected diag.Diagnostics }{ { - perms: []resources.Permission{ + perms: []resources.Permission[iam.PermissionLevel]{ {Level: CAN_MANAGE, UserName: "foo@bar.com"}, }, acl: []workspace.WorkspaceObjectAccessControlResponse{ @@ -30,7 +31,7 @@ func TestWorkspacePathPermissionsCompare(t *testing.T) { expected: nil, }, { - perms: []resources.Permission{ + perms: []resources.Permission[iam.PermissionLevel]{ {Level: CAN_MANAGE, UserName: "foo@bar.com"}, }, acl: []workspace.WorkspaceObjectAccessControlResponse{ @@ -50,7 +51,7 @@ func TestWorkspacePathPermissionsCompare(t *testing.T) { expected: nil, }, { - perms: []resources.Permission{ + perms: []resources.Permission[iam.PermissionLevel]{ {Level: CAN_VIEW, UserName: "foo@bar.com"}, {Level: CAN_MANAGE, ServicePrincipalName: "sp.com"}, }, @@ -65,7 +66,7 @@ func TestWorkspacePathPermissionsCompare(t *testing.T) { expected: nil, }, { - perms: []resources.Permission{ + perms: []resources.Permission[iam.PermissionLevel]{ {Level: CAN_MANAGE, UserName: "foo@bar.com"}, }, acl: []workspace.WorkspaceObjectAccessControlResponse{ @@ -94,7 +95,7 @@ func TestWorkspacePathPermissionsCompare(t *testing.T) { }, }, { - perms: []resources.Permission{ + perms: []resources.Permission[iam.PermissionLevel]{ {Level: CAN_MANAGE, UserName: "foo@bar.com"}, }, acl: []workspace.WorkspaceObjectAccessControlResponse{ @@ -128,13 +129,13 @@ func TestWorkspacePathPermissionsCompare(t *testing.T) { func TestWorkspacePathPermissionsCompareWithHierarchy(t *testing.T) { testCases := []struct { name string - perms []resources.Permission + perms []resources.Permission[iam.PermissionLevel] acl []workspace.WorkspaceObjectAccessControlResponse expected diag.Diagnostics }{ { name: "bundle grants higher permission than workspace - no warning", - perms: []resources.Permission{ + perms: []resources.Permission[iam.PermissionLevel]{ {Level: CAN_MANAGE, UserName: "foo@bar.com"}, }, acl: []workspace.WorkspaceObjectAccessControlResponse{ @@ -149,7 +150,7 @@ func TestWorkspacePathPermissionsCompareWithHierarchy(t *testing.T) { }, { name: "bundle grants lower permission than workspace - warning", - perms: []resources.Permission{ + perms: []resources.Permission[iam.PermissionLevel]{ {Level: CAN_VIEW, UserName: "foo@bar.com"}, }, acl: []workspace.WorkspaceObjectAccessControlResponse{ @@ -173,7 +174,7 @@ func TestWorkspacePathPermissionsCompareWithHierarchy(t *testing.T) { }, { name: "bundle grants same permission as workspace - no warning", - perms: []resources.Permission{ + perms: []resources.Permission[iam.PermissionLevel]{ {Level: CAN_MANAGE, UserName: "foo@bar.com"}, }, acl: []workspace.WorkspaceObjectAccessControlResponse{ @@ -213,6 +214,6 @@ func TestWorkspacePathPermissionsDeduplication(t *testing.T) { // Should only have one permission entry with the highest level require.Len(t, wp.Permissions, 1) - require.Equal(t, CAN_MANAGE, wp.Permissions[0].Level) + require.Equal(t, iam.PermissionLevel(CAN_MANAGE), wp.Permissions[0].Level) require.Equal(t, "foo@bar.com", wp.Permissions[0].UserName) } diff --git a/bundle/permissions/workspace_root.go b/bundle/permissions/workspace_root.go index 0f307fc546..e8fc82813d 100644 --- a/bundle/permissions/workspace_root.go +++ b/bundle/permissions/workspace_root.go @@ -37,7 +37,7 @@ func giveAccessForWorkspaceRoot(ctx context.Context, b *bundle.Bundle) error { var permissions []workspace.WorkspaceObjectAccessControlRequest for _, p := range b.Config.Permissions { - level, err := GetWorkspaceObjectPermissionLevel(p.Level) + level, err := GetWorkspaceObjectPermissionLevel(string(p.Level)) if err != nil { return err } diff --git a/bundle/permissions/workspace_root_test.go b/bundle/permissions/workspace_root_test.go index 1dd1c0cbfa..1b16f7802b 100644 --- a/bundle/permissions/workspace_root_test.go +++ b/bundle/permissions/workspace_root_test.go @@ -7,6 +7,7 @@ import ( "github.com/databricks/cli/bundle/config" "github.com/databricks/cli/bundle/config/resources" "github.com/databricks/databricks-sdk-go/experimental/mocks" + "github.com/databricks/databricks-sdk-go/service/iam" "github.com/databricks/databricks-sdk-go/service/jobs" "github.com/databricks/databricks-sdk-go/service/ml" "github.com/databricks/databricks-sdk-go/service/pipelines" @@ -26,7 +27,7 @@ func TestApplyWorkspaceRootPermissions(t *testing.T) { StatePath: "/Users/foo@bar.com/state", ResourcePath: "/Users/foo@bar.com/resources", }, - Permissions: []resources.Permission{ + Permissions: []resources.Permission[iam.PermissionLevel]{ {Level: CAN_MANAGE, UserName: "TestUser"}, {Level: CAN_VIEW, GroupName: "TestGroup"}, {Level: CAN_RUN, ServicePrincipalName: "TestServicePrincipal"}, @@ -86,7 +87,7 @@ func TestApplyWorkspaceRootPermissionsForAllPaths(t *testing.T) { StatePath: "/Users/foo@bar.com/state", ResourcePath: "/Users/foo@bar.com/resources", }, - Permissions: []resources.Permission{ + Permissions: []resources.Permission[iam.PermissionLevel]{ {Level: CAN_MANAGE, UserName: "TestUser"}, {Level: CAN_VIEW, GroupName: "TestGroup"}, {Level: CAN_RUN, ServicePrincipalName: "TestServicePrincipal"}, diff --git a/bundle/schema/jsonschema.json b/bundle/schema/jsonschema.json index 7445d4d48a..3376e134f4 100644 --- a/bundle/schema/jsonschema.json +++ b/bundle/schema/jsonschema.json @@ -86,7 +86,7 @@ "$ref": "#/$defs/string" }, "permissions": { - "$ref": "#/$defs/slice/github.com/databricks/cli/bundle/config/resources.Permission" + "$ref": "#/$defs/slice/github.com/databricks/cli/bundle/config/resources.Permission[github.com/databricks/databricks-sdk-go/service/iam.PermissionLevel]" }, "query_text": { "$ref": "#/$defs/string" @@ -158,7 +158,7 @@ "$ref": "#/$defs/string" }, "permissions": { - "$ref": "#/$defs/slice/github.com/databricks/cli/bundle/config/resources.Permission" + "$ref": "#/$defs/slice/github.com/databricks/cli/bundle/config/resources.Permission[github.com/databricks/databricks-sdk-go/service/apps.AppPermissionLevel]" }, "resources": { "description": "Resources for the app.", @@ -380,7 +380,7 @@ "$ref": "#/$defs/int" }, "permissions": { - "$ref": "#/$defs/slice/github.com/databricks/cli/bundle/config/resources.Permission" + "$ref": "#/$defs/slice/github.com/databricks/cli/bundle/config/resources.Permission[github.com/databricks/databricks-sdk-go/service/compute.ClusterPermissionLevel]" }, "policy_id": { "description": "The ID of the cluster policy used to create the cluster if applicable.", @@ -490,7 +490,7 @@ "$ref": "#/$defs/string" }, "permissions": { - "$ref": "#/$defs/slice/github.com/databricks/cli/bundle/config/resources.Permission" + "$ref": "#/$defs/slice/github.com/databricks/cli/bundle/config/resources.Permission[github.com/databricks/databricks-sdk-go/service/iam.PermissionLevel]" }, "serialized_dashboard": { "description": "The contents of the dashboard in serialized string form.\nThis field is excluded in List Dashboards responses.\nUse the [get dashboard API](https://docs.databricks.com/api/workspace/lakeview/get)\nto retrieve an example response, which includes the `serialized_dashboard` field.\nThis field provides the structure of the JSON string that represents the dashboard's\nlayout and components.", @@ -591,7 +591,7 @@ "$ref": "#/$defs/github.com/databricks/databricks-sdk-go/service/database.DatabaseInstanceRef" }, "permissions": { - "$ref": "#/$defs/slice/github.com/databricks/cli/bundle/config/resources.Permission" + "$ref": "#/$defs/slice/github.com/databricks/cli/bundle/config/resources.Permission[github.com/databricks/databricks-sdk-go/service/iam.PermissionLevel]" }, "retention_window_in_days": { "description": "The retention window for the instance. This is the time window in days\nfor which the historical data is retained. The default value is 7 days.\nValid values are 2 to 35 days.", @@ -733,7 +733,7 @@ "$ref": "#/$defs/github.com/databricks/databricks-sdk-go/service/jobs.PerformanceTarget" }, "permissions": { - "$ref": "#/$defs/slice/github.com/databricks/cli/bundle/config/resources.Permission" + "$ref": "#/$defs/slice/github.com/databricks/cli/bundle/config/resources.Permission[github.com/databricks/databricks-sdk-go/service/jobs.JobPermissionLevel]" }, "queue": { "description": "The queue settings of the job.", @@ -818,7 +818,7 @@ "$ref": "#/$defs/string" }, "permissions": { - "$ref": "#/$defs/slice/github.com/databricks/cli/bundle/config/resources.Permission" + "$ref": "#/$defs/slice/github.com/databricks/cli/bundle/config/resources.Permission[github.com/databricks/databricks-sdk-go/service/ml.ExperimentPermissionLevel]" }, "tags": { "description": "A collection of tags to set on the experiment. Maximum tag size and number of tags per request\ndepends on the storage backend. All storage backends are guaranteed to support tag keys up\nto 250 bytes in size and tag values up to 5000 bytes in size. All storage backends are also\nguaranteed to support up to 20 tags per request.", @@ -855,7 +855,7 @@ "$ref": "#/$defs/string" }, "permissions": { - "$ref": "#/$defs/slice/github.com/databricks/cli/bundle/config/resources.Permission" + "$ref": "#/$defs/slice/github.com/databricks/cli/bundle/config/resources.Permission[github.com/databricks/databricks-sdk-go/service/ml.RegisteredModelPermissionLevel]" }, "tags": { "description": "Additional metadata for registered model.", @@ -907,7 +907,7 @@ "$ref": "#/$defs/string" }, "permissions": { - "$ref": "#/$defs/slice/github.com/databricks/cli/bundle/config/resources.Permission" + "$ref": "#/$defs/slice/github.com/databricks/cli/bundle/config/resources.Permission[github.com/databricks/databricks-sdk-go/service/serving.ServingEndpointPermissionLevel]" }, "rate_limits": { "description": "Rate limits to be applied to the serving endpoint. NOTE: this field is deprecated, please use AI Gateway to manage rate limits.", @@ -936,38 +936,310 @@ } ] }, - "resources.Permission": { - "oneOf": [ - { - "type": "object", - "properties": { - "group_name": { - "description": "The name of the group that has the permission set in level.", - "$ref": "#/$defs/string" - }, - "level": { - "description": "The allowed permission for user, group, service principal defined for this permission.", - "$ref": "#/$defs/string" - }, - "service_principal_name": { - "description": "The name of the service principal that has the permission set in level.", - "$ref": "#/$defs/string" - }, - "user_name": { - "description": "The name of the user that has the permission set in level.", - "$ref": "#/$defs/string" + "resources.Permission[github.com": { + "databricks": { + "databricks-sdk-go": { + "service": { + "apps.AppPermissionLevel]": { + "oneOf": [ + { + "type": "object", + "properties": { + "group_name": { + "description": "The name of the group that has the permission set in level.", + "$ref": "#/$defs/string" + }, + "level": { + "description": "The allowed permission for user, group, service principal defined for this permission.", + "$ref": "#/$defs/github.com/databricks/databricks-sdk-go/service/apps.AppPermissionLevel" + }, + "service_principal_name": { + "description": "The name of the service principal that has the permission set in level.", + "$ref": "#/$defs/string" + }, + "user_name": { + "description": "The name of the user that has the permission set in level.", + "$ref": "#/$defs/string" + } + }, + "additionalProperties": false, + "required": [ + "level" + ] + }, + { + "type": "string", + "pattern": "\\$\\{(var(\\.[a-zA-Z]+([-_]?[a-zA-Z0-9]+)*(\\[[0-9]+\\])*)+)\\}" + } + ] + }, + "compute.ClusterPermissionLevel]": { + "oneOf": [ + { + "type": "object", + "properties": { + "group_name": { + "description": "The name of the group that has the permission set in level.", + "$ref": "#/$defs/string" + }, + "level": { + "description": "The allowed permission for user, group, service principal defined for this permission.", + "$ref": "#/$defs/github.com/databricks/databricks-sdk-go/service/compute.ClusterPermissionLevel" + }, + "service_principal_name": { + "description": "The name of the service principal that has the permission set in level.", + "$ref": "#/$defs/string" + }, + "user_name": { + "description": "The name of the user that has the permission set in level.", + "$ref": "#/$defs/string" + } + }, + "additionalProperties": false, + "required": [ + "level" + ] + }, + { + "type": "string", + "pattern": "\\$\\{(var(\\.[a-zA-Z]+([-_]?[a-zA-Z0-9]+)*(\\[[0-9]+\\])*)+)\\}" + } + ] + }, + "iam.PermissionLevel]": { + "oneOf": [ + { + "type": "object", + "properties": { + "group_name": { + "description": "The name of the group that has the permission set in level.", + "$ref": "#/$defs/string" + }, + "level": { + "description": "The allowed permission for user, group, service principal defined for this permission.", + "$ref": "#/$defs/github.com/databricks/databricks-sdk-go/service/iam.PermissionLevel" + }, + "service_principal_name": { + "description": "The name of the service principal that has the permission set in level.", + "$ref": "#/$defs/string" + }, + "user_name": { + "description": "The name of the user that has the permission set in level.", + "$ref": "#/$defs/string" + } + }, + "additionalProperties": false, + "required": [ + "level" + ] + }, + { + "type": "string", + "pattern": "\\$\\{(var(\\.[a-zA-Z]+([-_]?[a-zA-Z0-9]+)*(\\[[0-9]+\\])*)+)\\}" + } + ] + }, + "jobs.JobPermissionLevel]": { + "oneOf": [ + { + "type": "object", + "properties": { + "group_name": { + "description": "The name of the group that has the permission set in level.", + "$ref": "#/$defs/string" + }, + "level": { + "description": "The allowed permission for user, group, service principal defined for this permission.", + "$ref": "#/$defs/github.com/databricks/databricks-sdk-go/service/jobs.JobPermissionLevel" + }, + "service_principal_name": { + "description": "The name of the service principal that has the permission set in level.", + "$ref": "#/$defs/string" + }, + "user_name": { + "description": "The name of the user that has the permission set in level.", + "$ref": "#/$defs/string" + } + }, + "additionalProperties": false, + "required": [ + "level" + ] + }, + { + "type": "string", + "pattern": "\\$\\{(var(\\.[a-zA-Z]+([-_]?[a-zA-Z0-9]+)*(\\[[0-9]+\\])*)+)\\}" + } + ] + }, + "ml.ExperimentPermissionLevel]": { + "oneOf": [ + { + "type": "object", + "properties": { + "group_name": { + "description": "The name of the group that has the permission set in level.", + "$ref": "#/$defs/string" + }, + "level": { + "description": "The allowed permission for user, group, service principal defined for this permission.", + "$ref": "#/$defs/github.com/databricks/databricks-sdk-go/service/ml.ExperimentPermissionLevel" + }, + "service_principal_name": { + "description": "The name of the service principal that has the permission set in level.", + "$ref": "#/$defs/string" + }, + "user_name": { + "description": "The name of the user that has the permission set in level.", + "$ref": "#/$defs/string" + } + }, + "additionalProperties": false, + "required": [ + "level" + ] + }, + { + "type": "string", + "pattern": "\\$\\{(var(\\.[a-zA-Z]+([-_]?[a-zA-Z0-9]+)*(\\[[0-9]+\\])*)+)\\}" + } + ] + }, + "ml.RegisteredModelPermissionLevel]": { + "oneOf": [ + { + "type": "object", + "properties": { + "group_name": { + "description": "The name of the group that has the permission set in level.", + "$ref": "#/$defs/string" + }, + "level": { + "description": "The allowed permission for user, group, service principal defined for this permission.", + "$ref": "#/$defs/github.com/databricks/databricks-sdk-go/service/ml.RegisteredModelPermissionLevel" + }, + "service_principal_name": { + "description": "The name of the service principal that has the permission set in level.", + "$ref": "#/$defs/string" + }, + "user_name": { + "description": "The name of the user that has the permission set in level.", + "$ref": "#/$defs/string" + } + }, + "additionalProperties": false, + "required": [ + "level" + ] + }, + { + "type": "string", + "pattern": "\\$\\{(var(\\.[a-zA-Z]+([-_]?[a-zA-Z0-9]+)*(\\[[0-9]+\\])*)+)\\}" + } + ] + }, + "pipelines.PipelinePermissionLevel]": { + "oneOf": [ + { + "type": "object", + "properties": { + "group_name": { + "description": "The name of the group that has the permission set in level.", + "$ref": "#/$defs/string" + }, + "level": { + "description": "The allowed permission for user, group, service principal defined for this permission.", + "$ref": "#/$defs/github.com/databricks/databricks-sdk-go/service/pipelines.PipelinePermissionLevel" + }, + "service_principal_name": { + "description": "The name of the service principal that has the permission set in level.", + "$ref": "#/$defs/string" + }, + "user_name": { + "description": "The name of the user that has the permission set in level.", + "$ref": "#/$defs/string" + } + }, + "additionalProperties": false, + "required": [ + "level" + ] + }, + { + "type": "string", + "pattern": "\\$\\{(var(\\.[a-zA-Z]+([-_]?[a-zA-Z0-9]+)*(\\[[0-9]+\\])*)+)\\}" + } + ] + }, + "serving.ServingEndpointPermissionLevel]": { + "oneOf": [ + { + "type": "object", + "properties": { + "group_name": { + "description": "The name of the group that has the permission set in level.", + "$ref": "#/$defs/string" + }, + "level": { + "description": "The allowed permission for user, group, service principal defined for this permission.", + "$ref": "#/$defs/github.com/databricks/databricks-sdk-go/service/serving.ServingEndpointPermissionLevel" + }, + "service_principal_name": { + "description": "The name of the service principal that has the permission set in level.", + "$ref": "#/$defs/string" + }, + "user_name": { + "description": "The name of the user that has the permission set in level.", + "$ref": "#/$defs/string" + } + }, + "additionalProperties": false, + "required": [ + "level" + ] + }, + { + "type": "string", + "pattern": "\\$\\{(var(\\.[a-zA-Z]+([-_]?[a-zA-Z0-9]+)*(\\[[0-9]+\\])*)+)\\}" + } + ] + }, + "sql.WarehousePermissionLevel]": { + "oneOf": [ + { + "type": "object", + "properties": { + "group_name": { + "description": "The name of the group that has the permission set in level.", + "$ref": "#/$defs/string" + }, + "level": { + "description": "The allowed permission for user, group, service principal defined for this permission.", + "$ref": "#/$defs/github.com/databricks/databricks-sdk-go/service/sql.WarehousePermissionLevel" + }, + "service_principal_name": { + "description": "The name of the service principal that has the permission set in level.", + "$ref": "#/$defs/string" + }, + "user_name": { + "description": "The name of the user that has the permission set in level.", + "$ref": "#/$defs/string" + } + }, + "additionalProperties": false, + "required": [ + "level" + ] + }, + { + "type": "string", + "pattern": "\\$\\{(var(\\.[a-zA-Z]+([-_]?[a-zA-Z0-9]+)*(\\[[0-9]+\\])*)+)\\}" + } + ] } - }, - "additionalProperties": false, - "required": [ - "level" - ] - }, - { - "type": "string", - "pattern": "\\$\\{(var(\\.[a-zA-Z]+([-_]?[a-zA-Z0-9]+)*(\\[[0-9]+\\])*)+)\\}" + } } - ] + } }, "resources.Pipeline": { "oneOf": [ @@ -1053,7 +1325,7 @@ "$ref": "#/$defs/slice/github.com/databricks/databricks-sdk-go/service/pipelines.Notifications" }, "permissions": { - "$ref": "#/$defs/slice/github.com/databricks/cli/bundle/config/resources.Permission" + "$ref": "#/$defs/slice/github.com/databricks/cli/bundle/config/resources.Permission[github.com/databricks/databricks-sdk-go/service/pipelines.PipelinePermissionLevel]" }, "photon": { "description": "Whether Photon is enabled for this pipeline.", @@ -1243,7 +1515,7 @@ "$ref": "#/$defs/github.com/databricks/cli/bundle/config/resources.Lifecycle" }, "permissions": { - "$ref": "#/$defs/slice/github.com/databricks/cli/bundle/config/resources.Permission" + "$ref": "#/$defs/slice/github.com/databricks/cli/bundle/config/resources.Permission[github.com/databricks/databricks-sdk-go/service/iam.PermissionLevel]" }, "pg_version": { "$ref": "#/$defs/int" @@ -1600,7 +1872,7 @@ "$ref": "#/$defs/string" }, "permissions": { - "$ref": "#/$defs/slice/github.com/databricks/cli/bundle/config/resources.Permission" + "$ref": "#/$defs/slice/github.com/databricks/cli/bundle/config/resources.Permission[github.com/databricks/databricks-sdk-go/service/sql.WarehousePermissionLevel]" }, "spot_instance_policy": { "$ref": "#/$defs/github.com/databricks/databricks-sdk-go/service/sql.SpotInstancePolicy" @@ -2333,7 +2605,7 @@ }, "permissions": { "description": "The permissions for deploying and running the bundle in the target.", - "$ref": "#/$defs/slice/github.com/databricks/cli/bundle/config/resources.Permission" + "$ref": "#/$defs/slice/github.com/databricks/cli/bundle/config/resources.Permission[github.com/databricks/databricks-sdk-go/service/iam.PermissionLevel]" }, "presets": { "description": "The deployment presets for the target.", @@ -2581,6 +2853,9 @@ } ] }, + "apps.AppPermissionLevel": { + "type": "string" + }, "apps.AppResource": { "oneOf": [ { @@ -3971,6 +4246,9 @@ } ] }, + "compute.ClusterPermissionLevel": { + "type": "string" + }, "compute.ClusterSpec": { "oneOf": [ { @@ -5078,6 +5356,9 @@ } ] }, + "iam.PermissionLevel": { + "type": "string" + }, "jobs.AuthenticationMethod": { "oneOf": [ { @@ -5815,6 +6096,9 @@ } ] }, + "jobs.JobPermissionLevel": { + "type": "string" + }, "jobs.JobRunAs": { "oneOf": [ { @@ -7180,6 +7464,9 @@ } ] }, + "ml.ExperimentPermissionLevel": { + "type": "string" + }, "ml.ExperimentTag": { "oneOf": [ { @@ -7226,6 +7513,9 @@ } ] }, + "ml.RegisteredModelPermissionLevel": { + "type": "string" + }, "pipelines.AutoFullRefreshPolicy": { "oneOf": [ { @@ -7935,6 +8225,9 @@ } ] }, + "pipelines.PipelinePermissionLevel": { + "type": "string" + }, "pipelines.PipelineTrigger": { "oneOf": [ { @@ -9509,6 +9802,9 @@ } ] }, + "serving.ServingEndpointPermissionLevel": { + "type": "string" + }, "serving.ServingModelWorkloadType": { "oneOf": [ { @@ -9948,6 +10244,9 @@ } ] }, + "sql.WarehousePermissionLevel": { + "type": "string" + }, "workspace.AzureKeyVaultSecretScopeMetadata": { "oneOf": [ { @@ -10502,19 +10801,139 @@ } ] }, - "resources.Permission": { - "oneOf": [ - { - "type": "array", - "items": { - "$ref": "#/$defs/github.com/databricks/cli/bundle/config/resources.Permission" + "resources.Permission[github.com": { + "databricks": { + "databricks-sdk-go": { + "service": { + "apps.AppPermissionLevel]": { + "oneOf": [ + { + "type": "array", + "items": { + "$ref": "#/$defs/github.com/databricks/cli/bundle/config/resources.Permission[github.com/databricks/databricks-sdk-go/service/apps.AppPermissionLevel]" + } + }, + { + "type": "string", + "pattern": "\\$\\{(var(\\.[a-zA-Z]+([-_]?[a-zA-Z0-9]+)*(\\[[0-9]+\\])*)+)\\}" + } + ] + }, + "compute.ClusterPermissionLevel]": { + "oneOf": [ + { + "type": "array", + "items": { + "$ref": "#/$defs/github.com/databricks/cli/bundle/config/resources.Permission[github.com/databricks/databricks-sdk-go/service/compute.ClusterPermissionLevel]" + } + }, + { + "type": "string", + "pattern": "\\$\\{(var(\\.[a-zA-Z]+([-_]?[a-zA-Z0-9]+)*(\\[[0-9]+\\])*)+)\\}" + } + ] + }, + "iam.PermissionLevel]": { + "oneOf": [ + { + "type": "array", + "items": { + "$ref": "#/$defs/github.com/databricks/cli/bundle/config/resources.Permission[github.com/databricks/databricks-sdk-go/service/iam.PermissionLevel]" + } + }, + { + "type": "string", + "pattern": "\\$\\{(var(\\.[a-zA-Z]+([-_]?[a-zA-Z0-9]+)*(\\[[0-9]+\\])*)+)\\}" + } + ] + }, + "jobs.JobPermissionLevel]": { + "oneOf": [ + { + "type": "array", + "items": { + "$ref": "#/$defs/github.com/databricks/cli/bundle/config/resources.Permission[github.com/databricks/databricks-sdk-go/service/jobs.JobPermissionLevel]" + } + }, + { + "type": "string", + "pattern": "\\$\\{(var(\\.[a-zA-Z]+([-_]?[a-zA-Z0-9]+)*(\\[[0-9]+\\])*)+)\\}" + } + ] + }, + "ml.ExperimentPermissionLevel]": { + "oneOf": [ + { + "type": "array", + "items": { + "$ref": "#/$defs/github.com/databricks/cli/bundle/config/resources.Permission[github.com/databricks/databricks-sdk-go/service/ml.ExperimentPermissionLevel]" + } + }, + { + "type": "string", + "pattern": "\\$\\{(var(\\.[a-zA-Z]+([-_]?[a-zA-Z0-9]+)*(\\[[0-9]+\\])*)+)\\}" + } + ] + }, + "ml.RegisteredModelPermissionLevel]": { + "oneOf": [ + { + "type": "array", + "items": { + "$ref": "#/$defs/github.com/databricks/cli/bundle/config/resources.Permission[github.com/databricks/databricks-sdk-go/service/ml.RegisteredModelPermissionLevel]" + } + }, + { + "type": "string", + "pattern": "\\$\\{(var(\\.[a-zA-Z]+([-_]?[a-zA-Z0-9]+)*(\\[[0-9]+\\])*)+)\\}" + } + ] + }, + "pipelines.PipelinePermissionLevel]": { + "oneOf": [ + { + "type": "array", + "items": { + "$ref": "#/$defs/github.com/databricks/cli/bundle/config/resources.Permission[github.com/databricks/databricks-sdk-go/service/pipelines.PipelinePermissionLevel]" + } + }, + { + "type": "string", + "pattern": "\\$\\{(var(\\.[a-zA-Z]+([-_]?[a-zA-Z0-9]+)*(\\[[0-9]+\\])*)+)\\}" + } + ] + }, + "serving.ServingEndpointPermissionLevel]": { + "oneOf": [ + { + "type": "array", + "items": { + "$ref": "#/$defs/github.com/databricks/cli/bundle/config/resources.Permission[github.com/databricks/databricks-sdk-go/service/serving.ServingEndpointPermissionLevel]" + } + }, + { + "type": "string", + "pattern": "\\$\\{(var(\\.[a-zA-Z]+([-_]?[a-zA-Z0-9]+)*(\\[[0-9]+\\])*)+)\\}" + } + ] + }, + "sql.WarehousePermissionLevel]": { + "oneOf": [ + { + "type": "array", + "items": { + "$ref": "#/$defs/github.com/databricks/cli/bundle/config/resources.Permission[github.com/databricks/databricks-sdk-go/service/sql.WarehousePermissionLevel]" + } + }, + { + "type": "string", + "pattern": "\\$\\{(var(\\.[a-zA-Z]+([-_]?[a-zA-Z0-9]+)*(\\[[0-9]+\\])*)+)\\}" + } + ] + } } - }, - { - "type": "string", - "pattern": "\\$\\{(var(\\.[a-zA-Z]+([-_]?[a-zA-Z0-9]+)*(\\[[0-9]+\\])*)+)\\}" } - ] + } }, "resources.SecretScopePermission": { "oneOf": [ @@ -11132,7 +11551,7 @@ }, "permissions": { "description": "Defines a permission for a specific entity.", - "$ref": "#/$defs/slice/github.com/databricks/cli/bundle/config/resources.Permission", + "$ref": "#/$defs/slice/github.com/databricks/cli/bundle/config/resources.Permission[github.com/databricks/databricks-sdk-go/service/iam.PermissionLevel]", "markdownDescription": "A Sequence that defines the permissions to apply to experiments, jobs, pipelines, and models defined in the bundle, where each item in the sequence is a permission for a specific entity.\n\nSee [permissions](https://docs.databricks.com/dev-tools/bundles/settings.html#permissions) and [link](https://docs.databricks.com/dev-tools/bundles/permissions.html)." }, "presets": { diff --git a/bundle/tests/bundle_permissions_test.go b/bundle/tests/bundle_permissions_test.go index 9365208246..66cdd2db3f 100644 --- a/bundle/tests/bundle_permissions_test.go +++ b/bundle/tests/bundle_permissions_test.go @@ -7,52 +7,55 @@ import ( "github.com/databricks/cli/bundle" "github.com/databricks/cli/bundle/config/resources" + "github.com/databricks/databricks-sdk-go/service/iam" + "github.com/databricks/databricks-sdk-go/service/jobs" + "github.com/databricks/databricks-sdk-go/service/pipelines" "github.com/stretchr/testify/assert" "github.com/stretchr/testify/require" ) func TestBundlePermissions(t *testing.T) { b := load(t, "./bundle_permissions") - assert.Contains(t, b.Config.Permissions, resources.Permission{Level: "CAN_RUN", UserName: "test@company.com"}) - assert.NotContains(t, b.Config.Permissions, resources.Permission{Level: "CAN_MANAGE", GroupName: "devs"}) - assert.NotContains(t, b.Config.Permissions, resources.Permission{Level: "CAN_VIEW", ServicePrincipalName: "1234-abcd"}) - assert.NotContains(t, b.Config.Permissions, resources.Permission{Level: "CAN_RUN", UserName: "bot@company.com"}) + assert.Contains(t, b.Config.Permissions, resources.Permission[iam.PermissionLevel]{Level: "CAN_RUN", UserName: "test@company.com"}) + assert.NotContains(t, b.Config.Permissions, resources.Permission[iam.PermissionLevel]{Level: "CAN_MANAGE", GroupName: "devs"}) + assert.NotContains(t, b.Config.Permissions, resources.Permission[iam.PermissionLevel]{Level: "CAN_VIEW", ServicePrincipalName: "1234-abcd"}) + assert.NotContains(t, b.Config.Permissions, resources.Permission[iam.PermissionLevel]{Level: "CAN_RUN", UserName: "bot@company.com"}) diags := bundle.Apply(t.Context(), b, resourcemutator.ApplyBundlePermissions()) require.NoError(t, diags.Error()) pipelinePermissions := b.Config.Resources.Pipelines["nyc_taxi_pipeline"].Permissions - assert.Contains(t, pipelinePermissions, resources.Permission{Level: "CAN_RUN", UserName: "test@company.com"}) - assert.NotContains(t, pipelinePermissions, resources.Permission{Level: "CAN_MANAGE", GroupName: "devs"}) - assert.NotContains(t, pipelinePermissions, resources.Permission{Level: "CAN_VIEW", ServicePrincipalName: "1234-abcd"}) - assert.NotContains(t, pipelinePermissions, resources.Permission{Level: "CAN_RUN", UserName: "bot@company.com"}) + assert.Contains(t, pipelinePermissions, resources.Permission[pipelines.PipelinePermissionLevel]{Level: "CAN_RUN", UserName: "test@company.com"}) + assert.NotContains(t, pipelinePermissions, resources.Permission[pipelines.PipelinePermissionLevel]{Level: "CAN_MANAGE", GroupName: "devs"}) + assert.NotContains(t, pipelinePermissions, resources.Permission[pipelines.PipelinePermissionLevel]{Level: "CAN_VIEW", ServicePrincipalName: "1234-abcd"}) + assert.NotContains(t, pipelinePermissions, resources.Permission[pipelines.PipelinePermissionLevel]{Level: "CAN_RUN", UserName: "bot@company.com"}) jobsPermissions := b.Config.Resources.Jobs["pipeline_schedule"].Permissions - assert.Contains(t, jobsPermissions, resources.Permission{Level: "CAN_MANAGE_RUN", UserName: "test@company.com"}) - assert.NotContains(t, jobsPermissions, resources.Permission{Level: "CAN_MANAGE", GroupName: "devs"}) - assert.NotContains(t, jobsPermissions, resources.Permission{Level: "CAN_VIEW", ServicePrincipalName: "1234-abcd"}) - assert.NotContains(t, jobsPermissions, resources.Permission{Level: "CAN_MANAGE_RUN", UserName: "bot@company.com"}) + assert.Contains(t, jobsPermissions, resources.Permission[jobs.JobPermissionLevel]{Level: "CAN_MANAGE_RUN", UserName: "test@company.com"}) + assert.NotContains(t, jobsPermissions, resources.Permission[jobs.JobPermissionLevel]{Level: "CAN_MANAGE", GroupName: "devs"}) + assert.NotContains(t, jobsPermissions, resources.Permission[jobs.JobPermissionLevel]{Level: "CAN_VIEW", ServicePrincipalName: "1234-abcd"}) + assert.NotContains(t, jobsPermissions, resources.Permission[jobs.JobPermissionLevel]{Level: "CAN_MANAGE_RUN", UserName: "bot@company.com"}) } func TestBundlePermissionsDevTarget(t *testing.T) { b := loadTarget(t, "./bundle_permissions", "development") - assert.Contains(t, b.Config.Permissions, resources.Permission{Level: "CAN_RUN", UserName: "test@company.com"}) - assert.Contains(t, b.Config.Permissions, resources.Permission{Level: "CAN_MANAGE", GroupName: "devs"}) - assert.Contains(t, b.Config.Permissions, resources.Permission{Level: "CAN_VIEW", ServicePrincipalName: "1234-abcd"}) - assert.Contains(t, b.Config.Permissions, resources.Permission{Level: "CAN_RUN", UserName: "bot@company.com"}) + assert.Contains(t, b.Config.Permissions, resources.Permission[iam.PermissionLevel]{Level: "CAN_RUN", UserName: "test@company.com"}) + assert.Contains(t, b.Config.Permissions, resources.Permission[iam.PermissionLevel]{Level: "CAN_MANAGE", GroupName: "devs"}) + assert.Contains(t, b.Config.Permissions, resources.Permission[iam.PermissionLevel]{Level: "CAN_VIEW", ServicePrincipalName: "1234-abcd"}) + assert.Contains(t, b.Config.Permissions, resources.Permission[iam.PermissionLevel]{Level: "CAN_RUN", UserName: "bot@company.com"}) diags := bundle.Apply(t.Context(), b, resourcemutator.ApplyBundlePermissions()) require.NoError(t, diags.Error()) pipelinePermissions := b.Config.Resources.Pipelines["nyc_taxi_pipeline"].Permissions - assert.Contains(t, pipelinePermissions, resources.Permission{Level: "CAN_RUN", UserName: "test@company.com"}) - assert.Contains(t, pipelinePermissions, resources.Permission{Level: "CAN_MANAGE", GroupName: "devs"}) - assert.Contains(t, pipelinePermissions, resources.Permission{Level: "CAN_VIEW", ServicePrincipalName: "1234-abcd"}) - assert.Contains(t, pipelinePermissions, resources.Permission{Level: "CAN_RUN", UserName: "bot@company.com"}) + assert.Contains(t, pipelinePermissions, resources.Permission[pipelines.PipelinePermissionLevel]{Level: "CAN_RUN", UserName: "test@company.com"}) + assert.Contains(t, pipelinePermissions, resources.Permission[pipelines.PipelinePermissionLevel]{Level: "CAN_MANAGE", GroupName: "devs"}) + assert.Contains(t, pipelinePermissions, resources.Permission[pipelines.PipelinePermissionLevel]{Level: "CAN_VIEW", ServicePrincipalName: "1234-abcd"}) + assert.Contains(t, pipelinePermissions, resources.Permission[pipelines.PipelinePermissionLevel]{Level: "CAN_RUN", UserName: "bot@company.com"}) jobsPermissions := b.Config.Resources.Jobs["pipeline_schedule"].Permissions - assert.Contains(t, jobsPermissions, resources.Permission{Level: "CAN_MANAGE_RUN", UserName: "test@company.com"}) - assert.Contains(t, jobsPermissions, resources.Permission{Level: "CAN_MANAGE", GroupName: "devs"}) - assert.Contains(t, jobsPermissions, resources.Permission{Level: "CAN_VIEW", ServicePrincipalName: "1234-abcd"}) - assert.Contains(t, jobsPermissions, resources.Permission{Level: "CAN_MANAGE_RUN", UserName: "bot@company.com"}) + assert.Contains(t, jobsPermissions, resources.Permission[jobs.JobPermissionLevel]{Level: "CAN_MANAGE_RUN", UserName: "test@company.com"}) + assert.Contains(t, jobsPermissions, resources.Permission[jobs.JobPermissionLevel]{Level: "CAN_MANAGE", GroupName: "devs"}) + assert.Contains(t, jobsPermissions, resources.Permission[jobs.JobPermissionLevel]{Level: "CAN_VIEW", ServicePrincipalName: "1234-abcd"}) + assert.Contains(t, jobsPermissions, resources.Permission[jobs.JobPermissionLevel]{Level: "CAN_MANAGE_RUN", UserName: "bot@company.com"}) } diff --git a/bundle/tests/model_serving_endpoint_test.go b/bundle/tests/model_serving_endpoint_test.go index 900e93fa25..514f114737 100644 --- a/bundle/tests/model_serving_endpoint_test.go +++ b/bundle/tests/model_serving_endpoint_test.go @@ -5,6 +5,7 @@ import ( "github.com/databricks/cli/bundle/config" "github.com/databricks/cli/bundle/config/resources" + "github.com/databricks/databricks-sdk-go/service/serving" "github.com/stretchr/testify/assert" ) @@ -17,7 +18,7 @@ func assertExpected(t *testing.T, p *resources.ModelServingEndpoint) { assert.Equal(t, "model-name-1", p.Config.TrafficConfig.Routes[0].ServedModelName) assert.Equal(t, 100, p.Config.TrafficConfig.Routes[0].TrafficPercentage) - assert.Equal(t, resources.Permission{ + assert.Equal(t, resources.Permission[serving.ServingEndpointPermissionLevel]{ GroupName: "users", Level: "CAN_QUERY", }, p.Permissions[0]) From d0569b5e0293d2d0ed7a7d7660f73b81eedc84c5 Mon Sep 17 00:00:00 2001 From: Denis Bilenko Date: Mon, 9 Mar 2026 21:40:54 +0100 Subject: [PATCH 10/28] undo yaml --- bundle/internal/schema/annotations.yml | 230 +++++++++--------- .../schema/annotations_openapi_overrides.yml | 118 +++++++++ 2 files changed, 239 insertions(+), 109 deletions(-) diff --git a/bundle/internal/schema/annotations.yml b/bundle/internal/schema/annotations.yml index 24f952741c..2eb01cd85a 100644 --- a/bundle/internal/schema/annotations.yml +++ b/bundle/internal/schema/annotations.yml @@ -509,6 +509,19 @@ github.com/databricks/cli/bundle/config/resources.Alert: "warehouse_id": "description": |- PLACEHOLDER +github.com/databricks/cli/bundle/config/resources.AlertPermission: + "group_name": + "description": |- + PLACEHOLDER + "level": + "description": |- + PLACEHOLDER + "service_principal_name": + "description": |- + PLACEHOLDER + "user_name": + "description": |- + PLACEHOLDER github.com/databricks/cli/bundle/config/resources.App: "git_source": "description": |- @@ -532,6 +545,19 @@ github.com/databricks/cli/bundle/config/resources.AppEnvVar: "value_from": "description": |- PLACEHOLDER +github.com/databricks/cli/bundle/config/resources.AppPermission: + "group_name": + "description": |- + PLACEHOLDER + "level": + "description": |- + PLACEHOLDER + "service_principal_name": + "description": |- + PLACEHOLDER + "user_name": + "description": |- + PLACEHOLDER github.com/databricks/cli/bundle/config/resources.Catalog: "comment": "description": |- @@ -557,6 +583,19 @@ github.com/databricks/cli/bundle/config/resources.Catalog: "storage_root": "description": |- PLACEHOLDER +github.com/databricks/cli/bundle/config/resources.ClusterPermission: + "group_name": + "description": |- + PLACEHOLDER + "level": + "description": |- + PLACEHOLDER + "service_principal_name": + "description": |- + PLACEHOLDER + "user_name": + "description": |- + PLACEHOLDER github.com/databricks/cli/bundle/config/resources.Dashboard: "dataset_catalog": "description": |- @@ -564,10 +603,49 @@ github.com/databricks/cli/bundle/config/resources.Dashboard: "dataset_schema": "description": |- Sets the default schema for all datasets in this dashboard. When set, this overrides the schema specified in individual dataset definitions. +github.com/databricks/cli/bundle/config/resources.DashboardPermission: + "group_name": + "description": |- + PLACEHOLDER + "level": + "description": |- + PLACEHOLDER + "service_principal_name": + "description": |- + PLACEHOLDER + "user_name": + "description": |- + PLACEHOLDER github.com/databricks/cli/bundle/config/resources.DatabaseInstance: "effective_capacity": "description": |- PLACEHOLDER +github.com/databricks/cli/bundle/config/resources.DatabaseInstancePermission: + "group_name": + "description": |- + PLACEHOLDER + "level": + "description": |- + PLACEHOLDER + "service_principal_name": + "description": |- + PLACEHOLDER + "user_name": + "description": |- + PLACEHOLDER +github.com/databricks/cli/bundle/config/resources.DatabaseProjectPermission: + "group_name": + "description": |- + PLACEHOLDER + "level": + "description": |- + PLACEHOLDER + "service_principal_name": + "description": |- + PLACEHOLDER + "user_name": + "description": |- + PLACEHOLDER github.com/databricks/cli/bundle/config/resources.ExternalLocation: "comment": "description": |- @@ -605,138 +683,64 @@ github.com/databricks/cli/bundle/config/resources.ExternalLocation: "url": "description": |- PLACEHOLDER -github.com/databricks/cli/bundle/config/resources.Lifecycle: - "prevent_destroy": - "description": |- - Lifecycle setting to prevent the resource from being destroyed. -? github.com/databricks/cli/bundle/config/resources.Permission[github.com/databricks/databricks-sdk-go/service/apps.AppPermissionLevel] -: "-": - "description": |- - Defines a permission for a specific entity. - "markdown_description": |- - Defines a permission for a specific entity. See [\_](/dev-tools/bundles/settings.md#permissions) and [\_](/dev-tools/bundles/permissions.md). - "group_name": - "description": |- - The name of the group that has the permission set in level. - "level": - "description": |- - The allowed permission for user, group, service principal defined for this permission. - "service_principal_name": - "description": |- - The name of the service principal that has the permission set in level. - "user_name": - "description": |- - The name of the user that has the permission set in level. -? github.com/databricks/cli/bundle/config/resources.Permission[github.com/databricks/databricks-sdk-go/service/compute.ClusterPermissionLevel] -: "-": - "description": |- - Defines a permission for a specific entity. - "markdown_description": |- - Defines a permission for a specific entity. See [\_](/dev-tools/bundles/settings.md#permissions) and [\_](/dev-tools/bundles/permissions.md). - "group_name": - "description": |- - The name of the group that has the permission set in level. - "level": - "description": |- - The allowed permission for user, group, service principal defined for this permission. - "service_principal_name": - "description": |- - The name of the service principal that has the permission set in level. - "user_name": - "description": |- - The name of the user that has the permission set in level. -? github.com/databricks/cli/bundle/config/resources.Permission[github.com/databricks/databricks-sdk-go/service/iam.PermissionLevel] -: "-": - "description": |- - Defines a permission for a specific entity. - "markdown_description": |- - Defines a permission for a specific entity. See [\_](/dev-tools/bundles/settings.md#permissions) and [\_](/dev-tools/bundles/permissions.md). - "group_name": - "description": |- - The name of the group that has the permission set in level. - "level": - "description": |- - The allowed permission for user, group, service principal defined for this permission. - "service_principal_name": - "description": |- - The name of the service principal that has the permission set in level. - "user_name": - "description": |- - The name of the user that has the permission set in level. -? github.com/databricks/cli/bundle/config/resources.Permission[github.com/databricks/databricks-sdk-go/service/jobs.JobPermissionLevel] -: "-": - "description": |- - Defines a permission for a specific entity. - "markdown_description": |- - Defines a permission for a specific entity. See [\_](/dev-tools/bundles/settings.md#permissions) and [\_](/dev-tools/bundles/permissions.md). +github.com/databricks/cli/bundle/config/resources.JobPermission: "group_name": "description": |- - The name of the group that has the permission set in level. + PLACEHOLDER "level": "description": |- - The allowed permission for user, group, service principal defined for this permission. + PLACEHOLDER "service_principal_name": "description": |- - The name of the service principal that has the permission set in level. + PLACEHOLDER "user_name": "description": |- - The name of the user that has the permission set in level. -? github.com/databricks/cli/bundle/config/resources.Permission[github.com/databricks/databricks-sdk-go/service/ml.ExperimentPermissionLevel] -: "-": + PLACEHOLDER +github.com/databricks/cli/bundle/config/resources.Lifecycle: + "prevent_destroy": "description": |- - Defines a permission for a specific entity. - "markdown_description": |- - Defines a permission for a specific entity. See [\_](/dev-tools/bundles/settings.md#permissions) and [\_](/dev-tools/bundles/permissions.md). + Lifecycle setting to prevent the resource from being destroyed. +github.com/databricks/cli/bundle/config/resources.MlflowExperimentPermission: "group_name": "description": |- - The name of the group that has the permission set in level. + PLACEHOLDER "level": "description": |- - The allowed permission for user, group, service principal defined for this permission. + PLACEHOLDER "service_principal_name": "description": |- - The name of the service principal that has the permission set in level. + PLACEHOLDER "user_name": "description": |- - The name of the user that has the permission set in level. -? github.com/databricks/cli/bundle/config/resources.Permission[github.com/databricks/databricks-sdk-go/service/ml.RegisteredModelPermissionLevel] -: "-": - "description": |- - Defines a permission for a specific entity. - "markdown_description": |- - Defines a permission for a specific entity. See [\_](/dev-tools/bundles/settings.md#permissions) and [\_](/dev-tools/bundles/permissions.md). + PLACEHOLDER +github.com/databricks/cli/bundle/config/resources.MlflowModelPermission: "group_name": "description": |- - The name of the group that has the permission set in level. + PLACEHOLDER "level": "description": |- - The allowed permission for user, group, service principal defined for this permission. + PLACEHOLDER "service_principal_name": "description": |- - The name of the service principal that has the permission set in level. + PLACEHOLDER "user_name": "description": |- - The name of the user that has the permission set in level. -? github.com/databricks/cli/bundle/config/resources.Permission[github.com/databricks/databricks-sdk-go/service/pipelines.PipelinePermissionLevel] -: "-": - "description": |- - Defines a permission for a specific entity. - "markdown_description": |- - Defines a permission for a specific entity. See [\_](/dev-tools/bundles/settings.md#permissions) and [\_](/dev-tools/bundles/permissions.md). + PLACEHOLDER +github.com/databricks/cli/bundle/config/resources.ModelServingEndpointPermission: "group_name": "description": |- - The name of the group that has the permission set in level. + PLACEHOLDER "level": "description": |- - The allowed permission for user, group, service principal defined for this permission. + PLACEHOLDER "service_principal_name": "description": |- - The name of the service principal that has the permission set in level. + PLACEHOLDER "user_name": "description": |- - The name of the user that has the permission set in level. -? github.com/databricks/cli/bundle/config/resources.Permission[github.com/databricks/databricks-sdk-go/service/serving.ServingEndpointPermissionLevel] -: "-": + PLACEHOLDER +github.com/databricks/cli/bundle/config/resources.Permission: + "-": "description": |- Defines a permission for a specific entity. "markdown_description": |- @@ -753,24 +757,19 @@ github.com/databricks/cli/bundle/config/resources.Lifecycle: "user_name": "description": |- The name of the user that has the permission set in level. -? github.com/databricks/cli/bundle/config/resources.Permission[github.com/databricks/databricks-sdk-go/service/sql.WarehousePermissionLevel] -: "-": - "description": |- - Defines a permission for a specific entity. - "markdown_description": |- - Defines a permission for a specific entity. See [\_](/dev-tools/bundles/settings.md#permissions) and [\_](/dev-tools/bundles/permissions.md). +github.com/databricks/cli/bundle/config/resources.PipelinePermission: "group_name": "description": |- - The name of the group that has the permission set in level. + PLACEHOLDER "level": "description": |- - The allowed permission for user, group, service principal defined for this permission. + PLACEHOLDER "service_principal_name": "description": |- - The name of the service principal that has the permission set in level. + PLACEHOLDER "user_name": "description": |- - The name of the user that has the permission set in level. + PLACEHOLDER github.com/databricks/cli/bundle/config/resources.PostgresBranch: "branch_id": "description": |- @@ -950,6 +949,19 @@ github.com/databricks/cli/bundle/config/resources.SecretScopePermission: "user_name": "description": |- The name of the user that has the permission set in level. This field translates to a `principal` field in secret scope ACL. +github.com/databricks/cli/bundle/config/resources.SqlWarehousePermission: + "group_name": + "description": |- + PLACEHOLDER + "level": + "description": |- + PLACEHOLDER + "service_principal_name": + "description": |- + PLACEHOLDER + "user_name": + "description": |- + PLACEHOLDER github.com/databricks/cli/bundle/config/resources.SyncedDatabaseTable: "data_synchronization_status": "description": |- diff --git a/bundle/internal/schema/annotations_openapi_overrides.yml b/bundle/internal/schema/annotations_openapi_overrides.yml index 3a83d7a0d2..e517c2ebcc 100644 --- a/bundle/internal/schema/annotations_openapi_overrides.yml +++ b/bundle/internal/schema/annotations_openapi_overrides.yml @@ -14,6 +14,17 @@ github.com/databricks/cli/bundle/config/resources.Alert: "schedule": "description": |- PLACEHOLDER +github.com/databricks/cli/bundle/config/resources.AlertPermissionLevel: + "_": + "enum": + - |- + CAN_EDIT + - |- + CAN_MANAGE + - |- + CAN_READ + - |- + CAN_RUN github.com/databricks/cli/bundle/config/resources.App: "app_status": "description": |- @@ -69,6 +80,13 @@ github.com/databricks/cli/bundle/config/resources.App: "user_api_scopes": "description": |- PLACEHOLDER +github.com/databricks/cli/bundle/config/resources.AppPermissionLevel: + "_": + "enum": + - |- + CAN_MANAGE + - |- + CAN_USE github.com/databricks/cli/bundle/config/resources.Catalog: "grants": "description": |- @@ -127,6 +145,15 @@ github.com/databricks/cli/bundle/config/resources.Cluster: "workload_type": "description": |- PLACEHOLDER +github.com/databricks/cli/bundle/config/resources.ClusterPermissionLevel: + "_": + "enum": + - |- + CAN_MANAGE + - |- + CAN_RESTART + - |- + CAN_ATTACH_TO github.com/databricks/cli/bundle/config/resources.Dashboard: "_": "markdown_description": |- @@ -199,6 +226,17 @@ github.com/databricks/cli/bundle/config/resources.Dashboard: "warehouse_id": "description": |- The warehouse ID used to run the dashboard. +github.com/databricks/cli/bundle/config/resources.DashboardPermissionLevel: + "_": + "enum": + - |- + CAN_READ + - |- + CAN_RUN + - |- + CAN_EDIT + - |- + CAN_MANAGE github.com/databricks/cli/bundle/config/resources.DatabaseCatalog: "create_database_if_not_exists": "description": |- @@ -223,6 +261,22 @@ github.com/databricks/cli/bundle/config/resources.ExternalLocation: "lifecycle": "description": |- PLACEHOLDER +github.com/databricks/cli/bundle/config/resources.DatabaseInstancePermissionLevel: + "_": + "enum": + - |- + CAN_CREATE + - |- + CAN_USE + - |- + CAN_MANAGE +github.com/databricks/cli/bundle/config/resources.DatabaseProjectPermissionLevel: + "_": + "enum": + - |- + CAN_USE + - |- + CAN_MANAGE github.com/databricks/cli/bundle/config/resources.Job: "_": "markdown_description": |- @@ -254,6 +308,17 @@ github.com/databricks/cli/bundle/config/resources.Job: "run_as": "description": |- PLACEHOLDER +github.com/databricks/cli/bundle/config/resources.JobPermissionLevel: + "_": + "enum": + - |- + CAN_MANAGE + - |- + CAN_MANAGE_RUN + - |- + CAN_VIEW + - |- + IS_OWNER github.com/databricks/cli/bundle/config/resources.MlflowExperiment: "_": "markdown_description": |- @@ -277,6 +342,15 @@ github.com/databricks/cli/bundle/config/resources.MlflowExperiment: "permissions": "description": |- PLACEHOLDER +github.com/databricks/cli/bundle/config/resources.MlflowExperimentPermissionLevel: + "_": + "enum": + - |- + CAN_MANAGE + - |- + CAN_EDIT + - |- + CAN_READ github.com/databricks/cli/bundle/config/resources.MlflowModel: "_": "markdown_description": |- @@ -287,6 +361,19 @@ github.com/databricks/cli/bundle/config/resources.MlflowModel: "permissions": "description": |- PLACEHOLDER +github.com/databricks/cli/bundle/config/resources.MlflowModelPermissionLevel: + "_": + "enum": + - |- + CAN_EDIT + - |- + CAN_MANAGE + - |- + CAN_MANAGE_STAGING_VERSIONS + - |- + CAN_MANAGE_PRODUCTION_VERSIONS + - |- + CAN_READ github.com/databricks/cli/bundle/config/resources.ModelServingEndpoint: "_": "markdown_description": |- @@ -322,6 +409,15 @@ github.com/databricks/cli/bundle/config/resources.ModelServingEndpoint: "permissions": "description": |- PLACEHOLDER +github.com/databricks/cli/bundle/config/resources.ModelServingEndpointPermissionLevel: + "_": + "enum": + - |- + CAN_MANAGE + - |- + CAN_QUERY + - |- + CAN_VIEW github.com/databricks/cli/bundle/config/resources.Pipeline: "_": "markdown_description": |- @@ -361,6 +457,17 @@ github.com/databricks/cli/bundle/config/resources.Pipeline: "trigger": "deprecation_message": |- Use continuous instead +github.com/databricks/cli/bundle/config/resources.PipelinePermissionLevel: + "_": + "enum": + - |- + CAN_MANAGE + - |- + IS_OWNER + - |- + CAN_RUN + - |- + CAN_VIEW github.com/databricks/cli/bundle/config/resources.QualityMonitor: "_": "markdown_description": |- @@ -531,6 +638,17 @@ github.com/databricks/cli/bundle/config/resources.SqlWarehouse: "warehouse_type": "description": |- PLACEHOLDER +github.com/databricks/cli/bundle/config/resources.SqlWarehousePermissionLevel: + "_": + "enum": + - |- + CAN_MANAGE + - |- + CAN_USE + - |- + CAN_MONITOR + - |- + CAN_VIEW github.com/databricks/cli/bundle/config/resources.SyncedDatabaseTable: "lifecycle": "description": |- From 15367de1c7f53f37124fdd1c384ebad6101b6993 Mon Sep 17 00:00:00 2001 From: Denis Bilenko Date: Tue, 10 Mar 2026 14:25:00 +0100 Subject: [PATCH 11/28] Define named non-generic permission types for each resource Co-Authored-By: Claude Sonnet 4.6 --- .../apply_bundle_permissions_test.go | 78 +- .../config/mutator/resourcemutator/run_as.go | 4 +- .../validate_target_mode_test.go | 15 +- bundle/config/resources/alerts.go | 3 +- bundle/config/resources/apps.go | 2 +- bundle/config/resources/clusters.go | 2 +- bundle/config/resources/dashboard.go | 3 +- bundle/config/resources/database_instance.go | 3 +- bundle/config/resources/job.go | 2 +- bundle/config/resources/mlflow_experiment.go | 2 +- bundle/config/resources/mlflow_model.go | 2 +- .../resources/model_serving_endpoint.go | 2 +- bundle/config/resources/permission_types.go | 172 +++ bundle/config/resources/pipeline.go | 2 +- bundle/config/resources/postgres_project.go | 3 +- bundle/config/resources/sql_warehouses.go | 2 +- bundle/config/resources_types_test.go | 3 +- bundle/deploy/terraform/convert_test.go | 10 +- .../terraform/tfdyn/convert_alert_test.go | 3 +- .../terraform/tfdyn/convert_app_test.go | 2 +- .../terraform/tfdyn/convert_cluster_test.go | 2 +- .../terraform/tfdyn/convert_dashboard_test.go | 3 +- .../tfdyn/convert_database_instance_test.go | 3 +- .../tfdyn/convert_experiment_test.go | 2 +- .../terraform/tfdyn/convert_job_test.go | 2 +- .../convert_model_serving_endpoint_test.go | 2 +- .../terraform/tfdyn/convert_model_test.go | 2 +- .../tfdyn/convert_permissions_test.go | 5 +- .../terraform/tfdyn/convert_pipeline_test.go | 2 +- .../tfdyn/convert_postgres_project_test.go | 3 +- bundle/internal/schema/annotations.yml | 4 +- .../internal/schema/annotations_openapi.yml | 141 +++ .../schema/annotations_openapi_overrides.yml | 118 -- .../validation/generated/enum_fields.go | 21 +- bundle/schema/jsonschema.json | 1091 +++++++++++------ bundle/schema/jsonschema_for_docs.json | 452 ++++++- bundle/tests/bundle_permissions_test.go | 34 +- bundle/tests/model_serving_endpoint_test.go | 3 +- python/codegen/codegen/aliases_patch.py | 16 +- python/codegen/codegen/jsonschema.py | 48 +- python/codegen/codegen/main.py | 2 +- python/codegen/codegen/packages.py | 52 +- python/databricks/bundles/jobs/__init__.py | 22 +- python/databricks/bundles/jobs/_models/job.py | 9 +- .../bundles/jobs/_models/job_permission.py | 48 + .../jobs/_models/job_permission_level.py | 18 + .../bundles/jobs/_models/permission.py | 68 - .../databricks/bundles/pipelines/__init__.py | 22 +- .../bundles/pipelines/_models/permission.py | 68 - .../bundles/pipelines/_models/pipeline.py | 9 +- .../pipelines/_models/pipeline_permission.py | 48 + .../_models/pipeline_permission_level.py | 18 + 52 files changed, 1796 insertions(+), 857 deletions(-) create mode 100644 bundle/config/resources/permission_types.go create mode 100644 python/databricks/bundles/jobs/_models/job_permission.py create mode 100644 python/databricks/bundles/jobs/_models/job_permission_level.py delete mode 100644 python/databricks/bundles/jobs/_models/permission.py delete mode 100644 python/databricks/bundles/pipelines/_models/permission.py create mode 100644 python/databricks/bundles/pipelines/_models/pipeline_permission.py create mode 100644 python/databricks/bundles/pipelines/_models/pipeline_permission_level.py diff --git a/bundle/config/mutator/resourcemutator/apply_bundle_permissions_test.go b/bundle/config/mutator/resourcemutator/apply_bundle_permissions_test.go index be57b60b31..d3c439af7f 100644 --- a/bundle/config/mutator/resourcemutator/apply_bundle_permissions_test.go +++ b/bundle/config/mutator/resourcemutator/apply_bundle_permissions_test.go @@ -10,12 +10,8 @@ import ( "github.com/databricks/cli/bundle" "github.com/databricks/cli/bundle/config" "github.com/databricks/cli/bundle/config/resources" - appssdk "github.com/databricks/databricks-sdk-go/service/apps" "github.com/databricks/databricks-sdk-go/service/iam" "github.com/databricks/databricks-sdk-go/service/jobs" - "github.com/databricks/databricks-sdk-go/service/ml" - "github.com/databricks/databricks-sdk-go/service/pipelines" - "github.com/databricks/databricks-sdk-go/service/serving" "github.com/stretchr/testify/assert" "github.com/stretchr/testify/require" ) @@ -91,58 +87,58 @@ func TestApplyBundlePermissions(t *testing.T) { require.NoError(t, diags.Error()) require.Len(t, b.Config.Resources.Jobs["job_1"].Permissions, 3) - require.Contains(t, b.Config.Resources.Jobs["job_1"].Permissions, resources.Permission[jobs.JobPermissionLevel]{Level: "CAN_MANAGE", UserName: "TestUser"}) - require.Contains(t, b.Config.Resources.Jobs["job_1"].Permissions, resources.Permission[jobs.JobPermissionLevel]{Level: "CAN_VIEW", GroupName: "TestGroup"}) - require.Contains(t, b.Config.Resources.Jobs["job_1"].Permissions, resources.Permission[jobs.JobPermissionLevel]{Level: "CAN_MANAGE_RUN", ServicePrincipalName: "TestServicePrincipal"}) + require.Contains(t, b.Config.Resources.Jobs["job_1"].Permissions, resources.JobPermission{Level: "CAN_MANAGE", UserName: "TestUser"}) + require.Contains(t, b.Config.Resources.Jobs["job_1"].Permissions, resources.JobPermission{Level: "CAN_VIEW", GroupName: "TestGroup"}) + require.Contains(t, b.Config.Resources.Jobs["job_1"].Permissions, resources.JobPermission{Level: "CAN_MANAGE_RUN", ServicePrincipalName: "TestServicePrincipal"}) require.Len(t, b.Config.Resources.Jobs["job_2"].Permissions, 3) - require.Contains(t, b.Config.Resources.Jobs["job_2"].Permissions, resources.Permission[jobs.JobPermissionLevel]{Level: "CAN_MANAGE", UserName: "TestUser"}) - require.Contains(t, b.Config.Resources.Jobs["job_2"].Permissions, resources.Permission[jobs.JobPermissionLevel]{Level: "CAN_VIEW", GroupName: "TestGroup"}) - require.Contains(t, b.Config.Resources.Jobs["job_2"].Permissions, resources.Permission[jobs.JobPermissionLevel]{Level: "CAN_MANAGE_RUN", ServicePrincipalName: "TestServicePrincipal"}) + require.Contains(t, b.Config.Resources.Jobs["job_2"].Permissions, resources.JobPermission{Level: "CAN_MANAGE", UserName: "TestUser"}) + require.Contains(t, b.Config.Resources.Jobs["job_2"].Permissions, resources.JobPermission{Level: "CAN_VIEW", GroupName: "TestGroup"}) + require.Contains(t, b.Config.Resources.Jobs["job_2"].Permissions, resources.JobPermission{Level: "CAN_MANAGE_RUN", ServicePrincipalName: "TestServicePrincipal"}) require.Len(t, b.Config.Resources.Pipelines["pipeline_1"].Permissions, 3) - require.Contains(t, b.Config.Resources.Pipelines["pipeline_1"].Permissions, resources.Permission[pipelines.PipelinePermissionLevel]{Level: "CAN_MANAGE", UserName: "TestUser"}) - require.Contains(t, b.Config.Resources.Pipelines["pipeline_1"].Permissions, resources.Permission[pipelines.PipelinePermissionLevel]{Level: "CAN_VIEW", GroupName: "TestGroup"}) - require.Contains(t, b.Config.Resources.Pipelines["pipeline_1"].Permissions, resources.Permission[pipelines.PipelinePermissionLevel]{Level: "CAN_RUN", ServicePrincipalName: "TestServicePrincipal"}) + require.Contains(t, b.Config.Resources.Pipelines["pipeline_1"].Permissions, resources.PipelinePermission{Level: "CAN_MANAGE", UserName: "TestUser"}) + require.Contains(t, b.Config.Resources.Pipelines["pipeline_1"].Permissions, resources.PipelinePermission{Level: "CAN_VIEW", GroupName: "TestGroup"}) + require.Contains(t, b.Config.Resources.Pipelines["pipeline_1"].Permissions, resources.PipelinePermission{Level: "CAN_RUN", ServicePrincipalName: "TestServicePrincipal"}) require.Len(t, b.Config.Resources.Pipelines["pipeline_2"].Permissions, 3) - require.Contains(t, b.Config.Resources.Pipelines["pipeline_2"].Permissions, resources.Permission[pipelines.PipelinePermissionLevel]{Level: "CAN_MANAGE", UserName: "TestUser"}) - require.Contains(t, b.Config.Resources.Pipelines["pipeline_2"].Permissions, resources.Permission[pipelines.PipelinePermissionLevel]{Level: "CAN_VIEW", GroupName: "TestGroup"}) - require.Contains(t, b.Config.Resources.Pipelines["pipeline_2"].Permissions, resources.Permission[pipelines.PipelinePermissionLevel]{Level: "CAN_RUN", ServicePrincipalName: "TestServicePrincipal"}) + require.Contains(t, b.Config.Resources.Pipelines["pipeline_2"].Permissions, resources.PipelinePermission{Level: "CAN_MANAGE", UserName: "TestUser"}) + require.Contains(t, b.Config.Resources.Pipelines["pipeline_2"].Permissions, resources.PipelinePermission{Level: "CAN_VIEW", GroupName: "TestGroup"}) + require.Contains(t, b.Config.Resources.Pipelines["pipeline_2"].Permissions, resources.PipelinePermission{Level: "CAN_RUN", ServicePrincipalName: "TestServicePrincipal"}) require.Len(t, b.Config.Resources.Models["model_1"].Permissions, 2) - require.Contains(t, b.Config.Resources.Models["model_1"].Permissions, resources.Permission[ml.RegisteredModelPermissionLevel]{Level: "CAN_MANAGE", UserName: "TestUser"}) - require.Contains(t, b.Config.Resources.Models["model_1"].Permissions, resources.Permission[ml.RegisteredModelPermissionLevel]{Level: "CAN_READ", GroupName: "TestGroup"}) + require.Contains(t, b.Config.Resources.Models["model_1"].Permissions, resources.MlflowModelPermission{Level: "CAN_MANAGE", UserName: "TestUser"}) + require.Contains(t, b.Config.Resources.Models["model_1"].Permissions, resources.MlflowModelPermission{Level: "CAN_READ", GroupName: "TestGroup"}) require.Len(t, b.Config.Resources.Models["model_2"].Permissions, 2) - require.Contains(t, b.Config.Resources.Models["model_2"].Permissions, resources.Permission[ml.RegisteredModelPermissionLevel]{Level: "CAN_MANAGE", UserName: "TestUser"}) - require.Contains(t, b.Config.Resources.Models["model_2"].Permissions, resources.Permission[ml.RegisteredModelPermissionLevel]{Level: "CAN_READ", GroupName: "TestGroup"}) + require.Contains(t, b.Config.Resources.Models["model_2"].Permissions, resources.MlflowModelPermission{Level: "CAN_MANAGE", UserName: "TestUser"}) + require.Contains(t, b.Config.Resources.Models["model_2"].Permissions, resources.MlflowModelPermission{Level: "CAN_READ", GroupName: "TestGroup"}) require.Len(t, b.Config.Resources.Experiments["experiment_1"].Permissions, 2) - require.Contains(t, b.Config.Resources.Experiments["experiment_1"].Permissions, resources.Permission[ml.ExperimentPermissionLevel]{Level: "CAN_MANAGE", UserName: "TestUser"}) - require.Contains(t, b.Config.Resources.Experiments["experiment_1"].Permissions, resources.Permission[ml.ExperimentPermissionLevel]{Level: "CAN_READ", GroupName: "TestGroup"}) + require.Contains(t, b.Config.Resources.Experiments["experiment_1"].Permissions, resources.MlflowExperimentPermission{Level: "CAN_MANAGE", UserName: "TestUser"}) + require.Contains(t, b.Config.Resources.Experiments["experiment_1"].Permissions, resources.MlflowExperimentPermission{Level: "CAN_READ", GroupName: "TestGroup"}) require.Len(t, b.Config.Resources.Experiments["experiment_2"].Permissions, 2) - require.Contains(t, b.Config.Resources.Experiments["experiment_2"].Permissions, resources.Permission[ml.ExperimentPermissionLevel]{Level: "CAN_MANAGE", UserName: "TestUser"}) - require.Contains(t, b.Config.Resources.Experiments["experiment_2"].Permissions, resources.Permission[ml.ExperimentPermissionLevel]{Level: "CAN_READ", GroupName: "TestGroup"}) + require.Contains(t, b.Config.Resources.Experiments["experiment_2"].Permissions, resources.MlflowExperimentPermission{Level: "CAN_MANAGE", UserName: "TestUser"}) + require.Contains(t, b.Config.Resources.Experiments["experiment_2"].Permissions, resources.MlflowExperimentPermission{Level: "CAN_READ", GroupName: "TestGroup"}) require.Len(t, b.Config.Resources.ModelServingEndpoints["endpoint_1"].Permissions, 3) - require.Contains(t, b.Config.Resources.ModelServingEndpoints["endpoint_1"].Permissions, resources.Permission[serving.ServingEndpointPermissionLevel]{Level: "CAN_MANAGE", UserName: "TestUser"}) - require.Contains(t, b.Config.Resources.ModelServingEndpoints["endpoint_1"].Permissions, resources.Permission[serving.ServingEndpointPermissionLevel]{Level: "CAN_VIEW", GroupName: "TestGroup"}) - require.Contains(t, b.Config.Resources.ModelServingEndpoints["endpoint_1"].Permissions, resources.Permission[serving.ServingEndpointPermissionLevel]{Level: "CAN_QUERY", ServicePrincipalName: "TestServicePrincipal"}) + require.Contains(t, b.Config.Resources.ModelServingEndpoints["endpoint_1"].Permissions, resources.ModelServingEndpointPermission{Level: "CAN_MANAGE", UserName: "TestUser"}) + require.Contains(t, b.Config.Resources.ModelServingEndpoints["endpoint_1"].Permissions, resources.ModelServingEndpointPermission{Level: "CAN_VIEW", GroupName: "TestGroup"}) + require.Contains(t, b.Config.Resources.ModelServingEndpoints["endpoint_1"].Permissions, resources.ModelServingEndpointPermission{Level: "CAN_QUERY", ServicePrincipalName: "TestServicePrincipal"}) require.Len(t, b.Config.Resources.ModelServingEndpoints["endpoint_2"].Permissions, 3) - require.Contains(t, b.Config.Resources.ModelServingEndpoints["endpoint_2"].Permissions, resources.Permission[serving.ServingEndpointPermissionLevel]{Level: "CAN_MANAGE", UserName: "TestUser"}) - require.Contains(t, b.Config.Resources.ModelServingEndpoints["endpoint_2"].Permissions, resources.Permission[serving.ServingEndpointPermissionLevel]{Level: "CAN_VIEW", GroupName: "TestGroup"}) - require.Contains(t, b.Config.Resources.ModelServingEndpoints["endpoint_2"].Permissions, resources.Permission[serving.ServingEndpointPermissionLevel]{Level: "CAN_QUERY", ServicePrincipalName: "TestServicePrincipal"}) + require.Contains(t, b.Config.Resources.ModelServingEndpoints["endpoint_2"].Permissions, resources.ModelServingEndpointPermission{Level: "CAN_MANAGE", UserName: "TestUser"}) + require.Contains(t, b.Config.Resources.ModelServingEndpoints["endpoint_2"].Permissions, resources.ModelServingEndpointPermission{Level: "CAN_VIEW", GroupName: "TestGroup"}) + require.Contains(t, b.Config.Resources.ModelServingEndpoints["endpoint_2"].Permissions, resources.ModelServingEndpointPermission{Level: "CAN_QUERY", ServicePrincipalName: "TestServicePrincipal"}) require.Len(t, b.Config.Resources.Dashboards["dashboard_1"].Permissions, 2) - require.Contains(t, b.Config.Resources.Dashboards["dashboard_1"].Permissions, resources.Permission[iam.PermissionLevel]{Level: "CAN_MANAGE", UserName: "TestUser"}) - require.Contains(t, b.Config.Resources.Dashboards["dashboard_1"].Permissions, resources.Permission[iam.PermissionLevel]{Level: "CAN_READ", GroupName: "TestGroup"}) + require.Contains(t, b.Config.Resources.Dashboards["dashboard_1"].Permissions, resources.DashboardPermission{Level: "CAN_MANAGE", UserName: "TestUser"}) + require.Contains(t, b.Config.Resources.Dashboards["dashboard_1"].Permissions, resources.DashboardPermission{Level: "CAN_READ", GroupName: "TestGroup"}) require.Len(t, b.Config.Resources.Apps["app_1"].Permissions, 2) - require.Contains(t, b.Config.Resources.Apps["app_1"].Permissions, resources.Permission[appssdk.AppPermissionLevel]{Level: "CAN_MANAGE", UserName: "TestUser"}) - require.Contains(t, b.Config.Resources.Apps["app_1"].Permissions, resources.Permission[appssdk.AppPermissionLevel]{Level: "CAN_USE", GroupName: "TestGroup"}) + require.Contains(t, b.Config.Resources.Apps["app_1"].Permissions, resources.AppPermission{Level: "CAN_MANAGE", UserName: "TestUser"}) + require.Contains(t, b.Config.Resources.Apps["app_1"].Permissions, resources.AppPermission{Level: "CAN_USE", GroupName: "TestGroup"}) } func TestWarningOnOverlapPermission(t *testing.T) { @@ -161,7 +157,7 @@ func TestWarningOnOverlapPermission(t *testing.T) { JobSettings: jobs.JobSettings{ Name: "job_1", }, - Permissions: resources.Permissions[jobs.JobPermissionLevel]{ + Permissions: resources.JobPermissions{ {Level: "CAN_VIEW", UserName: "TestUser"}, }, }, @@ -169,7 +165,7 @@ func TestWarningOnOverlapPermission(t *testing.T) { JobSettings: jobs.JobSettings{ Name: "job_2", }, - Permissions: resources.Permissions[jobs.JobPermissionLevel]{ + Permissions: resources.JobPermissions{ {Level: "CAN_VIEW", UserName: "TestUser2"}, }, }, @@ -181,11 +177,11 @@ func TestWarningOnOverlapPermission(t *testing.T) { diags := bundle.Apply(t.Context(), b, ApplyBundlePermissions()) require.NoError(t, diags.Error()) - require.Contains(t, b.Config.Resources.Jobs["job_1"].Permissions, resources.Permission[jobs.JobPermissionLevel]{Level: "CAN_VIEW", UserName: "TestUser"}) - require.Contains(t, b.Config.Resources.Jobs["job_1"].Permissions, resources.Permission[jobs.JobPermissionLevel]{Level: "CAN_VIEW", GroupName: "TestGroup"}) - require.Contains(t, b.Config.Resources.Jobs["job_2"].Permissions, resources.Permission[jobs.JobPermissionLevel]{Level: "CAN_VIEW", UserName: "TestUser2"}) - require.Contains(t, b.Config.Resources.Jobs["job_2"].Permissions, resources.Permission[jobs.JobPermissionLevel]{Level: "CAN_MANAGE", UserName: "TestUser"}) - require.Contains(t, b.Config.Resources.Jobs["job_2"].Permissions, resources.Permission[jobs.JobPermissionLevel]{Level: "CAN_VIEW", GroupName: "TestGroup"}) + require.Contains(t, b.Config.Resources.Jobs["job_1"].Permissions, resources.JobPermission{Level: "CAN_VIEW", UserName: "TestUser"}) + require.Contains(t, b.Config.Resources.Jobs["job_1"].Permissions, resources.JobPermission{Level: "CAN_VIEW", GroupName: "TestGroup"}) + require.Contains(t, b.Config.Resources.Jobs["job_2"].Permissions, resources.JobPermission{Level: "CAN_VIEW", UserName: "TestUser2"}) + require.Contains(t, b.Config.Resources.Jobs["job_2"].Permissions, resources.JobPermission{Level: "CAN_MANAGE", UserName: "TestUser"}) + require.Contains(t, b.Config.Resources.Jobs["job_2"].Permissions, resources.JobPermission{Level: "CAN_VIEW", GroupName: "TestGroup"}) } func TestAllResourcesExplicitlyDefinedForPermissionsSupport(t *testing.T) { diff --git a/bundle/config/mutator/resourcemutator/run_as.go b/bundle/config/mutator/resourcemutator/run_as.go index 07efbb4592..7360048213 100644 --- a/bundle/config/mutator/resourcemutator/run_as.go +++ b/bundle/config/mutator/resourcemutator/run_as.go @@ -196,11 +196,11 @@ func setPipelineOwnersToRunAsIdentity(b *bundle.Bundle) { for i := range b.Config.Resources.Pipelines { pipeline := b.Config.Resources.Pipelines[i] - pipeline.Permissions = slices.DeleteFunc([]resources.Permission[pipelines.PipelinePermissionLevel](pipeline.Permissions), func(p resources.Permission[pipelines.PipelinePermissionLevel]) bool { + pipeline.Permissions = slices.DeleteFunc(pipeline.Permissions, func(p resources.PipelinePermission) bool { return (runAs.ServicePrincipalName != "" && p.ServicePrincipalName == runAs.ServicePrincipalName) || (runAs.UserName != "" && p.UserName == runAs.UserName) }) - pipeline.Permissions = append(pipeline.Permissions, resources.Permission[pipelines.PipelinePermissionLevel]{ + pipeline.Permissions = append(pipeline.Permissions, resources.PipelinePermission{ Level: pipelines.PipelinePermissionLevelIsOwner, ServicePrincipalName: runAs.ServicePrincipalName, UserName: runAs.UserName, diff --git a/bundle/config/mutator/resourcemutator/validate_target_mode_test.go b/bundle/config/mutator/resourcemutator/validate_target_mode_test.go index 5a2eac818a..6940ba8b69 100644 --- a/bundle/config/mutator/resourcemutator/validate_target_mode_test.go +++ b/bundle/config/mutator/resourcemutator/validate_target_mode_test.go @@ -4,11 +4,8 @@ import ( "testing" "github.com/databricks/cli/libs/diag" - "github.com/databricks/databricks-sdk-go/service/compute" "github.com/databricks/databricks-sdk-go/service/jobs" - "github.com/databricks/databricks-sdk-go/service/ml" "github.com/databricks/databricks-sdk-go/service/pipelines" - "github.com/databricks/databricks-sdk-go/service/serving" "github.com/stretchr/testify/assert" "github.com/databricks/cli/bundle" @@ -51,22 +48,22 @@ func TestProcessTargetModeProduction(t *testing.T) { diags = validateProductionMode(b, false) require.ErrorContains(t, diags.Error(), "A common practice is to use a username or principal name in this path, i.e. use\n\n root_path: /Workspace/Users/lennart@company.com/.bundle/${bundle.name}/${bundle.target}") - jobPermissions := resources.Permissions[jobs.JobPermissionLevel]{ + jobPermissions := resources.JobPermissions{ {Level: "CAN_MANAGE", UserName: "user@company.com"}, } - pipelinePermissions := resources.Permissions[pipelines.PipelinePermissionLevel]{ + pipelinePermissions := resources.PipelinePermissions{ {Level: "CAN_MANAGE", UserName: "user@company.com"}, } - experimentPermissions := resources.Permissions[ml.ExperimentPermissionLevel]{ + experimentPermissions := resources.MlflowExperimentPermissions{ {Level: "CAN_MANAGE", UserName: "user@company.com"}, } - modelPermissions := resources.Permissions[ml.RegisteredModelPermissionLevel]{ + modelPermissions := resources.MlflowModelPermissions{ {Level: "CAN_MANAGE", UserName: "user@company.com"}, } - endpointPermissions := resources.Permissions[serving.ServingEndpointPermissionLevel]{ + endpointPermissions := resources.ModelServingEndpointPermissions{ {Level: "CAN_MANAGE", UserName: "user@company.com"}, } - clusterPermissions := resources.Permissions[compute.ClusterPermissionLevel]{ + clusterPermissions := resources.ClusterPermissions{ {Level: "CAN_MANAGE", UserName: "user@company.com"}, } b.Config.Resources.Jobs["job1"].Permissions = jobPermissions diff --git a/bundle/config/resources/alerts.go b/bundle/config/resources/alerts.go index be34e0061c..d234b7becb 100644 --- a/bundle/config/resources/alerts.go +++ b/bundle/config/resources/alerts.go @@ -7,7 +7,6 @@ import ( "github.com/databricks/cli/libs/log" "github.com/databricks/databricks-sdk-go" "github.com/databricks/databricks-sdk-go/marshal" - "github.com/databricks/databricks-sdk-go/service/iam" "github.com/databricks/databricks-sdk-go/service/sql" ) @@ -15,7 +14,7 @@ type Alert struct { BaseResource sql.AlertV2 //nolint AlertV2 also defines Id and URL field with the same json tag "id" and "url" - Permissions Permissions[iam.PermissionLevel] `json:"permissions,omitempty"` + Permissions AlertPermissions `json:"permissions,omitempty"` // Filepath points to the local .dbalert.json file containing the alert definition. // If specified, any fields that are part of the .dbalert.json file schema will not be allowed in diff --git a/bundle/config/resources/apps.go b/bundle/config/resources/apps.go index a11c67810a..4a311e138c 100644 --- a/bundle/config/resources/apps.go +++ b/bundle/config/resources/apps.go @@ -50,7 +50,7 @@ type App struct { // This is used in conjunction with GitRepository (from apps.App) and is passed to the Deploy API. GitSource *apps.GitSource `json:"git_source,omitempty"` - Permissions Permissions[apps.AppPermissionLevel] `json:"permissions,omitempty"` + Permissions AppPermissions `json:"permissions,omitempty"` } func (a *App) UnmarshalJSON(b []byte) error { diff --git a/bundle/config/resources/clusters.go b/bundle/config/resources/clusters.go index dff7e92426..68b7f7505c 100644 --- a/bundle/config/resources/clusters.go +++ b/bundle/config/resources/clusters.go @@ -14,7 +14,7 @@ type Cluster struct { BaseResource compute.ClusterSpec - Permissions Permissions[compute.ClusterPermissionLevel] `json:"permissions,omitempty"` + Permissions ClusterPermissions `json:"permissions,omitempty"` } func (s *Cluster) UnmarshalJSON(b []byte) error { diff --git a/bundle/config/resources/dashboard.go b/bundle/config/resources/dashboard.go index b52d3e527b..4576b6d0b6 100644 --- a/bundle/config/resources/dashboard.go +++ b/bundle/config/resources/dashboard.go @@ -9,7 +9,6 @@ import ( "github.com/databricks/databricks-sdk-go" "github.com/databricks/databricks-sdk-go/marshal" "github.com/databricks/databricks-sdk-go/service/dashboards" - "github.com/databricks/databricks-sdk-go/service/iam" ) type DashboardConfig struct { @@ -81,7 +80,7 @@ type Dashboard struct { BaseResource DashboardConfig - Permissions Permissions[iam.PermissionLevel] `json:"permissions,omitempty"` + Permissions DashboardPermissions `json:"permissions,omitempty"` // FilePath points to the local `.lvdash.json` file containing the dashboard definition. // This is inlined into serialized_dashboard during deployment. The file_path is kept around diff --git a/bundle/config/resources/database_instance.go b/bundle/config/resources/database_instance.go index 6ffaa36b61..2bca667c5c 100644 --- a/bundle/config/resources/database_instance.go +++ b/bundle/config/resources/database_instance.go @@ -8,14 +8,13 @@ import ( "github.com/databricks/databricks-sdk-go" "github.com/databricks/databricks-sdk-go/service/database" - "github.com/databricks/databricks-sdk-go/service/iam" ) type DatabaseInstance struct { BaseResource database.DatabaseInstance - Permissions Permissions[iam.PermissionLevel] `json:"permissions,omitempty"` + Permissions DatabaseInstancePermissions `json:"permissions,omitempty"` } func (d *DatabaseInstance) Exists(ctx context.Context, w *databricks.WorkspaceClient, name string) (bool, error) { diff --git a/bundle/config/resources/job.go b/bundle/config/resources/job.go index 430c270d84..5c6c3a6b61 100644 --- a/bundle/config/resources/job.go +++ b/bundle/config/resources/job.go @@ -15,7 +15,7 @@ type Job struct { BaseResource jobs.JobSettings - Permissions Permissions[jobs.JobPermissionLevel] `json:"permissions,omitempty"` + Permissions JobPermissions `json:"permissions,omitempty"` } func (j *Job) UnmarshalJSON(b []byte) error { diff --git a/bundle/config/resources/mlflow_experiment.go b/bundle/config/resources/mlflow_experiment.go index a1f07aea9a..a37c4a58f6 100644 --- a/bundle/config/resources/mlflow_experiment.go +++ b/bundle/config/resources/mlflow_experiment.go @@ -14,7 +14,7 @@ type MlflowExperiment struct { BaseResource ml.CreateExperiment - Permissions Permissions[ml.ExperimentPermissionLevel] `json:"permissions,omitempty"` + Permissions MlflowExperimentPermissions `json:"permissions,omitempty"` } func (s *MlflowExperiment) UnmarshalJSON(b []byte) error { diff --git a/bundle/config/resources/mlflow_model.go b/bundle/config/resources/mlflow_model.go index 8935d07372..11d165f9d4 100644 --- a/bundle/config/resources/mlflow_model.go +++ b/bundle/config/resources/mlflow_model.go @@ -14,7 +14,7 @@ type MlflowModel struct { BaseResource ml.CreateModelRequest - Permissions Permissions[ml.RegisteredModelPermissionLevel] `json:"permissions,omitempty"` + Permissions MlflowModelPermissions `json:"permissions,omitempty"` } func (s *MlflowModel) UnmarshalJSON(b []byte) error { diff --git a/bundle/config/resources/model_serving_endpoint.go b/bundle/config/resources/model_serving_endpoint.go index a3a8fed6b1..53e388fdca 100644 --- a/bundle/config/resources/model_serving_endpoint.go +++ b/bundle/config/resources/model_serving_endpoint.go @@ -19,7 +19,7 @@ type ModelServingEndpoint struct { // This is a resource agnostic implementation of permissions for ACLs. // Implementation could be different based on the resource type. - Permissions Permissions[serving.ServingEndpointPermissionLevel] `json:"permissions,omitempty"` + Permissions ModelServingEndpointPermissions `json:"permissions,omitempty"` } func (s *ModelServingEndpoint) UnmarshalJSON(b []byte) error { diff --git a/bundle/config/resources/permission_types.go b/bundle/config/resources/permission_types.go new file mode 100644 index 0000000000..25edaec897 --- /dev/null +++ b/bundle/config/resources/permission_types.go @@ -0,0 +1,172 @@ +package resources + +import ( + "github.com/databricks/databricks-sdk-go/service/apps" + "github.com/databricks/databricks-sdk-go/service/compute" + "github.com/databricks/databricks-sdk-go/service/iam" + "github.com/databricks/databricks-sdk-go/service/jobs" + "github.com/databricks/databricks-sdk-go/service/ml" + "github.com/databricks/databricks-sdk-go/service/pipelines" + "github.com/databricks/databricks-sdk-go/service/serving" + "github.com/databricks/databricks-sdk-go/service/sql" +) + +// Each resource defines its own permission type so that the JSON schema names them distinctly. +// Using non-alias type definitions (not =) makes them appear as named types in the schema. +// The underlying struct is identical to Permission[L], enabling conversion to use generic methods. + +type ( + AlertPermission Permission[iam.PermissionLevel] + AlertPermissions []AlertPermission +) + +func (ps AlertPermissions) ToAccessControlRequests() []iam.AccessControlRequest { + result := make([]iam.AccessControlRequest, len(ps)) + for i, p := range ps { + result[i] = Permission[iam.PermissionLevel](p).ToAccessControlRequest() + } + return result +} + +type ( + AppPermission Permission[apps.AppPermissionLevel] + AppPermissions []AppPermission +) + +func (ps AppPermissions) ToAccessControlRequests() []iam.AccessControlRequest { + result := make([]iam.AccessControlRequest, len(ps)) + for i, p := range ps { + result[i] = Permission[apps.AppPermissionLevel](p).ToAccessControlRequest() + } + return result +} + +type ( + ClusterPermission Permission[compute.ClusterPermissionLevel] + ClusterPermissions []ClusterPermission +) + +func (ps ClusterPermissions) ToAccessControlRequests() []iam.AccessControlRequest { + result := make([]iam.AccessControlRequest, len(ps)) + for i, p := range ps { + result[i] = Permission[compute.ClusterPermissionLevel](p).ToAccessControlRequest() + } + return result +} + +type ( + DashboardPermission Permission[iam.PermissionLevel] + DashboardPermissions []DashboardPermission +) + +func (ps DashboardPermissions) ToAccessControlRequests() []iam.AccessControlRequest { + result := make([]iam.AccessControlRequest, len(ps)) + for i, p := range ps { + result[i] = Permission[iam.PermissionLevel](p).ToAccessControlRequest() + } + return result +} + +type ( + DatabaseInstancePermission Permission[iam.PermissionLevel] + DatabaseInstancePermissions []DatabaseInstancePermission +) + +func (ps DatabaseInstancePermissions) ToAccessControlRequests() []iam.AccessControlRequest { + result := make([]iam.AccessControlRequest, len(ps)) + for i, p := range ps { + result[i] = Permission[iam.PermissionLevel](p).ToAccessControlRequest() + } + return result +} + +type ( + DatabaseProjectPermission Permission[iam.PermissionLevel] + DatabaseProjectPermissions []DatabaseProjectPermission +) + +func (ps DatabaseProjectPermissions) ToAccessControlRequests() []iam.AccessControlRequest { + result := make([]iam.AccessControlRequest, len(ps)) + for i, p := range ps { + result[i] = Permission[iam.PermissionLevel](p).ToAccessControlRequest() + } + return result +} + +type ( + JobPermission Permission[jobs.JobPermissionLevel] + JobPermissions []JobPermission +) + +func (ps JobPermissions) ToAccessControlRequests() []iam.AccessControlRequest { + result := make([]iam.AccessControlRequest, len(ps)) + for i, p := range ps { + result[i] = Permission[jobs.JobPermissionLevel](p).ToAccessControlRequest() + } + return result +} + +type ( + MlflowExperimentPermission Permission[ml.ExperimentPermissionLevel] + MlflowExperimentPermissions []MlflowExperimentPermission +) + +func (ps MlflowExperimentPermissions) ToAccessControlRequests() []iam.AccessControlRequest { + result := make([]iam.AccessControlRequest, len(ps)) + for i, p := range ps { + result[i] = Permission[ml.ExperimentPermissionLevel](p).ToAccessControlRequest() + } + return result +} + +type ( + MlflowModelPermission Permission[ml.RegisteredModelPermissionLevel] + MlflowModelPermissions []MlflowModelPermission +) + +func (ps MlflowModelPermissions) ToAccessControlRequests() []iam.AccessControlRequest { + result := make([]iam.AccessControlRequest, len(ps)) + for i, p := range ps { + result[i] = Permission[ml.RegisteredModelPermissionLevel](p).ToAccessControlRequest() + } + return result +} + +type ( + ModelServingEndpointPermission Permission[serving.ServingEndpointPermissionLevel] + ModelServingEndpointPermissions []ModelServingEndpointPermission +) + +func (ps ModelServingEndpointPermissions) ToAccessControlRequests() []iam.AccessControlRequest { + result := make([]iam.AccessControlRequest, len(ps)) + for i, p := range ps { + result[i] = Permission[serving.ServingEndpointPermissionLevel](p).ToAccessControlRequest() + } + return result +} + +type ( + PipelinePermission Permission[pipelines.PipelinePermissionLevel] + PipelinePermissions []PipelinePermission +) + +func (ps PipelinePermissions) ToAccessControlRequests() []iam.AccessControlRequest { + result := make([]iam.AccessControlRequest, len(ps)) + for i, p := range ps { + result[i] = Permission[pipelines.PipelinePermissionLevel](p).ToAccessControlRequest() + } + return result +} + +type ( + SqlWarehousePermission Permission[sql.WarehousePermissionLevel] + SqlWarehousePermissions []SqlWarehousePermission +) + +func (ps SqlWarehousePermissions) ToAccessControlRequests() []iam.AccessControlRequest { + result := make([]iam.AccessControlRequest, len(ps)) + for i, p := range ps { + result[i] = Permission[sql.WarehousePermissionLevel](p).ToAccessControlRequest() + } + return result +} diff --git a/bundle/config/resources/pipeline.go b/bundle/config/resources/pipeline.go index 2ab2c16f24..c4ce111103 100644 --- a/bundle/config/resources/pipeline.go +++ b/bundle/config/resources/pipeline.go @@ -14,7 +14,7 @@ type Pipeline struct { BaseResource pipelines.CreatePipeline //nolint CreatePipeline also defines Id field with the same json tag "id" - Permissions Permissions[pipelines.PipelinePermissionLevel] `json:"permissions,omitempty"` + Permissions PipelinePermissions `json:"permissions,omitempty"` } func (p *Pipeline) UnmarshalJSON(b []byte) error { diff --git a/bundle/config/resources/postgres_project.go b/bundle/config/resources/postgres_project.go index 8211c61dbb..fe502586b1 100644 --- a/bundle/config/resources/postgres_project.go +++ b/bundle/config/resources/postgres_project.go @@ -7,7 +7,6 @@ import ( "github.com/databricks/cli/libs/log" "github.com/databricks/databricks-sdk-go" "github.com/databricks/databricks-sdk-go/marshal" - "github.com/databricks/databricks-sdk-go/service/iam" "github.com/databricks/databricks-sdk-go/service/postgres" ) @@ -31,7 +30,7 @@ type PostgresProject struct { BaseResource PostgresProjectConfig - Permissions Permissions[iam.PermissionLevel] `json:"permissions,omitempty"` + Permissions DatabaseProjectPermissions `json:"permissions,omitempty"` } func (p *PostgresProject) Exists(ctx context.Context, w *databricks.WorkspaceClient, name string) (bool, error) { diff --git a/bundle/config/resources/sql_warehouses.go b/bundle/config/resources/sql_warehouses.go index 7f52b5822c..b1026486df 100644 --- a/bundle/config/resources/sql_warehouses.go +++ b/bundle/config/resources/sql_warehouses.go @@ -14,7 +14,7 @@ type SqlWarehouse struct { BaseResource sql.CreateWarehouseRequest - Permissions Permissions[sql.WarehousePermissionLevel] `json:"permissions,omitempty"` + Permissions SqlWarehousePermissions `json:"permissions,omitempty"` } func (sw *SqlWarehouse) UnmarshalJSON(b []byte) error { diff --git a/bundle/config/resources_types_test.go b/bundle/config/resources_types_test.go index 2d5b45b9b8..4ca133f844 100644 --- a/bundle/config/resources_types_test.go +++ b/bundle/config/resources_types_test.go @@ -7,7 +7,6 @@ import ( "github.com/stretchr/testify/assert" "github.com/databricks/cli/bundle/config/resources" - "github.com/databricks/databricks-sdk-go/service/jobs" ) func TestResourcesTypesMap(t *testing.T) { @@ -19,5 +18,5 @@ func TestResourcesTypesMap(t *testing.T) { typ, ok = ResourcesTypes["jobs.permissions"] assert.True(t, ok, "resources type for 'jobs.permissions' not found in ResourcesTypes map") - assert.Equal(t, reflect.TypeOf(resources.Permissions[jobs.JobPermissionLevel]{}), typ, "resources type for 'jobs.permissions' mismatch") + assert.Equal(t, reflect.TypeOf(resources.JobPermissions{}), typ, "resources type for 'jobs.permissions' mismatch") } diff --git a/bundle/deploy/terraform/convert_test.go b/bundle/deploy/terraform/convert_test.go index dfb315eb9e..d3d5dbaced 100644 --- a/bundle/deploy/terraform/convert_test.go +++ b/bundle/deploy/terraform/convert_test.go @@ -91,7 +91,7 @@ func TestBundleToTerraformJob(t *testing.T) { func TestBundleToTerraformJobPermissions(t *testing.T) { src := resources.Job{ - Permissions: resources.Permissions[jobs.JobPermissionLevel]{ + Permissions: resources.JobPermissions{ { Level: "CAN_VIEW", UserName: "jane@doe.com", @@ -260,7 +260,7 @@ func TestBundleToTerraformPipeline(t *testing.T) { func TestBundleToTerraformPipelinePermissions(t *testing.T) { src := resources.Pipeline{ - Permissions: resources.Permissions[pipelines.PipelinePermissionLevel]{ + Permissions: resources.PipelinePermissions{ { Level: "CAN_VIEW", UserName: "jane@doe.com", @@ -331,7 +331,7 @@ func TestBundleToTerraformModelPermissions(t *testing.T) { CreateModelRequest: ml.CreateModelRequest{ Name: "name", }, - Permissions: resources.Permissions[ml.RegisteredModelPermissionLevel]{ + Permissions: resources.MlflowModelPermissions{ { Level: "CAN_READ", UserName: "jane@doe.com", @@ -385,7 +385,7 @@ func TestBundleToTerraformExperimentPermissions(t *testing.T) { CreateExperiment: ml.CreateExperiment{ Name: "name", }, - Permissions: resources.Permissions[ml.ExperimentPermissionLevel]{ + Permissions: resources.MlflowExperimentPermissions{ { Level: "CAN_READ", UserName: "jane@doe.com", @@ -481,7 +481,7 @@ func TestBundleToTerraformModelServingPermissions(t *testing.T) { }, }, }, - Permissions: resources.Permissions[serving.ServingEndpointPermissionLevel]{ + Permissions: resources.ModelServingEndpointPermissions{ { Level: "CAN_VIEW", UserName: "jane@doe.com", diff --git a/bundle/deploy/terraform/tfdyn/convert_alert_test.go b/bundle/deploy/terraform/tfdyn/convert_alert_test.go index 1b76bbf593..0d474b5356 100644 --- a/bundle/deploy/terraform/tfdyn/convert_alert_test.go +++ b/bundle/deploy/terraform/tfdyn/convert_alert_test.go @@ -7,7 +7,6 @@ import ( "github.com/databricks/cli/bundle/internal/tf/schema" "github.com/databricks/cli/libs/dyn" "github.com/databricks/cli/libs/dyn/convert" - "github.com/databricks/databricks-sdk-go/service/iam" "github.com/databricks/databricks-sdk-go/service/sql" "github.com/stretchr/testify/assert" "github.com/stretchr/testify/require" @@ -22,7 +21,7 @@ func TestConvertAlert(t *testing.T) { CustomSummary: "Test alert summary", CustomDescription: "Test alert description", }, - Permissions: resources.Permissions[iam.PermissionLevel]{ + Permissions: resources.AlertPermissions{ { Level: "CAN_VIEW", UserName: "jane@doe.com", diff --git a/bundle/deploy/terraform/tfdyn/convert_app_test.go b/bundle/deploy/terraform/tfdyn/convert_app_test.go index 8073280b5e..171d7f27a4 100644 --- a/bundle/deploy/terraform/tfdyn/convert_app_test.go +++ b/bundle/deploy/terraform/tfdyn/convert_app_test.go @@ -35,7 +35,7 @@ func TestConvertApp(t *testing.T) { }, }, }, - Permissions: resources.Permissions[appssdk.AppPermissionLevel]{ + Permissions: resources.AppPermissions{ { Level: "CAN_RUN", UserName: "jack@gmail.com", diff --git a/bundle/deploy/terraform/tfdyn/convert_cluster_test.go b/bundle/deploy/terraform/tfdyn/convert_cluster_test.go index ee8a82abe6..7d0c18c6ac 100644 --- a/bundle/deploy/terraform/tfdyn/convert_cluster_test.go +++ b/bundle/deploy/terraform/tfdyn/convert_cluster_test.go @@ -35,7 +35,7 @@ func TestConvertCluster(t *testing.T) { }, }, - Permissions: resources.Permissions[compute.ClusterPermissionLevel]{ + Permissions: resources.ClusterPermissions{ { Level: "CAN_RUN", UserName: "jack@gmail.com", diff --git a/bundle/deploy/terraform/tfdyn/convert_dashboard_test.go b/bundle/deploy/terraform/tfdyn/convert_dashboard_test.go index c8cca19769..0b827d8f19 100644 --- a/bundle/deploy/terraform/tfdyn/convert_dashboard_test.go +++ b/bundle/deploy/terraform/tfdyn/convert_dashboard_test.go @@ -7,7 +7,6 @@ import ( "github.com/databricks/cli/bundle/internal/tf/schema" "github.com/databricks/cli/libs/dyn" "github.com/databricks/cli/libs/dyn/convert" - "github.com/databricks/databricks-sdk-go/service/iam" "github.com/stretchr/testify/assert" "github.com/stretchr/testify/require" ) @@ -21,7 +20,7 @@ func TestConvertDashboard(t *testing.T) { EmbedCredentials: true, }, - Permissions: resources.Permissions[iam.PermissionLevel]{ + Permissions: resources.DashboardPermissions{ { Level: "CAN_VIEW", UserName: "jane@doe.com", diff --git a/bundle/deploy/terraform/tfdyn/convert_database_instance_test.go b/bundle/deploy/terraform/tfdyn/convert_database_instance_test.go index 925a5e0480..8b7a734a5e 100644 --- a/bundle/deploy/terraform/tfdyn/convert_database_instance_test.go +++ b/bundle/deploy/terraform/tfdyn/convert_database_instance_test.go @@ -8,7 +8,6 @@ import ( "github.com/databricks/cli/libs/dyn" "github.com/databricks/cli/libs/dyn/convert" "github.com/databricks/databricks-sdk-go/service/database" - "github.com/databricks/databricks-sdk-go/service/iam" "github.com/stretchr/testify/assert" "github.com/stretchr/testify/require" ) @@ -74,7 +73,7 @@ func TestConvertDatabaseInstanceWithPermissions(t *testing.T) { Name: "db-instance-with-permissions", Capacity: "CU_2", }, - Permissions: resources.Permissions[iam.PermissionLevel]{ + Permissions: resources.DatabaseInstancePermissions{ { Level: "CAN_USE", UserName: "user@example.com", diff --git a/bundle/deploy/terraform/tfdyn/convert_experiment_test.go b/bundle/deploy/terraform/tfdyn/convert_experiment_test.go index d86bc3dfba..f859603e7a 100644 --- a/bundle/deploy/terraform/tfdyn/convert_experiment_test.go +++ b/bundle/deploy/terraform/tfdyn/convert_experiment_test.go @@ -17,7 +17,7 @@ func TestConvertExperiment(t *testing.T) { CreateExperiment: ml.CreateExperiment{ Name: "name", }, - Permissions: resources.Permissions[ml.ExperimentPermissionLevel]{ + Permissions: resources.MlflowExperimentPermissions{ { Level: "CAN_READ", UserName: "jane@doe.com", diff --git a/bundle/deploy/terraform/tfdyn/convert_job_test.go b/bundle/deploy/terraform/tfdyn/convert_job_test.go index cf27d11a9d..412e4dc37b 100644 --- a/bundle/deploy/terraform/tfdyn/convert_job_test.go +++ b/bundle/deploy/terraform/tfdyn/convert_job_test.go @@ -71,7 +71,7 @@ func TestConvertJob(t *testing.T) { }, }, }, - Permissions: resources.Permissions[jobs.JobPermissionLevel]{ + Permissions: resources.JobPermissions{ { Level: "CAN_VIEW", UserName: "jane@doe.com", diff --git a/bundle/deploy/terraform/tfdyn/convert_model_serving_endpoint_test.go b/bundle/deploy/terraform/tfdyn/convert_model_serving_endpoint_test.go index c6ac5b1556..45e6d14f49 100644 --- a/bundle/deploy/terraform/tfdyn/convert_model_serving_endpoint_test.go +++ b/bundle/deploy/terraform/tfdyn/convert_model_serving_endpoint_test.go @@ -35,7 +35,7 @@ func TestConvertModelServingEndpoint(t *testing.T) { }, }, }, - Permissions: resources.Permissions[serving.ServingEndpointPermissionLevel]{ + Permissions: resources.ModelServingEndpointPermissions{ { Level: "CAN_VIEW", UserName: "jane@doe.com", diff --git a/bundle/deploy/terraform/tfdyn/convert_model_test.go b/bundle/deploy/terraform/tfdyn/convert_model_test.go index 93544b4707..7207a8dcd3 100644 --- a/bundle/deploy/terraform/tfdyn/convert_model_test.go +++ b/bundle/deploy/terraform/tfdyn/convert_model_test.go @@ -28,7 +28,7 @@ func TestConvertModel(t *testing.T) { }, }, }, - Permissions: resources.Permissions[ml.RegisteredModelPermissionLevel]{ + Permissions: resources.MlflowModelPermissions{ { Level: "CAN_READ", UserName: "jane@doe.com", diff --git a/bundle/deploy/terraform/tfdyn/convert_permissions_test.go b/bundle/deploy/terraform/tfdyn/convert_permissions_test.go index 3143681d12..a2b1df127d 100644 --- a/bundle/deploy/terraform/tfdyn/convert_permissions_test.go +++ b/bundle/deploy/terraform/tfdyn/convert_permissions_test.go @@ -7,14 +7,13 @@ import ( "github.com/databricks/cli/bundle/internal/tf/schema" "github.com/databricks/cli/libs/dyn" "github.com/databricks/cli/libs/dyn/convert" - "github.com/databricks/databricks-sdk-go/service/jobs" "github.com/stretchr/testify/assert" "github.com/stretchr/testify/require" ) func TestConvertPermissions(t *testing.T) { src := resources.Job{ - Permissions: resources.Permissions[jobs.JobPermissionLevel]{ + Permissions: resources.JobPermissions{ { Level: "CAN_VIEW", UserName: "jane@doe.com", @@ -73,7 +72,7 @@ func TestConvertPermissionsNil(t *testing.T) { func TestConvertPermissionsEmpty(t *testing.T) { src := resources.Job{ - Permissions: resources.Permissions[jobs.JobPermissionLevel]{}, + Permissions: resources.JobPermissions{}, } vin, err := convert.FromTyped(src, dyn.NilValue) diff --git a/bundle/deploy/terraform/tfdyn/convert_pipeline_test.go b/bundle/deploy/terraform/tfdyn/convert_pipeline_test.go index e6239dd341..9bf1e58499 100644 --- a/bundle/deploy/terraform/tfdyn/convert_pipeline_test.go +++ b/bundle/deploy/terraform/tfdyn/convert_pipeline_test.go @@ -64,7 +64,7 @@ func TestConvertPipeline(t *testing.T) { }, }, }, - Permissions: resources.Permissions[pipelines.PipelinePermissionLevel]{ + Permissions: resources.PipelinePermissions{ { Level: "CAN_VIEW", UserName: "jane@doe.com", diff --git a/bundle/deploy/terraform/tfdyn/convert_postgres_project_test.go b/bundle/deploy/terraform/tfdyn/convert_postgres_project_test.go index 26f0b44998..db40a13907 100644 --- a/bundle/deploy/terraform/tfdyn/convert_postgres_project_test.go +++ b/bundle/deploy/terraform/tfdyn/convert_postgres_project_test.go @@ -9,7 +9,6 @@ import ( "github.com/databricks/cli/libs/dyn" "github.com/databricks/cli/libs/dyn/convert" "github.com/databricks/databricks-sdk-go/common/types/duration" - "github.com/databricks/databricks-sdk-go/service/iam" "github.com/databricks/databricks-sdk-go/service/postgres" "github.com/stretchr/testify/assert" "github.com/stretchr/testify/require" @@ -65,7 +64,7 @@ func TestConvertPostgresProjectWithPermissions(t *testing.T) { PgVersion: 17, }, }, - Permissions: resources.Permissions[iam.PermissionLevel]{ + Permissions: resources.DatabaseProjectPermissions{ { Level: "CAN_USE", UserName: "user@example.com", diff --git a/bundle/internal/schema/annotations.yml b/bundle/internal/schema/annotations.yml index 2eb01cd85a..83da357b52 100644 --- a/bundle/internal/schema/annotations.yml +++ b/bundle/internal/schema/annotations.yml @@ -739,8 +739,8 @@ github.com/databricks/cli/bundle/config/resources.ModelServingEndpointPermission "user_name": "description": |- PLACEHOLDER -github.com/databricks/cli/bundle/config/resources.Permission: - "-": +? github.com/databricks/cli/bundle/config/resources.Permission[github.com/databricks/databricks-sdk-go/service/iam.PermissionLevel] +: "-": "description": |- Defines a permission for a specific entity. "markdown_description": |- diff --git a/bundle/internal/schema/annotations_openapi.yml b/bundle/internal/schema/annotations_openapi.yml index ad92c6a021..c75b9db7a0 100644 --- a/bundle/internal/schema/annotations_openapi.yml +++ b/bundle/internal/schema/annotations_openapi.yml @@ -1221,6 +1221,15 @@ github.com/databricks/databricks-sdk-go/service/apps.AppDeploymentStatus: State of the deployment. "x-databricks-field-behaviors_output_only": |- true +github.com/databricks/databricks-sdk-go/service/apps.AppPermissionLevel: + "_": + "description": |- + Permission level + "enum": + - |- + CAN_MANAGE + - |- + CAN_USE github.com/databricks/databricks-sdk-go/service/apps.AppResource: "app": {} "database": {} @@ -2031,6 +2040,17 @@ github.com/databricks/databricks-sdk-go/service/compute.ClusterLogConf: "description": |- destination needs to be provided, e.g. `{ "volumes": { "destination": "/Volumes/catalog/schema/volume/cluster_log" } }` +github.com/databricks/databricks-sdk-go/service/compute.ClusterPermissionLevel: + "_": + "description": |- + Permission level + "enum": + - |- + CAN_MANAGE + - |- + CAN_RESTART + - |- + CAN_ATTACH_TO github.com/databricks/databricks-sdk-go/service/compute.ClusterSpec: "_": "description": |- @@ -2966,6 +2986,49 @@ github.com/databricks/databricks-sdk-go/service/database.SyncedTableTriggeredUpd Progress of the active data synchronization pipeline. "x-databricks-field-behaviors_output_only": |- true +github.com/databricks/databricks-sdk-go/service/iam.PermissionLevel: + "_": + "description": |- + Permission level + "enum": + - |- + CAN_MANAGE + - |- + CAN_RESTART + - |- + CAN_ATTACH_TO + - |- + IS_OWNER + - |- + CAN_MANAGE_RUN + - |- + CAN_VIEW + - |- + CAN_READ + - |- + CAN_RUN + - |- + CAN_EDIT + - |- + CAN_USE + - |- + CAN_MANAGE_STAGING_VERSIONS + - |- + CAN_MANAGE_PRODUCTION_VERSIONS + - |- + CAN_EDIT_METADATA + - |- + CAN_VIEW_METADATA + - |- + CAN_BIND + - |- + CAN_QUERY + - |- + CAN_MONITOR + - |- + CAN_CREATE + - |- + CAN_MONITOR_ONLY github.com/databricks/databricks-sdk-go/service/jobs.AuthenticationMethod: "_": "enum": @@ -3339,6 +3402,19 @@ github.com/databricks/databricks-sdk-go/service/jobs.JobParameterDefinition: "name": "description": |- The name of the defined parameter. May only contain alphanumeric characters, `_`, `-`, and `.` +github.com/databricks/databricks-sdk-go/service/jobs.JobPermissionLevel: + "_": + "description": |- + Permission level + "enum": + - |- + CAN_MANAGE + - |- + IS_OWNER + - |- + CAN_MANAGE_RUN + - |- + CAN_VIEW github.com/databricks/databricks-sdk-go/service/jobs.JobRunAs: "_": "description": |- @@ -4120,6 +4196,17 @@ github.com/databricks/databricks-sdk-go/service/jobs.WebhookNotifications: "on_success": "description": |- An optional list of system notification IDs to call when the run completes successfully. A maximum of 3 destinations can be specified for the `on_success` property. +github.com/databricks/databricks-sdk-go/service/ml.ExperimentPermissionLevel: + "_": + "description": |- + Permission level + "enum": + - |- + CAN_MANAGE + - |- + CAN_EDIT + - |- + CAN_READ github.com/databricks/databricks-sdk-go/service/ml.ExperimentTag: "_": "description": |- @@ -4140,6 +4227,21 @@ github.com/databricks/databricks-sdk-go/service/ml.ModelTag: "value": "description": |- The tag value. +github.com/databricks/databricks-sdk-go/service/ml.RegisteredModelPermissionLevel: + "_": + "description": |- + Permission level + "enum": + - |- + CAN_MANAGE + - |- + CAN_MANAGE_PRODUCTION_VERSIONS + - |- + CAN_MANAGE_STAGING_VERSIONS + - |- + CAN_EDIT + - |- + CAN_READ github.com/databricks/databricks-sdk-go/service/pipelines.AutoFullRefreshPolicy: "_": "description": |- @@ -4603,6 +4705,19 @@ github.com/databricks/databricks-sdk-go/service/pipelines.PipelineLibrary: URI of the whl to be installed. "deprecation_message": |- This field is deprecated +github.com/databricks/databricks-sdk-go/service/pipelines.PipelinePermissionLevel: + "_": + "description": |- + Permission level + "enum": + - |- + CAN_MANAGE + - |- + IS_OWNER + - |- + CAN_RUN + - |- + CAN_VIEW github.com/databricks/databricks-sdk-go/service/pipelines.PipelineTrigger: "cron": {} "manual": {} @@ -5486,6 +5601,17 @@ github.com/databricks/databricks-sdk-go/service/serving.ServedModelInputWorkload GPU_LARGE - |- MULTIGPU_MEDIUM +github.com/databricks/databricks-sdk-go/service/serving.ServingEndpointPermissionLevel: + "_": + "description": |- + Permission level + "enum": + - |- + CAN_MANAGE + - |- + CAN_QUERY + - |- + CAN_VIEW github.com/databricks/databricks-sdk-go/service/serving.ServingModelWorkloadType: "_": "description": |- @@ -5705,6 +5831,21 @@ github.com/databricks/databricks-sdk-go/service/sql.SpotInstancePolicy: COST_OPTIMIZED - |- RELIABILITY_OPTIMIZED +github.com/databricks/databricks-sdk-go/service/sql.WarehousePermissionLevel: + "_": + "description": |- + Permission level + "enum": + - |- + CAN_MANAGE + - |- + IS_OWNER + - |- + CAN_USE + - |- + CAN_MONITOR + - |- + CAN_VIEW github.com/databricks/databricks-sdk-go/service/workspace.AzureKeyVaultSecretScopeMetadata: "_": "description": |- diff --git a/bundle/internal/schema/annotations_openapi_overrides.yml b/bundle/internal/schema/annotations_openapi_overrides.yml index e517c2ebcc..3a83d7a0d2 100644 --- a/bundle/internal/schema/annotations_openapi_overrides.yml +++ b/bundle/internal/schema/annotations_openapi_overrides.yml @@ -14,17 +14,6 @@ github.com/databricks/cli/bundle/config/resources.Alert: "schedule": "description": |- PLACEHOLDER -github.com/databricks/cli/bundle/config/resources.AlertPermissionLevel: - "_": - "enum": - - |- - CAN_EDIT - - |- - CAN_MANAGE - - |- - CAN_READ - - |- - CAN_RUN github.com/databricks/cli/bundle/config/resources.App: "app_status": "description": |- @@ -80,13 +69,6 @@ github.com/databricks/cli/bundle/config/resources.App: "user_api_scopes": "description": |- PLACEHOLDER -github.com/databricks/cli/bundle/config/resources.AppPermissionLevel: - "_": - "enum": - - |- - CAN_MANAGE - - |- - CAN_USE github.com/databricks/cli/bundle/config/resources.Catalog: "grants": "description": |- @@ -145,15 +127,6 @@ github.com/databricks/cli/bundle/config/resources.Cluster: "workload_type": "description": |- PLACEHOLDER -github.com/databricks/cli/bundle/config/resources.ClusterPermissionLevel: - "_": - "enum": - - |- - CAN_MANAGE - - |- - CAN_RESTART - - |- - CAN_ATTACH_TO github.com/databricks/cli/bundle/config/resources.Dashboard: "_": "markdown_description": |- @@ -226,17 +199,6 @@ github.com/databricks/cli/bundle/config/resources.Dashboard: "warehouse_id": "description": |- The warehouse ID used to run the dashboard. -github.com/databricks/cli/bundle/config/resources.DashboardPermissionLevel: - "_": - "enum": - - |- - CAN_READ - - |- - CAN_RUN - - |- - CAN_EDIT - - |- - CAN_MANAGE github.com/databricks/cli/bundle/config/resources.DatabaseCatalog: "create_database_if_not_exists": "description": |- @@ -261,22 +223,6 @@ github.com/databricks/cli/bundle/config/resources.ExternalLocation: "lifecycle": "description": |- PLACEHOLDER -github.com/databricks/cli/bundle/config/resources.DatabaseInstancePermissionLevel: - "_": - "enum": - - |- - CAN_CREATE - - |- - CAN_USE - - |- - CAN_MANAGE -github.com/databricks/cli/bundle/config/resources.DatabaseProjectPermissionLevel: - "_": - "enum": - - |- - CAN_USE - - |- - CAN_MANAGE github.com/databricks/cli/bundle/config/resources.Job: "_": "markdown_description": |- @@ -308,17 +254,6 @@ github.com/databricks/cli/bundle/config/resources.Job: "run_as": "description": |- PLACEHOLDER -github.com/databricks/cli/bundle/config/resources.JobPermissionLevel: - "_": - "enum": - - |- - CAN_MANAGE - - |- - CAN_MANAGE_RUN - - |- - CAN_VIEW - - |- - IS_OWNER github.com/databricks/cli/bundle/config/resources.MlflowExperiment: "_": "markdown_description": |- @@ -342,15 +277,6 @@ github.com/databricks/cli/bundle/config/resources.MlflowExperiment: "permissions": "description": |- PLACEHOLDER -github.com/databricks/cli/bundle/config/resources.MlflowExperimentPermissionLevel: - "_": - "enum": - - |- - CAN_MANAGE - - |- - CAN_EDIT - - |- - CAN_READ github.com/databricks/cli/bundle/config/resources.MlflowModel: "_": "markdown_description": |- @@ -361,19 +287,6 @@ github.com/databricks/cli/bundle/config/resources.MlflowModel: "permissions": "description": |- PLACEHOLDER -github.com/databricks/cli/bundle/config/resources.MlflowModelPermissionLevel: - "_": - "enum": - - |- - CAN_EDIT - - |- - CAN_MANAGE - - |- - CAN_MANAGE_STAGING_VERSIONS - - |- - CAN_MANAGE_PRODUCTION_VERSIONS - - |- - CAN_READ github.com/databricks/cli/bundle/config/resources.ModelServingEndpoint: "_": "markdown_description": |- @@ -409,15 +322,6 @@ github.com/databricks/cli/bundle/config/resources.ModelServingEndpoint: "permissions": "description": |- PLACEHOLDER -github.com/databricks/cli/bundle/config/resources.ModelServingEndpointPermissionLevel: - "_": - "enum": - - |- - CAN_MANAGE - - |- - CAN_QUERY - - |- - CAN_VIEW github.com/databricks/cli/bundle/config/resources.Pipeline: "_": "markdown_description": |- @@ -457,17 +361,6 @@ github.com/databricks/cli/bundle/config/resources.Pipeline: "trigger": "deprecation_message": |- Use continuous instead -github.com/databricks/cli/bundle/config/resources.PipelinePermissionLevel: - "_": - "enum": - - |- - CAN_MANAGE - - |- - IS_OWNER - - |- - CAN_RUN - - |- - CAN_VIEW github.com/databricks/cli/bundle/config/resources.QualityMonitor: "_": "markdown_description": |- @@ -638,17 +531,6 @@ github.com/databricks/cli/bundle/config/resources.SqlWarehouse: "warehouse_type": "description": |- PLACEHOLDER -github.com/databricks/cli/bundle/config/resources.SqlWarehousePermissionLevel: - "_": - "enum": - - |- - CAN_MANAGE - - |- - CAN_USE - - |- - CAN_MONITOR - - |- - CAN_VIEW github.com/databricks/cli/bundle/config/resources.SyncedDatabaseTable: "lifecycle": "description": |- diff --git a/bundle/internal/validation/generated/enum_fields.go b/bundle/internal/validation/generated/enum_fields.go index ae567b6185..81cf22dde8 100644 --- a/bundle/internal/validation/generated/enum_fields.go +++ b/bundle/internal/validation/generated/enum_fields.go @@ -8,12 +8,15 @@ var EnumFields = map[string][]string{ "artifacts.*.executable": {"bash", "sh", "cmd"}, "artifacts.*.type": {"whl", "jar"}, + "permissions[*].level": {"CAN_ATTACH_TO", "CAN_BIND", "CAN_CREATE", "CAN_EDIT", "CAN_EDIT_METADATA", "CAN_MANAGE", "CAN_MANAGE_PRODUCTION_VERSIONS", "CAN_MANAGE_RUN", "CAN_MANAGE_STAGING_VERSIONS", "CAN_MONITOR", "CAN_MONITOR_ONLY", "CAN_QUERY", "CAN_READ", "CAN_RESTART", "CAN_RUN", "CAN_USE", "CAN_VIEW", "CAN_VIEW_METADATA", "IS_OWNER"}, + "resources.alerts.*.evaluation.comparison_operator": {"EQUAL", "GREATER_THAN", "GREATER_THAN_OR_EQUAL", "IS_NOT_NULL", "IS_NULL", "LESS_THAN", "LESS_THAN_OR_EQUAL", "NOT_EQUAL"}, "resources.alerts.*.evaluation.empty_result_state": {"ERROR", "OK", "TRIGGERED", "UNKNOWN"}, "resources.alerts.*.evaluation.source.aggregation": {"AVG", "COUNT", "COUNT_DISTINCT", "MAX", "MEDIAN", "MIN", "STDDEV", "SUM"}, "resources.alerts.*.evaluation.state": {"ERROR", "OK", "TRIGGERED", "UNKNOWN"}, "resources.alerts.*.evaluation.threshold.column.aggregation": {"AVG", "COUNT", "COUNT_DISTINCT", "MAX", "MEDIAN", "MIN", "STDDEV", "SUM"}, "resources.alerts.*.lifecycle_state": {"ACTIVE", "DELETED"}, + "resources.alerts.*.permissions[*].level": {"CAN_ATTACH_TO", "CAN_BIND", "CAN_CREATE", "CAN_EDIT", "CAN_EDIT_METADATA", "CAN_MANAGE", "CAN_MANAGE_PRODUCTION_VERSIONS", "CAN_MANAGE_RUN", "CAN_MANAGE_STAGING_VERSIONS", "CAN_MONITOR", "CAN_MONITOR_ONLY", "CAN_QUERY", "CAN_READ", "CAN_RESTART", "CAN_RUN", "CAN_USE", "CAN_VIEW", "CAN_VIEW_METADATA", "IS_OWNER"}, "resources.alerts.*.schedule.pause_status": {"PAUSED", "UNPAUSED"}, "resources.apps.*.active_deployment.mode": {"AUTO_SYNC", "SNAPSHOT"}, @@ -23,6 +26,7 @@ var EnumFields = map[string][]string{ "resources.apps.*.compute_status.state": {"ACTIVE", "DELETING", "ERROR", "STARTING", "STOPPED", "STOPPING", "UPDATING"}, "resources.apps.*.pending_deployment.mode": {"AUTO_SYNC", "SNAPSHOT"}, "resources.apps.*.pending_deployment.status.state": {"CANCELLED", "FAILED", "IN_PROGRESS", "SUCCEEDED"}, + "resources.apps.*.permissions[*].level": {"CAN_MANAGE", "CAN_USE"}, "resources.apps.*.resources[*].database.permission": {"CAN_CONNECT_AND_CREATE"}, "resources.apps.*.resources[*].experiment.permission": {"CAN_EDIT", "CAN_MANAGE", "CAN_READ"}, "resources.apps.*.resources[*].genie_space.permission": {"CAN_EDIT", "CAN_MANAGE", "CAN_RUN", "CAN_VIEW"}, @@ -42,11 +46,16 @@ var EnumFields = map[string][]string{ "resources.clusters.*.data_security_mode": {"DATA_SECURITY_MODE_AUTO", "DATA_SECURITY_MODE_DEDICATED", "DATA_SECURITY_MODE_STANDARD", "LEGACY_PASSTHROUGH", "LEGACY_SINGLE_USER", "LEGACY_SINGLE_USER_STANDARD", "LEGACY_TABLE_ACL", "NONE", "SINGLE_USER", "USER_ISOLATION"}, "resources.clusters.*.gcp_attributes.availability": {"ON_DEMAND_GCP", "PREEMPTIBLE_GCP", "PREEMPTIBLE_WITH_FALLBACK_GCP"}, "resources.clusters.*.kind": {"CLASSIC_PREVIEW"}, + "resources.clusters.*.permissions[*].level": {"CAN_ATTACH_TO", "CAN_MANAGE", "CAN_RESTART"}, "resources.clusters.*.runtime_engine": {"NULL", "PHOTON", "STANDARD"}, - "resources.dashboards.*.lifecycle_state": {"ACTIVE", "TRASHED"}, + "resources.dashboards.*.lifecycle_state": {"ACTIVE", "TRASHED"}, + "resources.dashboards.*.permissions[*].level": {"CAN_ATTACH_TO", "CAN_BIND", "CAN_CREATE", "CAN_EDIT", "CAN_EDIT_METADATA", "CAN_MANAGE", "CAN_MANAGE_PRODUCTION_VERSIONS", "CAN_MANAGE_RUN", "CAN_MANAGE_STAGING_VERSIONS", "CAN_MONITOR", "CAN_MONITOR_ONLY", "CAN_QUERY", "CAN_READ", "CAN_RESTART", "CAN_RUN", "CAN_USE", "CAN_VIEW", "CAN_VIEW_METADATA", "IS_OWNER"}, + + "resources.database_instances.*.permissions[*].level": {"CAN_ATTACH_TO", "CAN_BIND", "CAN_CREATE", "CAN_EDIT", "CAN_EDIT_METADATA", "CAN_MANAGE", "CAN_MANAGE_PRODUCTION_VERSIONS", "CAN_MANAGE_RUN", "CAN_MANAGE_STAGING_VERSIONS", "CAN_MONITOR", "CAN_MONITOR_ONLY", "CAN_QUERY", "CAN_READ", "CAN_RESTART", "CAN_RUN", "CAN_USE", "CAN_VIEW", "CAN_VIEW_METADATA", "IS_OWNER"}, + "resources.database_instances.*.state": {"AVAILABLE", "DELETING", "FAILING_OVER", "STARTING", "STOPPED", "UPDATING"}, - "resources.database_instances.*.state": {"AVAILABLE", "DELETING", "FAILING_OVER", "STARTING", "STOPPED", "UPDATING"}, + "resources.experiments.*.permissions[*].level": {"CAN_EDIT", "CAN_MANAGE", "CAN_READ"}, "resources.external_locations.*.encryption_details.sse_encryption_details.algorithm": {"AWS_SSE_KMS", "AWS_SSE_S3"}, "resources.external_locations.*.grants[*].privileges[*]": {"ACCESS", "ALL_PRIVILEGES", "APPLY_TAG", "BROWSE", "CREATE", "CREATE_CATALOG", "CREATE_CLEAN_ROOM", "CREATE_CONNECTION", "CREATE_EXTERNAL_LOCATION", "CREATE_EXTERNAL_TABLE", "CREATE_EXTERNAL_VOLUME", "CREATE_FOREIGN_CATALOG", "CREATE_FOREIGN_SECURABLE", "CREATE_FUNCTION", "CREATE_MANAGED_STORAGE", "CREATE_MATERIALIZED_VIEW", "CREATE_MODEL", "CREATE_PROVIDER", "CREATE_RECIPIENT", "CREATE_SCHEMA", "CREATE_SERVICE_CREDENTIAL", "CREATE_SHARE", "CREATE_STORAGE_CREDENTIAL", "CREATE_TABLE", "CREATE_VIEW", "CREATE_VOLUME", "EXECUTE", "EXECUTE_CLEAN_ROOM_TASK", "EXTERNAL_USE_SCHEMA", "MANAGE", "MANAGE_ALLOWLIST", "MODIFY", "MODIFY_CLEAN_ROOM", "READ_FILES", "READ_PRIVATE_FILES", "READ_VOLUME", "REFRESH", "SELECT", "SET_SHARE_PERMISSION", "USAGE", "USE_CATALOG", "USE_CONNECTION", "USE_MARKETPLACE_ASSETS", "USE_PROVIDER", "USE_RECIPIENT", "USE_SCHEMA", "USE_SHARE", "WRITE_FILES", "WRITE_PRIVATE_FILES", "WRITE_VOLUME"}, @@ -68,6 +77,7 @@ var EnumFields = map[string][]string{ "resources.jobs.*.job_clusters[*].new_cluster.kind": {"CLASSIC_PREVIEW"}, "resources.jobs.*.job_clusters[*].new_cluster.runtime_engine": {"NULL", "PHOTON", "STANDARD"}, "resources.jobs.*.performance_target": {"PERFORMANCE_OPTIMIZED", "STANDARD"}, + "resources.jobs.*.permissions[*].level": {"CAN_MANAGE", "CAN_MANAGE_RUN", "CAN_VIEW", "IS_OWNER"}, "resources.jobs.*.schedule.pause_status": {"PAUSED", "UNPAUSED"}, "resources.jobs.*.tasks[*].compute.hardware_accelerator": {"GPU_1xA10", "GPU_8xH100"}, "resources.jobs.*.tasks[*].condition_task.op": {"EQUAL_TO", "GREATER_THAN", "GREATER_THAN_OR_EQUAL", "LESS_THAN", "LESS_THAN_OR_EQUAL", "NOT_EQUAL"}, @@ -122,9 +132,12 @@ var EnumFields = map[string][]string{ "resources.model_serving_endpoints.*.config.served_entities[*].external_model.provider": {"ai21labs", "amazon-bedrock", "anthropic", "cohere", "custom", "databricks-model-serving", "google-cloud-vertex-ai", "openai", "palm"}, "resources.model_serving_endpoints.*.config.served_entities[*].workload_type": {"CPU", "GPU_LARGE", "GPU_MEDIUM", "GPU_SMALL", "MULTIGPU_MEDIUM"}, "resources.model_serving_endpoints.*.config.served_models[*].workload_type": {"CPU", "GPU_LARGE", "GPU_MEDIUM", "GPU_SMALL", "MULTIGPU_MEDIUM"}, + "resources.model_serving_endpoints.*.permissions[*].level": {"CAN_MANAGE", "CAN_QUERY", "CAN_VIEW"}, "resources.model_serving_endpoints.*.rate_limits[*].key": {"endpoint", "user"}, "resources.model_serving_endpoints.*.rate_limits[*].renewal_period": {"minute"}, + "resources.models.*.permissions[*].level": {"CAN_EDIT", "CAN_MANAGE", "CAN_MANAGE_PRODUCTION_VERSIONS", "CAN_MANAGE_STAGING_VERSIONS", "CAN_READ"}, + "resources.pipelines.*.clusters[*].autoscale.mode": {"ENHANCED", "LEGACY"}, "resources.pipelines.*.clusters[*].aws_attributes.availability": {"ON_DEMAND", "SPOT", "SPOT_WITH_FALLBACK"}, "resources.pipelines.*.clusters[*].aws_attributes.ebs_volume_type": {"GENERAL_PURPOSE_SSD", "THROUGHPUT_OPTIMIZED_HDD"}, @@ -137,10 +150,13 @@ var EnumFields = map[string][]string{ "resources.pipelines.*.ingestion_definition.objects[*].table.table_configuration.scd_type": {"APPEND_ONLY", "SCD_TYPE_1", "SCD_TYPE_2"}, "resources.pipelines.*.ingestion_definition.source_type": {"BIGQUERY", "DYNAMICS365", "FOREIGN_CATALOG", "GA4_RAW_DATA", "MANAGED_POSTGRESQL", "MYSQL", "NETSUITE", "ORACLE", "POSTGRESQL", "SALESFORCE", "SERVICENOW", "SHAREPOINT", "SQLSERVER", "TERADATA", "WORKDAY_RAAS"}, "resources.pipelines.*.ingestion_definition.table_configuration.scd_type": {"APPEND_ONLY", "SCD_TYPE_1", "SCD_TYPE_2"}, + "resources.pipelines.*.permissions[*].level": {"CAN_MANAGE", "CAN_RUN", "CAN_VIEW", "IS_OWNER"}, "resources.pipelines.*.restart_window.days_of_week[*]": {"FRIDAY", "MONDAY", "SATURDAY", "SUNDAY", "THURSDAY", "TUESDAY", "WEDNESDAY"}, "resources.postgres_endpoints.*.endpoint_type": {"ENDPOINT_TYPE_READ_ONLY", "ENDPOINT_TYPE_READ_WRITE"}, + "resources.postgres_projects.*.permissions[*].level": {"CAN_ATTACH_TO", "CAN_BIND", "CAN_CREATE", "CAN_EDIT", "CAN_EDIT_METADATA", "CAN_MANAGE", "CAN_MANAGE_PRODUCTION_VERSIONS", "CAN_MANAGE_RUN", "CAN_MANAGE_STAGING_VERSIONS", "CAN_MONITOR", "CAN_MONITOR_ONLY", "CAN_QUERY", "CAN_READ", "CAN_RESTART", "CAN_RUN", "CAN_USE", "CAN_VIEW", "CAN_VIEW_METADATA", "IS_OWNER"}, + "resources.quality_monitors.*.custom_metrics[*].type": {"CUSTOM_METRIC_TYPE_AGGREGATE", "CUSTOM_METRIC_TYPE_DERIVED", "CUSTOM_METRIC_TYPE_DRIFT"}, "resources.quality_monitors.*.inference_log.problem_type": {"PROBLEM_TYPE_CLASSIFICATION", "PROBLEM_TYPE_REGRESSION"}, "resources.quality_monitors.*.schedule.pause_status": {"PAUSED", "UNPAUSED", "UNSPECIFIED"}, @@ -152,6 +168,7 @@ var EnumFields = map[string][]string{ "resources.secret_scopes.*.backend_type": {"AZURE_KEYVAULT", "DATABRICKS"}, "resources.sql_warehouses.*.channel.name": {"CHANNEL_NAME_CURRENT", "CHANNEL_NAME_CUSTOM", "CHANNEL_NAME_PREVIEW", "CHANNEL_NAME_PREVIOUS"}, + "resources.sql_warehouses.*.permissions[*].level": {"CAN_MANAGE", "CAN_MONITOR", "CAN_USE", "CAN_VIEW", "IS_OWNER"}, "resources.sql_warehouses.*.spot_instance_policy": {"COST_OPTIMIZED", "POLICY_UNSPECIFIED", "RELIABILITY_OPTIMIZED"}, "resources.sql_warehouses.*.warehouse_type": {"CLASSIC", "PRO", "TYPE_UNSPECIFIED"}, diff --git a/bundle/schema/jsonschema.json b/bundle/schema/jsonschema.json index 3376e134f4..b21b2fb95e 100644 --- a/bundle/schema/jsonschema.json +++ b/bundle/schema/jsonschema.json @@ -86,7 +86,7 @@ "$ref": "#/$defs/string" }, "permissions": { - "$ref": "#/$defs/slice/github.com/databricks/cli/bundle/config/resources.Permission[github.com/databricks/databricks-sdk-go/service/iam.PermissionLevel]" + "$ref": "#/$defs/slice/github.com/databricks/cli/bundle/config/resources.AlertPermission" }, "query_text": { "$ref": "#/$defs/string" @@ -121,6 +121,35 @@ } ] }, + "resources.AlertPermission": { + "oneOf": [ + { + "type": "object", + "properties": { + "group_name": { + "$ref": "#/$defs/string" + }, + "level": { + "$ref": "#/$defs/github.com/databricks/databricks-sdk-go/service/iam.PermissionLevel" + }, + "service_principal_name": { + "$ref": "#/$defs/string" + }, + "user_name": { + "$ref": "#/$defs/string" + } + }, + "additionalProperties": false, + "required": [ + "level" + ] + }, + { + "type": "string", + "pattern": "\\$\\{(var(\\.[a-zA-Z]+([-_]?[a-zA-Z0-9]+)*(\\[[0-9]+\\])*)+)\\}" + } + ] + }, "resources.App": { "oneOf": [ { @@ -158,7 +187,7 @@ "$ref": "#/$defs/string" }, "permissions": { - "$ref": "#/$defs/slice/github.com/databricks/cli/bundle/config/resources.Permission[github.com/databricks/databricks-sdk-go/service/apps.AppPermissionLevel]" + "$ref": "#/$defs/slice/github.com/databricks/cli/bundle/config/resources.AppPermission" }, "resources": { "description": "Resources for the app.", @@ -237,6 +266,35 @@ } ] }, + "resources.AppPermission": { + "oneOf": [ + { + "type": "object", + "properties": { + "group_name": { + "$ref": "#/$defs/string" + }, + "level": { + "$ref": "#/$defs/github.com/databricks/databricks-sdk-go/service/apps.AppPermissionLevel" + }, + "service_principal_name": { + "$ref": "#/$defs/string" + }, + "user_name": { + "$ref": "#/$defs/string" + } + }, + "additionalProperties": false, + "required": [ + "level" + ] + }, + { + "type": "string", + "pattern": "\\$\\{(var(\\.[a-zA-Z]+([-_]?[a-zA-Z0-9]+)*(\\[[0-9]+\\])*)+)\\}" + } + ] + }, "resources.Catalog": { "oneOf": [ { @@ -380,7 +438,7 @@ "$ref": "#/$defs/int" }, "permissions": { - "$ref": "#/$defs/slice/github.com/databricks/cli/bundle/config/resources.Permission[github.com/databricks/databricks-sdk-go/service/compute.ClusterPermissionLevel]" + "$ref": "#/$defs/slice/github.com/databricks/cli/bundle/config/resources.ClusterPermission" }, "policy_id": { "description": "The ID of the cluster policy used to create the cluster if applicable.", @@ -438,6 +496,35 @@ } ] }, + "resources.ClusterPermission": { + "oneOf": [ + { + "type": "object", + "properties": { + "group_name": { + "$ref": "#/$defs/string" + }, + "level": { + "$ref": "#/$defs/github.com/databricks/databricks-sdk-go/service/compute.ClusterPermissionLevel" + }, + "service_principal_name": { + "$ref": "#/$defs/string" + }, + "user_name": { + "$ref": "#/$defs/string" + } + }, + "additionalProperties": false, + "required": [ + "level" + ] + }, + { + "type": "string", + "pattern": "\\$\\{(var(\\.[a-zA-Z]+([-_]?[a-zA-Z0-9]+)*(\\[[0-9]+\\])*)+)\\}" + } + ] + }, "resources.Dashboard": { "oneOf": [ { @@ -490,7 +577,7 @@ "$ref": "#/$defs/string" }, "permissions": { - "$ref": "#/$defs/slice/github.com/databricks/cli/bundle/config/resources.Permission[github.com/databricks/databricks-sdk-go/service/iam.PermissionLevel]" + "$ref": "#/$defs/slice/github.com/databricks/cli/bundle/config/resources.DashboardPermission" }, "serialized_dashboard": { "description": "The contents of the dashboard in serialized string form.\nThis field is excluded in List Dashboards responses.\nUse the [get dashboard API](https://docs.databricks.com/api/workspace/lakeview/get)\nto retrieve an example response, which includes the `serialized_dashboard` field.\nThis field provides the structure of the JSON string that represents the dashboard's\nlayout and components.", @@ -514,6 +601,35 @@ } ] }, + "resources.DashboardPermission": { + "oneOf": [ + { + "type": "object", + "properties": { + "group_name": { + "$ref": "#/$defs/string" + }, + "level": { + "$ref": "#/$defs/github.com/databricks/databricks-sdk-go/service/iam.PermissionLevel" + }, + "service_principal_name": { + "$ref": "#/$defs/string" + }, + "user_name": { + "$ref": "#/$defs/string" + } + }, + "additionalProperties": false, + "required": [ + "level" + ] + }, + { + "type": "string", + "pattern": "\\$\\{(var(\\.[a-zA-Z]+([-_]?[a-zA-Z0-9]+)*(\\[[0-9]+\\])*)+)\\}" + } + ] + }, "resources.DatabaseCatalog": { "oneOf": [ { @@ -591,7 +707,7 @@ "$ref": "#/$defs/github.com/databricks/databricks-sdk-go/service/database.DatabaseInstanceRef" }, "permissions": { - "$ref": "#/$defs/slice/github.com/databricks/cli/bundle/config/resources.Permission[github.com/databricks/databricks-sdk-go/service/iam.PermissionLevel]" + "$ref": "#/$defs/slice/github.com/databricks/cli/bundle/config/resources.DatabaseInstancePermission" }, "retention_window_in_days": { "description": "The retention window for the instance. This is the time window in days\nfor which the historical data is retained. The default value is 7 days.\nValid values are 2 to 35 days.", @@ -617,6 +733,64 @@ } ] }, + "resources.DatabaseInstancePermission": { + "oneOf": [ + { + "type": "object", + "properties": { + "group_name": { + "$ref": "#/$defs/string" + }, + "level": { + "$ref": "#/$defs/github.com/databricks/databricks-sdk-go/service/iam.PermissionLevel" + }, + "service_principal_name": { + "$ref": "#/$defs/string" + }, + "user_name": { + "$ref": "#/$defs/string" + } + }, + "additionalProperties": false, + "required": [ + "level" + ] + }, + { + "type": "string", + "pattern": "\\$\\{(var(\\.[a-zA-Z]+([-_]?[a-zA-Z0-9]+)*(\\[[0-9]+\\])*)+)\\}" + } + ] + }, + "resources.DatabaseProjectPermission": { + "oneOf": [ + { + "type": "object", + "properties": { + "group_name": { + "$ref": "#/$defs/string" + }, + "level": { + "$ref": "#/$defs/github.com/databricks/databricks-sdk-go/service/iam.PermissionLevel" + }, + "service_principal_name": { + "$ref": "#/$defs/string" + }, + "user_name": { + "$ref": "#/$defs/string" + } + }, + "additionalProperties": false, + "required": [ + "level" + ] + }, + { + "type": "string", + "pattern": "\\$\\{(var(\\.[a-zA-Z]+([-_]?[a-zA-Z0-9]+)*(\\[[0-9]+\\])*)+)\\}" + } + ] + }, "resources.ExternalLocation": { "oneOf": [ { @@ -733,7 +907,7 @@ "$ref": "#/$defs/github.com/databricks/databricks-sdk-go/service/jobs.PerformanceTarget" }, "permissions": { - "$ref": "#/$defs/slice/github.com/databricks/cli/bundle/config/resources.Permission[github.com/databricks/databricks-sdk-go/service/jobs.JobPermissionLevel]" + "$ref": "#/$defs/slice/github.com/databricks/cli/bundle/config/resources.JobPermission" }, "queue": { "description": "The queue settings of the job.", @@ -782,6 +956,35 @@ } ] }, + "resources.JobPermission": { + "oneOf": [ + { + "type": "object", + "properties": { + "group_name": { + "$ref": "#/$defs/string" + }, + "level": { + "$ref": "#/$defs/github.com/databricks/databricks-sdk-go/service/jobs.JobPermissionLevel" + }, + "service_principal_name": { + "$ref": "#/$defs/string" + }, + "user_name": { + "$ref": "#/$defs/string" + } + }, + "additionalProperties": false, + "required": [ + "level" + ] + }, + { + "type": "string", + "pattern": "\\$\\{(var(\\.[a-zA-Z]+([-_]?[a-zA-Z0-9]+)*(\\[[0-9]+\\])*)+)\\}" + } + ] + }, "resources.Lifecycle": { "oneOf": [ { @@ -818,7 +1021,7 @@ "$ref": "#/$defs/string" }, "permissions": { - "$ref": "#/$defs/slice/github.com/databricks/cli/bundle/config/resources.Permission[github.com/databricks/databricks-sdk-go/service/ml.ExperimentPermissionLevel]" + "$ref": "#/$defs/slice/github.com/databricks/cli/bundle/config/resources.MlflowExperimentPermission" }, "tags": { "description": "A collection of tags to set on the experiment. Maximum tag size and number of tags per request\ndepends on the storage backend. All storage backends are guaranteed to support tag keys up\nto 250 bytes in size and tag values up to 5000 bytes in size. All storage backends are also\nguaranteed to support up to 20 tags per request.", @@ -837,6 +1040,35 @@ } ] }, + "resources.MlflowExperimentPermission": { + "oneOf": [ + { + "type": "object", + "properties": { + "group_name": { + "$ref": "#/$defs/string" + }, + "level": { + "$ref": "#/$defs/github.com/databricks/databricks-sdk-go/service/ml.ExperimentPermissionLevel" + }, + "service_principal_name": { + "$ref": "#/$defs/string" + }, + "user_name": { + "$ref": "#/$defs/string" + } + }, + "additionalProperties": false, + "required": [ + "level" + ] + }, + { + "type": "string", + "pattern": "\\$\\{(var(\\.[a-zA-Z]+([-_]?[a-zA-Z0-9]+)*(\\[[0-9]+\\])*)+)\\}" + } + ] + }, "resources.MlflowModel": { "oneOf": [ { @@ -855,7 +1087,7 @@ "$ref": "#/$defs/string" }, "permissions": { - "$ref": "#/$defs/slice/github.com/databricks/cli/bundle/config/resources.Permission[github.com/databricks/databricks-sdk-go/service/ml.RegisteredModelPermissionLevel]" + "$ref": "#/$defs/slice/github.com/databricks/cli/bundle/config/resources.MlflowModelPermission" }, "tags": { "description": "Additional metadata for registered model.", @@ -874,6 +1106,35 @@ } ] }, + "resources.MlflowModelPermission": { + "oneOf": [ + { + "type": "object", + "properties": { + "group_name": { + "$ref": "#/$defs/string" + }, + "level": { + "$ref": "#/$defs/github.com/databricks/databricks-sdk-go/service/ml.RegisteredModelPermissionLevel" + }, + "service_principal_name": { + "$ref": "#/$defs/string" + }, + "user_name": { + "$ref": "#/$defs/string" + } + }, + "additionalProperties": false, + "required": [ + "level" + ] + }, + { + "type": "string", + "pattern": "\\$\\{(var(\\.[a-zA-Z]+([-_]?[a-zA-Z0-9]+)*(\\[[0-9]+\\])*)+)\\}" + } + ] + }, "resources.ModelServingEndpoint": { "oneOf": [ { @@ -907,7 +1168,7 @@ "$ref": "#/$defs/string" }, "permissions": { - "$ref": "#/$defs/slice/github.com/databricks/cli/bundle/config/resources.Permission[github.com/databricks/databricks-sdk-go/service/serving.ServingEndpointPermissionLevel]" + "$ref": "#/$defs/slice/github.com/databricks/cli/bundle/config/resources.ModelServingEndpointPermission" }, "rate_limits": { "description": "Rate limits to be applied to the serving endpoint. NOTE: this field is deprecated, please use AI Gateway to manage rate limits.", @@ -936,275 +1197,40 @@ } ] }, - "resources.Permission[github.com": { - "databricks": { - "databricks-sdk-go": { - "service": { - "apps.AppPermissionLevel]": { - "oneOf": [ - { - "type": "object", - "properties": { - "group_name": { - "description": "The name of the group that has the permission set in level.", - "$ref": "#/$defs/string" - }, - "level": { - "description": "The allowed permission for user, group, service principal defined for this permission.", - "$ref": "#/$defs/github.com/databricks/databricks-sdk-go/service/apps.AppPermissionLevel" - }, - "service_principal_name": { - "description": "The name of the service principal that has the permission set in level.", - "$ref": "#/$defs/string" - }, - "user_name": { - "description": "The name of the user that has the permission set in level.", - "$ref": "#/$defs/string" - } - }, - "additionalProperties": false, - "required": [ - "level" - ] - }, - { - "type": "string", - "pattern": "\\$\\{(var(\\.[a-zA-Z]+([-_]?[a-zA-Z0-9]+)*(\\[[0-9]+\\])*)+)\\}" - } - ] - }, - "compute.ClusterPermissionLevel]": { - "oneOf": [ - { - "type": "object", - "properties": { - "group_name": { - "description": "The name of the group that has the permission set in level.", - "$ref": "#/$defs/string" - }, - "level": { - "description": "The allowed permission for user, group, service principal defined for this permission.", - "$ref": "#/$defs/github.com/databricks/databricks-sdk-go/service/compute.ClusterPermissionLevel" - }, - "service_principal_name": { - "description": "The name of the service principal that has the permission set in level.", - "$ref": "#/$defs/string" - }, - "user_name": { - "description": "The name of the user that has the permission set in level.", - "$ref": "#/$defs/string" - } - }, - "additionalProperties": false, - "required": [ - "level" - ] - }, - { - "type": "string", - "pattern": "\\$\\{(var(\\.[a-zA-Z]+([-_]?[a-zA-Z0-9]+)*(\\[[0-9]+\\])*)+)\\}" - } - ] - }, - "iam.PermissionLevel]": { - "oneOf": [ - { - "type": "object", - "properties": { - "group_name": { - "description": "The name of the group that has the permission set in level.", - "$ref": "#/$defs/string" - }, - "level": { - "description": "The allowed permission for user, group, service principal defined for this permission.", - "$ref": "#/$defs/github.com/databricks/databricks-sdk-go/service/iam.PermissionLevel" - }, - "service_principal_name": { - "description": "The name of the service principal that has the permission set in level.", - "$ref": "#/$defs/string" - }, - "user_name": { - "description": "The name of the user that has the permission set in level.", - "$ref": "#/$defs/string" - } - }, - "additionalProperties": false, - "required": [ - "level" - ] - }, - { - "type": "string", - "pattern": "\\$\\{(var(\\.[a-zA-Z]+([-_]?[a-zA-Z0-9]+)*(\\[[0-9]+\\])*)+)\\}" - } - ] - }, - "jobs.JobPermissionLevel]": { - "oneOf": [ - { - "type": "object", - "properties": { - "group_name": { - "description": "The name of the group that has the permission set in level.", - "$ref": "#/$defs/string" - }, - "level": { - "description": "The allowed permission for user, group, service principal defined for this permission.", - "$ref": "#/$defs/github.com/databricks/databricks-sdk-go/service/jobs.JobPermissionLevel" - }, - "service_principal_name": { - "description": "The name of the service principal that has the permission set in level.", - "$ref": "#/$defs/string" - }, - "user_name": { - "description": "The name of the user that has the permission set in level.", - "$ref": "#/$defs/string" - } - }, - "additionalProperties": false, - "required": [ - "level" - ] - }, - { - "type": "string", - "pattern": "\\$\\{(var(\\.[a-zA-Z]+([-_]?[a-zA-Z0-9]+)*(\\[[0-9]+\\])*)+)\\}" - } - ] - }, - "ml.ExperimentPermissionLevel]": { - "oneOf": [ - { - "type": "object", - "properties": { - "group_name": { - "description": "The name of the group that has the permission set in level.", - "$ref": "#/$defs/string" - }, - "level": { - "description": "The allowed permission for user, group, service principal defined for this permission.", - "$ref": "#/$defs/github.com/databricks/databricks-sdk-go/service/ml.ExperimentPermissionLevel" - }, - "service_principal_name": { - "description": "The name of the service principal that has the permission set in level.", - "$ref": "#/$defs/string" - }, - "user_name": { - "description": "The name of the user that has the permission set in level.", - "$ref": "#/$defs/string" - } - }, - "additionalProperties": false, - "required": [ - "level" - ] - }, - { - "type": "string", - "pattern": "\\$\\{(var(\\.[a-zA-Z]+([-_]?[a-zA-Z0-9]+)*(\\[[0-9]+\\])*)+)\\}" - } - ] - }, - "ml.RegisteredModelPermissionLevel]": { - "oneOf": [ - { - "type": "object", - "properties": { - "group_name": { - "description": "The name of the group that has the permission set in level.", - "$ref": "#/$defs/string" - }, - "level": { - "description": "The allowed permission for user, group, service principal defined for this permission.", - "$ref": "#/$defs/github.com/databricks/databricks-sdk-go/service/ml.RegisteredModelPermissionLevel" - }, - "service_principal_name": { - "description": "The name of the service principal that has the permission set in level.", - "$ref": "#/$defs/string" - }, - "user_name": { - "description": "The name of the user that has the permission set in level.", - "$ref": "#/$defs/string" - } - }, - "additionalProperties": false, - "required": [ - "level" - ] - }, - { - "type": "string", - "pattern": "\\$\\{(var(\\.[a-zA-Z]+([-_]?[a-zA-Z0-9]+)*(\\[[0-9]+\\])*)+)\\}" - } - ] + "resources.ModelServingEndpointPermission": { + "oneOf": [ + { + "type": "object", + "properties": { + "group_name": { + "$ref": "#/$defs/string" }, - "pipelines.PipelinePermissionLevel]": { - "oneOf": [ - { - "type": "object", - "properties": { - "group_name": { - "description": "The name of the group that has the permission set in level.", - "$ref": "#/$defs/string" - }, - "level": { - "description": "The allowed permission for user, group, service principal defined for this permission.", - "$ref": "#/$defs/github.com/databricks/databricks-sdk-go/service/pipelines.PipelinePermissionLevel" - }, - "service_principal_name": { - "description": "The name of the service principal that has the permission set in level.", - "$ref": "#/$defs/string" - }, - "user_name": { - "description": "The name of the user that has the permission set in level.", - "$ref": "#/$defs/string" - } - }, - "additionalProperties": false, - "required": [ - "level" - ] - }, - { - "type": "string", - "pattern": "\\$\\{(var(\\.[a-zA-Z]+([-_]?[a-zA-Z0-9]+)*(\\[[0-9]+\\])*)+)\\}" - } - ] + "level": { + "$ref": "#/$defs/github.com/databricks/databricks-sdk-go/service/serving.ServingEndpointPermissionLevel" }, - "serving.ServingEndpointPermissionLevel]": { - "oneOf": [ - { - "type": "object", - "properties": { - "group_name": { - "description": "The name of the group that has the permission set in level.", - "$ref": "#/$defs/string" - }, - "level": { - "description": "The allowed permission for user, group, service principal defined for this permission.", - "$ref": "#/$defs/github.com/databricks/databricks-sdk-go/service/serving.ServingEndpointPermissionLevel" - }, - "service_principal_name": { - "description": "The name of the service principal that has the permission set in level.", - "$ref": "#/$defs/string" - }, - "user_name": { - "description": "The name of the user that has the permission set in level.", - "$ref": "#/$defs/string" - } - }, - "additionalProperties": false, - "required": [ - "level" - ] - }, - { - "type": "string", - "pattern": "\\$\\{(var(\\.[a-zA-Z]+([-_]?[a-zA-Z0-9]+)*(\\[[0-9]+\\])*)+)\\}" - } - ] + "service_principal_name": { + "$ref": "#/$defs/string" }, - "sql.WarehousePermissionLevel]": { + "user_name": { + "$ref": "#/$defs/string" + } + }, + "additionalProperties": false, + "required": [ + "level" + ] + }, + { + "type": "string", + "pattern": "\\$\\{(var(\\.[a-zA-Z]+([-_]?[a-zA-Z0-9]+)*(\\[[0-9]+\\])*)+)\\}" + } + ] + }, + "resources.Permission[github.com": { + "databricks": { + "databricks-sdk-go": { + "service": { + "iam.PermissionLevel]": { "oneOf": [ { "type": "object", @@ -1215,7 +1241,7 @@ }, "level": { "description": "The allowed permission for user, group, service principal defined for this permission.", - "$ref": "#/$defs/github.com/databricks/databricks-sdk-go/service/sql.WarehousePermissionLevel" + "$ref": "#/$defs/github.com/databricks/databricks-sdk-go/service/iam.PermissionLevel" }, "service_principal_name": { "description": "The name of the service principal that has the permission set in level.", @@ -1325,7 +1351,7 @@ "$ref": "#/$defs/slice/github.com/databricks/databricks-sdk-go/service/pipelines.Notifications" }, "permissions": { - "$ref": "#/$defs/slice/github.com/databricks/cli/bundle/config/resources.Permission[github.com/databricks/databricks-sdk-go/service/pipelines.PipelinePermissionLevel]" + "$ref": "#/$defs/slice/github.com/databricks/cli/bundle/config/resources.PipelinePermission" }, "photon": { "description": "Whether Photon is enabled for this pipeline.", @@ -1388,6 +1414,35 @@ } ] }, + "resources.PipelinePermission": { + "oneOf": [ + { + "type": "object", + "properties": { + "group_name": { + "$ref": "#/$defs/string" + }, + "level": { + "$ref": "#/$defs/github.com/databricks/databricks-sdk-go/service/pipelines.PipelinePermissionLevel" + }, + "service_principal_name": { + "$ref": "#/$defs/string" + }, + "user_name": { + "$ref": "#/$defs/string" + } + }, + "additionalProperties": false, + "required": [ + "level" + ] + }, + { + "type": "string", + "pattern": "\\$\\{(var(\\.[a-zA-Z]+([-_]?[a-zA-Z0-9]+)*(\\[[0-9]+\\])*)+)\\}" + } + ] + }, "resources.PostgresBranch": { "oneOf": [ { @@ -1515,7 +1570,7 @@ "$ref": "#/$defs/github.com/databricks/cli/bundle/config/resources.Lifecycle" }, "permissions": { - "$ref": "#/$defs/slice/github.com/databricks/cli/bundle/config/resources.Permission[github.com/databricks/databricks-sdk-go/service/iam.PermissionLevel]" + "$ref": "#/$defs/slice/github.com/databricks/cli/bundle/config/resources.DatabaseProjectPermission" }, "pg_version": { "$ref": "#/$defs/int" @@ -1872,7 +1927,7 @@ "$ref": "#/$defs/string" }, "permissions": { - "$ref": "#/$defs/slice/github.com/databricks/cli/bundle/config/resources.Permission[github.com/databricks/databricks-sdk-go/service/sql.WarehousePermissionLevel]" + "$ref": "#/$defs/slice/github.com/databricks/cli/bundle/config/resources.SqlWarehousePermission" }, "spot_instance_policy": { "$ref": "#/$defs/github.com/databricks/databricks-sdk-go/service/sql.SpotInstancePolicy" @@ -1893,6 +1948,35 @@ } ] }, + "resources.SqlWarehousePermission": { + "oneOf": [ + { + "type": "object", + "properties": { + "group_name": { + "$ref": "#/$defs/string" + }, + "level": { + "$ref": "#/$defs/github.com/databricks/databricks-sdk-go/service/sql.WarehousePermissionLevel" + }, + "service_principal_name": { + "$ref": "#/$defs/string" + }, + "user_name": { + "$ref": "#/$defs/string" + } + }, + "additionalProperties": false, + "required": [ + "level" + ] + }, + { + "type": "string", + "pattern": "\\$\\{(var(\\.[a-zA-Z]+([-_]?[a-zA-Z0-9]+)*(\\[[0-9]+\\])*)+)\\}" + } + ] + }, "resources.SyncedDatabaseTable": { "oneOf": [ { @@ -2854,7 +2938,20 @@ ] }, "apps.AppPermissionLevel": { - "type": "string" + "oneOf": [ + { + "type": "string", + "description": "Permission level", + "enum": [ + "CAN_MANAGE", + "CAN_USE" + ] + }, + { + "type": "string", + "pattern": "\\$\\{(var(\\.[a-zA-Z]+([-_]?[a-zA-Z0-9]+)*(\\[[0-9]+\\])*)+)\\}" + } + ] }, "apps.AppResource": { "oneOf": [ @@ -4247,7 +4344,21 @@ ] }, "compute.ClusterPermissionLevel": { - "type": "string" + "oneOf": [ + { + "type": "string", + "description": "Permission level", + "enum": [ + "CAN_MANAGE", + "CAN_RESTART", + "CAN_ATTACH_TO" + ] + }, + { + "type": "string", + "pattern": "\\$\\{(var(\\.[a-zA-Z]+([-_]?[a-zA-Z0-9]+)*(\\[[0-9]+\\])*)+)\\}" + } + ] }, "compute.ClusterSpec": { "oneOf": [ @@ -5357,7 +5468,37 @@ ] }, "iam.PermissionLevel": { - "type": "string" + "oneOf": [ + { + "type": "string", + "description": "Permission level", + "enum": [ + "CAN_MANAGE", + "CAN_RESTART", + "CAN_ATTACH_TO", + "IS_OWNER", + "CAN_MANAGE_RUN", + "CAN_VIEW", + "CAN_READ", + "CAN_RUN", + "CAN_EDIT", + "CAN_USE", + "CAN_MANAGE_STAGING_VERSIONS", + "CAN_MANAGE_PRODUCTION_VERSIONS", + "CAN_EDIT_METADATA", + "CAN_VIEW_METADATA", + "CAN_BIND", + "CAN_QUERY", + "CAN_MONITOR", + "CAN_CREATE", + "CAN_MONITOR_ONLY" + ] + }, + { + "type": "string", + "pattern": "\\$\\{(var(\\.[a-zA-Z]+([-_]?[a-zA-Z0-9]+)*(\\[[0-9]+\\])*)+)\\}" + } + ] }, "jobs.AuthenticationMethod": { "oneOf": [ @@ -6097,7 +6238,22 @@ ] }, "jobs.JobPermissionLevel": { - "type": "string" + "oneOf": [ + { + "type": "string", + "description": "Permission level", + "enum": [ + "CAN_MANAGE", + "IS_OWNER", + "CAN_MANAGE_RUN", + "CAN_VIEW" + ] + }, + { + "type": "string", + "pattern": "\\$\\{(var(\\.[a-zA-Z]+([-_]?[a-zA-Z0-9]+)*(\\[[0-9]+\\])*)+)\\}" + } + ] }, "jobs.JobRunAs": { "oneOf": [ @@ -7465,7 +7621,21 @@ ] }, "ml.ExperimentPermissionLevel": { - "type": "string" + "oneOf": [ + { + "type": "string", + "description": "Permission level", + "enum": [ + "CAN_MANAGE", + "CAN_EDIT", + "CAN_READ" + ] + }, + { + "type": "string", + "pattern": "\\$\\{(var(\\.[a-zA-Z]+([-_]?[a-zA-Z0-9]+)*(\\[[0-9]+\\])*)+)\\}" + } + ] }, "ml.ExperimentTag": { "oneOf": [ @@ -7514,7 +7684,23 @@ ] }, "ml.RegisteredModelPermissionLevel": { - "type": "string" + "oneOf": [ + { + "type": "string", + "description": "Permission level", + "enum": [ + "CAN_MANAGE", + "CAN_MANAGE_PRODUCTION_VERSIONS", + "CAN_MANAGE_STAGING_VERSIONS", + "CAN_EDIT", + "CAN_READ" + ] + }, + { + "type": "string", + "pattern": "\\$\\{(var(\\.[a-zA-Z]+([-_]?[a-zA-Z0-9]+)*(\\[[0-9]+\\])*)+)\\}" + } + ] }, "pipelines.AutoFullRefreshPolicy": { "oneOf": [ @@ -8226,7 +8412,22 @@ ] }, "pipelines.PipelinePermissionLevel": { - "type": "string" + "oneOf": [ + { + "type": "string", + "description": "Permission level", + "enum": [ + "CAN_MANAGE", + "IS_OWNER", + "CAN_RUN", + "CAN_VIEW" + ] + }, + { + "type": "string", + "pattern": "\\$\\{(var(\\.[a-zA-Z]+([-_]?[a-zA-Z0-9]+)*(\\[[0-9]+\\])*)+)\\}" + } + ] }, "pipelines.PipelineTrigger": { "oneOf": [ @@ -9802,9 +10003,23 @@ } ] }, - "serving.ServingEndpointPermissionLevel": { - "type": "string" - }, + "serving.ServingEndpointPermissionLevel": { + "oneOf": [ + { + "type": "string", + "description": "Permission level", + "enum": [ + "CAN_MANAGE", + "CAN_QUERY", + "CAN_VIEW" + ] + }, + { + "type": "string", + "pattern": "\\$\\{(var(\\.[a-zA-Z]+([-_]?[a-zA-Z0-9]+)*(\\[[0-9]+\\])*)+)\\}" + } + ] + }, "serving.ServingModelWorkloadType": { "oneOf": [ { @@ -10245,7 +10460,23 @@ ] }, "sql.WarehousePermissionLevel": { - "type": "string" + "oneOf": [ + { + "type": "string", + "description": "Permission level", + "enum": [ + "CAN_MANAGE", + "IS_OWNER", + "CAN_USE", + "CAN_MONITOR", + "CAN_VIEW" + ] + }, + { + "type": "string", + "pattern": "\\$\\{(var(\\.[a-zA-Z]+([-_]?[a-zA-Z0-9]+)*(\\[[0-9]+\\])*)+)\\}" + } + ] }, "workspace.AzureKeyVaultSecretScopeMetadata": { "oneOf": [ @@ -10787,6 +11018,20 @@ "cli": { "bundle": { "config": { + "resources.AlertPermission": { + "oneOf": [ + { + "type": "array", + "items": { + "$ref": "#/$defs/github.com/databricks/cli/bundle/config/resources.AlertPermission" + } + }, + { + "type": "string", + "pattern": "\\$\\{(var(\\.[a-zA-Z]+([-_]?[a-zA-Z0-9]+)*(\\[[0-9]+\\])*)+)\\}" + } + ] + }, "resources.AppEnvVar": { "oneOf": [ { @@ -10801,38 +11046,136 @@ } ] }, + "resources.AppPermission": { + "oneOf": [ + { + "type": "array", + "items": { + "$ref": "#/$defs/github.com/databricks/cli/bundle/config/resources.AppPermission" + } + }, + { + "type": "string", + "pattern": "\\$\\{(var(\\.[a-zA-Z]+([-_]?[a-zA-Z0-9]+)*(\\[[0-9]+\\])*)+)\\}" + } + ] + }, + "resources.ClusterPermission": { + "oneOf": [ + { + "type": "array", + "items": { + "$ref": "#/$defs/github.com/databricks/cli/bundle/config/resources.ClusterPermission" + } + }, + { + "type": "string", + "pattern": "\\$\\{(var(\\.[a-zA-Z]+([-_]?[a-zA-Z0-9]+)*(\\[[0-9]+\\])*)+)\\}" + } + ] + }, + "resources.DashboardPermission": { + "oneOf": [ + { + "type": "array", + "items": { + "$ref": "#/$defs/github.com/databricks/cli/bundle/config/resources.DashboardPermission" + } + }, + { + "type": "string", + "pattern": "\\$\\{(var(\\.[a-zA-Z]+([-_]?[a-zA-Z0-9]+)*(\\[[0-9]+\\])*)+)\\}" + } + ] + }, + "resources.DatabaseInstancePermission": { + "oneOf": [ + { + "type": "array", + "items": { + "$ref": "#/$defs/github.com/databricks/cli/bundle/config/resources.DatabaseInstancePermission" + } + }, + { + "type": "string", + "pattern": "\\$\\{(var(\\.[a-zA-Z]+([-_]?[a-zA-Z0-9]+)*(\\[[0-9]+\\])*)+)\\}" + } + ] + }, + "resources.DatabaseProjectPermission": { + "oneOf": [ + { + "type": "array", + "items": { + "$ref": "#/$defs/github.com/databricks/cli/bundle/config/resources.DatabaseProjectPermission" + } + }, + { + "type": "string", + "pattern": "\\$\\{(var(\\.[a-zA-Z]+([-_]?[a-zA-Z0-9]+)*(\\[[0-9]+\\])*)+)\\}" + } + ] + }, + "resources.JobPermission": { + "oneOf": [ + { + "type": "array", + "items": { + "$ref": "#/$defs/github.com/databricks/cli/bundle/config/resources.JobPermission" + } + }, + { + "type": "string", + "pattern": "\\$\\{(var(\\.[a-zA-Z]+([-_]?[a-zA-Z0-9]+)*(\\[[0-9]+\\])*)+)\\}" + } + ] + }, + "resources.MlflowExperimentPermission": { + "oneOf": [ + { + "type": "array", + "items": { + "$ref": "#/$defs/github.com/databricks/cli/bundle/config/resources.MlflowExperimentPermission" + } + }, + { + "type": "string", + "pattern": "\\$\\{(var(\\.[a-zA-Z]+([-_]?[a-zA-Z0-9]+)*(\\[[0-9]+\\])*)+)\\}" + } + ] + }, + "resources.MlflowModelPermission": { + "oneOf": [ + { + "type": "array", + "items": { + "$ref": "#/$defs/github.com/databricks/cli/bundle/config/resources.MlflowModelPermission" + } + }, + { + "type": "string", + "pattern": "\\$\\{(var(\\.[a-zA-Z]+([-_]?[a-zA-Z0-9]+)*(\\[[0-9]+\\])*)+)\\}" + } + ] + }, + "resources.ModelServingEndpointPermission": { + "oneOf": [ + { + "type": "array", + "items": { + "$ref": "#/$defs/github.com/databricks/cli/bundle/config/resources.ModelServingEndpointPermission" + } + }, + { + "type": "string", + "pattern": "\\$\\{(var(\\.[a-zA-Z]+([-_]?[a-zA-Z0-9]+)*(\\[[0-9]+\\])*)+)\\}" + } + ] + }, "resources.Permission[github.com": { "databricks": { "databricks-sdk-go": { "service": { - "apps.AppPermissionLevel]": { - "oneOf": [ - { - "type": "array", - "items": { - "$ref": "#/$defs/github.com/databricks/cli/bundle/config/resources.Permission[github.com/databricks/databricks-sdk-go/service/apps.AppPermissionLevel]" - } - }, - { - "type": "string", - "pattern": "\\$\\{(var(\\.[a-zA-Z]+([-_]?[a-zA-Z0-9]+)*(\\[[0-9]+\\])*)+)\\}" - } - ] - }, - "compute.ClusterPermissionLevel]": { - "oneOf": [ - { - "type": "array", - "items": { - "$ref": "#/$defs/github.com/databricks/cli/bundle/config/resources.Permission[github.com/databricks/databricks-sdk-go/service/compute.ClusterPermissionLevel]" - } - }, - { - "type": "string", - "pattern": "\\$\\{(var(\\.[a-zA-Z]+([-_]?[a-zA-Z0-9]+)*(\\[[0-9]+\\])*)+)\\}" - } - ] - }, "iam.PermissionLevel]": { "oneOf": [ { @@ -10846,95 +11189,25 @@ "pattern": "\\$\\{(var(\\.[a-zA-Z]+([-_]?[a-zA-Z0-9]+)*(\\[[0-9]+\\])*)+)\\}" } ] - }, - "jobs.JobPermissionLevel]": { - "oneOf": [ - { - "type": "array", - "items": { - "$ref": "#/$defs/github.com/databricks/cli/bundle/config/resources.Permission[github.com/databricks/databricks-sdk-go/service/jobs.JobPermissionLevel]" - } - }, - { - "type": "string", - "pattern": "\\$\\{(var(\\.[a-zA-Z]+([-_]?[a-zA-Z0-9]+)*(\\[[0-9]+\\])*)+)\\}" - } - ] - }, - "ml.ExperimentPermissionLevel]": { - "oneOf": [ - { - "type": "array", - "items": { - "$ref": "#/$defs/github.com/databricks/cli/bundle/config/resources.Permission[github.com/databricks/databricks-sdk-go/service/ml.ExperimentPermissionLevel]" - } - }, - { - "type": "string", - "pattern": "\\$\\{(var(\\.[a-zA-Z]+([-_]?[a-zA-Z0-9]+)*(\\[[0-9]+\\])*)+)\\}" - } - ] - }, - "ml.RegisteredModelPermissionLevel]": { - "oneOf": [ - { - "type": "array", - "items": { - "$ref": "#/$defs/github.com/databricks/cli/bundle/config/resources.Permission[github.com/databricks/databricks-sdk-go/service/ml.RegisteredModelPermissionLevel]" - } - }, - { - "type": "string", - "pattern": "\\$\\{(var(\\.[a-zA-Z]+([-_]?[a-zA-Z0-9]+)*(\\[[0-9]+\\])*)+)\\}" - } - ] - }, - "pipelines.PipelinePermissionLevel]": { - "oneOf": [ - { - "type": "array", - "items": { - "$ref": "#/$defs/github.com/databricks/cli/bundle/config/resources.Permission[github.com/databricks/databricks-sdk-go/service/pipelines.PipelinePermissionLevel]" - } - }, - { - "type": "string", - "pattern": "\\$\\{(var(\\.[a-zA-Z]+([-_]?[a-zA-Z0-9]+)*(\\[[0-9]+\\])*)+)\\}" - } - ] - }, - "serving.ServingEndpointPermissionLevel]": { - "oneOf": [ - { - "type": "array", - "items": { - "$ref": "#/$defs/github.com/databricks/cli/bundle/config/resources.Permission[github.com/databricks/databricks-sdk-go/service/serving.ServingEndpointPermissionLevel]" - } - }, - { - "type": "string", - "pattern": "\\$\\{(var(\\.[a-zA-Z]+([-_]?[a-zA-Z0-9]+)*(\\[[0-9]+\\])*)+)\\}" - } - ] - }, - "sql.WarehousePermissionLevel]": { - "oneOf": [ - { - "type": "array", - "items": { - "$ref": "#/$defs/github.com/databricks/cli/bundle/config/resources.Permission[github.com/databricks/databricks-sdk-go/service/sql.WarehousePermissionLevel]" - } - }, - { - "type": "string", - "pattern": "\\$\\{(var(\\.[a-zA-Z]+([-_]?[a-zA-Z0-9]+)*(\\[[0-9]+\\])*)+)\\}" - } - ] } } } } }, + "resources.PipelinePermission": { + "oneOf": [ + { + "type": "array", + "items": { + "$ref": "#/$defs/github.com/databricks/cli/bundle/config/resources.PipelinePermission" + } + }, + { + "type": "string", + "pattern": "\\$\\{(var(\\.[a-zA-Z]+([-_]?[a-zA-Z0-9]+)*(\\[[0-9]+\\])*)+)\\}" + } + ] + }, "resources.SecretScopePermission": { "oneOf": [ { @@ -10948,6 +11221,20 @@ "pattern": "\\$\\{(var(\\.[a-zA-Z]+([-_]?[a-zA-Z0-9]+)*(\\[[0-9]+\\])*)+)\\}" } ] + }, + "resources.SqlWarehousePermission": { + "oneOf": [ + { + "type": "array", + "items": { + "$ref": "#/$defs/github.com/databricks/cli/bundle/config/resources.SqlWarehousePermission" + } + }, + { + "type": "string", + "pattern": "\\$\\{(var(\\.[a-zA-Z]+([-_]?[a-zA-Z0-9]+)*(\\[[0-9]+\\])*)+)\\}" + } + ] } }, "config.ArtifactFile": { diff --git a/bundle/schema/jsonschema_for_docs.json b/bundle/schema/jsonschema_for_docs.json index 811c957784..d5b4dfd6a5 100644 --- a/bundle/schema/jsonschema_for_docs.json +++ b/bundle/schema/jsonschema_for_docs.json @@ -43,7 +43,7 @@ "x-since-version": "v0.279.0" }, "permissions": { - "$ref": "#/$defs/slice/github.com/databricks/cli/bundle/config/resources.Permission", + "$ref": "#/$defs/slice/github.com/databricks/cli/bundle/config/resources.Permission[github.com/databricks/databricks-sdk-go/service/iam.PermissionLevel]", "x-since-version": "v0.279.0" }, "query_text": { @@ -121,7 +121,7 @@ "x-since-version": "v0.239.0" }, "permissions": { - "$ref": "#/$defs/slice/github.com/databricks/cli/bundle/config/resources.Permission", + "$ref": "#/$defs/slice/github.com/databricks/cli/bundle/config/resources.Permission[github.com/databricks/databricks-sdk-go/service/apps.AppPermissionLevel]", "x-since-version": "v0.239.0" }, "resources": { @@ -355,7 +355,7 @@ "x-since-version": "v0.229.0" }, "permissions": { - "$ref": "#/$defs/slice/github.com/databricks/cli/bundle/config/resources.Permission", + "$ref": "#/$defs/slice/github.com/databricks/cli/bundle/config/resources.Permission[github.com/databricks/databricks-sdk-go/service/compute.ClusterPermissionLevel]", "x-since-version": "v0.229.0" }, "policy_id": { @@ -482,7 +482,7 @@ "x-since-version": "v0.234.0" }, "permissions": { - "$ref": "#/$defs/slice/github.com/databricks/cli/bundle/config/resources.Permission", + "$ref": "#/$defs/slice/github.com/databricks/cli/bundle/config/resources.Permission[github.com/databricks/databricks-sdk-go/service/iam.PermissionLevel]", "x-since-version": "v0.232.0" }, "serialized_dashboard": { @@ -584,7 +584,7 @@ "x-since-version": "v0.265.0" }, "permissions": { - "$ref": "#/$defs/slice/github.com/databricks/cli/bundle/config/resources.Permission", + "$ref": "#/$defs/slice/github.com/databricks/cli/bundle/config/resources.Permission[github.com/databricks/databricks-sdk-go/service/iam.PermissionLevel]", "x-since-version": "v0.265.0" }, "retention_window_in_days": { @@ -740,7 +740,7 @@ "x-since-version": "v0.241.0" }, "permissions": { - "$ref": "#/$defs/slice/github.com/databricks/cli/bundle/config/resources.Permission", + "$ref": "#/$defs/slice/github.com/databricks/cli/bundle/config/resources.Permission[github.com/databricks/databricks-sdk-go/service/jobs.JobPermissionLevel]", "x-since-version": "v0.229.0" }, "queue": { @@ -823,7 +823,7 @@ "x-since-version": "v0.229.0" }, "permissions": { - "$ref": "#/$defs/slice/github.com/databricks/cli/bundle/config/resources.Permission", + "$ref": "#/$defs/slice/github.com/databricks/cli/bundle/config/resources.Permission[github.com/databricks/databricks-sdk-go/service/ml.ExperimentPermissionLevel]", "x-since-version": "v0.229.0" }, "tags": { @@ -857,7 +857,7 @@ "x-since-version": "v0.229.0" }, "permissions": { - "$ref": "#/$defs/slice/github.com/databricks/cli/bundle/config/resources.Permission", + "$ref": "#/$defs/slice/github.com/databricks/cli/bundle/config/resources.Permission[github.com/databricks/databricks-sdk-go/service/ml.RegisteredModelPermissionLevel]", "x-since-version": "v0.229.0" }, "tags": { @@ -910,7 +910,7 @@ "x-since-version": "v0.229.0" }, "permissions": { - "$ref": "#/$defs/slice/github.com/databricks/cli/bundle/config/resources.Permission", + "$ref": "#/$defs/slice/github.com/databricks/cli/bundle/config/resources.Permission[github.com/databricks/databricks-sdk-go/service/serving.ServingEndpointPermissionLevel]", "x-since-version": "v0.229.0" }, "rate_limits": { @@ -937,34 +937,238 @@ ], "markdownDescription": "The model_serving_endpoint resource allows you to define [model serving endpoints](https://docs.databricks.com/api/workspace/servingendpoints/create). See [link](https://docs.databricks.com/machine-learning/model-serving/manage-serving-endpoints.html)." }, - "resources.Permission": { - "type": "object", - "properties": { - "group_name": { - "description": "The name of the group that has the permission set in level.", - "$ref": "#/$defs/string", - "x-since-version": "v0.229.0" - }, - "level": { - "description": "The allowed permission for user, group, service principal defined for this permission.", - "$ref": "#/$defs/string", - "x-since-version": "v0.229.0" - }, - "service_principal_name": { - "description": "The name of the service principal that has the permission set in level.", - "$ref": "#/$defs/string", - "x-since-version": "v0.229.0" - }, - "user_name": { - "description": "The name of the user that has the permission set in level.", - "$ref": "#/$defs/string", - "x-since-version": "v0.229.0" + "resources.Permission[github.com": { + "databricks": { + "databricks-sdk-go": { + "service": { + "apps.AppPermissionLevel]": { + "type": "object", + "properties": { + "group_name": { + "description": "The name of the group that has the permission set in level.", + "$ref": "#/$defs/string" + }, + "level": { + "description": "The allowed permission for user, group, service principal defined for this permission.", + "$ref": "#/$defs/github.com/databricks/databricks-sdk-go/service/apps.AppPermissionLevel" + }, + "service_principal_name": { + "description": "The name of the service principal that has the permission set in level.", + "$ref": "#/$defs/string" + }, + "user_name": { + "description": "The name of the user that has the permission set in level.", + "$ref": "#/$defs/string" + } + }, + "additionalProperties": false, + "required": [ + "level" + ] + }, + "compute.ClusterPermissionLevel]": { + "type": "object", + "properties": { + "group_name": { + "description": "The name of the group that has the permission set in level.", + "$ref": "#/$defs/string" + }, + "level": { + "description": "The allowed permission for user, group, service principal defined for this permission.", + "$ref": "#/$defs/github.com/databricks/databricks-sdk-go/service/compute.ClusterPermissionLevel" + }, + "service_principal_name": { + "description": "The name of the service principal that has the permission set in level.", + "$ref": "#/$defs/string" + }, + "user_name": { + "description": "The name of the user that has the permission set in level.", + "$ref": "#/$defs/string" + } + }, + "additionalProperties": false, + "required": [ + "level" + ] + }, + "iam.PermissionLevel]": { + "type": "object", + "properties": { + "group_name": { + "description": "The name of the group that has the permission set in level.", + "$ref": "#/$defs/string" + }, + "level": { + "description": "The allowed permission for user, group, service principal defined for this permission.", + "$ref": "#/$defs/github.com/databricks/databricks-sdk-go/service/iam.PermissionLevel" + }, + "service_principal_name": { + "description": "The name of the service principal that has the permission set in level.", + "$ref": "#/$defs/string" + }, + "user_name": { + "description": "The name of the user that has the permission set in level.", + "$ref": "#/$defs/string" + } + }, + "additionalProperties": false, + "required": [ + "level" + ] + }, + "jobs.JobPermissionLevel]": { + "type": "object", + "properties": { + "group_name": { + "description": "The name of the group that has the permission set in level.", + "$ref": "#/$defs/string" + }, + "level": { + "description": "The allowed permission for user, group, service principal defined for this permission.", + "$ref": "#/$defs/github.com/databricks/databricks-sdk-go/service/jobs.JobPermissionLevel" + }, + "service_principal_name": { + "description": "The name of the service principal that has the permission set in level.", + "$ref": "#/$defs/string" + }, + "user_name": { + "description": "The name of the user that has the permission set in level.", + "$ref": "#/$defs/string" + } + }, + "additionalProperties": false, + "required": [ + "level" + ] + }, + "ml.ExperimentPermissionLevel]": { + "type": "object", + "properties": { + "group_name": { + "description": "The name of the group that has the permission set in level.", + "$ref": "#/$defs/string" + }, + "level": { + "description": "The allowed permission for user, group, service principal defined for this permission.", + "$ref": "#/$defs/github.com/databricks/databricks-sdk-go/service/ml.ExperimentPermissionLevel" + }, + "service_principal_name": { + "description": "The name of the service principal that has the permission set in level.", + "$ref": "#/$defs/string" + }, + "user_name": { + "description": "The name of the user that has the permission set in level.", + "$ref": "#/$defs/string" + } + }, + "additionalProperties": false, + "required": [ + "level" + ] + }, + "ml.RegisteredModelPermissionLevel]": { + "type": "object", + "properties": { + "group_name": { + "description": "The name of the group that has the permission set in level.", + "$ref": "#/$defs/string" + }, + "level": { + "description": "The allowed permission for user, group, service principal defined for this permission.", + "$ref": "#/$defs/github.com/databricks/databricks-sdk-go/service/ml.RegisteredModelPermissionLevel" + }, + "service_principal_name": { + "description": "The name of the service principal that has the permission set in level.", + "$ref": "#/$defs/string" + }, + "user_name": { + "description": "The name of the user that has the permission set in level.", + "$ref": "#/$defs/string" + } + }, + "additionalProperties": false, + "required": [ + "level" + ] + }, + "pipelines.PipelinePermissionLevel]": { + "type": "object", + "properties": { + "group_name": { + "description": "The name of the group that has the permission set in level.", + "$ref": "#/$defs/string" + }, + "level": { + "description": "The allowed permission for user, group, service principal defined for this permission.", + "$ref": "#/$defs/github.com/databricks/databricks-sdk-go/service/pipelines.PipelinePermissionLevel" + }, + "service_principal_name": { + "description": "The name of the service principal that has the permission set in level.", + "$ref": "#/$defs/string" + }, + "user_name": { + "description": "The name of the user that has the permission set in level.", + "$ref": "#/$defs/string" + } + }, + "additionalProperties": false, + "required": [ + "level" + ] + }, + "serving.ServingEndpointPermissionLevel]": { + "type": "object", + "properties": { + "group_name": { + "description": "The name of the group that has the permission set in level.", + "$ref": "#/$defs/string" + }, + "level": { + "description": "The allowed permission for user, group, service principal defined for this permission.", + "$ref": "#/$defs/github.com/databricks/databricks-sdk-go/service/serving.ServingEndpointPermissionLevel" + }, + "service_principal_name": { + "description": "The name of the service principal that has the permission set in level.", + "$ref": "#/$defs/string" + }, + "user_name": { + "description": "The name of the user that has the permission set in level.", + "$ref": "#/$defs/string" + } + }, + "additionalProperties": false, + "required": [ + "level" + ] + }, + "sql.WarehousePermissionLevel]": { + "type": "object", + "properties": { + "group_name": { + "description": "The name of the group that has the permission set in level.", + "$ref": "#/$defs/string" + }, + "level": { + "description": "The allowed permission for user, group, service principal defined for this permission.", + "$ref": "#/$defs/github.com/databricks/databricks-sdk-go/service/sql.WarehousePermissionLevel" + }, + "service_principal_name": { + "description": "The name of the service principal that has the permission set in level.", + "$ref": "#/$defs/string" + }, + "user_name": { + "description": "The name of the user that has the permission set in level.", + "$ref": "#/$defs/string" + } + }, + "additionalProperties": false, + "required": [ + "level" + ] + } + } } - }, - "additionalProperties": false, - "required": [ - "level" - ] + } }, "resources.Pipeline": { "type": "object", @@ -1067,7 +1271,7 @@ "x-since-version": "v0.229.0" }, "permissions": { - "$ref": "#/$defs/slice/github.com/databricks/cli/bundle/config/resources.Permission", + "$ref": "#/$defs/slice/github.com/databricks/cli/bundle/config/resources.Permission[github.com/databricks/databricks-sdk-go/service/pipelines.PipelinePermissionLevel]", "x-since-version": "v0.229.0" }, "photon": { @@ -1272,7 +1476,7 @@ "x-since-version": "v0.287.0" }, "permissions": { - "$ref": "#/$defs/slice/github.com/databricks/cli/bundle/config/resources.Permission", + "$ref": "#/$defs/slice/github.com/databricks/cli/bundle/config/resources.Permission[github.com/databricks/databricks-sdk-go/service/iam.PermissionLevel]", "x-since-version": "v0.292.0" }, "pg_version": { @@ -1635,7 +1839,7 @@ "x-since-version": "v0.260.0" }, "permissions": { - "$ref": "#/$defs/slice/github.com/databricks/cli/bundle/config/resources.Permission", + "$ref": "#/$defs/slice/github.com/databricks/cli/bundle/config/resources.Permission[github.com/databricks/databricks-sdk-go/service/sql.WarehousePermissionLevel]", "x-since-version": "v0.260.0" }, "spot_instance_policy": { @@ -2336,7 +2540,7 @@ }, "permissions": { "description": "The permissions for deploying and running the bundle in the target.", - "$ref": "#/$defs/slice/github.com/databricks/cli/bundle/config/resources.Permission", + "$ref": "#/$defs/slice/github.com/databricks/cli/bundle/config/resources.Permission[github.com/databricks/databricks-sdk-go/service/iam.PermissionLevel]", "x-since-version": "v0.229.0" }, "presets": { @@ -2554,6 +2758,14 @@ "type": "object", "additionalProperties": false }, + "apps.AppPermissionLevel": { + "type": "string", + "description": "Permission level", + "enum": [ + "CAN_MANAGE", + "CAN_USE" + ] + }, "apps.AppResource": { "type": "object", "properties": { @@ -3575,6 +3787,15 @@ }, "additionalProperties": false }, + "compute.ClusterPermissionLevel": { + "type": "string", + "description": "Permission level", + "enum": [ + "CAN_MANAGE", + "CAN_RESTART", + "CAN_ATTACH_TO" + ] + }, "compute.ClusterSpec": { "type": "object", "description": "Contains a snapshot of the latest user specified settings that were used to create/edit the cluster.", @@ -4444,6 +4665,31 @@ "description": "Detailed status of a synced table. Shown if the synced table is in the SYNCED_TRIGGERED_UPDATE\nor the SYNCED_NO_PENDING_UPDATE state.", "additionalProperties": false }, + "iam.PermissionLevel": { + "type": "string", + "description": "Permission level", + "enum": [ + "CAN_MANAGE", + "CAN_RESTART", + "CAN_ATTACH_TO", + "IS_OWNER", + "CAN_MANAGE_RUN", + "CAN_VIEW", + "CAN_READ", + "CAN_RUN", + "CAN_EDIT", + "CAN_USE", + "CAN_MANAGE_STAGING_VERSIONS", + "CAN_MANAGE_PRODUCTION_VERSIONS", + "CAN_EDIT_METADATA", + "CAN_VIEW_METADATA", + "CAN_BIND", + "CAN_QUERY", + "CAN_MONITOR", + "CAN_CREATE", + "CAN_MONITOR_ONLY" + ] + }, "jobs.AuthenticationMethod": { "type": "string", "enum": [ @@ -5025,6 +5271,16 @@ "name" ] }, + "jobs.JobPermissionLevel": { + "type": "string", + "description": "Permission level", + "enum": [ + "CAN_MANAGE", + "IS_OWNER", + "CAN_MANAGE_RUN", + "CAN_VIEW" + ] + }, "jobs.JobRunAs": { "type": "object", "description": "Write-only setting. Specifies the user or service principal that the job runs as. If not specified, the job runs as the user who created the job.\n\nEither `user_name` or `service_principal_name` should be specified. If not, an error is thrown.", @@ -6170,6 +6426,15 @@ }, "additionalProperties": false }, + "ml.ExperimentPermissionLevel": { + "type": "string", + "description": "Permission level", + "enum": [ + "CAN_MANAGE", + "CAN_EDIT", + "CAN_READ" + ] + }, "ml.ExperimentTag": { "type": "object", "description": "A tag for an experiment.", @@ -6204,6 +6469,17 @@ }, "additionalProperties": false }, + "ml.RegisteredModelPermissionLevel": { + "type": "string", + "description": "Permission level", + "enum": [ + "CAN_MANAGE", + "CAN_MANAGE_PRODUCTION_VERSIONS", + "CAN_MANAGE_STAGING_VERSIONS", + "CAN_EDIT", + "CAN_READ" + ] + }, "pipelines.AutoFullRefreshPolicy": { "type": "object", "description": "Policy for auto full refresh.", @@ -6786,6 +7062,16 @@ }, "additionalProperties": false }, + "pipelines.PipelinePermissionLevel": { + "type": "string", + "description": "Permission level", + "enum": [ + "CAN_MANAGE", + "IS_OWNER", + "CAN_RUN", + "CAN_VIEW" + ] + }, "pipelines.PipelineTrigger": { "type": "object", "properties": { @@ -8104,6 +8390,15 @@ "MULTIGPU_MEDIUM" ] }, + "serving.ServingEndpointPermissionLevel": { + "type": "string", + "description": "Permission level", + "enum": [ + "CAN_MANAGE", + "CAN_QUERY", + "CAN_VIEW" + ] + }, "serving.ServingModelWorkloadType": { "type": "string", "description": "Please keep this in sync with with workload types in InferenceEndpointEntities.scala", @@ -8404,6 +8699,17 @@ "RELIABILITY_OPTIMIZED" ] }, + "sql.WarehousePermissionLevel": { + "type": "string", + "description": "Permission level", + "enum": [ + "CAN_MANAGE", + "IS_OWNER", + "CAN_USE", + "CAN_MONITOR", + "CAN_VIEW" + ] + }, "workspace.AzureKeyVaultSecretScopeMetadata": { "type": "object", "description": "The metadata of the Azure KeyVault for a secret scope of type `AZURE_KEYVAULT`", @@ -8648,10 +8954,66 @@ "$ref": "#/$defs/github.com/databricks/cli/bundle/config/resources.AppEnvVar" } }, - "resources.Permission": { - "type": "array", - "items": { - "$ref": "#/$defs/github.com/databricks/cli/bundle/config/resources.Permission" + "resources.Permission[github.com": { + "databricks": { + "databricks-sdk-go": { + "service": { + "apps.AppPermissionLevel]": { + "type": "array", + "items": { + "$ref": "#/$defs/github.com/databricks/cli/bundle/config/resources.Permission[github.com/databricks/databricks-sdk-go/service/apps.AppPermissionLevel]" + } + }, + "compute.ClusterPermissionLevel]": { + "type": "array", + "items": { + "$ref": "#/$defs/github.com/databricks/cli/bundle/config/resources.Permission[github.com/databricks/databricks-sdk-go/service/compute.ClusterPermissionLevel]" + } + }, + "iam.PermissionLevel]": { + "type": "array", + "items": { + "$ref": "#/$defs/github.com/databricks/cli/bundle/config/resources.Permission[github.com/databricks/databricks-sdk-go/service/iam.PermissionLevel]" + } + }, + "jobs.JobPermissionLevel]": { + "type": "array", + "items": { + "$ref": "#/$defs/github.com/databricks/cli/bundle/config/resources.Permission[github.com/databricks/databricks-sdk-go/service/jobs.JobPermissionLevel]" + } + }, + "ml.ExperimentPermissionLevel]": { + "type": "array", + "items": { + "$ref": "#/$defs/github.com/databricks/cli/bundle/config/resources.Permission[github.com/databricks/databricks-sdk-go/service/ml.ExperimentPermissionLevel]" + } + }, + "ml.RegisteredModelPermissionLevel]": { + "type": "array", + "items": { + "$ref": "#/$defs/github.com/databricks/cli/bundle/config/resources.Permission[github.com/databricks/databricks-sdk-go/service/ml.RegisteredModelPermissionLevel]" + } + }, + "pipelines.PipelinePermissionLevel]": { + "type": "array", + "items": { + "$ref": "#/$defs/github.com/databricks/cli/bundle/config/resources.Permission[github.com/databricks/databricks-sdk-go/service/pipelines.PipelinePermissionLevel]" + } + }, + "serving.ServingEndpointPermissionLevel]": { + "type": "array", + "items": { + "$ref": "#/$defs/github.com/databricks/cli/bundle/config/resources.Permission[github.com/databricks/databricks-sdk-go/service/serving.ServingEndpointPermissionLevel]" + } + }, + "sql.WarehousePermissionLevel]": { + "type": "array", + "items": { + "$ref": "#/$defs/github.com/databricks/cli/bundle/config/resources.Permission[github.com/databricks/databricks-sdk-go/service/sql.WarehousePermissionLevel]" + } + } + } + } } }, "resources.SecretScopePermission": { @@ -8947,7 +9309,7 @@ }, "permissions": { "description": "Defines a permission for a specific entity.", - "$ref": "#/$defs/slice/github.com/databricks/cli/bundle/config/resources.Permission", + "$ref": "#/$defs/slice/github.com/databricks/cli/bundle/config/resources.Permission[github.com/databricks/databricks-sdk-go/service/iam.PermissionLevel]", "markdownDescription": "A Sequence that defines the permissions to apply to experiments, jobs, pipelines, and models defined in the bundle, where each item in the sequence is a permission for a specific entity.\n\nSee [permissions](https://docs.databricks.com/dev-tools/bundles/settings.html#permissions) and [link](https://docs.databricks.com/dev-tools/bundles/permissions.html).", "x-since-version": "v0.229.0" }, diff --git a/bundle/tests/bundle_permissions_test.go b/bundle/tests/bundle_permissions_test.go index 66cdd2db3f..d9625652b5 100644 --- a/bundle/tests/bundle_permissions_test.go +++ b/bundle/tests/bundle_permissions_test.go @@ -8,8 +8,6 @@ import ( "github.com/databricks/cli/bundle" "github.com/databricks/cli/bundle/config/resources" "github.com/databricks/databricks-sdk-go/service/iam" - "github.com/databricks/databricks-sdk-go/service/jobs" - "github.com/databricks/databricks-sdk-go/service/pipelines" "github.com/stretchr/testify/assert" "github.com/stretchr/testify/require" ) @@ -25,16 +23,16 @@ func TestBundlePermissions(t *testing.T) { require.NoError(t, diags.Error()) pipelinePermissions := b.Config.Resources.Pipelines["nyc_taxi_pipeline"].Permissions - assert.Contains(t, pipelinePermissions, resources.Permission[pipelines.PipelinePermissionLevel]{Level: "CAN_RUN", UserName: "test@company.com"}) - assert.NotContains(t, pipelinePermissions, resources.Permission[pipelines.PipelinePermissionLevel]{Level: "CAN_MANAGE", GroupName: "devs"}) - assert.NotContains(t, pipelinePermissions, resources.Permission[pipelines.PipelinePermissionLevel]{Level: "CAN_VIEW", ServicePrincipalName: "1234-abcd"}) - assert.NotContains(t, pipelinePermissions, resources.Permission[pipelines.PipelinePermissionLevel]{Level: "CAN_RUN", UserName: "bot@company.com"}) + assert.Contains(t, pipelinePermissions, resources.PipelinePermission{Level: "CAN_RUN", UserName: "test@company.com"}) + assert.NotContains(t, pipelinePermissions, resources.PipelinePermission{Level: "CAN_MANAGE", GroupName: "devs"}) + assert.NotContains(t, pipelinePermissions, resources.PipelinePermission{Level: "CAN_VIEW", ServicePrincipalName: "1234-abcd"}) + assert.NotContains(t, pipelinePermissions, resources.PipelinePermission{Level: "CAN_RUN", UserName: "bot@company.com"}) jobsPermissions := b.Config.Resources.Jobs["pipeline_schedule"].Permissions - assert.Contains(t, jobsPermissions, resources.Permission[jobs.JobPermissionLevel]{Level: "CAN_MANAGE_RUN", UserName: "test@company.com"}) - assert.NotContains(t, jobsPermissions, resources.Permission[jobs.JobPermissionLevel]{Level: "CAN_MANAGE", GroupName: "devs"}) - assert.NotContains(t, jobsPermissions, resources.Permission[jobs.JobPermissionLevel]{Level: "CAN_VIEW", ServicePrincipalName: "1234-abcd"}) - assert.NotContains(t, jobsPermissions, resources.Permission[jobs.JobPermissionLevel]{Level: "CAN_MANAGE_RUN", UserName: "bot@company.com"}) + assert.Contains(t, jobsPermissions, resources.JobPermission{Level: "CAN_MANAGE_RUN", UserName: "test@company.com"}) + assert.NotContains(t, jobsPermissions, resources.JobPermission{Level: "CAN_MANAGE", GroupName: "devs"}) + assert.NotContains(t, jobsPermissions, resources.JobPermission{Level: "CAN_VIEW", ServicePrincipalName: "1234-abcd"}) + assert.NotContains(t, jobsPermissions, resources.JobPermission{Level: "CAN_MANAGE_RUN", UserName: "bot@company.com"}) } func TestBundlePermissionsDevTarget(t *testing.T) { @@ -48,14 +46,14 @@ func TestBundlePermissionsDevTarget(t *testing.T) { require.NoError(t, diags.Error()) pipelinePermissions := b.Config.Resources.Pipelines["nyc_taxi_pipeline"].Permissions - assert.Contains(t, pipelinePermissions, resources.Permission[pipelines.PipelinePermissionLevel]{Level: "CAN_RUN", UserName: "test@company.com"}) - assert.Contains(t, pipelinePermissions, resources.Permission[pipelines.PipelinePermissionLevel]{Level: "CAN_MANAGE", GroupName: "devs"}) - assert.Contains(t, pipelinePermissions, resources.Permission[pipelines.PipelinePermissionLevel]{Level: "CAN_VIEW", ServicePrincipalName: "1234-abcd"}) - assert.Contains(t, pipelinePermissions, resources.Permission[pipelines.PipelinePermissionLevel]{Level: "CAN_RUN", UserName: "bot@company.com"}) + assert.Contains(t, pipelinePermissions, resources.PipelinePermission{Level: "CAN_RUN", UserName: "test@company.com"}) + assert.Contains(t, pipelinePermissions, resources.PipelinePermission{Level: "CAN_MANAGE", GroupName: "devs"}) + assert.Contains(t, pipelinePermissions, resources.PipelinePermission{Level: "CAN_VIEW", ServicePrincipalName: "1234-abcd"}) + assert.Contains(t, pipelinePermissions, resources.PipelinePermission{Level: "CAN_RUN", UserName: "bot@company.com"}) jobsPermissions := b.Config.Resources.Jobs["pipeline_schedule"].Permissions - assert.Contains(t, jobsPermissions, resources.Permission[jobs.JobPermissionLevel]{Level: "CAN_MANAGE_RUN", UserName: "test@company.com"}) - assert.Contains(t, jobsPermissions, resources.Permission[jobs.JobPermissionLevel]{Level: "CAN_MANAGE", GroupName: "devs"}) - assert.Contains(t, jobsPermissions, resources.Permission[jobs.JobPermissionLevel]{Level: "CAN_VIEW", ServicePrincipalName: "1234-abcd"}) - assert.Contains(t, jobsPermissions, resources.Permission[jobs.JobPermissionLevel]{Level: "CAN_MANAGE_RUN", UserName: "bot@company.com"}) + assert.Contains(t, jobsPermissions, resources.JobPermission{Level: "CAN_MANAGE_RUN", UserName: "test@company.com"}) + assert.Contains(t, jobsPermissions, resources.JobPermission{Level: "CAN_MANAGE", GroupName: "devs"}) + assert.Contains(t, jobsPermissions, resources.JobPermission{Level: "CAN_VIEW", ServicePrincipalName: "1234-abcd"}) + assert.Contains(t, jobsPermissions, resources.JobPermission{Level: "CAN_MANAGE_RUN", UserName: "bot@company.com"}) } diff --git a/bundle/tests/model_serving_endpoint_test.go b/bundle/tests/model_serving_endpoint_test.go index 514f114737..c0997c0f9a 100644 --- a/bundle/tests/model_serving_endpoint_test.go +++ b/bundle/tests/model_serving_endpoint_test.go @@ -5,7 +5,6 @@ import ( "github.com/databricks/cli/bundle/config" "github.com/databricks/cli/bundle/config/resources" - "github.com/databricks/databricks-sdk-go/service/serving" "github.com/stretchr/testify/assert" ) @@ -18,7 +17,7 @@ func assertExpected(t *testing.T, p *resources.ModelServingEndpoint) { assert.Equal(t, "model-name-1", p.Config.TrafficConfig.Routes[0].ServedModelName) assert.Equal(t, 100, p.Config.TrafficConfig.Routes[0].TrafficPercentage) - assert.Equal(t, resources.Permission[serving.ServingEndpointPermissionLevel]{ + assert.Equal(t, resources.ModelServingEndpointPermission{ GroupName: "users", Level: "CAN_QUERY", }, p.Permissions[0]) diff --git a/python/codegen/codegen/aliases_patch.py b/python/codegen/codegen/aliases_patch.py index 2af4710080..b938eeef47 100644 --- a/python/codegen/codegen/aliases_patch.py +++ b/python/codegen/codegen/aliases_patch.py @@ -23,17 +23,13 @@ "VolumeGrantPrivilegeParam": "PrivilegeParam", }, "jobs": { - "JobPermission": "Permission", - "JobPermissionDict": "PermissionDict", - "JobPermissionParam": "PermissionParam", - "JobPermissionLevel": "str", - "JobPermissionLevelParam": "str", + "Permission": "JobPermission", + "PermissionDict": "JobPermissionDict", + "PermissionParam": "JobPermissionParam", }, "pipelines": { - "PipelinePermission": "Permission", - "PipelinePermissionDict": "PermissionDict", - "PipelinePermissionParam": "PermissionParam", - "PipelinePermissionLevel": "str", - "PipelinePermissionLevelParam": "str", + "Permission": "PipelinePermission", + "PermissionDict": "PipelinePermissionDict", + "PermissionParam": "PipelinePermissionParam", }, } diff --git a/python/codegen/codegen/jsonschema.py b/python/codegen/codegen/jsonschema.py index ceed9203e7..41e20b220c 100644 --- a/python/codegen/codegen/jsonschema.py +++ b/python/codegen/codegen/jsonschema.py @@ -148,7 +148,7 @@ def get_schemas(): ) # we don't need all spec, only get supported types - flat_spec = {**sdk_types_spec, **resource_types_spec} + flat_spec = {**_flatten_spec(sdk_types_spec), **_flatten_spec(resource_types_spec)} flat_spec = { key: value for key, value in flat_spec.items() if packages.should_load_ref(key) } @@ -162,6 +162,52 @@ def get_schemas(): return output +def _is_schema(d: dict) -> bool: + """Check if a dict is a JSON schema definition (object or string type).""" + test = _unwrap_variable(d) or d + return test.get("type") in ("object", "string") + + +def _normalize_generic_key(path: str) -> str: + """ + Normalize a generic type path to a short schema key. + E.g., 'resources.Permission[github.com/.../jobs.JobPermissionLevel]' -> 'resources.JobPermission' + """ + if "[" not in path or not path.endswith("]"): + return path + + bracket_pos = path.index("[") + before = path[:bracket_pos] # e.g., 'resources.Permission' + type_param = path[ + bracket_pos + 1 : -1 + ] # e.g., 'github.com/.../jobs.JobPermissionLevel' + + type_ns = before.split(".")[0] # 'resources' + param_class = type_param.split("/")[-1].split(".")[-1] # 'JobPermissionLevel' + + if param_class.endswith("PermissionLevel"): + class_name = param_class[: -len("Level")] # 'JobPermission' + return f"{type_ns}.{class_name}" + + return path + + +def _flatten_spec(nested: dict, prefix: str = "") -> dict: + """ + Recursively flatten nested schema defs, normalizing generic type names. + Generic types like 'resources.Permission[.../jobs.JobPermissionLevel]' become 'resources.JobPermission'. + """ + result = {} + for k, v in nested.items(): + path = f"{prefix}/{k}" if prefix else k + if isinstance(v, dict): + if _is_schema(v): + result[_normalize_generic_key(path)] = v + else: + result.update(_flatten_spec(v, path)) + return result + + def _get_spec_path(spec: dict, path: list[str]) -> dict: for key in path: spec = spec[key] diff --git a/python/codegen/codegen/main.py b/python/codegen/codegen/main.py index d8764775c5..b1ba17dab4 100644 --- a/python/codegen/codegen/main.py +++ b/python/codegen/codegen/main.py @@ -252,7 +252,7 @@ def _collect_reachable_schemas( if schema.type == openapi.SchemaType.OBJECT: for field in schema.properties.values(): if field.ref: - name = field.ref.split("/")[-1] + name = packages.get_schema_key(field.ref) if not include_private and field.stage == openapi.Stage.PRIVATE: continue diff --git a/python/codegen/codegen/packages.py b/python/codegen/codegen/packages.py index 9774911feb..686cbaaa84 100644 --- a/python/codegen/codegen/packages.py +++ b/python/codegen/codegen/packages.py @@ -44,9 +44,41 @@ ] +def get_schema_key(ref: str) -> str: + """ + Get the schema dict key from a full $ref string, handling generic types. + E.g., '#/$defs/.../resources.Permission[.../jobs.JobPermissionLevel]' -> 'resources.JobPermission' + """ + if "[" not in ref or not ref.endswith("]"): + return ref.split("/")[-1] + + bracket_pos = ref.index("[") + before = ref[:bracket_pos] # e.g., '#/$defs/.../resources.Permission' + type_param = ref[bracket_pos + 1 : -1] # e.g., '.../jobs.JobPermissionLevel' + + type_ns = before.split("/")[-1].split(".")[0] # 'resources' + param_class = type_param.split("/")[-1].split(".")[-1] # 'JobPermissionLevel' + + if param_class.endswith("PermissionLevel"): + class_name = param_class[: -len("Level")] # 'JobPermission' + return f"{type_ns}.{class_name}" + + return ref.split("/")[-1] + + def get_class_name(ref: str) -> str: name = ref.split("/")[-1] - name = name.split(".")[-1] + + # Generic type: last segment is the type parameter like "jobs.JobPermissionLevel]" + if name.endswith("]"): + name = name[:-1] # strip "]" + param_class = name.split(".")[-1] # 'JobPermissionLevel' + if param_class.endswith("PermissionLevel"): + name = param_class[: -len("Level")] # 'JobPermission' + else: + name = param_class + else: + name = name.split(".")[-1] return RENAMES.get(name, name) @@ -58,6 +90,10 @@ def is_resource(ref: str) -> bool: def should_load_ref(ref: str) -> bool: name = ref.split("/")[-1] + # Skip Go generic type fragments (their names contain '[' from embedded package paths) + if "[" in name: + return False + for namespace in LOADED_NAMESPACES: if name.startswith(f"{namespace}."): return True @@ -77,6 +113,20 @@ def get_package(namespace: str, ref: str) -> Optional[str]: full_name = ref.split("/")[-1] + # Generic type: last segment is the type parameter like "jobs.JobPermissionLevel]" + if full_name.endswith("]"): + full_name = full_name[:-1] # strip "]" + if full_name in PRIMITIVES: + return None + param_ns = full_name.split(".")[0] # e.g., 'jobs' + param_class = full_name.split(".")[-1] # e.g., 'JobPermissionLevel' + if param_class.endswith("PermissionLevel"): + class_name = param_class[: -len("Level")] # 'JobPermission' + else: + class_name = param_class + package_name = re.sub(r"(? "Self": + return _transform(cls, value) + + def as_dict(self) -> "JobPermissionDict": + return _transform_to_json_value(self) # type:ignore + + +class JobPermissionDict(TypedDict, total=False): + """""" + + level: VariableOr[JobPermissionLevelParam] + + group_name: VariableOrOptional[str] + + service_principal_name: VariableOrOptional[str] + + user_name: VariableOrOptional[str] + + +JobPermissionParam = JobPermissionDict | JobPermission diff --git a/python/databricks/bundles/jobs/_models/job_permission_level.py b/python/databricks/bundles/jobs/_models/job_permission_level.py new file mode 100644 index 0000000000..c3ed76bd59 --- /dev/null +++ b/python/databricks/bundles/jobs/_models/job_permission_level.py @@ -0,0 +1,18 @@ +from enum import Enum +from typing import Literal + + +class JobPermissionLevel(Enum): + """ + Permission level + """ + + CAN_MANAGE = "CAN_MANAGE" + IS_OWNER = "IS_OWNER" + CAN_MANAGE_RUN = "CAN_MANAGE_RUN" + CAN_VIEW = "CAN_VIEW" + + +JobPermissionLevelParam = ( + Literal["CAN_MANAGE", "IS_OWNER", "CAN_MANAGE_RUN", "CAN_VIEW"] | JobPermissionLevel +) diff --git a/python/databricks/bundles/jobs/_models/permission.py b/python/databricks/bundles/jobs/_models/permission.py deleted file mode 100644 index 493137a3a9..0000000000 --- a/python/databricks/bundles/jobs/_models/permission.py +++ /dev/null @@ -1,68 +0,0 @@ -from dataclasses import dataclass -from typing import TYPE_CHECKING, TypedDict - -from databricks.bundles.core._transform import _transform -from databricks.bundles.core._transform_to_json import _transform_to_json_value -from databricks.bundles.core._variable import VariableOr, VariableOrOptional - -if TYPE_CHECKING: - from typing_extensions import Self - - -@dataclass(kw_only=True) -class Permission: - """""" - - level: VariableOr[str] - """ - The allowed permission for user, group, service principal defined for this permission. - """ - - group_name: VariableOrOptional[str] = None - """ - The name of the group that has the permission set in level. - """ - - service_principal_name: VariableOrOptional[str] = None - """ - The name of the service principal that has the permission set in level. - """ - - user_name: VariableOrOptional[str] = None - """ - The name of the user that has the permission set in level. - """ - - @classmethod - def from_dict(cls, value: "PermissionDict") -> "Self": - return _transform(cls, value) - - def as_dict(self) -> "PermissionDict": - return _transform_to_json_value(self) # type:ignore - - -class PermissionDict(TypedDict, total=False): - """""" - - level: VariableOr[str] - """ - The allowed permission for user, group, service principal defined for this permission. - """ - - group_name: VariableOrOptional[str] - """ - The name of the group that has the permission set in level. - """ - - service_principal_name: VariableOrOptional[str] - """ - The name of the service principal that has the permission set in level. - """ - - user_name: VariableOrOptional[str] - """ - The name of the user that has the permission set in level. - """ - - -PermissionParam = PermissionDict | Permission diff --git a/python/databricks/bundles/pipelines/__init__.py b/python/databricks/bundles/pipelines/__init__.py index 4050db989c..7dc3bacf7d 100644 --- a/python/databricks/bundles/pipelines/__init__.py +++ b/python/databricks/bundles/pipelines/__init__.py @@ -311,11 +311,6 @@ PathPatternDict, PathPatternParam, ) -from databricks.bundles.pipelines._models.permission import ( - Permission, - PermissionDict, - PermissionParam, -) from databricks.bundles.pipelines._models.pipeline import ( Pipeline, PipelineDict, @@ -340,6 +335,15 @@ PipelineLibraryDict, PipelineLibraryParam, ) +from databricks.bundles.pipelines._models.pipeline_permission import ( + PipelinePermission, + PipelinePermissionDict, + PipelinePermissionParam, +) +from databricks.bundles.pipelines._models.pipeline_permission_level import ( + PipelinePermissionLevel, + PipelinePermissionLevelParam, +) from databricks.bundles.pipelines._models.pipelines_environment import ( PipelinesEnvironment, PipelinesEnvironmentDict, @@ -411,8 +415,6 @@ WorkspaceStorageInfoParam, ) -PipelinePermission = Permission -PipelinePermissionDict = PermissionDict -PipelinePermissionLevel = str -PipelinePermissionLevelParam = str -PipelinePermissionParam = PermissionParam +Permission = PipelinePermission +PermissionDict = PipelinePermissionDict +PermissionParam = PipelinePermissionParam diff --git a/python/databricks/bundles/pipelines/_models/permission.py b/python/databricks/bundles/pipelines/_models/permission.py deleted file mode 100644 index 493137a3a9..0000000000 --- a/python/databricks/bundles/pipelines/_models/permission.py +++ /dev/null @@ -1,68 +0,0 @@ -from dataclasses import dataclass -from typing import TYPE_CHECKING, TypedDict - -from databricks.bundles.core._transform import _transform -from databricks.bundles.core._transform_to_json import _transform_to_json_value -from databricks.bundles.core._variable import VariableOr, VariableOrOptional - -if TYPE_CHECKING: - from typing_extensions import Self - - -@dataclass(kw_only=True) -class Permission: - """""" - - level: VariableOr[str] - """ - The allowed permission for user, group, service principal defined for this permission. - """ - - group_name: VariableOrOptional[str] = None - """ - The name of the group that has the permission set in level. - """ - - service_principal_name: VariableOrOptional[str] = None - """ - The name of the service principal that has the permission set in level. - """ - - user_name: VariableOrOptional[str] = None - """ - The name of the user that has the permission set in level. - """ - - @classmethod - def from_dict(cls, value: "PermissionDict") -> "Self": - return _transform(cls, value) - - def as_dict(self) -> "PermissionDict": - return _transform_to_json_value(self) # type:ignore - - -class PermissionDict(TypedDict, total=False): - """""" - - level: VariableOr[str] - """ - The allowed permission for user, group, service principal defined for this permission. - """ - - group_name: VariableOrOptional[str] - """ - The name of the group that has the permission set in level. - """ - - service_principal_name: VariableOrOptional[str] - """ - The name of the service principal that has the permission set in level. - """ - - user_name: VariableOrOptional[str] - """ - The name of the user that has the permission set in level. - """ - - -PermissionParam = PermissionDict | Permission diff --git a/python/databricks/bundles/pipelines/_models/pipeline.py b/python/databricks/bundles/pipelines/_models/pipeline.py index 1002c7c9eb..2b4bbed23b 100644 --- a/python/databricks/bundles/pipelines/_models/pipeline.py +++ b/python/databricks/bundles/pipelines/_models/pipeline.py @@ -33,7 +33,6 @@ Notifications, NotificationsParam, ) -from databricks.bundles.pipelines._models.permission import Permission, PermissionParam from databricks.bundles.pipelines._models.pipeline_cluster import ( PipelineCluster, PipelineClusterParam, @@ -42,6 +41,10 @@ PipelineLibrary, PipelineLibraryParam, ) +from databricks.bundles.pipelines._models.pipeline_permission import ( + PipelinePermission, + PipelinePermissionParam, +) from databricks.bundles.pipelines._models.pipelines_environment import ( PipelinesEnvironment, PipelinesEnvironmentParam, @@ -157,7 +160,7 @@ class Pipeline(Resource): List of notification settings for this pipeline. """ - permissions: VariableOrList[Permission] = field(default_factory=list) + permissions: VariableOrList[PipelinePermission] = field(default_factory=list) photon: VariableOrOptional[bool] = None """ @@ -322,7 +325,7 @@ class PipelineDict(TypedDict, total=False): List of notification settings for this pipeline. """ - permissions: VariableOrList[PermissionParam] + permissions: VariableOrList[PipelinePermissionParam] photon: VariableOrOptional[bool] """ diff --git a/python/databricks/bundles/pipelines/_models/pipeline_permission.py b/python/databricks/bundles/pipelines/_models/pipeline_permission.py new file mode 100644 index 0000000000..0a3ed95538 --- /dev/null +++ b/python/databricks/bundles/pipelines/_models/pipeline_permission.py @@ -0,0 +1,48 @@ +from dataclasses import dataclass +from typing import TYPE_CHECKING, TypedDict + +from databricks.bundles.core._transform import _transform +from databricks.bundles.core._transform_to_json import _transform_to_json_value +from databricks.bundles.core._variable import VariableOr, VariableOrOptional +from databricks.bundles.pipelines._models.pipeline_permission_level import ( + PipelinePermissionLevel, + PipelinePermissionLevelParam, +) + +if TYPE_CHECKING: + from typing_extensions import Self + + +@dataclass(kw_only=True) +class PipelinePermission: + """""" + + level: VariableOr[PipelinePermissionLevel] + + group_name: VariableOrOptional[str] = None + + service_principal_name: VariableOrOptional[str] = None + + user_name: VariableOrOptional[str] = None + + @classmethod + def from_dict(cls, value: "PipelinePermissionDict") -> "Self": + return _transform(cls, value) + + def as_dict(self) -> "PipelinePermissionDict": + return _transform_to_json_value(self) # type:ignore + + +class PipelinePermissionDict(TypedDict, total=False): + """""" + + level: VariableOr[PipelinePermissionLevelParam] + + group_name: VariableOrOptional[str] + + service_principal_name: VariableOrOptional[str] + + user_name: VariableOrOptional[str] + + +PipelinePermissionParam = PipelinePermissionDict | PipelinePermission diff --git a/python/databricks/bundles/pipelines/_models/pipeline_permission_level.py b/python/databricks/bundles/pipelines/_models/pipeline_permission_level.py new file mode 100644 index 0000000000..746bedc51a --- /dev/null +++ b/python/databricks/bundles/pipelines/_models/pipeline_permission_level.py @@ -0,0 +1,18 @@ +from enum import Enum +from typing import Literal + + +class PipelinePermissionLevel(Enum): + """ + Permission level + """ + + CAN_MANAGE = "CAN_MANAGE" + IS_OWNER = "IS_OWNER" + CAN_RUN = "CAN_RUN" + CAN_VIEW = "CAN_VIEW" + + +PipelinePermissionLevelParam = ( + Literal["CAN_MANAGE", "IS_OWNER", "CAN_RUN", "CAN_VIEW"] | PipelinePermissionLevel +) From 5ff1674b8afb88d07c9f5a76f4cd0447073fee92 Mon Sep 17 00:00:00 2001 From: Denis Bilenko Date: Tue, 10 Mar 2026 14:32:34 +0100 Subject: [PATCH 12/28] rm settings --- .claude/settings.local.json | 10 ---------- 1 file changed, 10 deletions(-) delete mode 100644 .claude/settings.local.json diff --git a/.claude/settings.local.json b/.claude/settings.local.json deleted file mode 100644 index 34ef364164..0000000000 --- a/.claude/settings.local.json +++ /dev/null @@ -1,10 +0,0 @@ -{ - "permissions": { - "allow": [ - "Bash(perl:*)", - "Bash(do:*)", - "Bash(sed:*)", - "Bash(python3:*)" - ] - } -} From 300c081d9971fd8a01f248ff42c2846bbeb8eb3b Mon Sep 17 00:00:00 2001 From: Denis Bilenko Date: Tue, 10 Mar 2026 14:43:57 +0100 Subject: [PATCH 13/28] Use IamPermission for generic iam.PermissionLevel; drop named slice types Co-Authored-By: Claude Sonnet 4.6 --- .../apply_bundle_permissions_test.go | 8 +- .../validate_target_mode_test.go | 12 +- bundle/config/resources/alerts.go | 2 +- bundle/config/resources/apps.go | 2 +- bundle/config/resources/clusters.go | 2 +- bundle/config/resources/dashboard.go | 2 +- bundle/config/resources/database_instance.go | 2 +- bundle/config/resources/job.go | 2 +- bundle/config/resources/mlflow_experiment.go | 2 +- bundle/config/resources/mlflow_model.go | 2 +- .../resources/model_serving_endpoint.go | 2 +- bundle/config/resources/permission.go | 18 -- bundle/config/resources/permission_types.go | 157 ++++-------------- bundle/config/resources/pipeline.go | 2 +- bundle/config/resources/postgres_project.go | 2 +- bundle/config/resources/sql_warehouses.go | 2 +- bundle/config/resources_types_test.go | 2 +- bundle/deploy/terraform/convert_test.go | 10 +- .../terraform/tfdyn/convert_alert_test.go | 2 +- .../terraform/tfdyn/convert_app_test.go | 2 +- .../terraform/tfdyn/convert_cluster_test.go | 2 +- .../terraform/tfdyn/convert_dashboard_test.go | 2 +- .../tfdyn/convert_database_instance_test.go | 2 +- .../tfdyn/convert_experiment_test.go | 2 +- .../terraform/tfdyn/convert_job_test.go | 2 +- .../convert_model_serving_endpoint_test.go | 2 +- .../terraform/tfdyn/convert_model_test.go | 2 +- .../tfdyn/convert_permissions_test.go | 4 +- .../terraform/tfdyn/convert_pipeline_test.go | 2 +- .../tfdyn/convert_postgres_project_test.go | 2 +- bundle/direct/dresources/permissions.go | 30 +++- 31 files changed, 93 insertions(+), 194 deletions(-) diff --git a/bundle/config/mutator/resourcemutator/apply_bundle_permissions_test.go b/bundle/config/mutator/resourcemutator/apply_bundle_permissions_test.go index d3c439af7f..989bc293a5 100644 --- a/bundle/config/mutator/resourcemutator/apply_bundle_permissions_test.go +++ b/bundle/config/mutator/resourcemutator/apply_bundle_permissions_test.go @@ -133,8 +133,8 @@ func TestApplyBundlePermissions(t *testing.T) { require.Contains(t, b.Config.Resources.ModelServingEndpoints["endpoint_2"].Permissions, resources.ModelServingEndpointPermission{Level: "CAN_QUERY", ServicePrincipalName: "TestServicePrincipal"}) require.Len(t, b.Config.Resources.Dashboards["dashboard_1"].Permissions, 2) - require.Contains(t, b.Config.Resources.Dashboards["dashboard_1"].Permissions, resources.DashboardPermission{Level: "CAN_MANAGE", UserName: "TestUser"}) - require.Contains(t, b.Config.Resources.Dashboards["dashboard_1"].Permissions, resources.DashboardPermission{Level: "CAN_READ", GroupName: "TestGroup"}) + require.Contains(t, b.Config.Resources.Dashboards["dashboard_1"].Permissions, resources.IamPermission{Level: "CAN_MANAGE", UserName: "TestUser"}) + require.Contains(t, b.Config.Resources.Dashboards["dashboard_1"].Permissions, resources.IamPermission{Level: "CAN_READ", GroupName: "TestGroup"}) require.Len(t, b.Config.Resources.Apps["app_1"].Permissions, 2) require.Contains(t, b.Config.Resources.Apps["app_1"].Permissions, resources.AppPermission{Level: "CAN_MANAGE", UserName: "TestUser"}) @@ -157,7 +157,7 @@ func TestWarningOnOverlapPermission(t *testing.T) { JobSettings: jobs.JobSettings{ Name: "job_1", }, - Permissions: resources.JobPermissions{ + Permissions: []resources.JobPermission{ {Level: "CAN_VIEW", UserName: "TestUser"}, }, }, @@ -165,7 +165,7 @@ func TestWarningOnOverlapPermission(t *testing.T) { JobSettings: jobs.JobSettings{ Name: "job_2", }, - Permissions: resources.JobPermissions{ + Permissions: []resources.JobPermission{ {Level: "CAN_VIEW", UserName: "TestUser2"}, }, }, diff --git a/bundle/config/mutator/resourcemutator/validate_target_mode_test.go b/bundle/config/mutator/resourcemutator/validate_target_mode_test.go index 6940ba8b69..c387889c2c 100644 --- a/bundle/config/mutator/resourcemutator/validate_target_mode_test.go +++ b/bundle/config/mutator/resourcemutator/validate_target_mode_test.go @@ -48,22 +48,22 @@ func TestProcessTargetModeProduction(t *testing.T) { diags = validateProductionMode(b, false) require.ErrorContains(t, diags.Error(), "A common practice is to use a username or principal name in this path, i.e. use\n\n root_path: /Workspace/Users/lennart@company.com/.bundle/${bundle.name}/${bundle.target}") - jobPermissions := resources.JobPermissions{ + jobPermissions := []resources.JobPermission{ {Level: "CAN_MANAGE", UserName: "user@company.com"}, } - pipelinePermissions := resources.PipelinePermissions{ + pipelinePermissions := []resources.PipelinePermission{ {Level: "CAN_MANAGE", UserName: "user@company.com"}, } - experimentPermissions := resources.MlflowExperimentPermissions{ + experimentPermissions := []resources.MlflowExperimentPermission{ {Level: "CAN_MANAGE", UserName: "user@company.com"}, } - modelPermissions := resources.MlflowModelPermissions{ + modelPermissions := []resources.MlflowModelPermission{ {Level: "CAN_MANAGE", UserName: "user@company.com"}, } - endpointPermissions := resources.ModelServingEndpointPermissions{ + endpointPermissions := []resources.ModelServingEndpointPermission{ {Level: "CAN_MANAGE", UserName: "user@company.com"}, } - clusterPermissions := resources.ClusterPermissions{ + clusterPermissions := []resources.ClusterPermission{ {Level: "CAN_MANAGE", UserName: "user@company.com"}, } b.Config.Resources.Jobs["job1"].Permissions = jobPermissions diff --git a/bundle/config/resources/alerts.go b/bundle/config/resources/alerts.go index d234b7becb..8dd8f4e690 100644 --- a/bundle/config/resources/alerts.go +++ b/bundle/config/resources/alerts.go @@ -14,7 +14,7 @@ type Alert struct { BaseResource sql.AlertV2 //nolint AlertV2 also defines Id and URL field with the same json tag "id" and "url" - Permissions AlertPermissions `json:"permissions,omitempty"` + Permissions []IamPermission `json:"permissions,omitempty"` // Filepath points to the local .dbalert.json file containing the alert definition. // If specified, any fields that are part of the .dbalert.json file schema will not be allowed in diff --git a/bundle/config/resources/apps.go b/bundle/config/resources/apps.go index 4a311e138c..e48f6e7dae 100644 --- a/bundle/config/resources/apps.go +++ b/bundle/config/resources/apps.go @@ -50,7 +50,7 @@ type App struct { // This is used in conjunction with GitRepository (from apps.App) and is passed to the Deploy API. GitSource *apps.GitSource `json:"git_source,omitempty"` - Permissions AppPermissions `json:"permissions,omitempty"` + Permissions []AppPermission `json:"permissions,omitempty"` } func (a *App) UnmarshalJSON(b []byte) error { diff --git a/bundle/config/resources/clusters.go b/bundle/config/resources/clusters.go index 68b7f7505c..c549ac4a6b 100644 --- a/bundle/config/resources/clusters.go +++ b/bundle/config/resources/clusters.go @@ -14,7 +14,7 @@ type Cluster struct { BaseResource compute.ClusterSpec - Permissions ClusterPermissions `json:"permissions,omitempty"` + Permissions []ClusterPermission `json:"permissions,omitempty"` } func (s *Cluster) UnmarshalJSON(b []byte) error { diff --git a/bundle/config/resources/dashboard.go b/bundle/config/resources/dashboard.go index 4576b6d0b6..35fd3a650b 100644 --- a/bundle/config/resources/dashboard.go +++ b/bundle/config/resources/dashboard.go @@ -80,7 +80,7 @@ type Dashboard struct { BaseResource DashboardConfig - Permissions DashboardPermissions `json:"permissions,omitempty"` + Permissions []IamPermission `json:"permissions,omitempty"` // FilePath points to the local `.lvdash.json` file containing the dashboard definition. // This is inlined into serialized_dashboard during deployment. The file_path is kept around diff --git a/bundle/config/resources/database_instance.go b/bundle/config/resources/database_instance.go index 2bca667c5c..2af4cb52c1 100644 --- a/bundle/config/resources/database_instance.go +++ b/bundle/config/resources/database_instance.go @@ -14,7 +14,7 @@ type DatabaseInstance struct { BaseResource database.DatabaseInstance - Permissions DatabaseInstancePermissions `json:"permissions,omitempty"` + Permissions []IamPermission `json:"permissions,omitempty"` } func (d *DatabaseInstance) Exists(ctx context.Context, w *databricks.WorkspaceClient, name string) (bool, error) { diff --git a/bundle/config/resources/job.go b/bundle/config/resources/job.go index 5c6c3a6b61..b2b7ff15f1 100644 --- a/bundle/config/resources/job.go +++ b/bundle/config/resources/job.go @@ -15,7 +15,7 @@ type Job struct { BaseResource jobs.JobSettings - Permissions JobPermissions `json:"permissions,omitempty"` + Permissions []JobPermission `json:"permissions,omitempty"` } func (j *Job) UnmarshalJSON(b []byte) error { diff --git a/bundle/config/resources/mlflow_experiment.go b/bundle/config/resources/mlflow_experiment.go index a37c4a58f6..0a2a36b840 100644 --- a/bundle/config/resources/mlflow_experiment.go +++ b/bundle/config/resources/mlflow_experiment.go @@ -14,7 +14,7 @@ type MlflowExperiment struct { BaseResource ml.CreateExperiment - Permissions MlflowExperimentPermissions `json:"permissions,omitempty"` + Permissions []MlflowExperimentPermission `json:"permissions,omitempty"` } func (s *MlflowExperiment) UnmarshalJSON(b []byte) error { diff --git a/bundle/config/resources/mlflow_model.go b/bundle/config/resources/mlflow_model.go index 11d165f9d4..a867f55a0e 100644 --- a/bundle/config/resources/mlflow_model.go +++ b/bundle/config/resources/mlflow_model.go @@ -14,7 +14,7 @@ type MlflowModel struct { BaseResource ml.CreateModelRequest - Permissions MlflowModelPermissions `json:"permissions,omitempty"` + Permissions []MlflowModelPermission `json:"permissions,omitempty"` } func (s *MlflowModel) UnmarshalJSON(b []byte) error { diff --git a/bundle/config/resources/model_serving_endpoint.go b/bundle/config/resources/model_serving_endpoint.go index 53e388fdca..d3e390596d 100644 --- a/bundle/config/resources/model_serving_endpoint.go +++ b/bundle/config/resources/model_serving_endpoint.go @@ -19,7 +19,7 @@ type ModelServingEndpoint struct { // This is a resource agnostic implementation of permissions for ACLs. // Implementation could be different based on the resource type. - Permissions ModelServingEndpointPermissions `json:"permissions,omitempty"` + Permissions []ModelServingEndpointPermission `json:"permissions,omitempty"` } func (s *ModelServingEndpoint) UnmarshalJSON(b []byte) error { diff --git a/bundle/config/resources/permission.go b/bundle/config/resources/permission.go index 219e354f3a..582f1c6b1b 100644 --- a/bundle/config/resources/permission.go +++ b/bundle/config/resources/permission.go @@ -26,24 +26,6 @@ func (p Permission[L]) ToAccessControlRequest() iam.AccessControlRequest { } } -// Permissions is a named slice of Permission[L] that implements PermissionsSlice, -// allowing generic code to work with any instantiation via the interface. -type Permissions[L ~string] []Permission[L] - -// PermissionsSlice is implemented by any Permissions[L], enabling type-agnostic -// conversion to iam.AccessControlRequest without reflection or type switches. -type PermissionsSlice interface { - ToAccessControlRequests() []iam.AccessControlRequest -} - -func (ps Permissions[L]) ToAccessControlRequests() []iam.AccessControlRequest { - result := make([]iam.AccessControlRequest, len(ps)) - for i, p := range ps { - result[i] = p.ToAccessControlRequest() - } - return result -} - func (p Permission[L]) String() string { if p.UserName != "" { return fmt.Sprintf("level: %s, user_name: %s", p.Level, p.UserName) diff --git a/bundle/config/resources/permission_types.go b/bundle/config/resources/permission_types.go index 25edaec897..157e95f688 100644 --- a/bundle/config/resources/permission_types.go +++ b/bundle/config/resources/permission_types.go @@ -15,158 +15,57 @@ import ( // Using non-alias type definitions (not =) makes them appear as named types in the schema. // The underlying struct is identical to Permission[L], enabling conversion to use generic methods. -type ( - AlertPermission Permission[iam.PermissionLevel] - AlertPermissions []AlertPermission -) +// IamPermission is used for resources that use the generic iam.PermissionLevel (Alert, Dashboard, DatabaseInstance, PostgresProject). +type IamPermission Permission[iam.PermissionLevel] -func (ps AlertPermissions) ToAccessControlRequests() []iam.AccessControlRequest { - result := make([]iam.AccessControlRequest, len(ps)) - for i, p := range ps { - result[i] = Permission[iam.PermissionLevel](p).ToAccessControlRequest() - } - return result +func (p IamPermission) ToAccessControlRequest() iam.AccessControlRequest { + return Permission[iam.PermissionLevel](p).ToAccessControlRequest() } -type ( - AppPermission Permission[apps.AppPermissionLevel] - AppPermissions []AppPermission -) +type AppPermission Permission[apps.AppPermissionLevel] -func (ps AppPermissions) ToAccessControlRequests() []iam.AccessControlRequest { - result := make([]iam.AccessControlRequest, len(ps)) - for i, p := range ps { - result[i] = Permission[apps.AppPermissionLevel](p).ToAccessControlRequest() - } - return result +func (p AppPermission) ToAccessControlRequest() iam.AccessControlRequest { + return Permission[apps.AppPermissionLevel](p).ToAccessControlRequest() } -type ( - ClusterPermission Permission[compute.ClusterPermissionLevel] - ClusterPermissions []ClusterPermission -) +type ClusterPermission Permission[compute.ClusterPermissionLevel] -func (ps ClusterPermissions) ToAccessControlRequests() []iam.AccessControlRequest { - result := make([]iam.AccessControlRequest, len(ps)) - for i, p := range ps { - result[i] = Permission[compute.ClusterPermissionLevel](p).ToAccessControlRequest() - } - return result +func (p ClusterPermission) ToAccessControlRequest() iam.AccessControlRequest { + return Permission[compute.ClusterPermissionLevel](p).ToAccessControlRequest() } -type ( - DashboardPermission Permission[iam.PermissionLevel] - DashboardPermissions []DashboardPermission -) +type JobPermission Permission[jobs.JobPermissionLevel] -func (ps DashboardPermissions) ToAccessControlRequests() []iam.AccessControlRequest { - result := make([]iam.AccessControlRequest, len(ps)) - for i, p := range ps { - result[i] = Permission[iam.PermissionLevel](p).ToAccessControlRequest() - } - return result +func (p JobPermission) ToAccessControlRequest() iam.AccessControlRequest { + return Permission[jobs.JobPermissionLevel](p).ToAccessControlRequest() } -type ( - DatabaseInstancePermission Permission[iam.PermissionLevel] - DatabaseInstancePermissions []DatabaseInstancePermission -) +type MlflowExperimentPermission Permission[ml.ExperimentPermissionLevel] -func (ps DatabaseInstancePermissions) ToAccessControlRequests() []iam.AccessControlRequest { - result := make([]iam.AccessControlRequest, len(ps)) - for i, p := range ps { - result[i] = Permission[iam.PermissionLevel](p).ToAccessControlRequest() - } - return result +func (p MlflowExperimentPermission) ToAccessControlRequest() iam.AccessControlRequest { + return Permission[ml.ExperimentPermissionLevel](p).ToAccessControlRequest() } -type ( - DatabaseProjectPermission Permission[iam.PermissionLevel] - DatabaseProjectPermissions []DatabaseProjectPermission -) +type MlflowModelPermission Permission[ml.RegisteredModelPermissionLevel] -func (ps DatabaseProjectPermissions) ToAccessControlRequests() []iam.AccessControlRequest { - result := make([]iam.AccessControlRequest, len(ps)) - for i, p := range ps { - result[i] = Permission[iam.PermissionLevel](p).ToAccessControlRequest() - } - return result +func (p MlflowModelPermission) ToAccessControlRequest() iam.AccessControlRequest { + return Permission[ml.RegisteredModelPermissionLevel](p).ToAccessControlRequest() } -type ( - JobPermission Permission[jobs.JobPermissionLevel] - JobPermissions []JobPermission -) +type ModelServingEndpointPermission Permission[serving.ServingEndpointPermissionLevel] -func (ps JobPermissions) ToAccessControlRequests() []iam.AccessControlRequest { - result := make([]iam.AccessControlRequest, len(ps)) - for i, p := range ps { - result[i] = Permission[jobs.JobPermissionLevel](p).ToAccessControlRequest() - } - return result +func (p ModelServingEndpointPermission) ToAccessControlRequest() iam.AccessControlRequest { + return Permission[serving.ServingEndpointPermissionLevel](p).ToAccessControlRequest() } -type ( - MlflowExperimentPermission Permission[ml.ExperimentPermissionLevel] - MlflowExperimentPermissions []MlflowExperimentPermission -) +type PipelinePermission Permission[pipelines.PipelinePermissionLevel] -func (ps MlflowExperimentPermissions) ToAccessControlRequests() []iam.AccessControlRequest { - result := make([]iam.AccessControlRequest, len(ps)) - for i, p := range ps { - result[i] = Permission[ml.ExperimentPermissionLevel](p).ToAccessControlRequest() - } - return result +func (p PipelinePermission) ToAccessControlRequest() iam.AccessControlRequest { + return Permission[pipelines.PipelinePermissionLevel](p).ToAccessControlRequest() } -type ( - MlflowModelPermission Permission[ml.RegisteredModelPermissionLevel] - MlflowModelPermissions []MlflowModelPermission -) - -func (ps MlflowModelPermissions) ToAccessControlRequests() []iam.AccessControlRequest { - result := make([]iam.AccessControlRequest, len(ps)) - for i, p := range ps { - result[i] = Permission[ml.RegisteredModelPermissionLevel](p).ToAccessControlRequest() - } - return result -} - -type ( - ModelServingEndpointPermission Permission[serving.ServingEndpointPermissionLevel] - ModelServingEndpointPermissions []ModelServingEndpointPermission -) - -func (ps ModelServingEndpointPermissions) ToAccessControlRequests() []iam.AccessControlRequest { - result := make([]iam.AccessControlRequest, len(ps)) - for i, p := range ps { - result[i] = Permission[serving.ServingEndpointPermissionLevel](p).ToAccessControlRequest() - } - return result -} - -type ( - PipelinePermission Permission[pipelines.PipelinePermissionLevel] - PipelinePermissions []PipelinePermission -) - -func (ps PipelinePermissions) ToAccessControlRequests() []iam.AccessControlRequest { - result := make([]iam.AccessControlRequest, len(ps)) - for i, p := range ps { - result[i] = Permission[pipelines.PipelinePermissionLevel](p).ToAccessControlRequest() - } - return result -} - -type ( - SqlWarehousePermission Permission[sql.WarehousePermissionLevel] - SqlWarehousePermissions []SqlWarehousePermission -) +type SqlWarehousePermission Permission[sql.WarehousePermissionLevel] -func (ps SqlWarehousePermissions) ToAccessControlRequests() []iam.AccessControlRequest { - result := make([]iam.AccessControlRequest, len(ps)) - for i, p := range ps { - result[i] = Permission[sql.WarehousePermissionLevel](p).ToAccessControlRequest() - } - return result +func (p SqlWarehousePermission) ToAccessControlRequest() iam.AccessControlRequest { + return Permission[sql.WarehousePermissionLevel](p).ToAccessControlRequest() } diff --git a/bundle/config/resources/pipeline.go b/bundle/config/resources/pipeline.go index c4ce111103..e213ff9c00 100644 --- a/bundle/config/resources/pipeline.go +++ b/bundle/config/resources/pipeline.go @@ -14,7 +14,7 @@ type Pipeline struct { BaseResource pipelines.CreatePipeline //nolint CreatePipeline also defines Id field with the same json tag "id" - Permissions PipelinePermissions `json:"permissions,omitempty"` + Permissions []PipelinePermission `json:"permissions,omitempty"` } func (p *Pipeline) UnmarshalJSON(b []byte) error { diff --git a/bundle/config/resources/postgres_project.go b/bundle/config/resources/postgres_project.go index fe502586b1..be67280f70 100644 --- a/bundle/config/resources/postgres_project.go +++ b/bundle/config/resources/postgres_project.go @@ -30,7 +30,7 @@ type PostgresProject struct { BaseResource PostgresProjectConfig - Permissions DatabaseProjectPermissions `json:"permissions,omitempty"` + Permissions []IamPermission `json:"permissions,omitempty"` } func (p *PostgresProject) Exists(ctx context.Context, w *databricks.WorkspaceClient, name string) (bool, error) { diff --git a/bundle/config/resources/sql_warehouses.go b/bundle/config/resources/sql_warehouses.go index b1026486df..bed567b805 100644 --- a/bundle/config/resources/sql_warehouses.go +++ b/bundle/config/resources/sql_warehouses.go @@ -14,7 +14,7 @@ type SqlWarehouse struct { BaseResource sql.CreateWarehouseRequest - Permissions SqlWarehousePermissions `json:"permissions,omitempty"` + Permissions []SqlWarehousePermission `json:"permissions,omitempty"` } func (sw *SqlWarehouse) UnmarshalJSON(b []byte) error { diff --git a/bundle/config/resources_types_test.go b/bundle/config/resources_types_test.go index 4ca133f844..577f1dd003 100644 --- a/bundle/config/resources_types_test.go +++ b/bundle/config/resources_types_test.go @@ -18,5 +18,5 @@ func TestResourcesTypesMap(t *testing.T) { typ, ok = ResourcesTypes["jobs.permissions"] assert.True(t, ok, "resources type for 'jobs.permissions' not found in ResourcesTypes map") - assert.Equal(t, reflect.TypeOf(resources.JobPermissions{}), typ, "resources type for 'jobs.permissions' mismatch") + assert.Equal(t, reflect.TypeOf([]resources.JobPermission{}), typ, "resources type for 'jobs.permissions' mismatch") } diff --git a/bundle/deploy/terraform/convert_test.go b/bundle/deploy/terraform/convert_test.go index d3d5dbaced..564340190d 100644 --- a/bundle/deploy/terraform/convert_test.go +++ b/bundle/deploy/terraform/convert_test.go @@ -91,7 +91,7 @@ func TestBundleToTerraformJob(t *testing.T) { func TestBundleToTerraformJobPermissions(t *testing.T) { src := resources.Job{ - Permissions: resources.JobPermissions{ + Permissions: []resources.JobPermission{ { Level: "CAN_VIEW", UserName: "jane@doe.com", @@ -260,7 +260,7 @@ func TestBundleToTerraformPipeline(t *testing.T) { func TestBundleToTerraformPipelinePermissions(t *testing.T) { src := resources.Pipeline{ - Permissions: resources.PipelinePermissions{ + Permissions: []resources.PipelinePermission{ { Level: "CAN_VIEW", UserName: "jane@doe.com", @@ -331,7 +331,7 @@ func TestBundleToTerraformModelPermissions(t *testing.T) { CreateModelRequest: ml.CreateModelRequest{ Name: "name", }, - Permissions: resources.MlflowModelPermissions{ + Permissions: []resources.MlflowModelPermission{ { Level: "CAN_READ", UserName: "jane@doe.com", @@ -385,7 +385,7 @@ func TestBundleToTerraformExperimentPermissions(t *testing.T) { CreateExperiment: ml.CreateExperiment{ Name: "name", }, - Permissions: resources.MlflowExperimentPermissions{ + Permissions: []resources.MlflowExperimentPermission{ { Level: "CAN_READ", UserName: "jane@doe.com", @@ -481,7 +481,7 @@ func TestBundleToTerraformModelServingPermissions(t *testing.T) { }, }, }, - Permissions: resources.ModelServingEndpointPermissions{ + Permissions: []resources.ModelServingEndpointPermission{ { Level: "CAN_VIEW", UserName: "jane@doe.com", diff --git a/bundle/deploy/terraform/tfdyn/convert_alert_test.go b/bundle/deploy/terraform/tfdyn/convert_alert_test.go index 0d474b5356..2b5f95f14b 100644 --- a/bundle/deploy/terraform/tfdyn/convert_alert_test.go +++ b/bundle/deploy/terraform/tfdyn/convert_alert_test.go @@ -21,7 +21,7 @@ func TestConvertAlert(t *testing.T) { CustomSummary: "Test alert summary", CustomDescription: "Test alert description", }, - Permissions: resources.AlertPermissions{ + Permissions: []resources.IamPermission{ { Level: "CAN_VIEW", UserName: "jane@doe.com", diff --git a/bundle/deploy/terraform/tfdyn/convert_app_test.go b/bundle/deploy/terraform/tfdyn/convert_app_test.go index 171d7f27a4..48481a0936 100644 --- a/bundle/deploy/terraform/tfdyn/convert_app_test.go +++ b/bundle/deploy/terraform/tfdyn/convert_app_test.go @@ -35,7 +35,7 @@ func TestConvertApp(t *testing.T) { }, }, }, - Permissions: resources.AppPermissions{ + Permissions: []resources.AppPermission{ { Level: "CAN_RUN", UserName: "jack@gmail.com", diff --git a/bundle/deploy/terraform/tfdyn/convert_cluster_test.go b/bundle/deploy/terraform/tfdyn/convert_cluster_test.go index 7d0c18c6ac..c28e874413 100644 --- a/bundle/deploy/terraform/tfdyn/convert_cluster_test.go +++ b/bundle/deploy/terraform/tfdyn/convert_cluster_test.go @@ -35,7 +35,7 @@ func TestConvertCluster(t *testing.T) { }, }, - Permissions: resources.ClusterPermissions{ + Permissions: []resources.ClusterPermission{ { Level: "CAN_RUN", UserName: "jack@gmail.com", diff --git a/bundle/deploy/terraform/tfdyn/convert_dashboard_test.go b/bundle/deploy/terraform/tfdyn/convert_dashboard_test.go index 0b827d8f19..4188cc9178 100644 --- a/bundle/deploy/terraform/tfdyn/convert_dashboard_test.go +++ b/bundle/deploy/terraform/tfdyn/convert_dashboard_test.go @@ -20,7 +20,7 @@ func TestConvertDashboard(t *testing.T) { EmbedCredentials: true, }, - Permissions: resources.DashboardPermissions{ + Permissions: []resources.IamPermission{ { Level: "CAN_VIEW", UserName: "jane@doe.com", diff --git a/bundle/deploy/terraform/tfdyn/convert_database_instance_test.go b/bundle/deploy/terraform/tfdyn/convert_database_instance_test.go index 8b7a734a5e..0edac98bd0 100644 --- a/bundle/deploy/terraform/tfdyn/convert_database_instance_test.go +++ b/bundle/deploy/terraform/tfdyn/convert_database_instance_test.go @@ -73,7 +73,7 @@ func TestConvertDatabaseInstanceWithPermissions(t *testing.T) { Name: "db-instance-with-permissions", Capacity: "CU_2", }, - Permissions: resources.DatabaseInstancePermissions{ + Permissions: []resources.IamPermission{ { Level: "CAN_USE", UserName: "user@example.com", diff --git a/bundle/deploy/terraform/tfdyn/convert_experiment_test.go b/bundle/deploy/terraform/tfdyn/convert_experiment_test.go index f859603e7a..9140ef13ac 100644 --- a/bundle/deploy/terraform/tfdyn/convert_experiment_test.go +++ b/bundle/deploy/terraform/tfdyn/convert_experiment_test.go @@ -17,7 +17,7 @@ func TestConvertExperiment(t *testing.T) { CreateExperiment: ml.CreateExperiment{ Name: "name", }, - Permissions: resources.MlflowExperimentPermissions{ + Permissions: []resources.MlflowExperimentPermission{ { Level: "CAN_READ", UserName: "jane@doe.com", diff --git a/bundle/deploy/terraform/tfdyn/convert_job_test.go b/bundle/deploy/terraform/tfdyn/convert_job_test.go index 412e4dc37b..782075fc7f 100644 --- a/bundle/deploy/terraform/tfdyn/convert_job_test.go +++ b/bundle/deploy/terraform/tfdyn/convert_job_test.go @@ -71,7 +71,7 @@ func TestConvertJob(t *testing.T) { }, }, }, - Permissions: resources.JobPermissions{ + Permissions: []resources.JobPermission{ { Level: "CAN_VIEW", UserName: "jane@doe.com", diff --git a/bundle/deploy/terraform/tfdyn/convert_model_serving_endpoint_test.go b/bundle/deploy/terraform/tfdyn/convert_model_serving_endpoint_test.go index 45e6d14f49..fbb3d39a72 100644 --- a/bundle/deploy/terraform/tfdyn/convert_model_serving_endpoint_test.go +++ b/bundle/deploy/terraform/tfdyn/convert_model_serving_endpoint_test.go @@ -35,7 +35,7 @@ func TestConvertModelServingEndpoint(t *testing.T) { }, }, }, - Permissions: resources.ModelServingEndpointPermissions{ + Permissions: []resources.ModelServingEndpointPermission{ { Level: "CAN_VIEW", UserName: "jane@doe.com", diff --git a/bundle/deploy/terraform/tfdyn/convert_model_test.go b/bundle/deploy/terraform/tfdyn/convert_model_test.go index 7207a8dcd3..3d7e8b22c4 100644 --- a/bundle/deploy/terraform/tfdyn/convert_model_test.go +++ b/bundle/deploy/terraform/tfdyn/convert_model_test.go @@ -28,7 +28,7 @@ func TestConvertModel(t *testing.T) { }, }, }, - Permissions: resources.MlflowModelPermissions{ + Permissions: []resources.MlflowModelPermission{ { Level: "CAN_READ", UserName: "jane@doe.com", diff --git a/bundle/deploy/terraform/tfdyn/convert_permissions_test.go b/bundle/deploy/terraform/tfdyn/convert_permissions_test.go index a2b1df127d..4d0d2d7481 100644 --- a/bundle/deploy/terraform/tfdyn/convert_permissions_test.go +++ b/bundle/deploy/terraform/tfdyn/convert_permissions_test.go @@ -13,7 +13,7 @@ import ( func TestConvertPermissions(t *testing.T) { src := resources.Job{ - Permissions: resources.JobPermissions{ + Permissions: []resources.JobPermission{ { Level: "CAN_VIEW", UserName: "jane@doe.com", @@ -72,7 +72,7 @@ func TestConvertPermissionsNil(t *testing.T) { func TestConvertPermissionsEmpty(t *testing.T) { src := resources.Job{ - Permissions: resources.JobPermissions{}, + Permissions: []resources.JobPermission{}, } vin, err := convert.FromTyped(src, dyn.NilValue) diff --git a/bundle/deploy/terraform/tfdyn/convert_pipeline_test.go b/bundle/deploy/terraform/tfdyn/convert_pipeline_test.go index 9bf1e58499..f16b6b8595 100644 --- a/bundle/deploy/terraform/tfdyn/convert_pipeline_test.go +++ b/bundle/deploy/terraform/tfdyn/convert_pipeline_test.go @@ -64,7 +64,7 @@ func TestConvertPipeline(t *testing.T) { }, }, }, - Permissions: resources.PipelinePermissions{ + Permissions: []resources.PipelinePermission{ { Level: "CAN_VIEW", UserName: "jane@doe.com", diff --git a/bundle/deploy/terraform/tfdyn/convert_postgres_project_test.go b/bundle/deploy/terraform/tfdyn/convert_postgres_project_test.go index db40a13907..6e8492b935 100644 --- a/bundle/deploy/terraform/tfdyn/convert_postgres_project_test.go +++ b/bundle/deploy/terraform/tfdyn/convert_postgres_project_test.go @@ -64,7 +64,7 @@ func TestConvertPostgresProjectWithPermissions(t *testing.T) { PgVersion: 17, }, }, - Permissions: resources.DatabaseProjectPermissions{ + Permissions: []resources.IamPermission{ { Level: "CAN_USE", UserName: "user@example.com", diff --git a/bundle/direct/dresources/permissions.go b/bundle/direct/dresources/permissions.go index 32c941394b..0ffc1d62da 100644 --- a/bundle/direct/dresources/permissions.go +++ b/bundle/direct/dresources/permissions.go @@ -3,9 +3,9 @@ package dresources import ( "context" "fmt" + "reflect" "strings" - "github.com/databricks/cli/bundle/config/resources" "github.com/databricks/cli/libs/structs/structvar" "github.com/databricks/databricks-sdk-go" "github.com/databricks/databricks-sdk-go/service/iam" @@ -54,13 +54,11 @@ func PreparePermissionsInputConfig(inputConfig any, node string) (*structvar.Str return nil, fmt.Errorf("unsupported permissions resource type: %s", resourceType) } - permsSlice, ok := inputConfig.(resources.PermissionsSlice) - if !ok { - return nil, fmt.Errorf("expected resources.PermissionsSlice, got %T", inputConfig) + permissions, err := toAccessControlRequests(inputConfig) + if err != nil { + return nil, err } - permissions := permsSlice.ToAccessControlRequests() - objectIdRef := prefix + "${" + baseNode + ".id}" // For permissions, model serving endpoint uses its internal ID, which is different // from its CRUD APIs which use the name. @@ -95,6 +93,26 @@ func (*ResourcePermissions) PrepareState(s *PermissionsState) *PermissionsState return s } +// toAccessControlRequests converts any slice of permission structs to []iam.AccessControlRequest. +// All permission types share the same underlying struct layout (Level, UserName, ServicePrincipalName, GroupName). +func toAccessControlRequests(ps any) ([]iam.AccessControlRequest, error) { + v := reflect.ValueOf(ps) + if v.Kind() != reflect.Slice { + return nil, fmt.Errorf("expected permissions slice, got %T", ps) + } + result := make([]iam.AccessControlRequest, v.Len()) + for i := range v.Len() { + elem := v.Index(i) + result[i] = iam.AccessControlRequest{ + PermissionLevel: iam.PermissionLevel(elem.FieldByName("Level").String()), + UserName: elem.FieldByName("UserName").String(), + ServicePrincipalName: elem.FieldByName("ServicePrincipalName").String(), + GroupName: elem.FieldByName("GroupName").String(), + } + } + return result, nil +} + func accessControlRequestKey(x iam.AccessControlRequest) (string, string) { if x.UserName != "" { return "user_name", x.UserName From e493014874ecb1202da4aeeca3c3bb449d5e969a Mon Sep 17 00:00:00 2001 From: Denis Bilenko Date: Tue, 10 Mar 2026 14:55:18 +0100 Subject: [PATCH 14/28] Replace resources.Permission[iam.PermissionLevel] with resources.IamPermission Co-Authored-By: Claude Sonnet 4.6 --- .../apply_bundle_permissions.go | 20 +- .../apply_bundle_permissions_test.go | 5 +- bundle/config/resources/permission_types.go | 4 + bundle/config/root.go | 3 +- bundle/config/target.go | 3 +- .../validate/folder_permissions_test.go | 9 +- bundle/internal/schema/annotations.yml | 83 +----- .../permission_diagnostics_test.go | 6 +- bundle/permissions/permission_report_test.go | 9 +- bundle/permissions/terraform_errors_test.go | 9 +- bundle/permissions/validate_test.go | 5 +- .../permissions/workspace_path_permissions.go | 16 +- .../workspace_path_permissions_test.go | 20 +- bundle/permissions/workspace_root_test.go | 5 +- bundle/schema/jsonschema.json | 266 +++--------------- bundle/tests/bundle_permissions_test.go | 17 +- 16 files changed, 113 insertions(+), 367 deletions(-) diff --git a/bundle/config/mutator/resourcemutator/apply_bundle_permissions.go b/bundle/config/mutator/resourcemutator/apply_bundle_permissions.go index f859badd8d..3713cfff9b 100644 --- a/bundle/config/mutator/resourcemutator/apply_bundle_permissions.go +++ b/bundle/config/mutator/resourcemutator/apply_bundle_permissions.go @@ -105,7 +105,7 @@ func (m *bundlePermissions) Apply(ctx context.Context, b *bundle.Bundle) diag.Di err = b.Config.Mutate(func(v dyn.Value) (dyn.Value, error) { for key, pattern := range patterns { v, err = dyn.MapByPattern(v, pattern, func(p dyn.Path, v dyn.Value) (dyn.Value, error) { - var permissions []resources.Permission[iam.PermissionLevel] + var permissions []resources.IamPermission pv, err := dyn.Get(v, "permissions") // If the permissions field is not found, we set to an empty array if err != nil { @@ -166,12 +166,12 @@ func (m *bundlePermissions) Name() string { func convertPermissions( ctx context.Context, - bundlePermissions []resources.Permission[iam.PermissionLevel], - resourcePermissions []resources.Permission[iam.PermissionLevel], + bundlePermissions []resources.IamPermission, + resourcePermissions []resources.IamPermission, resourceName string, lm map[string]string, -) []resources.Permission[iam.PermissionLevel] { - var permissions []resources.Permission[iam.PermissionLevel] +) []resources.IamPermission { + var permissions []resources.IamPermission for _, p := range bundlePermissions { level, ok := lm[string(p.Level)] // If there is no bundle permission level defined in the map, it means @@ -184,7 +184,7 @@ func convertPermissions( continue } - permissions = append(permissions, resources.Permission[iam.PermissionLevel]{ + permissions = append(permissions, resources.IamPermission{ Level: iam.PermissionLevel(level), UserName: p.UserName, GroupName: p.GroupName, @@ -196,8 +196,8 @@ func convertPermissions( } func isPermissionOverlap( - permission resources.Permission[iam.PermissionLevel], - resourcePermissions []resources.Permission[iam.PermissionLevel], + permission resources.IamPermission, + resourcePermissions []resources.IamPermission, resourceName string, ) (bool, diag.Diagnostics) { var diagnostics diag.Diagnostics @@ -226,8 +226,8 @@ func isPermissionOverlap( func notifyForPermissionOverlap( ctx context.Context, - permission resources.Permission[iam.PermissionLevel], - resourcePermissions []resources.Permission[iam.PermissionLevel], + permission resources.IamPermission, + resourcePermissions []resources.IamPermission, resourceName string, ) bool { isOverlap, _ := isPermissionOverlap(permission, resourcePermissions, resourceName) diff --git a/bundle/config/mutator/resourcemutator/apply_bundle_permissions_test.go b/bundle/config/mutator/resourcemutator/apply_bundle_permissions_test.go index 989bc293a5..5929a49ca6 100644 --- a/bundle/config/mutator/resourcemutator/apply_bundle_permissions_test.go +++ b/bundle/config/mutator/resourcemutator/apply_bundle_permissions_test.go @@ -10,7 +10,6 @@ import ( "github.com/databricks/cli/bundle" "github.com/databricks/cli/bundle/config" "github.com/databricks/cli/bundle/config/resources" - "github.com/databricks/databricks-sdk-go/service/iam" "github.com/databricks/databricks-sdk-go/service/jobs" "github.com/stretchr/testify/assert" "github.com/stretchr/testify/require" @@ -37,7 +36,7 @@ func TestApplyBundlePermissions(t *testing.T) { Workspace: config.Workspace{ RootPath: "/Users/foo@bar.com", }, - Permissions: []resources.Permission[iam.PermissionLevel]{ + Permissions: []resources.IamPermission{ {Level: permissions.CAN_MANAGE, UserName: "TestUser"}, {Level: permissions.CAN_VIEW, GroupName: "TestGroup"}, {Level: permissions.CAN_RUN, ServicePrincipalName: "TestServicePrincipal"}, @@ -147,7 +146,7 @@ func TestWarningOnOverlapPermission(t *testing.T) { Workspace: config.Workspace{ RootPath: "/Users/foo@bar.com", }, - Permissions: []resources.Permission[iam.PermissionLevel]{ + Permissions: []resources.IamPermission{ {Level: permissions.CAN_MANAGE, UserName: "TestUser"}, {Level: permissions.CAN_VIEW, GroupName: "TestGroup"}, }, diff --git a/bundle/config/resources/permission_types.go b/bundle/config/resources/permission_types.go index 157e95f688..56608d7d00 100644 --- a/bundle/config/resources/permission_types.go +++ b/bundle/config/resources/permission_types.go @@ -22,6 +22,10 @@ func (p IamPermission) ToAccessControlRequest() iam.AccessControlRequest { return Permission[iam.PermissionLevel](p).ToAccessControlRequest() } +func (p IamPermission) String() string { + return Permission[iam.PermissionLevel](p).String() +} + type AppPermission Permission[apps.AppPermissionLevel] func (p AppPermission) ToAccessControlRequest() iam.AccessControlRequest { diff --git a/bundle/config/root.go b/bundle/config/root.go index ab8e7512d2..25b3d9ac46 100644 --- a/bundle/config/root.go +++ b/bundle/config/root.go @@ -17,7 +17,6 @@ import ( "github.com/databricks/cli/libs/dyn/merge" "github.com/databricks/cli/libs/dyn/yamlloader" "github.com/databricks/cli/libs/log" - "github.com/databricks/databricks-sdk-go/service/iam" "github.com/databricks/databricks-sdk-go/service/jobs" ) @@ -76,7 +75,7 @@ type Root struct { // Permissions section allows to define permissions which will be // applied to all resources defined in bundle - Permissions []resources.Permission[iam.PermissionLevel] `json:"permissions,omitempty"` + Permissions []resources.IamPermission `json:"permissions,omitempty"` // Locations is an output-only field that holds configuration location // information for every path in the configuration tree. diff --git a/bundle/config/target.go b/bundle/config/target.go index d5f1f2d052..3d8842a724 100644 --- a/bundle/config/target.go +++ b/bundle/config/target.go @@ -3,7 +3,6 @@ package config import ( "github.com/databricks/cli/bundle/config/resources" "github.com/databricks/cli/bundle/config/variable" - "github.com/databricks/databricks-sdk-go/service/iam" "github.com/databricks/databricks-sdk-go/service/jobs" ) @@ -69,7 +68,7 @@ type Target struct { Sync *Sync `json:"sync,omitempty"` - Permissions []resources.Permission[iam.PermissionLevel] `json:"permissions,omitempty"` + Permissions []resources.IamPermission `json:"permissions,omitempty"` } const ( diff --git a/bundle/config/validate/folder_permissions_test.go b/bundle/config/validate/folder_permissions_test.go index dc699ba2a5..a6d77b4973 100644 --- a/bundle/config/validate/folder_permissions_test.go +++ b/bundle/config/validate/folder_permissions_test.go @@ -10,7 +10,6 @@ import ( "github.com/databricks/cli/libs/diag" "github.com/databricks/databricks-sdk-go/apierr" "github.com/databricks/databricks-sdk-go/experimental/mocks" - "github.com/databricks/databricks-sdk-go/service/iam" "github.com/databricks/databricks-sdk-go/service/workspace" "github.com/stretchr/testify/mock" "github.com/stretchr/testify/require" @@ -26,7 +25,7 @@ func TestFolderPermissionsInheritedWhenRootPathDoesNotExist(t *testing.T) { StatePath: "/Workspace/Users/foo@bar.com/state", ResourcePath: "/Workspace/Users/foo@bar.com/resources", }, - Permissions: []resources.Permission[iam.PermissionLevel]{ + Permissions: []resources.IamPermission{ {Level: permissions.CAN_MANAGE, UserName: "foo@bar.com"}, }, }, @@ -83,7 +82,7 @@ func TestValidateFolderPermissionsFailsOnMissingBundlePermission(t *testing.T) { StatePath: "/Workspace/Users/foo@bar.com/state", ResourcePath: "/Workspace/Users/foo@bar.com/resources", }, - Permissions: []resources.Permission[iam.PermissionLevel]{ + Permissions: []resources.IamPermission{ {Level: permissions.CAN_MANAGE, UserName: "foo@bar.com"}, }, }, @@ -137,7 +136,7 @@ func TestValidateFolderPermissionsFailsOnPermissionMismatch(t *testing.T) { StatePath: "/Workspace/Users/foo@bar.com/state", ResourcePath: "/Workspace/Users/foo@bar.com/resources", }, - Permissions: []resources.Permission[iam.PermissionLevel]{ + Permissions: []resources.IamPermission{ {Level: permissions.CAN_MANAGE, UserName: "foo@bar.com"}, }, }, @@ -180,7 +179,7 @@ func TestValidateFolderPermissionsFailsOnNoRootFolder(t *testing.T) { StatePath: "/NotExisting/state", ResourcePath: "/NotExisting/resources", }, - Permissions: []resources.Permission[iam.PermissionLevel]{ + Permissions: []resources.IamPermission{ {Level: permissions.CAN_MANAGE, UserName: "foo@bar.com"}, }, }, diff --git a/bundle/internal/schema/annotations.yml b/bundle/internal/schema/annotations.yml index 83da357b52..adb6c14186 100644 --- a/bundle/internal/schema/annotations.yml +++ b/bundle/internal/schema/annotations.yml @@ -509,19 +509,6 @@ github.com/databricks/cli/bundle/config/resources.Alert: "warehouse_id": "description": |- PLACEHOLDER -github.com/databricks/cli/bundle/config/resources.AlertPermission: - "group_name": - "description": |- - PLACEHOLDER - "level": - "description": |- - PLACEHOLDER - "service_principal_name": - "description": |- - PLACEHOLDER - "user_name": - "description": |- - PLACEHOLDER github.com/databricks/cli/bundle/config/resources.App: "git_source": "description": |- @@ -603,49 +590,10 @@ github.com/databricks/cli/bundle/config/resources.Dashboard: "dataset_schema": "description": |- Sets the default schema for all datasets in this dashboard. When set, this overrides the schema specified in individual dataset definitions. -github.com/databricks/cli/bundle/config/resources.DashboardPermission: - "group_name": - "description": |- - PLACEHOLDER - "level": - "description": |- - PLACEHOLDER - "service_principal_name": - "description": |- - PLACEHOLDER - "user_name": - "description": |- - PLACEHOLDER github.com/databricks/cli/bundle/config/resources.DatabaseInstance: "effective_capacity": "description": |- PLACEHOLDER -github.com/databricks/cli/bundle/config/resources.DatabaseInstancePermission: - "group_name": - "description": |- - PLACEHOLDER - "level": - "description": |- - PLACEHOLDER - "service_principal_name": - "description": |- - PLACEHOLDER - "user_name": - "description": |- - PLACEHOLDER -github.com/databricks/cli/bundle/config/resources.DatabaseProjectPermission: - "group_name": - "description": |- - PLACEHOLDER - "level": - "description": |- - PLACEHOLDER - "service_principal_name": - "description": |- - PLACEHOLDER - "user_name": - "description": |- - PLACEHOLDER github.com/databricks/cli/bundle/config/resources.ExternalLocation: "comment": "description": |- @@ -683,7 +631,7 @@ github.com/databricks/cli/bundle/config/resources.ExternalLocation: "url": "description": |- PLACEHOLDER -github.com/databricks/cli/bundle/config/resources.JobPermission: +github.com/databricks/cli/bundle/config/resources.IamPermission: "group_name": "description": |- PLACEHOLDER @@ -696,11 +644,7 @@ github.com/databricks/cli/bundle/config/resources.JobPermission: "user_name": "description": |- PLACEHOLDER -github.com/databricks/cli/bundle/config/resources.Lifecycle: - "prevent_destroy": - "description": |- - Lifecycle setting to prevent the resource from being destroyed. -github.com/databricks/cli/bundle/config/resources.MlflowExperimentPermission: +github.com/databricks/cli/bundle/config/resources.JobPermission: "group_name": "description": |- PLACEHOLDER @@ -713,7 +657,11 @@ github.com/databricks/cli/bundle/config/resources.MlflowExperimentPermission: "user_name": "description": |- PLACEHOLDER -github.com/databricks/cli/bundle/config/resources.MlflowModelPermission: +github.com/databricks/cli/bundle/config/resources.Lifecycle: + "prevent_destroy": + "description": |- + Lifecycle setting to prevent the resource from being destroyed. +github.com/databricks/cli/bundle/config/resources.MlflowExperimentPermission: "group_name": "description": |- PLACEHOLDER @@ -726,7 +674,7 @@ github.com/databricks/cli/bundle/config/resources.MlflowModelPermission: "user_name": "description": |- PLACEHOLDER -github.com/databricks/cli/bundle/config/resources.ModelServingEndpointPermission: +github.com/databricks/cli/bundle/config/resources.MlflowModelPermission: "group_name": "description": |- PLACEHOLDER @@ -739,24 +687,19 @@ github.com/databricks/cli/bundle/config/resources.ModelServingEndpointPermission "user_name": "description": |- PLACEHOLDER -? github.com/databricks/cli/bundle/config/resources.Permission[github.com/databricks/databricks-sdk-go/service/iam.PermissionLevel] -: "-": - "description": |- - Defines a permission for a specific entity. - "markdown_description": |- - Defines a permission for a specific entity. See [\_](/dev-tools/bundles/settings.md#permissions) and [\_](/dev-tools/bundles/permissions.md). +github.com/databricks/cli/bundle/config/resources.ModelServingEndpointPermission: "group_name": "description": |- - The name of the group that has the permission set in level. + PLACEHOLDER "level": "description": |- - The allowed permission for user, group, service principal defined for this permission. + PLACEHOLDER "service_principal_name": "description": |- - The name of the service principal that has the permission set in level. + PLACEHOLDER "user_name": "description": |- - The name of the user that has the permission set in level. + PLACEHOLDER github.com/databricks/cli/bundle/config/resources.PipelinePermission: "group_name": "description": |- diff --git a/bundle/permissions/permission_diagnostics_test.go b/bundle/permissions/permission_diagnostics_test.go index c206fd14ac..ede17bb62d 100644 --- a/bundle/permissions/permission_diagnostics_test.go +++ b/bundle/permissions/permission_diagnostics_test.go @@ -13,7 +13,7 @@ import ( ) func TestPermissionDiagnosticsApplySuccess(t *testing.T) { - b := mockBundle([]resources.Permission[iam.PermissionLevel]{ + b := mockBundle([]resources.IamPermission{ {Level: "CAN_MANAGE", UserName: "testuser@databricks.com"}, }) @@ -29,7 +29,7 @@ func TestPermissionDiagnosticsEmpty(t *testing.T) { } func TestPermissionDiagnosticsApplyFail(t *testing.T) { - b := mockBundle([]resources.Permission[iam.PermissionLevel]{ + b := mockBundle([]resources.IamPermission{ {Level: "CAN_VIEW", UserName: "testuser@databricks.com"}, }) @@ -48,7 +48,7 @@ func TestPermissionDiagnosticsApplyFail(t *testing.T) { require.Contains(t, diags[0].Summary, expectedMsg) } -func mockBundle(permissions []resources.Permission[iam.PermissionLevel]) *bundle.Bundle { +func mockBundle(permissions []resources.IamPermission) *bundle.Bundle { return &bundle.Bundle{ Config: config.Root{ Workspace: config.Workspace{ diff --git a/bundle/permissions/permission_report_test.go b/bundle/permissions/permission_report_test.go index b3810dae51..5c77564512 100644 --- a/bundle/permissions/permission_report_test.go +++ b/bundle/permissions/permission_report_test.go @@ -5,12 +5,11 @@ import ( "github.com/databricks/cli/bundle/config/resources" "github.com/databricks/cli/bundle/permissions" - "github.com/databricks/databricks-sdk-go/service/iam" "github.com/stretchr/testify/require" ) func TestPermissionsReportPermissionDeniedWithGroup(t *testing.T) { - b := mockBundle([]resources.Permission[iam.PermissionLevel]{ + b := mockBundle([]resources.IamPermission{ {Level: "CAN_MANAGE", GroupName: "testgroup"}, }) @@ -23,7 +22,7 @@ func TestPermissionsReportPermissionDeniedWithGroup(t *testing.T) { } func TestPermissionsReportPermissionDeniedWithOtherGroup(t *testing.T) { - b := mockBundle([]resources.Permission[iam.PermissionLevel]{ + b := mockBundle([]resources.IamPermission{ {Level: "CAN_MANAGE", GroupName: "othergroup"}, }) @@ -37,7 +36,7 @@ func TestPermissionsReportPermissionDeniedWithOtherGroup(t *testing.T) { } func TestPermissionsReportPermissionDeniedWithoutPermission(t *testing.T) { - b := mockBundle([]resources.Permission[iam.PermissionLevel]{ + b := mockBundle([]resources.IamPermission{ {Level: "CAN_VIEW", UserName: "testuser@databricks.com"}, }) @@ -63,7 +62,7 @@ func TestPermissionsReportPermissionDeniedNilPermission(t *testing.T) { } func TestPermissionsReportFindOtherOwners(t *testing.T) { - b := mockBundle([]resources.Permission[iam.PermissionLevel]{ + b := mockBundle([]resources.IamPermission{ {Level: "CAN_MANAGE", GroupName: "testgroup"}, {Level: "CAN_MANAGE", UserName: "alice@databricks.com"}, }) diff --git a/bundle/permissions/terraform_errors_test.go b/bundle/permissions/terraform_errors_test.go index bf5b136a5f..887e0a0a4a 100644 --- a/bundle/permissions/terraform_errors_test.go +++ b/bundle/permissions/terraform_errors_test.go @@ -6,14 +6,13 @@ import ( "github.com/databricks/cli/bundle/config/resources" "github.com/databricks/cli/bundle/permissions" - "github.com/databricks/databricks-sdk-go/service/iam" "github.com/databricks/databricks-sdk-go/service/jobs" "github.com/stretchr/testify/require" ) func TestTryExtendTerraformPermissionError1(t *testing.T) { ctx := t.Context() - b := mockBundle([]resources.Permission[iam.PermissionLevel]{ + b := mockBundle([]resources.IamPermission{ {Level: "CAN_MANAGE", UserName: "alice@databricks.com"}, }) err := permissions.TryExtendTerraformPermissionError(ctx, b, errors.New("Error: terraform apply: exit status 1\n"+ @@ -34,7 +33,7 @@ func TestTryExtendTerraformPermissionError1(t *testing.T) { func TestTryExtendTerraformPermissionError2(t *testing.T) { ctx := t.Context() - b := mockBundle([]resources.Permission[iam.PermissionLevel]{ + b := mockBundle([]resources.IamPermission{ {Level: "CAN_MANAGE", UserName: "alice@databricks.com"}, {Level: "CAN_MANAGE", UserName: "bob@databricks.com"}, }) @@ -55,7 +54,7 @@ func TestTryExtendTerraformPermissionError2(t *testing.T) { func TestTryExtendTerraformPermissionError3(t *testing.T) { ctx := t.Context() - b := mockBundle([]resources.Permission[iam.PermissionLevel]{ + b := mockBundle([]resources.IamPermission{ {Level: "CAN_MANAGE", UserName: "testuser@databricks.com"}, }) err := permissions.TryExtendTerraformPermissionError(ctx, b, errors.New("Error: terraform apply: exit status 1\n"+ @@ -75,7 +74,7 @@ func TestTryExtendTerraformPermissionError3(t *testing.T) { func TestTryExtendTerraformPermissionErrorNotOwner(t *testing.T) { ctx := t.Context() - b := mockBundle([]resources.Permission[iam.PermissionLevel]{ + b := mockBundle([]resources.IamPermission{ {Level: "CAN_MANAGE", GroupName: "data_team@databricks.com"}, }) b.Config.RunAs = &jobs.JobRunAs{ diff --git a/bundle/permissions/validate_test.go b/bundle/permissions/validate_test.go index b5406df950..85b3da0cd4 100644 --- a/bundle/permissions/validate_test.go +++ b/bundle/permissions/validate_test.go @@ -8,7 +8,6 @@ import ( "github.com/databricks/cli/bundle/config/resources" "github.com/databricks/cli/libs/diag" "github.com/databricks/databricks-sdk-go/experimental/mocks" - "github.com/databricks/databricks-sdk-go/service/iam" "github.com/databricks/databricks-sdk-go/service/jobs" "github.com/stretchr/testify/require" ) @@ -19,7 +18,7 @@ func TestValidateSharedRootPermissionsForShared(t *testing.T) { Workspace: config.Workspace{ RootPath: "/Workspace/Shared/foo/bar", }, - Permissions: []resources.Permission[iam.PermissionLevel]{ + Permissions: []resources.IamPermission{ {Level: CAN_MANAGE, GroupName: "users"}, }, Resources: config.Resources{ @@ -44,7 +43,7 @@ func TestValidateSharedRootPermissionsForSharedError(t *testing.T) { Workspace: config.Workspace{ RootPath: "/Workspace/Shared/foo/bar", }, - Permissions: []resources.Permission[iam.PermissionLevel]{ + Permissions: []resources.IamPermission{ {Level: CAN_MANAGE, UserName: "foo@bar.com"}, }, Resources: config.Resources{ diff --git a/bundle/permissions/workspace_path_permissions.go b/bundle/permissions/workspace_path_permissions.go index 94d2eb1e43..f6928b6124 100644 --- a/bundle/permissions/workspace_path_permissions.go +++ b/bundle/permissions/workspace_path_permissions.go @@ -12,11 +12,11 @@ import ( type WorkspacePathPermissions struct { Path string - Permissions []resources.Permission[iam.PermissionLevel] + Permissions []resources.IamPermission } func ObjectAclToResourcePermissions(path string, acl []workspace.WorkspaceObjectAccessControlResponse) *WorkspacePathPermissions { - var permissions []resources.Permission[iam.PermissionLevel] + var permissions []resources.IamPermission for _, a := range acl { // Skip the admin group because it's added to all resources by default. if a.GroupName == "admins" { @@ -33,7 +33,7 @@ func ObjectAclToResourcePermissions(path string, acl []workspace.WorkspaceObject } if highestLevel != "" { - permissions = append(permissions, resources.Permission[iam.PermissionLevel]{ + permissions = append(permissions, resources.IamPermission{ Level: highestLevel, GroupName: a.GroupName, UserName: a.UserName, @@ -45,7 +45,7 @@ func ObjectAclToResourcePermissions(path string, acl []workspace.WorkspaceObject return &WorkspacePathPermissions{Permissions: permissions, Path: path} } -func (p WorkspacePathPermissions) Compare(perms []resources.Permission[iam.PermissionLevel]) diag.Diagnostics { +func (p WorkspacePathPermissions) Compare(perms []resources.IamPermission) diag.Diagnostics { var diags diag.Diagnostics // Check the permissions in the workspace and see if they are all set in the bundle. @@ -67,7 +67,7 @@ func (p WorkspacePathPermissions) Compare(perms []resources.Permission[iam.Permi } // samePrincipal checks if two permissions refer to the same user/group/service principal. -func samePrincipal(a, b resources.Permission[iam.PermissionLevel]) bool { +func samePrincipal(a, b resources.IamPermission) bool { return a.UserName == b.UserName && a.GroupName == b.GroupName && a.ServicePrincipalName == b.ServicePrincipalName @@ -76,8 +76,8 @@ func samePrincipal(a, b resources.Permission[iam.PermissionLevel]) bool { // containsAll checks if all permissions in permA (workspace) are accounted for in permB (bundle). // A workspace permission is considered accounted for if the bundle has the same principal // with an equal or higher permission level. -func containsAll(permA, permB []resources.Permission[iam.PermissionLevel]) (bool, []resources.Permission[iam.PermissionLevel]) { - var missing []resources.Permission[iam.PermissionLevel] +func containsAll(permA, permB []resources.IamPermission) (bool, []resources.IamPermission) { + var missing []resources.IamPermission for _, a := range permA { found := false for _, b := range permB { @@ -104,7 +104,7 @@ func convertWorkspaceObjectPermissionLevel(level workspace.WorkspaceObjectPermis } } -func toString(p []resources.Permission[iam.PermissionLevel]) string { +func toString(p []resources.IamPermission) string { var sb strings.Builder for _, perm := range p { sb.WriteString(fmt.Sprintf("- %s\n", perm.String())) diff --git a/bundle/permissions/workspace_path_permissions_test.go b/bundle/permissions/workspace_path_permissions_test.go index d34a9b6dd0..0c8883a6ad 100644 --- a/bundle/permissions/workspace_path_permissions_test.go +++ b/bundle/permissions/workspace_path_permissions_test.go @@ -12,12 +12,12 @@ import ( func TestWorkspacePathPermissionsCompare(t *testing.T) { testCases := []struct { - perms []resources.Permission[iam.PermissionLevel] + perms []resources.IamPermission acl []workspace.WorkspaceObjectAccessControlResponse expected diag.Diagnostics }{ { - perms: []resources.Permission[iam.PermissionLevel]{ + perms: []resources.IamPermission{ {Level: CAN_MANAGE, UserName: "foo@bar.com"}, }, acl: []workspace.WorkspaceObjectAccessControlResponse{ @@ -31,7 +31,7 @@ func TestWorkspacePathPermissionsCompare(t *testing.T) { expected: nil, }, { - perms: []resources.Permission[iam.PermissionLevel]{ + perms: []resources.IamPermission{ {Level: CAN_MANAGE, UserName: "foo@bar.com"}, }, acl: []workspace.WorkspaceObjectAccessControlResponse{ @@ -51,7 +51,7 @@ func TestWorkspacePathPermissionsCompare(t *testing.T) { expected: nil, }, { - perms: []resources.Permission[iam.PermissionLevel]{ + perms: []resources.IamPermission{ {Level: CAN_VIEW, UserName: "foo@bar.com"}, {Level: CAN_MANAGE, ServicePrincipalName: "sp.com"}, }, @@ -66,7 +66,7 @@ func TestWorkspacePathPermissionsCompare(t *testing.T) { expected: nil, }, { - perms: []resources.Permission[iam.PermissionLevel]{ + perms: []resources.IamPermission{ {Level: CAN_MANAGE, UserName: "foo@bar.com"}, }, acl: []workspace.WorkspaceObjectAccessControlResponse{ @@ -95,7 +95,7 @@ func TestWorkspacePathPermissionsCompare(t *testing.T) { }, }, { - perms: []resources.Permission[iam.PermissionLevel]{ + perms: []resources.IamPermission{ {Level: CAN_MANAGE, UserName: "foo@bar.com"}, }, acl: []workspace.WorkspaceObjectAccessControlResponse{ @@ -129,13 +129,13 @@ func TestWorkspacePathPermissionsCompare(t *testing.T) { func TestWorkspacePathPermissionsCompareWithHierarchy(t *testing.T) { testCases := []struct { name string - perms []resources.Permission[iam.PermissionLevel] + perms []resources.IamPermission acl []workspace.WorkspaceObjectAccessControlResponse expected diag.Diagnostics }{ { name: "bundle grants higher permission than workspace - no warning", - perms: []resources.Permission[iam.PermissionLevel]{ + perms: []resources.IamPermission{ {Level: CAN_MANAGE, UserName: "foo@bar.com"}, }, acl: []workspace.WorkspaceObjectAccessControlResponse{ @@ -150,7 +150,7 @@ func TestWorkspacePathPermissionsCompareWithHierarchy(t *testing.T) { }, { name: "bundle grants lower permission than workspace - warning", - perms: []resources.Permission[iam.PermissionLevel]{ + perms: []resources.IamPermission{ {Level: CAN_VIEW, UserName: "foo@bar.com"}, }, acl: []workspace.WorkspaceObjectAccessControlResponse{ @@ -174,7 +174,7 @@ func TestWorkspacePathPermissionsCompareWithHierarchy(t *testing.T) { }, { name: "bundle grants same permission as workspace - no warning", - perms: []resources.Permission[iam.PermissionLevel]{ + perms: []resources.IamPermission{ {Level: CAN_MANAGE, UserName: "foo@bar.com"}, }, acl: []workspace.WorkspaceObjectAccessControlResponse{ diff --git a/bundle/permissions/workspace_root_test.go b/bundle/permissions/workspace_root_test.go index 1b16f7802b..cfaade001d 100644 --- a/bundle/permissions/workspace_root_test.go +++ b/bundle/permissions/workspace_root_test.go @@ -7,7 +7,6 @@ import ( "github.com/databricks/cli/bundle/config" "github.com/databricks/cli/bundle/config/resources" "github.com/databricks/databricks-sdk-go/experimental/mocks" - "github.com/databricks/databricks-sdk-go/service/iam" "github.com/databricks/databricks-sdk-go/service/jobs" "github.com/databricks/databricks-sdk-go/service/ml" "github.com/databricks/databricks-sdk-go/service/pipelines" @@ -27,7 +26,7 @@ func TestApplyWorkspaceRootPermissions(t *testing.T) { StatePath: "/Users/foo@bar.com/state", ResourcePath: "/Users/foo@bar.com/resources", }, - Permissions: []resources.Permission[iam.PermissionLevel]{ + Permissions: []resources.IamPermission{ {Level: CAN_MANAGE, UserName: "TestUser"}, {Level: CAN_VIEW, GroupName: "TestGroup"}, {Level: CAN_RUN, ServicePrincipalName: "TestServicePrincipal"}, @@ -87,7 +86,7 @@ func TestApplyWorkspaceRootPermissionsForAllPaths(t *testing.T) { StatePath: "/Users/foo@bar.com/state", ResourcePath: "/Users/foo@bar.com/resources", }, - Permissions: []resources.Permission[iam.PermissionLevel]{ + Permissions: []resources.IamPermission{ {Level: CAN_MANAGE, UserName: "TestUser"}, {Level: CAN_VIEW, GroupName: "TestGroup"}, {Level: CAN_RUN, ServicePrincipalName: "TestServicePrincipal"}, diff --git a/bundle/schema/jsonschema.json b/bundle/schema/jsonschema.json index b21b2fb95e..e048f995ea 100644 --- a/bundle/schema/jsonschema.json +++ b/bundle/schema/jsonschema.json @@ -86,7 +86,7 @@ "$ref": "#/$defs/string" }, "permissions": { - "$ref": "#/$defs/slice/github.com/databricks/cli/bundle/config/resources.AlertPermission" + "$ref": "#/$defs/slice/github.com/databricks/cli/bundle/config/resources.IamPermission" }, "query_text": { "$ref": "#/$defs/string" @@ -121,35 +121,6 @@ } ] }, - "resources.AlertPermission": { - "oneOf": [ - { - "type": "object", - "properties": { - "group_name": { - "$ref": "#/$defs/string" - }, - "level": { - "$ref": "#/$defs/github.com/databricks/databricks-sdk-go/service/iam.PermissionLevel" - }, - "service_principal_name": { - "$ref": "#/$defs/string" - }, - "user_name": { - "$ref": "#/$defs/string" - } - }, - "additionalProperties": false, - "required": [ - "level" - ] - }, - { - "type": "string", - "pattern": "\\$\\{(var(\\.[a-zA-Z]+([-_]?[a-zA-Z0-9]+)*(\\[[0-9]+\\])*)+)\\}" - } - ] - }, "resources.App": { "oneOf": [ { @@ -577,7 +548,7 @@ "$ref": "#/$defs/string" }, "permissions": { - "$ref": "#/$defs/slice/github.com/databricks/cli/bundle/config/resources.DashboardPermission" + "$ref": "#/$defs/slice/github.com/databricks/cli/bundle/config/resources.IamPermission" }, "serialized_dashboard": { "description": "The contents of the dashboard in serialized string form.\nThis field is excluded in List Dashboards responses.\nUse the [get dashboard API](https://docs.databricks.com/api/workspace/lakeview/get)\nto retrieve an example response, which includes the `serialized_dashboard` field.\nThis field provides the structure of the JSON string that represents the dashboard's\nlayout and components.", @@ -601,35 +572,6 @@ } ] }, - "resources.DashboardPermission": { - "oneOf": [ - { - "type": "object", - "properties": { - "group_name": { - "$ref": "#/$defs/string" - }, - "level": { - "$ref": "#/$defs/github.com/databricks/databricks-sdk-go/service/iam.PermissionLevel" - }, - "service_principal_name": { - "$ref": "#/$defs/string" - }, - "user_name": { - "$ref": "#/$defs/string" - } - }, - "additionalProperties": false, - "required": [ - "level" - ] - }, - { - "type": "string", - "pattern": "\\$\\{(var(\\.[a-zA-Z]+([-_]?[a-zA-Z0-9]+)*(\\[[0-9]+\\])*)+)\\}" - } - ] - }, "resources.DatabaseCatalog": { "oneOf": [ { @@ -707,7 +649,7 @@ "$ref": "#/$defs/github.com/databricks/databricks-sdk-go/service/database.DatabaseInstanceRef" }, "permissions": { - "$ref": "#/$defs/slice/github.com/databricks/cli/bundle/config/resources.DatabaseInstancePermission" + "$ref": "#/$defs/slice/github.com/databricks/cli/bundle/config/resources.IamPermission" }, "retention_window_in_days": { "description": "The retention window for the instance. This is the time window in days\nfor which the historical data is retained. The default value is 7 days.\nValid values are 2 to 35 days.", @@ -733,64 +675,6 @@ } ] }, - "resources.DatabaseInstancePermission": { - "oneOf": [ - { - "type": "object", - "properties": { - "group_name": { - "$ref": "#/$defs/string" - }, - "level": { - "$ref": "#/$defs/github.com/databricks/databricks-sdk-go/service/iam.PermissionLevel" - }, - "service_principal_name": { - "$ref": "#/$defs/string" - }, - "user_name": { - "$ref": "#/$defs/string" - } - }, - "additionalProperties": false, - "required": [ - "level" - ] - }, - { - "type": "string", - "pattern": "\\$\\{(var(\\.[a-zA-Z]+([-_]?[a-zA-Z0-9]+)*(\\[[0-9]+\\])*)+)\\}" - } - ] - }, - "resources.DatabaseProjectPermission": { - "oneOf": [ - { - "type": "object", - "properties": { - "group_name": { - "$ref": "#/$defs/string" - }, - "level": { - "$ref": "#/$defs/github.com/databricks/databricks-sdk-go/service/iam.PermissionLevel" - }, - "service_principal_name": { - "$ref": "#/$defs/string" - }, - "user_name": { - "$ref": "#/$defs/string" - } - }, - "additionalProperties": false, - "required": [ - "level" - ] - }, - { - "type": "string", - "pattern": "\\$\\{(var(\\.[a-zA-Z]+([-_]?[a-zA-Z0-9]+)*(\\[[0-9]+\\])*)+)\\}" - } - ] - }, "resources.ExternalLocation": { "oneOf": [ { @@ -846,6 +730,35 @@ } ] }, + "resources.IamPermission": { + "oneOf": [ + { + "type": "object", + "properties": { + "group_name": { + "$ref": "#/$defs/string" + }, + "level": { + "$ref": "#/$defs/github.com/databricks/databricks-sdk-go/service/iam.PermissionLevel" + }, + "service_principal_name": { + "$ref": "#/$defs/string" + }, + "user_name": { + "$ref": "#/$defs/string" + } + }, + "additionalProperties": false, + "required": [ + "level" + ] + }, + { + "type": "string", + "pattern": "\\$\\{(var(\\.[a-zA-Z]+([-_]?[a-zA-Z0-9]+)*(\\[[0-9]+\\])*)+)\\}" + } + ] + }, "resources.Job": { "oneOf": [ { @@ -1226,47 +1139,6 @@ } ] }, - "resources.Permission[github.com": { - "databricks": { - "databricks-sdk-go": { - "service": { - "iam.PermissionLevel]": { - "oneOf": [ - { - "type": "object", - "properties": { - "group_name": { - "description": "The name of the group that has the permission set in level.", - "$ref": "#/$defs/string" - }, - "level": { - "description": "The allowed permission for user, group, service principal defined for this permission.", - "$ref": "#/$defs/github.com/databricks/databricks-sdk-go/service/iam.PermissionLevel" - }, - "service_principal_name": { - "description": "The name of the service principal that has the permission set in level.", - "$ref": "#/$defs/string" - }, - "user_name": { - "description": "The name of the user that has the permission set in level.", - "$ref": "#/$defs/string" - } - }, - "additionalProperties": false, - "required": [ - "level" - ] - }, - { - "type": "string", - "pattern": "\\$\\{(var(\\.[a-zA-Z]+([-_]?[a-zA-Z0-9]+)*(\\[[0-9]+\\])*)+)\\}" - } - ] - } - } - } - } - }, "resources.Pipeline": { "oneOf": [ { @@ -1570,7 +1442,7 @@ "$ref": "#/$defs/github.com/databricks/cli/bundle/config/resources.Lifecycle" }, "permissions": { - "$ref": "#/$defs/slice/github.com/databricks/cli/bundle/config/resources.DatabaseProjectPermission" + "$ref": "#/$defs/slice/github.com/databricks/cli/bundle/config/resources.IamPermission" }, "pg_version": { "$ref": "#/$defs/int" @@ -2689,7 +2561,7 @@ }, "permissions": { "description": "The permissions for deploying and running the bundle in the target.", - "$ref": "#/$defs/slice/github.com/databricks/cli/bundle/config/resources.Permission[github.com/databricks/databricks-sdk-go/service/iam.PermissionLevel]" + "$ref": "#/$defs/slice/github.com/databricks/cli/bundle/config/resources.IamPermission" }, "presets": { "description": "The deployment presets for the target.", @@ -11018,20 +10890,6 @@ "cli": { "bundle": { "config": { - "resources.AlertPermission": { - "oneOf": [ - { - "type": "array", - "items": { - "$ref": "#/$defs/github.com/databricks/cli/bundle/config/resources.AlertPermission" - } - }, - { - "type": "string", - "pattern": "\\$\\{(var(\\.[a-zA-Z]+([-_]?[a-zA-Z0-9]+)*(\\[[0-9]+\\])*)+)\\}" - } - ] - }, "resources.AppEnvVar": { "oneOf": [ { @@ -11074,40 +10932,12 @@ } ] }, - "resources.DashboardPermission": { - "oneOf": [ - { - "type": "array", - "items": { - "$ref": "#/$defs/github.com/databricks/cli/bundle/config/resources.DashboardPermission" - } - }, - { - "type": "string", - "pattern": "\\$\\{(var(\\.[a-zA-Z]+([-_]?[a-zA-Z0-9]+)*(\\[[0-9]+\\])*)+)\\}" - } - ] - }, - "resources.DatabaseInstancePermission": { + "resources.IamPermission": { "oneOf": [ { "type": "array", "items": { - "$ref": "#/$defs/github.com/databricks/cli/bundle/config/resources.DatabaseInstancePermission" - } - }, - { - "type": "string", - "pattern": "\\$\\{(var(\\.[a-zA-Z]+([-_]?[a-zA-Z0-9]+)*(\\[[0-9]+\\])*)+)\\}" - } - ] - }, - "resources.DatabaseProjectPermission": { - "oneOf": [ - { - "type": "array", - "items": { - "$ref": "#/$defs/github.com/databricks/cli/bundle/config/resources.DatabaseProjectPermission" + "$ref": "#/$defs/github.com/databricks/cli/bundle/config/resources.IamPermission" } }, { @@ -11172,28 +11002,6 @@ } ] }, - "resources.Permission[github.com": { - "databricks": { - "databricks-sdk-go": { - "service": { - "iam.PermissionLevel]": { - "oneOf": [ - { - "type": "array", - "items": { - "$ref": "#/$defs/github.com/databricks/cli/bundle/config/resources.Permission[github.com/databricks/databricks-sdk-go/service/iam.PermissionLevel]" - } - }, - { - "type": "string", - "pattern": "\\$\\{(var(\\.[a-zA-Z]+([-_]?[a-zA-Z0-9]+)*(\\[[0-9]+\\])*)+)\\}" - } - ] - } - } - } - } - }, "resources.PipelinePermission": { "oneOf": [ { @@ -11838,7 +11646,7 @@ }, "permissions": { "description": "Defines a permission for a specific entity.", - "$ref": "#/$defs/slice/github.com/databricks/cli/bundle/config/resources.Permission[github.com/databricks/databricks-sdk-go/service/iam.PermissionLevel]", + "$ref": "#/$defs/slice/github.com/databricks/cli/bundle/config/resources.IamPermission", "markdownDescription": "A Sequence that defines the permissions to apply to experiments, jobs, pipelines, and models defined in the bundle, where each item in the sequence is a permission for a specific entity.\n\nSee [permissions](https://docs.databricks.com/dev-tools/bundles/settings.html#permissions) and [link](https://docs.databricks.com/dev-tools/bundles/permissions.html)." }, "presets": { diff --git a/bundle/tests/bundle_permissions_test.go b/bundle/tests/bundle_permissions_test.go index d9625652b5..51e11af3a0 100644 --- a/bundle/tests/bundle_permissions_test.go +++ b/bundle/tests/bundle_permissions_test.go @@ -7,17 +7,16 @@ import ( "github.com/databricks/cli/bundle" "github.com/databricks/cli/bundle/config/resources" - "github.com/databricks/databricks-sdk-go/service/iam" "github.com/stretchr/testify/assert" "github.com/stretchr/testify/require" ) func TestBundlePermissions(t *testing.T) { b := load(t, "./bundle_permissions") - assert.Contains(t, b.Config.Permissions, resources.Permission[iam.PermissionLevel]{Level: "CAN_RUN", UserName: "test@company.com"}) - assert.NotContains(t, b.Config.Permissions, resources.Permission[iam.PermissionLevel]{Level: "CAN_MANAGE", GroupName: "devs"}) - assert.NotContains(t, b.Config.Permissions, resources.Permission[iam.PermissionLevel]{Level: "CAN_VIEW", ServicePrincipalName: "1234-abcd"}) - assert.NotContains(t, b.Config.Permissions, resources.Permission[iam.PermissionLevel]{Level: "CAN_RUN", UserName: "bot@company.com"}) + assert.Contains(t, b.Config.Permissions, resources.IamPermission{Level: "CAN_RUN", UserName: "test@company.com"}) + assert.NotContains(t, b.Config.Permissions, resources.IamPermission{Level: "CAN_MANAGE", GroupName: "devs"}) + assert.NotContains(t, b.Config.Permissions, resources.IamPermission{Level: "CAN_VIEW", ServicePrincipalName: "1234-abcd"}) + assert.NotContains(t, b.Config.Permissions, resources.IamPermission{Level: "CAN_RUN", UserName: "bot@company.com"}) diags := bundle.Apply(t.Context(), b, resourcemutator.ApplyBundlePermissions()) require.NoError(t, diags.Error()) @@ -37,10 +36,10 @@ func TestBundlePermissions(t *testing.T) { func TestBundlePermissionsDevTarget(t *testing.T) { b := loadTarget(t, "./bundle_permissions", "development") - assert.Contains(t, b.Config.Permissions, resources.Permission[iam.PermissionLevel]{Level: "CAN_RUN", UserName: "test@company.com"}) - assert.Contains(t, b.Config.Permissions, resources.Permission[iam.PermissionLevel]{Level: "CAN_MANAGE", GroupName: "devs"}) - assert.Contains(t, b.Config.Permissions, resources.Permission[iam.PermissionLevel]{Level: "CAN_VIEW", ServicePrincipalName: "1234-abcd"}) - assert.Contains(t, b.Config.Permissions, resources.Permission[iam.PermissionLevel]{Level: "CAN_RUN", UserName: "bot@company.com"}) + assert.Contains(t, b.Config.Permissions, resources.IamPermission{Level: "CAN_RUN", UserName: "test@company.com"}) + assert.Contains(t, b.Config.Permissions, resources.IamPermission{Level: "CAN_MANAGE", GroupName: "devs"}) + assert.Contains(t, b.Config.Permissions, resources.IamPermission{Level: "CAN_VIEW", ServicePrincipalName: "1234-abcd"}) + assert.Contains(t, b.Config.Permissions, resources.IamPermission{Level: "CAN_RUN", UserName: "bot@company.com"}) diags := bundle.Apply(t.Context(), b, resourcemutator.ApplyBundlePermissions()) require.NoError(t, diags.Error()) From b824f3c7f1e6671e16cbae6298cdc3979a99df4f Mon Sep 17 00:00:00 2001 From: Denis Bilenko Date: Tue, 10 Mar 2026 15:16:50 +0100 Subject: [PATCH 15/28] Fix exhaustruct lint: add ForceSendFields to AccessControlRequest Co-Authored-By: Claude Sonnet 4.6 --- bundle/direct/dresources/permissions.go | 1 + 1 file changed, 1 insertion(+) diff --git a/bundle/direct/dresources/permissions.go b/bundle/direct/dresources/permissions.go index 0ffc1d62da..7c1db603e1 100644 --- a/bundle/direct/dresources/permissions.go +++ b/bundle/direct/dresources/permissions.go @@ -108,6 +108,7 @@ func toAccessControlRequests(ps any) ([]iam.AccessControlRequest, error) { UserName: elem.FieldByName("UserName").String(), ServicePrincipalName: elem.FieldByName("ServicePrincipalName").String(), GroupName: elem.FieldByName("GroupName").String(), + ForceSendFields: nil, } } return result, nil From 660aef2acbda5cd42fc9f3469bb0c346937cf3dd Mon Sep 17 00:00:00 2001 From: Denis Bilenko Date: Tue, 10 Mar 2026 15:25:29 +0100 Subject: [PATCH 16/28] Fix toAccessControlRequests to dereference pointer-to-slice Co-Authored-By: Claude Sonnet 4.6 --- bundle/direct/dresources/permissions.go | 3 +++ 1 file changed, 3 insertions(+) diff --git a/bundle/direct/dresources/permissions.go b/bundle/direct/dresources/permissions.go index 7c1db603e1..724c38eaef 100644 --- a/bundle/direct/dresources/permissions.go +++ b/bundle/direct/dresources/permissions.go @@ -97,6 +97,9 @@ func (*ResourcePermissions) PrepareState(s *PermissionsState) *PermissionsState // All permission types share the same underlying struct layout (Level, UserName, ServicePrincipalName, GroupName). func toAccessControlRequests(ps any) ([]iam.AccessControlRequest, error) { v := reflect.ValueOf(ps) + if v.Kind() == reflect.Pointer { + v = v.Elem() + } if v.Kind() != reflect.Slice { return nil, fmt.Errorf("expected permissions slice, got %T", ps) } From 419012de0122dd1856b8a58ec55911c7ca42224b Mon Sep 17 00:00:00 2001 From: Denis Bilenko Date: Tue, 10 Mar 2026 15:32:24 +0100 Subject: [PATCH 17/28] update refschema --- acceptance/bundle/refschema/out.fields.txt | 40 +++++++++++----------- 1 file changed, 20 insertions(+), 20 deletions(-) diff --git a/acceptance/bundle/refschema/out.fields.txt b/acceptance/bundle/refschema/out.fields.txt index eac815bff4..47c8164d37 100644 --- a/acceptance/bundle/refschema/out.fields.txt +++ b/acceptance/bundle/refschema/out.fields.txt @@ -38,10 +38,10 @@ resources.alerts.*.lifecycle_state sql.AlertLifecycleState ALL resources.alerts.*.modified_status string INPUT resources.alerts.*.owner_user_name string ALL resources.alerts.*.parent_path string ALL -resources.alerts.*.permissions []resources.AlertPermission INPUT -resources.alerts.*.permissions[*] resources.AlertPermission INPUT +resources.alerts.*.permissions []resources.IamPermission INPUT +resources.alerts.*.permissions[*] resources.IamPermission INPUT resources.alerts.*.permissions[*].group_name string INPUT -resources.alerts.*.permissions[*].level resources.AlertPermissionLevel INPUT +resources.alerts.*.permissions[*].level iam.PermissionLevel INPUT resources.alerts.*.permissions[*].service_principal_name string INPUT resources.alerts.*.permissions[*].user_name string INPUT resources.alerts.*.query_text string ALL @@ -166,7 +166,7 @@ resources.apps.*.pending_deployment.update_time string ALL resources.apps.*.permissions []resources.AppPermission INPUT resources.apps.*.permissions[*] resources.AppPermission INPUT resources.apps.*.permissions[*].group_name string INPUT -resources.apps.*.permissions[*].level resources.AppPermissionLevel INPUT +resources.apps.*.permissions[*].level apps.AppPermissionLevel INPUT resources.apps.*.permissions[*].service_principal_name string INPUT resources.apps.*.permissions[*].user_name string INPUT resources.apps.*.resources []apps.AppResource ALL @@ -397,7 +397,7 @@ resources.clusters.*.num_workers int ALL resources.clusters.*.permissions []resources.ClusterPermission INPUT resources.clusters.*.permissions[*] resources.ClusterPermission INPUT resources.clusters.*.permissions[*].group_name string INPUT -resources.clusters.*.permissions[*].level resources.ClusterPermissionLevel INPUT +resources.clusters.*.permissions[*].level compute.ClusterPermissionLevel INPUT resources.clusters.*.permissions[*].service_principal_name string INPUT resources.clusters.*.permissions[*].user_name string INPUT resources.clusters.*.policy_id string ALL @@ -561,10 +561,10 @@ resources.dashboards.*.lifecycle_state dashboards.LifecycleState ALL resources.dashboards.*.modified_status string INPUT resources.dashboards.*.parent_path string ALL resources.dashboards.*.path string ALL -resources.dashboards.*.permissions []resources.DashboardPermission INPUT -resources.dashboards.*.permissions[*] resources.DashboardPermission INPUT +resources.dashboards.*.permissions []resources.IamPermission INPUT +resources.dashboards.*.permissions[*] resources.IamPermission INPUT resources.dashboards.*.permissions[*].group_name string INPUT -resources.dashboards.*.permissions[*].level resources.DashboardPermissionLevel INPUT +resources.dashboards.*.permissions[*].level iam.PermissionLevel INPUT resources.dashboards.*.permissions[*].service_principal_name string INPUT resources.dashboards.*.permissions[*].user_name string INPUT resources.dashboards.*.published bool REMOTE STATE @@ -628,10 +628,10 @@ resources.database_instances.*.parent_instance_ref.effective_lsn string ALL resources.database_instances.*.parent_instance_ref.lsn string ALL resources.database_instances.*.parent_instance_ref.name string ALL resources.database_instances.*.parent_instance_ref.uid string ALL -resources.database_instances.*.permissions []resources.DatabaseInstancePermission INPUT -resources.database_instances.*.permissions[*] resources.DatabaseInstancePermission INPUT +resources.database_instances.*.permissions []resources.IamPermission INPUT +resources.database_instances.*.permissions[*] resources.IamPermission INPUT resources.database_instances.*.permissions[*].group_name string INPUT -resources.database_instances.*.permissions[*].level resources.DatabaseInstancePermissionLevel INPUT +resources.database_instances.*.permissions[*].level iam.PermissionLevel INPUT resources.database_instances.*.permissions[*].service_principal_name string INPUT resources.database_instances.*.permissions[*].user_name string INPUT resources.database_instances.*.pg_version string ALL @@ -663,7 +663,7 @@ resources.experiments.*.name string ALL resources.experiments.*.permissions []resources.MlflowExperimentPermission INPUT resources.experiments.*.permissions[*] resources.MlflowExperimentPermission INPUT resources.experiments.*.permissions[*].group_name string INPUT -resources.experiments.*.permissions[*].level resources.MlflowExperimentPermissionLevel INPUT +resources.experiments.*.permissions[*].level ml.ExperimentPermissionLevel INPUT resources.experiments.*.permissions[*].service_principal_name string INPUT resources.experiments.*.permissions[*].user_name string INPUT resources.experiments.*.tags []ml.ExperimentTag ALL @@ -926,7 +926,7 @@ resources.jobs.*.performance_target jobs.PerformanceTarget ALL resources.jobs.*.permissions []resources.JobPermission INPUT resources.jobs.*.permissions[*] resources.JobPermission INPUT resources.jobs.*.permissions[*].group_name string INPUT -resources.jobs.*.permissions[*].level resources.JobPermissionLevel INPUT +resources.jobs.*.permissions[*].level jobs.JobPermissionLevel INPUT resources.jobs.*.permissions[*].service_principal_name string INPUT resources.jobs.*.permissions[*].user_name string INPUT resources.jobs.*.queue *jobs.QueueSettings ALL @@ -2103,7 +2103,7 @@ resources.model_serving_endpoints.*.name string INPUT STATE resources.model_serving_endpoints.*.permissions []resources.ModelServingEndpointPermission INPUT resources.model_serving_endpoints.*.permissions[*] resources.ModelServingEndpointPermission INPUT resources.model_serving_endpoints.*.permissions[*].group_name string INPUT -resources.model_serving_endpoints.*.permissions[*].level resources.ModelServingEndpointPermissionLevel INPUT +resources.model_serving_endpoints.*.permissions[*].level serving.ServingEndpointPermissionLevel INPUT resources.model_serving_endpoints.*.permissions[*].service_principal_name string INPUT resources.model_serving_endpoints.*.permissions[*].user_name string INPUT resources.model_serving_endpoints.*.rate_limits []serving.RateLimit INPUT STATE @@ -2154,7 +2154,7 @@ resources.models.*.permission_level ml.PermissionLevel REMOTE resources.models.*.permissions []resources.MlflowModelPermission INPUT resources.models.*.permissions[*] resources.MlflowModelPermission INPUT resources.models.*.permissions[*].group_name string INPUT -resources.models.*.permissions[*].level resources.MlflowModelPermissionLevel INPUT +resources.models.*.permissions[*].level ml.RegisteredModelPermissionLevel INPUT resources.models.*.permissions[*].service_principal_name string INPUT resources.models.*.permissions[*].user_name string INPUT resources.models.*.tags []ml.ModelTag ALL @@ -2479,7 +2479,7 @@ resources.pipelines.*.notifications[*].email_recipients[*] string ALL resources.pipelines.*.permissions []resources.PipelinePermission INPUT resources.pipelines.*.permissions[*] resources.PipelinePermission INPUT resources.pipelines.*.permissions[*].group_name string INPUT -resources.pipelines.*.permissions[*].level resources.PipelinePermissionLevel INPUT +resources.pipelines.*.permissions[*].level pipelines.PipelinePermissionLevel INPUT resources.pipelines.*.permissions[*].service_principal_name string INPUT resources.pipelines.*.permissions[*].user_name string INPUT resources.pipelines.*.photon bool ALL @@ -2634,10 +2634,10 @@ resources.postgres_projects.*.lifecycle resources.Lifecycle INPUT resources.postgres_projects.*.lifecycle.prevent_destroy bool INPUT resources.postgres_projects.*.modified_status string INPUT resources.postgres_projects.*.name string REMOTE -resources.postgres_projects.*.permissions []resources.DatabaseProjectPermission INPUT -resources.postgres_projects.*.permissions[*] resources.DatabaseProjectPermission INPUT +resources.postgres_projects.*.permissions []resources.IamPermission INPUT +resources.postgres_projects.*.permissions[*] resources.IamPermission INPUT resources.postgres_projects.*.permissions[*].group_name string INPUT -resources.postgres_projects.*.permissions[*].level resources.DatabaseProjectPermissionLevel INPUT +resources.postgres_projects.*.permissions[*].level iam.PermissionLevel INPUT resources.postgres_projects.*.permissions[*].service_principal_name string INPUT resources.postgres_projects.*.permissions[*].user_name string INPUT resources.postgres_projects.*.pg_version int INPUT STATE @@ -2884,7 +2884,7 @@ resources.sql_warehouses.*.odbc_params.protocol string REMOTE resources.sql_warehouses.*.permissions []resources.SqlWarehousePermission INPUT resources.sql_warehouses.*.permissions[*] resources.SqlWarehousePermission INPUT resources.sql_warehouses.*.permissions[*].group_name string INPUT -resources.sql_warehouses.*.permissions[*].level resources.SqlWarehousePermissionLevel INPUT +resources.sql_warehouses.*.permissions[*].level sql.WarehousePermissionLevel INPUT resources.sql_warehouses.*.permissions[*].service_principal_name string INPUT resources.sql_warehouses.*.permissions[*].user_name string INPUT resources.sql_warehouses.*.spot_instance_policy sql.SpotInstancePolicy ALL From c622af0337312e596c73d6f746ad3cda00365a69 Mon Sep 17 00:00:00 2001 From: Denis Bilenko Date: Tue, 10 Mar 2026 16:19:49 +0100 Subject: [PATCH 18/28] Restore formatting and revert unrelated ssh changes Co-Authored-By: Claude Sonnet 4.6 --- .../validate_target_mode_test.go | 32 +++++++++++++++---- .../terraform/tfdyn/convert_app_test.go | 18 +++++------ 2 files changed, 34 insertions(+), 16 deletions(-) diff --git a/bundle/config/mutator/resourcemutator/validate_target_mode_test.go b/bundle/config/mutator/resourcemutator/validate_target_mode_test.go index c387889c2c..bf82773d9e 100644 --- a/bundle/config/mutator/resourcemutator/validate_target_mode_test.go +++ b/bundle/config/mutator/resourcemutator/validate_target_mode_test.go @@ -5,12 +5,12 @@ import ( "github.com/databricks/cli/libs/diag" "github.com/databricks/databricks-sdk-go/service/jobs" - "github.com/databricks/databricks-sdk-go/service/pipelines" "github.com/stretchr/testify/assert" "github.com/databricks/cli/bundle" "github.com/databricks/cli/bundle/config" "github.com/databricks/cli/bundle/config/resources" + "github.com/databricks/databricks-sdk-go/service/pipelines" "github.com/stretchr/testify/require" ) @@ -49,22 +49,40 @@ func TestProcessTargetModeProduction(t *testing.T) { require.ErrorContains(t, diags.Error(), "A common practice is to use a username or principal name in this path, i.e. use\n\n root_path: /Workspace/Users/lennart@company.com/.bundle/${bundle.name}/${bundle.target}") jobPermissions := []resources.JobPermission{ - {Level: "CAN_MANAGE", UserName: "user@company.com"}, + { + Level: "CAN_MANAGE", + UserName: "user@company.com", + }, } pipelinePermissions := []resources.PipelinePermission{ - {Level: "CAN_MANAGE", UserName: "user@company.com"}, + { + Level: "CAN_MANAGE", + UserName: "user@company.com", + }, } experimentPermissions := []resources.MlflowExperimentPermission{ - {Level: "CAN_MANAGE", UserName: "user@company.com"}, + { + Level: "CAN_MANAGE", + UserName: "user@company.com", + }, } modelPermissions := []resources.MlflowModelPermission{ - {Level: "CAN_MANAGE", UserName: "user@company.com"}, + { + Level: "CAN_MANAGE", + UserName: "user@company.com", + }, } endpointPermissions := []resources.ModelServingEndpointPermission{ - {Level: "CAN_MANAGE", UserName: "user@company.com"}, + { + Level: "CAN_MANAGE", + UserName: "user@company.com", + }, } clusterPermissions := []resources.ClusterPermission{ - {Level: "CAN_MANAGE", UserName: "user@company.com"}, + { + Level: "CAN_MANAGE", + UserName: "user@company.com", + }, } b.Config.Resources.Jobs["job1"].Permissions = jobPermissions b.Config.Resources.Jobs["job1"].RunAs = &jobs.JobRunAs{UserName: "user@company.com"} diff --git a/bundle/deploy/terraform/tfdyn/convert_app_test.go b/bundle/deploy/terraform/tfdyn/convert_app_test.go index 48481a0936..896d0e52d5 100644 --- a/bundle/deploy/terraform/tfdyn/convert_app_test.go +++ b/bundle/deploy/terraform/tfdyn/convert_app_test.go @@ -7,7 +7,7 @@ import ( "github.com/databricks/cli/bundle/internal/tf/schema" "github.com/databricks/cli/libs/dyn" "github.com/databricks/cli/libs/dyn/convert" - appssdk "github.com/databricks/databricks-sdk-go/service/apps" + "github.com/databricks/databricks-sdk-go/service/apps" "github.com/stretchr/testify/assert" "github.com/stretchr/testify/require" ) @@ -15,20 +15,20 @@ import ( func TestConvertApp(t *testing.T) { src := resources.App{ SourceCodePath: "./app", - App: appssdk.App{ + App: apps.App{ Name: "app_id", Description: "app description", - Resources: []appssdk.AppResource{ + Resources: []apps.AppResource{ { Name: "job1", - Job: &appssdk.AppResourceJob{ + Job: &apps.AppResourceJob{ Id: "1234", Permission: "CAN_MANAGE_RUN", }, }, { Name: "sql1", - SqlWarehouse: &appssdk.AppResourceSqlWarehouse{ + SqlWarehouse: &apps.AppResourceSqlWarehouse{ Id: "5678", Permission: "CAN_USE", }, @@ -97,19 +97,19 @@ func TestConvertApp(t *testing.T) { func TestConvertAppWithNoDescription(t *testing.T) { src := resources.App{ SourceCodePath: "./app", - App: appssdk.App{ + App: apps.App{ Name: "app_id", - Resources: []appssdk.AppResource{ + Resources: []apps.AppResource{ { Name: "job1", - Job: &appssdk.AppResourceJob{ + Job: &apps.AppResourceJob{ Id: "1234", Permission: "CAN_MANAGE_RUN", }, }, { Name: "sql1", - SqlWarehouse: &appssdk.AppResourceSqlWarehouse{ + SqlWarehouse: &apps.AppResourceSqlWarehouse{ Id: "5678", Permission: "CAN_USE", }, From 79441e24d9a47f275923f78393ecaed82e34ca5b Mon Sep 17 00:00:00 2001 From: Denis Bilenko Date: Tue, 10 Mar 2026 16:32:23 +0100 Subject: [PATCH 19/28] Remove unused ToAccessControlRequest methods and wrapper boilerplate Co-Authored-By: Claude Sonnet 4.6 --- bundle/config/resources/permission.go | 12 ------ bundle/config/resources/permission_types.go | 43 --------------------- 2 files changed, 55 deletions(-) diff --git a/bundle/config/resources/permission.go b/bundle/config/resources/permission.go index 582f1c6b1b..33912b7088 100644 --- a/bundle/config/resources/permission.go +++ b/bundle/config/resources/permission.go @@ -3,8 +3,6 @@ package resources import ( "fmt" "strings" - - "github.com/databricks/databricks-sdk-go/service/iam" ) // Permission holds the permission level setting for a single principal. @@ -16,16 +14,6 @@ type Permission[L ~string] struct { GroupName string `json:"group_name,omitempty"` } -// ToAccessControlRequest converts to the SDK type used by the permissions API. -func (p Permission[L]) ToAccessControlRequest() iam.AccessControlRequest { - return iam.AccessControlRequest{ - PermissionLevel: iam.PermissionLevel(p.Level), - UserName: p.UserName, - ServicePrincipalName: p.ServicePrincipalName, - GroupName: p.GroupName, - } -} - func (p Permission[L]) String() string { if p.UserName != "" { return fmt.Sprintf("level: %s, user_name: %s", p.Level, p.UserName) diff --git a/bundle/config/resources/permission_types.go b/bundle/config/resources/permission_types.go index 56608d7d00..a952e45f35 100644 --- a/bundle/config/resources/permission_types.go +++ b/bundle/config/resources/permission_types.go @@ -18,58 +18,15 @@ import ( // IamPermission is used for resources that use the generic iam.PermissionLevel (Alert, Dashboard, DatabaseInstance, PostgresProject). type IamPermission Permission[iam.PermissionLevel] -func (p IamPermission) ToAccessControlRequest() iam.AccessControlRequest { - return Permission[iam.PermissionLevel](p).ToAccessControlRequest() -} - func (p IamPermission) String() string { return Permission[iam.PermissionLevel](p).String() } type AppPermission Permission[apps.AppPermissionLevel] - -func (p AppPermission) ToAccessControlRequest() iam.AccessControlRequest { - return Permission[apps.AppPermissionLevel](p).ToAccessControlRequest() -} - type ClusterPermission Permission[compute.ClusterPermissionLevel] - -func (p ClusterPermission) ToAccessControlRequest() iam.AccessControlRequest { - return Permission[compute.ClusterPermissionLevel](p).ToAccessControlRequest() -} - type JobPermission Permission[jobs.JobPermissionLevel] - -func (p JobPermission) ToAccessControlRequest() iam.AccessControlRequest { - return Permission[jobs.JobPermissionLevel](p).ToAccessControlRequest() -} - type MlflowExperimentPermission Permission[ml.ExperimentPermissionLevel] - -func (p MlflowExperimentPermission) ToAccessControlRequest() iam.AccessControlRequest { - return Permission[ml.ExperimentPermissionLevel](p).ToAccessControlRequest() -} - type MlflowModelPermission Permission[ml.RegisteredModelPermissionLevel] - -func (p MlflowModelPermission) ToAccessControlRequest() iam.AccessControlRequest { - return Permission[ml.RegisteredModelPermissionLevel](p).ToAccessControlRequest() -} - type ModelServingEndpointPermission Permission[serving.ServingEndpointPermissionLevel] - -func (p ModelServingEndpointPermission) ToAccessControlRequest() iam.AccessControlRequest { - return Permission[serving.ServingEndpointPermissionLevel](p).ToAccessControlRequest() -} - type PipelinePermission Permission[pipelines.PipelinePermissionLevel] - -func (p PipelinePermission) ToAccessControlRequest() iam.AccessControlRequest { - return Permission[pipelines.PipelinePermissionLevel](p).ToAccessControlRequest() -} - type SqlWarehousePermission Permission[sql.WarehousePermissionLevel] - -func (p SqlWarehousePermission) ToAccessControlRequest() iam.AccessControlRequest { - return Permission[sql.WarehousePermissionLevel](p).ToAccessControlRequest() -} From 77312127481e0880dcac400e875a583a10c1dda0 Mon Sep 17 00:00:00 2001 From: Denis Bilenko Date: Tue, 10 Mar 2026 16:43:56 +0100 Subject: [PATCH 20/28] Rename Permission -> PermissionT, IamPermission -> Permission to minimize diff Co-Authored-By: Claude Sonnet 4.6 --- .../apply_bundle_permissions.go | 20 +++---- .../apply_bundle_permissions_test.go | 8 +-- bundle/config/resources/alerts.go | 2 +- bundle/config/resources/dashboard.go | 2 +- bundle/config/resources/database_instance.go | 2 +- bundle/config/resources/permission.go | 8 +-- bundle/config/resources/permission_types.go | 28 +++++----- bundle/config/resources/postgres_project.go | 2 +- bundle/config/root.go | 2 +- bundle/config/target.go | 2 +- .../validate/folder_permissions_test.go | 8 +-- .../terraform/tfdyn/convert_alert_test.go | 2 +- .../terraform/tfdyn/convert_dashboard_test.go | 2 +- .../tfdyn/convert_database_instance_test.go | 2 +- .../tfdyn/convert_postgres_project_test.go | 2 +- bundle/internal/schema/annotations.yml | 2 +- .../permission_diagnostics_test.go | 6 +-- bundle/permissions/permission_report_test.go | 8 +-- bundle/permissions/terraform_errors_test.go | 8 +-- bundle/permissions/validate_test.go | 4 +- .../permissions/workspace_path_permissions.go | 16 +++--- .../workspace_path_permissions_test.go | 20 +++---- bundle/permissions/workspace_root_test.go | 4 +- bundle/tests/bundle_permissions_test.go | 16 +++--- cmd/root/.azure/az.json | 1 + cmd/root/.azure/az.sess | 1 + cmd/root/.azure/azureProfile.json | 1 + cmd/root/.azure/commandIndex.json | 1 + cmd/root/.azure/config | 3 ++ cmd/root/.azure/versionCheck.json | 1 + .../Microsoft/DeveloperTools/deviceid | 1 + pr.txt | 53 +++++++++++++++++++ python/codegen/codegen/jsonschema.py | 31 +---------- python/codegen/codegen/packages.py | 51 +----------------- 34 files changed, 155 insertions(+), 165 deletions(-) create mode 100644 cmd/root/.azure/az.json create mode 100644 cmd/root/.azure/az.sess create mode 100644 cmd/root/.azure/azureProfile.json create mode 100644 cmd/root/.azure/commandIndex.json create mode 100644 cmd/root/.azure/config create mode 100644 cmd/root/.azure/versionCheck.json create mode 100644 cmd/root/Library/Application Support/Microsoft/DeveloperTools/deviceid create mode 100644 pr.txt diff --git a/bundle/config/mutator/resourcemutator/apply_bundle_permissions.go b/bundle/config/mutator/resourcemutator/apply_bundle_permissions.go index 3713cfff9b..73ce556868 100644 --- a/bundle/config/mutator/resourcemutator/apply_bundle_permissions.go +++ b/bundle/config/mutator/resourcemutator/apply_bundle_permissions.go @@ -105,7 +105,7 @@ func (m *bundlePermissions) Apply(ctx context.Context, b *bundle.Bundle) diag.Di err = b.Config.Mutate(func(v dyn.Value) (dyn.Value, error) { for key, pattern := range patterns { v, err = dyn.MapByPattern(v, pattern, func(p dyn.Path, v dyn.Value) (dyn.Value, error) { - var permissions []resources.IamPermission + var permissions []resources.Permission pv, err := dyn.Get(v, "permissions") // If the permissions field is not found, we set to an empty array if err != nil { @@ -166,12 +166,12 @@ func (m *bundlePermissions) Name() string { func convertPermissions( ctx context.Context, - bundlePermissions []resources.IamPermission, - resourcePermissions []resources.IamPermission, + bundlePermissions []resources.Permission, + resourcePermissions []resources.Permission, resourceName string, lm map[string]string, -) []resources.IamPermission { - var permissions []resources.IamPermission +) []resources.Permission { + var permissions []resources.Permission for _, p := range bundlePermissions { level, ok := lm[string(p.Level)] // If there is no bundle permission level defined in the map, it means @@ -184,7 +184,7 @@ func convertPermissions( continue } - permissions = append(permissions, resources.IamPermission{ + permissions = append(permissions, resources.Permission{ Level: iam.PermissionLevel(level), UserName: p.UserName, GroupName: p.GroupName, @@ -196,8 +196,8 @@ func convertPermissions( } func isPermissionOverlap( - permission resources.IamPermission, - resourcePermissions []resources.IamPermission, + permission resources.Permission, + resourcePermissions []resources.Permission, resourceName string, ) (bool, diag.Diagnostics) { var diagnostics diag.Diagnostics @@ -226,8 +226,8 @@ func isPermissionOverlap( func notifyForPermissionOverlap( ctx context.Context, - permission resources.IamPermission, - resourcePermissions []resources.IamPermission, + permission resources.Permission, + resourcePermissions []resources.Permission, resourceName string, ) bool { isOverlap, _ := isPermissionOverlap(permission, resourcePermissions, resourceName) diff --git a/bundle/config/mutator/resourcemutator/apply_bundle_permissions_test.go b/bundle/config/mutator/resourcemutator/apply_bundle_permissions_test.go index 5929a49ca6..ba12113034 100644 --- a/bundle/config/mutator/resourcemutator/apply_bundle_permissions_test.go +++ b/bundle/config/mutator/resourcemutator/apply_bundle_permissions_test.go @@ -36,7 +36,7 @@ func TestApplyBundlePermissions(t *testing.T) { Workspace: config.Workspace{ RootPath: "/Users/foo@bar.com", }, - Permissions: []resources.IamPermission{ + Permissions: []resources.Permission{ {Level: permissions.CAN_MANAGE, UserName: "TestUser"}, {Level: permissions.CAN_VIEW, GroupName: "TestGroup"}, {Level: permissions.CAN_RUN, ServicePrincipalName: "TestServicePrincipal"}, @@ -132,8 +132,8 @@ func TestApplyBundlePermissions(t *testing.T) { require.Contains(t, b.Config.Resources.ModelServingEndpoints["endpoint_2"].Permissions, resources.ModelServingEndpointPermission{Level: "CAN_QUERY", ServicePrincipalName: "TestServicePrincipal"}) require.Len(t, b.Config.Resources.Dashboards["dashboard_1"].Permissions, 2) - require.Contains(t, b.Config.Resources.Dashboards["dashboard_1"].Permissions, resources.IamPermission{Level: "CAN_MANAGE", UserName: "TestUser"}) - require.Contains(t, b.Config.Resources.Dashboards["dashboard_1"].Permissions, resources.IamPermission{Level: "CAN_READ", GroupName: "TestGroup"}) + require.Contains(t, b.Config.Resources.Dashboards["dashboard_1"].Permissions, resources.Permission{Level: "CAN_MANAGE", UserName: "TestUser"}) + require.Contains(t, b.Config.Resources.Dashboards["dashboard_1"].Permissions, resources.Permission{Level: "CAN_READ", GroupName: "TestGroup"}) require.Len(t, b.Config.Resources.Apps["app_1"].Permissions, 2) require.Contains(t, b.Config.Resources.Apps["app_1"].Permissions, resources.AppPermission{Level: "CAN_MANAGE", UserName: "TestUser"}) @@ -146,7 +146,7 @@ func TestWarningOnOverlapPermission(t *testing.T) { Workspace: config.Workspace{ RootPath: "/Users/foo@bar.com", }, - Permissions: []resources.IamPermission{ + Permissions: []resources.Permission{ {Level: permissions.CAN_MANAGE, UserName: "TestUser"}, {Level: permissions.CAN_VIEW, GroupName: "TestGroup"}, }, diff --git a/bundle/config/resources/alerts.go b/bundle/config/resources/alerts.go index 8dd8f4e690..bfdd64e900 100644 --- a/bundle/config/resources/alerts.go +++ b/bundle/config/resources/alerts.go @@ -14,7 +14,7 @@ type Alert struct { BaseResource sql.AlertV2 //nolint AlertV2 also defines Id and URL field with the same json tag "id" and "url" - Permissions []IamPermission `json:"permissions,omitempty"` + Permissions []Permission `json:"permissions,omitempty"` // Filepath points to the local .dbalert.json file containing the alert definition. // If specified, any fields that are part of the .dbalert.json file schema will not be allowed in diff --git a/bundle/config/resources/dashboard.go b/bundle/config/resources/dashboard.go index 35fd3a650b..c108ac8abe 100644 --- a/bundle/config/resources/dashboard.go +++ b/bundle/config/resources/dashboard.go @@ -80,7 +80,7 @@ type Dashboard struct { BaseResource DashboardConfig - Permissions []IamPermission `json:"permissions,omitempty"` + Permissions []Permission `json:"permissions,omitempty"` // FilePath points to the local `.lvdash.json` file containing the dashboard definition. // This is inlined into serialized_dashboard during deployment. The file_path is kept around diff --git a/bundle/config/resources/database_instance.go b/bundle/config/resources/database_instance.go index 2af4cb52c1..2ba19b84f4 100644 --- a/bundle/config/resources/database_instance.go +++ b/bundle/config/resources/database_instance.go @@ -14,7 +14,7 @@ type DatabaseInstance struct { BaseResource database.DatabaseInstance - Permissions []IamPermission `json:"permissions,omitempty"` + Permissions []Permission `json:"permissions,omitempty"` } func (d *DatabaseInstance) Exists(ctx context.Context, w *databricks.WorkspaceClient, name string) (bool, error) { diff --git a/bundle/config/resources/permission.go b/bundle/config/resources/permission.go index 33912b7088..11863cbabd 100644 --- a/bundle/config/resources/permission.go +++ b/bundle/config/resources/permission.go @@ -5,8 +5,10 @@ import ( "strings" ) -// Permission holds the permission level setting for a single principal. -type Permission[L ~string] struct { +// PermissionT holds the permission level setting for a single principal. +// It is a generic type parameterized by the permission level type L. +// Use the named types (e.g. JobPermission, Permission) instead of this type directly. +type PermissionT[L ~string] struct { Level L `json:"level"` UserName string `json:"user_name,omitempty"` @@ -14,7 +16,7 @@ type Permission[L ~string] struct { GroupName string `json:"group_name,omitempty"` } -func (p Permission[L]) String() string { +func (p PermissionT[L]) String() string { if p.UserName != "" { return fmt.Sprintf("level: %s, user_name: %s", p.Level, p.UserName) } diff --git a/bundle/config/resources/permission_types.go b/bundle/config/resources/permission_types.go index a952e45f35..a40b5c8eec 100644 --- a/bundle/config/resources/permission_types.go +++ b/bundle/config/resources/permission_types.go @@ -13,20 +13,22 @@ import ( // Each resource defines its own permission type so that the JSON schema names them distinctly. // Using non-alias type definitions (not =) makes them appear as named types in the schema. -// The underlying struct is identical to Permission[L], enabling conversion to use generic methods. +// The underlying struct is identical to PermissionT[L], enabling conversion to use generic methods. -// IamPermission is used for resources that use the generic iam.PermissionLevel (Alert, Dashboard, DatabaseInstance, PostgresProject). -type IamPermission Permission[iam.PermissionLevel] +// Permission is used for resources that use the generic iam.PermissionLevel (Alert, Dashboard, DatabaseInstance, PostgresProject). +type Permission PermissionT[iam.PermissionLevel] -func (p IamPermission) String() string { - return Permission[iam.PermissionLevel](p).String() +func (p Permission) String() string { + return PermissionT[iam.PermissionLevel](p).String() } -type AppPermission Permission[apps.AppPermissionLevel] -type ClusterPermission Permission[compute.ClusterPermissionLevel] -type JobPermission Permission[jobs.JobPermissionLevel] -type MlflowExperimentPermission Permission[ml.ExperimentPermissionLevel] -type MlflowModelPermission Permission[ml.RegisteredModelPermissionLevel] -type ModelServingEndpointPermission Permission[serving.ServingEndpointPermissionLevel] -type PipelinePermission Permission[pipelines.PipelinePermissionLevel] -type SqlWarehousePermission Permission[sql.WarehousePermissionLevel] +type ( + AppPermission PermissionT[apps.AppPermissionLevel] + ClusterPermission PermissionT[compute.ClusterPermissionLevel] + JobPermission PermissionT[jobs.JobPermissionLevel] + MlflowExperimentPermission PermissionT[ml.ExperimentPermissionLevel] + MlflowModelPermission PermissionT[ml.RegisteredModelPermissionLevel] + ModelServingEndpointPermission PermissionT[serving.ServingEndpointPermissionLevel] + PipelinePermission PermissionT[pipelines.PipelinePermissionLevel] + SqlWarehousePermission PermissionT[sql.WarehousePermissionLevel] +) diff --git a/bundle/config/resources/postgres_project.go b/bundle/config/resources/postgres_project.go index be67280f70..d76a6ee135 100644 --- a/bundle/config/resources/postgres_project.go +++ b/bundle/config/resources/postgres_project.go @@ -30,7 +30,7 @@ type PostgresProject struct { BaseResource PostgresProjectConfig - Permissions []IamPermission `json:"permissions,omitempty"` + Permissions []Permission `json:"permissions,omitempty"` } func (p *PostgresProject) Exists(ctx context.Context, w *databricks.WorkspaceClient, name string) (bool, error) { diff --git a/bundle/config/root.go b/bundle/config/root.go index 25b3d9ac46..861791fc34 100644 --- a/bundle/config/root.go +++ b/bundle/config/root.go @@ -75,7 +75,7 @@ type Root struct { // Permissions section allows to define permissions which will be // applied to all resources defined in bundle - Permissions []resources.IamPermission `json:"permissions,omitempty"` + Permissions []resources.Permission `json:"permissions,omitempty"` // Locations is an output-only field that holds configuration location // information for every path in the configuration tree. diff --git a/bundle/config/target.go b/bundle/config/target.go index 3d8842a724..fae9c940b3 100644 --- a/bundle/config/target.go +++ b/bundle/config/target.go @@ -68,7 +68,7 @@ type Target struct { Sync *Sync `json:"sync,omitempty"` - Permissions []resources.IamPermission `json:"permissions,omitempty"` + Permissions []resources.Permission `json:"permissions,omitempty"` } const ( diff --git a/bundle/config/validate/folder_permissions_test.go b/bundle/config/validate/folder_permissions_test.go index a6d77b4973..394ffee4e2 100644 --- a/bundle/config/validate/folder_permissions_test.go +++ b/bundle/config/validate/folder_permissions_test.go @@ -25,7 +25,7 @@ func TestFolderPermissionsInheritedWhenRootPathDoesNotExist(t *testing.T) { StatePath: "/Workspace/Users/foo@bar.com/state", ResourcePath: "/Workspace/Users/foo@bar.com/resources", }, - Permissions: []resources.IamPermission{ + Permissions: []resources.Permission{ {Level: permissions.CAN_MANAGE, UserName: "foo@bar.com"}, }, }, @@ -82,7 +82,7 @@ func TestValidateFolderPermissionsFailsOnMissingBundlePermission(t *testing.T) { StatePath: "/Workspace/Users/foo@bar.com/state", ResourcePath: "/Workspace/Users/foo@bar.com/resources", }, - Permissions: []resources.IamPermission{ + Permissions: []resources.Permission{ {Level: permissions.CAN_MANAGE, UserName: "foo@bar.com"}, }, }, @@ -136,7 +136,7 @@ func TestValidateFolderPermissionsFailsOnPermissionMismatch(t *testing.T) { StatePath: "/Workspace/Users/foo@bar.com/state", ResourcePath: "/Workspace/Users/foo@bar.com/resources", }, - Permissions: []resources.IamPermission{ + Permissions: []resources.Permission{ {Level: permissions.CAN_MANAGE, UserName: "foo@bar.com"}, }, }, @@ -179,7 +179,7 @@ func TestValidateFolderPermissionsFailsOnNoRootFolder(t *testing.T) { StatePath: "/NotExisting/state", ResourcePath: "/NotExisting/resources", }, - Permissions: []resources.IamPermission{ + Permissions: []resources.Permission{ {Level: permissions.CAN_MANAGE, UserName: "foo@bar.com"}, }, }, diff --git a/bundle/deploy/terraform/tfdyn/convert_alert_test.go b/bundle/deploy/terraform/tfdyn/convert_alert_test.go index 2b5f95f14b..25d5942519 100644 --- a/bundle/deploy/terraform/tfdyn/convert_alert_test.go +++ b/bundle/deploy/terraform/tfdyn/convert_alert_test.go @@ -21,7 +21,7 @@ func TestConvertAlert(t *testing.T) { CustomSummary: "Test alert summary", CustomDescription: "Test alert description", }, - Permissions: []resources.IamPermission{ + Permissions: []resources.Permission{ { Level: "CAN_VIEW", UserName: "jane@doe.com", diff --git a/bundle/deploy/terraform/tfdyn/convert_dashboard_test.go b/bundle/deploy/terraform/tfdyn/convert_dashboard_test.go index 4188cc9178..d8f05dc76c 100644 --- a/bundle/deploy/terraform/tfdyn/convert_dashboard_test.go +++ b/bundle/deploy/terraform/tfdyn/convert_dashboard_test.go @@ -20,7 +20,7 @@ func TestConvertDashboard(t *testing.T) { EmbedCredentials: true, }, - Permissions: []resources.IamPermission{ + Permissions: []resources.Permission{ { Level: "CAN_VIEW", UserName: "jane@doe.com", diff --git a/bundle/deploy/terraform/tfdyn/convert_database_instance_test.go b/bundle/deploy/terraform/tfdyn/convert_database_instance_test.go index 0edac98bd0..533c69abed 100644 --- a/bundle/deploy/terraform/tfdyn/convert_database_instance_test.go +++ b/bundle/deploy/terraform/tfdyn/convert_database_instance_test.go @@ -73,7 +73,7 @@ func TestConvertDatabaseInstanceWithPermissions(t *testing.T) { Name: "db-instance-with-permissions", Capacity: "CU_2", }, - Permissions: []resources.IamPermission{ + Permissions: []resources.Permission{ { Level: "CAN_USE", UserName: "user@example.com", diff --git a/bundle/deploy/terraform/tfdyn/convert_postgres_project_test.go b/bundle/deploy/terraform/tfdyn/convert_postgres_project_test.go index 6e8492b935..696a6e76c2 100644 --- a/bundle/deploy/terraform/tfdyn/convert_postgres_project_test.go +++ b/bundle/deploy/terraform/tfdyn/convert_postgres_project_test.go @@ -64,7 +64,7 @@ func TestConvertPostgresProjectWithPermissions(t *testing.T) { PgVersion: 17, }, }, - Permissions: []resources.IamPermission{ + Permissions: []resources.Permission{ { Level: "CAN_USE", UserName: "user@example.com", diff --git a/bundle/internal/schema/annotations.yml b/bundle/internal/schema/annotations.yml index adb6c14186..9808ce7a39 100644 --- a/bundle/internal/schema/annotations.yml +++ b/bundle/internal/schema/annotations.yml @@ -631,7 +631,7 @@ github.com/databricks/cli/bundle/config/resources.ExternalLocation: "url": "description": |- PLACEHOLDER -github.com/databricks/cli/bundle/config/resources.IamPermission: +github.com/databricks/cli/bundle/config/resources.Permission: "group_name": "description": |- PLACEHOLDER diff --git a/bundle/permissions/permission_diagnostics_test.go b/bundle/permissions/permission_diagnostics_test.go index ede17bb62d..e5a9214f43 100644 --- a/bundle/permissions/permission_diagnostics_test.go +++ b/bundle/permissions/permission_diagnostics_test.go @@ -13,7 +13,7 @@ import ( ) func TestPermissionDiagnosticsApplySuccess(t *testing.T) { - b := mockBundle([]resources.IamPermission{ + b := mockBundle([]resources.Permission{ {Level: "CAN_MANAGE", UserName: "testuser@databricks.com"}, }) @@ -29,7 +29,7 @@ func TestPermissionDiagnosticsEmpty(t *testing.T) { } func TestPermissionDiagnosticsApplyFail(t *testing.T) { - b := mockBundle([]resources.IamPermission{ + b := mockBundle([]resources.Permission{ {Level: "CAN_VIEW", UserName: "testuser@databricks.com"}, }) @@ -48,7 +48,7 @@ func TestPermissionDiagnosticsApplyFail(t *testing.T) { require.Contains(t, diags[0].Summary, expectedMsg) } -func mockBundle(permissions []resources.IamPermission) *bundle.Bundle { +func mockBundle(permissions []resources.Permission) *bundle.Bundle { return &bundle.Bundle{ Config: config.Root{ Workspace: config.Workspace{ diff --git a/bundle/permissions/permission_report_test.go b/bundle/permissions/permission_report_test.go index 5c77564512..52437aacdf 100644 --- a/bundle/permissions/permission_report_test.go +++ b/bundle/permissions/permission_report_test.go @@ -9,7 +9,7 @@ import ( ) func TestPermissionsReportPermissionDeniedWithGroup(t *testing.T) { - b := mockBundle([]resources.IamPermission{ + b := mockBundle([]resources.Permission{ {Level: "CAN_MANAGE", GroupName: "testgroup"}, }) @@ -22,7 +22,7 @@ func TestPermissionsReportPermissionDeniedWithGroup(t *testing.T) { } func TestPermissionsReportPermissionDeniedWithOtherGroup(t *testing.T) { - b := mockBundle([]resources.IamPermission{ + b := mockBundle([]resources.Permission{ {Level: "CAN_MANAGE", GroupName: "othergroup"}, }) @@ -36,7 +36,7 @@ func TestPermissionsReportPermissionDeniedWithOtherGroup(t *testing.T) { } func TestPermissionsReportPermissionDeniedWithoutPermission(t *testing.T) { - b := mockBundle([]resources.IamPermission{ + b := mockBundle([]resources.Permission{ {Level: "CAN_VIEW", UserName: "testuser@databricks.com"}, }) @@ -62,7 +62,7 @@ func TestPermissionsReportPermissionDeniedNilPermission(t *testing.T) { } func TestPermissionsReportFindOtherOwners(t *testing.T) { - b := mockBundle([]resources.IamPermission{ + b := mockBundle([]resources.Permission{ {Level: "CAN_MANAGE", GroupName: "testgroup"}, {Level: "CAN_MANAGE", UserName: "alice@databricks.com"}, }) diff --git a/bundle/permissions/terraform_errors_test.go b/bundle/permissions/terraform_errors_test.go index 887e0a0a4a..1ad008e251 100644 --- a/bundle/permissions/terraform_errors_test.go +++ b/bundle/permissions/terraform_errors_test.go @@ -12,7 +12,7 @@ import ( func TestTryExtendTerraformPermissionError1(t *testing.T) { ctx := t.Context() - b := mockBundle([]resources.IamPermission{ + b := mockBundle([]resources.Permission{ {Level: "CAN_MANAGE", UserName: "alice@databricks.com"}, }) err := permissions.TryExtendTerraformPermissionError(ctx, b, errors.New("Error: terraform apply: exit status 1\n"+ @@ -33,7 +33,7 @@ func TestTryExtendTerraformPermissionError1(t *testing.T) { func TestTryExtendTerraformPermissionError2(t *testing.T) { ctx := t.Context() - b := mockBundle([]resources.IamPermission{ + b := mockBundle([]resources.Permission{ {Level: "CAN_MANAGE", UserName: "alice@databricks.com"}, {Level: "CAN_MANAGE", UserName: "bob@databricks.com"}, }) @@ -54,7 +54,7 @@ func TestTryExtendTerraformPermissionError2(t *testing.T) { func TestTryExtendTerraformPermissionError3(t *testing.T) { ctx := t.Context() - b := mockBundle([]resources.IamPermission{ + b := mockBundle([]resources.Permission{ {Level: "CAN_MANAGE", UserName: "testuser@databricks.com"}, }) err := permissions.TryExtendTerraformPermissionError(ctx, b, errors.New("Error: terraform apply: exit status 1\n"+ @@ -74,7 +74,7 @@ func TestTryExtendTerraformPermissionError3(t *testing.T) { func TestTryExtendTerraformPermissionErrorNotOwner(t *testing.T) { ctx := t.Context() - b := mockBundle([]resources.IamPermission{ + b := mockBundle([]resources.Permission{ {Level: "CAN_MANAGE", GroupName: "data_team@databricks.com"}, }) b.Config.RunAs = &jobs.JobRunAs{ diff --git a/bundle/permissions/validate_test.go b/bundle/permissions/validate_test.go index 85b3da0cd4..5cd3f05104 100644 --- a/bundle/permissions/validate_test.go +++ b/bundle/permissions/validate_test.go @@ -18,7 +18,7 @@ func TestValidateSharedRootPermissionsForShared(t *testing.T) { Workspace: config.Workspace{ RootPath: "/Workspace/Shared/foo/bar", }, - Permissions: []resources.IamPermission{ + Permissions: []resources.Permission{ {Level: CAN_MANAGE, GroupName: "users"}, }, Resources: config.Resources{ @@ -43,7 +43,7 @@ func TestValidateSharedRootPermissionsForSharedError(t *testing.T) { Workspace: config.Workspace{ RootPath: "/Workspace/Shared/foo/bar", }, - Permissions: []resources.IamPermission{ + Permissions: []resources.Permission{ {Level: CAN_MANAGE, UserName: "foo@bar.com"}, }, Resources: config.Resources{ diff --git a/bundle/permissions/workspace_path_permissions.go b/bundle/permissions/workspace_path_permissions.go index f6928b6124..7d593d719e 100644 --- a/bundle/permissions/workspace_path_permissions.go +++ b/bundle/permissions/workspace_path_permissions.go @@ -12,11 +12,11 @@ import ( type WorkspacePathPermissions struct { Path string - Permissions []resources.IamPermission + Permissions []resources.Permission } func ObjectAclToResourcePermissions(path string, acl []workspace.WorkspaceObjectAccessControlResponse) *WorkspacePathPermissions { - var permissions []resources.IamPermission + var permissions []resources.Permission for _, a := range acl { // Skip the admin group because it's added to all resources by default. if a.GroupName == "admins" { @@ -33,7 +33,7 @@ func ObjectAclToResourcePermissions(path string, acl []workspace.WorkspaceObject } if highestLevel != "" { - permissions = append(permissions, resources.IamPermission{ + permissions = append(permissions, resources.Permission{ Level: highestLevel, GroupName: a.GroupName, UserName: a.UserName, @@ -45,7 +45,7 @@ func ObjectAclToResourcePermissions(path string, acl []workspace.WorkspaceObject return &WorkspacePathPermissions{Permissions: permissions, Path: path} } -func (p WorkspacePathPermissions) Compare(perms []resources.IamPermission) diag.Diagnostics { +func (p WorkspacePathPermissions) Compare(perms []resources.Permission) diag.Diagnostics { var diags diag.Diagnostics // Check the permissions in the workspace and see if they are all set in the bundle. @@ -67,7 +67,7 @@ func (p WorkspacePathPermissions) Compare(perms []resources.IamPermission) diag. } // samePrincipal checks if two permissions refer to the same user/group/service principal. -func samePrincipal(a, b resources.IamPermission) bool { +func samePrincipal(a, b resources.Permission) bool { return a.UserName == b.UserName && a.GroupName == b.GroupName && a.ServicePrincipalName == b.ServicePrincipalName @@ -76,8 +76,8 @@ func samePrincipal(a, b resources.IamPermission) bool { // containsAll checks if all permissions in permA (workspace) are accounted for in permB (bundle). // A workspace permission is considered accounted for if the bundle has the same principal // with an equal or higher permission level. -func containsAll(permA, permB []resources.IamPermission) (bool, []resources.IamPermission) { - var missing []resources.IamPermission +func containsAll(permA, permB []resources.Permission) (bool, []resources.Permission) { + var missing []resources.Permission for _, a := range permA { found := false for _, b := range permB { @@ -104,7 +104,7 @@ func convertWorkspaceObjectPermissionLevel(level workspace.WorkspaceObjectPermis } } -func toString(p []resources.IamPermission) string { +func toString(p []resources.Permission) string { var sb strings.Builder for _, perm := range p { sb.WriteString(fmt.Sprintf("- %s\n", perm.String())) diff --git a/bundle/permissions/workspace_path_permissions_test.go b/bundle/permissions/workspace_path_permissions_test.go index 0c8883a6ad..fe1ac6fd6a 100644 --- a/bundle/permissions/workspace_path_permissions_test.go +++ b/bundle/permissions/workspace_path_permissions_test.go @@ -12,12 +12,12 @@ import ( func TestWorkspacePathPermissionsCompare(t *testing.T) { testCases := []struct { - perms []resources.IamPermission + perms []resources.Permission acl []workspace.WorkspaceObjectAccessControlResponse expected diag.Diagnostics }{ { - perms: []resources.IamPermission{ + perms: []resources.Permission{ {Level: CAN_MANAGE, UserName: "foo@bar.com"}, }, acl: []workspace.WorkspaceObjectAccessControlResponse{ @@ -31,7 +31,7 @@ func TestWorkspacePathPermissionsCompare(t *testing.T) { expected: nil, }, { - perms: []resources.IamPermission{ + perms: []resources.Permission{ {Level: CAN_MANAGE, UserName: "foo@bar.com"}, }, acl: []workspace.WorkspaceObjectAccessControlResponse{ @@ -51,7 +51,7 @@ func TestWorkspacePathPermissionsCompare(t *testing.T) { expected: nil, }, { - perms: []resources.IamPermission{ + perms: []resources.Permission{ {Level: CAN_VIEW, UserName: "foo@bar.com"}, {Level: CAN_MANAGE, ServicePrincipalName: "sp.com"}, }, @@ -66,7 +66,7 @@ func TestWorkspacePathPermissionsCompare(t *testing.T) { expected: nil, }, { - perms: []resources.IamPermission{ + perms: []resources.Permission{ {Level: CAN_MANAGE, UserName: "foo@bar.com"}, }, acl: []workspace.WorkspaceObjectAccessControlResponse{ @@ -95,7 +95,7 @@ func TestWorkspacePathPermissionsCompare(t *testing.T) { }, }, { - perms: []resources.IamPermission{ + perms: []resources.Permission{ {Level: CAN_MANAGE, UserName: "foo@bar.com"}, }, acl: []workspace.WorkspaceObjectAccessControlResponse{ @@ -129,13 +129,13 @@ func TestWorkspacePathPermissionsCompare(t *testing.T) { func TestWorkspacePathPermissionsCompareWithHierarchy(t *testing.T) { testCases := []struct { name string - perms []resources.IamPermission + perms []resources.Permission acl []workspace.WorkspaceObjectAccessControlResponse expected diag.Diagnostics }{ { name: "bundle grants higher permission than workspace - no warning", - perms: []resources.IamPermission{ + perms: []resources.Permission{ {Level: CAN_MANAGE, UserName: "foo@bar.com"}, }, acl: []workspace.WorkspaceObjectAccessControlResponse{ @@ -150,7 +150,7 @@ func TestWorkspacePathPermissionsCompareWithHierarchy(t *testing.T) { }, { name: "bundle grants lower permission than workspace - warning", - perms: []resources.IamPermission{ + perms: []resources.Permission{ {Level: CAN_VIEW, UserName: "foo@bar.com"}, }, acl: []workspace.WorkspaceObjectAccessControlResponse{ @@ -174,7 +174,7 @@ func TestWorkspacePathPermissionsCompareWithHierarchy(t *testing.T) { }, { name: "bundle grants same permission as workspace - no warning", - perms: []resources.IamPermission{ + perms: []resources.Permission{ {Level: CAN_MANAGE, UserName: "foo@bar.com"}, }, acl: []workspace.WorkspaceObjectAccessControlResponse{ diff --git a/bundle/permissions/workspace_root_test.go b/bundle/permissions/workspace_root_test.go index cfaade001d..1dd1c0cbfa 100644 --- a/bundle/permissions/workspace_root_test.go +++ b/bundle/permissions/workspace_root_test.go @@ -26,7 +26,7 @@ func TestApplyWorkspaceRootPermissions(t *testing.T) { StatePath: "/Users/foo@bar.com/state", ResourcePath: "/Users/foo@bar.com/resources", }, - Permissions: []resources.IamPermission{ + Permissions: []resources.Permission{ {Level: CAN_MANAGE, UserName: "TestUser"}, {Level: CAN_VIEW, GroupName: "TestGroup"}, {Level: CAN_RUN, ServicePrincipalName: "TestServicePrincipal"}, @@ -86,7 +86,7 @@ func TestApplyWorkspaceRootPermissionsForAllPaths(t *testing.T) { StatePath: "/Users/foo@bar.com/state", ResourcePath: "/Users/foo@bar.com/resources", }, - Permissions: []resources.IamPermission{ + Permissions: []resources.Permission{ {Level: CAN_MANAGE, UserName: "TestUser"}, {Level: CAN_VIEW, GroupName: "TestGroup"}, {Level: CAN_RUN, ServicePrincipalName: "TestServicePrincipal"}, diff --git a/bundle/tests/bundle_permissions_test.go b/bundle/tests/bundle_permissions_test.go index 51e11af3a0..44eed0c153 100644 --- a/bundle/tests/bundle_permissions_test.go +++ b/bundle/tests/bundle_permissions_test.go @@ -13,10 +13,10 @@ import ( func TestBundlePermissions(t *testing.T) { b := load(t, "./bundle_permissions") - assert.Contains(t, b.Config.Permissions, resources.IamPermission{Level: "CAN_RUN", UserName: "test@company.com"}) - assert.NotContains(t, b.Config.Permissions, resources.IamPermission{Level: "CAN_MANAGE", GroupName: "devs"}) - assert.NotContains(t, b.Config.Permissions, resources.IamPermission{Level: "CAN_VIEW", ServicePrincipalName: "1234-abcd"}) - assert.NotContains(t, b.Config.Permissions, resources.IamPermission{Level: "CAN_RUN", UserName: "bot@company.com"}) + assert.Contains(t, b.Config.Permissions, resources.Permission{Level: "CAN_RUN", UserName: "test@company.com"}) + assert.NotContains(t, b.Config.Permissions, resources.Permission{Level: "CAN_MANAGE", GroupName: "devs"}) + assert.NotContains(t, b.Config.Permissions, resources.Permission{Level: "CAN_VIEW", ServicePrincipalName: "1234-abcd"}) + assert.NotContains(t, b.Config.Permissions, resources.Permission{Level: "CAN_RUN", UserName: "bot@company.com"}) diags := bundle.Apply(t.Context(), b, resourcemutator.ApplyBundlePermissions()) require.NoError(t, diags.Error()) @@ -36,10 +36,10 @@ func TestBundlePermissions(t *testing.T) { func TestBundlePermissionsDevTarget(t *testing.T) { b := loadTarget(t, "./bundle_permissions", "development") - assert.Contains(t, b.Config.Permissions, resources.IamPermission{Level: "CAN_RUN", UserName: "test@company.com"}) - assert.Contains(t, b.Config.Permissions, resources.IamPermission{Level: "CAN_MANAGE", GroupName: "devs"}) - assert.Contains(t, b.Config.Permissions, resources.IamPermission{Level: "CAN_VIEW", ServicePrincipalName: "1234-abcd"}) - assert.Contains(t, b.Config.Permissions, resources.IamPermission{Level: "CAN_RUN", UserName: "bot@company.com"}) + assert.Contains(t, b.Config.Permissions, resources.Permission{Level: "CAN_RUN", UserName: "test@company.com"}) + assert.Contains(t, b.Config.Permissions, resources.Permission{Level: "CAN_MANAGE", GroupName: "devs"}) + assert.Contains(t, b.Config.Permissions, resources.Permission{Level: "CAN_VIEW", ServicePrincipalName: "1234-abcd"}) + assert.Contains(t, b.Config.Permissions, resources.Permission{Level: "CAN_RUN", UserName: "bot@company.com"}) diags := bundle.Apply(t.Context(), b, resourcemutator.ApplyBundlePermissions()) require.NoError(t, diags.Error()) diff --git a/cmd/root/.azure/az.json b/cmd/root/.azure/az.json new file mode 100644 index 0000000000..22fdca1b26 --- /dev/null +++ b/cmd/root/.azure/az.json @@ -0,0 +1 @@ +{} \ No newline at end of file diff --git a/cmd/root/.azure/az.sess b/cmd/root/.azure/az.sess new file mode 100644 index 0000000000..22fdca1b26 --- /dev/null +++ b/cmd/root/.azure/az.sess @@ -0,0 +1 @@ +{} \ No newline at end of file diff --git a/cmd/root/.azure/azureProfile.json b/cmd/root/.azure/azureProfile.json new file mode 100644 index 0000000000..f7a4d87d78 --- /dev/null +++ b/cmd/root/.azure/azureProfile.json @@ -0,0 +1 @@ +{"installationId": "ccddb118-1c8b-11f1-a1e0-9a40ca0dc229"} \ No newline at end of file diff --git a/cmd/root/.azure/commandIndex.json b/cmd/root/.azure/commandIndex.json new file mode 100644 index 0000000000..9e60849b61 --- /dev/null +++ b/cmd/root/.azure/commandIndex.json @@ -0,0 +1 @@ +{"version": "2.74.0", "cloudProfile": "latest", "commandIndex": {"acr": ["azure.cli.command_modules.acr"], "aks": ["azure.cli.command_modules.acs", "azure.cli.command_modules.serviceconnector"], "advisor": ["azure.cli.command_modules.advisor"], "ams": ["azure.cli.command_modules.ams"], "apim": ["azure.cli.command_modules.apim"], "appconfig": ["azure.cli.command_modules.appconfig"], "webapp": ["azure.cli.command_modules.appservice", "azure.cli.command_modules.serviceconnector"], "functionapp": ["azure.cli.command_modules.appservice", "azure.cli.command_modules.serviceconnector"], "appservice": ["azure.cli.command_modules.appservice"], "staticwebapp": ["azure.cli.command_modules.appservice"], "logicapp": ["azure.cli.command_modules.appservice"], "aro": ["azure.cli.command_modules.aro"], "backup": ["azure.cli.command_modules.backup"], "batch": ["azure.cli.command_modules.batch"], "batchai": ["azure.cli.command_modules.batchai"], "billing": ["azure.cli.command_modules.billing"], "bot": ["azure.cli.command_modules.botservice"], "afd": ["azure.cli.command_modules.cdn"], "cdn": ["azure.cli.command_modules.cdn"], "cloud": ["azure.cli.command_modules.cloud"], "cognitiveservices": ["azure.cli.command_modules.cognitiveservices"], "compute-recommender": ["azure.cli.command_modules.compute_recommender"], "compute-fleet": ["azure.cli.command_modules.computefleet"], "config": ["azure.cli.command_modules.config"], "configure": ["azure.cli.command_modules.configure"], "cache": ["azure.cli.command_modules.configure"], "consumption": ["azure.cli.command_modules.consumption"], "container": ["azure.cli.command_modules.container"], "containerapp": ["azure.cli.command_modules.containerapp", "azure.cli.command_modules.serviceconnector"], "cosmosdb": ["azure.cli.command_modules.cosmosdb"], "managed-cassandra": ["azure.cli.command_modules.cosmosdb"], "databoxedge": ["azure.cli.command_modules.databoxedge"], "dls": ["azure.cli.command_modules.dls"], "dms": ["azure.cli.command_modules.dms"], "eventgrid": ["azure.cli.command_modules.eventgrid"], "eventhubs": ["azure.cli.command_modules.eventhubs"], "extension": ["azure.cli.command_modules.extension"], "feedback": ["azure.cli.command_modules.feedback"], "survey": ["azure.cli.command_modules.feedback"], "find": ["azure.cli.command_modules.find"], "hdinsight": ["azure.cli.command_modules.hdinsight"], "identity": ["azure.cli.command_modules.identity"], "interactive": ["azure.cli.command_modules.interactive"], "iot": ["azure.cli.command_modules.iot"], "keyvault": ["azure.cli.command_modules.keyvault"], "lab": ["azure.cli.command_modules.lab"], "managedservices": ["azure.cli.command_modules.managedservices"], "maps": ["azure.cli.command_modules.maps"], "term": ["azure.cli.command_modules.marketplaceordering"], "monitor": ["azure.cli.command_modules.monitor"], "mysql": ["azure.cli.command_modules.mysql", "azure.cli.command_modules.rdbms"], "netappfiles": ["azure.cli.command_modules.netappfiles"], "network": ["azure.cli.command_modules.network", "azure.cli.command_modules.privatedns"], "policy": ["azure.cli.command_modules.policyinsights", "azure.cli.command_modules.resource"], "login": ["azure.cli.command_modules.profile"], "logout": ["azure.cli.command_modules.profile"], "self-test": ["azure.cli.command_modules.profile"], "account": ["azure.cli.command_modules.profile", "azure.cli.command_modules.resource"], "mariadb": ["azure.cli.command_modules.rdbms"], "postgres": ["azure.cli.command_modules.rdbms"], "redis": ["azure.cli.command_modules.redis"], "relay": ["azure.cli.command_modules.relay"], "data-boundary": ["azure.cli.command_modules.resource"], "group": ["azure.cli.command_modules.resource"], "resource": ["azure.cli.command_modules.resource"], "provider": ["azure.cli.command_modules.resource"], "feature": ["azure.cli.command_modules.resource"], "tag": ["azure.cli.command_modules.resource"], "deployment": ["azure.cli.command_modules.resource"], "deployment-scripts": ["azure.cli.command_modules.resource"], "ts": ["azure.cli.command_modules.resource"], "stack": ["azure.cli.command_modules.resource"], "lock": ["azure.cli.command_modules.resource"], "managedapp": ["azure.cli.command_modules.resource"], "bicep": ["azure.cli.command_modules.resource"], "resourcemanagement": ["azure.cli.command_modules.resource"], "private-link": ["azure.cli.command_modules.resource"], "role": ["azure.cli.command_modules.role"], "ad": ["azure.cli.command_modules.role"], "search": ["azure.cli.command_modules.search"], "security": ["azure.cli.command_modules.security"], "servicebus": ["azure.cli.command_modules.servicebus"], "connection": ["azure.cli.command_modules.serviceconnector"], "sf": ["azure.cli.command_modules.servicefabric"], "signalr": ["azure.cli.command_modules.signalr"], "sql": ["azure.cli.command_modules.sql", "azure.cli.command_modules.sqlvm"], "storage": ["azure.cli.command_modules.storage"], "synapse": ["azure.cli.command_modules.synapse"], "rest": ["azure.cli.command_modules.util"], "version": ["azure.cli.command_modules.util"], "upgrade": ["azure.cli.command_modules.util"], "demo": ["azure.cli.command_modules.util"], "snapshot": ["azure.cli.command_modules.vm"], "disk-access": ["azure.cli.command_modules.vm"], "sig": ["azure.cli.command_modules.vm"], "vmss": ["azure.cli.command_modules.vm"], "restore-point": ["azure.cli.command_modules.vm"], "image": ["azure.cli.command_modules.vm"], "capacity": ["azure.cli.command_modules.vm"], "vm": ["azure.cli.command_modules.vm"], "disk": ["azure.cli.command_modules.vm"], "ppg": ["azure.cli.command_modules.vm"], "disk-encryption-set": ["azure.cli.command_modules.vm"], "sshkey": ["azure.cli.command_modules.vm"]}} \ No newline at end of file diff --git a/cmd/root/.azure/config b/cmd/root/.azure/config new file mode 100644 index 0000000000..0ed7f34d6c --- /dev/null +++ b/cmd/root/.azure/config @@ -0,0 +1,3 @@ +[cloud] +name = AzureCloud + diff --git a/cmd/root/.azure/versionCheck.json b/cmd/root/.azure/versionCheck.json new file mode 100644 index 0000000000..6925929d3a --- /dev/null +++ b/cmd/root/.azure/versionCheck.json @@ -0,0 +1 @@ +{"versions": {"azure-cli": {"local": "2.74.0", "pypi": "2.84.0"}, "core": {"local": "2.74.0", "pypi": "2.84.0"}, "telemetry": {"local": "1.1.0", "pypi": "1.1.0"}}, "update_time": "2026-03-10 15:16:57.303355"} \ No newline at end of file diff --git a/cmd/root/Library/Application Support/Microsoft/DeveloperTools/deviceid b/cmd/root/Library/Application Support/Microsoft/DeveloperTools/deviceid new file mode 100644 index 0000000000..d8c93d70f8 --- /dev/null +++ b/cmd/root/Library/Application Support/Microsoft/DeveloperTools/deviceid @@ -0,0 +1 @@ +a22040df-bada-4d6a-a56f-8762e92685de \ No newline at end of file diff --git a/pr.txt b/pr.txt new file mode 100644 index 0000000000..4169c93759 --- /dev/null +++ b/pr.txt @@ -0,0 +1,53 @@ +Title: Simplify bundle permission types using Go generics and SDK enum types + +--- + +## Summary + +This PR simplifies the bundle permission type system by: + +1. **Replacing 12 hand-coded permission structs with a single generic `Permission[L ~string]`** — removing ~200 lines of boilerplate (`GetLevel()`, `GetUserName()`, `GetAPIRequestObjectType()`, per-type struct definitions, etc.). + +2. **Using SDK-native enum types for permission levels** — e.g. `jobs.JobPermissionLevel`, `pipelines.PipelinePermissionLevel`, `serving.ServingEndpointPermissionLevel`. The JSON schema now shows the actual valid enum values per resource instead of plain strings. + +3. **Defining named non-generic type aliases per resource** (e.g. `type JobPermission Permission[jobs.JobPermissionLevel]`) so that the JSON schema, YAML, and Python codegen see distinct named types (`JobPermission`, `PipelinePermission`, etc.) rather than generic instantiation paths. + +4. **Simplifying Python codegen** — removing the `_normalize_generic_key` workaround, cleaning up `packages.py`, and deleting the now-redundant per-resource `permission.py` files in favor of properly named `job_permission.py` / `pipeline_permission.py`. + +### Before + +```go +// 12 nearly-identical structs + 12×4 interface methods + 12 GetAPIRequestObjectType() +type JobPermission struct { + Level JobPermissionLevel `json:"level"` + UserName string `json:"user_name,omitempty"` + ... +} +func (p JobPermission) GetLevel() string { return string(p.Level) } +func (p JobPermission) GetUserName() string { return p.UserName } +// ... etc for all 12 types +``` + +### After + +```go +// One generic base type +type Permission[L ~string] struct { + Level L `json:"level"` + UserName string `json:"user_name,omitempty"` + ... +} + +// Named non-generic types per resource (for schema/codegen clarity) +type ( + JobPermission Permission[jobs.JobPermissionLevel] + JobPermissions []JobPermission +) +``` + +## Test plan + +- [ ] `make test` passes +- [ ] `make schema` regenerated — schema now shows resource-specific enum values for `level` fields +- [ ] Python codegen: `cd python && make codegen && make test` passes +- [ ] Acceptance test `TestAccept/bundle/refschema` updated and passing diff --git a/python/codegen/codegen/jsonschema.py b/python/codegen/codegen/jsonschema.py index 41e20b220c..a4786371d4 100644 --- a/python/codegen/codegen/jsonschema.py +++ b/python/codegen/codegen/jsonschema.py @@ -168,41 +168,14 @@ def _is_schema(d: dict) -> bool: return test.get("type") in ("object", "string") -def _normalize_generic_key(path: str) -> str: - """ - Normalize a generic type path to a short schema key. - E.g., 'resources.Permission[github.com/.../jobs.JobPermissionLevel]' -> 'resources.JobPermission' - """ - if "[" not in path or not path.endswith("]"): - return path - - bracket_pos = path.index("[") - before = path[:bracket_pos] # e.g., 'resources.Permission' - type_param = path[ - bracket_pos + 1 : -1 - ] # e.g., 'github.com/.../jobs.JobPermissionLevel' - - type_ns = before.split(".")[0] # 'resources' - param_class = type_param.split("/")[-1].split(".")[-1] # 'JobPermissionLevel' - - if param_class.endswith("PermissionLevel"): - class_name = param_class[: -len("Level")] # 'JobPermission' - return f"{type_ns}.{class_name}" - - return path - - def _flatten_spec(nested: dict, prefix: str = "") -> dict: - """ - Recursively flatten nested schema defs, normalizing generic type names. - Generic types like 'resources.Permission[.../jobs.JobPermissionLevel]' become 'resources.JobPermission'. - """ + """Recursively flatten nested schema defs.""" result = {} for k, v in nested.items(): path = f"{prefix}/{k}" if prefix else k if isinstance(v, dict): if _is_schema(v): - result[_normalize_generic_key(path)] = v + result[path] = v else: result.update(_flatten_spec(v, path)) return result diff --git a/python/codegen/codegen/packages.py b/python/codegen/codegen/packages.py index 686cbaaa84..7d1beb4992 100644 --- a/python/codegen/codegen/packages.py +++ b/python/codegen/codegen/packages.py @@ -45,41 +45,11 @@ def get_schema_key(ref: str) -> str: - """ - Get the schema dict key from a full $ref string, handling generic types. - E.g., '#/$defs/.../resources.Permission[.../jobs.JobPermissionLevel]' -> 'resources.JobPermission' - """ - if "[" not in ref or not ref.endswith("]"): - return ref.split("/")[-1] - - bracket_pos = ref.index("[") - before = ref[:bracket_pos] # e.g., '#/$defs/.../resources.Permission' - type_param = ref[bracket_pos + 1 : -1] # e.g., '.../jobs.JobPermissionLevel' - - type_ns = before.split("/")[-1].split(".")[0] # 'resources' - param_class = type_param.split("/")[-1].split(".")[-1] # 'JobPermissionLevel' - - if param_class.endswith("PermissionLevel"): - class_name = param_class[: -len("Level")] # 'JobPermission' - return f"{type_ns}.{class_name}" - return ref.split("/")[-1] def get_class_name(ref: str) -> str: - name = ref.split("/")[-1] - - # Generic type: last segment is the type parameter like "jobs.JobPermissionLevel]" - if name.endswith("]"): - name = name[:-1] # strip "]" - param_class = name.split(".")[-1] # 'JobPermissionLevel' - if param_class.endswith("PermissionLevel"): - name = param_class[: -len("Level")] # 'JobPermission' - else: - name = param_class - else: - name = name.split(".")[-1] - + name = ref.split("/")[-1].split(".")[-1] return RENAMES.get(name, name) @@ -90,10 +60,6 @@ def is_resource(ref: str) -> bool: def should_load_ref(ref: str) -> bool: name = ref.split("/")[-1] - # Skip Go generic type fragments (their names contain '[' from embedded package paths) - if "[" in name: - return False - for namespace in LOADED_NAMESPACES: if name.startswith(f"{namespace}."): return True @@ -110,23 +76,8 @@ def get_package(namespace: str, ref: str) -> Optional[str]: Returns Python package for a given OpenAPI ref. Returns None for builtin types. """ - full_name = ref.split("/")[-1] - # Generic type: last segment is the type parameter like "jobs.JobPermissionLevel]" - if full_name.endswith("]"): - full_name = full_name[:-1] # strip "]" - if full_name in PRIMITIVES: - return None - param_ns = full_name.split(".")[0] # e.g., 'jobs' - param_class = full_name.split(".")[-1] # e.g., 'JobPermissionLevel' - if param_class.endswith("PermissionLevel"): - class_name = param_class[: -len("Level")] # 'JobPermission' - else: - class_name = param_class - package_name = re.sub(r"(? Date: Tue, 10 Mar 2026 16:45:39 +0100 Subject: [PATCH 21/28] Remove accidentally committed files Co-Authored-By: Claude Sonnet 4.6 --- cmd/root/.azure/az.json | 1 - cmd/root/.azure/az.sess | 1 - cmd/root/.azure/azureProfile.json | 1 - cmd/root/.azure/commandIndex.json | 1 - cmd/root/.azure/config | 3 -- cmd/root/.azure/versionCheck.json | 1 - .../Microsoft/DeveloperTools/deviceid | 1 - pr.txt | 53 ------------------- 8 files changed, 62 deletions(-) delete mode 100644 cmd/root/.azure/az.json delete mode 100644 cmd/root/.azure/az.sess delete mode 100644 cmd/root/.azure/azureProfile.json delete mode 100644 cmd/root/.azure/commandIndex.json delete mode 100644 cmd/root/.azure/config delete mode 100644 cmd/root/.azure/versionCheck.json delete mode 100644 cmd/root/Library/Application Support/Microsoft/DeveloperTools/deviceid delete mode 100644 pr.txt diff --git a/cmd/root/.azure/az.json b/cmd/root/.azure/az.json deleted file mode 100644 index 22fdca1b26..0000000000 --- a/cmd/root/.azure/az.json +++ /dev/null @@ -1 +0,0 @@ -{} \ No newline at end of file diff --git a/cmd/root/.azure/az.sess b/cmd/root/.azure/az.sess deleted file mode 100644 index 22fdca1b26..0000000000 --- a/cmd/root/.azure/az.sess +++ /dev/null @@ -1 +0,0 @@ -{} \ No newline at end of file diff --git a/cmd/root/.azure/azureProfile.json b/cmd/root/.azure/azureProfile.json deleted file mode 100644 index f7a4d87d78..0000000000 --- a/cmd/root/.azure/azureProfile.json +++ /dev/null @@ -1 +0,0 @@ -{"installationId": "ccddb118-1c8b-11f1-a1e0-9a40ca0dc229"} \ No newline at end of file diff --git a/cmd/root/.azure/commandIndex.json b/cmd/root/.azure/commandIndex.json deleted file mode 100644 index 9e60849b61..0000000000 --- a/cmd/root/.azure/commandIndex.json +++ /dev/null @@ -1 +0,0 @@ -{"version": "2.74.0", "cloudProfile": "latest", "commandIndex": {"acr": ["azure.cli.command_modules.acr"], "aks": ["azure.cli.command_modules.acs", "azure.cli.command_modules.serviceconnector"], "advisor": ["azure.cli.command_modules.advisor"], "ams": ["azure.cli.command_modules.ams"], "apim": ["azure.cli.command_modules.apim"], "appconfig": ["azure.cli.command_modules.appconfig"], "webapp": ["azure.cli.command_modules.appservice", "azure.cli.command_modules.serviceconnector"], "functionapp": ["azure.cli.command_modules.appservice", "azure.cli.command_modules.serviceconnector"], "appservice": ["azure.cli.command_modules.appservice"], "staticwebapp": ["azure.cli.command_modules.appservice"], "logicapp": ["azure.cli.command_modules.appservice"], "aro": ["azure.cli.command_modules.aro"], "backup": ["azure.cli.command_modules.backup"], "batch": ["azure.cli.command_modules.batch"], "batchai": ["azure.cli.command_modules.batchai"], "billing": ["azure.cli.command_modules.billing"], "bot": ["azure.cli.command_modules.botservice"], "afd": ["azure.cli.command_modules.cdn"], "cdn": ["azure.cli.command_modules.cdn"], "cloud": ["azure.cli.command_modules.cloud"], "cognitiveservices": ["azure.cli.command_modules.cognitiveservices"], "compute-recommender": ["azure.cli.command_modules.compute_recommender"], "compute-fleet": ["azure.cli.command_modules.computefleet"], "config": ["azure.cli.command_modules.config"], "configure": ["azure.cli.command_modules.configure"], "cache": ["azure.cli.command_modules.configure"], "consumption": ["azure.cli.command_modules.consumption"], "container": ["azure.cli.command_modules.container"], "containerapp": ["azure.cli.command_modules.containerapp", "azure.cli.command_modules.serviceconnector"], "cosmosdb": ["azure.cli.command_modules.cosmosdb"], "managed-cassandra": ["azure.cli.command_modules.cosmosdb"], "databoxedge": ["azure.cli.command_modules.databoxedge"], "dls": ["azure.cli.command_modules.dls"], "dms": ["azure.cli.command_modules.dms"], "eventgrid": ["azure.cli.command_modules.eventgrid"], "eventhubs": ["azure.cli.command_modules.eventhubs"], "extension": ["azure.cli.command_modules.extension"], "feedback": ["azure.cli.command_modules.feedback"], "survey": ["azure.cli.command_modules.feedback"], "find": ["azure.cli.command_modules.find"], "hdinsight": ["azure.cli.command_modules.hdinsight"], "identity": ["azure.cli.command_modules.identity"], "interactive": ["azure.cli.command_modules.interactive"], "iot": ["azure.cli.command_modules.iot"], "keyvault": ["azure.cli.command_modules.keyvault"], "lab": ["azure.cli.command_modules.lab"], "managedservices": ["azure.cli.command_modules.managedservices"], "maps": ["azure.cli.command_modules.maps"], "term": ["azure.cli.command_modules.marketplaceordering"], "monitor": ["azure.cli.command_modules.monitor"], "mysql": ["azure.cli.command_modules.mysql", "azure.cli.command_modules.rdbms"], "netappfiles": ["azure.cli.command_modules.netappfiles"], "network": ["azure.cli.command_modules.network", "azure.cli.command_modules.privatedns"], "policy": ["azure.cli.command_modules.policyinsights", "azure.cli.command_modules.resource"], "login": ["azure.cli.command_modules.profile"], "logout": ["azure.cli.command_modules.profile"], "self-test": ["azure.cli.command_modules.profile"], "account": ["azure.cli.command_modules.profile", "azure.cli.command_modules.resource"], "mariadb": ["azure.cli.command_modules.rdbms"], "postgres": ["azure.cli.command_modules.rdbms"], "redis": ["azure.cli.command_modules.redis"], "relay": ["azure.cli.command_modules.relay"], "data-boundary": ["azure.cli.command_modules.resource"], "group": ["azure.cli.command_modules.resource"], "resource": ["azure.cli.command_modules.resource"], "provider": ["azure.cli.command_modules.resource"], "feature": ["azure.cli.command_modules.resource"], "tag": ["azure.cli.command_modules.resource"], "deployment": ["azure.cli.command_modules.resource"], "deployment-scripts": ["azure.cli.command_modules.resource"], "ts": ["azure.cli.command_modules.resource"], "stack": ["azure.cli.command_modules.resource"], "lock": ["azure.cli.command_modules.resource"], "managedapp": ["azure.cli.command_modules.resource"], "bicep": ["azure.cli.command_modules.resource"], "resourcemanagement": ["azure.cli.command_modules.resource"], "private-link": ["azure.cli.command_modules.resource"], "role": ["azure.cli.command_modules.role"], "ad": ["azure.cli.command_modules.role"], "search": ["azure.cli.command_modules.search"], "security": ["azure.cli.command_modules.security"], "servicebus": ["azure.cli.command_modules.servicebus"], "connection": ["azure.cli.command_modules.serviceconnector"], "sf": ["azure.cli.command_modules.servicefabric"], "signalr": ["azure.cli.command_modules.signalr"], "sql": ["azure.cli.command_modules.sql", "azure.cli.command_modules.sqlvm"], "storage": ["azure.cli.command_modules.storage"], "synapse": ["azure.cli.command_modules.synapse"], "rest": ["azure.cli.command_modules.util"], "version": ["azure.cli.command_modules.util"], "upgrade": ["azure.cli.command_modules.util"], "demo": ["azure.cli.command_modules.util"], "snapshot": ["azure.cli.command_modules.vm"], "disk-access": ["azure.cli.command_modules.vm"], "sig": ["azure.cli.command_modules.vm"], "vmss": ["azure.cli.command_modules.vm"], "restore-point": ["azure.cli.command_modules.vm"], "image": ["azure.cli.command_modules.vm"], "capacity": ["azure.cli.command_modules.vm"], "vm": ["azure.cli.command_modules.vm"], "disk": ["azure.cli.command_modules.vm"], "ppg": ["azure.cli.command_modules.vm"], "disk-encryption-set": ["azure.cli.command_modules.vm"], "sshkey": ["azure.cli.command_modules.vm"]}} \ No newline at end of file diff --git a/cmd/root/.azure/config b/cmd/root/.azure/config deleted file mode 100644 index 0ed7f34d6c..0000000000 --- a/cmd/root/.azure/config +++ /dev/null @@ -1,3 +0,0 @@ -[cloud] -name = AzureCloud - diff --git a/cmd/root/.azure/versionCheck.json b/cmd/root/.azure/versionCheck.json deleted file mode 100644 index 6925929d3a..0000000000 --- a/cmd/root/.azure/versionCheck.json +++ /dev/null @@ -1 +0,0 @@ -{"versions": {"azure-cli": {"local": "2.74.0", "pypi": "2.84.0"}, "core": {"local": "2.74.0", "pypi": "2.84.0"}, "telemetry": {"local": "1.1.0", "pypi": "1.1.0"}}, "update_time": "2026-03-10 15:16:57.303355"} \ No newline at end of file diff --git a/cmd/root/Library/Application Support/Microsoft/DeveloperTools/deviceid b/cmd/root/Library/Application Support/Microsoft/DeveloperTools/deviceid deleted file mode 100644 index d8c93d70f8..0000000000 --- a/cmd/root/Library/Application Support/Microsoft/DeveloperTools/deviceid +++ /dev/null @@ -1 +0,0 @@ -a22040df-bada-4d6a-a56f-8762e92685de \ No newline at end of file diff --git a/pr.txt b/pr.txt deleted file mode 100644 index 4169c93759..0000000000 --- a/pr.txt +++ /dev/null @@ -1,53 +0,0 @@ -Title: Simplify bundle permission types using Go generics and SDK enum types - ---- - -## Summary - -This PR simplifies the bundle permission type system by: - -1. **Replacing 12 hand-coded permission structs with a single generic `Permission[L ~string]`** — removing ~200 lines of boilerplate (`GetLevel()`, `GetUserName()`, `GetAPIRequestObjectType()`, per-type struct definitions, etc.). - -2. **Using SDK-native enum types for permission levels** — e.g. `jobs.JobPermissionLevel`, `pipelines.PipelinePermissionLevel`, `serving.ServingEndpointPermissionLevel`. The JSON schema now shows the actual valid enum values per resource instead of plain strings. - -3. **Defining named non-generic type aliases per resource** (e.g. `type JobPermission Permission[jobs.JobPermissionLevel]`) so that the JSON schema, YAML, and Python codegen see distinct named types (`JobPermission`, `PipelinePermission`, etc.) rather than generic instantiation paths. - -4. **Simplifying Python codegen** — removing the `_normalize_generic_key` workaround, cleaning up `packages.py`, and deleting the now-redundant per-resource `permission.py` files in favor of properly named `job_permission.py` / `pipeline_permission.py`. - -### Before - -```go -// 12 nearly-identical structs + 12×4 interface methods + 12 GetAPIRequestObjectType() -type JobPermission struct { - Level JobPermissionLevel `json:"level"` - UserName string `json:"user_name,omitempty"` - ... -} -func (p JobPermission) GetLevel() string { return string(p.Level) } -func (p JobPermission) GetUserName() string { return p.UserName } -// ... etc for all 12 types -``` - -### After - -```go -// One generic base type -type Permission[L ~string] struct { - Level L `json:"level"` - UserName string `json:"user_name,omitempty"` - ... -} - -// Named non-generic types per resource (for schema/codegen clarity) -type ( - JobPermission Permission[jobs.JobPermissionLevel] - JobPermissions []JobPermission -) -``` - -## Test plan - -- [ ] `make test` passes -- [ ] `make schema` regenerated — schema now shows resource-specific enum values for `level` fields -- [ ] Python codegen: `cd python && make codegen && make test` passes -- [ ] Acceptance test `TestAccept/bundle/refschema` updated and passing From 71f56674f3bb21769afafd19d596f4d09c41f62e Mon Sep 17 00:00:00 2001 From: Denis Bilenko Date: Tue, 10 Mar 2026 16:48:04 +0100 Subject: [PATCH 22/28] Update annotations.yml for Permission rename Co-Authored-By: Claude Sonnet 4.6 --- bundle/internal/schema/annotations.yml | 18 +++++++++--------- 1 file changed, 9 insertions(+), 9 deletions(-) diff --git a/bundle/internal/schema/annotations.yml b/bundle/internal/schema/annotations.yml index 9808ce7a39..f1dda37485 100644 --- a/bundle/internal/schema/annotations.yml +++ b/bundle/internal/schema/annotations.yml @@ -631,7 +631,7 @@ github.com/databricks/cli/bundle/config/resources.ExternalLocation: "url": "description": |- PLACEHOLDER -github.com/databricks/cli/bundle/config/resources.Permission: +github.com/databricks/cli/bundle/config/resources.JobPermission: "group_name": "description": |- PLACEHOLDER @@ -644,7 +644,11 @@ github.com/databricks/cli/bundle/config/resources.Permission: "user_name": "description": |- PLACEHOLDER -github.com/databricks/cli/bundle/config/resources.JobPermission: +github.com/databricks/cli/bundle/config/resources.Lifecycle: + "prevent_destroy": + "description": |- + Lifecycle setting to prevent the resource from being destroyed. +github.com/databricks/cli/bundle/config/resources.MlflowExperimentPermission: "group_name": "description": |- PLACEHOLDER @@ -657,11 +661,7 @@ github.com/databricks/cli/bundle/config/resources.JobPermission: "user_name": "description": |- PLACEHOLDER -github.com/databricks/cli/bundle/config/resources.Lifecycle: - "prevent_destroy": - "description": |- - Lifecycle setting to prevent the resource from being destroyed. -github.com/databricks/cli/bundle/config/resources.MlflowExperimentPermission: +github.com/databricks/cli/bundle/config/resources.MlflowModelPermission: "group_name": "description": |- PLACEHOLDER @@ -674,7 +674,7 @@ github.com/databricks/cli/bundle/config/resources.MlflowExperimentPermission: "user_name": "description": |- PLACEHOLDER -github.com/databricks/cli/bundle/config/resources.MlflowModelPermission: +github.com/databricks/cli/bundle/config/resources.ModelServingEndpointPermission: "group_name": "description": |- PLACEHOLDER @@ -687,7 +687,7 @@ github.com/databricks/cli/bundle/config/resources.MlflowModelPermission: "user_name": "description": |- PLACEHOLDER -github.com/databricks/cli/bundle/config/resources.ModelServingEndpointPermission: +github.com/databricks/cli/bundle/config/resources.Permission: "group_name": "description": |- PLACEHOLDER From b26d2aafe8218b81296c7f638f9e203d618d5f01 Mon Sep 17 00:00:00 2001 From: Denis Bilenko Date: Tue, 10 Mar 2026 16:48:30 +0100 Subject: [PATCH 23/28] Regenerate schema after Permission rename Co-Authored-By: Claude Sonnet 4.6 --- acceptance/bundle/refschema/out.fields.txt | 16 +- bundle/schema/jsonschema.json | 90 ++-- bundle/schema/jsonschema_for_docs.json | 594 ++++++++++----------- 3 files changed, 342 insertions(+), 358 deletions(-) diff --git a/acceptance/bundle/refschema/out.fields.txt b/acceptance/bundle/refschema/out.fields.txt index 47c8164d37..1d6fba2d8d 100644 --- a/acceptance/bundle/refschema/out.fields.txt +++ b/acceptance/bundle/refschema/out.fields.txt @@ -38,8 +38,8 @@ resources.alerts.*.lifecycle_state sql.AlertLifecycleState ALL resources.alerts.*.modified_status string INPUT resources.alerts.*.owner_user_name string ALL resources.alerts.*.parent_path string ALL -resources.alerts.*.permissions []resources.IamPermission INPUT -resources.alerts.*.permissions[*] resources.IamPermission INPUT +resources.alerts.*.permissions []resources.Permission INPUT +resources.alerts.*.permissions[*] resources.Permission INPUT resources.alerts.*.permissions[*].group_name string INPUT resources.alerts.*.permissions[*].level iam.PermissionLevel INPUT resources.alerts.*.permissions[*].service_principal_name string INPUT @@ -561,8 +561,8 @@ resources.dashboards.*.lifecycle_state dashboards.LifecycleState ALL resources.dashboards.*.modified_status string INPUT resources.dashboards.*.parent_path string ALL resources.dashboards.*.path string ALL -resources.dashboards.*.permissions []resources.IamPermission INPUT -resources.dashboards.*.permissions[*] resources.IamPermission INPUT +resources.dashboards.*.permissions []resources.Permission INPUT +resources.dashboards.*.permissions[*] resources.Permission INPUT resources.dashboards.*.permissions[*].group_name string INPUT resources.dashboards.*.permissions[*].level iam.PermissionLevel INPUT resources.dashboards.*.permissions[*].service_principal_name string INPUT @@ -628,8 +628,8 @@ resources.database_instances.*.parent_instance_ref.effective_lsn string ALL resources.database_instances.*.parent_instance_ref.lsn string ALL resources.database_instances.*.parent_instance_ref.name string ALL resources.database_instances.*.parent_instance_ref.uid string ALL -resources.database_instances.*.permissions []resources.IamPermission INPUT -resources.database_instances.*.permissions[*] resources.IamPermission INPUT +resources.database_instances.*.permissions []resources.Permission INPUT +resources.database_instances.*.permissions[*] resources.Permission INPUT resources.database_instances.*.permissions[*].group_name string INPUT resources.database_instances.*.permissions[*].level iam.PermissionLevel INPUT resources.database_instances.*.permissions[*].service_principal_name string INPUT @@ -2634,8 +2634,8 @@ resources.postgres_projects.*.lifecycle resources.Lifecycle INPUT resources.postgres_projects.*.lifecycle.prevent_destroy bool INPUT resources.postgres_projects.*.modified_status string INPUT resources.postgres_projects.*.name string REMOTE -resources.postgres_projects.*.permissions []resources.IamPermission INPUT -resources.postgres_projects.*.permissions[*] resources.IamPermission INPUT +resources.postgres_projects.*.permissions []resources.Permission INPUT +resources.postgres_projects.*.permissions[*] resources.Permission INPUT resources.postgres_projects.*.permissions[*].group_name string INPUT resources.postgres_projects.*.permissions[*].level iam.PermissionLevel INPUT resources.postgres_projects.*.permissions[*].service_principal_name string INPUT diff --git a/bundle/schema/jsonschema.json b/bundle/schema/jsonschema.json index e048f995ea..408cf64e7c 100644 --- a/bundle/schema/jsonschema.json +++ b/bundle/schema/jsonschema.json @@ -86,7 +86,7 @@ "$ref": "#/$defs/string" }, "permissions": { - "$ref": "#/$defs/slice/github.com/databricks/cli/bundle/config/resources.IamPermission" + "$ref": "#/$defs/slice/github.com/databricks/cli/bundle/config/resources.Permission" }, "query_text": { "$ref": "#/$defs/string" @@ -548,7 +548,7 @@ "$ref": "#/$defs/string" }, "permissions": { - "$ref": "#/$defs/slice/github.com/databricks/cli/bundle/config/resources.IamPermission" + "$ref": "#/$defs/slice/github.com/databricks/cli/bundle/config/resources.Permission" }, "serialized_dashboard": { "description": "The contents of the dashboard in serialized string form.\nThis field is excluded in List Dashboards responses.\nUse the [get dashboard API](https://docs.databricks.com/api/workspace/lakeview/get)\nto retrieve an example response, which includes the `serialized_dashboard` field.\nThis field provides the structure of the JSON string that represents the dashboard's\nlayout and components.", @@ -649,7 +649,7 @@ "$ref": "#/$defs/github.com/databricks/databricks-sdk-go/service/database.DatabaseInstanceRef" }, "permissions": { - "$ref": "#/$defs/slice/github.com/databricks/cli/bundle/config/resources.IamPermission" + "$ref": "#/$defs/slice/github.com/databricks/cli/bundle/config/resources.Permission" }, "retention_window_in_days": { "description": "The retention window for the instance. This is the time window in days\nfor which the historical data is retained. The default value is 7 days.\nValid values are 2 to 35 days.", @@ -730,35 +730,6 @@ } ] }, - "resources.IamPermission": { - "oneOf": [ - { - "type": "object", - "properties": { - "group_name": { - "$ref": "#/$defs/string" - }, - "level": { - "$ref": "#/$defs/github.com/databricks/databricks-sdk-go/service/iam.PermissionLevel" - }, - "service_principal_name": { - "$ref": "#/$defs/string" - }, - "user_name": { - "$ref": "#/$defs/string" - } - }, - "additionalProperties": false, - "required": [ - "level" - ] - }, - { - "type": "string", - "pattern": "\\$\\{(var(\\.[a-zA-Z]+([-_]?[a-zA-Z0-9]+)*(\\[[0-9]+\\])*)+)\\}" - } - ] - }, "resources.Job": { "oneOf": [ { @@ -1139,6 +1110,35 @@ } ] }, + "resources.Permission": { + "oneOf": [ + { + "type": "object", + "properties": { + "group_name": { + "$ref": "#/$defs/string" + }, + "level": { + "$ref": "#/$defs/github.com/databricks/databricks-sdk-go/service/iam.PermissionLevel" + }, + "service_principal_name": { + "$ref": "#/$defs/string" + }, + "user_name": { + "$ref": "#/$defs/string" + } + }, + "additionalProperties": false, + "required": [ + "level" + ] + }, + { + "type": "string", + "pattern": "\\$\\{(var(\\.[a-zA-Z]+([-_]?[a-zA-Z0-9]+)*(\\[[0-9]+\\])*)+)\\}" + } + ] + }, "resources.Pipeline": { "oneOf": [ { @@ -1442,7 +1442,7 @@ "$ref": "#/$defs/github.com/databricks/cli/bundle/config/resources.Lifecycle" }, "permissions": { - "$ref": "#/$defs/slice/github.com/databricks/cli/bundle/config/resources.IamPermission" + "$ref": "#/$defs/slice/github.com/databricks/cli/bundle/config/resources.Permission" }, "pg_version": { "$ref": "#/$defs/int" @@ -2561,7 +2561,7 @@ }, "permissions": { "description": "The permissions for deploying and running the bundle in the target.", - "$ref": "#/$defs/slice/github.com/databricks/cli/bundle/config/resources.IamPermission" + "$ref": "#/$defs/slice/github.com/databricks/cli/bundle/config/resources.Permission" }, "presets": { "description": "The deployment presets for the target.", @@ -10932,12 +10932,12 @@ } ] }, - "resources.IamPermission": { + "resources.JobPermission": { "oneOf": [ { "type": "array", "items": { - "$ref": "#/$defs/github.com/databricks/cli/bundle/config/resources.IamPermission" + "$ref": "#/$defs/github.com/databricks/cli/bundle/config/resources.JobPermission" } }, { @@ -10946,12 +10946,12 @@ } ] }, - "resources.JobPermission": { + "resources.MlflowExperimentPermission": { "oneOf": [ { "type": "array", "items": { - "$ref": "#/$defs/github.com/databricks/cli/bundle/config/resources.JobPermission" + "$ref": "#/$defs/github.com/databricks/cli/bundle/config/resources.MlflowExperimentPermission" } }, { @@ -10960,12 +10960,12 @@ } ] }, - "resources.MlflowExperimentPermission": { + "resources.MlflowModelPermission": { "oneOf": [ { "type": "array", "items": { - "$ref": "#/$defs/github.com/databricks/cli/bundle/config/resources.MlflowExperimentPermission" + "$ref": "#/$defs/github.com/databricks/cli/bundle/config/resources.MlflowModelPermission" } }, { @@ -10974,12 +10974,12 @@ } ] }, - "resources.MlflowModelPermission": { + "resources.ModelServingEndpointPermission": { "oneOf": [ { "type": "array", "items": { - "$ref": "#/$defs/github.com/databricks/cli/bundle/config/resources.MlflowModelPermission" + "$ref": "#/$defs/github.com/databricks/cli/bundle/config/resources.ModelServingEndpointPermission" } }, { @@ -10988,12 +10988,12 @@ } ] }, - "resources.ModelServingEndpointPermission": { + "resources.Permission": { "oneOf": [ { "type": "array", "items": { - "$ref": "#/$defs/github.com/databricks/cli/bundle/config/resources.ModelServingEndpointPermission" + "$ref": "#/$defs/github.com/databricks/cli/bundle/config/resources.Permission" } }, { @@ -11646,7 +11646,7 @@ }, "permissions": { "description": "Defines a permission for a specific entity.", - "$ref": "#/$defs/slice/github.com/databricks/cli/bundle/config/resources.IamPermission", + "$ref": "#/$defs/slice/github.com/databricks/cli/bundle/config/resources.Permission", "markdownDescription": "A Sequence that defines the permissions to apply to experiments, jobs, pipelines, and models defined in the bundle, where each item in the sequence is a permission for a specific entity.\n\nSee [permissions](https://docs.databricks.com/dev-tools/bundles/settings.html#permissions) and [link](https://docs.databricks.com/dev-tools/bundles/permissions.html)." }, "presets": { diff --git a/bundle/schema/jsonschema_for_docs.json b/bundle/schema/jsonschema_for_docs.json index d5b4dfd6a5..a804c3cbd6 100644 --- a/bundle/schema/jsonschema_for_docs.json +++ b/bundle/schema/jsonschema_for_docs.json @@ -43,7 +43,7 @@ "x-since-version": "v0.279.0" }, "permissions": { - "$ref": "#/$defs/slice/github.com/databricks/cli/bundle/config/resources.Permission[github.com/databricks/databricks-sdk-go/service/iam.PermissionLevel]", + "$ref": "#/$defs/slice/github.com/databricks/cli/bundle/config/resources.Permission", "x-since-version": "v0.279.0" }, "query_text": { @@ -121,7 +121,7 @@ "x-since-version": "v0.239.0" }, "permissions": { - "$ref": "#/$defs/slice/github.com/databricks/cli/bundle/config/resources.Permission[github.com/databricks/databricks-sdk-go/service/apps.AppPermissionLevel]", + "$ref": "#/$defs/slice/github.com/databricks/cli/bundle/config/resources.AppPermission", "x-since-version": "v0.239.0" }, "resources": { @@ -189,6 +189,31 @@ "name" ] }, + "resources.AppPermission": { + "type": "object", + "properties": { + "group_name": { + "$ref": "#/$defs/string", + "x-since-version": "v0.247.0" + }, + "level": { + "$ref": "#/$defs/github.com/databricks/databricks-sdk-go/service/apps.AppPermissionLevel", + "x-since-version": "v0.247.0" + }, + "service_principal_name": { + "$ref": "#/$defs/string", + "x-since-version": "v0.247.0" + }, + "user_name": { + "$ref": "#/$defs/string", + "x-since-version": "v0.247.0" + } + }, + "additionalProperties": false, + "required": [ + "level" + ] + }, "resources.Catalog": { "type": "object", "properties": { @@ -355,7 +380,7 @@ "x-since-version": "v0.229.0" }, "permissions": { - "$ref": "#/$defs/slice/github.com/databricks/cli/bundle/config/resources.Permission[github.com/databricks/databricks-sdk-go/service/compute.ClusterPermissionLevel]", + "$ref": "#/$defs/slice/github.com/databricks/cli/bundle/config/resources.ClusterPermission", "x-since-version": "v0.229.0" }, "policy_id": { @@ -420,6 +445,31 @@ "additionalProperties": false, "markdownDescription": "The cluster resource defines an [all-purpose cluster](https://docs.databricks.com/api/workspace/clusters/create)." }, + "resources.ClusterPermission": { + "type": "object", + "properties": { + "group_name": { + "$ref": "#/$defs/string", + "x-since-version": "v0.247.0" + }, + "level": { + "$ref": "#/$defs/github.com/databricks/databricks-sdk-go/service/compute.ClusterPermissionLevel", + "x-since-version": "v0.247.0" + }, + "service_principal_name": { + "$ref": "#/$defs/string", + "x-since-version": "v0.247.0" + }, + "user_name": { + "$ref": "#/$defs/string", + "x-since-version": "v0.247.0" + } + }, + "additionalProperties": false, + "required": [ + "level" + ] + }, "resources.Dashboard": { "type": "object", "properties": { @@ -482,7 +532,7 @@ "x-since-version": "v0.234.0" }, "permissions": { - "$ref": "#/$defs/slice/github.com/databricks/cli/bundle/config/resources.Permission[github.com/databricks/databricks-sdk-go/service/iam.PermissionLevel]", + "$ref": "#/$defs/slice/github.com/databricks/cli/bundle/config/resources.Permission", "x-since-version": "v0.232.0" }, "serialized_dashboard": { @@ -584,7 +634,7 @@ "x-since-version": "v0.265.0" }, "permissions": { - "$ref": "#/$defs/slice/github.com/databricks/cli/bundle/config/resources.Permission[github.com/databricks/databricks-sdk-go/service/iam.PermissionLevel]", + "$ref": "#/$defs/slice/github.com/databricks/cli/bundle/config/resources.Permission", "x-since-version": "v0.265.0" }, "retention_window_in_days": { @@ -740,7 +790,7 @@ "x-since-version": "v0.241.0" }, "permissions": { - "$ref": "#/$defs/slice/github.com/databricks/cli/bundle/config/resources.Permission[github.com/databricks/databricks-sdk-go/service/jobs.JobPermissionLevel]", + "$ref": "#/$defs/slice/github.com/databricks/cli/bundle/config/resources.JobPermission", "x-since-version": "v0.229.0" }, "queue": { @@ -793,6 +843,31 @@ "additionalProperties": false, "markdownDescription": "The job resource allows you to define [jobs and their corresponding tasks](https://docs.databricks.com/api/workspace/jobs/create) in your bundle. For information about jobs, see [link](https://docs.databricks.com/jobs/index.html). For a tutorial that uses a Databricks Asset Bundles template to create a job, see [link](https://docs.databricks.com/dev-tools/bundles/jobs-tutorial.html)." }, + "resources.JobPermission": { + "type": "object", + "properties": { + "group_name": { + "$ref": "#/$defs/string", + "x-since-version": "v0.247.0" + }, + "level": { + "$ref": "#/$defs/github.com/databricks/databricks-sdk-go/service/jobs.JobPermissionLevel", + "x-since-version": "v0.247.0" + }, + "service_principal_name": { + "$ref": "#/$defs/string", + "x-since-version": "v0.247.0" + }, + "user_name": { + "$ref": "#/$defs/string", + "x-since-version": "v0.247.0" + } + }, + "additionalProperties": false, + "required": [ + "level" + ] + }, "resources.Lifecycle": { "type": "object", "properties": { @@ -823,7 +898,7 @@ "x-since-version": "v0.229.0" }, "permissions": { - "$ref": "#/$defs/slice/github.com/databricks/cli/bundle/config/resources.Permission[github.com/databricks/databricks-sdk-go/service/ml.ExperimentPermissionLevel]", + "$ref": "#/$defs/slice/github.com/databricks/cli/bundle/config/resources.MlflowExperimentPermission", "x-since-version": "v0.229.0" }, "tags": { @@ -838,6 +913,31 @@ ], "markdownDescription": "The experiment resource allows you to define [MLflow experiments](https://docs.databricks.com/api/workspace/experiments/createexperiment) in a bundle. For information about MLflow experiments, see [link](https://docs.databricks.com/mlflow/experiments.html)." }, + "resources.MlflowExperimentPermission": { + "type": "object", + "properties": { + "group_name": { + "$ref": "#/$defs/string", + "x-since-version": "v0.247.0" + }, + "level": { + "$ref": "#/$defs/github.com/databricks/databricks-sdk-go/service/ml.ExperimentPermissionLevel", + "x-since-version": "v0.247.0" + }, + "service_principal_name": { + "$ref": "#/$defs/string", + "x-since-version": "v0.247.0" + }, + "user_name": { + "$ref": "#/$defs/string", + "x-since-version": "v0.247.0" + } + }, + "additionalProperties": false, + "required": [ + "level" + ] + }, "resources.MlflowModel": { "type": "object", "properties": { @@ -857,7 +957,7 @@ "x-since-version": "v0.229.0" }, "permissions": { - "$ref": "#/$defs/slice/github.com/databricks/cli/bundle/config/resources.Permission[github.com/databricks/databricks-sdk-go/service/ml.RegisteredModelPermissionLevel]", + "$ref": "#/$defs/slice/github.com/databricks/cli/bundle/config/resources.MlflowModelPermission", "x-since-version": "v0.229.0" }, "tags": { @@ -872,6 +972,31 @@ ], "markdownDescription": "The model resource allows you to define [legacy models](https://docs.databricks.com/api/workspace/modelregistry/createmodel) in bundles. Databricks recommends you use Unity Catalog [registered models](https://docs.databricks.com/dev-tools/bundles/reference.html#registered-model) instead." }, + "resources.MlflowModelPermission": { + "type": "object", + "properties": { + "group_name": { + "$ref": "#/$defs/string", + "x-since-version": "v0.247.0" + }, + "level": { + "$ref": "#/$defs/github.com/databricks/databricks-sdk-go/service/ml.RegisteredModelPermissionLevel", + "x-since-version": "v0.247.0" + }, + "service_principal_name": { + "$ref": "#/$defs/string", + "x-since-version": "v0.247.0" + }, + "user_name": { + "$ref": "#/$defs/string", + "x-since-version": "v0.247.0" + } + }, + "additionalProperties": false, + "required": [ + "level" + ] + }, "resources.ModelServingEndpoint": { "type": "object", "properties": { @@ -910,7 +1035,7 @@ "x-since-version": "v0.229.0" }, "permissions": { - "$ref": "#/$defs/slice/github.com/databricks/cli/bundle/config/resources.Permission[github.com/databricks/databricks-sdk-go/service/serving.ServingEndpointPermissionLevel]", + "$ref": "#/$defs/slice/github.com/databricks/cli/bundle/config/resources.ModelServingEndpointPermission", "x-since-version": "v0.229.0" }, "rate_limits": { @@ -937,238 +1062,55 @@ ], "markdownDescription": "The model_serving_endpoint resource allows you to define [model serving endpoints](https://docs.databricks.com/api/workspace/servingendpoints/create). See [link](https://docs.databricks.com/machine-learning/model-serving/manage-serving-endpoints.html)." }, - "resources.Permission[github.com": { - "databricks": { - "databricks-sdk-go": { - "service": { - "apps.AppPermissionLevel]": { - "type": "object", - "properties": { - "group_name": { - "description": "The name of the group that has the permission set in level.", - "$ref": "#/$defs/string" - }, - "level": { - "description": "The allowed permission for user, group, service principal defined for this permission.", - "$ref": "#/$defs/github.com/databricks/databricks-sdk-go/service/apps.AppPermissionLevel" - }, - "service_principal_name": { - "description": "The name of the service principal that has the permission set in level.", - "$ref": "#/$defs/string" - }, - "user_name": { - "description": "The name of the user that has the permission set in level.", - "$ref": "#/$defs/string" - } - }, - "additionalProperties": false, - "required": [ - "level" - ] - }, - "compute.ClusterPermissionLevel]": { - "type": "object", - "properties": { - "group_name": { - "description": "The name of the group that has the permission set in level.", - "$ref": "#/$defs/string" - }, - "level": { - "description": "The allowed permission for user, group, service principal defined for this permission.", - "$ref": "#/$defs/github.com/databricks/databricks-sdk-go/service/compute.ClusterPermissionLevel" - }, - "service_principal_name": { - "description": "The name of the service principal that has the permission set in level.", - "$ref": "#/$defs/string" - }, - "user_name": { - "description": "The name of the user that has the permission set in level.", - "$ref": "#/$defs/string" - } - }, - "additionalProperties": false, - "required": [ - "level" - ] - }, - "iam.PermissionLevel]": { - "type": "object", - "properties": { - "group_name": { - "description": "The name of the group that has the permission set in level.", - "$ref": "#/$defs/string" - }, - "level": { - "description": "The allowed permission for user, group, service principal defined for this permission.", - "$ref": "#/$defs/github.com/databricks/databricks-sdk-go/service/iam.PermissionLevel" - }, - "service_principal_name": { - "description": "The name of the service principal that has the permission set in level.", - "$ref": "#/$defs/string" - }, - "user_name": { - "description": "The name of the user that has the permission set in level.", - "$ref": "#/$defs/string" - } - }, - "additionalProperties": false, - "required": [ - "level" - ] - }, - "jobs.JobPermissionLevel]": { - "type": "object", - "properties": { - "group_name": { - "description": "The name of the group that has the permission set in level.", - "$ref": "#/$defs/string" - }, - "level": { - "description": "The allowed permission for user, group, service principal defined for this permission.", - "$ref": "#/$defs/github.com/databricks/databricks-sdk-go/service/jobs.JobPermissionLevel" - }, - "service_principal_name": { - "description": "The name of the service principal that has the permission set in level.", - "$ref": "#/$defs/string" - }, - "user_name": { - "description": "The name of the user that has the permission set in level.", - "$ref": "#/$defs/string" - } - }, - "additionalProperties": false, - "required": [ - "level" - ] - }, - "ml.ExperimentPermissionLevel]": { - "type": "object", - "properties": { - "group_name": { - "description": "The name of the group that has the permission set in level.", - "$ref": "#/$defs/string" - }, - "level": { - "description": "The allowed permission for user, group, service principal defined for this permission.", - "$ref": "#/$defs/github.com/databricks/databricks-sdk-go/service/ml.ExperimentPermissionLevel" - }, - "service_principal_name": { - "description": "The name of the service principal that has the permission set in level.", - "$ref": "#/$defs/string" - }, - "user_name": { - "description": "The name of the user that has the permission set in level.", - "$ref": "#/$defs/string" - } - }, - "additionalProperties": false, - "required": [ - "level" - ] - }, - "ml.RegisteredModelPermissionLevel]": { - "type": "object", - "properties": { - "group_name": { - "description": "The name of the group that has the permission set in level.", - "$ref": "#/$defs/string" - }, - "level": { - "description": "The allowed permission for user, group, service principal defined for this permission.", - "$ref": "#/$defs/github.com/databricks/databricks-sdk-go/service/ml.RegisteredModelPermissionLevel" - }, - "service_principal_name": { - "description": "The name of the service principal that has the permission set in level.", - "$ref": "#/$defs/string" - }, - "user_name": { - "description": "The name of the user that has the permission set in level.", - "$ref": "#/$defs/string" - } - }, - "additionalProperties": false, - "required": [ - "level" - ] - }, - "pipelines.PipelinePermissionLevel]": { - "type": "object", - "properties": { - "group_name": { - "description": "The name of the group that has the permission set in level.", - "$ref": "#/$defs/string" - }, - "level": { - "description": "The allowed permission for user, group, service principal defined for this permission.", - "$ref": "#/$defs/github.com/databricks/databricks-sdk-go/service/pipelines.PipelinePermissionLevel" - }, - "service_principal_name": { - "description": "The name of the service principal that has the permission set in level.", - "$ref": "#/$defs/string" - }, - "user_name": { - "description": "The name of the user that has the permission set in level.", - "$ref": "#/$defs/string" - } - }, - "additionalProperties": false, - "required": [ - "level" - ] - }, - "serving.ServingEndpointPermissionLevel]": { - "type": "object", - "properties": { - "group_name": { - "description": "The name of the group that has the permission set in level.", - "$ref": "#/$defs/string" - }, - "level": { - "description": "The allowed permission for user, group, service principal defined for this permission.", - "$ref": "#/$defs/github.com/databricks/databricks-sdk-go/service/serving.ServingEndpointPermissionLevel" - }, - "service_principal_name": { - "description": "The name of the service principal that has the permission set in level.", - "$ref": "#/$defs/string" - }, - "user_name": { - "description": "The name of the user that has the permission set in level.", - "$ref": "#/$defs/string" - } - }, - "additionalProperties": false, - "required": [ - "level" - ] - }, - "sql.WarehousePermissionLevel]": { - "type": "object", - "properties": { - "group_name": { - "description": "The name of the group that has the permission set in level.", - "$ref": "#/$defs/string" - }, - "level": { - "description": "The allowed permission for user, group, service principal defined for this permission.", - "$ref": "#/$defs/github.com/databricks/databricks-sdk-go/service/sql.WarehousePermissionLevel" - }, - "service_principal_name": { - "description": "The name of the service principal that has the permission set in level.", - "$ref": "#/$defs/string" - }, - "user_name": { - "description": "The name of the user that has the permission set in level.", - "$ref": "#/$defs/string" - } - }, - "additionalProperties": false, - "required": [ - "level" - ] - } - } + "resources.ModelServingEndpointPermission": { + "type": "object", + "properties": { + "group_name": { + "$ref": "#/$defs/string", + "x-since-version": "v0.247.0" + }, + "level": { + "$ref": "#/$defs/github.com/databricks/databricks-sdk-go/service/serving.ServingEndpointPermissionLevel", + "x-since-version": "v0.247.0" + }, + "service_principal_name": { + "$ref": "#/$defs/string", + "x-since-version": "v0.247.0" + }, + "user_name": { + "$ref": "#/$defs/string", + "x-since-version": "v0.247.0" + } + }, + "additionalProperties": false, + "required": [ + "level" + ] + }, + "resources.Permission": { + "type": "object", + "properties": { + "group_name": { + "$ref": "#/$defs/string", + "x-since-version": "v0.229.0" + }, + "level": { + "$ref": "#/$defs/github.com/databricks/databricks-sdk-go/service/iam.PermissionLevel", + "x-since-version": "v0.229.0" + }, + "service_principal_name": { + "$ref": "#/$defs/string", + "x-since-version": "v0.229.0" + }, + "user_name": { + "$ref": "#/$defs/string", + "x-since-version": "v0.229.0" } - } + }, + "additionalProperties": false, + "required": [ + "level" + ] }, "resources.Pipeline": { "type": "object", @@ -1271,7 +1213,7 @@ "x-since-version": "v0.229.0" }, "permissions": { - "$ref": "#/$defs/slice/github.com/databricks/cli/bundle/config/resources.Permission[github.com/databricks/databricks-sdk-go/service/pipelines.PipelinePermissionLevel]", + "$ref": "#/$defs/slice/github.com/databricks/cli/bundle/config/resources.PipelinePermission", "x-since-version": "v0.229.0" }, "photon": { @@ -1340,6 +1282,31 @@ "additionalProperties": false, "markdownDescription": "The pipeline resource allows you to create Delta Live Tables [pipelines](https://docs.databricks.com/api/workspace/pipelines/create). For information about pipelines, see [link](https://docs.databricks.com/dlt/index.html). For a tutorial that uses the Databricks Asset Bundles template to create a pipeline, see [link](https://docs.databricks.com/dev-tools/bundles/pipelines-tutorial.html)." }, + "resources.PipelinePermission": { + "type": "object", + "properties": { + "group_name": { + "$ref": "#/$defs/string", + "x-since-version": "v0.247.0" + }, + "level": { + "$ref": "#/$defs/github.com/databricks/databricks-sdk-go/service/pipelines.PipelinePermissionLevel", + "x-since-version": "v0.247.0" + }, + "service_principal_name": { + "$ref": "#/$defs/string", + "x-since-version": "v0.247.0" + }, + "user_name": { + "$ref": "#/$defs/string", + "x-since-version": "v0.247.0" + } + }, + "additionalProperties": false, + "required": [ + "level" + ] + }, "resources.PostgresBranch": { "type": "object", "properties": { @@ -1476,7 +1443,7 @@ "x-since-version": "v0.287.0" }, "permissions": { - "$ref": "#/$defs/slice/github.com/databricks/cli/bundle/config/resources.Permission[github.com/databricks/databricks-sdk-go/service/iam.PermissionLevel]", + "$ref": "#/$defs/slice/github.com/databricks/cli/bundle/config/resources.Permission", "x-since-version": "v0.292.0" }, "pg_version": { @@ -1839,7 +1806,7 @@ "x-since-version": "v0.260.0" }, "permissions": { - "$ref": "#/$defs/slice/github.com/databricks/cli/bundle/config/resources.Permission[github.com/databricks/databricks-sdk-go/service/sql.WarehousePermissionLevel]", + "$ref": "#/$defs/slice/github.com/databricks/cli/bundle/config/resources.SqlWarehousePermission", "x-since-version": "v0.260.0" }, "spot_instance_policy": { @@ -1858,6 +1825,31 @@ }, "additionalProperties": false }, + "resources.SqlWarehousePermission": { + "type": "object", + "properties": { + "group_name": { + "$ref": "#/$defs/string", + "x-since-version": "v0.260.0" + }, + "level": { + "$ref": "#/$defs/github.com/databricks/databricks-sdk-go/service/sql.WarehousePermissionLevel", + "x-since-version": "v0.260.0" + }, + "service_principal_name": { + "$ref": "#/$defs/string", + "x-since-version": "v0.260.0" + }, + "user_name": { + "$ref": "#/$defs/string", + "x-since-version": "v0.260.0" + } + }, + "additionalProperties": false, + "required": [ + "level" + ] + }, "resources.SyncedDatabaseTable": { "type": "object", "description": "Next field marker: 18", @@ -2540,7 +2532,7 @@ }, "permissions": { "description": "The permissions for deploying and running the bundle in the target.", - "$ref": "#/$defs/slice/github.com/databricks/cli/bundle/config/resources.Permission[github.com/databricks/databricks-sdk-go/service/iam.PermissionLevel]", + "$ref": "#/$defs/slice/github.com/databricks/cli/bundle/config/resources.Permission", "x-since-version": "v0.229.0" }, "presets": { @@ -8954,66 +8946,52 @@ "$ref": "#/$defs/github.com/databricks/cli/bundle/config/resources.AppEnvVar" } }, - "resources.Permission[github.com": { - "databricks": { - "databricks-sdk-go": { - "service": { - "apps.AppPermissionLevel]": { - "type": "array", - "items": { - "$ref": "#/$defs/github.com/databricks/cli/bundle/config/resources.Permission[github.com/databricks/databricks-sdk-go/service/apps.AppPermissionLevel]" - } - }, - "compute.ClusterPermissionLevel]": { - "type": "array", - "items": { - "$ref": "#/$defs/github.com/databricks/cli/bundle/config/resources.Permission[github.com/databricks/databricks-sdk-go/service/compute.ClusterPermissionLevel]" - } - }, - "iam.PermissionLevel]": { - "type": "array", - "items": { - "$ref": "#/$defs/github.com/databricks/cli/bundle/config/resources.Permission[github.com/databricks/databricks-sdk-go/service/iam.PermissionLevel]" - } - }, - "jobs.JobPermissionLevel]": { - "type": "array", - "items": { - "$ref": "#/$defs/github.com/databricks/cli/bundle/config/resources.Permission[github.com/databricks/databricks-sdk-go/service/jobs.JobPermissionLevel]" - } - }, - "ml.ExperimentPermissionLevel]": { - "type": "array", - "items": { - "$ref": "#/$defs/github.com/databricks/cli/bundle/config/resources.Permission[github.com/databricks/databricks-sdk-go/service/ml.ExperimentPermissionLevel]" - } - }, - "ml.RegisteredModelPermissionLevel]": { - "type": "array", - "items": { - "$ref": "#/$defs/github.com/databricks/cli/bundle/config/resources.Permission[github.com/databricks/databricks-sdk-go/service/ml.RegisteredModelPermissionLevel]" - } - }, - "pipelines.PipelinePermissionLevel]": { - "type": "array", - "items": { - "$ref": "#/$defs/github.com/databricks/cli/bundle/config/resources.Permission[github.com/databricks/databricks-sdk-go/service/pipelines.PipelinePermissionLevel]" - } - }, - "serving.ServingEndpointPermissionLevel]": { - "type": "array", - "items": { - "$ref": "#/$defs/github.com/databricks/cli/bundle/config/resources.Permission[github.com/databricks/databricks-sdk-go/service/serving.ServingEndpointPermissionLevel]" - } - }, - "sql.WarehousePermissionLevel]": { - "type": "array", - "items": { - "$ref": "#/$defs/github.com/databricks/cli/bundle/config/resources.Permission[github.com/databricks/databricks-sdk-go/service/sql.WarehousePermissionLevel]" - } - } - } - } + "resources.AppPermission": { + "type": "array", + "items": { + "$ref": "#/$defs/github.com/databricks/cli/bundle/config/resources.AppPermission" + } + }, + "resources.ClusterPermission": { + "type": "array", + "items": { + "$ref": "#/$defs/github.com/databricks/cli/bundle/config/resources.ClusterPermission" + } + }, + "resources.JobPermission": { + "type": "array", + "items": { + "$ref": "#/$defs/github.com/databricks/cli/bundle/config/resources.JobPermission" + } + }, + "resources.MlflowExperimentPermission": { + "type": "array", + "items": { + "$ref": "#/$defs/github.com/databricks/cli/bundle/config/resources.MlflowExperimentPermission" + } + }, + "resources.MlflowModelPermission": { + "type": "array", + "items": { + "$ref": "#/$defs/github.com/databricks/cli/bundle/config/resources.MlflowModelPermission" + } + }, + "resources.ModelServingEndpointPermission": { + "type": "array", + "items": { + "$ref": "#/$defs/github.com/databricks/cli/bundle/config/resources.ModelServingEndpointPermission" + } + }, + "resources.Permission": { + "type": "array", + "items": { + "$ref": "#/$defs/github.com/databricks/cli/bundle/config/resources.Permission" + } + }, + "resources.PipelinePermission": { + "type": "array", + "items": { + "$ref": "#/$defs/github.com/databricks/cli/bundle/config/resources.PipelinePermission" } }, "resources.SecretScopePermission": { @@ -9021,6 +8999,12 @@ "items": { "$ref": "#/$defs/github.com/databricks/cli/bundle/config/resources.SecretScopePermission" } + }, + "resources.SqlWarehousePermission": { + "type": "array", + "items": { + "$ref": "#/$defs/github.com/databricks/cli/bundle/config/resources.SqlWarehousePermission" + } } }, "config.ArtifactFile": { @@ -9309,7 +9293,7 @@ }, "permissions": { "description": "Defines a permission for a specific entity.", - "$ref": "#/$defs/slice/github.com/databricks/cli/bundle/config/resources.Permission[github.com/databricks/databricks-sdk-go/service/iam.PermissionLevel]", + "$ref": "#/$defs/slice/github.com/databricks/cli/bundle/config/resources.Permission", "markdownDescription": "A Sequence that defines the permissions to apply to experiments, jobs, pipelines, and models defined in the bundle, where each item in the sequence is a permission for a specific entity.\n\nSee [permissions](https://docs.databricks.com/dev-tools/bundles/settings.html#permissions) and [link](https://docs.databricks.com/dev-tools/bundles/permissions.html).", "x-since-version": "v0.229.0" }, From 5a7137cbb79f66e7ea2ae485a3115f8080f2016f Mon Sep 17 00:00:00 2001 From: Denis Bilenko Date: Tue, 10 Mar 2026 16:51:18 +0100 Subject: [PATCH 24/28] Restore descriptions for resources.Permission in annotations Co-Authored-By: Claude Sonnet 4.6 --- bundle/internal/schema/annotations.yml | 13 +++++++++---- bundle/schema/jsonschema.json | 4 ++++ 2 files changed, 13 insertions(+), 4 deletions(-) diff --git a/bundle/internal/schema/annotations.yml b/bundle/internal/schema/annotations.yml index f1dda37485..d12e9ef16f 100644 --- a/bundle/internal/schema/annotations.yml +++ b/bundle/internal/schema/annotations.yml @@ -688,18 +688,23 @@ github.com/databricks/cli/bundle/config/resources.ModelServingEndpointPermission "description": |- PLACEHOLDER github.com/databricks/cli/bundle/config/resources.Permission: + "-": + "description": |- + Defines a permission for a specific entity. + "markdown_description": |- + Defines a permission for a specific entity. See [\_](/dev-tools/bundles/settings.md#permissions) and [\_](/dev-tools/bundles/permissions.md). "group_name": "description": |- - PLACEHOLDER + The name of the group that has the permission set in level. "level": "description": |- - PLACEHOLDER + The allowed permission for user, group, service principal defined for this permission. "service_principal_name": "description": |- - PLACEHOLDER + The name of the service principal that has the permission set in level. "user_name": "description": |- - PLACEHOLDER + The name of the user that has the permission set in level. github.com/databricks/cli/bundle/config/resources.PipelinePermission: "group_name": "description": |- diff --git a/bundle/schema/jsonschema.json b/bundle/schema/jsonschema.json index 408cf64e7c..ee53c6652f 100644 --- a/bundle/schema/jsonschema.json +++ b/bundle/schema/jsonschema.json @@ -1116,15 +1116,19 @@ "type": "object", "properties": { "group_name": { + "description": "The name of the group that has the permission set in level.", "$ref": "#/$defs/string" }, "level": { + "description": "The allowed permission for user, group, service principal defined for this permission.", "$ref": "#/$defs/github.com/databricks/databricks-sdk-go/service/iam.PermissionLevel" }, "service_principal_name": { + "description": "The name of the service principal that has the permission set in level.", "$ref": "#/$defs/string" }, "user_name": { + "description": "The name of the user that has the permission set in level.", "$ref": "#/$defs/string" } }, From d82dc7285e83302d492434d3fbd70ff7efdc9577 Mon Sep 17 00:00:00 2001 From: Denis Bilenko Date: Tue, 10 Mar 2026 16:53:12 +0100 Subject: [PATCH 25/28] update next changelog --- NEXT_CHANGELOG.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/NEXT_CHANGELOG.md b/NEXT_CHANGELOG.md index 6bf2f63608..df13a8d87e 100644 --- a/NEXT_CHANGELOG.md +++ b/NEXT_CHANGELOG.md @@ -6,7 +6,7 @@ ### Bundles * Modify grants to use SDK types ([#4666](https://github.com/databricks/cli/pull/4666)) -* Modify permissions to use a shared Permission type ([#4686](https://github.com/databricks/cli/pull/4686)) +* Modify permissions to use SDK types where available ([#4686](https://github.com/databricks/cli/pull/4686)) ### Dependency updates - Bump databricks-sdk-go from v0.112.0 to v0.117.0. From bff60d4ae2a92a22b305cf4bd482b12e2558e976 Mon Sep 17 00:00:00 2001 From: Denis Bilenko Date: Tue, 10 Mar 2026 16:58:19 +0100 Subject: [PATCH 26/28] Remove unnecessary Permission aliases and clean up _flatten_spec in Python codegen Co-Authored-By: Claude Sonnet 4.6 --- python/codegen/codegen/aliases_patch.py | 10 ---------- python/codegen/codegen/jsonschema.py | 10 ++-------- python/databricks/bundles/jobs/__init__.py | 7 ------- python/databricks/bundles/pipelines/__init__.py | 7 ------- 4 files changed, 2 insertions(+), 32 deletions(-) diff --git a/python/codegen/codegen/aliases_patch.py b/python/codegen/codegen/aliases_patch.py index b938eeef47..864f70afb9 100644 --- a/python/codegen/codegen/aliases_patch.py +++ b/python/codegen/codegen/aliases_patch.py @@ -22,14 +22,4 @@ "VolumeGrantPrivilege": "Privilege", "VolumeGrantPrivilegeParam": "PrivilegeParam", }, - "jobs": { - "Permission": "JobPermission", - "PermissionDict": "JobPermissionDict", - "PermissionParam": "JobPermissionParam", - }, - "pipelines": { - "Permission": "PipelinePermission", - "PermissionDict": "PipelinePermissionDict", - "PermissionParam": "PipelinePermissionParam", - }, } diff --git a/python/codegen/codegen/jsonschema.py b/python/codegen/codegen/jsonschema.py index a4786371d4..eff9da7c08 100644 --- a/python/codegen/codegen/jsonschema.py +++ b/python/codegen/codegen/jsonschema.py @@ -162,19 +162,13 @@ def get_schemas(): return output -def _is_schema(d: dict) -> bool: - """Check if a dict is a JSON schema definition (object or string type).""" - test = _unwrap_variable(d) or d - return test.get("type") in ("object", "string") - - def _flatten_spec(nested: dict, prefix: str = "") -> dict: - """Recursively flatten nested schema defs.""" result = {} for k, v in nested.items(): path = f"{prefix}/{k}" if prefix else k if isinstance(v, dict): - if _is_schema(v): + test = _unwrap_variable(v) or v + if test.get("type") in ("object", "string"): result[path] = v else: result.update(_flatten_spec(v, path)) diff --git a/python/databricks/bundles/jobs/__init__.py b/python/databricks/bundles/jobs/__init__.py index c408a6225f..3e98d2acd6 100644 --- a/python/databricks/bundles/jobs/__init__.py +++ b/python/databricks/bundles/jobs/__init__.py @@ -173,9 +173,6 @@ "PeriodicTriggerConfigurationParam", "PeriodicTriggerConfigurationTimeUnit", "PeriodicTriggerConfigurationTimeUnitParam", - "Permission", - "PermissionDict", - "PermissionParam", "PipelineParams", "PipelineParamsDict", "PipelineParamsParam", @@ -749,7 +746,3 @@ def _resolve_recursive_imports(): _resolve_recursive_imports() - -Permission = JobPermission -PermissionDict = JobPermissionDict -PermissionParam = JobPermissionParam diff --git a/python/databricks/bundles/pipelines/__init__.py b/python/databricks/bundles/pipelines/__init__.py index 7dc3bacf7d..2aef912fa7 100644 --- a/python/databricks/bundles/pipelines/__init__.py +++ b/python/databricks/bundles/pipelines/__init__.py @@ -90,9 +90,6 @@ "PathPattern", "PathPatternDict", "PathPatternParam", - "Permission", - "PermissionDict", - "PermissionParam", "Pipeline", "PipelineCluster", "PipelineClusterAutoscale", @@ -414,7 +411,3 @@ WorkspaceStorageInfoDict, WorkspaceStorageInfoParam, ) - -Permission = PipelinePermission -PermissionDict = PipelinePermissionDict -PermissionParam = PipelinePermissionParam From 1f1ae538a6290393c1e3cd3380fa087200449374 Mon Sep 17 00:00:00 2001 From: Denis Bilenko Date: Tue, 10 Mar 2026 17:03:54 +0100 Subject: [PATCH 27/28] Revert unnecessary Python codegen changes The spec is already flat with dotted keys; no logic changes needed. Co-Authored-By: Claude Sonnet 4.6 --- python/codegen/codegen/jsonschema.py | 15 +-------------- python/codegen/codegen/main.py | 2 +- python/codegen/codegen/packages.py | 9 ++++----- 3 files changed, 6 insertions(+), 20 deletions(-) diff --git a/python/codegen/codegen/jsonschema.py b/python/codegen/codegen/jsonschema.py index eff9da7c08..ceed9203e7 100644 --- a/python/codegen/codegen/jsonschema.py +++ b/python/codegen/codegen/jsonschema.py @@ -148,7 +148,7 @@ def get_schemas(): ) # we don't need all spec, only get supported types - flat_spec = {**_flatten_spec(sdk_types_spec), **_flatten_spec(resource_types_spec)} + flat_spec = {**sdk_types_spec, **resource_types_spec} flat_spec = { key: value for key, value in flat_spec.items() if packages.should_load_ref(key) } @@ -162,19 +162,6 @@ def get_schemas(): return output -def _flatten_spec(nested: dict, prefix: str = "") -> dict: - result = {} - for k, v in nested.items(): - path = f"{prefix}/{k}" if prefix else k - if isinstance(v, dict): - test = _unwrap_variable(v) or v - if test.get("type") in ("object", "string"): - result[path] = v - else: - result.update(_flatten_spec(v, path)) - return result - - def _get_spec_path(spec: dict, path: list[str]) -> dict: for key in path: spec = spec[key] diff --git a/python/codegen/codegen/main.py b/python/codegen/codegen/main.py index b1ba17dab4..d8764775c5 100644 --- a/python/codegen/codegen/main.py +++ b/python/codegen/codegen/main.py @@ -252,7 +252,7 @@ def _collect_reachable_schemas( if schema.type == openapi.SchemaType.OBJECT: for field in schema.properties.values(): if field.ref: - name = packages.get_schema_key(field.ref) + name = field.ref.split("/")[-1] if not include_private and field.stage == openapi.Stage.PRIVATE: continue diff --git a/python/codegen/codegen/packages.py b/python/codegen/codegen/packages.py index 7d1beb4992..9774911feb 100644 --- a/python/codegen/codegen/packages.py +++ b/python/codegen/codegen/packages.py @@ -44,12 +44,10 @@ ] -def get_schema_key(ref: str) -> str: - return ref.split("/")[-1] - - def get_class_name(ref: str) -> str: - name = ref.split("/")[-1].split(".")[-1] + name = ref.split("/")[-1] + name = name.split(".")[-1] + return RENAMES.get(name, name) @@ -76,6 +74,7 @@ def get_package(namespace: str, ref: str) -> Optional[str]: Returns Python package for a given OpenAPI ref. Returns None for builtin types. """ + full_name = ref.split("/")[-1] if full_name in PRIMITIVES: From 92607356717c48a1f3df732a0fb0174a333a4cd6 Mon Sep 17 00:00:00 2001 From: Denis Bilenko Date: Wed, 11 Mar 2026 12:02:54 +0100 Subject: [PATCH 28/28] add NEXT_CHANGELOG --- NEXT_CHANGELOG.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/NEXT_CHANGELOG.md b/NEXT_CHANGELOG.md index df13a8d87e..6df186e7f4 100644 --- a/NEXT_CHANGELOG.md +++ b/NEXT_CHANGELOG.md @@ -6,7 +6,7 @@ ### Bundles * Modify grants to use SDK types ([#4666](https://github.com/databricks/cli/pull/4666)) -* Modify permissions to use SDK types where available ([#4686](https://github.com/databricks/cli/pull/4686)) +* Modify permissions to use SDK types where available. This makes DABs validate permission levels, producing a warning on the unknown ones ([#4686](https://github.com/databricks/cli/pull/4686)) ### Dependency updates - Bump databricks-sdk-go from v0.112.0 to v0.117.0.