2023-07-25 11:35:08 +00:00
|
|
|
package libraries
|
|
|
|
|
|
|
|
import (
|
|
|
|
"context"
|
|
|
|
"fmt"
|
2023-08-29 08:26:09 +00:00
|
|
|
"net/url"
|
2023-07-25 11:35:08 +00:00
|
|
|
"path/filepath"
|
2023-08-07 09:55:30 +00:00
|
|
|
"strings"
|
2023-07-25 11:35:08 +00:00
|
|
|
|
|
|
|
"github.com/databricks/cli/bundle"
|
|
|
|
"github.com/databricks/cli/bundle/config"
|
|
|
|
"github.com/databricks/cli/libs/cmdio"
|
|
|
|
"github.com/databricks/databricks-sdk-go/service/compute"
|
|
|
|
"github.com/databricks/databricks-sdk-go/service/jobs"
|
|
|
|
)
|
|
|
|
|
|
|
|
type match struct {
|
|
|
|
}
|
|
|
|
|
|
|
|
func MatchWithArtifacts() bundle.Mutator {
|
|
|
|
return &match{}
|
|
|
|
}
|
|
|
|
|
|
|
|
func (a *match) Name() string {
|
|
|
|
return "libraries.MatchWithArtifacts"
|
|
|
|
}
|
|
|
|
|
|
|
|
func (a *match) Apply(ctx context.Context, b *bundle.Bundle) error {
|
2023-08-17 09:11:39 +00:00
|
|
|
tasks := findAllTasks(b)
|
|
|
|
for _, task := range tasks {
|
|
|
|
if isMissingRequiredLibraries(task) {
|
|
|
|
return fmt.Errorf("task '%s' is missing required libraries. Please include your package code in task libraries block", task.TaskKey)
|
|
|
|
}
|
|
|
|
for j := range task.Libraries {
|
|
|
|
lib := &task.Libraries[j]
|
|
|
|
err := findArtifactsAndMarkForUpload(ctx, lib, b)
|
|
|
|
if err != nil {
|
|
|
|
return err
|
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
|
|
|
return nil
|
|
|
|
}
|
|
|
|
|
|
|
|
func findAllTasks(b *bundle.Bundle) []*jobs.Task {
|
2023-07-25 11:35:08 +00:00
|
|
|
r := b.Config.Resources
|
2023-08-17 09:11:39 +00:00
|
|
|
result := make([]*jobs.Task, 0)
|
2023-07-25 11:35:08 +00:00
|
|
|
for k := range b.Config.Resources.Jobs {
|
|
|
|
tasks := r.Jobs[k].JobSettings.Tasks
|
|
|
|
for i := range tasks {
|
|
|
|
task := &tasks[i]
|
2023-08-17 09:11:39 +00:00
|
|
|
result = append(result, task)
|
2023-07-25 11:35:08 +00:00
|
|
|
}
|
|
|
|
}
|
2023-08-17 09:11:39 +00:00
|
|
|
|
|
|
|
return result
|
|
|
|
}
|
|
|
|
|
2023-09-08 11:08:21 +00:00
|
|
|
func FindAllWheelTasksWithLocalLibraries(b *bundle.Bundle) []*jobs.Task {
|
2023-08-17 09:11:39 +00:00
|
|
|
tasks := findAllTasks(b)
|
|
|
|
wheelTasks := make([]*jobs.Task, 0)
|
|
|
|
for _, task := range tasks {
|
2023-09-08 11:08:21 +00:00
|
|
|
if task.PythonWheelTask != nil && IsTaskWithLocalLibraries(task) {
|
2023-08-17 09:11:39 +00:00
|
|
|
wheelTasks = append(wheelTasks, task)
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
return wheelTasks
|
2023-07-25 11:35:08 +00:00
|
|
|
}
|
|
|
|
|
2023-09-08 11:08:21 +00:00
|
|
|
func IsTaskWithLocalLibraries(task *jobs.Task) bool {
|
|
|
|
for _, l := range task.Libraries {
|
|
|
|
if isLocalLibrary(&l) {
|
|
|
|
return true
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
return false
|
|
|
|
}
|
|
|
|
|
2023-09-08 13:45:21 +00:00
|
|
|
func IsTaskWithWorkspaceLibraries(task *jobs.Task) bool {
|
|
|
|
for _, l := range task.Libraries {
|
|
|
|
path := libPath(&l)
|
|
|
|
if isWorkspacePath(path) {
|
|
|
|
return true
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
return false
|
|
|
|
}
|
|
|
|
|
2023-07-25 11:35:08 +00:00
|
|
|
func isMissingRequiredLibraries(task *jobs.Task) bool {
|
|
|
|
if task.Libraries != nil {
|
|
|
|
return false
|
|
|
|
}
|
|
|
|
|
|
|
|
return task.PythonWheelTask != nil || task.SparkJarTask != nil
|
|
|
|
}
|
|
|
|
|
|
|
|
func findLibraryMatches(lib *compute.Library, b *bundle.Bundle) ([]string, error) {
|
|
|
|
path := libPath(lib)
|
|
|
|
if path == "" {
|
|
|
|
return nil, nil
|
|
|
|
}
|
|
|
|
|
|
|
|
fullPath := filepath.Join(b.Config.Path, path)
|
|
|
|
return filepath.Glob(fullPath)
|
|
|
|
}
|
|
|
|
|
|
|
|
func findArtifactsAndMarkForUpload(ctx context.Context, lib *compute.Library, b *bundle.Bundle) error {
|
|
|
|
matches, err := findLibraryMatches(lib, b)
|
|
|
|
if err != nil {
|
|
|
|
return err
|
|
|
|
}
|
|
|
|
|
2023-07-26 12:58:52 +00:00
|
|
|
if len(matches) == 0 && isLocalLibrary(lib) {
|
2023-08-29 08:26:09 +00:00
|
|
|
return fmt.Errorf("file %s is referenced in libraries section but doesn't exist on the local file system", libPath(lib))
|
2023-07-26 12:58:52 +00:00
|
|
|
}
|
|
|
|
|
2023-07-25 11:35:08 +00:00
|
|
|
for _, match := range matches {
|
|
|
|
af, err := findArtifactFileByLocalPath(match, b)
|
|
|
|
if err != nil {
|
2023-08-29 08:26:09 +00:00
|
|
|
cmdio.LogString(ctx, fmt.Sprintf("%s. Skipping uploading. In order to use the define 'artifacts' section", err.Error()))
|
2023-07-25 11:35:08 +00:00
|
|
|
} else {
|
|
|
|
af.Libraries = append(af.Libraries, lib)
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
return nil
|
|
|
|
}
|
|
|
|
|
|
|
|
func findArtifactFileByLocalPath(path string, b *bundle.Bundle) (*config.ArtifactFile, error) {
|
|
|
|
for _, a := range b.Config.Artifacts {
|
|
|
|
for k := range a.Files {
|
|
|
|
if a.Files[k].Source == path {
|
|
|
|
return &a.Files[k], nil
|
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2023-08-29 08:26:09 +00:00
|
|
|
return nil, fmt.Errorf("artifact section is not defined for file at %s", path)
|
2023-07-25 11:35:08 +00:00
|
|
|
}
|
|
|
|
|
|
|
|
func libPath(library *compute.Library) string {
|
|
|
|
if library.Whl != "" {
|
|
|
|
return library.Whl
|
|
|
|
}
|
|
|
|
if library.Jar != "" {
|
|
|
|
return library.Jar
|
|
|
|
}
|
|
|
|
if library.Egg != "" {
|
|
|
|
return library.Egg
|
|
|
|
}
|
|
|
|
|
|
|
|
return ""
|
|
|
|
}
|
2023-07-26 12:58:52 +00:00
|
|
|
|
|
|
|
func isLocalLibrary(library *compute.Library) bool {
|
2023-08-07 09:55:30 +00:00
|
|
|
path := libPath(library)
|
|
|
|
if path == "" {
|
|
|
|
return false
|
|
|
|
}
|
|
|
|
|
2023-10-04 13:23:13 +00:00
|
|
|
return IsLocalPath(path)
|
|
|
|
}
|
|
|
|
|
|
|
|
func IsLocalPath(path string) bool {
|
2023-08-29 08:26:09 +00:00
|
|
|
if isExplicitFileScheme(path) {
|
|
|
|
return true
|
|
|
|
}
|
|
|
|
|
|
|
|
if isRemoteStorageScheme(path) {
|
|
|
|
return false
|
|
|
|
}
|
|
|
|
|
|
|
|
return !isWorkspacePath(path)
|
|
|
|
}
|
|
|
|
|
|
|
|
func isExplicitFileScheme(path string) bool {
|
|
|
|
return strings.HasPrefix(path, "file://")
|
2023-08-07 09:55:30 +00:00
|
|
|
}
|
|
|
|
|
2023-08-29 08:26:09 +00:00
|
|
|
func isRemoteStorageScheme(path string) bool {
|
|
|
|
url, err := url.Parse(path)
|
|
|
|
if err != nil {
|
|
|
|
return false
|
|
|
|
}
|
|
|
|
|
|
|
|
if url.Scheme == "" {
|
|
|
|
return false
|
|
|
|
}
|
|
|
|
|
2023-09-08 09:53:57 +00:00
|
|
|
// If the path starts with scheme:/ format, it's a correct remote storage scheme
|
|
|
|
return strings.HasPrefix(path, url.Scheme+":/")
|
2023-08-29 08:26:09 +00:00
|
|
|
|
2023-08-07 09:55:30 +00:00
|
|
|
}
|
|
|
|
|
|
|
|
func isWorkspacePath(path string) bool {
|
|
|
|
return strings.HasPrefix(path, "/Workspace/") ||
|
|
|
|
strings.HasPrefix(path, "/Users/") ||
|
|
|
|
strings.HasPrefix(path, "/Shared/")
|
2023-07-26 12:58:52 +00:00
|
|
|
}
|