Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
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
3 changes: 2 additions & 1 deletion acceptance/acceptance_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -953,7 +953,8 @@ func runTest(t *testing.T,
cmd.Env = append(cmd.Env, "CLOUD_ENV_BASE="+envBase)

// Must be added PrepareReplacementsUser, otherwise conflicts with [USERNAME]
testdiff.PrepareReplacementsUUID(t, &repls)
// UniqueUUID defaults to false: number distinct UUIDs only when a test opts in.
testdiff.PrepareReplacementsUUID(t, &repls, isTruePtr(config.UniqueUUID))

// User replacements:
repls.Repls = append(repls.Repls, config.Repls...)
Expand Down
40 changes: 31 additions & 9 deletions acceptance/bin/repls.py
Original file line number Diff line number Diff line change
Expand Up @@ -29,7 +29,7 @@ def read_entries():


def read_repls():
"""Return (pattern, replacement) pairs in the order they must be applied."""
"""Return (pattern, replacement, distinct) tuples in application order."""
entries = []

for item in read_entries():
Expand All @@ -38,35 +38,57 @@ def read_repls():
literal = item.get("Literal")

if literal is None:
# "Distinct" is not honoured here; unlike the harness, we do not number the matches.
entries.append((order, item["Old"], new))
entries.append((order, item["Old"], new, item["Distinct"]))
continue

# Set() in libs/testdiff also registers the JSON-encoded form of the value, so that
# values with quotes or backslashes are replaced inside JSON output as well.
encoded = json.dumps(literal, ensure_ascii=False)[1:-1]
if encoded != literal:
entries.append((order, re.escape(encoded), new))
entries.append((order, re.escape(literal), new))
entries.append((order, re.escape(encoded), new, False))
entries.append((order, re.escape(literal), new, False))

# Stable sort: replacements with the same order are applied in the order they were added.
entries.sort(key=lambda entry: entry[0])

return [(old, new) for _, old, new in entries]
return [(old, new, distinct) for _, old, new, distinct in entries]


def compile_repls():
"""Same as read_repls(), with patterns compiled. Invalid patterns are reported and skipped."""
result = []
for old, new in read_repls():
for old, new, distinct in read_repls():
try:
result.append((re.compile(old), new))
result.append((re.compile(old), new, distinct))
except re.error as e:
print(f"Regex error for pattern {old}: {e}", file=sys.stderr)
return result


def replace_all(patterns, s):
for comp, new in patterns:
r"""Apply replacements with the same per-output numbering as the Go harness.

>>> distinct = [(re.compile(r"item-\w+"), "[ITEM]", True)]
>>> replace_all(distinct, "item-b item-a item-b")
'[ITEM][0] [ITEM][1] [ITEM][0]'
>>> replace_all(distinct, "item-a item-a")
'[ITEM] [ITEM]'
>>> replace_all(distinct, "no matches")
'no matches'
>>> replace_all(distinct, "[ITEM][0] [ITEM][1] [ITEM][0]")
'[ITEM][0] [ITEM][1] [ITEM][0]'
>>> replace_all([(re.compile(r"item-\w+"), "[ITEM]", False)], "item-a item-b")
'[ITEM] [ITEM]'
"""
for comp, new, distinct in patterns:
if distinct:
seen = {}
for match in comp.finditer(s):
value = match.group(0)
if value not in seen:
seen[value] = len(seen)
if len(seen) > 1:
s = comp.sub(lambda match, new=new, seen=seen: match.expand(new) + f"[{seen[match.group(0)]}]", s)
continue
s = comp.sub(new, s)
return s
Original file line number Diff line number Diff line change
Expand Up @@ -6,15 +6,15 @@
"browse_only": false,
"catalog_name": "main",
"catalog_type": "MANAGED_CATALOG",
"created_at": [UNIX_TIME_MILLIS][0],
"created_at": [UNIX_TIME_MILLIS],
"created_by": "[USERNAME]",
"enable_predictive_optimization": "INHERIT",
"full_name": "main.schema-[UNIQUE_NAME]",
"metastore_id": "[UUID]",
"name": "schema-[UNIQUE_NAME]",
"owner": "[USERNAME]",
"schema_id": "[UUID]",
"updated_at": [UNIX_TIME_MILLIS][0],
"updated_at": [UNIX_TIME_MILLIS],
"updated_by": "[USERNAME]"
}

Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -6,15 +6,15 @@
"browse_only": false,
"catalog_name": "main",
"catalog_type": "MANAGED_CATALOG",
"created_at": [UNIX_TIME_MILLIS][0],
"created_at": [UNIX_TIME_MILLIS],
"created_by": "[USERNAME]",
"enable_predictive_optimization": "INHERIT",
"full_name": "main.schema-[UNIQUE_NAME]",
"metastore_id": "[UUID]",
"name": "schema-[UNIQUE_NAME]",
"owner": "[USERNAME]",
"schema_id": "[UUID]",
"updated_at": [UNIX_TIME_MILLIS][0],
"updated_at": [UNIX_TIME_MILLIS],
"updated_by": "[USERNAME]"
}

Expand Down
46 changes: 46 additions & 0 deletions acceptance/bundle/deploy/immutable/no-artifacts/out.plan.txt
Original file line number Diff line number Diff line change
@@ -0,0 +1,46 @@

>>> [CLI] bundle plan -o json

>>> jq .plan["resources.jobs.my_job"].new_state.value.tasks plan.json
[
{
"notebook_task": {
"base_parameters": {
"path": "/Workspace/Users/[UUID]/.snapshots/[SNAPSHOT_HASH]/[SNAPSHOT_HASH]-[UUID]/snapshot/files/some_path"
},
"notebook_path": "/Workspace/Users/[UUID]/.snapshots/[SNAPSHOT_HASH]/[SNAPSHOT_HASH]-[UUID]/snapshot/files/src/notebook"
},
"task_key": "notebook_task"
},
{
"environment_key": "env",
"spark_python_task": {
"python_file": "/Workspace/Users/[UUID]/.snapshots/[SNAPSHOT_HASH]/[SNAPSHOT_HASH]-[UUID]/snapshot/files/src/main.py"
},
"task_key": "spark_python_task"
}
]

>>> jq .plan["resources.internal_immutable_snapshots.immutable"] plan.json
{
"action": "create",
"new_state": {
"value": {
"relative_path": "[SNAPSHOT_HASH]/[SNAPSHOT_HASH]-[UUID]",
"full_path": "/Workspace/Users/[UUID]/.snapshots/[SNAPSHOT_HASH]/[SNAPSHOT_HASH]-[UUID]/snapshot",
"bundle_id": "[SNAPSHOT_HASH]",
"acl": [
{
"user_name": "[USERNAME]",
"permission_level": "CAN_READ"
}
],
"can_manage": [
{
"service_principal_name": "[USERNAME]"
}
],
"zip_path": "[TEST_TMP_DIR]/.databricks/bundle/default/snapshots/[SNAPSHOT_HASH]-[UUID].zip"
}
}
}
46 changes: 0 additions & 46 deletions acceptance/bundle/deploy/immutable/no-artifacts/output.txt
Original file line number Diff line number Diff line change
Expand Up @@ -8,52 +8,6 @@ Workspace:

Validation OK!

>>> [CLI] bundle plan -o json

>>> jq .plan["resources.jobs.my_job"].new_state.value.tasks plan.json
[
{
"notebook_task": {
"base_parameters": {
"path": "/Workspace/Users/[UUID]/.snapshots/[SNAPSHOT_HASH]/[SNAPSHOT_HASH]-[UUID]/snapshot/files/some_path"
},
"notebook_path": "/Workspace/Users/[UUID]/.snapshots/[SNAPSHOT_HASH]/[SNAPSHOT_HASH]-[UUID]/snapshot/files/src/notebook"
},
"task_key": "notebook_task"
},
{
"environment_key": "env",
"spark_python_task": {
"python_file": "/Workspace/Users/[UUID]/.snapshots/[SNAPSHOT_HASH]/[SNAPSHOT_HASH]-[UUID]/snapshot/files/src/main.py"
},
"task_key": "spark_python_task"
}
]

>>> jq .plan["resources.internal_immutable_snapshots.immutable"] plan.json
{
"action": "create",
"new_state": {
"value": {
"relative_path": "[SNAPSHOT_HASH]/[SNAPSHOT_HASH]-[UUID]",
"full_path": "/Workspace/Users/[UUID]/.snapshots/[SNAPSHOT_HASH]/[SNAPSHOT_HASH]-[UUID]/snapshot",
"bundle_id": "[SNAPSHOT_HASH]",
"acl": [
{
"user_name": "[USERNAME]",
"permission_level": "CAN_READ"
}
],
"can_manage": [
{
"service_principal_name": "[USERNAME]"
}
],
"zip_path": "[TEST_TMP_DIR]/.databricks/bundle/default/snapshots/[SNAPSHOT_HASH]-[UUID].zip"
}
}
}

>>> [CLI] bundle deploy
Created internal_immutable_snapshots.immutable
Created jobs.my_job
Expand Down
10 changes: 7 additions & 3 deletions acceptance/bundle/deploy/immutable/no-artifacts/script
Original file line number Diff line number Diff line change
@@ -1,9 +1,13 @@
envsubst < databricks.yml.tmpl > databricks.yml

trace $CLI bundle validate
trace $CLI bundle plan -o json > plan.json
trace jq '.plan["resources.jobs.my_job"].new_state.value.tasks' plan.json
trace jq '.plan["resources.internal_immutable_snapshots.immutable"]' plan.json
# A direct deploy generates a new snapshot UUID, while --plan reuses the planned
# one. Keep the initial plan in its own golden so both variants agree per file.
{
trace $CLI bundle plan -o json > plan.json
trace jq '.plan["resources.jobs.my_job"].new_state.value.tasks' plan.json
trace jq '.plan["resources.internal_immutable_snapshots.immutable"]' plan.json
} > out.plan.txt 2>&1

trace $CLI bundle deploy $(readplanarg plan.json)

Expand Down
8 changes: 4 additions & 4 deletions acceptance/bundle/deploy/readplan/whl-upload/output.txt
Original file line number Diff line number Diff line change
@@ -1,7 +1,7 @@

=== bundle deploy uploads the wheel
>>> [CLI] bundle deploy
Uploading .databricks/bundle/default/patched_wheels/my_test_code_my_test_code/my_test_code-0.0.1+[UNIX_TIME_NANOS][0]-py3-none-any.whl...
Uploading .databricks/bundle/default/patched_wheels/my_test_code_my_test_code/my_test_code-0.0.1+[UNIX_TIME_NANOS]-py3-none-any.whl...
Uploading bundle files to /Workspace/Users/[USERNAME]/.bundle/whl-upload/default/files...
Created jobs.my_job
Files: 2 uploaded, 0 deleted
Expand All @@ -10,7 +10,7 @@ Resources: 1 created, 0 changed, 0 deleted, 0 unchanged
>>> print_requests.py //artifacts/.internal --del-field raw_body
{
"method": "POST",
"path": "/api/2.0/workspace-files/import-file/Workspace/Users/[USERNAME]/.bundle/whl-upload/default/artifacts/.internal/my_test_code-0.0.1+[UNIX_TIME_NANOS][0]-py3-none-any.whl",
"path": "/api/2.0/workspace-files/import-file/Workspace/Users/[USERNAME]/.bundle/whl-upload/default/artifacts/.internal/my_test_code-0.0.1+[UNIX_TIME_NANOS]-py3-none-any.whl",
"q": {
"overwrite": "true"
}
Expand All @@ -26,7 +26,7 @@ Destroy: 1 deleted

=== bundle deploy --plan also uploads the wheel
>>> [CLI] bundle deploy --plan tmp.plan.json
Uploading .databricks/bundle/default/patched_wheels/my_test_code_my_test_code/my_test_code-0.0.1+[UNIX_TIME_NANOS][0]-py3-none-any.whl...
Uploading .databricks/bundle/default/patched_wheels/my_test_code_my_test_code/my_test_code-0.0.1+[UNIX_TIME_NANOS]-py3-none-any.whl...
Uploading bundle files to /Workspace/Users/[USERNAME]/.bundle/whl-upload/default/files...
Created jobs.my_job
Files: 4 uploaded, 0 deleted
Expand All @@ -35,7 +35,7 @@ Resources: 1 created, 0 changed, 0 deleted, 0 unchanged
>>> print_requests.py //artifacts/.internal --del-field raw_body
{
"method": "POST",
"path": "/api/2.0/workspace-files/import-file/Workspace/Users/[USERNAME]/.bundle/whl-upload/default/artifacts/.internal/my_test_code-0.0.1+[UNIX_TIME_NANOS][0]-py3-none-any.whl",
"path": "/api/2.0/workspace-files/import-file/Workspace/Users/[USERNAME]/.bundle/whl-upload/default/artifacts/.internal/my_test_code-0.0.1+[UNIX_TIME_NANOS]-py3-none-any.whl",
"q": {
"overwrite": "true"
}
Expand Down
Original file line number Diff line number Diff line change
@@ -1,7 +1,7 @@

>>> [CLI] bundle deploy
Building my_test_code...
Uploading .databricks/bundle/default/patched_wheels/my_test_code_my_test_code/my_test_code-0.0.1+[UNIX_TIME_NANOS][0]-py3-none-any.whl...
Uploading .databricks/bundle/default/patched_wheels/my_test_code_my_test_code/my_test_code-0.0.1+[UNIX_TIME_NANOS]-py3-none-any.whl...
Uploading bundle files to /Workspace/Users/[USERNAME]/.bundle/[UNIQUE_NAME]/files...
Created jobs.some_other_job
Files: 13 uploaded, 0 deleted
Expand All @@ -10,7 +10,7 @@ Resources: 1 created, 0 changed, 0 deleted, 0 unchanged
=== The environment dependency keeps its [train] extras suffix
>>> [CLI] jobs get [SOME_OTHER_JOB_ID]
[
"/Workspace/Users/[USERNAME]/.bundle/[UNIQUE_NAME]/artifacts/.internal/my_test_code-0.0.1+[UNIX_TIME_NANOS][0]-py3-none-any.whl[train]"
"/Workspace/Users/[USERNAME]/.bundle/[UNIQUE_NAME]/artifacts/.internal/my_test_code-0.0.1+[UNIX_TIME_NANOS]-py3-none-any.whl[train]"
]

>>> [CLI] bundle run some_other_job
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -50,7 +50,7 @@
"job_cluster_key": "job_cluster",
"libraries": [
{
"whl": "/Workspace/Users/[USERNAME]/.bundle/my_default_python/dev/artifacts/.internal/my_default_python-0.0.1+[UNIX_TIME_NANOS][0]-py3-none-any.whl"
"whl": "/Workspace/Users/[USERNAME]/.bundle/my_default_python/dev/artifacts/.internal/my_default_python-0.0.1+[UNIX_TIME_NANOS]-py3-none-any.whl"
}
],
"notebook_task": {
Expand All @@ -67,7 +67,7 @@
"job_cluster_key": "job_cluster",
"libraries": [
{
"whl": "/Workspace/Users/[USERNAME]/.bundle/my_default_python/dev/artifacts/.internal/my_default_python-0.0.1+[UNIX_TIME_NANOS][0]-py3-none-any.whl"
"whl": "/Workspace/Users/[USERNAME]/.bundle/my_default_python/dev/artifacts/.internal/my_default_python-0.0.1+[UNIX_TIME_NANOS]-py3-none-any.whl"
}
],
"python_wheel_task": {
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -188,7 +188,7 @@
"provider_config": [],
"pypi": [],
"requirements": "",
"whl": "/Workspace/Users/[USERNAME]/.bundle/my_default_python/dev/artifacts/.internal/my_default_python-0.0.1+[UNIX_TIME_NANOS][0]-py3-none-any.whl"
"whl": "/Workspace/Users/[USERNAME]/.bundle/my_default_python/dev/artifacts/.internal/my_default_python-0.0.1+[UNIX_TIME_NANOS]-py3-none-any.whl"
}
],
"max_retries": 0,
Expand Down Expand Up @@ -262,7 +262,7 @@
"provider_config": [],
"pypi": [],
"requirements": "",
"whl": "/Workspace/Users/[USERNAME]/.bundle/my_default_python/dev/artifacts/.internal/my_default_python-0.0.1+[UNIX_TIME_NANOS][0]-py3-none-any.whl"
"whl": "/Workspace/Users/[USERNAME]/.bundle/my_default_python/dev/artifacts/.internal/my_default_python-0.0.1+[UNIX_TIME_NANOS]-py3-none-any.whl"
}
],
"max_retries": 0,
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -172,7 +172,7 @@
"browse_only": false,
"catalog_name": "main",
"comment": "mycomment",
"created_at": [UNIX_TIME_MILLIS][0],
"created_at": [UNIX_TIME_MILLIS],
"created_by": "[USERNAME]",
"full_name": "main.schema_grants.mymodel",
"id": "main.schema_grants.mymodel",
Expand All @@ -186,7 +186,7 @@
],
"schema_name": "schema_grants",
"storage_location": null,
"updated_at": [UNIX_TIME_MILLIS][0],
"updated_at": [UNIX_TIME_MILLIS],
"updated_by": "[USERNAME]"
},
"sensitive_attributes": [],
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -65,8 +65,8 @@
"serialized_dashboard": {
"action": "skip",
"reason": "etag_based",
"old": "sha256:[HASH][0]",
"new": "sha256:[HASH][0]",
"old": "sha256:[HASH]",
"new": "sha256:[HASH]",
"remote": "{}\n"
},
"update_time": {
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -18,15 +18,15 @@ Resources: 2 created, 0 changed, 0 deleted, 0 unchanged
"method": "POST",
"path": "/api/2.2/jobs/run-now",
"body": {
"idempotency_token": "[IDEMPOTENCY_TOKEN][0]",
"idempotency_token": "[IDEMPOTENCY_TOKEN]",
"job_id": [MY_JOB_ID]
}
}
{
"method": "POST",
"path": "/api/2.2/jobs/run-now",
"body": {
"idempotency_token": "[IDEMPOTENCY_TOKEN][0]",
"idempotency_token": "[IDEMPOTENCY_TOKEN]",
"job_id": [MY_JOB_ID]
}
}
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -5,11 +5,11 @@ json.serial = 1;
json.plan.resources.model_serving_endpoints.my_endpoint.action = "recreate";
json.plan.resources.model_serving_endpoints.my_endpoint.new_state.value.name = "[ENDPOINT_NAME_2]";
json.plan.resources.model_serving_endpoints.my_endpoint.remote_state.description = "";
json.plan.resources.model_serving_endpoints.my_endpoint.remote_state.endpoint_details.creation_timestamp = [UNIX_TIME_MILLIS][0];
json.plan.resources.model_serving_endpoints.my_endpoint.remote_state.endpoint_details.creation_timestamp = [UNIX_TIME_MILLIS];
json.plan.resources.model_serving_endpoints.my_endpoint.remote_state.endpoint_details.creator = "[USERNAME]";
json.plan.resources.model_serving_endpoints.my_endpoint.remote_state.endpoint_details.description = "";
json.plan.resources.model_serving_endpoints.my_endpoint.remote_state.endpoint_details.id = "[ENDPOINT_ID_1]";
json.plan.resources.model_serving_endpoints.my_endpoint.remote_state.endpoint_details.last_updated_timestamp = [UNIX_TIME_MILLIS][0];
json.plan.resources.model_serving_endpoints.my_endpoint.remote_state.endpoint_details.last_updated_timestamp = [UNIX_TIME_MILLIS];
json.plan.resources.model_serving_endpoints.my_endpoint.remote_state.endpoint_details.name = "[ENDPOINT_NAME_1]";
json.plan.resources.model_serving_endpoints.my_endpoint.remote_state.endpoint_details.permission_level = "CAN_MANAGE";
json.plan.resources.model_serving_endpoints.my_endpoint.remote_state.endpoint_details.route_optimized = false;
Expand Down
Loading
Loading