Skip to content

Commit 6576d8e

Browse files
authored
Allow pipelines reference environment dependencies outside sync root (#3816)
## Changes Check that pipeline can reference environment dependencies outside sync root <!-- If your PR needs to be included in the release notes for next release, add a separate entry in NEXT_CHANGELOG.md as part of your PR. -->
1 parent bc10093 commit 6576d8e

File tree

10 files changed

+159
-79
lines changed

10 files changed

+159
-79
lines changed

acceptance/bundle/libraries/outside_of_bundle_root/bundle/databricks.yml

Lines changed: 9 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,9 @@
11
bundle:
22
name: outside_of_bundle_root
33

4+
include:
5+
- resources/*.yml
6+
47
variables:
58
cluster:
69
default:
@@ -25,4 +28,9 @@ resources:
2528
entry_point: main
2629
package_name: my_default_python
2730
libraries:
28-
- whl: ../*.whl
31+
- whl: ../lib/*.whl
32+
environments:
33+
- environment_key: default
34+
spec:
35+
dependencies:
36+
- ../lib/*.whl
Lines changed: 7 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,7 @@
1+
resources:
2+
pipelines:
3+
pipeline:
4+
name: "test"
5+
environment:
6+
dependencies:
7+
- ../../lib/*.whl

acceptance/bundle/libraries/outside_of_bundle_root/test.whl renamed to acceptance/bundle/libraries/outside_of_bundle_root/lib/test.whl

File renamed without changes.

acceptance/bundle/libraries/outside_of_bundle_root/output.txt

Lines changed: 31 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -1,13 +1,32 @@
11

2+
>>> [CLI] bundle validate
3+
Name: outside_of_bundle_root
4+
Target: default
5+
Workspace:
6+
User: [USERNAME]
7+
Path: /Workspace/Users/[USERNAME]/.bundle/outside_of_bundle_root/default
8+
9+
Validation OK!
10+
211
>>> [CLI] bundle validate -o json
312
[
413
{
5-
"whl": "../*.whl"
14+
"whl": "../lib/*.whl"
615
}
716
]
817

18+
>>> [CLI] bundle validate -o json
19+
[
20+
"../lib/*.whl"
21+
]
22+
23+
>>> [CLI] bundle validate -o json
24+
[
25+
"../lib/*.whl"
26+
]
27+
928
>>> [CLI] bundle deploy
10-
Uploading ../test.whl...
29+
Uploading ../lib/test.whl...
1130
Uploading bundle files to /Workspace/Users/[USERNAME]/.bundle/outside_of_bundle_root/default/files...
1231
Deploying resources...
1332
Updating deployment state...
@@ -21,6 +40,16 @@ Deployment complete!
2140
}
2241
]
2342

43+
>>> cat out.requests.txt
44+
[
45+
"/Workspace/Users/[USERNAME]/.bundle/outside_of_bundle_root/default/artifacts/.internal/test.whl"
46+
]
47+
48+
>>> cat out.requests.txt
49+
[
50+
"/Workspace/Users/[USERNAME]/.bundle/outside_of_bundle_root/default/artifacts/.internal/test.whl"
51+
]
52+
2453
>>> cat out.requests.txt
2554
{
2655
"method": "POST",
Lines changed: 5 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,12 +1,16 @@
11
cd bundle
2-
2+
trace $CLI bundle validate
33
trace $CLI bundle validate -o json | jq '.resources.jobs.test.tasks[0].libraries'
4+
trace $CLI bundle validate -o json | jq '.resources.jobs.test.environments[0].spec.dependencies'
5+
trace $CLI bundle validate -o json | jq '.resources.pipelines.pipeline.environment.dependencies'
46
trace $CLI bundle deploy
57

68
cd ..
79

810
title "Check that the job libraries are uploaded and the path is correct in the job"
911
trace cat out.requests.txt | jq 'select(.path == "/api/2.2/jobs/create")' | jq '.body.tasks[0].libraries'
12+
trace cat out.requests.txt | jq 'select(.path == "/api/2.2/jobs/create")' | jq '.body.environments[0].spec.dependencies'
13+
trace cat out.requests.txt | jq 'select(.path == "/api/2.0/pipelines" and .method == "POST")' | jq '.body.environment.dependencies'
1014
trace cat out.requests.txt | jq 'select(.path | test("/api/2.0/workspace-files/import-file/Workspace/Users/.*/.bundle/outside_of_bundle_root/default/artifacts/.internal/test.whl"))'
1115

1216
rm out.requests.txt

bundle/config/mutator/paths/pipeline_paths_visitor.go

Lines changed: 59 additions & 33 deletions
Original file line numberDiff line numberDiff line change
@@ -14,14 +14,42 @@ type pipelineRewritePattern struct {
1414
skipRewrite func(string) bool
1515
}
1616

17+
// Base pattern to match all libraries in all pipelines.
18+
var base = dyn.NewPattern(
19+
dyn.Key("resources"),
20+
dyn.Key("pipelines"),
21+
dyn.AnyKey(),
22+
)
23+
1724
func pipelineRewritePatterns() []pipelineRewritePattern {
18-
// Base pattern to match all libraries in all pipelines.
19-
base := dyn.NewPattern(
20-
dyn.Key("resources"),
21-
dyn.Key("pipelines"),
22-
dyn.AnyKey(),
23-
)
25+
// Compile list of configuration paths to rewrite.
26+
allPatterns := []pipelineRewritePattern{
27+
{
28+
pattern: base.Append(dyn.Key("libraries"), dyn.AnyIndex(), dyn.Key("notebook"), dyn.Key("path")),
29+
mode: TranslateModeNotebook,
30+
skipRewrite: noSkipRewrite,
31+
},
32+
{
33+
pattern: base.Append(dyn.Key("libraries"), dyn.AnyIndex(), dyn.Key("file"), dyn.Key("path")),
34+
mode: TranslateModeFile,
35+
skipRewrite: noSkipRewrite,
36+
},
37+
{
38+
pattern: base.Append(dyn.Key("libraries"), dyn.AnyIndex(), dyn.Key("glob"), dyn.Key("include")),
39+
mode: TranslateModeGlob,
40+
skipRewrite: noSkipRewrite,
41+
},
42+
{
43+
pattern: base.Append(dyn.Key("root_path")),
44+
mode: TranslateModeDirectory,
45+
skipRewrite: noSkipRewrite,
46+
},
47+
}
2448

49+
return allPatterns
50+
}
51+
52+
func pipelineLibrariesRewritePatterns() []pipelineRewritePattern {
2553
pipelineEnvironmentsPatterns := []pipelineRewritePattern{
2654
{
2755
pattern: dyn.NewPattern(
@@ -57,33 +85,7 @@ func pipelineRewritePatterns() []pipelineRewritePattern {
5785
},
5886
}
5987

60-
// Compile list of configuration paths to rewrite.
61-
allPatterns := []pipelineRewritePattern{
62-
{
63-
pattern: base.Append(dyn.Key("libraries"), dyn.AnyIndex(), dyn.Key("notebook"), dyn.Key("path")),
64-
mode: TranslateModeNotebook,
65-
skipRewrite: noSkipRewrite,
66-
},
67-
{
68-
pattern: base.Append(dyn.Key("libraries"), dyn.AnyIndex(), dyn.Key("file"), dyn.Key("path")),
69-
mode: TranslateModeFile,
70-
skipRewrite: noSkipRewrite,
71-
},
72-
{
73-
pattern: base.Append(dyn.Key("libraries"), dyn.AnyIndex(), dyn.Key("glob"), dyn.Key("include")),
74-
mode: TranslateModeGlob,
75-
skipRewrite: noSkipRewrite,
76-
},
77-
{
78-
pattern: base.Append(dyn.Key("root_path")),
79-
mode: TranslateModeDirectory,
80-
skipRewrite: noSkipRewrite,
81-
},
82-
}
83-
84-
allPatterns = append(allPatterns, pipelineEnvironmentsPatterns...)
85-
allPatterns = append(allPatterns, pipelineEnvironmentsPatternsWithPipFlags...)
86-
return allPatterns
88+
return append(pipelineEnvironmentsPatterns, pipelineEnvironmentsPatternsWithPipFlags...)
8789
}
8890

8991
func VisitPipelinePaths(value dyn.Value, fn VisitFunc) (dyn.Value, error) {
@@ -109,3 +111,27 @@ func VisitPipelinePaths(value dyn.Value, fn VisitFunc) (dyn.Value, error) {
109111

110112
return newValue, nil
111113
}
114+
115+
func VisitPipelineLibrariesPaths(value dyn.Value, fn VisitFunc) (dyn.Value, error) {
116+
var err error
117+
newValue := value
118+
119+
for _, rewritePattern := range pipelineLibrariesRewritePatterns() {
120+
newValue, err = dyn.MapByPattern(newValue, rewritePattern.pattern, func(p dyn.Path, v dyn.Value) (dyn.Value, error) {
121+
sv, ok := v.AsString()
122+
if !ok {
123+
return v, nil
124+
}
125+
if rewritePattern.skipRewrite(sv) {
126+
return v, nil
127+
}
128+
129+
return fn(p, rewritePattern.mode, v)
130+
})
131+
if err != nil {
132+
return dyn.InvalidValue, err
133+
}
134+
}
135+
136+
return newValue, nil
137+
}

bundle/config/mutator/paths/pipeline_paths_visitor_test.go

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -46,6 +46,7 @@ func TestVisitPipelinePaths(t *testing.T) {
4646
}
4747

4848
actual := collectVisitedPaths(t, root, VisitPipelinePaths)
49+
actual = append(actual, collectVisitedPaths(t, root, VisitPipelineLibrariesPaths)...)
4950
expected := []dyn.Path{
5051
dyn.MustPathFromString("resources.pipelines.pipeline0.libraries[0].file.path"),
5152
dyn.MustPathFromString("resources.pipelines.pipeline0.libraries[1].notebook.path"),

bundle/config/mutator/paths/visitor.go

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -15,6 +15,7 @@ func VisitPaths(root dyn.Value, fn VisitFunc) (dyn.Value, error) {
1515
VisitArtifactPaths,
1616
VisitDashboardPaths,
1717
VisitPipelinePaths,
18+
VisitPipelineLibrariesPaths,
1819
}
1920

2021
newRoot := root

bundle/config/mutator/translate_paths.go

Lines changed: 2 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -326,7 +326,8 @@ func (m *translatePaths) Apply(ctx context.Context, b *bundle.Bundle) diag.Diagn
326326
return applyTranslations(ctx, b, t, []func(context.Context, dyn.Value) (dyn.Value, error){
327327
t.applyJobTranslations(paths.VisitJobPaths, false),
328328
t.applyJobTranslations(paths.VisitJobLibrariesPaths, true),
329-
t.applyPipelineTranslations,
329+
t.applyPipelineTranslations(paths.VisitPipelinePaths, false),
330+
t.applyPipelineTranslations(paths.VisitPipelineLibrariesPaths, true),
330331
t.applyArtifactTranslations,
331332
t.applyAppsTranslations,
332333
})

bundle/config/mutator/translate_paths_pipelines.go

Lines changed: 44 additions & 41 deletions
Original file line numberDiff line numberDiff line change
@@ -12,55 +12,58 @@ import (
1212
"github.com/databricks/cli/libs/logdiag"
1313
)
1414

15-
func (t *translateContext) applyPipelineTranslations(ctx context.Context, v dyn.Value) (dyn.Value, error) {
16-
var err error
15+
func (t *translateContext) applyPipelineTranslations(visitor visitFunc, allowOutsideSyncRoot bool) translateFunc {
16+
return func(ctx context.Context, v dyn.Value) (dyn.Value, error) {
17+
var err error
1718

18-
fallback, err := gatherFallbackPaths(v, "pipelines")
19-
if err != nil {
20-
return dyn.InvalidValue, err
21-
}
22-
23-
return paths.VisitPipelinePaths(v, func(p dyn.Path, mode paths.TranslateMode, v dyn.Value) (dyn.Value, error) {
24-
key := p[2].Key()
25-
opts := translateOptions{
26-
Mode: mode,
27-
}
28-
29-
// Handle path as if it's relative to the bundle root
30-
nv, err := t.rewriteValue(ctx, p, v, t.b.BundleRootPath, opts)
31-
if err == nil {
32-
return nv, nil
19+
fallback, err := gatherFallbackPaths(v, "pipelines")
20+
if err != nil {
21+
return dyn.InvalidValue, err
3322
}
3423

35-
// If we failed to rewrite the path, it uses an old path format which relied on fallback.
36-
if fallback[key] != "" {
37-
dir, nerr := locationDirectory(v.Location())
38-
if nerr != nil {
39-
return dyn.InvalidValue, nerr
24+
return visitor(v, func(p dyn.Path, mode paths.TranslateMode, v dyn.Value) (dyn.Value, error) {
25+
key := p[2].Key()
26+
opts := translateOptions{
27+
Mode: mode,
28+
AllowPathOutsideSyncRoot: allowOutsideSyncRoot,
4029
}
4130

42-
dirRel, nerr := filepath.Rel(t.b.BundleRootPath, dir)
43-
if nerr != nil {
44-
return dyn.InvalidValue, nerr
31+
// Handle path as if it's relative to the bundle root
32+
nv, err := t.rewriteValue(ctx, p, v, t.b.BundleRootPath, opts)
33+
if err == nil {
34+
return nv, nil
4535
}
4636

47-
originalPath, nerr := filepath.Rel(dirRel, v.MustString())
48-
if nerr != nil {
49-
return dyn.InvalidValue, nerr
50-
}
37+
// If we failed to rewrite the path, it uses an old path format which relied on fallback.
38+
if fallback[key] != "" {
39+
dir, nerr := locationDirectory(v.Location())
40+
if nerr != nil {
41+
return dyn.InvalidValue, nerr
42+
}
5143

52-
originalValue := dyn.NewValue(originalPath, v.Locations())
53-
nv, nerr := t.rewriteValue(ctx, p, originalValue, fallback[key], opts)
54-
if nerr == nil {
55-
logdiag.LogDiag(ctx, diag.Diagnostic{
56-
Severity: diag.Error,
57-
Summary: fmt.Sprintf("path %s is defined relative to the %s directory (%s). Please update the path to be relative to the file where it is defined or use earlier version of CLI (0.261.0 or earlier).", originalPath, fallback[key], v.Location()),
58-
Locations: v.Locations(),
59-
})
60-
return nv, nil
44+
dirRel, nerr := filepath.Rel(t.b.BundleRootPath, dir)
45+
if nerr != nil {
46+
return dyn.InvalidValue, nerr
47+
}
48+
49+
originalPath, nerr := filepath.Rel(dirRel, v.MustString())
50+
if nerr != nil {
51+
return dyn.InvalidValue, nerr
52+
}
53+
54+
originalValue := dyn.NewValue(originalPath, v.Locations())
55+
nv, nerr := t.rewriteValue(ctx, p, originalValue, fallback[key], opts)
56+
if nerr == nil {
57+
logdiag.LogDiag(ctx, diag.Diagnostic{
58+
Severity: diag.Error,
59+
Summary: fmt.Sprintf("path %s is defined relative to the %s directory (%s). Please update the path to be relative to the file where it is defined or use earlier version of CLI (0.261.0 or earlier).", originalPath, fallback[key], v.Location()),
60+
Locations: v.Locations(),
61+
})
62+
return nv, nil
63+
}
6164
}
62-
}
6365

64-
return dyn.InvalidValue, err
65-
})
66+
return dyn.InvalidValue, err
67+
})
68+
}
6669
}

0 commit comments

Comments
 (0)