Skip to content
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

Handle ${workspace.file_path} references in source-linked deployments #2046

Merged
merged 10 commits into from
Jan 8, 2025
22 changes: 0 additions & 22 deletions bundle/config/mutator/apply_presets.go
Original file line number Diff line number Diff line change
Expand Up @@ -9,7 +9,6 @@ import (

"github.com/databricks/cli/bundle"
"github.com/databricks/cli/bundle/config"
"github.com/databricks/cli/libs/dbr"
"github.com/databricks/cli/libs/diag"
"github.com/databricks/cli/libs/dyn"
"github.com/databricks/cli/libs/textutil"
Expand Down Expand Up @@ -222,27 +221,6 @@ func (m *applyPresets) Apply(ctx context.Context, b *bundle.Bundle) diag.Diagnos
dashboard.DisplayName = prefix + dashboard.DisplayName
}

if config.IsExplicitlyEnabled((b.Config.Presets.SourceLinkedDeployment)) {
isDatabricksWorkspace := dbr.RunsOnRuntime(ctx) && strings.HasPrefix(b.SyncRootPath, "/Workspace/")
if !isDatabricksWorkspace {
target := b.Config.Bundle.Target
path := dyn.NewPath(dyn.Key("targets"), dyn.Key(target), dyn.Key("presets"), dyn.Key("source_linked_deployment"))
diags = diags.Append(
diag.Diagnostic{
Severity: diag.Warning,
Summary: "source-linked deployment is available only in the Databricks Workspace",
Paths: []dyn.Path{
path,
},
Locations: b.Config.GetLocations(path[2:].String()),
},
)

disabled := false
b.Config.Presets.SourceLinkedDeployment = &disabled
}
}

return diags
}

Expand Down
88 changes: 0 additions & 88 deletions bundle/config/mutator/apply_presets_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -2,16 +2,12 @@ package mutator_test

import (
"context"
"runtime"
"testing"

"github.com/databricks/cli/bundle"
"github.com/databricks/cli/bundle/config"
"github.com/databricks/cli/bundle/config/mutator"
"github.com/databricks/cli/bundle/config/resources"
"github.com/databricks/cli/bundle/internal/bundletest"
"github.com/databricks/cli/libs/dbr"
"github.com/databricks/cli/libs/dyn"
"github.com/databricks/databricks-sdk-go/service/catalog"
"github.com/databricks/databricks-sdk-go/service/jobs"
"github.com/stretchr/testify/require"
Expand Down Expand Up @@ -398,87 +394,3 @@ func TestApplyPresetsResourceNotDefined(t *testing.T) {
})
}
}

func TestApplyPresetsSourceLinkedDeployment(t *testing.T) {
if runtime.GOOS == "windows" {
t.Skip("this test is not applicable on Windows because source-linked mode works only in the Databricks Workspace")
}

testContext := context.Background()
enabled := true
disabled := false
workspacePath := "/Workspace/user.name@company.com"

tests := []struct {
bundlePath string
ctx context.Context
name string
initialValue *bool
expectedValue *bool
expectedWarning string
}{
{
name: "preset enabled, bundle in Workspace, databricks runtime",
bundlePath: workspacePath,
ctx: dbr.MockRuntime(testContext, true),
initialValue: &enabled,
expectedValue: &enabled,
},
{
name: "preset enabled, bundle not in Workspace, databricks runtime",
bundlePath: "/Users/user.name@company.com",
ctx: dbr.MockRuntime(testContext, true),
initialValue: &enabled,
expectedValue: &disabled,
expectedWarning: "source-linked deployment is available only in the Databricks Workspace",
},
{
name: "preset enabled, bundle in Workspace, not databricks runtime",
bundlePath: workspacePath,
ctx: dbr.MockRuntime(testContext, false),
initialValue: &enabled,
expectedValue: &disabled,
expectedWarning: "source-linked deployment is available only in the Databricks Workspace",
},
{
name: "preset disabled, bundle in Workspace, databricks runtime",
bundlePath: workspacePath,
ctx: dbr.MockRuntime(testContext, true),
initialValue: &disabled,
expectedValue: &disabled,
},
{
name: "preset nil, bundle in Workspace, databricks runtime",
bundlePath: workspacePath,
ctx: dbr.MockRuntime(testContext, true),
initialValue: nil,
expectedValue: nil,
},
}

for _, tt := range tests {
t.Run(tt.name, func(t *testing.T) {
b := &bundle.Bundle{
SyncRootPath: tt.bundlePath,
Config: config.Root{
Presets: config.Presets{
SourceLinkedDeployment: tt.initialValue,
},
},
}

bundletest.SetLocation(b, "presets.source_linked_deployment", []dyn.Location{{File: "databricks.yml"}})
diags := bundle.Apply(tt.ctx, b, mutator.ApplyPresets())
if diags.HasError() {
t.Fatalf("unexpected error: %v", diags)
}

if tt.expectedWarning != "" {
require.Equal(t, tt.expectedWarning, diags[0].Summary)
require.NotEmpty(t, diags[0].Locations)
}

require.Equal(t, tt.expectedValue, b.Config.Presets.SourceLinkedDeployment)
})
}
}
75 changes: 75 additions & 0 deletions bundle/config/mutator/apply_source_linked_deployment_preset.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,75 @@
package mutator

import (
"context"
"strings"

"github.com/databricks/cli/bundle"
"github.com/databricks/cli/bundle/config"
"github.com/databricks/cli/libs/dbr"
"github.com/databricks/cli/libs/diag"
"github.com/databricks/cli/libs/dyn"
)

type applySourceLinkedDeploymentPreset struct{}

// Apply source-linked deployment preset
func ApplySourceLinkedDeploymentPreset() *applySourceLinkedDeploymentPreset {
return &applySourceLinkedDeploymentPreset{}
}

func (m *applySourceLinkedDeploymentPreset) Name() string {
return "ApplySourceLinkedDeploymentPreset"
}

func (m *applySourceLinkedDeploymentPreset) Apply(ctx context.Context, b *bundle.Bundle) diag.Diagnostics {
if config.IsExplicitlyDisabled(b.Config.Presets.SourceLinkedDeployment) {
return nil
}

var diags diag.Diagnostics
isDatabricksWorkspace := dbr.RunsOnRuntime(ctx) && strings.HasPrefix(b.SyncRootPath, "/Workspace/")
target := b.Config.Bundle.Target

if config.IsExplicitlyEnabled((b.Config.Presets.SourceLinkedDeployment)) {
if !isDatabricksWorkspace {
path := dyn.NewPath(dyn.Key("targets"), dyn.Key(target), dyn.Key("presets"), dyn.Key("source_linked_deployment"))
Copy link
Contributor

Choose a reason for hiding this comment

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

This won't work -- the targets field is cleared after selecting it.

But because the target is merged into the main configuration, you can use presets.source_linked_deployment directly and it will return the original location.

diags = diags.Append(
diag.Diagnostic{
Severity: diag.Warning,
Summary: "source-linked deployment is available only in the Databricks Workspace",
Paths: []dyn.Path{
path,
},
Locations: b.Config.GetLocations(path[2:].String()),
},
)

disabled := false
b.Config.Presets.SourceLinkedDeployment = &disabled
Copy link
Contributor

Choose a reason for hiding this comment

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

SInce you're mutating the config here, you'd have to call b.Config.Mutate and do all the mutation inside the callback there

Copy link
Contributor Author

@ilyakuz-db ilyakuz-db Jan 6, 2025

Choose a reason for hiding this comment

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

Thanks, I will update it! Could you elaborate what is the difference between this mutation and normal assignment to struct. Seems that it works good in both cases but I may miss some context

Copy link
Contributor Author

Choose a reason for hiding this comment

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

Discussed with Pieter that it's better to skip dynamic assignment here in favor of simplicity, but I can update it in another PR if it doesn't feel right

return diags
}
}

if isDatabricksWorkspace && b.Config.Bundle.Mode == config.Development {
enabled := true
b.Config.Presets.SourceLinkedDeployment = &enabled
}

if b.Config.Workspace.FilePath != "" && config.IsExplicitlyEnabled(b.Config.Presets.SourceLinkedDeployment) {
Copy link
Contributor Author

Choose a reason for hiding this comment

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

Added warning for cases when workspace.file_path is defined by user but deploy is running in source-linked mode

Copy link
Contributor

Choose a reason for hiding this comment

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

Please include a comment saying that this mutator runs before these fields are defaulted. I was puzzled by this but then found it is run before the mutators that populate its default value.

path := dyn.NewPath(dyn.Key("targets"), dyn.Key(target), dyn.Key("workspace"), dyn.Key("file_path"))
Copy link
Contributor

Choose a reason for hiding this comment

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

Same issue as above w.r.t. the path not existing.

Copy link
Contributor Author

Choose a reason for hiding this comment

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

Seems like it works fine, or do we expect other behavior here?

image image

Copy link
Contributor

Choose a reason for hiding this comment

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

Ah, I was confused by the path including targets.<target>.

This call site and the one above index it with path[2:].

It looks like it is not used in full, so the first two elements can be removed from the assignment.


diags = diags.Append(
diag.Diagnostic{
Severity: diag.Warning,
Summary: "workspace.file_path setting will be ignored in source-linked deployment mode",
Copy link
Contributor

Choose a reason for hiding this comment

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

This warning should have a Detail section explaining the issue further.

Paths: []dyn.Path{
path[2:],
},
Locations: b.Config.GetLocations(path[2:].String()),
},
)
}

return diags
}
122 changes: 122 additions & 0 deletions bundle/config/mutator/apply_source_linked_deployment_preset_test.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,122 @@
package mutator_test

import (
"context"
"runtime"
"testing"

"github.com/databricks/cli/bundle"
"github.com/databricks/cli/bundle/config"
"github.com/databricks/cli/bundle/config/mutator"
"github.com/databricks/cli/bundle/internal/bundletest"
"github.com/databricks/cli/libs/dbr"
"github.com/databricks/cli/libs/dyn"
"github.com/stretchr/testify/require"
)

func TestApplyPresetsSourceLinkedDeployment(t *testing.T) {
if runtime.GOOS == "windows" {
t.Skip("this test is not applicable on Windows because source-linked mode works only in the Databricks Workspace")
}

testContext := context.Background()
enabled := true
disabled := false
workspacePath := "/Workspace/user.name@company.com"

tests := []struct {
name string
ctx context.Context
mutateBundle func(b *bundle.Bundle)
initialValue *bool
expectedValue *bool
expectedWarning string
}{
{
name: "preset enabled, bundle in Workspace, databricks runtime",
ctx: dbr.MockRuntime(testContext, true),
initialValue: &enabled,
expectedValue: &enabled,
},
{
name: "preset enabled, bundle not in Workspace, databricks runtime",
ctx: dbr.MockRuntime(testContext, true),
mutateBundle: func(b *bundle.Bundle) {
b.SyncRootPath = "/Users/user.name@company.com"
},
initialValue: &enabled,
expectedValue: &disabled,
expectedWarning: "source-linked deployment is available only in the Databricks Workspace",
},
{
name: "preset enabled, bundle in Workspace, not databricks runtime",
ctx: dbr.MockRuntime(testContext, false),
initialValue: &enabled,
expectedValue: &disabled,
expectedWarning: "source-linked deployment is available only in the Databricks Workspace",
},
{
name: "preset disabled, bundle in Workspace, databricks runtime",
ctx: dbr.MockRuntime(testContext, true),
initialValue: &disabled,
expectedValue: &disabled,
},
{
name: "preset nil, bundle in Workspace, databricks runtime",
ctx: dbr.MockRuntime(testContext, true),
initialValue: nil,
expectedValue: nil,
},
{
name: "preset nil, dev mode true, bundle in Workspace, databricks runtime",
ctx: dbr.MockRuntime(testContext, true),
mutateBundle: func(b *bundle.Bundle) {
b.Config.Bundle.Mode = config.Development
},
initialValue: nil,
expectedValue: &enabled,
},
{
name: "preset enabled, workspace.file_path is defined by user",
ctx: dbr.MockRuntime(testContext, true),
mutateBundle: func(b *bundle.Bundle) {
b.Config.Workspace.FilePath = "file_path"
},
initialValue: &enabled,
expectedValue: &enabled,
expectedWarning: "workspace.file_path setting will be ignored in source-linked deployment mode",
},
}

for _, tt := range tests {
t.Run(tt.name, func(t *testing.T) {
b := &bundle.Bundle{
SyncRootPath: workspacePath,
Config: config.Root{
Presets: config.Presets{
SourceLinkedDeployment: tt.initialValue,
},
},
}

if tt.mutateBundle != nil {
tt.mutateBundle(b)
}

bundletest.SetLocation(b, "presets.source_linked_deployment", []dyn.Location{{File: "databricks.yml"}})
bundletest.SetLocation(b, "workspace.file_path", []dyn.Location{{File: "databricks.yml"}})

diags := bundle.Apply(tt.ctx, b, mutator.ApplySourceLinkedDeploymentPreset())
if diags.HasError() {
t.Fatalf("unexpected error: %v", diags)
}

if tt.expectedWarning != "" {
require.Equal(t, tt.expectedWarning, diags[0].Summary)
require.NotEmpty(t, diags[0].Locations)
}

require.Equal(t, tt.expectedValue, b.Config.Presets.SourceLinkedDeployment)
})
}
}
9 changes: 0 additions & 9 deletions bundle/config/mutator/process_target_mode.go
Original file line number Diff line number Diff line change
Expand Up @@ -6,7 +6,6 @@ import (

"github.com/databricks/cli/bundle"
"github.com/databricks/cli/bundle/config"
"github.com/databricks/cli/libs/dbr"
"github.com/databricks/cli/libs/diag"
"github.com/databricks/cli/libs/dyn"
"github.com/databricks/cli/libs/iamutil"
Expand Down Expand Up @@ -58,14 +57,6 @@ func transformDevelopmentMode(ctx context.Context, b *bundle.Bundle) {
t.TriggerPauseStatus = config.Paused
}

if !config.IsExplicitlyDisabled(t.SourceLinkedDeployment) {
isInWorkspace := strings.HasPrefix(b.SyncRootPath, "/Workspace/")
if isInWorkspace && dbr.RunsOnRuntime(ctx) {
enabled := true
t.SourceLinkedDeployment = &enabled
}
}

if !config.IsExplicitlyDisabled(t.PipelinesDevelopment) {
enabled := true
t.PipelinesDevelopment = &enabled
Expand Down
Loading
Loading