Skip to content

Commit

Permalink
Return diag.Diagnostics from mutators (#1305)
Browse files Browse the repository at this point in the history
## Changes

This diagnostics type allows us to capture multiple warnings as well as
errors in the return value. This is a preparation for returning
additional warnings from mutators in case we detect non-fatal problems.

* All return statements that previously returned an error now return
`diag.FromErr`
* All return statements that previously returned `fmt.Errorf` now return
`diag.Errorf`
* All `err != nil` checks now use `diags.HasError()` or `diags.Error()`

## Tests

* Existing tests pass.
* I confirmed no call site under `./bundle` or `./cmd/bundle` uses
`errors.Is` on the return value from mutators. This is relevant because
we cannot wrap errors with `%w` when calling `diag.Errorf` (like
`fmt.Errorf`; context in golang/go#47641).
  • Loading branch information
pietern authored Mar 25, 2024
1 parent 9cf3dbe commit ed19466
Show file tree
Hide file tree
Showing 141 changed files with 841 additions and 698 deletions.
5 changes: 3 additions & 2 deletions bundle/artifacts/all.go
Original file line number Diff line number Diff line change
Expand Up @@ -7,6 +7,7 @@ import (
"slices"

"github.com/databricks/cli/bundle"
"github.com/databricks/cli/libs/diag"
"golang.org/x/exp/maps"
)

Expand All @@ -21,7 +22,7 @@ func (m *all) Name() string {
return fmt.Sprintf("artifacts.%sAll", m.name)
}

func (m *all) Apply(ctx context.Context, b *bundle.Bundle) error {
func (m *all) Apply(ctx context.Context, b *bundle.Bundle) diag.Diagnostics {
var out []bundle.Mutator

// Iterate with stable ordering.
Expand All @@ -31,7 +32,7 @@ func (m *all) Apply(ctx context.Context, b *bundle.Bundle) error {
for _, name := range keys {
m, err := m.fn(name)
if err != nil {
return err
return diag.FromErr(err)
}
if m != nil {
out = append(out, m)
Expand Down
19 changes: 10 additions & 9 deletions bundle/artifacts/artifacts.go
Original file line number Diff line number Diff line change
Expand Up @@ -14,6 +14,7 @@ import (
"github.com/databricks/cli/bundle/config"
"github.com/databricks/cli/bundle/libraries"
"github.com/databricks/cli/libs/cmdio"
"github.com/databricks/cli/libs/diag"
"github.com/databricks/cli/libs/filer"
"github.com/databricks/cli/libs/log"
)
Expand Down Expand Up @@ -57,17 +58,17 @@ func (m *basicBuild) Name() string {
return fmt.Sprintf("artifacts.Build(%s)", m.name)
}

func (m *basicBuild) Apply(ctx context.Context, b *bundle.Bundle) error {
func (m *basicBuild) Apply(ctx context.Context, b *bundle.Bundle) diag.Diagnostics {
artifact, ok := b.Config.Artifacts[m.name]
if !ok {
return fmt.Errorf("artifact doesn't exist: %s", m.name)
return diag.Errorf("artifact doesn't exist: %s", m.name)
}

cmdio.LogString(ctx, fmt.Sprintf("Building %s...", m.name))

out, err := artifact.Build(ctx)
if err != nil {
return fmt.Errorf("build for %s failed, error: %w, output: %s", m.name, err, out)
return diag.Errorf("build for %s failed, error: %v, output: %s", m.name, err, out)
}
log.Infof(ctx, "Build succeeded")

Expand All @@ -87,29 +88,29 @@ func (m *basicUpload) Name() string {
return fmt.Sprintf("artifacts.Upload(%s)", m.name)
}

func (m *basicUpload) Apply(ctx context.Context, b *bundle.Bundle) error {
func (m *basicUpload) Apply(ctx context.Context, b *bundle.Bundle) diag.Diagnostics {
artifact, ok := b.Config.Artifacts[m.name]
if !ok {
return fmt.Errorf("artifact doesn't exist: %s", m.name)
return diag.Errorf("artifact doesn't exist: %s", m.name)
}

if len(artifact.Files) == 0 {
return fmt.Errorf("artifact source is not configured: %s", m.name)
return diag.Errorf("artifact source is not configured: %s", m.name)
}

uploadPath, err := getUploadBasePath(b)
if err != nil {
return err
return diag.FromErr(err)
}

client, err := filer.NewWorkspaceFilesClient(b.WorkspaceClient(), uploadPath)
if err != nil {
return err
return diag.FromErr(err)
}

err = uploadArtifact(ctx, b, artifact, uploadPath, client)
if err != nil {
return fmt.Errorf("upload for %s failed, error: %w", m.name, err)
return diag.Errorf("upload for %s failed, error: %v", m.name, err)
}

return nil
Expand Down
3 changes: 2 additions & 1 deletion bundle/artifacts/autodetect.go
Original file line number Diff line number Diff line change
Expand Up @@ -5,6 +5,7 @@ import (

"github.com/databricks/cli/bundle"
"github.com/databricks/cli/bundle/artifacts/whl"
"github.com/databricks/cli/libs/diag"
"github.com/databricks/cli/libs/log"
)

Expand All @@ -19,7 +20,7 @@ func (m *autodetect) Name() string {
return "artifacts.DetectPackages"
}

func (m *autodetect) Apply(ctx context.Context, b *bundle.Bundle) error {
func (m *autodetect) Apply(ctx context.Context, b *bundle.Bundle) diag.Diagnostics {
// If artifacts section explicitly defined, do not try to auto detect packages
if b.Config.Artifacts != nil {
log.Debugf(ctx, "artifacts block is defined, skipping auto-detecting")
Expand Down
7 changes: 4 additions & 3 deletions bundle/artifacts/build.go
Original file line number Diff line number Diff line change
Expand Up @@ -6,6 +6,7 @@ import (
"path/filepath"

"github.com/databricks/cli/bundle"
"github.com/databricks/cli/libs/diag"
)

func BuildAll() bundle.Mutator {
Expand All @@ -27,18 +28,18 @@ func (m *build) Name() string {
return fmt.Sprintf("artifacts.Build(%s)", m.name)
}

func (m *build) Apply(ctx context.Context, b *bundle.Bundle) error {
func (m *build) Apply(ctx context.Context, b *bundle.Bundle) diag.Diagnostics {
artifact, ok := b.Config.Artifacts[m.name]
if !ok {
return fmt.Errorf("artifact doesn't exist: %s", m.name)
return diag.Errorf("artifact doesn't exist: %s", m.name)
}

// Skip building if build command is not specified or infered
if artifact.BuildCommand == "" {
// If no build command was specified or infered and there is no
// artifact output files specified, artifact is misconfigured
if len(artifact.Files) == 0 {
return fmt.Errorf("misconfigured artifact: please specify 'build' or 'files' property")
return diag.Errorf("misconfigured artifact: please specify 'build' or 'files' property")
}
return nil
}
Expand Down
5 changes: 3 additions & 2 deletions bundle/artifacts/infer.go
Original file line number Diff line number Diff line change
Expand Up @@ -7,6 +7,7 @@ import (
"github.com/databricks/cli/bundle"
"github.com/databricks/cli/bundle/artifacts/whl"
"github.com/databricks/cli/bundle/config"
"github.com/databricks/cli/libs/diag"
)

var inferMutators map[config.ArtifactType]mutatorFactory = map[config.ArtifactType]mutatorFactory{
Expand Down Expand Up @@ -41,10 +42,10 @@ func (m *infer) Name() string {
return fmt.Sprintf("artifacts.Infer(%s)", m.name)
}

func (m *infer) Apply(ctx context.Context, b *bundle.Bundle) error {
func (m *infer) Apply(ctx context.Context, b *bundle.Bundle) diag.Diagnostics {
artifact, ok := b.Config.Artifacts[m.name]
if !ok {
return fmt.Errorf("artifact doesn't exist: %s", m.name)
return diag.Errorf("artifact doesn't exist: %s", m.name)
}

// only try to infer command if it's not already defined
Expand Down
17 changes: 9 additions & 8 deletions bundle/artifacts/upload.go
Original file line number Diff line number Diff line change
Expand Up @@ -7,6 +7,7 @@ import (

"github.com/databricks/cli/bundle"
"github.com/databricks/cli/bundle/config"
"github.com/databricks/cli/libs/diag"
"github.com/databricks/databricks-sdk-go/service/workspace"
)

Expand All @@ -33,14 +34,14 @@ func (m *upload) Name() string {
return fmt.Sprintf("artifacts.Upload(%s)", m.name)
}

func (m *upload) Apply(ctx context.Context, b *bundle.Bundle) error {
func (m *upload) Apply(ctx context.Context, b *bundle.Bundle) diag.Diagnostics {
artifact, ok := b.Config.Artifacts[m.name]
if !ok {
return fmt.Errorf("artifact doesn't exist: %s", m.name)
return diag.Errorf("artifact doesn't exist: %s", m.name)
}

if len(artifact.Files) == 0 {
return fmt.Errorf("artifact source is not configured: %s", m.name)
return diag.Errorf("artifact source is not configured: %s", m.name)
}

// Check if source paths are absolute, if not, make them absolute
Expand All @@ -57,11 +58,11 @@ func (m *upload) Apply(ctx context.Context, b *bundle.Bundle) error {
for _, f := range artifact.Files {
matches, err := filepath.Glob(f.Source)
if err != nil {
return fmt.Errorf("unable to find files for %s: %w", f.Source, err)
return diag.Errorf("unable to find files for %s: %v", f.Source, err)
}

if len(matches) == 0 {
return fmt.Errorf("no files found for %s", f.Source)
return diag.Errorf("no files found for %s", f.Source)
}

for _, match := range matches {
Expand All @@ -81,10 +82,10 @@ func (m *cleanUp) Name() string {
return "artifacts.CleanUp"
}

func (m *cleanUp) Apply(ctx context.Context, b *bundle.Bundle) error {
func (m *cleanUp) Apply(ctx context.Context, b *bundle.Bundle) diag.Diagnostics {
uploadPath, err := getUploadBasePath(b)
if err != nil {
return err
return diag.FromErr(err)
}

b.WorkspaceClient().Workspace.Delete(ctx, workspace.Delete{
Expand All @@ -94,7 +95,7 @@ func (m *cleanUp) Apply(ctx context.Context, b *bundle.Bundle) error {

err = b.WorkspaceClient().Workspace.MkdirsByPath(ctx, uploadPath)
if err != nil {
return fmt.Errorf("unable to create directory for %s: %w", uploadPath, err)
return diag.Errorf("unable to create directory for %s: %v", uploadPath, err)
}

return nil
Expand Down
11 changes: 6 additions & 5 deletions bundle/artifacts/upload_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -9,13 +9,14 @@ import (
"github.com/databricks/cli/bundle"
"github.com/databricks/cli/bundle/config"
"github.com/databricks/cli/bundle/internal/bundletest"
"github.com/databricks/cli/libs/diag"
"github.com/databricks/cli/libs/testfile"
"github.com/stretchr/testify/require"
)

type noop struct{}

func (n *noop) Apply(context.Context, *bundle.Bundle) error {
func (n *noop) Apply(context.Context, *bundle.Bundle) diag.Diagnostics {
return nil
}

Expand Down Expand Up @@ -57,8 +58,8 @@ func TestExpandGlobFilesSource(t *testing.T) {
return &noop{}
}

err = bundle.Apply(context.Background(), b, u)
require.NoError(t, err)
diags := bundle.Apply(context.Background(), b, u)
require.NoError(t, diags.Error())

require.Equal(t, 2, len(b.Config.Artifacts["test"].Files))
require.Equal(t, filepath.Join(rootPath, "test", "myjar1.jar"), b.Config.Artifacts["test"].Files[0].Source)
Expand Down Expand Up @@ -93,6 +94,6 @@ func TestExpandGlobFilesSourceWithNoMatches(t *testing.T) {
return &noop{}
}

err = bundle.Apply(context.Background(), b, u)
require.ErrorContains(t, err, "no files found for")
diags := bundle.Apply(context.Background(), b, u)
require.ErrorContains(t, diags.Error(), "no files found for")
}
5 changes: 3 additions & 2 deletions bundle/artifacts/whl/autodetect.go
Original file line number Diff line number Diff line change
Expand Up @@ -11,6 +11,7 @@ import (
"github.com/databricks/cli/bundle"
"github.com/databricks/cli/bundle/config"
"github.com/databricks/cli/bundle/libraries"
"github.com/databricks/cli/libs/diag"
"github.com/databricks/cli/libs/log"
)

Expand All @@ -25,7 +26,7 @@ func (m *detectPkg) Name() string {
return "artifacts.whl.AutoDetect"
}

func (m *detectPkg) Apply(ctx context.Context, b *bundle.Bundle) error {
func (m *detectPkg) Apply(ctx context.Context, b *bundle.Bundle) diag.Diagnostics {
wheelTasks := libraries.FindAllWheelTasksWithLocalLibraries(b)
if len(wheelTasks) == 0 {
log.Infof(ctx, "No local wheel tasks in databricks.yml config, skipping auto detect")
Expand All @@ -50,7 +51,7 @@ func (m *detectPkg) Apply(ctx context.Context, b *bundle.Bundle) error {

pkgPath, err := filepath.Abs(b.Config.Path)
if err != nil {
return err
return diag.FromErr(err)
}
b.Config.Artifacts[module] = &config.Artifact{
Path: pkgPath,
Expand Down
9 changes: 5 additions & 4 deletions bundle/artifacts/whl/build.go
Original file line number Diff line number Diff line change
Expand Up @@ -9,6 +9,7 @@ import (
"github.com/databricks/cli/bundle"
"github.com/databricks/cli/bundle/config"
"github.com/databricks/cli/libs/cmdio"
"github.com/databricks/cli/libs/diag"
"github.com/databricks/cli/libs/log"
"github.com/databricks/cli/libs/python"
)
Expand All @@ -27,10 +28,10 @@ func (m *build) Name() string {
return fmt.Sprintf("artifacts.whl.Build(%s)", m.name)
}

func (m *build) Apply(ctx context.Context, b *bundle.Bundle) error {
func (m *build) Apply(ctx context.Context, b *bundle.Bundle) diag.Diagnostics {
artifact, ok := b.Config.Artifacts[m.name]
if !ok {
return fmt.Errorf("artifact doesn't exist: %s", m.name)
return diag.Errorf("artifact doesn't exist: %s", m.name)
}

cmdio.LogString(ctx, fmt.Sprintf("Building %s...", m.name))
Expand All @@ -43,13 +44,13 @@ func (m *build) Apply(ctx context.Context, b *bundle.Bundle) error {

out, err := artifact.Build(ctx)
if err != nil {
return fmt.Errorf("build failed %s, error: %w, output: %s", m.name, err, out)
return diag.Errorf("build failed %s, error: %v, output: %s", m.name, err, out)
}
log.Infof(ctx, "Build succeeded")

wheels := python.FindFilesWithSuffixInPath(distPath, ".whl")
if len(wheels) == 0 {
return fmt.Errorf("cannot find built wheel in %s for package %s", dir, m.name)
return diag.Errorf("cannot find built wheel in %s for package %s", dir, m.name)
}
for _, wheel := range wheels {
artifact.Files = append(artifact.Files, config.ArtifactFile{
Expand Down
3 changes: 2 additions & 1 deletion bundle/artifacts/whl/from_libraries.go
Original file line number Diff line number Diff line change
Expand Up @@ -7,6 +7,7 @@ import (
"github.com/databricks/cli/bundle"
"github.com/databricks/cli/bundle/config"
"github.com/databricks/cli/bundle/libraries"
"github.com/databricks/cli/libs/diag"
"github.com/databricks/cli/libs/log"
)

Expand All @@ -20,7 +21,7 @@ func (m *fromLibraries) Name() string {
return "artifacts.whl.DefineArtifactsFromLibraries"
}

func (*fromLibraries) Apply(ctx context.Context, b *bundle.Bundle) error {
func (*fromLibraries) Apply(ctx context.Context, b *bundle.Bundle) diag.Diagnostics {
if len(b.Config.Artifacts) != 0 {
log.Debugf(ctx, "Skipping defining artifacts from libraries because artifacts section is explicitly defined")
return nil
Expand Down
5 changes: 3 additions & 2 deletions bundle/artifacts/whl/infer.go
Original file line number Diff line number Diff line change
Expand Up @@ -5,18 +5,19 @@ import (
"fmt"

"github.com/databricks/cli/bundle"
"github.com/databricks/cli/libs/diag"
"github.com/databricks/cli/libs/python"
)

type infer struct {
name string
}

func (m *infer) Apply(ctx context.Context, b *bundle.Bundle) error {
func (m *infer) Apply(ctx context.Context, b *bundle.Bundle) diag.Diagnostics {
artifact := b.Config.Artifacts[m.name]
py, err := python.DetectExecutable(ctx)
if err != nil {
return err
return diag.FromErr(err)
}

// Note: using --build-number (build tag) flag does not help with re-installing
Expand Down
3 changes: 2 additions & 1 deletion bundle/config/mutator/default_target.go
Original file line number Diff line number Diff line change
Expand Up @@ -6,6 +6,7 @@ import (

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

type defineDefaultTarget struct {
Expand All @@ -24,7 +25,7 @@ func (m *defineDefaultTarget) Name() string {
return fmt.Sprintf("DefineDefaultTarget(%s)", m.name)
}

func (m *defineDefaultTarget) Apply(_ context.Context, b *bundle.Bundle) error {
func (m *defineDefaultTarget) Apply(_ context.Context, b *bundle.Bundle) diag.Diagnostics {
// Nothing to do if the configuration has at least 1 target.
if len(b.Config.Targets) > 0 {
return nil
Expand Down
10 changes: 6 additions & 4 deletions bundle/config/mutator/default_target_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -13,8 +13,9 @@ import (

func TestDefaultTarget(t *testing.T) {
b := &bundle.Bundle{}
err := bundle.Apply(context.Background(), b, mutator.DefineDefaultTarget())
require.NoError(t, err)
diags := bundle.Apply(context.Background(), b, mutator.DefineDefaultTarget())
require.NoError(t, diags.Error())

env, ok := b.Config.Targets["default"]
assert.True(t, ok)
assert.Equal(t, &config.Target{}, env)
Expand All @@ -28,8 +29,9 @@ func TestDefaultTargetAlreadySpecified(t *testing.T) {
},
},
}
err := bundle.Apply(context.Background(), b, mutator.DefineDefaultTarget())
require.NoError(t, err)
diags := bundle.Apply(context.Background(), b, mutator.DefineDefaultTarget())
require.NoError(t, diags.Error())

_, ok := b.Config.Targets["default"]
assert.False(t, ok)
}
Loading

0 comments on commit ed19466

Please sign in to comment.