Compare commits

..

2 Commits

Author SHA1 Message Date
Denis Bilenko 0a36681bef
Add golangci-lint v1.62.2 (#1953) 2024-12-04 17:40:19 +00:00
shreyas-goenka 0da17f6ec6
Add default value for `volume_type` for DABs (#1952)
## Changes

The Unity Catalog volumes API requires a `volume_type` argument when
creating volumes. In the context of DABs, it's unnecessary to require
users to specify the volume type every time. We can default to "MANAGED"
instead.

This PR is similar to https://github.com/databricks/cli/pull/1743 which
does the same for dashboards.

## Tests
Unit test
2024-12-04 11:05:54 +00:00
13 changed files with 279 additions and 7 deletions

View File

@ -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

19
.golangci.yaml Normal file
View File

@ -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/

View File

@ -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,

View File

@ -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 ./...

View File

@ -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
}

View File

@ -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))
}

View File

@ -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)

View File

@ -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 {

View File

@ -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

View File

@ -0,0 +1,9 @@
bundle:
name: a volume
resources:
volumes:
foo:
catalog_name: main
name: my_volume
schema_name: myschema

View File

@ -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(),

View File

@ -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": [
{ {

View File

@ -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.