diff --git a/.nextchanges/bundles/job-runs-cmdio-resource-key.md b/.nextchanges/bundles/job-runs-cmdio-resource-key.md new file mode 100644 index 00000000000..4267d570cc1 --- /dev/null +++ b/.nextchanges/bundles/job-runs-cmdio-resource-key.md @@ -0,0 +1 @@ +direct: job_runs deploy progress lines now include the resource key (e.g. `Output from job_runs.foo: id=123: ...`) so concurrent runs are easier to tell apart. diff --git a/acceptance/bundle/resources/job_runs/basic/output.txt b/acceptance/bundle/resources/job_runs/basic/output.txt index ec091495cb0..c2060d422a8 100644 --- a/acceptance/bundle/resources/job_runs/basic/output.txt +++ b/acceptance/bundle/resources/job_runs/basic/output.txt @@ -34,8 +34,8 @@ Resources: >>> [CLI] bundle deploy Uploading bundle files to /Workspace/Users/[USERNAME]/.bundle/job-runs-basic/default/files... Deploying resources... -job run [MY_RUN_ID]: Run URL: [DATABRICKS_URL]/jobs/[MY_JOB_ID]/runs/[MY_RUN_ID]?o=[NUMID] -job run [MY_RUN_ID]: SUCCESS +Output from job_runs.my_run: id=[MY_RUN_ID]: Run URL: [DATABRICKS_URL]/jobs/[MY_JOB_ID]/runs/[MY_RUN_ID]?o=[NUMID] +Output from job_runs.my_run: id=[MY_RUN_ID]: SUCCESS Updating deployment state... Deployment complete! diff --git a/acceptance/bundle/resources/job_runs/failed_run/output.txt b/acceptance/bundle/resources/job_runs/failed_run/output.txt index c4e18e5c04a..449684e588a 100644 --- a/acceptance/bundle/resources/job_runs/failed_run/output.txt +++ b/acceptance/bundle/resources/job_runs/failed_run/output.txt @@ -3,7 +3,7 @@ >>> [CLI] bundle deploy Uploading bundle files to /Workspace/Users/[USERNAME]/.bundle/[UNIQUE_NAME]/files... Deploying resources... -job run [MY_RUN_ID]: Run URL: [RUN_URL] +Output from job_runs.my_run: id=[MY_RUN_ID]: Run URL: [RUN_URL] Error: cannot create resources.job_runs.my_run: waiting after creating id=[MY_RUN_ID]: run did not succeed: FAILED: Task main failed with message: Workload failed, see run output for details. task "main": RuntimeError: intentional failure run page: [RUN_URL] @@ -58,7 +58,7 @@ Updating deployment state... >>> [CLI] bundle deploy Uploading bundle files to /Workspace/Users/[USERNAME]/.bundle/[UNIQUE_NAME]/files... Deploying resources... -job run [MY_RUN_ID_2]: Run URL: [RUN_URL] +Output from job_runs.my_run: id=[MY_RUN_ID_2]: Run URL: [RUN_URL] Error: cannot recreate resources.job_runs.my_run: waiting after creating id=[MY_RUN_ID_2]: run did not succeed: FAILED: Task main failed with message: Workload failed, see run output for details. task "main": RuntimeError: intentional failure run page: [RUN_URL] diff --git a/acceptance/bundle/resources/job_runs/interrupted_run/output.txt b/acceptance/bundle/resources/job_runs/interrupted_run/output.txt index 4aed52de21f..4dad2071c5c 100644 --- a/acceptance/bundle/resources/job_runs/interrupted_run/output.txt +++ b/acceptance/bundle/resources/job_runs/interrupted_run/output.txt @@ -70,8 +70,8 @@ Exit code: 1 >>> [CLI] bundle deploy --plan tmp.plan.json Uploading bundle files to /Workspace/Users/[USERNAME]/.bundle/job-runs-interrupted-run/default/files... Deploying resources... -job run [MY_RUN_ID]: Run URL: [DATABRICKS_URL]/jobs/[MY_JOB_ID]/runs/[MY_RUN_ID]?o=[NUMID] -job run [MY_RUN_ID]: SUCCESS +Output from job_runs.my_run: id=[MY_RUN_ID]: Run URL: [DATABRICKS_URL]/jobs/[MY_JOB_ID]/runs/[MY_RUN_ID]?o=[NUMID] +Output from job_runs.my_run: id=[MY_RUN_ID]: SUCCESS Updating deployment state... Deployment complete! diff --git a/acceptance/bundle/resources/job_runs/job_parameters/output.txt b/acceptance/bundle/resources/job_runs/job_parameters/output.txt index d213b254d90..f07879acc78 100644 --- a/acceptance/bundle/resources/job_runs/job_parameters/output.txt +++ b/acceptance/bundle/resources/job_runs/job_parameters/output.txt @@ -3,8 +3,8 @@ >>> [CLI] bundle deploy Uploading bundle files to /Workspace/Users/[USERNAME]/.bundle/job-runs-job-parameters/default/files... Deploying resources... -job run [MY_RUN_ID]: Run URL: [DATABRICKS_URL]/jobs/[MY_JOB_ID]/runs/[MY_RUN_ID]?o=[NUMID] -job run [MY_RUN_ID]: SUCCESS +Output from job_runs.my_run: id=[MY_RUN_ID]: Run URL: [DATABRICKS_URL]/jobs/[MY_JOB_ID]/runs/[MY_RUN_ID]?o=[NUMID] +Output from job_runs.my_run: id=[MY_RUN_ID]: SUCCESS Updating deployment state... Deployment complete! diff --git a/acceptance/bundle/resources/job_runs/redeploy/output.txt b/acceptance/bundle/resources/job_runs/redeploy/output.txt index d75f19efb40..1c375bc0617 100644 --- a/acceptance/bundle/resources/job_runs/redeploy/output.txt +++ b/acceptance/bundle/resources/job_runs/redeploy/output.txt @@ -3,8 +3,8 @@ >>> [CLI] bundle deploy Uploading bundle files to /Workspace/Users/[USERNAME]/.bundle/job-runs-redeploy/default/files... Deploying resources... -job run [MY_RUN_ID]: Run URL: [DATABRICKS_URL]/jobs/[MY_JOB_ID]/runs/[MY_RUN_ID]?o=[NUMID] -job run [MY_RUN_ID]: SUCCESS +Output from job_runs.my_run: id=[MY_RUN_ID]: Run URL: [DATABRICKS_URL]/jobs/[MY_JOB_ID]/runs/[MY_RUN_ID]?o=[NUMID] +Output from job_runs.my_run: id=[MY_RUN_ID]: SUCCESS Updating deployment state... Deployment complete! @@ -96,8 +96,8 @@ Resources: >>> [CLI] bundle deploy Uploading bundle files to /Workspace/Users/[USERNAME]/.bundle/job-runs-redeploy/default/files... Deploying resources... -job run [MY_RUN_ID_2]: Run URL: [DATABRICKS_URL]/jobs/[MY_JOB_ID]/runs/[MY_RUN_ID_2]?o=[NUMID] -job run [MY_RUN_ID_2]: SUCCESS +Output from job_runs.my_run: id=[MY_RUN_ID_2]: Run URL: [DATABRICKS_URL]/jobs/[MY_JOB_ID]/runs/[MY_RUN_ID_2]?o=[NUMID] +Output from job_runs.my_run: id=[MY_RUN_ID_2]: SUCCESS Updating deployment state... Deployment complete! diff --git a/acceptance/bundle/resources/job_runs/retried_run_now/output.txt b/acceptance/bundle/resources/job_runs/retried_run_now/output.txt index 2811de8bb90..ffb26d1485b 100644 --- a/acceptance/bundle/resources/job_runs/retried_run_now/output.txt +++ b/acceptance/bundle/resources/job_runs/retried_run_now/output.txt @@ -3,8 +3,8 @@ >>> [CLI] bundle deploy Uploading bundle files to /Workspace/Users/[USERNAME]/.bundle/job-runs-retried-run-now/default/files... Deploying resources... -job run [MY_RUN_ID]: Run URL: [DATABRICKS_URL]/jobs/[MY_JOB_ID]/runs/[MY_RUN_ID]?o=[NUMID] -job run [MY_RUN_ID]: SUCCESS +Output from job_runs.my_run: id=[MY_RUN_ID]: Run URL: [DATABRICKS_URL]/jobs/[MY_JOB_ID]/runs/[MY_RUN_ID]?o=[NUMID] +Output from job_runs.my_run: id=[MY_RUN_ID]: SUCCESS Updating deployment state... Deployment complete! diff --git a/acceptance/bundle/resources/job_runs/wait/output.txt b/acceptance/bundle/resources/job_runs/wait/output.txt index b96624932a1..163818bc34c 100644 --- a/acceptance/bundle/resources/job_runs/wait/output.txt +++ b/acceptance/bundle/resources/job_runs/wait/output.txt @@ -3,8 +3,8 @@ >>> [CLI] bundle deploy Uploading bundle files to /Workspace/Users/[USERNAME]/.bundle/[UNIQUE_NAME]/files... Deploying resources... -job run [MY_RUN_ID]: Run URL: [RUN_URL] -job run [MY_RUN_ID]: SUCCESS +Output from job_runs.my_run: id=[MY_RUN_ID]: Run URL: [RUN_URL] +Output from job_runs.my_run: id=[MY_RUN_ID]: SUCCESS Updating deployment state... Deployment complete! diff --git a/bundle/direct/apply.go b/bundle/direct/apply.go index cbb0a2d45ff..81e2279630c 100644 --- a/bundle/direct/apply.go +++ b/bundle/direct/apply.go @@ -6,6 +6,7 @@ import ( "errors" "fmt" "reflect" + "strings" "github.com/databricks/cli/bundle/deployplan" "github.com/databricks/cli/bundle/direct/dresources" @@ -14,8 +15,14 @@ import ( "github.com/databricks/databricks-sdk-go/apierr" ) +func (d *DeploymentUnit) withResourceKey(ctx context.Context) context.Context { + // Match plan output (e.g. "job_runs.foo"), not the internal "resources." form. + return dresources.WithResourceKey(ctx, strings.TrimPrefix(d.ResourceKey, "resources.")) +} + func (d *DeploymentUnit) Destroy(ctx context.Context, db *dstate.DeploymentState) error { ctx = log.WithPrefix(ctx, "destroying "+d.ResourceKey) + ctx = d.withResourceKey(ctx) id := db.GetResourceID(d.ResourceKey) if id == "" { log.Infof(ctx, "Cannot delete %s: missing from state", d.ResourceKey) @@ -27,6 +34,7 @@ func (d *DeploymentUnit) Destroy(ctx context.Context, db *dstate.DeploymentState func (d *DeploymentUnit) Deploy(ctx context.Context, db *dstate.DeploymentState, newState any, actionType deployplan.ActionType, planEntry *deployplan.PlanEntry) error { ctx = log.WithPrefix(ctx, "deploying "+d.ResourceKey) + ctx = d.withResourceKey(ctx) if actionType == deployplan.Create { return d.Create(ctx, db, newState) } diff --git a/bundle/direct/dresources/job_run.go b/bundle/direct/dresources/job_run.go index ac549b876cd..b91a110511a 100644 --- a/bundle/direct/dresources/job_run.go +++ b/bundle/direct/dresources/job_run.go @@ -329,11 +329,13 @@ func runIsTerminal(state jobs.RunLifeCycleState) bool { state == jobs.RunLifeCycleStateInternalError } -// reportRunLine names the run, since resources deploy concurrently onto one stream. +// reportRunLine names the resource and run id so concurrent deploys stay readable. +// Deploy attaches the key via [WithResourceKey]. func reportRunLine(ctx context.Context, runID int64, msg string) { - if cmdio.HasIO(ctx) { - cmdio.LogString(ctx, fmt.Sprintf("job run %d: %s", runID, msg)) + if !cmdio.HasIO(ctx) { + return } + cmdio.LogString(ctx, fmt.Sprintf("Output from %s: id=%d: %s", ResourceKey(ctx), runID, msg)) } // DoUpdate finishes the wait an interrupted deploy abandoned. diff --git a/bundle/direct/dresources/job_run_test.go b/bundle/direct/dresources/job_run_test.go index 62a22a9745f..456b20f1903 100644 --- a/bundle/direct/dresources/job_run_test.go +++ b/bundle/direct/dresources/job_run_test.go @@ -11,6 +11,7 @@ import ( "time" "github.com/databricks/cli/bundle/config/resources" + "github.com/databricks/cli/libs/cmdio" "github.com/databricks/cli/libs/structs/structpath" "github.com/databricks/cli/libs/testserver" "github.com/databricks/databricks-sdk-go" @@ -79,6 +80,15 @@ func TestJobRunWaitSucceeds(t *testing.T) { assert.Equal(t, jobs.RunResultStateSuccess, remote.State.ResultState) } +func TestReportRunLineIncludesResourceKey(t *testing.T) { + ctx, stderr := cmdio.NewTestContextWithStderr(t.Context()) + ctx = WithResourceKey(ctx, "job_runs.my_run") + + reportRunLine(ctx, 123, "SUCCESS") + + assert.Equal(t, "Output from job_runs.my_run: id=123: SUCCESS\n", stderr.String()) +} + func TestJobRunWaitFailsOnFailedResult(t *testing.T) { client := jobRunClient(t, &jobs.RunState{ LifeCycleState: jobs.RunLifeCycleStateTerminated, diff --git a/bundle/direct/dresources/resource_key.go b/bundle/direct/dresources/resource_key.go new file mode 100644 index 00000000000..640c02243d4 --- /dev/null +++ b/bundle/direct/dresources/resource_key.go @@ -0,0 +1,17 @@ +package dresources + +import "context" + +type resourceKeyType struct{} + +// WithResourceKey attaches the bundle resource key used in cmdio progress lines. +// key is the plan form without the "resources." prefix (e.g. "job_runs.foo"). +func WithResourceKey(ctx context.Context, key string) context.Context { + return context.WithValue(ctx, resourceKeyType{}, key) +} + +// ResourceKey returns the key attached by [WithResourceKey], or "" if none. +func ResourceKey(ctx context.Context) string { + key, _ := ctx.Value(resourceKeyType{}).(string) + return key +} diff --git a/bundle/direct/dresources/resource_key_test.go b/bundle/direct/dresources/resource_key_test.go new file mode 100644 index 00000000000..b6e9c72b6eb --- /dev/null +++ b/bundle/direct/dresources/resource_key_test.go @@ -0,0 +1,13 @@ +package dresources + +import ( + "testing" + + "github.com/stretchr/testify/assert" +) + +func TestResourceKeyRoundTrip(t *testing.T) { + ctx := WithResourceKey(t.Context(), "job_runs.foo") + assert.Equal(t, "job_runs.foo", ResourceKey(ctx)) + assert.Empty(t, ResourceKey(t.Context())) +}