diff --git a/acceptance/bundle/resources/pipelines/drift/parameters/databricks.yml b/acceptance/bundle/resources/pipelines/drift/parameters/databricks.yml new file mode 100644 index 0000000000..2e7d3d1bee --- /dev/null +++ b/acceptance/bundle/resources/pipelines/drift/parameters/databricks.yml @@ -0,0 +1,12 @@ +bundle: + name: pipeline-parameters + +resources: + pipelines: + my: + name: test-pipeline + parameters: + my_param: my_value + libraries: + - file: + path: "./foo.py" diff --git a/acceptance/bundle/resources/pipelines/drift/parameters/foo.py b/acceptance/bundle/resources/pipelines/drift/parameters/foo.py new file mode 100644 index 0000000000..e69de29bb2 diff --git a/acceptance/bundle/resources/pipelines/drift/parameters/out.test.toml b/acceptance/bundle/resources/pipelines/drift/parameters/out.test.toml new file mode 100644 index 0000000000..1a3e24fa57 --- /dev/null +++ b/acceptance/bundle/resources/pipelines/drift/parameters/out.test.toml @@ -0,0 +1,5 @@ +Local = true +Cloud = false +GOOSOnPR.darwin = false +GOOSOnPR.windows = false +EnvMatrix.DATABRICKS_BUNDLE_ENGINE = ["direct"] diff --git a/acceptance/bundle/resources/pipelines/drift/parameters/output.txt b/acceptance/bundle/resources/pipelines/drift/parameters/output.txt new file mode 100644 index 0000000000..9043ddc35b --- /dev/null +++ b/acceptance/bundle/resources/pipelines/drift/parameters/output.txt @@ -0,0 +1,29 @@ + +=== Initial deployment +>>> [CLI] bundle deploy +Uploading bundle files to /Workspace/Users/[USERNAME]/.bundle/pipeline-parameters/default/files... +Deploying resources... +Updating deployment state... +Deployment complete! + +=== Plan is a no-op: pipeline parameters round-trip on read +>>> [CLI] bundle plan +Plan: 0 to add, 0 to change, 0 to delete, 1 unchanged + +=== Redeploy is a no-op (no update call) +>>> [CLI] bundle deploy +Uploading bundle files to /Workspace/Users/[USERNAME]/.bundle/pipeline-parameters/default/files... +Deploying resources... +Updating deployment state... +Deployment complete! + +>>> print_requests.py //api/2.0/pipelines +json.method = "POST"; +json.path = "/api/2.0/pipelines"; +json.body.channel = "CURRENT"; +json.body.deployment.kind = "BUNDLE"; +json.body.deployment.metadata_file_path = "/Workspace/Users/[USERNAME]/.bundle/pipeline-parameters/default/state/metadata.json"; +json.body.edition = "ADVANCED"; +json.body.libraries[0].file.path = "/Workspace/Users/[USERNAME]/.bundle/pipeline-parameters/default/files/foo.py"; +json.body.name = "test-pipeline"; +json.body.parameters.my_param = "my_value"; diff --git a/acceptance/bundle/resources/pipelines/drift/parameters/script b/acceptance/bundle/resources/pipelines/drift/parameters/script new file mode 100644 index 0000000000..6773f24891 --- /dev/null +++ b/acceptance/bundle/resources/pipelines/drift/parameters/script @@ -0,0 +1,11 @@ +echo "*" > .gitignore + +title "Initial deployment" +trace $CLI bundle deploy + +title "Plan is a no-op: pipeline parameters round-trip on read" +trace $CLI bundle plan | contains.py "Plan: 0 to add, 0 to change, 0 to delete, 1 unchanged" + +title "Redeploy is a no-op (no update call)" +trace $CLI bundle deploy +trace print_requests.py //api/2.0/pipelines | gron.py | contains.py '!json.method = "PUT"' diff --git a/acceptance/bundle/resources/pipelines/drift/parameters/test.toml b/acceptance/bundle/resources/pipelines/drift/parameters/test.toml new file mode 100644 index 0000000000..90122785fc --- /dev/null +++ b/acceptance/bundle/resources/pipelines/drift/parameters/test.toml @@ -0,0 +1,5 @@ +RecordRequests = true + +# Direct engine only: the gap is in its read-back path (GetPipelineResponse.Parameters). +# Terraform tracks parameters in its own state. Mirrors catalogs/drift/managed_properties. +EnvMatrix.DATABRICKS_BUNDLE_ENGINE = ["direct"] diff --git a/libs/testserver/pipelines.go b/libs/testserver/pipelines.go index cc639387b6..3512c8e5f5 100644 --- a/libs/testserver/pipelines.go +++ b/libs/testserver/pipelines.go @@ -52,6 +52,18 @@ func (s *FakeWorkspace) PipelineCreate(req Request) Response { var r pipelines.GetPipelineResponse r.Spec = &spec + // parameters is not on PipelineSpec (only on CreatePipeline), so the decode above + // drops it. The backend echoes it on GetPipelineResponse.Parameters; mirror that + // here, else a re-read misses it and the CLI plans a perpetual update. + var create pipelines.CreatePipeline + if err := json.Unmarshal(req.Body, &create); err != nil { + return Response{ + Body: fmt.Sprintf("cannot unmarshal request body: %s", err), + StatusCode: 400, + } + } + r.Parameters = create.Parameters + pipelineId := nextUUID() r.PipelineId = pipelineId r.CreatorUserName = "tester@databricks.com" @@ -100,7 +112,18 @@ func (s *FakeWorkspace) PipelineUpdate(req Request, pipelineId string) Response } } + // parameters is on EditPipeline, not PipelineSpec; round-trip it like + // PipelineCreate does. + var edit pipelines.EditPipeline + if err := json.Unmarshal(req.Body, &edit); err != nil { + return Response{ + Body: fmt.Sprintf("internal error: %s", err), + StatusCode: 400, + } + } + item.Spec = &spec + item.Parameters = edit.Parameters setSpecDefaults(&spec, pipelineId) s.Pipelines[pipelineId] = item