mirror of https://github.com/databricks/cli.git
Compare commits
2 Commits
0e088eb9f8
...
0a36681bef
Author | SHA1 | Date |
---|---|---|
|
0a36681bef | |
|
0da17f6ec6 |
|
@ -45,7 +45,6 @@ jobs:
|
||||||
echo "GOPATH=$(go env GOPATH)" >> $GITHUB_ENV
|
echo "GOPATH=$(go env GOPATH)" >> $GITHUB_ENV
|
||||||
echo "$(go env GOPATH)/bin" >> $GITHUB_PATH
|
echo "$(go env GOPATH)/bin" >> $GITHUB_PATH
|
||||||
go install gotest.tools/gotestsum@latest
|
go install gotest.tools/gotestsum@latest
|
||||||
go install honnef.co/go/tools/cmd/staticcheck@latest
|
|
||||||
|
|
||||||
- name: Pull external libraries
|
- name: Pull external libraries
|
||||||
run: |
|
run: |
|
||||||
|
@ -53,7 +52,7 @@ jobs:
|
||||||
pip3 install wheel
|
pip3 install wheel
|
||||||
|
|
||||||
- name: Run tests
|
- name: Run tests
|
||||||
run: make test
|
run: make testonly
|
||||||
|
|
||||||
- name: Publish test coverage
|
- name: Publish test coverage
|
||||||
uses: codecov/codecov-action@v4
|
uses: codecov/codecov-action@v4
|
||||||
|
@ -90,6 +89,20 @@ jobs:
|
||||||
# Exit with status code 1 if there are differences (i.e. unformatted files)
|
# Exit with status code 1 if there are differences (i.e. unformatted files)
|
||||||
git diff --exit-code
|
git diff --exit-code
|
||||||
|
|
||||||
|
golangci:
|
||||||
|
name: lint
|
||||||
|
runs-on: ubuntu-latest
|
||||||
|
steps:
|
||||||
|
- uses: actions/checkout@v4
|
||||||
|
- uses: actions/setup-go@v5
|
||||||
|
with:
|
||||||
|
go-version: 1.23.2
|
||||||
|
- name: golangci-lint
|
||||||
|
uses: golangci/golangci-lint-action@v6
|
||||||
|
with:
|
||||||
|
version: v1.62.2
|
||||||
|
args: --timeout=15m
|
||||||
|
|
||||||
validate-bundle-schema:
|
validate-bundle-schema:
|
||||||
runs-on: ubuntu-latest
|
runs-on: ubuntu-latest
|
||||||
|
|
||||||
|
|
|
@ -0,0 +1,19 @@
|
||||||
|
linters:
|
||||||
|
disable-all: true
|
||||||
|
enable:
|
||||||
|
# errcheck and govet are part of default setup and should be included but give too many errors now
|
||||||
|
# once errors are fixed, they should be enabled here:
|
||||||
|
#- errcheck
|
||||||
|
- gosimple
|
||||||
|
#- govet
|
||||||
|
- ineffassign
|
||||||
|
- staticcheck
|
||||||
|
- unused
|
||||||
|
- gofmt
|
||||||
|
linters-settings:
|
||||||
|
gofmt:
|
||||||
|
rewrite-rules:
|
||||||
|
- pattern: 'a[b:len(a)]'
|
||||||
|
replacement: 'a[b:]'
|
||||||
|
issues:
|
||||||
|
exclude-dirs-use-default: false # recommended by docs https://golangci-lint.run/usage/false-positives/
|
|
@ -3,6 +3,10 @@
|
||||||
"editor.insertSpaces": false,
|
"editor.insertSpaces": false,
|
||||||
"editor.formatOnSave": true
|
"editor.formatOnSave": true
|
||||||
},
|
},
|
||||||
|
"go.lintTool": "golangci-lint",
|
||||||
|
"go.lintFlags": [
|
||||||
|
"--fast"
|
||||||
|
],
|
||||||
"files.trimTrailingWhitespace": true,
|
"files.trimTrailingWhitespace": true,
|
||||||
"files.insertFinalNewline": true,
|
"files.insertFinalNewline": true,
|
||||||
"files.trimFinalNewlines": true,
|
"files.trimFinalNewlines": true,
|
||||||
|
|
12
Makefile
12
Makefile
|
@ -7,10 +7,16 @@ fmt:
|
||||||
@gofmt -w $(shell find . -type f -name '*.go' -not -path "./vendor/*")
|
@gofmt -w $(shell find . -type f -name '*.go' -not -path "./vendor/*")
|
||||||
|
|
||||||
lint: vendor
|
lint: vendor
|
||||||
@echo "✓ Linting source code with https://staticcheck.io/ ..."
|
@echo "✓ Linting source code with https://golangci-lint.run/ ..."
|
||||||
@staticcheck ./...
|
@golangci-lint run ./...
|
||||||
|
|
||||||
test: lint
|
lintfix: vendor
|
||||||
|
@echo "✓ Linting source code with 'golangci-lint run --fix' ..."
|
||||||
|
@golangci-lint run --fix ./...
|
||||||
|
|
||||||
|
test: lint testonly
|
||||||
|
|
||||||
|
testonly:
|
||||||
@echo "✓ Running tests ..."
|
@echo "✓ Running tests ..."
|
||||||
@gotestsum --format pkgname-and-test-fails --no-summary=skipped --raw-command go test -v -json -short -coverprofile=coverage.txt ./...
|
@gotestsum --format pkgname-and-test-fails --no-summary=skipped --raw-command go test -v -json -short -coverprofile=coverage.txt ./...
|
||||||
|
|
||||||
|
|
|
@ -0,0 +1,44 @@
|
||||||
|
package mutator
|
||||||
|
|
||||||
|
import (
|
||||||
|
"context"
|
||||||
|
|
||||||
|
"github.com/databricks/cli/bundle"
|
||||||
|
"github.com/databricks/cli/libs/diag"
|
||||||
|
"github.com/databricks/cli/libs/dyn"
|
||||||
|
)
|
||||||
|
|
||||||
|
type configureVolumeDefaults struct{}
|
||||||
|
|
||||||
|
func ConfigureVolumeDefaults() bundle.Mutator {
|
||||||
|
return &configureVolumeDefaults{}
|
||||||
|
}
|
||||||
|
|
||||||
|
func (m *configureVolumeDefaults) Name() string {
|
||||||
|
return "ConfigureVolumeDefaults"
|
||||||
|
}
|
||||||
|
|
||||||
|
func (m *configureVolumeDefaults) Apply(ctx context.Context, b *bundle.Bundle) diag.Diagnostics {
|
||||||
|
var diags diag.Diagnostics
|
||||||
|
|
||||||
|
pattern := dyn.NewPattern(
|
||||||
|
dyn.Key("resources"),
|
||||||
|
dyn.Key("volumes"),
|
||||||
|
dyn.AnyKey(),
|
||||||
|
)
|
||||||
|
|
||||||
|
// Configure defaults for all volumes.
|
||||||
|
err := b.Config.Mutate(func(v dyn.Value) (dyn.Value, error) {
|
||||||
|
return dyn.MapByPattern(v, pattern, func(p dyn.Path, v dyn.Value) (dyn.Value, error) {
|
||||||
|
var err error
|
||||||
|
v, err = setIfNotExists(v, dyn.NewPath(dyn.Key("volume_type")), dyn.V("MANAGED"))
|
||||||
|
if err != nil {
|
||||||
|
return dyn.InvalidValue, err
|
||||||
|
}
|
||||||
|
return v, nil
|
||||||
|
})
|
||||||
|
})
|
||||||
|
|
||||||
|
diags = diags.Extend(diag.FromErr(err))
|
||||||
|
return diags
|
||||||
|
}
|
|
@ -0,0 +1,75 @@
|
||||||
|
package mutator_test
|
||||||
|
|
||||||
|
import (
|
||||||
|
"context"
|
||||||
|
"testing"
|
||||||
|
|
||||||
|
"github.com/databricks/cli/bundle"
|
||||||
|
"github.com/databricks/cli/bundle/config"
|
||||||
|
"github.com/databricks/cli/bundle/config/mutator"
|
||||||
|
"github.com/databricks/cli/bundle/config/resources"
|
||||||
|
"github.com/databricks/cli/bundle/internal/bundletest"
|
||||||
|
"github.com/databricks/cli/libs/dyn"
|
||||||
|
"github.com/databricks/databricks-sdk-go/service/catalog"
|
||||||
|
"github.com/stretchr/testify/assert"
|
||||||
|
"github.com/stretchr/testify/require"
|
||||||
|
)
|
||||||
|
|
||||||
|
func TestConfigureVolumeDefaultsVolumeType(t *testing.T) {
|
||||||
|
b := &bundle.Bundle{
|
||||||
|
Config: config.Root{
|
||||||
|
Resources: config.Resources{
|
||||||
|
Volumes: map[string]*resources.Volume{
|
||||||
|
"v1": {
|
||||||
|
// Empty string is skipped.
|
||||||
|
// See below for how it is set.
|
||||||
|
CreateVolumeRequestContent: &catalog.CreateVolumeRequestContent{
|
||||||
|
VolumeType: "",
|
||||||
|
},
|
||||||
|
},
|
||||||
|
"v2": {
|
||||||
|
// Non-empty string is skipped.
|
||||||
|
CreateVolumeRequestContent: &catalog.CreateVolumeRequestContent{
|
||||||
|
VolumeType: "already-set",
|
||||||
|
},
|
||||||
|
},
|
||||||
|
"v3": {
|
||||||
|
// No volume type set.
|
||||||
|
},
|
||||||
|
"v4": nil,
|
||||||
|
},
|
||||||
|
},
|
||||||
|
},
|
||||||
|
}
|
||||||
|
|
||||||
|
// We can't set an empty string in the typed configuration.
|
||||||
|
// Do it on the dyn.Value directly.
|
||||||
|
bundletest.Mutate(t, b, func(v dyn.Value) (dyn.Value, error) {
|
||||||
|
return dyn.Set(v, "resources.volumes.v1.volume_type", dyn.V(""))
|
||||||
|
})
|
||||||
|
|
||||||
|
diags := bundle.Apply(context.Background(), b, mutator.ConfigureVolumeDefaults())
|
||||||
|
require.NoError(t, diags.Error())
|
||||||
|
|
||||||
|
var v dyn.Value
|
||||||
|
var err error
|
||||||
|
|
||||||
|
// Set to empty string; unchanged.
|
||||||
|
v, err = dyn.Get(b.Config.Value(), "resources.volumes.v1.volume_type")
|
||||||
|
require.NoError(t, err)
|
||||||
|
assert.Equal(t, "", v.MustString())
|
||||||
|
|
||||||
|
// Set to non-empty string; unchanged.
|
||||||
|
v, err = dyn.Get(b.Config.Value(), "resources.volumes.v2.volume_type")
|
||||||
|
require.NoError(t, err)
|
||||||
|
assert.Equal(t, "already-set", v.MustString())
|
||||||
|
|
||||||
|
// Not set; set to default.
|
||||||
|
v, err = dyn.Get(b.Config.Value(), "resources.volumes.v3.volume_type")
|
||||||
|
require.NoError(t, err)
|
||||||
|
assert.Equal(t, "MANAGED", v.MustString())
|
||||||
|
|
||||||
|
// No valid volume; No change.
|
||||||
|
_, err = dyn.Get(b.Config.Value(), "resources.volumes.v4.volume_type")
|
||||||
|
assert.True(t, dyn.IsCannotTraverseNilError(err))
|
||||||
|
}
|
|
@ -56,7 +56,7 @@ func (m *importResource) Apply(ctx context.Context, b *bundle.Bundle) diag.Diagn
|
||||||
buf := bytes.NewBuffer(nil)
|
buf := bytes.NewBuffer(nil)
|
||||||
tf.SetStdout(buf)
|
tf.SetStdout(buf)
|
||||||
|
|
||||||
//lint:ignore SA1019 We use legacy -state flag for now to plan the import changes based on temporary state file
|
//nolint:staticcheck // SA1019 We use legacy -state flag for now to plan the import changes based on temporary state file
|
||||||
changed, err := tf.Plan(ctx, tfexec.State(tmpState), tfexec.Target(importAddress))
|
changed, err := tf.Plan(ctx, tfexec.State(tmpState), tfexec.Target(importAddress))
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return diag.Errorf("terraform plan: %v", err)
|
return diag.Errorf("terraform plan: %v", err)
|
||||||
|
|
|
@ -93,6 +93,24 @@ func removeJobsFields(typ reflect.Type, s jsonschema.Schema) jsonschema.Schema {
|
||||||
return s
|
return s
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// While volume_type is required in the volume create API, DABs automatically sets
|
||||||
|
// it's value to "MANAGED" if it's not provided. Thus, we make it optional
|
||||||
|
// in the bundle schema.
|
||||||
|
func makeVolumeTypeOptional(typ reflect.Type, s jsonschema.Schema) jsonschema.Schema {
|
||||||
|
if typ != reflect.TypeOf(resources.Volume{}) {
|
||||||
|
return s
|
||||||
|
}
|
||||||
|
|
||||||
|
res := []string{}
|
||||||
|
for _, r := range s.Required {
|
||||||
|
if r != "volume_type" {
|
||||||
|
res = append(res, r)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
s.Required = res
|
||||||
|
return s
|
||||||
|
}
|
||||||
|
|
||||||
func main() {
|
func main() {
|
||||||
if len(os.Args) != 2 {
|
if len(os.Args) != 2 {
|
||||||
fmt.Println("Usage: go run main.go <output-file>")
|
fmt.Println("Usage: go run main.go <output-file>")
|
||||||
|
@ -118,6 +136,7 @@ func main() {
|
||||||
p.addDescriptions,
|
p.addDescriptions,
|
||||||
p.addEnums,
|
p.addEnums,
|
||||||
removeJobsFields,
|
removeJobsFields,
|
||||||
|
makeVolumeTypeOptional,
|
||||||
addInterpolationPatterns,
|
addInterpolationPatterns,
|
||||||
})
|
})
|
||||||
if err != nil {
|
if err != nil {
|
||||||
|
|
|
@ -0,0 +1,10 @@
|
||||||
|
bundle:
|
||||||
|
name: volume with incorrect type
|
||||||
|
|
||||||
|
resources:
|
||||||
|
volumes:
|
||||||
|
foo:
|
||||||
|
catalog_name: main
|
||||||
|
name: my_volume
|
||||||
|
schema_name: myschema
|
||||||
|
volume_type: incorrect_type
|
|
@ -0,0 +1,9 @@
|
||||||
|
bundle:
|
||||||
|
name: a volume
|
||||||
|
|
||||||
|
resources:
|
||||||
|
volumes:
|
||||||
|
foo:
|
||||||
|
catalog_name: main
|
||||||
|
name: my_volume
|
||||||
|
schema_name: myschema
|
|
@ -68,6 +68,7 @@ func Initialize() bundle.Mutator {
|
||||||
mutator.SetRunAs(),
|
mutator.SetRunAs(),
|
||||||
mutator.OverrideCompute(),
|
mutator.OverrideCompute(),
|
||||||
mutator.ConfigureDashboardDefaults(),
|
mutator.ConfigureDashboardDefaults(),
|
||||||
|
mutator.ConfigureVolumeDefaults(),
|
||||||
mutator.ProcessTargetMode(),
|
mutator.ProcessTargetMode(),
|
||||||
mutator.ApplyPresets(),
|
mutator.ApplyPresets(),
|
||||||
mutator.DefaultQueueing(),
|
mutator.DefaultQueueing(),
|
||||||
|
|
|
@ -791,6 +791,51 @@
|
||||||
}
|
}
|
||||||
]
|
]
|
||||||
},
|
},
|
||||||
|
"resources.Volume": {
|
||||||
|
"anyOf": [
|
||||||
|
{
|
||||||
|
"type": "object",
|
||||||
|
"properties": {
|
||||||
|
"catalog_name": {
|
||||||
|
"description": "The name of the catalog where the schema and the volume are",
|
||||||
|
"$ref": "#/$defs/string"
|
||||||
|
},
|
||||||
|
"comment": {
|
||||||
|
"description": "The comment attached to the volume",
|
||||||
|
"$ref": "#/$defs/string"
|
||||||
|
},
|
||||||
|
"grants": {
|
||||||
|
"$ref": "#/$defs/slice/github.com/databricks/cli/bundle/config/resources.Grant"
|
||||||
|
},
|
||||||
|
"name": {
|
||||||
|
"description": "The name of the volume",
|
||||||
|
"$ref": "#/$defs/string"
|
||||||
|
},
|
||||||
|
"schema_name": {
|
||||||
|
"description": "The name of the schema where the volume is",
|
||||||
|
"$ref": "#/$defs/string"
|
||||||
|
},
|
||||||
|
"storage_location": {
|
||||||
|
"description": "The storage location on the cloud",
|
||||||
|
"$ref": "#/$defs/string"
|
||||||
|
},
|
||||||
|
"volume_type": {
|
||||||
|
"$ref": "#/$defs/github.com/databricks/databricks-sdk-go/service/catalog.VolumeType"
|
||||||
|
}
|
||||||
|
},
|
||||||
|
"additionalProperties": false,
|
||||||
|
"required": [
|
||||||
|
"catalog_name",
|
||||||
|
"name",
|
||||||
|
"schema_name"
|
||||||
|
]
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"type": "string",
|
||||||
|
"pattern": "\\$\\{(var(\\.[a-zA-Z]+([-_]?[a-zA-Z0-9]+)*(\\[[0-9]+\\])*)+)\\}"
|
||||||
|
}
|
||||||
|
]
|
||||||
|
},
|
||||||
"variable.Lookup": {
|
"variable.Lookup": {
|
||||||
"anyOf": [
|
"anyOf": [
|
||||||
{
|
{
|
||||||
|
@ -963,6 +1008,9 @@
|
||||||
},
|
},
|
||||||
"name": {
|
"name": {
|
||||||
"$ref": "#/$defs/string"
|
"$ref": "#/$defs/string"
|
||||||
|
},
|
||||||
|
"uuid": {
|
||||||
|
"$ref": "#/$defs/string"
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
"additionalProperties": false,
|
"additionalProperties": false,
|
||||||
|
@ -1157,6 +1205,9 @@
|
||||||
},
|
},
|
||||||
"schemas": {
|
"schemas": {
|
||||||
"$ref": "#/$defs/map/github.com/databricks/cli/bundle/config/resources.Schema"
|
"$ref": "#/$defs/map/github.com/databricks/cli/bundle/config/resources.Schema"
|
||||||
|
},
|
||||||
|
"volumes": {
|
||||||
|
"$ref": "#/$defs/map/github.com/databricks/cli/bundle/config/resources.Volume"
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
"additionalProperties": false
|
"additionalProperties": false
|
||||||
|
@ -1558,6 +1609,13 @@
|
||||||
}
|
}
|
||||||
]
|
]
|
||||||
},
|
},
|
||||||
|
"catalog.VolumeType": {
|
||||||
|
"type": "string",
|
||||||
|
"enum": [
|
||||||
|
"EXTERNAL",
|
||||||
|
"MANAGED"
|
||||||
|
]
|
||||||
|
},
|
||||||
"compute.Adlsgen2Info": {
|
"compute.Adlsgen2Info": {
|
||||||
"anyOf": [
|
"anyOf": [
|
||||||
{
|
{
|
||||||
|
@ -5565,6 +5623,20 @@
|
||||||
}
|
}
|
||||||
]
|
]
|
||||||
},
|
},
|
||||||
|
"resources.Volume": {
|
||||||
|
"anyOf": [
|
||||||
|
{
|
||||||
|
"type": "object",
|
||||||
|
"additionalProperties": {
|
||||||
|
"$ref": "#/$defs/github.com/databricks/cli/bundle/config/resources.Volume"
|
||||||
|
}
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"type": "string",
|
||||||
|
"pattern": "\\$\\{(var(\\.[a-zA-Z]+([-_]?[a-zA-Z0-9]+)*(\\[[0-9]+\\])*)+)\\}"
|
||||||
|
}
|
||||||
|
]
|
||||||
|
},
|
||||||
"variable.TargetVariable": {
|
"variable.TargetVariable": {
|
||||||
"anyOf": [
|
"anyOf": [
|
||||||
{
|
{
|
||||||
|
|
|
@ -250,7 +250,7 @@ func (t *cobraTestRunner) RunBackground() {
|
||||||
// Reset context on command for the next test.
|
// Reset context on command for the next test.
|
||||||
// These commands are globals so we have to clean up to the best of our ability after each run.
|
// These commands are globals so we have to clean up to the best of our ability after each run.
|
||||||
// See https://github.com/spf13/cobra/blob/a6f198b635c4b18fff81930c40d464904e55b161/command.go#L1062-L1066
|
// See https://github.com/spf13/cobra/blob/a6f198b635c4b18fff81930c40d464904e55b161/command.go#L1062-L1066
|
||||||
//lint:ignore SA1012 cobra sets the context and doesn't clear it
|
//nolint:staticcheck // cobra sets the context and doesn't clear it
|
||||||
cli.SetContext(nil)
|
cli.SetContext(nil)
|
||||||
|
|
||||||
// Make caller aware of error.
|
// Make caller aware of error.
|
||||||
|
|
Loading…
Reference in New Issue