From f4eb81b5d67d7c45664befbfe0eaecde9db5991f Mon Sep 17 00:00:00 2001 From: Gerald Pape Date: Wed, 6 Mar 2024 13:20:10 +0100 Subject: [PATCH] Add validation to skip-auto-generation flag --- cmd/helm-schema/cli.go | 2 +- cmd/helm-schema/main.go | 13 ++++++--- pkg/schema/schema.go | 59 ++++++++++++++++++++++++++++++++++------- 3 files changed, 59 insertions(+), 15 deletions(-) diff --git a/cmd/helm-schema/cli.go b/cmd/helm-schema/cli.go index bbd2bc0..0e80c62 100644 --- a/cmd/helm-schema/cli.go +++ b/cmd/helm-schema/cli.go @@ -62,7 +62,7 @@ func newCommand(run func(cmd *cobra.Command, args []string) error) (*cobra.Comma cmd.PersistentFlags(). StringP("output-file", "o", "values.schema.json", "jsonschema file path relative to each chart directory to which jsonschema will be written") cmd.PersistentFlags(). - StringSliceP("skip-auto-generation", "k", []string{}, "do not auto-create the given fields (e.g. title,description,...)") + StringSliceP("skip-auto-generation", "k", []string{}, "comma separated list of fields to skip from being created by default (possible: title, description, required, default, additionalProperties)") viper.AutomaticEnv() viper.SetEnvPrefix("HELM_SCHEMA") diff --git a/cmd/helm-schema/main.go b/cmd/helm-schema/main.go index bffd6be..57f5c2f 100644 --- a/cmd/helm-schema/main.go +++ b/cmd/helm-schema/main.go @@ -49,7 +49,7 @@ type Result struct { func worker( dryRun, keepFullComment, dontRemoveHelmDocsPrefix bool, valueFileNames []string, - skipAutoGeneration []string, + skipAutoGenerationConfig *schema.SkipAutoGenerationConfig, outFile string, queue <-chan string, results chan<- Result, @@ -121,7 +121,7 @@ func worker( continue } - result.Schema = schema.YamlToSchema(&values, keepFullComment, dontRemoveHelmDocsPrefix, skipAutoGeneration, nil) + result.Schema = schema.YamlToSchema(&values, keepFullComment, dontRemoveHelmDocsPrefix, skipAutoGenerationConfig, nil) results <- result } @@ -146,6 +146,11 @@ func exec(cmd *cobra.Command, _ []string) error { } workersCount := runtime.NumCPU() * 2 + skipConfig, err := schema.NewSkipAutoGenerationConfig(skipAutoGeneration) + if err != nil { + return err + } + // 1. Start a producer that searches Chart.yaml and values.yaml files queue := make(chan string) resultsChan := make(chan Result) @@ -172,7 +177,7 @@ func exec(cmd *cobra.Command, _ []string) error { keepFullComment, dontRemoveHelmDocsPrefix, valueFileNames, - skipAutoGeneration, + skipConfig, outFile, queue, resultsChan, @@ -194,7 +199,7 @@ loop: } // sort results with topology sort - results, err := util.TopSort[*Result, string](results, func(i *Result) string { + results, err = util.TopSort[*Result, string](results, func(i *Result) string { return i.Chart.Name }, func(d *Result) []string { diff --git a/pkg/schema/schema.go b/pkg/schema/schema.go index 2680ad0..4fed80b 100644 --- a/pkg/schema/schema.go +++ b/pkg/schema/schema.go @@ -292,6 +292,45 @@ func (s Schema) Validate() error { return nil } +var possibleSkipFields = []string{"title", "description", "required", "default", "additionalProperties"} + +type SkipAutoGenerationConfig struct { + Title, Description, Required, Default, AdditionalProperties bool +} + +func NewSkipAutoGenerationConfig(flag []string) (*SkipAutoGenerationConfig, error) { + var config SkipAutoGenerationConfig + + var invalidFlags []string + + for _, fieldName := range flag { + if !slices.Contains(possibleSkipFields, fieldName) { + invalidFlags = append(invalidFlags, fieldName) + } + if fieldName == "title" { + config.Title = true + } + if fieldName == "description" { + config.Description = true + } + if fieldName == "required" { + config.Required = true + } + if fieldName == "default" { + config.Default = true + } + if fieldName == "additionalProperties" { + config.AdditionalProperties = true + } + } + + if len(invalidFlags) != 0 { + return nil, fmt.Errorf("unsupported field names '%s' for skipping auto-generation", strings.Join(invalidFlags, "', '")) + } + + return &config, nil +} + func typeFromTag(tag string) ([]string, error) { switch tag { case nullTag: @@ -355,7 +394,7 @@ func YamlToSchema( node *yaml.Node, keepFullComment bool, dontRemoveHelmDocsPrefix bool, - skipAutoGeneration []string, + skipAutoGeneration *SkipAutoGenerationConfig, parentRequiredProperties *[]string, ) Schema { var schema Schema @@ -386,10 +425,10 @@ func YamlToSchema( schema.Properties["global"] = &Schema{ Type: []string{"object"}, } - if !slices.Contains(skipAutoGeneration, "title") { + if !skipAutoGeneration.Title { schema.Properties["global"].Title = "global" } - if !slices.Contains(skipAutoGeneration, "description") { + if !skipAutoGeneration.Description { schema.Properties["global"].Description = "Global values are values that can be accessed from any chart or subchart by exactly the same name." } } @@ -398,7 +437,7 @@ func YamlToSchema( schema.RequiredProperties = requiredProperties } // always disable on top level - if !slices.Contains(skipAutoGeneration, "additionalProperties") { + if !skipAutoGeneration.AdditionalProperties { schema.AdditionalProperties = new(bool) } case yaml.MappingNode: @@ -438,27 +477,27 @@ func YamlToSchema( } // Add key to required array of parent - if keyNodeSchema.Required || (!slices.Contains(skipAutoGeneration, "required") && !keyNodeSchema.HasData) { + if keyNodeSchema.Required || (!skipAutoGeneration.Required && !keyNodeSchema.HasData) { *parentRequiredProperties = append(*parentRequiredProperties, keyNode.Value) } - if !slices.Contains(skipAutoGeneration, "additionalProperties") && valueNode.Kind == yaml.MappingNode && + if !skipAutoGeneration.AdditionalProperties && valueNode.Kind == yaml.MappingNode && (!keyNodeSchema.HasData || keyNodeSchema.AdditionalProperties == nil) { keyNodeSchema.AdditionalProperties = new(bool) } // If no title was set, use the key value - if keyNodeSchema.Title == "" && !slices.Contains(skipAutoGeneration, "title") { + if keyNodeSchema.Title == "" && !skipAutoGeneration.Title { keyNodeSchema.Title = keyNode.Value } // If no description was set, use the rest of the comment as description - if keyNodeSchema.Description == "" && !slices.Contains(skipAutoGeneration, "description") { + if keyNodeSchema.Description == "" && !skipAutoGeneration.Description { keyNodeSchema.Description = description } // If no default value was set, use the values node value as default - if !slices.Contains(skipAutoGeneration, "default") && keyNodeSchema.Default == nil && valueNode.Kind == yaml.ScalarNode { + if !skipAutoGeneration.Default && keyNodeSchema.Default == nil && valueNode.Kind == yaml.ScalarNode { keyNodeSchema.Default = valueNode.Value } @@ -494,7 +533,7 @@ func YamlToSchema( itemSchema.RequiredProperties = itemRequiredProperties } - if !slices.Contains(skipAutoGeneration, "additionalProperties") && itemNode.Kind == yaml.MappingNode && (!itemSchema.HasData || itemSchema.AdditionalProperties == nil) { + if !skipAutoGeneration.AdditionalProperties && itemNode.Kind == yaml.MappingNode && (!itemSchema.HasData || itemSchema.AdditionalProperties == nil) { itemSchema.AdditionalProperties = new(bool) }