Skip to content

Commit

Permalink
Merge pull request #98 from dadav/feat/select_deps
Browse files Browse the repository at this point in the history
feat: Add dependency filter functionality
  • Loading branch information
dadav authored Dec 18, 2024
2 parents 98b4f86 + 5ce2d9c commit 2089934
Show file tree
Hide file tree
Showing 8 changed files with 201 additions and 90 deletions.
1 change: 1 addition & 0 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -81,6 +81,7 @@ Flags:
-r, --add-schema-reference "add reference to schema in values.yaml if not found"
-a, --append-newline "append newline to generated jsonschema at the end of the file"
-c, --chart-search-root string "directory to search recursively within for charts (default ".")"
-i, --dependencies-filter strings "only generate schema for specified dependencies (comma-separated list of dependency names)"
-x, --dont-strip-helm-docs-prefix "disable the removal of the helm-docs prefix (--)"
-d, --dry-run "don't actually create files just print to stdout passed"
-p, --helm-docs-compatibility-mode "parse and use helm-docs comments"
Expand Down
2 changes: 2 additions & 0 deletions cmd/helm-schema/cli.go
Original file line number Diff line number Diff line change
Expand Up @@ -71,6 +71,8 @@ func newCommand(run func(cmd *cobra.Command, args []string) error) (*cobra.Comma
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{}, "comma separated list of fields to skip from being created by default (possible: title, description, required, default, additionalProperties)")
cmd.PersistentFlags().
StringSliceP("dependencies-filter", "i", []string{}, "only generate schema for specified dependencies (comma-separated list of dependency names)")

viper.AutomaticEnv()
viper.SetEnvPrefix("HELM_SCHEMA")
Expand Down
64 changes: 45 additions & 19 deletions cmd/helm-schema/main.go
Original file line number Diff line number Diff line change
Expand Up @@ -12,11 +12,13 @@ import (
log "github.com/sirupsen/logrus"
"github.com/spf13/cobra"
"github.com/spf13/viper"
"gopkg.in/yaml.v3"

"github.com/dadav/helm-schema/pkg/chart"
"github.com/dadav/helm-schema/pkg/schema"
)

func searchFiles(startPath, fileName string, queue chan<- string, errs chan<- error) {
func searchFiles(chartSearchRoot, startPath, fileName string, dependenciesFilter map[string]bool, queue chan<- string, errs chan<- error) {
defer close(queue)
err := filepath.Walk(startPath, func(path string, info os.FileInfo, err error) error {
if err != nil {
Expand All @@ -25,7 +27,30 @@ func searchFiles(startPath, fileName string, queue chan<- string, errs chan<- er
}

if !info.IsDir() && info.Name() == fileName {
queue <- path
if filepath.Dir(path) == chartSearchRoot {
queue <- path
return nil
}

if len(dependenciesFilter) > 0 {
chartData, err := os.ReadFile(path)
if err != nil {
errs <- fmt.Errorf("failed to read Chart.yaml at %s: %w", path, err)
return nil
}

var chart chart.ChartFile
if err := yaml.Unmarshal(chartData, &chart); err != nil {
errs <- fmt.Errorf("failed to parse Chart.yaml at %s: %w", path, err)
return nil
}

if dependenciesFilter[chart.Name] {
queue <- path
}
} else {
queue <- path
}
}

return nil
Expand All @@ -50,6 +75,11 @@ func exec(cmd *cobra.Command, _ []string) error {
outFile := viper.GetString("output-file")
dontRemoveHelmDocsPrefix := viper.GetBool("dont-strip-helm-docs-prefix")
appendNewline := viper.GetBool("append-newline")
dependenciesFilter := viper.GetStringSlice("dependencies-filter")
dependenciesFilterMap := make(map[string]bool)
for _, dep := range dependenciesFilter {
dependenciesFilterMap[dep] = true
}
if err := viper.UnmarshalKey("value-files", &valueFileNames); err != nil {
return err
}
Expand All @@ -63,16 +93,14 @@ func exec(cmd *cobra.Command, _ []string) error {
return err
}

// 1. Start a producer that searches Chart.yaml and values.yaml files
queue := make(chan string)
resultsChan := make(chan schema.Result)
results := []*schema.Result{}
errs := make(chan error)
done := make(chan struct{})

go searchFiles(chartSearchRoot, "Chart.yaml", queue, errs)
go searchFiles(chartSearchRoot, chartSearchRoot, "Chart.yaml", dependenciesFilterMap, queue, errs)

// 2. Start workers and every worker does:
wg := sync.WaitGroup{}
go func() {
wg.Wait()
Expand Down Expand Up @@ -113,10 +141,8 @@ loop:
}
}

// sort results with topology sort (only if we're checking the dependencies)
if !noDeps {
// sort results with topology sort
results, err = schema.TopoSort(results)
results, err = schema.TopoSort(results, dependenciesFilterMap)
if err != nil {
if _, ok := err.(*schema.CircularError); !ok {
log.Errorf("Error while sorting results: %s", err)
Expand All @@ -128,16 +154,16 @@ loop:
}

conditionsToPatch := make(map[string][]string)
// Sort results if dependencies should be processed
// Need to resolve the dependencies from deepest level to highest

if !noDeps {
// Iterate over deps to find conditions we need to patch (dependencies that have a condition)
for _, result := range results {
if len(result.Errors) > 0 {
continue
}
for _, dep := range result.Chart.Dependencies {
if len(dependenciesFilterMap) > 0 && !dependenciesFilterMap[dep.Name] {
continue
}

if dep.Condition != "" {
conditionKeys := strings.Split(dep.Condition, ".")
conditionsToPatch[conditionKeys[0]] = conditionKeys[1:]
Expand All @@ -149,9 +175,7 @@ loop:
chartNameToResult := make(map[string]*schema.Result)
foundErrors := false

// process results
for _, result := range results {
// Error handling
if len(result.Errors) > 0 {
foundErrors = true
if result.Chart != nil {
Expand All @@ -172,7 +196,9 @@ loop:

log.Debugf("Processing result for chart: %s (%s)", result.Chart.Name, result.ChartPath)
if !noDeps {
// Patch condition into schema if needed
chartNameToResult[result.Chart.Name] = result
log.Debugf("Stored chart %s in chartNameToResult", result.Chart.Name)

if patch, ok := conditionsToPatch[result.Chart.Name]; ok {
schemaToPatch := &result.Schema
lastIndex := len(patch) - 1
Expand Down Expand Up @@ -200,6 +226,10 @@ loop:
}

for _, dep := range result.Chart.Dependencies {
if len(dependenciesFilterMap) > 0 && !dependenciesFilterMap[dep.Name] {
continue
}

if dep.Name != "" {
if dependencyResult, ok := chartNameToResult[dep.Name]; ok {
log.Debugf(
Expand All @@ -213,8 +243,6 @@ loop:
Description: dependencyResult.Chart.Description,
Properties: dependencyResult.Schema.Properties,
}
// you don't NEED to overwrite the values
// so every required check will be disabled
depSchema.DisableRequiredProperties()

if dep.Alias != "" {
Expand All @@ -230,10 +258,8 @@ loop:
log.Warnf("Dependency without name found (checkout %s).", result.ChartPath)
}
}
chartNameToResult[result.Chart.Name] = result
}

// Print to stdout or write to file
jsonStr, err := result.Schema.ToJson()
if err != nil {
log.Error(err)
Expand Down
5 changes: 2 additions & 3 deletions go.mod
Original file line number Diff line number Diff line change
Expand Up @@ -3,10 +3,9 @@ module github.com/dadav/helm-schema
go 1.23.1

require (
github.com/Masterminds/semver/v3 v3.3.1
github.com/dadav/go-jsonpointer v0.0.0-20240918181927-335cbee8c279
github.com/deckarep/golang-set/v2 v2.7.0
github.com/magiconair/properties v1.8.9
github.com/norwoodj/helm-docs v1.14.2
github.com/santhosh-tekuri/jsonschema/v6 v6.0.1
github.com/sirupsen/logrus v1.9.3
github.com/spf13/cobra v1.8.1
Expand All @@ -16,6 +15,7 @@ require (

require (
github.com/Masterminds/goutils v1.1.1 // indirect
github.com/Masterminds/semver/v3 v3.3.1 // indirect
github.com/Masterminds/sprig/v3 v3.2.3 // indirect
github.com/fsnotify/fsnotify v1.7.0 // indirect
github.com/google/uuid v1.4.0 // indirect
Expand All @@ -26,7 +26,6 @@ require (
github.com/mitchellh/copystructure v1.2.0 // indirect
github.com/mitchellh/mapstructure v1.5.0 // indirect
github.com/mitchellh/reflectwalk v1.0.2 // indirect
github.com/norwoodj/helm-docs v1.14.2 // indirect
github.com/pelletier/go-toml/v2 v2.2.2 // indirect
github.com/pkg/errors v0.9.1 // indirect
github.com/sagikazarmark/locafero v0.4.0 // indirect
Expand Down
10 changes: 2 additions & 8 deletions go.sum
Original file line number Diff line number Diff line change
Expand Up @@ -12,16 +12,14 @@ github.com/davecgh/go-spew v1.1.0/go.mod h1:J7Y8YcW2NihsgmVo/mv3lAwl/skON4iLHjSs
github.com/davecgh/go-spew v1.1.1/go.mod h1:J7Y8YcW2NihsgmVo/mv3lAwl/skON4iLHjSsI+c5H38=
github.com/davecgh/go-spew v1.1.2-0.20180830191138-d8f796af33cc h1:U9qPSI2PIWSS1VwoXQT9A3Wy9MM3WgvqSxFWenqJduM=
github.com/davecgh/go-spew v1.1.2-0.20180830191138-d8f796af33cc/go.mod h1:J7Y8YcW2NihsgmVo/mv3lAwl/skON4iLHjSsI+c5H38=
github.com/deckarep/golang-set/v2 v2.7.0 h1:gIloKvD7yH2oip4VLhsv3JyLLFnC0Y2mlusgcvJYW5k=
github.com/deckarep/golang-set/v2 v2.7.0/go.mod h1:VAky9rY/yGXJOLEDv3OMci+7wtDpOF4IN+y82NBOac4=
github.com/dlclark/regexp2 v1.11.0 h1:G/nrcoOa7ZXlpoa/91N3X7mM3r8eIlMBBJZvsz/mxKI=
github.com/dlclark/regexp2 v1.11.0/go.mod h1:DHkYz0B9wPfa6wondMfaivmHpzrQ3v9q8cnmRbL6yW8=
github.com/frankban/quicktest v1.14.6 h1:7Xjx+VpznH+oBnejlPUj8oUpdxnVs4f8XU8WnHkI4W8=
github.com/frankban/quicktest v1.14.6/go.mod h1:4ptaffx2x8+WTWXmUCuVU6aPUX1/Mz7zb5vbUoiM6w0=
github.com/fsnotify/fsnotify v1.7.0 h1:8JEhPFa5W2WU7YfeZzPNqzMP6Lwt7L2715Ggo0nosvA=
github.com/fsnotify/fsnotify v1.7.0/go.mod h1:40Bi/Hjc2AVfZrqy+aj+yEI+/bRxZnMJyTJwOpGvigM=
github.com/google/go-cmp v0.5.9 h1:O2Tfq5qg4qc4AmwVlvv0oLiVAGB7enBSJ2x2DqQFi38=
github.com/google/go-cmp v0.5.9/go.mod h1:17dUlkBOakJ0+DkrSSNjCkIjxS6bF9zb3elmeNGIjoY=
github.com/google/go-cmp v0.6.0 h1:ofyhxvXcZhMsU5ulbFiLKl/XBFqE1GSq7atu8tAmTRI=
github.com/google/go-cmp v0.6.0/go.mod h1:17dUlkBOakJ0+DkrSSNjCkIjxS6bF9zb3elmeNGIjoY=
github.com/google/uuid v1.1.1/go.mod h1:TIyPZe4MgqvfeYDBFedMoGGpEw/LqOeaOT+nhxU+yHo=
github.com/google/uuid v1.4.0 h1:MtMxsa51/r9yyhkyLsVeVt0B+BGQZzpQiTQ4eHZ8bc4=
github.com/google/uuid v1.4.0/go.mod h1:TIyPZe4MgqvfeYDBFedMoGGpEw/LqOeaOT+nhxU+yHo=
Expand All @@ -39,10 +37,6 @@ github.com/kr/pretty v0.3.1 h1:flRD4NNwYAUpkphVc1HcthR4KEIFJ65n8Mw5qdRn3LE=
github.com/kr/pretty v0.3.1/go.mod h1:hoEshYVHaxMs3cyo3Yncou5ZscifuDolrwPKZanG3xk=
github.com/kr/text v0.2.0 h1:5Nx0Ya0ZqY2ygV366QzturHI13Jq95ApcVaJBhpS+AY=
github.com/kr/text v0.2.0/go.mod h1:eLer722TekiGuMkidMxC/pM04lWEeraHUUmBw8l2grE=
github.com/magiconair/properties v1.8.7 h1:IeQXZAiQcpL9mgcAe1Nu6cX9LLw6ExEHKjN0VQdvPDY=
github.com/magiconair/properties v1.8.7/go.mod h1:Dhd985XPs7jluiymwWYZ0G4Z61jb3vdS329zhj2hYo0=
github.com/magiconair/properties v1.8.8 h1:Ver94o/KW27O7MbhemLysbQUa6lCdvy5Ol62vcYn4Q0=
github.com/magiconair/properties v1.8.8/go.mod h1:Dhd985XPs7jluiymwWYZ0G4Z61jb3vdS329zhj2hYo0=
github.com/magiconair/properties v1.8.9 h1:nWcCbLq1N2v/cpNsy5WvQ37Fb+YElfq20WJ/a8RkpQM=
github.com/magiconair/properties v1.8.9/go.mod h1:Dhd985XPs7jluiymwWYZ0G4Z61jb3vdS329zhj2hYo0=
github.com/mitchellh/copystructure v1.0.0/go.mod h1:SNtv71yrdKgLRyLFxmLdkAbkKEFWgYaq1OVrnRcwhnw=
Expand Down
88 changes: 88 additions & 0 deletions pkg/chart/chart_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -3,6 +3,8 @@ package chart
import (
"bytes"
"testing"

"gopkg.in/yaml.v3"
)

func TestReadChartFile(t *testing.T) {
Expand Down Expand Up @@ -30,3 +32,89 @@ func TestReadChartFile(t *testing.T) {
t.Errorf("Expected Dependency name was test, but got %v", c.Dependencies[0].Name)
}
}

func TestChartFileParsing(t *testing.T) {
tests := []struct {
name string
input string
expected ChartFile
wantErr bool
}{
{
name: "basic chart file",
input: `
name: mychart
description: A test chart
dependencies:
- name: dep1
alias: aliased-dep
condition: subchart.enabled`,
expected: ChartFile{
Name: "mychart",
Description: "A test chart",
Dependencies: []*Dependency{
{
Name: "dep1",
Alias: "aliased-dep",
Condition: "subchart.enabled",
},
},
},
wantErr: false,
},
{
name: "chart file without dependencies",
input: `
name: standalone
description: A standalone chart`,
expected: ChartFile{
Name: "standalone",
Description: "A standalone chart",
},
wantErr: false,
},
{
name: "invalid yaml",
input: `
name: broken
description: [broken`,
wantErr: true,
},
}

for _, tt := range tests {
t.Run(tt.name, func(t *testing.T) {
var got ChartFile
err := yaml.Unmarshal([]byte(tt.input), &got)

if (err != nil) != tt.wantErr {
t.Errorf("yaml.Unmarshal() error = %v, wantErr %v", err, tt.wantErr)
return
}

if !tt.wantErr {
if got.Name != tt.expected.Name {
t.Errorf("Name = %v, want %v", got.Name, tt.expected.Name)
}
if got.Description != tt.expected.Description {
t.Errorf("Description = %v, want %v", got.Description, tt.expected.Description)
}
if len(got.Dependencies) != len(tt.expected.Dependencies) {
t.Errorf("Dependencies length = %v, want %v", len(got.Dependencies), len(tt.expected.Dependencies))
return
}
for i, dep := range got.Dependencies {
if dep.Name != tt.expected.Dependencies[i].Name {
t.Errorf("Dependency[%d].Name = %v, want %v", i, dep.Name, tt.expected.Dependencies[i].Name)
}
if dep.Alias != tt.expected.Dependencies[i].Alias {
t.Errorf("Dependency[%d].Alias = %v, want %v", i, dep.Alias, tt.expected.Dependencies[i].Alias)
}
if dep.Condition != tt.expected.Dependencies[i].Condition {
t.Errorf("Dependency[%d].Condition = %v, want %v", i, dep.Condition, tt.expected.Dependencies[i].Condition)
}
}
}
})
}
}
4 changes: 1 addition & 3 deletions pkg/schema/schema.go
Original file line number Diff line number Diff line change
Expand Up @@ -866,9 +866,7 @@ func YamlToSchema(
itemRequiredProperties := []string{}
itemSchema := YamlToSchema(valuesPath, itemNode, keepFullComment, helmDocsCompatibilityMode, dontRemoveHelmDocsPrefix, skipAutoGeneration, &itemRequiredProperties)

for _, req := range itemRequiredProperties {
itemSchema.Required.Strings = append(itemSchema.Required.Strings, req)
}
itemSchema.Required.Strings = append(itemSchema.Required.Strings, itemRequiredProperties...)

if !skipAutoGeneration.AdditionalProperties && itemNode.Kind == yaml.MappingNode && (!itemSchema.HasData || itemSchema.AdditionalProperties == nil) {
itemSchema.AdditionalProperties = new(bool)
Expand Down
Loading

0 comments on commit 2089934

Please sign in to comment.