mirror of https://github.com/databricks/cli.git
Add support for validating CLI version when loading a jsonschema object (#883)
## Changes Updates to bundle templates can require updated versions of the CLI. This PR extends the JSON schema representation to allow template authors to set a min CLI version they require for their templates. This is required to make improvements/additions to the mlops-stacks repo ## Tests Tested using unit tests and manually. For manualy testing, I created a custom build of the CLI using go releaser and then tested it against a local instance of mlops-stack When mlops-stack schema has: ``` "min_databricks_cli_version": "v5000.1.1", ``` output (error as expected) ``` shreyas.goenka@THW32HFW6T bricks % ./dist/cli_darwin_arm64/databricks bundle init ~/mlops-stack Error: minimum CLI version "v5000.1.1" is greater than current CLI version "v0.207.2-dev+1b992c0". Please upgrade your current Databricks CLI ``` When the mlops-stack schema has: ``` "min_databricks_cli_version": "v0.1.1", ``` output (validation passes) ``` shreyas.goenka@THW32HFW6T bricks % ./dist/cli_darwin_arm64/databricks bundle init ~/mlops-stack Welcome to MLOps Stack. For detailed information on project generation, see the README at https://github.com/databricks/mlops-stack/blob/main/README.md. Project Name [my-mlops-project]: ^C ```
This commit is contained in:
parent
7139487c2f
commit
3700785dfa
|
@ -33,6 +33,8 @@ var info Info
|
||||||
|
|
||||||
var once sync.Once
|
var once sync.Once
|
||||||
|
|
||||||
|
var DefaultSemver = "0.0.0-dev"
|
||||||
|
|
||||||
// getDefaultBuildVersion uses build information stored by Go itself
|
// getDefaultBuildVersion uses build information stored by Go itself
|
||||||
// to synthesize a build version if one wasn't set.
|
// to synthesize a build version if one wasn't set.
|
||||||
// This is necessary if the binary was not built through goreleaser.
|
// This is necessary if the binary was not built through goreleaser.
|
||||||
|
@ -47,7 +49,7 @@ func getDefaultBuildVersion() string {
|
||||||
m[s.Key] = s.Value
|
m[s.Key] = s.Value
|
||||||
}
|
}
|
||||||
|
|
||||||
out := "0.0.0-dev"
|
out := DefaultSemver
|
||||||
|
|
||||||
// Append revision as build metadata.
|
// Append revision as build metadata.
|
||||||
if v, ok := m["vcs.revision"]; ok {
|
if v, ok := m["vcs.revision"]; ok {
|
||||||
|
|
|
@ -18,4 +18,9 @@ type Extension struct {
|
||||||
// PatternMatchFailureMessage is a user defined message that is displayed to the
|
// PatternMatchFailureMessage is a user defined message that is displayed to the
|
||||||
// user if a JSON schema pattern match fails.
|
// user if a JSON schema pattern match fails.
|
||||||
PatternMatchFailureMessage string `json:"pattern_match_failure_message,omitempty"`
|
PatternMatchFailureMessage string `json:"pattern_match_failure_message,omitempty"`
|
||||||
|
|
||||||
|
// Set the minimum semver version of this CLI to validate when loading this schema.
|
||||||
|
// If the CLI version is less than this value, then validation for this
|
||||||
|
// schema will fail.
|
||||||
|
MinDatabricksCliVersion string `json:"min_databricks_cli_version,omitempty"`
|
||||||
}
|
}
|
||||||
|
|
|
@ -6,6 +6,9 @@ import (
|
||||||
"os"
|
"os"
|
||||||
"regexp"
|
"regexp"
|
||||||
"slices"
|
"slices"
|
||||||
|
|
||||||
|
"github.com/databricks/cli/internal/build"
|
||||||
|
"golang.org/x/mod/semver"
|
||||||
)
|
)
|
||||||
|
|
||||||
// defines schema for a json object
|
// defines schema for a json object
|
||||||
|
@ -67,8 +70,8 @@ const (
|
||||||
IntegerType Type = "integer"
|
IntegerType Type = "integer"
|
||||||
)
|
)
|
||||||
|
|
||||||
func (schema *Schema) validate() error {
|
|
||||||
// Validate property types are all valid JSON schema types.
|
// Validate property types are all valid JSON schema types.
|
||||||
|
func (schema *Schema) validateSchemaPropertyTypes() error {
|
||||||
for _, v := range schema.Properties {
|
for _, v := range schema.Properties {
|
||||||
switch v.Type {
|
switch v.Type {
|
||||||
case NumberType, BooleanType, StringType, IntegerType:
|
case NumberType, BooleanType, StringType, IntegerType:
|
||||||
|
@ -83,8 +86,11 @@ func (schema *Schema) validate() error {
|
||||||
return fmt.Errorf("type %s is not a recognized json schema type", v.Type)
|
return fmt.Errorf("type %s is not a recognized json schema type", v.Type)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
return nil
|
||||||
|
}
|
||||||
|
|
||||||
// Validate default property values are consistent with types.
|
// Validate default property values are consistent with types.
|
||||||
|
func (schema *Schema) validateSchemaDefaultValueTypes() error {
|
||||||
for name, property := range schema.Properties {
|
for name, property := range schema.Properties {
|
||||||
if property.Default == nil {
|
if property.Default == nil {
|
||||||
continue
|
continue
|
||||||
|
@ -93,8 +99,11 @@ func (schema *Schema) validate() error {
|
||||||
return fmt.Errorf("type validation for default value of property %s failed: %w", name, err)
|
return fmt.Errorf("type validation for default value of property %s failed: %w", name, err)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
return nil
|
||||||
|
}
|
||||||
|
|
||||||
// Validate enum field values for properties are consistent with types.
|
// Validate enum field values for properties are consistent with types.
|
||||||
|
func (schema *Schema) validateSchemaEnumValueTypes() error {
|
||||||
for name, property := range schema.Properties {
|
for name, property := range schema.Properties {
|
||||||
if property.Enum == nil {
|
if property.Enum == nil {
|
||||||
continue
|
continue
|
||||||
|
@ -106,8 +115,11 @@ func (schema *Schema) validate() error {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
return nil
|
||||||
|
}
|
||||||
|
|
||||||
// Validate default value is contained in the list of enums if both are defined.
|
// Validate default value is contained in the list of enums if both are defined.
|
||||||
|
func (schema *Schema) validateSchemaDefaultValueIsInEnums() error {
|
||||||
for name, property := range schema.Properties {
|
for name, property := range schema.Properties {
|
||||||
if property.Default == nil || property.Enum == nil {
|
if property.Default == nil || property.Enum == nil {
|
||||||
continue
|
continue
|
||||||
|
@ -118,8 +130,11 @@ func (schema *Schema) validate() error {
|
||||||
return fmt.Errorf("list of enum values for property %s does not contain default value %v: %v", name, property.Default, property.Enum)
|
return fmt.Errorf("list of enum values for property %s does not contain default value %v: %v", name, property.Default, property.Enum)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
return nil
|
||||||
|
}
|
||||||
|
|
||||||
// Validate usage of "pattern" is consistent.
|
// Validate usage of "pattern" is consistent.
|
||||||
|
func (schema *Schema) validateSchemaPattern() error {
|
||||||
for name, property := range schema.Properties {
|
for name, property := range schema.Properties {
|
||||||
pattern := property.Pattern
|
pattern := property.Pattern
|
||||||
if pattern == "" {
|
if pattern == "" {
|
||||||
|
@ -153,6 +168,47 @@ func (schema *Schema) validate() error {
|
||||||
return nil
|
return nil
|
||||||
}
|
}
|
||||||
|
|
||||||
|
func (schema *Schema) validateSchemaMinimumCliVersion(currentVersion string) func() error {
|
||||||
|
return func() error {
|
||||||
|
if schema.MinDatabricksCliVersion == "" {
|
||||||
|
return nil
|
||||||
|
}
|
||||||
|
|
||||||
|
// Ignore this validation rule for local builds.
|
||||||
|
if semver.Compare("v"+build.DefaultSemver, currentVersion) == 0 {
|
||||||
|
return nil
|
||||||
|
}
|
||||||
|
|
||||||
|
// Confirm that MinDatabricksCliVersion is a valid semver.
|
||||||
|
if !semver.IsValid(schema.MinDatabricksCliVersion) {
|
||||||
|
return fmt.Errorf("invalid minimum CLI version %q specified. Please specify the version in the format v0.0.0", schema.MinDatabricksCliVersion)
|
||||||
|
}
|
||||||
|
|
||||||
|
// Confirm that MinDatabricksCliVersion is less than or equal to the current version.
|
||||||
|
if semver.Compare(schema.MinDatabricksCliVersion, currentVersion) > 0 {
|
||||||
|
return fmt.Errorf("minimum CLI version %q is greater than current CLI version %q. Please upgrade your current Databricks CLI", schema.MinDatabricksCliVersion, currentVersion)
|
||||||
|
}
|
||||||
|
return nil
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
func (schema *Schema) validate() error {
|
||||||
|
for _, fn := range []func() error{
|
||||||
|
schema.validateSchemaPropertyTypes,
|
||||||
|
schema.validateSchemaDefaultValueTypes,
|
||||||
|
schema.validateSchemaEnumValueTypes,
|
||||||
|
schema.validateSchemaDefaultValueIsInEnums,
|
||||||
|
schema.validateSchemaPattern,
|
||||||
|
schema.validateSchemaMinimumCliVersion("v" + build.GetInfo().Version),
|
||||||
|
} {
|
||||||
|
err := fn()
|
||||||
|
if err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return nil
|
||||||
|
}
|
||||||
|
|
||||||
func Load(path string) (*Schema, error) {
|
func Load(path string) (*Schema, error) {
|
||||||
b, err := os.ReadFile(path)
|
b, err := os.ReadFile(path)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
|
|
|
@ -222,3 +222,42 @@ func TestSchemaValidatePatternEnum(t *testing.T) {
|
||||||
}
|
}
|
||||||
assert.NoError(t, s.validate())
|
assert.NoError(t, s.validate())
|
||||||
}
|
}
|
||||||
|
|
||||||
|
func TestValidateSchemaMinimumCliVersionWithInvalidSemver(t *testing.T) {
|
||||||
|
s := &Schema{
|
||||||
|
Extension: Extension{
|
||||||
|
MinDatabricksCliVersion: "1.0.5",
|
||||||
|
},
|
||||||
|
}
|
||||||
|
err := s.validateSchemaMinimumCliVersion("v2.0.1")()
|
||||||
|
assert.ErrorContains(t, err, "invalid minimum CLI version \"1.0.5\" specified. Please specify the version in the format v0.0.0")
|
||||||
|
|
||||||
|
s.MinDatabricksCliVersion = "v1.0.5"
|
||||||
|
err = s.validateSchemaMinimumCliVersion("v2.0.1")()
|
||||||
|
assert.NoError(t, err)
|
||||||
|
}
|
||||||
|
|
||||||
|
func TestValidateSchemaMinimumCliVersion(t *testing.T) {
|
||||||
|
s := &Schema{
|
||||||
|
Extension: Extension{
|
||||||
|
MinDatabricksCliVersion: "v1.0.5",
|
||||||
|
},
|
||||||
|
}
|
||||||
|
err := s.validateSchemaMinimumCliVersion("v2.0.1")()
|
||||||
|
assert.NoError(t, err)
|
||||||
|
|
||||||
|
err = s.validateSchemaMinimumCliVersion("v1.0.5")()
|
||||||
|
assert.NoError(t, err)
|
||||||
|
|
||||||
|
err = s.validateSchemaMinimumCliVersion("v1.0.6")()
|
||||||
|
assert.NoError(t, err)
|
||||||
|
|
||||||
|
err = s.validateSchemaMinimumCliVersion("v1.0.4")()
|
||||||
|
assert.ErrorContains(t, err, `minimum CLI version "v1.0.5" is greater than current CLI version "v1.0.4". Please upgrade your current Databricks CLI`)
|
||||||
|
|
||||||
|
err = s.validateSchemaMinimumCliVersion("v0.0.1")()
|
||||||
|
assert.ErrorContains(t, err, "minimum CLI version \"v1.0.5\" is greater than current CLI version \"v0.0.1\". Please upgrade your current Databricks CLI")
|
||||||
|
|
||||||
|
err = s.validateSchemaMinimumCliVersion("v0.0.0-dev")()
|
||||||
|
assert.NoError(t, err)
|
||||||
|
}
|
||||||
|
|
Loading…
Reference in New Issue