feat: add --write-output flag to helmfile fetch for air-gapped environments (#2572)
* feat: add --write-output flag to helmfile fetch for air-gapped environments Add --write-output flag to helmfile fetch that outputs a modified helmfile.yaml with chart references updated to point to downloaded local chart paths. Combined with --output-dir, this enables preparing all charts for deployment in air-gapped environments. Usage: helmfile fetch --output-dir ./charts --write-output > helmfile-airgapped.yaml Fixes #2571 Signed-off-by: yxxhero <yxxhero@users.noreply.github.com> Signed-off-by: yxxhero <aiopsclub@163.com> * fix: update fetch-write-output integration test grep to match YAML list item chart field Agent-Logs-Url: https://github.com/helmfile/helmfile/sessions/d00f71ab-d40d-4220-9b11-97674597685f Co-authored-by: yxxhero <11087727+yxxhero@users.noreply.github.com> * fix: send status messages to stderr and enforce sequential processing in --write-output mode Agent-Logs-Url: https://github.com/helmfile/helmfile/sessions/d338e24c-4f6f-4a59-a319-4b975e0efdcb Co-authored-by: yxxhero <11087727+yxxhero@users.noreply.github.com> * fix: restore SequentialHelmfiles after Fetch and use %s for YAML string formatting Agent-Logs-Url: https://github.com/helmfile/helmfile/sessions/cfa9f3f4-c72f-4760-9c51-88bc6f30add2 Co-authored-by: yxxhero <11087727+yxxhero@users.noreply.github.com> * test: add test for SequentialHelmfiles restore after Fetch with --write-output Agent-Logs-Url: https://github.com/helmfile/helmfile/sessions/cfa9f3f4-c72f-4760-9c51-88bc6f30add2 Co-authored-by: yxxhero <11087727+yxxhero@users.noreply.github.com> * fix: disable live output on --write-output and fix shell quoting/portability in integration test Agent-Logs-Url: https://github.com/helmfile/helmfile/sessions/b0eb0d3d-493b-4d77-b8eb-2a5c0ce70d86 Co-authored-by: yxxhero <11087727+yxxhero@users.noreply.github.com> * fix: use unquoted ${helmfile} variable to allow word splitting for EXTRA_HELMFILE_FLAGS Agent-Logs-Url: https://github.com/helmfile/helmfile/sessions/d025a111-f7d0-439e-bf14-5508c40d0b51 Co-authored-by: yxxhero <11087727+yxxhero@users.noreply.github.com> * fix: restore helm.EnableLiveOutput after Fetch --write-output via defer Agent-Logs-Url: https://github.com/helmfile/helmfile/sessions/ddb8c5fc-ebd1-4f09-9474-5da58938a219 Co-authored-by: yxxhero <11087727+yxxhero@users.noreply.github.com> * test: strengthen enableLiveOutput restore assertion with non-trivial initial value Agent-Logs-Url: https://github.com/helmfile/helmfile/sessions/d1d0ba9e-5c97-48e1-b761-8bdee391efb2 Co-authored-by: yxxhero <11087727+yxxhero@users.noreply.github.com> * feat: restrict --write-output to a single helmfile state file with clear error Agent-Logs-Url: https://github.com/helmfile/helmfile/sessions/f608a0d0-7f52-4e3f-9fac-ab966bd01efb Co-authored-by: yxxhero <11087727+yxxhero@users.noreply.github.com> * refactor: apply code review suggestions for variable and test naming Agent-Logs-Url: https://github.com/helmfile/helmfile/sessions/f608a0d0-7f52-4e3f-9fac-ab966bd01efb Co-authored-by: yxxhero <11087727+yxxhero@users.noreply.github.com> * fix: replace naked return with explicit return ok, errs to fix nakedret lint error Agent-Logs-Url: https://github.com/helmfile/helmfile/sessions/4b060131-a977-44b0-98f7-42bc108ae8e8 Co-authored-by: yxxhero <11087727+yxxhero@users.noreply.github.com> * fix: buffer YAML output and update --write-output flag description Agent-Logs-Url: https://github.com/helmfile/helmfile/sessions/50c6ad2e-125c-43c1-b9c3-37fe1686a8eb Co-authored-by: yxxhero <11087727+yxxhero@users.noreply.github.com> * fix: shorten --write-output flag description, move detail to Long help Agent-Logs-Url: https://github.com/helmfile/helmfile/sessions/50c6ad2e-125c-43c1-b9c3-37fe1686a8eb Co-authored-by: yxxhero <11087727+yxxhero@users.noreply.github.com> --------- Signed-off-by: yxxhero <yxxhero@users.noreply.github.com> Signed-off-by: yxxhero <aiopsclub@163.com> Co-authored-by: copilot-swe-agent[bot] <198982749+Copilot@users.noreply.github.com>
This commit is contained in:
parent
a8e8b67086
commit
08a22772f7
|
|
@ -15,6 +15,14 @@ func NewFetchCmd(globalCfg *config.GlobalImpl) *cobra.Command {
|
|||
cmd := &cobra.Command{
|
||||
Use: "fetch",
|
||||
Short: "Fetch charts from state file",
|
||||
Long: `Fetch downloads all charts referenced in the Helmfile state.
|
||||
|
||||
Useful for air-gapped environments: download charts with --output-dir and --write-output,
|
||||
then transfer the output directory and the generated helmfile.yaml to the air-gapped environment.
|
||||
|
||||
The --write-output flag requires a single helmfile state file specified with -f.
|
||||
It fails if the input resolves to multiple state files (e.g. a directory or a helmfile
|
||||
with nested helmfiles: entries).`,
|
||||
RunE: func(cmd *cobra.Command, args []string) error {
|
||||
fetchImpl := config.NewFetchImpl(globalCfg, fetchOptions)
|
||||
err := config.NewCLIConfigImpl(fetchImpl.GlobalImpl)
|
||||
|
|
@ -35,6 +43,7 @@ func NewFetchCmd(globalCfg *config.GlobalImpl) *cobra.Command {
|
|||
f.IntVar(&fetchOptions.Concurrency, "concurrency", 0, "maximum number of concurrent helm processes to run, 0 is unlimited")
|
||||
f.StringVar(&fetchOptions.OutputDir, "output-dir", "", "directory to store charts (default: temporary directory which is deleted when the command terminates)")
|
||||
f.StringVar(&fetchOptions.OutputDirTemplate, "output-dir-template", state.DefaultFetchOutputDirTemplate, "go text template for generating the output directory. Available fields: {{ .OutputDir }}, {{ .ChartName }}, {{ .Release.* }}, {{ .Environment.Name }}, {{ .Environment.KubeContext }}, {{ .Environment.Values.* }}")
|
||||
f.BoolVar(&fetchOptions.WriteOutput, "write-output", false, "write a helmfile.yaml to stdout with chart references updated to local chart paths; requires --output-dir and a single helmfile (use -f)")
|
||||
|
||||
return cmd
|
||||
}
|
||||
|
|
|
|||
|
|
@ -393,7 +393,49 @@ func (a *App) Unittest(c UnittestConfigProvider) error {
|
|||
}
|
||||
|
||||
func (a *App) Fetch(c FetchConfigProvider) error {
|
||||
return a.ForEachState(func(run *Run) (ok bool, errs []error) {
|
||||
if c.WriteOutput() && c.OutputDir() == "" {
|
||||
return fmt.Errorf("--output-dir is required when --write-output is set")
|
||||
}
|
||||
|
||||
if c.WriteOutput() {
|
||||
// Force sequential processing to ensure YAML documents are emitted in order
|
||||
// without interleaving when multiple helmfile state files are processed.
|
||||
// Restore the original value when Fetch returns so the App instance is not
|
||||
// permanently mutated (important for tests and library usage).
|
||||
prev := a.SequentialHelmfiles
|
||||
a.SequentialHelmfiles = true
|
||||
defer func() { a.SequentialHelmfiles = prev }()
|
||||
}
|
||||
|
||||
// processedStateFileCount tracks how many state files have been processed when
|
||||
// --write-output is set; used to detect multi-file inputs early and return
|
||||
// a clear error instead of silently producing semantically incorrect YAML.
|
||||
var processedStateFileCount int
|
||||
|
||||
// yamlOutput buffers the generated YAML document so that nothing is written to
|
||||
// stdout until ForEachState completes successfully. This prevents partial/corrupted
|
||||
// output reaching stdout when a later state file (or chart download error) causes
|
||||
// the operation to fail.
|
||||
var yamlOutput strings.Builder
|
||||
|
||||
err := a.ForEachState(func(run *Run) (ok bool, errs []error) {
|
||||
if c.WriteOutput() {
|
||||
processedStateFileCount++
|
||||
if processedStateFileCount > 1 {
|
||||
return false, []error{fmt.Errorf(
|
||||
"--write-output requires a single helmfile state file, but multiple were found; " +
|
||||
"use -f to specify a single helmfile instead of a directory or a helmfile with nested helmfiles: entries",
|
||||
)}
|
||||
}
|
||||
|
||||
// Disable live output to avoid Helm progress/status lines being streamed
|
||||
// to stdout and corrupting the YAML document emitted by --write-output.
|
||||
// Restore the original value when this callback returns so the cached helm
|
||||
// exec instance is not permanently mutated (important for tests and library usage).
|
||||
run.helm.SetEnableLiveOutput(false)
|
||||
defer run.helm.SetEnableLiveOutput(a.EnableLiveOutput)
|
||||
}
|
||||
|
||||
prepErr := run.withPreparedCharts("pull", state.ChartPrepareOptions{
|
||||
ForceDownload: true,
|
||||
SkipRefresh: c.SkipRefresh(),
|
||||
|
|
@ -403,6 +445,27 @@ func (a *App) Fetch(c FetchConfigProvider) error {
|
|||
OutputDirTemplate: c.OutputDirTemplate(),
|
||||
Concurrency: c.Concurrency(),
|
||||
}, func() []error {
|
||||
if c.WriteOutput() {
|
||||
for i := range run.state.Releases {
|
||||
rel := &run.state.Releases[i]
|
||||
if rel.ChartPath != "" {
|
||||
rel.Chart = rel.ChartPath
|
||||
rel.ChartPath = ""
|
||||
}
|
||||
}
|
||||
|
||||
stateYaml, yamlErr := run.state.ToYaml()
|
||||
if yamlErr != nil {
|
||||
return []error{yamlErr}
|
||||
}
|
||||
|
||||
sourceFile, pathErr := run.state.FullFilePath()
|
||||
if pathErr != nil {
|
||||
return []error{pathErr}
|
||||
}
|
||||
fmt.Fprintf(&yamlOutput, "---\n# Source: %s\n\n%s", sourceFile, stateYaml)
|
||||
}
|
||||
|
||||
return nil
|
||||
})
|
||||
|
||||
|
|
@ -410,8 +473,14 @@ func (a *App) Fetch(c FetchConfigProvider) error {
|
|||
errs = append(errs, prepErr)
|
||||
}
|
||||
|
||||
return
|
||||
return ok, errs
|
||||
}, false, SetFilter(true))
|
||||
|
||||
if err == nil && c.WriteOutput() {
|
||||
fmt.Print(yamlOutput.String())
|
||||
}
|
||||
|
||||
return err
|
||||
}
|
||||
|
||||
func (a *App) Sync(c SyncConfigProvider) error {
|
||||
|
|
|
|||
|
|
@ -2478,6 +2478,10 @@ func (c configImpl) EnforceNeedsAreInstalled() bool {
|
|||
return c.enforceNeedsAreInstalled
|
||||
}
|
||||
|
||||
func (c configImpl) WriteOutput() bool {
|
||||
return false
|
||||
}
|
||||
|
||||
type applyConfig struct {
|
||||
args string
|
||||
cascade string
|
||||
|
|
@ -2805,8 +2809,9 @@ func MockExecer(logger *zap.SugaredLogger, kubeContext string) (helmexec.Interfa
|
|||
// mocking helmexec.Interface
|
||||
|
||||
type mockHelmExec struct {
|
||||
templated []mockTemplates
|
||||
repos []mockRepo
|
||||
templated []mockTemplates
|
||||
repos []mockRepo
|
||||
enableLiveOutput bool
|
||||
}
|
||||
|
||||
type mockTemplates struct {
|
||||
|
|
@ -2846,6 +2851,7 @@ func (helm *mockHelmExec) SetHelmBinary(bin string) {
|
|||
}
|
||||
|
||||
func (helm *mockHelmExec) SetEnableLiveOutput(enableLiveOutput bool) {
|
||||
helm.enableLiveOutput = enableLiveOutput
|
||||
}
|
||||
|
||||
func (helm *mockHelmExec) SetDisableForceUpdate(forceUpdate bool) {
|
||||
|
|
@ -4587,6 +4593,168 @@ releases:
|
|||
"state should contain source helmfile name:\n%s\n", out)
|
||||
}
|
||||
|
||||
type fetchConfigImpl struct {
|
||||
configImpl
|
||||
outputDir string
|
||||
outputDirTemplate string
|
||||
writeOutput bool
|
||||
}
|
||||
|
||||
func (f fetchConfigImpl) OutputDir() string {
|
||||
return f.outputDir
|
||||
}
|
||||
|
||||
func (f fetchConfigImpl) OutputDirTemplate() string {
|
||||
return f.outputDirTemplate
|
||||
}
|
||||
|
||||
func (f fetchConfigImpl) WriteOutput() bool {
|
||||
return f.writeOutput
|
||||
}
|
||||
|
||||
func TestFetch_WriteOutputRequiresOutputDir(t *testing.T) {
|
||||
files := map[string]string{
|
||||
"/path/to/helmfile.yaml": `
|
||||
releases:
|
||||
- name: myrelease1
|
||||
chart: mychart1
|
||||
`,
|
||||
}
|
||||
|
||||
var buffer bytes.Buffer
|
||||
syncWriter := testhelper.NewSyncWriter(&buffer)
|
||||
logger := helmexec.NewLogger(syncWriter, "debug")
|
||||
|
||||
app := appWithFs(&App{
|
||||
OverrideHelmBinary: DefaultHelmBinary,
|
||||
fs: ffs.DefaultFileSystem(),
|
||||
OverrideKubeContext: "default",
|
||||
DisableKubeVersionAutoDetection: true,
|
||||
Env: "default",
|
||||
Logger: logger,
|
||||
Namespace: "testNamespace",
|
||||
}, files)
|
||||
|
||||
expectNoCallsToHelm(app)
|
||||
|
||||
err := app.Fetch(fetchConfigImpl{
|
||||
writeOutput: true,
|
||||
outputDir: "",
|
||||
})
|
||||
assert.Error(t, err)
|
||||
assert.Contains(t, err.Error(), "--output-dir is required")
|
||||
}
|
||||
|
||||
func TestFetch_WriteOutput_ErrorsOnMultipleStateFiles(t *testing.T) {
|
||||
// Two separate helmfile state files in a helmfile.d directory simulate the
|
||||
// multi-file scenario that --write-output cannot safely handle: the resulting
|
||||
// multi-document YAML stream would be merged by Helmfile in a way that can
|
||||
// alter semantics (helmDefaults override, broken relative paths, etc.).
|
||||
files := map[string]string{
|
||||
"/path/to/helmfile.d/first.yaml": `
|
||||
releases:
|
||||
- name: release1
|
||||
chart: chart1
|
||||
`,
|
||||
"/path/to/helmfile.d/second.yaml": `
|
||||
releases:
|
||||
- name: release2
|
||||
chart: chart2
|
||||
`,
|
||||
}
|
||||
|
||||
var buffer bytes.Buffer
|
||||
syncWriter := testhelper.NewSyncWriter(&buffer)
|
||||
logger := helmexec.NewLogger(syncWriter, "debug")
|
||||
|
||||
valsRuntime, err := vals.New(vals.Options{CacheSize: 32})
|
||||
if err != nil {
|
||||
t.Fatalf("unexpected error creating vals runtime: %v", err)
|
||||
}
|
||||
|
||||
helm := &mockHelmExec{}
|
||||
|
||||
app := appWithFs(&App{
|
||||
OverrideHelmBinary: DefaultHelmBinary,
|
||||
fs: ffs.DefaultFileSystem(),
|
||||
OverrideKubeContext: "default",
|
||||
DisableKubeVersionAutoDetection: true,
|
||||
Env: "default",
|
||||
Logger: logger,
|
||||
helms: map[helmKey]helmexec.Interface{
|
||||
createHelmKey(DefaultHelmBinary, "default"): helm,
|
||||
},
|
||||
Namespace: "testNamespace",
|
||||
valsRuntime: valsRuntime,
|
||||
}, files)
|
||||
|
||||
outputDir := t.TempDir()
|
||||
|
||||
fetchErr := app.Fetch(fetchConfigImpl{
|
||||
writeOutput: true,
|
||||
outputDir: outputDir,
|
||||
})
|
||||
assert.Error(t, fetchErr, "expected error when --write-output is used with multiple state files")
|
||||
assert.Contains(t, fetchErr.Error(), "--write-output requires a single helmfile state file")
|
||||
}
|
||||
|
||||
func TestFetch_WriteOutputRestoresSequentialHelmfiles(t *testing.T) {
|
||||
files := map[string]string{
|
||||
"/path/to/helmfile.yaml": `
|
||||
releases:
|
||||
- name: myrelease1
|
||||
chart: mychart1
|
||||
`,
|
||||
}
|
||||
|
||||
var buffer bytes.Buffer
|
||||
syncWriter := testhelper.NewSyncWriter(&buffer)
|
||||
logger := helmexec.NewLogger(syncWriter, "debug")
|
||||
|
||||
valsRuntime, err := vals.New(vals.Options{CacheSize: 32})
|
||||
if err != nil {
|
||||
t.Fatalf("unexpected error creating vals runtime: %v", err)
|
||||
}
|
||||
|
||||
// Use a real mock helm exec (not noCallHelmExec) so that Fetch can proceed
|
||||
// past the validation check and enter the SequentialHelmfiles mutation block.
|
||||
// Start with enableLiveOutput = true so the restore path is actually exercised:
|
||||
// Fetch will call SetEnableLiveOutput(false), then the deferred restore call
|
||||
// SetEnableLiveOutput(true) (a.EnableLiveOutput). If the defer were missing,
|
||||
// helm.enableLiveOutput would remain false and the assertion below would fail.
|
||||
helm := &mockHelmExec{enableLiveOutput: true}
|
||||
|
||||
app := appWithFs(&App{
|
||||
OverrideHelmBinary: DefaultHelmBinary,
|
||||
fs: ffs.DefaultFileSystem(),
|
||||
OverrideKubeContext: "default",
|
||||
DisableKubeVersionAutoDetection: true,
|
||||
Env: "default",
|
||||
Logger: logger,
|
||||
helms: map[helmKey]helmexec.Interface{
|
||||
createHelmKey(DefaultHelmBinary, "default"): helm,
|
||||
},
|
||||
Namespace: "testNamespace",
|
||||
valsRuntime: valsRuntime,
|
||||
// Start with SequentialHelmfiles = false; it must be restored after Fetch.
|
||||
SequentialHelmfiles: false,
|
||||
// Start with EnableLiveOutput = true; the deferred restore must bring it back.
|
||||
EnableLiveOutput: true,
|
||||
}, files)
|
||||
|
||||
outputDir := t.TempDir()
|
||||
|
||||
// Fetch with --write-output + --output-dir enters the mutation block,
|
||||
// temporarily sets SequentialHelmfiles = true and helm.EnableLiveOutput = false,
|
||||
// then restores both when it returns.
|
||||
_ = app.Fetch(fetchConfigImpl{
|
||||
writeOutput: true,
|
||||
outputDir: outputDir,
|
||||
})
|
||||
assert.False(t, app.SequentialHelmfiles, "SequentialHelmfiles should be restored to false after Fetch returns")
|
||||
assert.True(t, helm.enableLiveOutput, "helm.enableLiveOutput should be restored to true (a.EnableLiveOutput) after Fetch returns")
|
||||
}
|
||||
|
||||
func TestList(t *testing.T) {
|
||||
files := map[string]string{
|
||||
"/path/to/helmfile.d/first.yaml": `
|
||||
|
|
|
|||
|
|
@ -242,6 +242,7 @@ type FetchConfigProvider interface {
|
|||
SkipRefresh() bool
|
||||
OutputDir() string
|
||||
OutputDirTemplate() string
|
||||
WriteOutput() bool
|
||||
|
||||
concurrencyConfig
|
||||
}
|
||||
|
|
|
|||
|
|
@ -88,7 +88,7 @@ func (r *Run) withPreparedCharts(helmfileCommand string, opts state.ChartPrepare
|
|||
dir = tempDir
|
||||
} else {
|
||||
dir = opts.OutputDir
|
||||
fmt.Printf("Charts will be downloaded to: %s\n", dir)
|
||||
fmt.Fprintf(os.Stderr, "Charts will be downloaded to: %s\n", dir)
|
||||
}
|
||||
|
||||
if _, err := r.state.TriggerGlobalPrepareEvent(helmfileCommand); err != nil {
|
||||
|
|
|
|||
|
|
@ -1,6 +1,6 @@
|
|||
package config
|
||||
|
||||
// FetchOptions is the options for the build command
|
||||
// FetchOptions is the options for the fetch command
|
||||
type FetchOptions struct {
|
||||
// Concurrency is the maximum number of concurrent helm processes to run, 0 is unlimited
|
||||
Concurrency int
|
||||
|
|
@ -8,14 +8,16 @@ type FetchOptions struct {
|
|||
OutputDir string
|
||||
// OutputDirTemplate is the go template to generate the path of output directory
|
||||
OutputDirTemplate string
|
||||
// WriteOutput writes a helmfile.yaml with chart references updated to point to downloaded local chart paths
|
||||
WriteOutput bool
|
||||
}
|
||||
|
||||
// NewFetchOptions creates a new Apply
|
||||
// NewFetchOptions creates a new FetchOptions
|
||||
func NewFetchOptions() *FetchOptions {
|
||||
return &FetchOptions{}
|
||||
}
|
||||
|
||||
// FetchImpl is impl for applyOptions
|
||||
// FetchImpl is impl for fetchOptions
|
||||
type FetchImpl struct {
|
||||
*GlobalImpl
|
||||
*FetchOptions
|
||||
|
|
@ -43,3 +45,8 @@ func (c *FetchImpl) OutputDir() string {
|
|||
func (c *FetchImpl) OutputDirTemplate() string {
|
||||
return c.FetchOptions.OutputDirTemplate
|
||||
}
|
||||
|
||||
// WriteOutput returns whether to write a modified helmfile.yaml with local chart paths
|
||||
func (c *FetchImpl) WriteOutput() bool {
|
||||
return c.FetchOptions.WriteOutput
|
||||
}
|
||||
|
|
|
|||
|
|
@ -99,6 +99,7 @@ ${kubectl} create namespace ${test_ns} || fail "Could not create namespace ${tes
|
|||
. ${dir}/test-cases/issue-2502-race-condition-local-chart.sh
|
||||
. ${dir}/test-cases/chart-deps-condition.sh
|
||||
. ${dir}/test-cases/fetch-forl-local-chart.sh
|
||||
. ${dir}/test-cases/fetch-write-output.sh
|
||||
. ${dir}/test-cases/suppress-output-line-regex.sh
|
||||
. ${dir}/test-cases/chartify-jsonPatches-and-strategicMergePatches.sh
|
||||
. ${dir}/test-cases/include-template-func.sh
|
||||
|
|
|
|||
|
|
@ -0,0 +1,39 @@
|
|||
fetch_write_output_input_dir="${cases_dir}/fetch-write-output/input"
|
||||
|
||||
fetch_write_output_tmp=$(mktemp -d)
|
||||
|
||||
case_title="fetch with --write-output for air-gapped environments"
|
||||
|
||||
test_start "$case_title"
|
||||
|
||||
info "Testing helmfile fetch --write-output with local chart"
|
||||
output=$(${helmfile} -f "${fetch_write_output_input_dir}/helmfile.yaml.gotmpl" fetch --output-dir "${fetch_write_output_tmp}" --write-output 2>/dev/null) \
|
||||
|| fail "\"helmfile fetch --write-output\" shouldn't fail"
|
||||
|
||||
info "Verifying stdout does not contain non-YAML status messages"
|
||||
echo "${output}" | grep -q "^Charts will be downloaded to:" && fail "stdout should not contain 'Charts will be downloaded to:' (should be on stderr)" || true
|
||||
|
||||
info "Verifying output contains YAML document separator"
|
||||
echo "${output}" | grep -q "^---" || fail "output should contain YAML document separator"
|
||||
|
||||
info "Verifying output contains source helmfile reference"
|
||||
echo "${output}" | grep -q "# Source:" || fail "output should contain source helmfile reference"
|
||||
|
||||
info "Verifying output contains release name"
|
||||
echo "${output}" | grep -q "name: local-chart" || fail "output should contain release name"
|
||||
|
||||
info "Verifying output contains updated chart path pointing to output dir"
|
||||
echo "${output}" | grep -q "chart:" || fail "output should contain chart field"
|
||||
|
||||
info "Verifying chart files exist in output directory"
|
||||
cat "${fetch_write_output_tmp}/helmfile-tests/local-chart/raw/latest/Chart.yaml" || fail "Chart.yaml should exist in fetched output directory"
|
||||
|
||||
info "Verifying the chart path in output matches the actual downloaded location"
|
||||
chart_path=$(echo "${output}" | grep -E "^[[:space:]]+(-[[:space:]]+)?chart:" | head -1 | sed 's/.*chart: *//' | tr -d '"')
|
||||
if [ ! -f "${chart_path}/Chart.yaml" ]; then
|
||||
fail "chart path '${chart_path}' from output should point to a directory containing Chart.yaml"
|
||||
fi
|
||||
|
||||
rm -rf "${fetch_write_output_tmp}"
|
||||
|
||||
test_pass "$case_title"
|
||||
|
|
@ -0,0 +1,4 @@
|
|||
releases:
|
||||
- name: local-chart
|
||||
chart: ../../../charts/raw
|
||||
namespace: local-chart
|
||||
Loading…
Reference in New Issue