Skip to content

Add secret scopes support in assets bundling #2744

New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Merged
merged 40 commits into from
May 14, 2025
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
Show all changes
40 commits
Select commit Hold shift + click to select a range
97a9ed7
Add secret scopes support in assets bundling
anton-107 Apr 22, 2025
dbe8e11
test creating secrets in a newly created secret scope
anton-107 Apr 23, 2025
5116564
run `make docs`
anton-107 Apr 23, 2025
98e3587
run `make schema`
anton-107 Apr 23, 2025
2af6e75
fix TestTerraformToBundleEmptyRemoteResources
anton-107 Apr 23, 2025
88b7908
fix TestTerraformToBundleModifiedResources
anton-107 Apr 23, 2025
faa796d
fix TestTerraformToBundleEmptyLocalResources
anton-107 Apr 23, 2025
03bab9a
fix TestRunAsNoErrorForSupportedResources
anton-107 Apr 23, 2025
c7befba
fix TestRunAsErrorForUnsupportedResources
anton-107 Apr 23, 2025
3064404
fix TestAllResourcesMocked
anton-107 Apr 23, 2025
57c72bf
fix TestAllNonUcResourcesAreRenamed
anton-107 Apr 23, 2025
674823b
add bundle permissions support to secret scopes
anton-107 Apr 23, 2025
03740e0
implement Exists for SecretScopes
anton-107 Apr 23, 2025
8c33d55
remove URL field from SecretScope; fix lint
anton-107 Apr 23, 2025
f421f6d
list scopes and acls in the acceptance test
anton-107 Apr 25, 2025
defc01b
add acceptance test for binding secret scopes
anton-107 Apr 25, 2025
0452b85
[wip] todo statement to handle permissions
anton-107 Apr 29, 2025
3d2ea8b
add random suffix to the scope name in acceptance test
anton-107 May 1, 2025
af48f72
map secret scope permissions to secret acl
anton-107 May 1, 2025
d7e1ceb
make schema
anton-107 May 1, 2025
8a7b1cc
add a test for a secret scope backed by azure keyvault
anton-107 May 2, 2025
bcafeaa
add a test for a setting top level permissions to a secret scope
anton-107 May 2, 2025
9453ec9
sort requests output to make the permissions test deterministic
anton-107 May 2, 2025
9c03d00
disable local-only tests in the cloud; make cloud test deterministic
anton-107 May 2, 2025
c8b70a0
add enum values for resource.SecretScopePermissionLevel in jsonschema
anton-107 May 6, 2025
6abed4e
minor cleanup: remove debug line and fix spelling in the comment
anton-107 May 6, 2025
e8491c5
drop the initial_manage_principal field from SecretScope struct
anton-107 May 6, 2025
9668d17
make schema
anton-107 May 6, 2025
e53d070
make docs; fix lint
anton-107 May 6, 2025
f6cfd0a
add fields descriptions to the annotations.yml
anton-107 May 6, 2025
77dd526
make schema
anton-107 May 6, 2025
c8b49be
Update bundle/config/resources/secret_scope.go
anton-107 May 9, 2025
ecc1e07
add secret_scopes to the allow list for run_as
anton-107 May 9, 2025
66b9260
add fields directly to SecretScope struct without embedding *workspac…
anton-107 May 9, 2025
54fc36f
change the annotation for service_principal_name field for the secret…
anton-107 May 12, 2025
c920f61
add a comment on using list api to check if a scope exists
anton-107 May 12, 2025
ecb90df
fix NEXT_CHANGELOG.md bullet formatting
anton-107 May 12, 2025
ed2979a
add a comment on inlining fields to SecretScope struct
anton-107 May 12, 2025
056a1cf
make schema
anton-107 May 12, 2025
fe13c7e
fix bundle name in bundle/deployment/bind/secret-scope acc test
anton-107 May 14, 2025
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
1 change: 1 addition & 0 deletions NEXT_CHANGELOG.md
Original file line number Diff line number Diff line change
Expand Up @@ -15,5 +15,6 @@
* Fixed normalising requirements file path in dependencies section ([#2861](https://github.com/databricks/cli/pull/2861))
* Fix default-python template not to add environments when serverless=yes and include\_python=no ([#2866](https://github.com/databricks/cli/pull/2866))
* Fixed handling of Unicode characters in Python support ([#2873](https://github.com/databricks/cli/pull/2873))
* Added support for secret scopes in DABs ([#2744](https://github.com/databricks/cli/pull/2744))

### API Changes
Original file line number Diff line number Diff line change
@@ -0,0 +1,11 @@
bundle:
name: deploy-secret-scope-azure-backend

resources:
secret_scopes:
secret_scope_azure:
name: test-secrets-azure-backend
backend_type: "AZURE_KEYVAULT"
keyvault_metadata:
resource_id: my_azure_keyvault_id
dns_name: my_azure_keyvault_dns_name
16 changes: 16 additions & 0 deletions acceptance/bundle/deploy/secret-scope/backend-type/output.txt
Original file line number Diff line number Diff line change
@@ -0,0 +1,16 @@

>>> [CLI] bundle deploy
Uploading bundle files to /Workspace/Users/[USERNAME]/.bundle/deploy-secret-scope-azure-backend/default/files...
Deploying resources...
Updating deployment state...
Deployment complete!

>>> jq -s .[] | select(.path=="/api/2.0/secrets/scopes/create") | .body out.requests.txt
{
"backend_azure_keyvault": {
"dns_name": "my_azure_keyvault_dns_name",
"resource_id": "my_azure_keyvault_id"
},
"scope": "test-secrets-azure-backend",
"scope_backend_type": "AZURE_KEYVAULT"
}
3 changes: 3 additions & 0 deletions acceptance/bundle/deploy/secret-scope/backend-type/script
Original file line number Diff line number Diff line change
@@ -0,0 +1,3 @@
trace $CLI bundle deploy
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Although it's currently not forbidden to have nested tests, I'm thinking for making them illegal because I cannot figure out how to select one without running the other. So I'd suggest we avoid them unless you have an idea how to run outer without nested?

Example

# want to run only bundle/sync
~/work/cli/acceptance/bundle/sync % go test ../.. -run ^TestAccept$/^bundle$/^sync$ -v | grep PASS 
--- PASS: TestAccept (1.00s)
    --- PASS: TestAccept/bundle/sync/dryrun (0.61s)
    --- PASS: TestAccept/bundle/sync (1.02s)

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

i suggest to do this in a follow-up PR

Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Sure, I plan to update the update all nested tests not be nested and make test runner complain about it, so if you can take care of the ones you added, that would be appreciated.

trace jq -s '.[] | select(.path=="/api/2.0/secrets/scopes/create") | .body' out.requests.txt
rm out.requests.txt
Original file line number Diff line number Diff line change
@@ -0,0 +1,4 @@
Local = true
Cloud = false

RecordRequests = true
13 changes: 13 additions & 0 deletions acceptance/bundle/deploy/secret-scope/databricks.yml.tmpl
Original file line number Diff line number Diff line change
@@ -0,0 +1,13 @@
bundle:
name: deploy-secret-scope-test-$UNIQUE_NAME

resources:
secret_scopes:
secret_scope1:
name: $SECRET_SCOPE_NAME
backend_type: "DATABRICKS"
permissions:
- user_name: admins
level: WRITE
- user_name: users
level: READ
53 changes: 53 additions & 0 deletions acceptance/bundle/deploy/secret-scope/output.txt
Original file line number Diff line number Diff line change
@@ -0,0 +1,53 @@

>>> [CLI] bundle deploy
Uploading bundle files to /Workspace/Users/[USERNAME]/.bundle/deploy-secret-scope-test-[UNIQUE_NAME]/default/files...
Deploying resources...
Updating deployment state...
Deployment complete!

>>> [CLI] bundle summary --output json
{
"backend_type": "DATABRICKS",
"modified_status": "created",
"name": "my-secrets-[UUID]",
"permissions": [
{
"level": "WRITE",
"user_name": "admins"
},
{
"level": "READ",
"user_name": "users"
}
]
}

>>> [CLI] secrets list-scopes -o json
{
"backend_type": "DATABRICKS",
"name": "my-secrets-[UUID]"
}

>>> [CLI] secrets list-acls my-secrets-[UUID]
{"permission":"MANAGE","principal":"[USERNAME]"}
{"permission":"READ","principal":"users"}
{"permission":"WRITE","principal":"admins"}

>>> [CLI] secrets put-secret my-secrets-[UUID] my-key --string-value my-secret-value

>>> [CLI] secrets get-secret my-secrets-[UUID] my-key
{
"key":"my-key",
"value":"bXktc2VjcmV0LXZhbHVl"
}

>>> [CLI] bundle destroy --auto-approve
The following resources will be deleted:
delete secret_acl secret_acl_secret_scope1_0
delete secret_acl secret_acl_secret_scope1_1
delete secret_scope secret_scope1

All files and directories at the following location will be deleted: /Workspace/Users/[USERNAME]/.bundle/deploy-secret-scope-test-[UNIQUE_NAME]/default

Deleting files...
Destroy complete!
Original file line number Diff line number Diff line change
@@ -0,0 +1,15 @@
bundle:
name: deploy-secret-scope-with-permissions

resources:
secret_scopes:
secret_scope_azure:
name: test-secrets-permissions

permissions:
- user_name: $CURRENT_USER_NAME
level: CAN_MANAGE
- group_name: users
level: CAN_VIEW
- group_name: admins
level: CAN_MANAGE
10 changes: 10 additions & 0 deletions acceptance/bundle/deploy/secret-scope/permissions/output.txt
Original file line number Diff line number Diff line change
@@ -0,0 +1,10 @@

>>> [CLI] bundle deploy
Uploading bundle files to /Workspace/Users/[USERNAME]/.bundle/deploy-secret-scope-with-permissions/default/files...
Deploying resources...
Updating deployment state...
Deployment complete!

>>> jq -s -c .[] | select(.path=="/api/2.0/secrets/acls/put") | .body out.requests.txt
{"permission":"MANAGE","principal":"admins","scope":"test-secrets-permissions"}
{"permission":"READ","principal":"users","scope":"test-secrets-permissions"}
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Do you know why the current user is missing from the output here?

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

that's because dabs silently filters out all permissions for the current user name in

// The databricks terraform provider does not allow changing the permissions of
// current user. The current user is implied to be the owner of all deployed resources.
// This mutator removes the current user from the permissions of all resources.

Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Can you confirm this actually leads to the desired behavior here?

If I recall correctly, the integration test user is also a member of the "admins" group, so secret r/w will still be possible in the integration test, but may not be if you use a non-admin user to provision it.

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Secret scope api always give the caller the MANAGE permission if initial_manage_principal is omitted (and in dabs we always omit it). All the other permissions that we specify here give additional groups permission and they do not affect the permission that the caller has.

This is also shown by the bundle/deploy/secret-scope test in this PR

Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Thanks -- I mistakenly thought this was the output of the above, and thought the current user had their ACL stripped entirely, but this is the set of PUT requests from the client into the server.

>>> [CLI] secrets list-acls my-secrets-[UUID]
{"permission":"MANAGE","principal":"[USERNAME]"}
{"permission":"READ","principal":"users"}
{"permission":"WRITE","principal":"admins"}

4 changes: 4 additions & 0 deletions acceptance/bundle/deploy/secret-scope/permissions/script
Original file line number Diff line number Diff line change
@@ -0,0 +1,4 @@
envsubst < databricks.yml.tmpl > databricks.yml
trace $CLI bundle deploy #--log-level TRACE
trace jq -s -c '.[] | select(.path=="/api/2.0/secrets/acls/put") | .body' out.requests.txt | sort
rm out.requests.txt
6 changes: 6 additions & 0 deletions acceptance/bundle/deploy/secret-scope/permissions/test.toml
Original file line number Diff line number Diff line change
@@ -0,0 +1,6 @@
Local = true
Cloud = false
RecordRequests = true

[[Server]]
Pattern = "PUT /api/2.0/permissions/directories/{objectId}"
21 changes: 21 additions & 0 deletions acceptance/bundle/deploy/secret-scope/script
Original file line number Diff line number Diff line change
@@ -0,0 +1,21 @@
SECRET_SCOPE_NAME="my-secrets-$(uuid)"
if [ -z "$CLOUD_ENV" ]; then
SECRET_SCOPE_NAME="my-secrets-6260d50f-e8ff-4905-8f28-812345678903" # use hard-coded uuid when running locally
fi
export SECRET_SCOPE_NAME

envsubst < databricks.yml.tmpl > databricks.yml

cleanup() {
trace $CLI bundle destroy --auto-approve
}
trap cleanup EXIT

trace $CLI bundle deploy
trace $CLI bundle summary --output json | jq '.resources.secret_scopes.secret_scope1'
trace $CLI secrets list-scopes -o json | jq --arg value ${SECRET_SCOPE_NAME} '.[] | select(.name == $value)'

trace $CLI secrets list-acls ${SECRET_SCOPE_NAME} | jq -c '.[]' | sort

trace $CLI secrets put-secret ${SECRET_SCOPE_NAME} my-key --string-value "my-secret-value"
trace $CLI secrets get-secret ${SECRET_SCOPE_NAME} my-key
75 changes: 75 additions & 0 deletions acceptance/bundle/deploy/secret-scope/test.toml
Original file line number Diff line number Diff line change
@@ -0,0 +1,75 @@
Cloud = true
Local = true

Ignore = [
"databricks.yml",
]

[[Server]]
Pattern = "POST /api/2.0/secrets/scopes/create"
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

[optional] We have CRUD implementations in test server for various resources and it's not a lot of work but makes it usable for other tests. We definitely going to need it when we add tests for terraformless implementation.

https://github.com/databricks/cli/blob/main/acceptance/internal/handlers.go#L220
https://github.com/databricks/cli/tree/main/libs/testserver

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

i suggest to do this in a follow-up PR


[[Server]]
Pattern = "GET /api/2.0/secrets/scopes/list"
Response.Body = '''
{
"scopes": [
{
"backend_type": "DATABRICKS",
"name": "my-secrets-6260d50f-e8ff-4905-8f28-812345678903"
},
{
"backend_type": "AZURE_KEYVAULT",
"name": "test-secrets-azure-backend"
},
{
"backend_type": "DATABRICKS",
"name": "test-secrets-permissions"
}
]
}
'''

[[Server]]
Pattern = "POST /api/2.0/secrets/scopes/delete"

[[Server]]
Pattern = "POST /api/2.0/secrets/put"

[[Server]]
Pattern = "POST /api/2.0/secrets/acls/put"

[[Server]]
Pattern = "GET /api/2.0/secrets/get"
Response.Body = '''
{
"key":"my-key",
"value":"bXktc2VjcmV0LXZhbHVl"
}
'''

[[Server]]
Pattern = "GET /api/2.0/secrets/acls/list"
Response.Body = '''
{
"items": [
{
"permission": "READ",
"principal": "users"
},
{
"permission": "WRITE",
"principal": "admins"
},
{
"permission": "MANAGE",
"principal": "[USERNAME]"
}
]
}
'''

[[Server]]
Pattern = "GET /api/2.0/secrets/acls/get"

[[Server]]
Pattern = "POST /api/2.0/secrets/acls/delete"
Original file line number Diff line number Diff line change
@@ -0,0 +1,7 @@
bundle:
name: bind-secret-scope-test-$UNIQUE_NAME

resources:
secret_scopes:
secret_scope1:
name: $SECRET_SCOPE_NAME
35 changes: 35 additions & 0 deletions acceptance/bundle/deployment/bind/secret-scope/output.txt
Original file line number Diff line number Diff line change
@@ -0,0 +1,35 @@

>>> [CLI] secrets create-scope test-secret-scope-[UUID]

>>> [CLI] bundle deployment bind secret_scope1 test-secret-scope-[UUID] --auto-approve
Updating deployment state...
Successfully bound secret_scope with an id 'test-secret-scope-[UUID]'. Run 'bundle deploy' to deploy changes to your workspace

>>> [CLI] bundle deploy
Uploading bundle files to /Workspace/Users/[USERNAME]/.bundle/bind-secret-scope-test-[UNIQUE_NAME]/default/files...
Deploying resources...
Updating deployment state...
Deployment complete!

>>> [CLI] secrets list-scopes -o json
{
"backend_type": "DATABRICKS",
"name": "test-secret-scope-[UUID]"
}

>>> [CLI] bundle deployment unbind secret_scope1
Updating deployment state...

>>> [CLI] bundle destroy --auto-approve
All files and directories at the following location will be deleted: /Workspace/Users/[USERNAME]/.bundle/bind-secret-scope-test-[UNIQUE_NAME]/default

Deleting files...
Destroy complete!

>>> [CLI] secrets list-scopes -o json
{
"backend_type": "DATABRICKS",
"name": "test-secret-scope-[UUID]"
}

>>> [CLI] secrets delete-scope test-secret-scope-[UUID]
26 changes: 26 additions & 0 deletions acceptance/bundle/deployment/bind/secret-scope/script
Original file line number Diff line number Diff line change
@@ -0,0 +1,26 @@
SECRET_SCOPE_NAME="test-secret-scope-$(uuid)"
if [ -z "$CLOUD_ENV" ]; then
SECRET_SCOPE_NAME="test-secret-scope-6260d50f-e8ff-4905-8f28-812345678903" # use hard-coded uuid when running locally
fi
export SECRET_SCOPE_NAME
envsubst < databricks.yml.tmpl > databricks.yml

# Create a pre-defined volume:
trace $CLI secrets create-scope "${SECRET_SCOPE_NAME}"

cleanup() {
trace $CLI secrets delete-scope "${SECRET_SCOPE_NAME}"
}
trap cleanup EXIT

trace $CLI bundle deployment bind secret_scope1 "${SECRET_SCOPE_NAME}" --auto-approve

trace $CLI bundle deploy

trace $CLI secrets list-scopes -o json | jq --arg value ${SECRET_SCOPE_NAME} '.[] | select(.name == $value)'

trace $CLI bundle deployment unbind secret_scope1

trace $CLI bundle destroy --auto-approve

trace $CLI secrets list-scopes -o json | jq --arg value ${SECRET_SCOPE_NAME} '.[] | select(.name == $value)'
26 changes: 26 additions & 0 deletions acceptance/bundle/deployment/bind/secret-scope/test.toml
Original file line number Diff line number Diff line change
@@ -0,0 +1,26 @@
Local = true
Cloud = true
RequiresUnityCatalog = true

Ignore = [
"databricks.yml",
]

[[Server]]
Pattern = "POST /api/2.0/secrets/scopes/create"

[[Server]]
Pattern = "GET /api/2.0/secrets/scopes/list"
Response.Body = '''
{
"scopes": [
{
"backend_type": "DATABRICKS",
"name": "test-secret-scope-6260d50f-e8ff-4905-8f28-812345678903"
}
]
}
'''

[[Server]]
Pattern = "POST /api/2.0/secrets/scopes/delete"
Original file line number Diff line number Diff line change
Expand Up @@ -51,6 +51,10 @@ var (
permissions.CAN_MANAGE: "CAN_MANAGE",
permissions.CAN_VIEW: "CAN_USE",
},
"secret_scopes": {
permissions.CAN_MANAGE: "MANAGE",
permissions.CAN_VIEW: "READ",
},
}
)

Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -152,6 +152,11 @@ func mockBundle(mode config.Mode) *bundle.Bundle {
},
},
},
SecretScopes: map[string]*resources.SecretScope{
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

This test is a good candidate to be converted to acceptance tests (before or after this PR).

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

i suggest to do this in a follow-up PR

"secretScope1": {
Name: "secretScope1",
},
},
},
},
SyncRoot: vfs.MustNew("/Users/[email protected]"),
Expand Down Expand Up @@ -318,8 +323,8 @@ func TestAllNonUcResourcesAreRenamed(t *testing.T) {
nameField := resource.Elem().FieldByName("Name")
resourceType := resources.Type().Field(i).Name

// Skip apps, as they are not renamed
if resourceType == "Apps" {
// Skip resources that are not renamed
if resourceType == "Apps" || resourceType == "SecretScopes" {
continue
}

Expand Down
2 changes: 2 additions & 0 deletions bundle/config/mutator/resourcemutator/run_as_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -43,6 +43,7 @@ func allResourceTypes(t *testing.T) []string {
"quality_monitors",
"registered_models",
"schemas",
"secret_scopes",
"volumes",
},
resourceTypes,
Expand Down Expand Up @@ -142,6 +143,7 @@ var allowList = []string{
"registered_models",
"experiments",
"schemas",
"secret_scopes",
"volumes",
}

Expand Down
Loading
Loading