Skip to content

Commit c4f4717

Browse files
andrewnesterhectorcast-db
authored andcommitted
Added support for glob patterns in pipeline libraries section (#833)
## Changes Now it's possible to specify glob pattern in pipeline libraries section and DAB will add all matched files as libraries ``` pipelines: dummy: name: " DLT with Python files" target: "dlt_python_files" libraries: - file: path: ./*.py ``` ## Tests Added unit test
1 parent 8985605 commit c4f4717

File tree

4 files changed

+248
-0
lines changed

4 files changed

+248
-0
lines changed
Lines changed: 89 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,89 @@
1+
package mutator
2+
3+
import (
4+
"context"
5+
"fmt"
6+
"path/filepath"
7+
8+
"github.com/databricks/cli/bundle"
9+
"github.com/databricks/cli/bundle/libraries"
10+
"github.com/databricks/databricks-sdk-go/service/pipelines"
11+
)
12+
13+
type expandPipelineGlobPaths struct{}
14+
15+
func ExpandPipelineGlobPaths() bundle.Mutator {
16+
return &expandPipelineGlobPaths{}
17+
}
18+
19+
func (m *expandPipelineGlobPaths) Apply(_ context.Context, b *bundle.Bundle) error {
20+
for key, pipeline := range b.Config.Resources.Pipelines {
21+
dir, err := pipeline.ConfigFileDirectory()
22+
if err != nil {
23+
return fmt.Errorf("unable to determine directory for pipeline %s: %w", key, err)
24+
}
25+
26+
expandedLibraries := make([]pipelines.PipelineLibrary, 0)
27+
for i := 0; i < len(pipeline.Libraries); i++ {
28+
29+
library := &pipeline.Libraries[i]
30+
path := getGlobPatternToExpand(library)
31+
if path == "" || !libraries.IsLocalPath(path) {
32+
expandedLibraries = append(expandedLibraries, *library)
33+
continue
34+
}
35+
36+
matches, err := filepath.Glob(filepath.Join(dir, path))
37+
if err != nil {
38+
return err
39+
}
40+
41+
for _, match := range matches {
42+
m, err := filepath.Rel(dir, match)
43+
if err != nil {
44+
return err
45+
}
46+
expandedLibraries = append(expandedLibraries, cloneWithPath(library, m))
47+
}
48+
}
49+
pipeline.Libraries = expandedLibraries
50+
}
51+
52+
return nil
53+
}
54+
55+
func getGlobPatternToExpand(library *pipelines.PipelineLibrary) string {
56+
if library.File != nil {
57+
return library.File.Path
58+
}
59+
60+
if library.Notebook != nil {
61+
return library.Notebook.Path
62+
}
63+
64+
return ""
65+
}
66+
67+
func cloneWithPath(library *pipelines.PipelineLibrary, path string) pipelines.PipelineLibrary {
68+
if library.File != nil {
69+
return pipelines.PipelineLibrary{
70+
File: &pipelines.FileLibrary{
71+
Path: path,
72+
},
73+
}
74+
}
75+
76+
if library.Notebook != nil {
77+
return pipelines.PipelineLibrary{
78+
Notebook: &pipelines.NotebookLibrary{
79+
Path: path,
80+
},
81+
}
82+
}
83+
84+
return pipelines.PipelineLibrary{}
85+
}
86+
87+
func (*expandPipelineGlobPaths) Name() string {
88+
return "ExpandPipelineGlobPaths"
89+
}
Lines changed: 154 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,154 @@
1+
package mutator
2+
3+
import (
4+
"context"
5+
"os"
6+
"path/filepath"
7+
"testing"
8+
9+
"github.com/databricks/cli/bundle"
10+
"github.com/databricks/cli/bundle/config"
11+
"github.com/databricks/cli/bundle/config/paths"
12+
"github.com/databricks/cli/bundle/config/resources"
13+
"github.com/databricks/databricks-sdk-go/service/compute"
14+
"github.com/databricks/databricks-sdk-go/service/pipelines"
15+
"github.com/stretchr/testify/require"
16+
)
17+
18+
func touchEmptyFile(t *testing.T, path string) {
19+
err := os.MkdirAll(filepath.Dir(path), 0700)
20+
require.NoError(t, err)
21+
f, err := os.Create(path)
22+
require.NoError(t, err)
23+
f.Close()
24+
}
25+
26+
func TestExpandGlobPathsInPipelines(t *testing.T) {
27+
dir := t.TempDir()
28+
29+
touchEmptyFile(t, filepath.Join(dir, "test1.ipynb"))
30+
touchEmptyFile(t, filepath.Join(dir, "test/test2.ipynb"))
31+
touchEmptyFile(t, filepath.Join(dir, "test/test3.ipynb"))
32+
touchEmptyFile(t, filepath.Join(dir, "test1.jar"))
33+
touchEmptyFile(t, filepath.Join(dir, "test/test2.jar"))
34+
touchEmptyFile(t, filepath.Join(dir, "test/test3.jar"))
35+
touchEmptyFile(t, filepath.Join(dir, "test1.py"))
36+
touchEmptyFile(t, filepath.Join(dir, "test/test2.py"))
37+
touchEmptyFile(t, filepath.Join(dir, "test/test3.py"))
38+
39+
b := &bundle.Bundle{
40+
Config: config.Root{
41+
Path: dir,
42+
Resources: config.Resources{
43+
Pipelines: map[string]*resources.Pipeline{
44+
"pipeline": {
45+
Paths: paths.Paths{
46+
ConfigFilePath: filepath.Join(dir, "resource.yml"),
47+
},
48+
PipelineSpec: &pipelines.PipelineSpec{
49+
Libraries: []pipelines.PipelineLibrary{
50+
{
51+
Notebook: &pipelines.NotebookLibrary{
52+
Path: "./**/*.ipynb",
53+
},
54+
},
55+
{
56+
Jar: "./*.jar",
57+
},
58+
{
59+
File: &pipelines.FileLibrary{
60+
Path: "./**/*.py",
61+
},
62+
},
63+
{
64+
Maven: &compute.MavenLibrary{
65+
Coordinates: "org.jsoup:jsoup:1.7.2",
66+
},
67+
},
68+
{
69+
Notebook: &pipelines.NotebookLibrary{
70+
Path: "./test1.ipynb",
71+
},
72+
},
73+
{
74+
Notebook: &pipelines.NotebookLibrary{
75+
Path: "/Workspace/Users/[email protected]/test.ipynb",
76+
},
77+
},
78+
{
79+
Notebook: &pipelines.NotebookLibrary{
80+
Path: "dbfs:/[email protected]/test.ipynb",
81+
},
82+
},
83+
},
84+
},
85+
},
86+
},
87+
},
88+
},
89+
}
90+
91+
m := ExpandPipelineGlobPaths()
92+
err := bundle.Apply(context.Background(), b, m)
93+
require.NoError(t, err)
94+
95+
libraries := b.Config.Resources.Pipelines["pipeline"].Libraries
96+
require.Len(t, libraries, 9)
97+
98+
// Making sure glob patterns are expanded correctly
99+
require.True(t, containsNotebook(libraries, filepath.Join("test", "test2.ipynb")))
100+
require.True(t, containsNotebook(libraries, filepath.Join("test", "test3.ipynb")))
101+
require.True(t, containsFile(libraries, filepath.Join("test", "test2.py")))
102+
require.True(t, containsFile(libraries, filepath.Join("test", "test3.py")))
103+
104+
// Making sure exact file references work as well
105+
require.True(t, containsNotebook(libraries, "test1.ipynb"))
106+
107+
// Making sure absolute pass to remote FS file references work as well
108+
require.True(t, containsNotebook(libraries, "/Workspace/Users/[email protected]/test.ipynb"))
109+
require.True(t, containsNotebook(libraries, "dbfs:/[email protected]/test.ipynb"))
110+
111+
// Making sure other libraries are not replaced
112+
require.True(t, containsJar(libraries, "./*.jar"))
113+
require.True(t, containsMaven(libraries, "org.jsoup:jsoup:1.7.2"))
114+
}
115+
116+
func containsNotebook(libraries []pipelines.PipelineLibrary, path string) bool {
117+
for _, l := range libraries {
118+
if l.Notebook != nil && l.Notebook.Path == path {
119+
return true
120+
}
121+
}
122+
123+
return false
124+
}
125+
126+
func containsJar(libraries []pipelines.PipelineLibrary, path string) bool {
127+
for _, l := range libraries {
128+
if l.Jar == path {
129+
return true
130+
}
131+
}
132+
133+
return false
134+
}
135+
136+
func containsMaven(libraries []pipelines.PipelineLibrary, coordinates string) bool {
137+
for _, l := range libraries {
138+
if l.Maven != nil && l.Maven.Coordinates == coordinates {
139+
return true
140+
}
141+
}
142+
143+
return false
144+
}
145+
146+
func containsFile(libraries []pipelines.PipelineLibrary, path string) bool {
147+
for _, l := range libraries {
148+
if l.File != nil && l.File.Path == path {
149+
return true
150+
}
151+
}
152+
153+
return false
154+
}

bundle/libraries/libraries.go

Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -161,6 +161,10 @@ func isLocalLibrary(library *compute.Library) bool {
161161
return false
162162
}
163163

164+
return IsLocalPath(path)
165+
}
166+
167+
func IsLocalPath(path string) bool {
164168
if isExplicitFileScheme(path) {
165169
return true
166170
}

bundle/phases/initialize.go

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -31,6 +31,7 @@ func Initialize() bundle.Mutator {
3131
),
3232
mutator.OverrideCompute(),
3333
mutator.ProcessTargetMode(),
34+
mutator.ExpandPipelineGlobPaths(),
3435
mutator.TranslatePaths(),
3536
python.WrapperWarning(),
3637
terraform.Initialize(),

0 commit comments

Comments
 (0)