diff --git a/chartvalidator/checker/appsets.go b/chartvalidator/checker/appsets.go index 04477cf..1c6dc96 100644 --- a/chartvalidator/checker/appsets.go +++ b/chartvalidator/checker/appsets.go @@ -170,4 +170,4 @@ func str(v any) string { return s } return fmt.Sprintf("%v", v) -} \ No newline at end of file +} diff --git a/chartvalidator/checker/engine_app_checker.go b/chartvalidator/checker/engine_app_checker.go index 5e23b13..6b9f261 100644 --- a/chartvalidator/checker/engine_app_checker.go +++ b/chartvalidator/checker/engine_app_checker.go @@ -3,6 +3,7 @@ package main import ( "context" "fmt" + "path/filepath" "sync" ) @@ -21,75 +22,84 @@ type AppCheckerEngine struct { resultChan chan AppCheckResult errorChan chan ErrorResult - ChartRenderingEngine *ChartRenderingEngine + ChartRenderingEngine *ChartRenderingEngine ManifestValidationEngine *ManifestValidationEngine - ImageExtractionEngine *ImageExtractionEngine + ImageExtractionEngine *ImageExtractionEngine DockerValidationEngine *DockerImageValidationEngine - context context.Context - executor CommandExecutor + context context.Context + executor CommandExecutor workerWaitGroup sync.WaitGroup name string } -func NewAppCheckerEngine(context context.Context, outputDir string) *AppCheckerEngine { +func NewAppCheckerEngine(context context.Context, outputDir string, apiVersions []string) *AppCheckerEngine { errorChan := make(chan ErrorResult) cre := ChartRenderingEngine{ - inputChan: make(chan ChartRenderParams), - resultChan: make(chan RenderResult), - errorChan: errorChan, - outputDir: outputDir, - context: context, - executor: &RealCommandExecutor{}, - name: "ChartRenderer", + inputChan: make(chan ChartRenderParams), + resultChan: make(chan RenderResult), + errorChan: errorChan, + outputDir: outputDir, + context: context, + executor: &RealCommandExecutor{}, + name: "ChartRenderer", + apiVersions: apiVersions, + } + + // Construct absolute path to schemas directory + schemasDir, err := filepath.Abs("schemas") + if err != nil { + // If we can't get the absolute path, use relative path as fallback + schemasDir = "schemas" } mve := ManifestValidationEngine{ - inputChan: cre.resultChan, - resultChan: make(chan ManifestValidationResult), - errorChan: errorChan, - context: context, - executor: &RealCommandExecutor{}, - name: "ManifestValidator", + inputChan: cre.resultChan, + resultChan: make(chan ManifestValidationResult), + errorChan: errorChan, + context: context, + executor: &RealCommandExecutor{}, + name: "ManifestValidator", + schemasDir: schemasDir, workerWaitGroup: sync.WaitGroup{}, } iee := ImageExtractionEngine{ - inputChan: mve.resultChan, - outputChan: make(chan ImageExtractionResult), - errorChan: errorChan, - context: context, - name: "ImageExtractor", + inputChan: mve.resultChan, + outputChan: make(chan ImageExtractionResult), + errorChan: errorChan, + context: context, + name: "ImageExtractor", workerWaitGroup: sync.WaitGroup{}, } dve := DockerImageValidationEngine{ - inputChan: iee.outputChan, - outputChan: make(chan DockerImageValidationResult), - context: context, - executor: &RealCommandExecutor{}, - name: "DockerValidator", - cache: map[string]DockerImageValidationResult{}, - pending: map[string]*sync.WaitGroup{}, - cacheLock: sync.RWMutex{}, + inputChan: iee.outputChan, + outputChan: make(chan DockerImageValidationResult), + context: context, + executor: &RealCommandExecutor{}, + name: "DockerValidator", + cache: map[string]DockerImageValidationResult{}, + pending: map[string]*sync.WaitGroup{}, + cacheLock: sync.RWMutex{}, workerWaitGroup: sync.WaitGroup{}, } - + return &AppCheckerEngine{ inputChan: make(chan AppCheckInstruction), resultChan: make(chan AppCheckResult), errorChan: make(chan ErrorResult), - context: context, - executor: &RealCommandExecutor{}, + context: context, + executor: &RealCommandExecutor{}, - ChartRenderingEngine: &cre, + ChartRenderingEngine: &cre, ManifestValidationEngine: &mve, - ImageExtractionEngine: &iee, + ImageExtractionEngine: &iee, DockerValidationEngine: &dve, name: "AppChecker", @@ -98,7 +108,7 @@ func NewAppCheckerEngine(context context.Context, outputDir string) *AppCheckerE func (engine *AppCheckerEngine) allDoneWorker() { engine.workerWaitGroup.Wait() - logEngineDebug(engine.name,-1,"all workers done, closing output channel") + logEngineDebug(engine.name, -1, "all workers done, closing output channel") close(engine.resultChan) } @@ -113,7 +123,7 @@ func (engine *AppCheckerEngine) Start(workerCount int) { // Pour the input instructions into the chart renderer engine.workerWaitGroup.Add(1) go engine.pumpAppCheckInstructionsToChartRenderer() - engine.workerWaitGroup.Add(1) + engine.workerWaitGroup.Add(1) go engine.pumpOutputsToAppCheckResults() go engine.allDoneWorker() @@ -147,14 +157,7 @@ func (engine *AppCheckerEngine) pumpOutputsToAppCheckResults() { func (engine *AppCheckerEngine) pumpAppCheckInstructionsToChartRenderer() { defer engine.workerWaitGroup.Done() for instruction := range engine.inputChan { - engine.ChartRenderingEngine.inputChan <- ChartRenderParams{ - Env: instruction.Chart.Env, - ChartName: instruction.Chart.ChartName, - RepoURL: instruction.Chart.RepoURL, - ChartVersion: instruction.Chart.ChartVersion, - BaseValuesFile: instruction.Chart.BaseValuesFile, - ValuesOverride: instruction.Chart.ValuesOverride, - } + engine.ChartRenderingEngine.inputChan <- instruction.Chart } close(engine.ChartRenderingEngine.inputChan) -} \ No newline at end of file +} diff --git a/chartvalidator/checker/engine_chart_rendering.go b/chartvalidator/checker/engine_chart_rendering.go index 2252701..5fa75f8 100644 --- a/chartvalidator/checker/engine_chart_rendering.go +++ b/chartvalidator/checker/engine_chart_rendering.go @@ -10,21 +10,21 @@ import ( "sync" ) - type ChartRenderingEngine struct { inputChan chan ChartRenderParams resultChan chan RenderResult errorChan chan ErrorResult - outputDir string - context context.Context - executor CommandExecutor - name string + outputDir string + context context.Context + executor CommandExecutor + name string workerWaitGroup sync.WaitGroup + apiVersions []string } type RenderResult struct { - Chart ChartRenderParams + Chart ChartRenderParams ManifestPath string } @@ -36,7 +36,7 @@ func (engine *ChartRenderingEngine) Start(workerCount int) { } for i := 0; i < workerCount; i++ { - engine.workerWaitGroup.Add(1) + engine.workerWaitGroup.Add(1) go func(workerId int) { engine.worker(workerId) }(i) @@ -46,7 +46,7 @@ func (engine *ChartRenderingEngine) Start(workerCount int) { func (engine *ChartRenderingEngine) allDoneWorker() { engine.workerWaitGroup.Wait() - logEngineDebug(engine.name,-1,"all workers done, closing output channel") + logEngineDebug(engine.name, -1, "all workers done, closing output channel") close(engine.resultChan) } @@ -74,7 +74,6 @@ func (engine *ChartRenderingEngine) worker(workerId int) { } } - func (engine *ChartRenderingEngine) renderSingleChart(chart ChartRenderParams, workerId int) (*RenderResult, error) { if !engine.executor.FileExists(chart.BaseValuesFile) { @@ -96,16 +95,24 @@ func (engine *ChartRenderingEngine) renderSingleChart(chart ChartRenderParams, w "-f", chart.ValuesOverride, "--version", chart.ChartVersion, "--include-crds", + "--kube-version", kubernetesVersion, + } + + // Add API versions if any + for _, apiVer := range engine.apiVersions { + if apiVer != "" { + args = append(args, "--api-versions", apiVer) + } } logEngineDebug(engine.name, workerId, fmt.Sprintf("helm %s", strings.Join(args, " "))) cmd := engine.executor.CommandContext(engine.context, "helm", args...) - + // Set working directory to current directory so relative paths work if wd, err := os.Getwd(); err == nil { cmd.SetDir(wd) } - + output, err := cmd.CombinedOutput() if err != nil { msg := fmt.Sprintf("helm command failed: %s\nOutput: %s", err.Error(), string(output)) @@ -122,7 +129,7 @@ func (engine *ChartRenderingEngine) renderSingleChart(chart ChartRenderParams, w logEngineWarning(engine.name, workerId, msg) return nil, fmt.Errorf("failed to get absolute path for output dir: %w", err) } - + randStr := generateRandomString(6) filename := fmt.Sprintf("%s_%s.yaml", chart.ChartName, randStr) outputPath := filepath.Join(absOutputDir, filename) diff --git a/chartvalidator/checker/engine_chart_rendering_test.go b/chartvalidator/checker/engine_chart_rendering_test.go index 91a8da6..b261751 100644 --- a/chartvalidator/checker/engine_chart_rendering_test.go +++ b/chartvalidator/checker/engine_chart_rendering_test.go @@ -10,17 +10,18 @@ import ( // Helper function to create and start a chart rendering engine func createEngine(mockExecutor *MockCommandExecutor, includeErrorChan bool) *ChartRenderingEngine { engine := &ChartRenderingEngine{ - inputChan: make(chan ChartRenderParams), - resultChan: make(chan RenderResult), - outputDir: "test_output", - context: context.Background(), - executor: mockExecutor, + inputChan: make(chan ChartRenderParams), + resultChan: make(chan RenderResult), + outputDir: "test_output", + context: context.Background(), + executor: mockExecutor, + apiVersions: []string{"something", "something-else"}, } - + if includeErrorChan { engine.errorChan = make(chan ErrorResult) } - + engine.Start(1) return engine } @@ -43,7 +44,7 @@ func TestRenderBasics(t *testing.T) { assertChartFieldsMatch(t, testChart, result.Chart) // Verify the command that was executed - expectedCommand := "helm template test-chart --release-name test-chart --repo https://example.com/charts -f values.yaml -f override.yaml --version 1.0.0 --include-crds" + expectedCommand := "helm template test-chart --release-name test-chart --repo https://example.com/charts -f values.yaml -f override.yaml --version 1.0.0 --include-crds --kube-version 1.33.0 --api-versions something --api-versions something-else" actualCommand := mockExecutor.GetFullCommand() assert.Equal(t, expectedCommand, actualCommand) } @@ -65,4 +66,4 @@ func TestRenderBaseFileNotExist(t *testing.T) { assert.Equal(t, errorResult.Chart.ChartName, testChart.ChartName) assert.NotNil(t, errorResult.Error) assert.Contains(t, errorResult.Error.Error(), "base values file does not exist") -} \ No newline at end of file +} diff --git a/chartvalidator/checker/engine_manifest_validation.go b/chartvalidator/checker/engine_manifest_validation.go index 654ece0..46ea6b8 100644 --- a/chartvalidator/checker/engine_manifest_validation.go +++ b/chartvalidator/checker/engine_manifest_validation.go @@ -9,12 +9,9 @@ import ( "sync" ) - - - type ManifestValidationResult struct { ManifestFile string - Chart ChartRenderParams + Chart ChartRenderParams Error error } @@ -23,15 +20,16 @@ type ManifestValidationEngine struct { resultChan chan ManifestValidationResult errorChan chan ErrorResult - context context.Context - executor CommandExecutor - name string + context context.Context + executor CommandExecutor + name string + schemasDir string workerWaitGroup sync.WaitGroup } func (engine *ManifestValidationEngine) Start(workerCount int) { for i := 0; i < workerCount; i++ { - engine.workerWaitGroup.Add(1) + engine.workerWaitGroup.Add(1) go func(workerId int) { engine.worker(workerId) }(i) @@ -41,7 +39,7 @@ func (engine *ManifestValidationEngine) Start(workerCount int) { func (engine *ManifestValidationEngine) allDoneWorker() { engine.workerWaitGroup.Wait() - logEngineDebug(engine.name,-1,"all workers done, closing output channel") + logEngineDebug(engine.name, -1, "all workers done, closing output channel") close(engine.resultChan) } @@ -54,11 +52,11 @@ func (engine *ManifestValidationEngine) worker(workerId int) { logEngineDebug(engine.name, workerId, "input closed") return } - result, err := engine.validateManifest(input.Chart,input.ManifestPath, workerId) + result, err := engine.validateManifest(input.Chart, input.ManifestPath, workerId) if err != nil { engine.errorChan <- ErrorResult{ Chart: input.Chart, - Error: fmt.Errorf("failed to validate manifest %s: %w", input.ManifestPath, err), + Error: fmt.Errorf("failed to validate manifest %s: %w", input.ManifestPath, err), } continue } else { @@ -70,7 +68,7 @@ func (engine *ManifestValidationEngine) worker(workerId int) { return } } -} +} func (engine *ManifestValidationEngine) validateManifest(chart ChartRenderParams, manifestFile string, workerId int) (*ManifestValidationResult, error) { @@ -79,34 +77,47 @@ func (engine *ManifestValidationEngine) validateManifest(chart ChartRenderParams logEngineWarning(engine.name, workerId, msg) return nil, fmt.Errorf("manifest file does not exist: %s", manifestFile) } - // Build kubeconform command + // Build kubeconform command with absolute schema paths + schemaPath1 := filepath.Join(engine.schemasDir, "{{.ResourceKind}}_{{.ResourceAPIVersion}}.json") + schemaPath2 := filepath.Join(engine.schemasDir, "{{.ResourceKind}}-{{.Group}}-{{.ResourceAPIVersion}}.json") + args := []string{ "-strict", "-summary", "-schema-location", "default", "-schema-location", "https://raw.githubusercontent.com/datreeio/CRDs-catalog/main/{{.Group}}/{{.ResourceKind}}_{{.ResourceAPIVersion}}.json", - "-schema-location", "ci/schemas/{{ .ResourceKind }}_{{ .ResourceAPIVersion }}.json", + "-schema-location", schemaPath1, + "-schema-location", schemaPath2, "-verbose", + "-skip", "CustomResourceDefinition", + "-kubernetes-version", kubernetesVersion, "-exit-on-error", manifestFile, } - cmd := engine.executor.CommandContext(engine.context, - "kubeconform", args... + cmd := engine.executor.CommandContext(engine.context, + "kubeconform", args..., ) + + // Set working directory to current directory so relative paths work + if wd, err := os.Getwd(); err == nil { + cmd.SetDir(wd) + } + cmdStr := fmt.Sprintf("%s %s", filepath.Base(cmd.GetPath()), strings.Join(args, " ")) logEngineDebug(engine.name, workerId, fmt.Sprintf("executing: %s", cmdStr)) - - if err := cmd.Run(); err != nil { - msg := fmt.Sprintf("kubeconform command failed: %s", err.Error()) + + output, err := cmd.CombinedOutput() + if err != nil { + msg := fmt.Sprintf("kubeconform command failed: %s\nOutput: %s", err.Error(), string(output)) logEngineWarning(engine.name, workerId, msg) return nil, fmt.Errorf("kubeconform command failed: %w", err) } - logEngineDebug(engine.name, workerId, fmt.Sprintf("succeeded: %s", cmdStr)) + logEngineDebug(engine.name, workerId, fmt.Sprintf("succeeded: %s\nOutput: %s", cmdStr, string(output))) return &ManifestValidationResult{ - ManifestFile: manifestFile, - Error: nil, - Chart: chart, + ManifestFile: manifestFile, + Error: nil, + Chart: chart, }, nil } diff --git a/chartvalidator/checker/engine_manifest_validation_test.go b/chartvalidator/checker/engine_manifest_validation_test.go index 1f47cba..86b46b0 100644 --- a/chartvalidator/checker/engine_manifest_validation_test.go +++ b/chartvalidator/checker/engine_manifest_validation_test.go @@ -22,9 +22,14 @@ func TestManifestValidationEngine(t *testing.T) { // Verify manifest file path is correct assert.Equal(t, testManifestFile, result.ManifestFile, "Expected correct manifest file path") - // Verify the command that was executed - expectedCommand := "kubeconform -strict -summary -schema-location default -schema-location https://raw.githubusercontent.com/datreeio/CRDs-catalog/main/{{.Group}}/{{.ResourceKind}}_{{.ResourceAPIVersion}}.json -schema-location ci/schemas/{{ .ResourceKind }}_{{ .ResourceAPIVersion }}.json -verbose -exit-on-error test_data/example.yaml" - assertCommandExecution(t, mockExecutor, expectedCommand) + // Verify the command that was executed (note: schemasDir will be an absolute path in tests) + // We just check that the command contains the key elements + actualCommand := mockExecutor.GetFullCommand() + assert.Contains(t, actualCommand, "kubeconform") + assert.Contains(t, actualCommand, "-strict") + assert.Contains(t, actualCommand, "-skip CustomResourceDefinition") + assert.Contains(t, actualCommand, "-kubernetes-version 1.33.0") + assert.Contains(t, actualCommand, "test_data/example.yaml") close(engine.inputChan) } @@ -59,7 +64,7 @@ func TestManifestValidationEngineMultipleFiles(t *testing.T) { { name: "configmap manifest2", manifestPath: "test_data/configmap.yaml", - }, + }, } mockExecutor := createManifestValidationMockExecutor() @@ -121,4 +126,4 @@ func TestManifestValidationEngineWithError(t *testing.T) { close(engine.inputChan) engine.workerWaitGroup.Wait() -} \ No newline at end of file +} diff --git a/chartvalidator/checker/main.go b/chartvalidator/checker/main.go index 3ca44ad..0f2cb21 100644 --- a/chartvalidator/checker/main.go +++ b/chartvalidator/checker/main.go @@ -5,11 +5,13 @@ import ( "flag" "fmt" "os" + "strings" "sync" ) var srcPrefix string = "../" var verboseLogging bool = false +var kubernetesVersion string = "1.33.0" func main() { if len(os.Args) < 2 { @@ -45,17 +47,17 @@ func printUsage() { fmt.Println("Use 'run-manifest-checks -h' to see command-specific flags.") } - - func runChartChecksCommand(args []string) { fs := flag.NewFlagSet("run-checks", flag.ExitOnError) var ( - singleEnv = fs.String("env", "", "Only process this environment (folder name under -envdir).") - envDir = fs.String("envdir", "../env", "Base directory containing environment folders.") - outputDir = fs.String("output", "manifests", "Output directory for rendered charts.") - verbose = fs.Bool("v", false, "Enable verbose logging.") - ) + singleEnv = fs.String("env", "", "Only process this environment (folder name under -envdir).") + envDir = fs.String("envdir", "../env", "Base directory containing environment folders.") + outputDir = fs.String("output", "manifests", "Output directory for rendered charts.") + apiVersions = fs.String("api-versions", "", "Comma-separated list of additional Kubernetes API versions to pass to Helm.") + kubernetesVersionArg = fs.String("k8s-version", "1.33.0", "Kubernetes version to use for manifest validation.") + verbose = fs.Bool("v", false, "Enable verbose logging.") + ) fs.Usage = func() { fmt.Println("Usage: run-manifest-checks run-checks [flags]") @@ -69,7 +71,7 @@ func runChartChecksCommand(args []string) { fmt.Println(" 5. Validate that each Docker image exists in the registry.") fmt.Println("") fmt.Println("Docker needs to be authenticated to the registries used by the charts for image validation to work.") - fmt.Println("") + fmt.Println("") fs.PrintDefaults() } @@ -78,8 +80,12 @@ func runChartChecksCommand(args []string) { } verboseLogging = *verbose - - if err := runAllChartChecks(*singleEnv, *envDir, *outputDir); err != nil { + kubernetesVersion = *kubernetesVersionArg + apiVersionsList := strings.Split(*apiVersions, ",") + if len(apiVersionsList) == 1 && apiVersionsList[0] == "" { + apiVersionsList = []string{} + } + if err := runAllChartChecks(*singleEnv, *envDir, *outputDir, apiVersionsList); err != nil { fmt.Fprintf(os.Stderr, "Error running chart checks: %v\n", err) os.Exit(1) } @@ -90,17 +96,19 @@ func runRenderOnlyCommand(args []string) { fs := flag.NewFlagSet("render-only", flag.ExitOnError) var ( - singleEnv = fs.String("env", "", "Only process this environment (folder name under -envdir).") - envDir = fs.String("envdir", "../env", "Base directory containing environment folders.") - outputDir = fs.String("output", "manifests", "Output directory for rendered charts.") - verbose = fs.Bool("v", false, "Enable verbose logging.") - ) + singleEnv = fs.String("env", "", "Only process this environment (folder name under -envdir).") + envDir = fs.String("envdir", "../env", "Base directory containing environment folders.") + outputDir = fs.String("output", "manifests", "Output directory for rendered charts.") + apiVersions = fs.String("api-versions", "", "Comma-separated list of additional Kubernetes API versions to pass to Helm.") + kubernetesVersionArg = fs.String("k8s-version", "1.33.0", "Kubernetes version to use for manifest validation.") + verbose = fs.Bool("v", false, "Enable verbose logging.") + ) fs.Usage = func() { fmt.Println("Usage: run-manifest-checks render-only [flags]") fmt.Println("") fmt.Println("Renders all charts found in the ApplicationSets in the specified environment and outputs the manifests to the specified output directory.") - fmt.Println("") + fmt.Println("") fs.PrintDefaults() } @@ -109,22 +117,26 @@ func runRenderOnlyCommand(args []string) { } verboseLogging = *verbose + kubernetesVersion = *kubernetesVersionArg - if err := runAllChartRenders(*singleEnv, *envDir, *outputDir); err != nil { + apiVersionsList := strings.Split(*apiVersions, ",") + if len(apiVersionsList) == 1 && apiVersionsList[0] == "" { + apiVersionsList = []string{} + } + if err := runAllChartRenders(*singleEnv, *envDir, *outputDir, apiVersionsList); err != nil { fmt.Fprintf(os.Stderr, "Error running chart renders: %v\n", err) os.Exit(1) } } - -func runAllChartRenders(singleEnv, envDir, outputDir string) error { +func runAllChartRenders(singleEnv, envDir, outputDir string, apiVersions []string) error { fmt.Println("Starting chart renders...") params, err := findChartsInAppsets(envDir, singleEnv) if err != nil { return fmt.Errorf("failed to find charts in ApplicationSets: %w", err) } - + fmt.Printf("Found %d charts to process.\n", len(params)) context := context.Background() @@ -135,14 +147,15 @@ func runAllChartRenders(singleEnv, envDir, outputDir string) error { } renderer := ChartRenderingEngine{ - context: context, - executor: &RealCommandExecutor{}, - outputDir: outputDir, - inputChan: make(chan ChartRenderParams), - resultChan: make(chan RenderResult), - name: "ChartRenderer", - errorChan: make(chan ErrorResult), + context: context, + executor: &RealCommandExecutor{}, + outputDir: outputDir, + inputChan: make(chan ChartRenderParams), + resultChan: make(chan RenderResult), + name: "ChartRenderer", + errorChan: make(chan ErrorResult), workerWaitGroup: sync.WaitGroup{}, + apiVersions: apiVersions, } renderer.Start(10) @@ -170,13 +183,13 @@ func runAllChartRenders(singleEnv, envDir, outputDir string) error { return nil } -func runAllChartChecks(singleEnv, envDir, outputDir string) error { +func runAllChartChecks(singleEnv, envDir, outputDir string, apiVersions []string) error { fmt.Println("Starting chart checks...") params, err := findChartsInAppsets(envDir, singleEnv) if err != nil { return fmt.Errorf("failed to find charts in ApplicationSets: %w", err) } - + fmt.Printf("Found %d charts to process.\n", len(params)) context := context.Background() @@ -186,7 +199,7 @@ func runAllChartChecks(singleEnv, envDir, outputDir string) error { return fmt.Errorf("failed to clear output directory: %w", err) } - appChecker := NewAppCheckerEngine(context, outputDir) + appChecker := NewAppCheckerEngine(context, outputDir, apiVersions) appChecker.Start(10) go func() { @@ -214,4 +227,4 @@ func runAllChartChecks(singleEnv, envDir, outputDir string) error { fmt.Println("Some chart checks failed. See above for details.") return fmt.Errorf("one or more chart checks failed") } -} \ No newline at end of file +} diff --git a/chartvalidator/checker/types.go b/chartvalidator/checker/types.go index 7bea375..095e151 100644 --- a/chartvalidator/checker/types.go +++ b/chartvalidator/checker/types.go @@ -18,9 +18,9 @@ type DockerImageValidationResult struct { } type ImageExtractionResult struct { - Chart ChartRenderParams + Chart ChartRenderParams ManifestFile string - Image string + Image string } // ChartRenderParams represents a Helm chart configuration extracted from ApplicationSet files @@ -64,9 +64,9 @@ type validationFailure struct { // imageCheckSetup manages image checking infrastructure type imageCheckSetup struct { - inputPipe chan *imageCheck - resultPipe chan *imageCheck - results map[string]*imageCheck - workerWg sync.WaitGroup - resultsWg sync.WaitGroup -} \ No newline at end of file + inputPipe chan *imageCheck + resultPipe chan *imageCheck + results map[string]*imageCheck + workerWg sync.WaitGroup + resultsWg sync.WaitGroup +}