This commit is contained in:
Shreyas Goenka 2023-04-24 12:21:44 +02:00
commit 59a77bc842
No known key found for this signature in database
GPG Key ID: 92A07DF49CCB0622
35 changed files with 328 additions and 222 deletions

View File

@ -43,6 +43,6 @@ jobs:
uses: goreleaser/goreleaser-action@v4
with:
version: latest
args: release --rm-dist
args: release
env:
GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }}

5
CHANGELOG.md Normal file
View File

@ -0,0 +1,5 @@
# Version changelog
## 0.0.30
* Initial preview release of the Bricks CLI.

View File

@ -24,7 +24,7 @@ build: vendor
snapshot:
@echo "✓ Building dev snapshot"
@goreleaser build --snapshot --rm-dist --single-target
@goreleaser build --snapshot --clean --single-target
vendor:
@echo "✓ Filling vendor folder with library code ..."

View File

@ -1,28 +1,21 @@
# Bricks CLI 🧱 [![build](https://github.com/databricks/bricks/workflows/build/badge.svg?branch=main)](https://github.com/databricks/bricks/actions?query=workflow%3Abuild+branch%3Amain)
# Bricks CLI
_Where's "data"? Secured by the unity catalog. Projects build lifecycle is secured by `bricks` 🧱_
[![build](https://github.com/databricks/bricks/workflows/build/badge.svg?branch=main)](https://github.com/databricks/bricks/actions?query=workflow%3Abuild+branch%3Amain)
This is an early PoC at this stage. `make build` (or [download the latest from releases page](https://github.com/databricks/bricks/releases)).
This project is in private preview.
Reuses authentication from Databricks CLI. And terraform provider. See details here: https://registry.terraform.io/providers/databrickslabs/databricks/latest/docs#environment-variables
Documentation is available at https://docs.databricks.com/dev-tools/cli/bricks-cli.html.
Supports:
* Databricks CLI
* Databricks CLI Profiles
* Azure CLI Auth
* Azure MSI Auth
* Azure SPN Auth
* Google OIDC Auth
* Direct `DATABRICKS_HOST`, `DATABRICKS_TOKEN` or `DATABRICKS_USERNAME` + `DATABRICKS_PASSWORD` variables.
## Installation
What works:
This CLI is packaged as a dependency-free binary executable and may be located in any directory.
* `./bricks fs ls /`
* `./bricks test`
* `./bricks launch test.py`
For convenient access, copy the `bricks` binary to any directory listed in `$PATH`.
What doesn't work:
Confirm the binary works by executing `bricks version`.
* Everything else.
## Authentication
This project reuses some code from Databricks Terraform Provider
This CLI follows the Databricks Unified Authentication principles.
You can find a detailed description at https://github.com/databricks/databricks-sdk-go#authentication.

View File

@ -6,9 +6,12 @@ import (
"fmt"
"reflect"
"regexp"
"sort"
"strings"
"github.com/databricks/bricks/bundle"
"golang.org/x/exp/maps"
"golang.org/x/exp/slices"
)
const Delimiter = "."
@ -63,7 +66,14 @@ func (s *stringField) interpolate(fns []LookupFunction, lookup map[string]string
}
type accumulator struct {
// all string fields in the bundle config
strings map[string]*stringField
// contains path -> resolved_string mapping for string fields in the config
// The resolved strings will NOT contain any variable references that could
// have been resolved, however there might still be references that cannot
// be resolved
memo map[string]string
}
// jsonFieldName returns the name in a field's `json` tag.
@ -138,25 +148,7 @@ func (a *accumulator) walk(scope []string, rv reflect.Value, s setter) {
}
}
// Gathers the strings for a list of paths.
// The fields in these paths may not depend on other fields,
// as we don't support full DAG lookup yet (only single level).
func (a *accumulator) gather(paths []string) (map[string]string, error) {
var out = make(map[string]string)
for _, path := range paths {
f, ok := a.strings[path]
if !ok {
return nil, fmt.Errorf("%s is not defined", path)
}
deps := f.dependsOn()
if len(deps) > 0 {
return nil, fmt.Errorf("%s depends on %s", path, strings.Join(deps, ", "))
}
out[path] = f.Get()
}
return out, nil
}
// walk and gather all string fields in the config
func (a *accumulator) start(v any) {
rv := reflect.ValueOf(v)
if rv.Type().Kind() != reflect.Pointer {
@ -168,25 +160,64 @@ func (a *accumulator) start(v any) {
}
a.strings = make(map[string]*stringField)
a.memo = make(map[string]string)
a.walk([]string{}, rv, nilSetter{})
}
func (a *accumulator) expand(fns ...LookupFunction) error {
for path, v := range a.strings {
ds := v.dependsOn()
if len(ds) == 0 {
continue
// recursively interpolate variables in a depth first manner
func (a *accumulator) Resolve(path string, seenPaths []string, fns ...LookupFunction) error {
// return early if the path is already resolved
if _, ok := a.memo[path]; ok {
return nil
}
// Create map to be used for interpolation
m, err := a.gather(ds)
// fetch the string node to resolve
field, ok := a.strings[path]
if !ok {
return fmt.Errorf("could not find string field with path %s", path)
}
// return early if the string field has no variables to interpolate
if len(field.dependsOn()) == 0 {
a.memo[path] = field.Get()
return nil
}
// resolve all variables refered in the root string field
for _, childFieldPath := range field.dependsOn() {
// error if there is a loop in variable interpolation
if slices.Contains(seenPaths, childFieldPath) {
return fmt.Errorf("cycle detected in field resolution: %s", strings.Join(append(seenPaths, childFieldPath), " -> "))
}
// recursive resolve variables in the child fields
err := a.Resolve(childFieldPath, append(seenPaths, childFieldPath), fns...)
if err != nil {
return fmt.Errorf("cannot interpolate %s: %w", path, err)
return err
}
}
v.interpolate(fns, m)
// interpolate root string once all variable references in it have been resolved
field.interpolate(fns, a.memo)
// record interpolated string in memo
a.memo[path] = field.Get()
return nil
}
// Interpolate all string fields in the config
func (a *accumulator) expand(fns ...LookupFunction) error {
// sorting paths for stable order of iteration
paths := maps.Keys(a.strings)
sort.Strings(paths)
// iterate over paths for all strings fields in the config
for _, path := range paths {
err := a.Resolve(path, []string{path}, fns...)
if err != nil {
return err
}
}
return nil
}

View File

@ -97,3 +97,31 @@ func TestInterpolationWithMap(t *testing.T) {
assert.Equal(t, "a", f.F["a"])
assert.Equal(t, "a", f.F["b"])
}
func TestInterpolationWithResursiveVariableReferences(t *testing.T) {
f := foo{
A: "a",
B: "(${a})",
C: "${a} ${b}",
}
err := expand(&f)
require.NoError(t, err)
assert.Equal(t, "a", f.A)
assert.Equal(t, "(a)", f.B)
assert.Equal(t, "a (a)", f.C)
}
func TestInterpolationVariableLoopError(t *testing.T) {
d := "${b}"
f := foo{
A: "a",
B: "${c}",
C: "${d}",
D: &d,
}
err := expand(&f)
assert.ErrorContains(t, err, "cycle detected in field resolution: b -> c -> d -> b")
}

View File

@ -7,7 +7,7 @@ import (
"github.com/databricks/bricks/bundle"
"github.com/databricks/bricks/bundle/config"
"github.com/databricks/bricks/bundle/config/mutator"
"github.com/databricks/databricks-sdk-go/service/scim"
"github.com/databricks/databricks-sdk-go/service/iam"
"github.com/stretchr/testify/assert"
"github.com/stretchr/testify/require"
)
@ -16,7 +16,7 @@ func TestExpandWorkspaceRoot(t *testing.T) {
bundle := &bundle.Bundle{
Config: config.Root{
Workspace: config.Workspace{
CurrentUser: &scim.User{
CurrentUser: &iam.User{
UserName: "jane@doe.com",
},
RootPath: "~/foo",
@ -32,7 +32,7 @@ func TestExpandWorkspaceRootDoesNothing(t *testing.T) {
bundle := &bundle.Bundle{
Config: config.Root{
Workspace: config.Workspace{
CurrentUser: &scim.User{
CurrentUser: &iam.User{
UserName: "jane@doe.com",
},
RootPath: "/Users/charly@doe.com/foo",
@ -48,7 +48,7 @@ func TestExpandWorkspaceRootWithoutRoot(t *testing.T) {
bundle := &bundle.Bundle{
Config: config.Root{
Workspace: config.Workspace{
CurrentUser: &scim.User{
CurrentUser: &iam.User{
UserName: "jane@doe.com",
},
},

View File

@ -1,11 +1,11 @@
package resources
import "github.com/databricks/databricks-sdk-go/service/mlflow"
import "github.com/databricks/databricks-sdk-go/service/ml"
type MlflowExperiment struct {
Permissions []Permission `json:"permissions,omitempty"`
Paths
*mlflow.Experiment
*ml.Experiment
}

View File

@ -1,11 +1,11 @@
package resources
import "github.com/databricks/databricks-sdk-go/service/mlflow"
import "github.com/databricks/databricks-sdk-go/service/ml"
type MlflowModel struct {
Permissions []Permission `json:"permissions,omitempty"`
Paths
*mlflow.RegisteredModel
*ml.Model
}

View File

@ -7,7 +7,7 @@ import (
"github.com/databricks/bricks/libs/databrickscfg"
"github.com/databricks/databricks-sdk-go"
"github.com/databricks/databricks-sdk-go/config"
"github.com/databricks/databricks-sdk-go/service/scim"
"github.com/databricks/databricks-sdk-go/service/iam"
)
// Workspace defines configurables at the workspace level.
@ -37,7 +37,7 @@ type Workspace struct {
// CurrentUser holds the current user.
// This is set after configuration initialization.
CurrentUser *scim.User `json:"current_user,omitempty" bundle:"readonly"`
CurrentUser *iam.User `json:"current_user,omitempty" bundle:"readonly"`
// Remote workspace base path for deployment state, for artifacts, as synchronization target.
// This defaults to "~/.bundle/${bundle.name}/${bundle.environment}" where "~" expands to

View File

@ -5,10 +5,9 @@ import (
"github.com/databricks/bricks/bundle/config"
"github.com/databricks/bricks/bundle/config/resources"
"github.com/databricks/databricks-sdk-go/service/clusters"
"github.com/databricks/databricks-sdk-go/service/compute"
"github.com/databricks/databricks-sdk-go/service/jobs"
"github.com/databricks/databricks-sdk-go/service/libraries"
"github.com/databricks/databricks-sdk-go/service/mlflow"
"github.com/databricks/databricks-sdk-go/service/ml"
"github.com/databricks/databricks-sdk-go/service/pipelines"
"github.com/stretchr/testify/assert"
"github.com/stretchr/testify/require"
@ -21,7 +20,7 @@ func TestConvertJob(t *testing.T) {
JobClusters: []jobs.JobCluster{
{
JobClusterKey: "key",
NewCluster: &clusters.BaseClusterInfo{
NewCluster: &compute.BaseClusterInfo{
SparkVersion: "10.4.x-scala2.12",
},
},
@ -82,9 +81,9 @@ func TestConvertJobTaskLibraries(t *testing.T) {
Tasks: []jobs.JobTaskSettings{
{
TaskKey: "key",
Libraries: []libraries.Library{
Libraries: []compute.Library{
{
Pypi: &libraries.PythonPyPiLibrary{
Pypi: &compute.PythonPyPiLibrary{
Package: "mlflow",
},
},
@ -171,10 +170,10 @@ func TestConvertPipelinePermissions(t *testing.T) {
func TestConvertModel(t *testing.T) {
var src = resources.MlflowModel{
RegisteredModel: &mlflow.RegisteredModel{
Model: &ml.Model{
Name: "name",
Description: "description",
Tags: []mlflow.RegisteredModelTag{
Tags: []ml.ModelTag{
{
Key: "k1",
Value: "v1",
@ -235,7 +234,7 @@ func TestConvertModelPermissions(t *testing.T) {
func TestConvertExperiment(t *testing.T) {
var src = resources.MlflowExperiment{
Experiment: &mlflow.Experiment{
Experiment: &ml.Experiment{
Name: "name",
},
}

View File

@ -112,7 +112,7 @@ func (r *jobRunner) logFailedTasks(ctx context.Context, runId int64) {
red := color.New(color.FgRed).SprintFunc()
green := color.New(color.FgGreen).SprintFunc()
yellow := color.New(color.FgYellow).SprintFunc()
run, err := w.Jobs.GetRun(ctx, jobs.GetRun{
run, err := w.Jobs.GetRun(ctx, jobs.GetRunRequest{
RunId: runId,
})
if err != nil {
@ -126,7 +126,7 @@ func (r *jobRunner) logFailedTasks(ctx context.Context, runId int64) {
if isSuccess(task) {
log.Infof(ctx, "task %s completed successfully", green(task.TaskKey))
} else if isFailed(task) {
taskInfo, err := w.Jobs.GetRunOutput(ctx, jobs.GetRunOutput{
taskInfo, err := w.Jobs.GetRunOutput(ctx, jobs.GetRunOutputRequest{
RunId: task.RunId,
})
if err != nil {

View File

@ -48,7 +48,7 @@ func (out *JobOutput) String() (string, error) {
}
func GetJobOutput(ctx context.Context, w *databricks.WorkspaceClient, runId int64) (*JobOutput, error) {
jobRun, err := w.Jobs.GetRun(ctx, jobs.GetRun{
jobRun, err := w.Jobs.GetRun(ctx, jobs.GetRunRequest{
RunId: runId,
})
if err != nil {
@ -58,7 +58,7 @@ func GetJobOutput(ctx context.Context, w *databricks.WorkspaceClient, runId int6
TaskOutputs: make(map[string]RunOutput),
}
for _, task := range jobRun.Tasks {
jobRunOutput, err := w.Jobs.GetRunOutput(ctx, jobs.GetRunOutput{
jobRunOutput, err := w.Jobs.GetRunOutput(ctx, jobs.GetRunOutputRequest{
RunId: task.RunId,
})
if err != nil {

View File

@ -54,7 +54,7 @@ func (r *pipelineRunner) logErrorEvent(ctx context.Context, pipelineId string, u
// Otherwise for long lived pipelines, there can be a lot of unnecessary
// latency due to multiple pagination API calls needed underneath the hood for
// ListPipelineEventsAll
res, err := w.Pipelines.Impl().ListPipelineEvents(ctx, pipelines.ListPipelineEvents{
res, err := w.Pipelines.Impl().ListPipelineEvents(ctx, pipelines.ListPipelineEventsRequest{
Filter: `level='ERROR'`,
MaxResults: 100,
PipelineId: pipelineId,

View File

@ -9,6 +9,15 @@ import (
"github.com/databricks/databricks-sdk-go/service/pipelines"
)
// The dlt backend computes events for pipeline runs which are accessable through
// the 2.0/pipelines/{pipeline_id}/events API
//
// There are 4 levels for these events: ("ERROR", "WARN", "INFO", "METRICS")
//
// Here's short introduction to a few important events we display on the console:
//
// 1. `update_progress`: A state transition occured for the entire pipeline update
// 2. `flow_progress`: A state transition occured for a single flow in the pipeine
type ProgressEvent pipelines.PipelineEvent
func (event *ProgressEvent) String() string {
@ -21,8 +30,8 @@ func (event *ProgressEvent) String() string {
result.WriteString(event.Level.String() + " ")
result.WriteString(fmt.Sprintf(`"%s"`, event.Message))
// construct error string
if event.Error != nil {
// construct error string if level=`Error`
if event.Level == pipelines.EventLevelError && event.Error != nil {
for _, exception := range event.Error.Exceptions {
result.WriteString(fmt.Sprintf("\n%s", exception.Message))
}
@ -69,7 +78,7 @@ func (l *UpdateTracker) Events(ctx context.Context) ([]ProgressEvent, error) {
}
// we only check the most recent 100 events for progress
response, err := l.w.Pipelines.Impl().ListPipelineEvents(ctx, pipelines.ListPipelineEvents{
response, err := l.w.Pipelines.Impl().ListPipelineEvents(ctx, pipelines.ListPipelineEventsRequest{
PipelineId: l.PipelineId,
MaxResults: 100,
Filter: filter,

View File

@ -55,3 +55,24 @@ func TestUpdateErrorEventToString(t *testing.T) {
}
assert.Equal(t, "2023-03-27T23:30:36.122Z update_progress ERROR \"failed to update pipeline\"\nparsing error", event.String())
}
func TestUpdateErrorIgnoredForWarnEvents(t *testing.T) {
event := ProgressEvent{
EventType: "update_progress",
Message: "failed to update pipeline",
Level: pipelines.EventLevelWarn,
Origin: &pipelines.Origin{
FlowName: "my_flow",
PipelineName: "my_pipeline",
},
Timestamp: "2023-03-27T23:30:36.122Z",
Error: &pipelines.ErrorDetail{
Exceptions: []pipelines.SerializedException{
{
Message: "THIS IS IGNORED",
},
},
},
}
assert.Equal(t, "2023-03-27T23:30:36.122Z update_progress WARN \"failed to update pipeline\"", event.String())
}

View File

@ -0,0 +1,10 @@
bundle:
name: foo ${workspace.profile}
workspace:
profile: bar
resources:
jobs:
my_job:
name: "${bundle.name} | ${workspace.profile}"

View File

@ -0,0 +1,23 @@
package config_tests
import (
"context"
"testing"
"github.com/databricks/bricks/bundle"
"github.com/databricks/bricks/bundle/config/interpolation"
"github.com/stretchr/testify/assert"
"github.com/stretchr/testify/require"
)
func TestInterpolation(t *testing.T) {
b := load(t, "./interpolation")
err := bundle.Apply(context.Background(), b, []bundle.Mutator{
interpolation.Interpolate(
interpolation.IncludeLookupsInPath("bundle"),
interpolation.IncludeLookupsInPath("workspace"),
)})
require.NoError(t, err)
assert.Equal(t, "foo bar", b.Config.Bundle.Name)
assert.Equal(t, "foo bar | bar", b.Config.Resources.Jobs["my_job"].Name)
}

View File

@ -1,101 +0,0 @@
package deploy
import (
"os"
"path/filepath"
"github.com/databricks/bricks/bundle/deployer"
"github.com/databricks/bricks/cmd/bundle/debug"
"github.com/databricks/bricks/libs/log"
"github.com/databricks/databricks-sdk-go"
"github.com/hashicorp/go-version"
"github.com/hashicorp/hc-install/product"
"github.com/hashicorp/hc-install/releases"
"github.com/spf13/cobra"
)
// TODO: will add integration test once terraform binary is bundled with bricks
var deployTerraformCmd = &cobra.Command{
Use: "deploy",
Short: "deploys resources defined in a terraform config to a Databricks workspace",
RunE: func(cmd *cobra.Command, args []string) error {
ctx := cmd.Context()
// default to cwd
if *localRoot == "" {
cwd, err := os.Getwd()
if err != nil {
return err
}
*localRoot = cwd
}
if *terraformBinaryPath == "" {
installer := releases.ExactVersion{
Product: product.Terraform,
Version: version.Must(version.NewVersion("1.2.4")),
}
execPath, err := installer.Install(ctx)
if err != nil {
log.Errorf(ctx, "error installing Terraform: %s", err)
}
*terraformBinaryPath = execPath
defer installer.Remove(ctx)
}
// TODO: load bundle and get the workspace client from there once bundles
// are stable
wsc, err := databricks.NewWorkspaceClient()
if err != nil {
return err
}
d, err := deployer.Create(ctx, *env, *localRoot, *remoteRoot, wsc)
if err != nil {
return err
}
if *terraformHcl == "" {
*terraformHcl = filepath.Join(d.DefaultTerraformRoot())
}
status, err := d.ApplyTerraformConfig(ctx, *terraformHcl, *terraformBinaryPath, *isForced)
switch status {
case deployer.Failed:
log.Errorf(ctx, "failed to initiate deployment")
case deployer.NoChanges:
log.Infof(ctx, "no changes detected")
case deployer.Partial:
log.Errorf(ctx, "started deployment, but failed to complete")
case deployer.PartialButUntracked:
log.Errorf(ctx, "started deployment, but failed to complete. Any partially deployed resources in this run are untracked in the databricks workspace and might not be cleaned up on future deployments")
case deployer.CompleteButUntracked:
log.Errorf(ctx, "deployment complete. Failed to track deployed resources. Any deployed resources in this run are untracked in the databricks workspace and might not be cleaned up on future deployments")
case deployer.Complete:
log.Infof(ctx, "deployment complete")
}
return err
},
}
var remoteRoot *string
var localRoot *string
var env *string
var isForced *bool
var terraformHcl *string
// TODO: remove this arguement once we package a terraform binary with the bricks cli
var terraformBinaryPath *string
func init() {
remoteRoot = deployTerraformCmd.Flags().String("remote-root", "", "workspace root of the project eg: /Repos/me@example.com/test-repo")
localRoot = deployTerraformCmd.Flags().String("local-root", "", "path to the root directory of the DAB project. default: current working dir")
terraformBinaryPath = deployTerraformCmd.Flags().String("terraform-cli-binary", "", "path to a terraform CLI executable binary")
env = deployTerraformCmd.Flags().String("env", "development", "environment to deploy on. default: development")
isForced = deployTerraformCmd.Flags().Bool("force", false, "force deploy your DAB to the workspace. default: false")
terraformHcl = deployTerraformCmd.Flags().String("terraform-hcl", "", "path to the terraform config file from project root")
deployTerraformCmd.MarkFlagRequired("remote-root")
debug.AddCommand(deployTerraformCmd)
}

View File

@ -8,7 +8,7 @@ import (
// rootCmd represents the root command for the bundle subcommand.
var rootCmd = &cobra.Command{
Use: "bundle",
Short: "Databricks Application Bundles",
Short: "Databricks Asset Bundles",
}
func AddCommand(cmd *cobra.Command) {

View File

@ -7,6 +7,7 @@ import (
"strings"
"github.com/databricks/bricks/internal/build"
"github.com/databricks/bricks/libs/cmdio"
"github.com/databricks/bricks/libs/log"
"github.com/spf13/cobra"
"golang.org/x/exp/slog"
@ -15,8 +16,7 @@ import (
// RootCmd represents the base command when called without any subcommands
var RootCmd = &cobra.Command{
Use: "bricks",
Short: "Databricks project lifecycle management",
Long: `Where's "data"? Secured by the unity catalog. Projects build lifecycle is secured by bricks`,
Short: "Bricks CLI",
// Cobra prints the usage string to stderr if a command returns an error.
// This usage string should only be displayed if an invalid combination of flags
@ -24,6 +24,9 @@ var RootCmd = &cobra.Command{
// The usage string is include in [flagErrorFunc] for flag errors only.
SilenceUsage: true,
// Silence error printing by cobra. Errors are printed through cmdio.
SilenceErrors: true,
PersistentPreRunE: func(cmd *cobra.Command, args []string) error {
ctx := cmd.Context()
@ -65,6 +68,11 @@ func Execute() {
// Run the command
cmd, err := RootCmd.ExecuteContextC(ctx)
if err != nil {
// If cmdio logger initialization succeeds, then this function logs with the
// initialized cmdio logger, otherwise with the default cmdio logger
cmdio.LogError(cmd.Context(), err)
}
// Log exit status and error
// We only log if logger initialization succeeded and is stored in command

View File

@ -18,7 +18,7 @@ func fetchDirs(ctx context.Context, wsc *databricks.WorkspaceClient, path string
go func() {
defer close(ch)
files, err := wsc.Workspace.ListAll(ctx, workspace.List{
files, err := wsc.Workspace.ListAll(ctx, workspace.ListWorkspaceRequest{
Path: path,
})
if err != nil {

11
go.mod
View File

@ -3,7 +3,7 @@ module github.com/databricks/bricks
go 1.18
require (
github.com/databricks/databricks-sdk-go v0.7.0
github.com/databricks/databricks-sdk-go v0.8.0
github.com/ghodss/yaml v1.0.0 // MIT + NOTICE
github.com/pkg/browser v0.0.0-20210911075715-681adbf594b8 // BSD-2-Clause
github.com/sabhiram/go-gitignore v0.0.0-20210923224102-525f6e181f06 // MIT
@ -28,6 +28,7 @@ require (
require (
github.com/apparentlymart/go-textseg/v13 v13.0.0 // indirect
github.com/google/s2a-go v0.1.0 // indirect
github.com/hashicorp/go-cleanhttp v0.5.2 // indirect
github.com/mattn/go-colorable v0.1.13 // indirect
github.com/mattn/go-isatty v0.0.17 // indirect
@ -48,14 +49,14 @@ require (
github.com/pmezard/go-difflib v1.0.0 // indirect
github.com/spf13/pflag v1.0.5
go.opencensus.io v0.24.0 // indirect
golang.org/x/net v0.8.0 // indirect
golang.org/x/oauth2 v0.6.0
golang.org/x/net v0.9.0 // indirect
golang.org/x/oauth2 v0.7.0
golang.org/x/sys v0.7.0 // indirect
golang.org/x/text v0.9.0
golang.org/x/time v0.3.0 // indirect
google.golang.org/api v0.115.0 // indirect
google.golang.org/api v0.118.0 // indirect
google.golang.org/appengine v1.6.7 // indirect
google.golang.org/genproto v0.0.0-20230331144136-dcfb400f0633 // indirect
google.golang.org/genproto v0.0.0-20230403163135-c38d8f061ccd // indirect
google.golang.org/grpc v1.54.0 // indirect
google.golang.org/protobuf v1.30.0 // indirect
gopkg.in/yaml.v2 v2.4.0 // indirect

63
go.sum
View File

@ -1,4 +1,5 @@
cloud.google.com/go v0.26.0/go.mod h1:aQUYkXzVsufM+DwF1aE+0xfcU+56JwCaLick0ClmMTw=
cloud.google.com/go v0.34.0/go.mod h1:aQUYkXzVsufM+DwF1aE+0xfcU+56JwCaLick0ClmMTw=
cloud.google.com/go/compute v1.19.0 h1:+9zda3WGgW1ZSTlVppLCYFIr48Pa35q1uG2N1itbCEQ=
cloud.google.com/go/compute v1.19.0/go.mod h1:rikpw2y+UMidAe9tISo04EHNOIf42RLYF/q8Bs93scU=
cloud.google.com/go/compute/metadata v0.2.3 h1:mg4jlk7mCAj6xXp9UJ4fjI9VUI5rubuGBW5aJ7UnBMY=
@ -7,15 +8,22 @@ github.com/BurntSushi/toml v0.3.1/go.mod h1:xHWCNGjB5oqiDr8zfno3MHue2Ht5sIBksp03
github.com/Microsoft/go-winio v0.5.2 h1:a9IhgEQBCUEk6QCdml9CiJGhAws+YwffDHEMp1VMrpA=
github.com/ProtonMail/go-crypto v0.0.0-20230217124315-7d5c6f04bbb8 h1:wPbRQzjjwFc0ih8puEVAOFGELsn1zoIIYdxvML7mDxA=
github.com/acomagu/bufpipe v1.0.4 h1:e3H4WUzM3npvo5uv95QuJM3cQspFNtFBzvJ2oNjKIDQ=
github.com/antihax/optional v1.0.0/go.mod h1:uupD/76wgC+ih3iEmQUL+0Ugr19nfwCT1kdvxnR2qWY=
github.com/apparentlymart/go-textseg/v13 v13.0.0 h1:Y+KvPE1NYz0xl601PVImeQfFyEy6iT90AvPUL1NNfNw=
github.com/apparentlymart/go-textseg/v13 v13.0.0/go.mod h1:ZK2fH7c4NqDTLtiYLvIkEghdlcqw7yxLeM89kiTRPUo=
github.com/census-instrumentation/opencensus-proto v0.2.1/go.mod h1:f6KPmirojxKA12rnyqOA5BBL4O983OfeGPqjHWSTneU=
github.com/cespare/xxhash/v2 v2.1.1/go.mod h1:VGX0DQ3Q6kWi7AoAeZDth3/j3BFtOZR5XLFGgcrjCOs=
github.com/client9/misspell v0.3.4/go.mod h1:qj6jICC3Q7zFZvVWo7KLAzC3yx5G7kyvSDkc90ppPyw=
github.com/cloudflare/circl v1.1.0 h1:bZgT/A+cikZnKIwn7xL2OBj012Bmvho/o6RpRvv3GKY=
github.com/cncf/udpa/go v0.0.0-20191209042840-269d4d468f6f/go.mod h1:M8M6+tZqaGXZJjfX53e64911xZQV5JYwmTeXPW+k8Sc=
github.com/cncf/udpa/go v0.0.0-20201120205902-5459f2c99403/go.mod h1:WmhPx2Nbnhtbo57+VJT5O0JRkEi1Wbu0z5j0R8u5Hbk=
github.com/cncf/udpa/go v0.0.0-20210930031921-04548b0d99d4/go.mod h1:6pvJx4me5XPnfI9Z40ddWsdw2W/uZgQLFXToKeRcDiI=
github.com/cncf/xds/go v0.0.0-20210805033703-aa0b78936158/go.mod h1:eXthEFrGJvWHgFFCl3hGmgk+/aYT6PnTQLykKQRLhEs=
github.com/cncf/xds/go v0.0.0-20210922020428-25de7278fc84/go.mod h1:eXthEFrGJvWHgFFCl3hGmgk+/aYT6PnTQLykKQRLhEs=
github.com/cncf/xds/go v0.0.0-20211011173535-cb28da3451f1/go.mod h1:eXthEFrGJvWHgFFCl3hGmgk+/aYT6PnTQLykKQRLhEs=
github.com/cpuguy83/go-md2man/v2 v2.0.2/go.mod h1:tgQtvFlXSQOSOSIRvRPT7W67SCa46tRHOmNcaadrF8o=
github.com/databricks/databricks-sdk-go v0.7.0 h1:eBVJ1QOzK2EW3VV/s0eit24Xv610KZMUgSzYmN35vI4=
github.com/databricks/databricks-sdk-go v0.7.0/go.mod h1:I2aRssbBGMrwZ5iXakQAwK3I7VYono5tW3LxP7MYKeI=
github.com/databricks/databricks-sdk-go v0.8.0 h1:LNWpcjDDFB9glT3aj6+Ie8Nlk3346TGCHUyUs3D5sU4=
github.com/databricks/databricks-sdk-go v0.8.0/go.mod h1:aEMLe00mMlf5gvkD8PInqOM+dIAimeuAM6U+9eQN2yE=
github.com/davecgh/go-spew v1.1.0/go.mod h1:J7Y8YcW2NihsgmVo/mv3lAwl/skON4iLHjSsI+c5H38=
github.com/davecgh/go-spew v1.1.1 h1:vj9j/u1bqnvCEfJOwUhtlOARqs3+rkHYY13jYWTU97c=
github.com/davecgh/go-spew v1.1.1/go.mod h1:J7Y8YcW2NihsgmVo/mv3lAwl/skON4iLHjSsI+c5H38=
@ -23,6 +31,8 @@ github.com/emirpasic/gods v1.18.1 h1:FXtiHYKDGKCW2KzwZKx0iC0PQmdlorYgdFG9jPXJ1Bc
github.com/envoyproxy/go-control-plane v0.9.0/go.mod h1:YTl/9mNaCwkRvm6d1a2C3ymFceY/DCBVvsKhRF0iEA4=
github.com/envoyproxy/go-control-plane v0.9.1-0.20191026205805-5f8ba28d4473/go.mod h1:YTl/9mNaCwkRvm6d1a2C3ymFceY/DCBVvsKhRF0iEA4=
github.com/envoyproxy/go-control-plane v0.9.4/go.mod h1:6rpuAdCZL397s3pYoYcLgu1mIlRU8Am5FuJP05cCM98=
github.com/envoyproxy/go-control-plane v0.9.9-0.20201210154907-fd9021fe5dad/go.mod h1:cXg6YxExXjJnVBQHBLXeUAgxn2UodCpnH306RInaBQk=
github.com/envoyproxy/go-control-plane v0.9.10-0.20210907150352-cf90f659a021/go.mod h1:AFq3mo9L8Lqqiid3OhADV3RfLJnjiw63cSpi+fDTRC0=
github.com/envoyproxy/protoc-gen-validate v0.1.0/go.mod h1:iSmxcyjqTsJpI2R4NaDN7+kN2VEUnK/pcBlmesArF7c=
github.com/fatih/color v1.15.0 h1:kOqh6YHBtK8aywxGerMG2Eq3H6Qgoqeo13Bk2Mv/nBs=
github.com/fatih/color v1.15.0/go.mod h1:0h5ZqXfHYED7Bhv2ZJamyIOUej9KtShiJESRwBDUSsw=
@ -38,14 +48,17 @@ github.com/golang/mock v1.1.1/go.mod h1:oTYuIxOrZwtPieC+H1uAHpcLFnEyAGVDL/k47Jfb
github.com/golang/protobuf v1.2.0/go.mod h1:6lQm79b+lXiMfvg/cZm0SGofjICqVBUtrP5yJMmIC1U=
github.com/golang/protobuf v1.3.1/go.mod h1:6lQm79b+lXiMfvg/cZm0SGofjICqVBUtrP5yJMmIC1U=
github.com/golang/protobuf v1.3.2/go.mod h1:6lQm79b+lXiMfvg/cZm0SGofjICqVBUtrP5yJMmIC1U=
github.com/golang/protobuf v1.3.3/go.mod h1:vzj43D7+SQXF/4pzW/hwtAqwc6iTitCiVSaWz5lYuqw=
github.com/golang/protobuf v1.4.0-rc.1/go.mod h1:ceaxUfeHdC40wWswd/P6IGgMaK3YpKi5j83Wpe3EHw8=
github.com/golang/protobuf v1.4.0-rc.1.0.20200221234624-67d41d38c208/go.mod h1:xKAWHe0F5eneWXFV3EuXVDTCmh+JuBKY0li0aMyXATA=
github.com/golang/protobuf v1.4.0-rc.2/go.mod h1:LlEzMj4AhA7rCAGe4KMBDvJI+AwstrUpVNzEA03Pprs=
github.com/golang/protobuf v1.4.0-rc.4.0.20200313231945-b860323f09d0/go.mod h1:WU3c8KckQ9AFe+yFwt9sWVRKCVIyN9cPHBJSNnbL67w=
github.com/golang/protobuf v1.4.0/go.mod h1:jodUvKwWbYaEsadDk5Fwe5c77LiNKVO9IDvqG2KuDX0=
github.com/golang/protobuf v1.4.1/go.mod h1:U8fpvMrcmy5pZrNK1lt4xCsGvpyWQ/VVv6QDs8UjoX8=
github.com/golang/protobuf v1.4.2/go.mod h1:oDoupMAO8OvCJWAcko0GGGIgR6R6ocIYbsSw735rRwI=
github.com/golang/protobuf v1.4.3/go.mod h1:oDoupMAO8OvCJWAcko0GGGIgR6R6ocIYbsSw735rRwI=
github.com/golang/protobuf v1.5.0/go.mod h1:FsONVRAS9T7sI+LIUmWTfcYkHO4aIWwzhcaSAoJOfIk=
github.com/golang/protobuf v1.5.2/go.mod h1:XVQd3VNwM+JqD3oG2Ue2ip4fOMUkwXdXDdiuN0vRsmY=
github.com/golang/protobuf v1.5.3 h1:KhyjKVUg7Usr/dYsdSqoFveMYd5ko72D+zANwlG1mmg=
github.com/golang/protobuf v1.5.3/go.mod h1:XVQd3VNwM+JqD3oG2Ue2ip4fOMUkwXdXDdiuN0vRsmY=
github.com/google/go-cmp v0.2.0/go.mod h1:oXzfMopK8JAjlY9xF4vHSVASa0yLyX7SntLO5aqRK0M=
@ -59,12 +72,15 @@ github.com/google/go-cmp v0.5.5/go.mod h1:v8dTdLbMG2kIc/vJvl+f65V22dbkXbowE6jgT/
github.com/google/go-cmp v0.5.9 h1:O2Tfq5qg4qc4AmwVlvv0oLiVAGB7enBSJ2x2DqQFi38=
github.com/google/go-querystring v1.1.0 h1:AnCroh3fv4ZBgVIf1Iwtovgjaw/GiKJo8M8yD/fhyJ8=
github.com/google/go-querystring v1.1.0/go.mod h1:Kcdr2DB4koayq7X8pmAG4sNG59So17icRSOU623lUBU=
github.com/google/s2a-go v0.1.0 h1:3Qm0liEiCErViKERO2Su5wp+9PfMRiuS6XB5FvpKnYQ=
github.com/google/s2a-go v0.1.0/go.mod h1:OJpEgntRZo8ugHpF9hkoLJbS5dSI20XZeXJ9JVywLlM=
github.com/google/uuid v1.1.2/go.mod h1:TIyPZe4MgqvfeYDBFedMoGGpEw/LqOeaOT+nhxU+yHo=
github.com/google/uuid v1.3.0 h1:t6JiXgmwXMjEs8VusXIJk2BXHsn+wx8BZdTaoZ5fu7I=
github.com/google/uuid v1.3.0/go.mod h1:TIyPZe4MgqvfeYDBFedMoGGpEw/LqOeaOT+nhxU+yHo=
github.com/googleapis/enterprise-certificate-proxy v0.2.3 h1:yk9/cqRKtT9wXZSsRH9aurXEpJX+U6FLtpYTdC3R06k=
github.com/googleapis/enterprise-certificate-proxy v0.2.3/go.mod h1:AwSRAtLfXpU5Nm3pW+v7rGDHp09LsPtGY9MduiEsR9k=
github.com/googleapis/gax-go/v2 v2.8.0 h1:UBtEZqx1bjXtOQ5BVTkuYghXrr3N4V123VKJK67vJZc=
github.com/grpc-ecosystem/grpc-gateway v1.16.0/go.mod h1:BDjrQk3hbvj6Nolgz8mAMFbcEtjT1g+wF4CSlocrBnw=
github.com/hashicorp/go-cleanhttp v0.5.2 h1:035FKYIWjmULyFRBKPs8TBQoi0x6d9G4xc9neXJWAZQ=
github.com/hashicorp/go-cleanhttp v0.5.2/go.mod h1:kO/YDlP8L1346E6Sodw+PrpBSV4/SoxCXGY6BqNFT48=
github.com/hashicorp/go-version v1.6.0 h1:feTTfFNnjP967rlCxM/I9g701jU+RN74YKx2mOkIeek=
@ -92,6 +108,7 @@ github.com/pkg/browser v0.0.0-20210911075715-681adbf594b8/go.mod h1:HKlIX3XHQyzL
github.com/pmezard/go-difflib v1.0.0 h1:4DBwDE0NGyQoBHbLQYPwSUPoCMWR5BEzIk/f1lZbAQM=
github.com/pmezard/go-difflib v1.0.0/go.mod h1:iKH77koFhYxTK1pcRnkKkqfTogsbg7gZNVY4sRDYZ/4=
github.com/prometheus/client_model v0.0.0-20190812154241-14fe0d1b01d4/go.mod h1:xMI15A0UPsDsEKsMN9yxemIoYk6Tm2C1GtYGdfGttqA=
github.com/rogpeppe/fastuuid v1.2.0/go.mod h1:jVj6XXZzXRy/MSR5jhDC/2q6DgLz+nrA6LYCDYWNEvQ=
github.com/russross/blackfriday/v2 v2.1.0/go.mod h1:+Rmxgy9KzJVeS9/2gXHxylqXiyQDYRxCVz55jmeOWTM=
github.com/sabhiram/go-gitignore v0.0.0-20210923224102-525f6e181f06 h1:OkMGxebDjyw0ULyrTYWeN0UNCCkmCWfjPnIA2W6oviI=
github.com/sabhiram/go-gitignore v0.0.0-20210923224102-525f6e181f06/go.mod h1:+ePHsJ1keEjQtpvf9HHw0f4ZeJ0TLRsxhunSI2hYJSs=
@ -104,7 +121,9 @@ github.com/spf13/pflag v1.0.5/go.mod h1:McXfInJRrz4CZXVZOBLb0bTZqETkiAhM9Iw0y3An
github.com/stretchr/objx v0.1.0/go.mod h1:HFkY916IF+rwdDfMAkV7OtwuqBVzrE8GR6GFx+wExME=
github.com/stretchr/objx v0.4.0/go.mod h1:YvHI0jy2hoMjB+UWwv71VJQ9isScKT/TqJzVSSt89Yw=
github.com/stretchr/objx v0.5.0/go.mod h1:Yh+to48EsGEfYuaHDzXPcE3xhTkx73EhmCGUpEOglKo=
github.com/stretchr/testify v1.5.1/go.mod h1:5W2xD1RspED5o8YsWQXVCued0rvSQ+mT+I5cxcmMvtA=
github.com/stretchr/testify v1.6.1/go.mod h1:6Fq8oRcR53rry900zMqJjRRixrwX3KX962/h/Wwjteg=
github.com/stretchr/testify v1.7.0/go.mod h1:6Fq8oRcR53rry900zMqJjRRixrwX3KX962/h/Wwjteg=
github.com/stretchr/testify v1.7.1/go.mod h1:6Fq8oRcR53rry900zMqJjRRixrwX3KX962/h/Wwjteg=
github.com/stretchr/testify v1.8.0/go.mod h1:yNjHg4UonilssWZ8iaSj1OCr/vHnekPRkoO+kdMU+MU=
github.com/stretchr/testify v1.8.1/go.mod h1:w2LPCIKwWwSfY2zedu0+kehJoqGctiVI29o6fzry7u4=
@ -117,8 +136,10 @@ github.com/zclconf/go-cty v1.13.0 h1:It5dfKTTZHe9aeppbNOda3mN7Ag7sg6QkBNm6TkyFa0
github.com/zclconf/go-cty v1.13.0/go.mod h1:YKQzy/7pZ7iq2jNFzy5go57xdxdWoLLpaEp4u238AE0=
go.opencensus.io v0.24.0 h1:y73uSU6J157QMP2kn2r30vwW1A2W2WFwSCGnAVxeaD0=
go.opencensus.io v0.24.0/go.mod h1:vNK8G9p7aAivkbmorf4v+7Hgx+Zs0yY+0fOtgBfjQKo=
go.opentelemetry.io/proto/otlp v0.7.0/go.mod h1:PqfVotwruBrMGOCsRd/89rSnXhoiJIqeYNgFYFoEGnI=
golang.org/x/crypto v0.0.0-20190308221718-c2843e01d9a2/go.mod h1:djNgcEr1/C05ACkg1iLfiJU5Ep61QUkGW8qpdssI0+w=
golang.org/x/crypto v0.0.0-20200622213623-75b288015ac9/go.mod h1:LzIPMQfyMNhhGPhUkYOs5KpL4U8rLKemX1yGLhDgUto=
golang.org/x/crypto v0.0.0-20210921155107-089bfa567519/go.mod h1:GvvjBRRGRdwPK5ydBHafDWAxML/pGHZbMvKqRZ5+Abc=
golang.org/x/crypto v0.7.0 h1:AvwMYaRytfdeVt3u6mLaxYtErKYjxA2OXjJ1HHq6t3A=
golang.org/x/crypto v0.7.0/go.mod h1:pYwdfH91IfpZVANVyUOhSIPZaFoJGxTFbZhFTx+dXZU=
golang.org/x/exp v0.0.0-20190121172915-509febef88a4/go.mod h1:CJ0aWSM057203Lf6IL+f9T1iT9GByDxfZKAQTCR3kQA=
@ -131,34 +152,49 @@ golang.org/x/mod v0.10.0 h1:lFO9qtOdlre5W1jxS3r/4szv2/6iXxScdzjoBMXNhYk=
golang.org/x/mod v0.10.0/go.mod h1:iBbtSCu2XBx23ZKBPSOrRkjjQPZFPuis4dIYUhu/chs=
golang.org/x/net v0.0.0-20180724234803-3673e40ba225/go.mod h1:mL1N/T3taQHkDXs73rZJwtUhF3w3ftmwwsq0BUmARs4=
golang.org/x/net v0.0.0-20180826012351-8a410e7b638d/go.mod h1:mL1N/T3taQHkDXs73rZJwtUhF3w3ftmwwsq0BUmARs4=
golang.org/x/net v0.0.0-20190108225652-1e06a53dbb7e/go.mod h1:mL1N/T3taQHkDXs73rZJwtUhF3w3ftmwwsq0BUmARs4=
golang.org/x/net v0.0.0-20190213061140-3a22650c66bd/go.mod h1:mL1N/T3taQHkDXs73rZJwtUhF3w3ftmwwsq0BUmARs4=
golang.org/x/net v0.0.0-20190311183353-d8887717615a/go.mod h1:t9HGtf8HONx5eT2rtn7q6eTqICYqUVnKs3thJo3Qplg=
golang.org/x/net v0.0.0-20190404232315-eb5bcb51f2a3/go.mod h1:t9HGtf8HONx5eT2rtn7q6eTqICYqUVnKs3thJo3Qplg=
golang.org/x/net v0.0.0-20190603091049-60506f45cf65/go.mod h1:HSz+uSET+XFnRR8LxR5pz3Of3rY3CfYBVs4xY44aLks=
golang.org/x/net v0.0.0-20200822124328-c89045814202/go.mod h1:/O7V0waA8r7cgGh81Ro3o1hOxt32SMVPicZroKQ2sZA=
golang.org/x/net v0.0.0-20201110031124-69a78807bb2b/go.mod h1:sp8m0HH+o8qH0wwXwYZr8TS3Oi6o0r6Gce1SSxlDquU=
golang.org/x/net v0.8.0 h1:Zrh2ngAOFYneWTAIAPethzeaQLuHwhuBkuV6ZiRnUaQ=
golang.org/x/net v0.8.0/go.mod h1:QVkue5JL9kW//ek3r6jTKnTFis1tRmNAW2P1shuFdJc=
golang.org/x/net v0.0.0-20210226172049-e18ecbb05110/go.mod h1:m0MpNAwzfU5UDzcl9v0D8zg8gWTRqZa9RBIspLL5mdg=
golang.org/x/net v0.0.0-20211015210444-4f30a5c0130f/go.mod h1:9nx3DQGgdP8bBQD5qxJ1jj9UTztislL4KSBs9R2vV5Y=
golang.org/x/net v0.9.0 h1:aWJ/m6xSmxWBx+V0XRHTlrYrPG56jKsLdTFmsSsCzOM=
golang.org/x/net v0.9.0/go.mod h1:d48xBJpPfHeWQsugry2m+kC02ZBRGRgulfHnEXEuWns=
golang.org/x/oauth2 v0.0.0-20180821212333-d2e6202438be/go.mod h1:N/0e6XlmueqKjAGxoOufVs8QHGRruUQn6yWY3a++T0U=
golang.org/x/oauth2 v0.6.0 h1:Lh8GPgSKBfWSwFvtuWOfeI3aAAnbXTSutYxJiOJFgIw=
golang.org/x/oauth2 v0.6.0/go.mod h1:ycmewcwgD4Rpr3eZJLSB4Kyyljb3qDh40vJ8STE5HKw=
golang.org/x/oauth2 v0.0.0-20200107190931-bf48bf16ab8d/go.mod h1:gOpvHmFTYa4IltrdGE7lF6nIHvwfUNPOp7c8zoXwtLw=
golang.org/x/oauth2 v0.7.0 h1:qe6s0zUXlPX80/dITx3440hWZ7GwMwgDDyrSGTPJG/g=
golang.org/x/oauth2 v0.7.0/go.mod h1:hPLQkd9LyjfXTiRohC/41GhcFqxisoUQ99sCUOHO9x4=
golang.org/x/sync v0.0.0-20180314180146-1d60e4601c6f/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM=
golang.org/x/sync v0.0.0-20181108010431-42b317875d0f/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM=
golang.org/x/sync v0.0.0-20181221193216-37e7f081c4d4/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM=
golang.org/x/sync v0.0.0-20190423024810-112230192c58/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM=
golang.org/x/sync v0.0.0-20210220032951-036812b2e83c/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM=
golang.org/x/sync v0.1.0 h1:wsuoTGHzEhffawBOhz5CYhcrV4IdKZbEyZjBMuTp12o=
golang.org/x/sync v0.1.0/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM=
golang.org/x/sys v0.0.0-20180830151530-49385e6e1522/go.mod h1:STP8DvDyc/dI5b8T5hshtkjS+E42TnysNCUPdjciGhY=
golang.org/x/sys v0.0.0-20190215142949-d0b11bdaac8a/go.mod h1:STP8DvDyc/dI5b8T5hshtkjS+E42TnysNCUPdjciGhY=
golang.org/x/sys v0.0.0-20190412213103-97732733099d/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
golang.org/x/sys v0.0.0-20200323222414-85ca7c5b95cd/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
golang.org/x/sys v0.0.0-20200930185726-fdedc70b468f/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
golang.org/x/sys v0.0.0-20201119102817-f84b799fce68/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
golang.org/x/sys v0.0.0-20210423082822-04245dca01da/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
golang.org/x/sys v0.0.0-20210615035016-665e8c7367d1/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg=
golang.org/x/sys v0.0.0-20210616045830-e2b7044e8c71/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg=
golang.org/x/sys v0.0.0-20211019181941-9d821ace8654/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg=
golang.org/x/sys v0.0.0-20220811171246-fbc7d0a398ab/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg=
golang.org/x/sys v0.7.0 h1:3jlCCIQZPdOYu1h8BkNvLz8Kgwtae2cagcG/VamtZRU=
golang.org/x/sys v0.7.0/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg=
golang.org/x/term v0.0.0-20201126162022-7de9c90e9dd1/go.mod h1:bj7SfCRtBDWHUb9snDiAeCFNEtKQo2Wmx5Cou7ajbmo=
golang.org/x/term v0.7.0 h1:BEvjmm5fURWqcfbSKTdpkDXYBrUS1c0m8agp14W48vQ=
golang.org/x/term v0.7.0/go.mod h1:P32HKFT3hSsZrRxla30E9HqToFYAQPCMs/zFMBUFqPY=
golang.org/x/text v0.3.0/go.mod h1:NqM8EUOU14njkJ3fqMW+pc6Ldnwhi/IjpwHt7yyuwOQ=
golang.org/x/text v0.3.2/go.mod h1:bEr9sfX3Q8Zfm5fL9x+3itogRgK3+ptLWKqgva+5dAk=
golang.org/x/text v0.3.3/go.mod h1:5Zoc/QRtKVWzQhOtBMvqHzDpF6irO9z98xDceosuGiQ=
golang.org/x/text v0.3.6/go.mod h1:5Zoc/QRtKVWzQhOtBMvqHzDpF6irO9z98xDceosuGiQ=
golang.org/x/text v0.3.7/go.mod h1:u+2+/6zg+i71rQMx5EYifcz6MCKuco9NR6JIITiCfzQ=
golang.org/x/text v0.9.0 h1:2sjJmO8cDvYveuX97RDLsxlyUxLl+GHoLxBiRdHllBE=
golang.org/x/text v0.9.0/go.mod h1:e1OnstbJyHTd6l/uOt8jFFHp6TRDWZR/bV3emEE/zU8=
golang.org/x/time v0.3.0 h1:rg5rLMjNzMS1RkNLzCG38eapWhnYLFYXDXj2gOlr8j4=
@ -169,22 +205,27 @@ golang.org/x/tools v0.0.0-20190226205152-f727befe758c/go.mod h1:9Yl7xja0Znq3iFh3
golang.org/x/tools v0.0.0-20190311212946-11955173bddd/go.mod h1:LCzVGOaR6xXOjkQ3onu1FJEFr0SW1gC7cKk1uF8kGRs=
golang.org/x/tools v0.0.0-20190524140312-2c0ae7006135/go.mod h1:RgjU9mgBXZiqYHBnxXauZ1Gv1EHHAz9KjViQ78xBX0Q=
golang.org/x/xerrors v0.0.0-20191204190536-9bdfabe68543/go.mod h1:I/5z698sn9Ka8TeJc9MKroUUfqBBauWjQqLJ2OPfmY0=
google.golang.org/api v0.115.0 h1:6FFkVvStt4YqXSx3azKyzj7fXerGnVlLJ/eud01nBDE=
google.golang.org/api v0.115.0/go.mod h1:9cD4/t6uvd9naoEJFA+M96d0IuB6BqFuyhpw68+mRGg=
golang.org/x/xerrors v0.0.0-20200804184101-5ec99f83aff1/go.mod h1:I/5z698sn9Ka8TeJc9MKroUUfqBBauWjQqLJ2OPfmY0=
google.golang.org/api v0.118.0 h1:FNfHq9Z2GKULxu7cEhCaB0wWQHg43UpomrrN+24ZRdE=
google.golang.org/api v0.118.0/go.mod h1:76TtD3vkgmZ66zZzp72bUUklpmQmKlhh6sYtIjYK+5E=
google.golang.org/appengine v1.1.0/go.mod h1:EbEs0AVv82hx2wNQdGPgUI5lhzA/G0D9YwlJXL52JkM=
google.golang.org/appengine v1.4.0/go.mod h1:xpcJRLb0r/rnEns0DIKYYv+WjYCduHsrkT7/EB5XEv4=
google.golang.org/appengine v1.6.7 h1:FZR1q0exgwxzPzp/aF+VccGrSfxfPpkBqjIIEq3ru6c=
google.golang.org/appengine v1.6.7/go.mod h1:8WjMMxjGQR8xUklV/ARdw2HLXBOI7O7uCIDZVag1xfc=
google.golang.org/genproto v0.0.0-20180817151627-c66870c02cf8/go.mod h1:JiN7NxoALGmiZfu7CAH4rXhgtRTLTxftemlI0sWmxmc=
google.golang.org/genproto v0.0.0-20190819201941-24fa4b261c55/go.mod h1:DMBHOl98Agz4BDEuKkezgsaosCRResVns1a3J2ZsMNc=
google.golang.org/genproto v0.0.0-20200513103714-09dca8ec2884/go.mod h1:55QSHmfGQM9UVYDPBsyGGes0y52j32PQ3BqQfXhyH3c=
google.golang.org/genproto v0.0.0-20200526211855-cb27e3aa2013/go.mod h1:NbSheEEYHJ7i3ixzK3sjbqSGDJWnxyFXZblF3eUsNvo=
google.golang.org/genproto v0.0.0-20230331144136-dcfb400f0633 h1:0BOZf6qNozI3pkN3fJLwNubheHJYHhMh91GRFOWWK08=
google.golang.org/genproto v0.0.0-20230331144136-dcfb400f0633/go.mod h1:UUQDJDOlWu4KYeJZffbWgBkS1YFobzKbLVfK69pe0Ak=
google.golang.org/genproto v0.0.0-20230403163135-c38d8f061ccd h1:sLpv7bNL1AsX3fdnWh9WVh7ejIzXdOc1RRHGeAmeStU=
google.golang.org/genproto v0.0.0-20230403163135-c38d8f061ccd/go.mod h1:UUQDJDOlWu4KYeJZffbWgBkS1YFobzKbLVfK69pe0Ak=
google.golang.org/grpc v1.19.0/go.mod h1:mqu4LbDTu4XGKhr4mRzUsmM4RtVoemTSY81AxZiDr8c=
google.golang.org/grpc v1.23.0/go.mod h1:Y5yQAOtifL1yxbo5wqy6BxZv8vAUGQwXBOALyacEbxg=
google.golang.org/grpc v1.25.1/go.mod h1:c3i+UQWmh7LiEpx4sFZnkU36qjEYZ0imhYfXVyQciAY=
google.golang.org/grpc v1.27.0/go.mod h1:qbnxyOmOxrQa7FizSgH+ReBfzJrCY1pSN7KXBS8abTk=
google.golang.org/grpc v1.33.1/go.mod h1:fr5YgcSWrqhRRxogOsw7RzIpsmvOZ6IcH4kBYTpR3n0=
google.golang.org/grpc v1.33.2/go.mod h1:JMHMWHQWaTccqQQlmk3MJZS+GWXOdAesneDmEnv2fbc=
google.golang.org/grpc v1.36.0/go.mod h1:qjiiYl8FncCW8feJPdyg3v6XW24KsRHe+dy9BAGRRjU=
google.golang.org/grpc v1.45.0/go.mod h1:lN7owxKUQEqMfSyQikvvk5tf/6zMPsrK+ONuO11+0rQ=
google.golang.org/grpc v1.54.0 h1:EhTqbhiYeixwWQtAEZAxmV9MGqcjEU2mFx52xCzNyag=
google.golang.org/grpc v1.54.0/go.mod h1:PUSEXI6iWghWaB6lXM4knEgpJNu2qUcKfDtNci3EC2g=
google.golang.org/protobuf v0.0.0-20200109180630-ec00e32a8dfd/go.mod h1:DFci5gLYBciE7Vtevhsrf46CRTquxDuWsQurQQe4oz8=
@ -205,6 +246,8 @@ gopkg.in/check.v1 v0.0.0-20161208181325-20d25e280405/go.mod h1:Co6ibVJAznAaIkqp8
gopkg.in/ini.v1 v1.67.0 h1:Dgnx+6+nfE+IfzjUEISNeydPJh9AXNNsWbGP9KzCsOA=
gopkg.in/ini.v1 v1.67.0/go.mod h1:pNLf8WUiyNEtQjuu5G5vTm06TEv9tsIgeAvK8hOrP4k=
gopkg.in/warnings.v0 v0.1.2 h1:wFXVbFY8DY5/xOe1ECiWdKCzZlxgshcYVNkBHstARME=
gopkg.in/yaml.v2 v2.2.2/go.mod h1:hI93XBmqTisBFMUTm0b8Fm+jr3Dg1NNxqwp+5A1VGuI=
gopkg.in/yaml.v2 v2.2.3/go.mod h1:hI93XBmqTisBFMUTm0b8Fm+jr3Dg1NNxqwp+5A1VGuI=
gopkg.in/yaml.v2 v2.4.0 h1:D8xgwECY7CYvx+Y2n4sBz93Jn9JRvxdiyyo8CTfuKaY=
gopkg.in/yaml.v2 v2.4.0/go.mod h1:RDklbk79AGWmwhnvt/jBztapEOGDOx6ZbXqjP6csGnQ=
gopkg.in/yaml.v3 v3.0.0-20200313102051-9f266ea9e77c/go.mod h1:K4uyk7z7BCEPqu6E+C64Yfv1cQ7kz7rIZviUmN+EgEM=

View File

@ -11,7 +11,7 @@ import (
lockpkg "github.com/databricks/bricks/libs/locker"
"github.com/databricks/databricks-sdk-go"
"github.com/databricks/databricks-sdk-go/service/repos"
"github.com/databricks/databricks-sdk-go/service/workspace"
"github.com/stretchr/testify/assert"
"github.com/stretchr/testify/require"
)
@ -26,7 +26,7 @@ func createRemoteTestProject(t *testing.T, projectNamePrefix string, wsc *databr
assert.NoError(t, err)
remoteProjectRoot := fmt.Sprintf("/Repos/%s/%s", me.UserName, RandomName(projectNamePrefix))
repoInfo, err := wsc.Repos.Create(ctx, repos.CreateRepo{
repoInfo, err := wsc.Repos.Create(ctx, workspace.CreateRepo{
Path: remoteProjectRoot,
Url: EmptyRepoUrl,
Provider: "gitHub",

View File

@ -19,7 +19,6 @@ import (
"github.com/databricks/bricks/libs/testfile"
"github.com/databricks/databricks-sdk-go"
"github.com/databricks/databricks-sdk-go/client"
"github.com/databricks/databricks-sdk-go/service/repos"
"github.com/databricks/databricks-sdk-go/service/workspace"
"github.com/stretchr/testify/assert"
"github.com/stretchr/testify/require"
@ -37,7 +36,7 @@ func setupRepo(t *testing.T, wsc *databricks.WorkspaceClient, ctx context.Contex
require.NoError(t, err)
repoPath := fmt.Sprintf("/Repos/%s/%s", me.UserName, RandomName("empty-repo-sync-integration-"))
repoInfo, err := wsc.Repos.Create(ctx, repos.CreateRepo{
repoInfo, err := wsc.Repos.Create(ctx, workspace.CreateRepo{
Path: repoPath,
Url: repoUrl,
Provider: "gitHub",
@ -71,13 +70,13 @@ type assertSync struct {
func (a *assertSync) remoteDirContent(ctx context.Context, relativeDir string, expectedFiles []string) {
remoteDir := path.Join(a.remoteRoot, relativeDir)
a.c.Eventually(func() bool {
objects, err := a.w.Workspace.ListAll(ctx, workspace.List{
objects, err := a.w.Workspace.ListAll(ctx, workspace.ListWorkspaceRequest{
Path: remoteDir,
})
require.NoError(a.t, err)
return len(objects) == len(expectedFiles)
}, 30*time.Second, 5*time.Second)
objects, err := a.w.Workspace.ListAll(ctx, workspace.List{
objects, err := a.w.Workspace.ListAll(ctx, workspace.ListWorkspaceRequest{
Path: remoteDir,
})
require.NoError(a.t, err)

15
libs/cmdio/error_event.go Normal file
View File

@ -0,0 +1,15 @@
package cmdio
import "fmt"
type ErrorEvent struct {
Error string `json:"error"`
}
func (event *ErrorEvent) String() string {
return fmt.Sprintf("Error: %s", event.Error)
}
func (event *ErrorEvent) IsInplaceSupported() bool {
return false
}

View File

@ -4,6 +4,7 @@ import (
"bufio"
"context"
"encoding/json"
"fmt"
"io"
"os"
@ -71,6 +72,16 @@ func LogNewline(ctx context.Context) {
logger.Log(&NewlineEvent{})
}
func LogError(ctx context.Context, err error) {
logger, ok := FromContext(ctx)
if !ok {
logger = Default()
}
logger.Log(&ErrorEvent{
Error: err.Error(),
})
}
func Ask(ctx context.Context, question string) (bool, error) {
logger, ok := FromContext(ctx)
if !ok {
@ -80,6 +91,10 @@ func Ask(ctx context.Context, question string) (bool, error) {
}
func (l *Logger) Ask(question string) (bool, error) {
if l.Mode == flags.ModeJson {
return false, fmt.Errorf("question prompts are not supported in json mode")
}
l.Writer.Write([]byte(question))
ans, err := l.Reader.ReadString('\n')

14
libs/cmdio/logger_test.go Normal file
View File

@ -0,0 +1,14 @@
package cmdio
import (
"testing"
"github.com/databricks/bricks/libs/flags"
"github.com/stretchr/testify/assert"
)
func TestAskFailedInJsonMode(t *testing.T) {
l := NewLogger(flags.ModeJson)
_, err := l.Ask("What is your spirit animal?")
assert.ErrorContains(t, err, "question prompts are not supported in json mode")
}

View File

@ -9,7 +9,7 @@ import (
"github.com/databricks/bricks/libs/log"
"github.com/databricks/databricks-sdk-go"
"github.com/databricks/databricks-sdk-go/apierr"
"github.com/databricks/databricks-sdk-go/service/scim"
"github.com/databricks/databricks-sdk-go/service/iam"
"github.com/databricks/databricks-sdk-go/service/workspace"
)
@ -29,7 +29,7 @@ func isPathNestedUnder(child, parent string) bool {
}
// Check if the specified path is nested under one of the allowed base paths.
func checkPathNestedUnderBasePaths(me *scim.User, p string) error {
func checkPathNestedUnderBasePaths(me *iam.User, p string) error {
validBasePaths := []string{
path.Clean(fmt.Sprintf("/Users/%s", me.UserName)),
path.Clean(fmt.Sprintf("/Repos/%s", me.UserName)),
@ -49,7 +49,7 @@ func checkPathNestedUnderBasePaths(me *scim.User, p string) error {
return nil
}
func repoPathForPath(me *scim.User, remotePath string) string {
func repoPathForPath(me *iam.User, remotePath string) string {
base := path.Clean(fmt.Sprintf("/Repos/%s", me.UserName))
remotePath = path.Clean(remotePath)
for strings.HasPrefix(path.Dir(remotePath), base) && path.Dir(remotePath) != base {

View File

@ -3,12 +3,12 @@ package sync
import (
"testing"
"github.com/databricks/databricks-sdk-go/service/scim"
"github.com/databricks/databricks-sdk-go/service/iam"
"github.com/stretchr/testify/assert"
)
func TestPathNestedUnderBasePaths(t *testing.T) {
me := scim.User{
me := iam.User{
UserName: "jane@doe.com",
}
@ -39,7 +39,7 @@ func TestPathNestedUnderBasePaths(t *testing.T) {
}
func TestPathToRepoPath(t *testing.T) {
me := scim.User{
me := iam.User{
UserName: "jane@doe.com",
}

View File

@ -5,7 +5,6 @@ import (
_ "github.com/databricks/bricks/cmd/auth"
_ "github.com/databricks/bricks/cmd/bundle"
_ "github.com/databricks/bricks/cmd/bundle/debug"
_ "github.com/databricks/bricks/cmd/bundle/debug/deploy"
_ "github.com/databricks/bricks/cmd/fs"
"github.com/databricks/bricks/cmd/root"
_ "github.com/databricks/bricks/cmd/sync"

View File

@ -10,7 +10,7 @@ import (
"github.com/databricks/bricks/libs/log"
"github.com/databricks/databricks-sdk-go"
"github.com/databricks/databricks-sdk-go/service/dbfs"
"github.com/databricks/databricks-sdk-go/service/files"
)
func BuildWheel(ctx context.Context, dir string) (string, error) {
@ -72,7 +72,7 @@ func UploadWheelToDBFSWithPEP503(ctx context.Context, dir string) (string, error
return "", err
}
defer wf.Close()
h, err := wsc.Dbfs.Open(ctx, dbfsLoc, dbfs.FileModeOverwrite|dbfs.FileModeWrite)
h, err := wsc.Dbfs.Open(ctx, dbfsLoc, files.FileModeOverwrite|files.FileModeWrite)
if err != nil {
return "", err
}

View File

@ -1,5 +0,0 @@
name: dev
profile: demo
isolation: none
dev_cluster:
cluster_name: Shared Autoscaling

View File

@ -1 +0,0 @@
spark.sql('show tables').show()