diff --git a/acceptance/bundle/deploy/yaml-sync-empty-grants/databricks.yml b/acceptance/bundle/deploy/yaml-sync-empty-grants/databricks.yml new file mode 100644 index 00000000000..87958c8e630 --- /dev/null +++ b/acceptance/bundle/deploy/yaml-sync-empty-grants/databricks.yml @@ -0,0 +1,9 @@ +bundle: + name: yaml-sync-empty-grants + +resources: + schemas: + schema1: + name: myschema + catalog_name: main + grants: [] diff --git a/acceptance/bundle/deploy/yaml-sync-empty-grants/out.test.toml b/acceptance/bundle/deploy/yaml-sync-empty-grants/out.test.toml new file mode 100644 index 00000000000..65156e0457c --- /dev/null +++ b/acceptance/bundle/deploy/yaml-sync-empty-grants/out.test.toml @@ -0,0 +1,3 @@ +Local = true +Cloud = false +EnvMatrix.DATABRICKS_BUNDLE_ENGINE = ["terraform"] diff --git a/acceptance/bundle/deploy/yaml-sync-empty-grants/output.txt b/acceptance/bundle/deploy/yaml-sync-empty-grants/output.txt new file mode 100644 index 00000000000..fa2643665d8 --- /dev/null +++ b/acceptance/bundle/deploy/yaml-sync-empty-grants/output.txt @@ -0,0 +1,8 @@ + +>>> [CLI] bundle deploy +Uploading bundle files to /Workspace/Users/[USERNAME]/.bundle/yaml-sync-empty-grants/default/files... +Deploying resources... +Updating deployment state... +Warn: Failed to create config snapshot: state conversion failed +Warn: Config snapshot: state entry not found for "resources.schemas.schema1.grants" +Deployment complete! diff --git a/acceptance/bundle/deploy/yaml-sync-empty-grants/script b/acceptance/bundle/deploy/yaml-sync-empty-grants/script new file mode 100644 index 00000000000..68ebb78d775 --- /dev/null +++ b/acceptance/bundle/deploy/yaml-sync-empty-grants/script @@ -0,0 +1 @@ +trace $CLI bundle deploy diff --git a/acceptance/bundle/deploy/yaml-sync-empty-grants/test.toml b/acceptance/bundle/deploy/yaml-sync-empty-grants/test.toml new file mode 100644 index 00000000000..5799c23b638 --- /dev/null +++ b/acceptance/bundle/deploy/yaml-sync-empty-grants/test.toml @@ -0,0 +1,6 @@ +# The YAML-sync state upload only runs for the terraform engine; no direct-engine variant. +[Env] +DATABRICKS_BUNDLE_ENABLE_EXPERIMENTAL_YAML_SYNC = "true" + +[EnvMatrix] +DATABRICKS_BUNDLE_ENGINE = ["terraform"] diff --git a/bundle/statemgmt/upload_state_for_yaml_sync.go b/bundle/statemgmt/upload_state_for_yaml_sync.go index 0399c7b31ff..6573d1dc56f 100644 --- a/bundle/statemgmt/upload_state_for_yaml_sync.go +++ b/bundle/statemgmt/upload_state_for_yaml_sync.go @@ -54,6 +54,20 @@ func (m *uploadStateForYamlSync) Apply(ctx context.Context, b *bundle.Bundle) di return nil } + // convertState reuses direct-engine code that reports failures via logdiag, + // and this mutator must not fail a deploy that already succeeded. + ctx = logdiag.IsolatedContext(ctx) + logdiag.SetCollect(ctx, true) + defer func() { + for _, d := range logdiag.FlushCollected(ctx) { + msg := d.Summary + if d.Detail != "" { + msg += ": " + d.Detail + } + log.Warnf(ctx, "Config snapshot: %s", msg) + } + }() + _, snapshotPath := b.StateFilenameConfigSnapshot(ctx) created, err := m.convertState(ctx, b, snapshotPath) @@ -202,6 +216,12 @@ func (m *uploadStateForYamlSync) convertState(ctx context.Context, b *bundle.Bun return false, err } + // Apply reports failures via logdiag instead of returning an error. Don't + // upload a snapshot that is missing entries for the failed resources. + if logdiag.HasError(ctx) { + return false, errors.New("state conversion failed") + } + return true, nil } diff --git a/libs/logdiag/logdiag.go b/libs/logdiag/logdiag.go index 896d105751b..28ed3b5ba21 100644 --- a/libs/logdiag/logdiag.go +++ b/libs/logdiag/logdiag.go @@ -48,6 +48,12 @@ func InitContext(ctx context.Context) context.Context { if ok { panic("internal error: must not call InitContext() twice") } + return IsolatedContext(ctx) +} + +// IsolatedContext returns a child context with a fresh diagnostics state; +// diagnostics logged through it do not affect the parent's. +func IsolatedContext(ctx context.Context) context.Context { val := LogDiagData{ TargetSeverity: 255, mu: &sync.Mutex{}, diff --git a/libs/logdiag/logdiag_test.go b/libs/logdiag/logdiag_test.go new file mode 100644 index 00000000000..ecf12ee78d1 --- /dev/null +++ b/libs/logdiag/logdiag_test.go @@ -0,0 +1,24 @@ +package logdiag_test + +import ( + "errors" + "testing" + + "github.com/databricks/cli/libs/logdiag" + "github.com/stretchr/testify/assert" +) + +func TestIsolatedContext(t *testing.T) { + ctx := logdiag.InitContext(t.Context()) + logdiag.SetCollect(ctx, true) + + isolated := logdiag.IsolatedContext(ctx) + logdiag.SetCollect(isolated, true) + logdiag.LogError(isolated, errors.New("inner failure")) + + assert.True(t, logdiag.HasError(isolated)) + assert.Len(t, logdiag.FlushCollected(isolated), 1) + + assert.False(t, logdiag.HasError(ctx)) + assert.Empty(t, logdiag.FlushCollected(ctx)) +}