Issue-1883 fix (#2058)

* Issue-1883 fix

Signed-off-by: zhaque44 <haque.zubair@gmail.com>
Signed-off-by: yxxhero <aiopsclub@163.com>
This commit is contained in:
Zubair Haque 2025-11-18 18:45:56 -05:00 committed by GitHub
parent 258b8a0b30
commit ab5e9a1326
No known key found for this signature in database
GPG Key ID: B5690EEEBB952194
14 changed files with 176 additions and 78 deletions

View File

@ -47,6 +47,7 @@ func NewApplyCmd(globalCfg *config.GlobalImpl) *cobra.Command {
f.BoolVar(&applyOptions.SkipCRDs, "skip-crds", false, "if set, no CRDs will be installed on sync. By default, CRDs are installed if not already present")
f.BoolVar(&applyOptions.SkipNeeds, "skip-needs", true, `do not automatically include releases from the target release's "needs" when --selector/-l flag is provided. Does nothing when --selector/-l flag is not provided. Defaults to true when --include-needs or --include-transitive-needs is not provided`)
f.BoolVar(&applyOptions.IncludeNeeds, "include-needs", false, `automatically include releases from the target release's "needs" when --selector/-l flag is provided. Does nothing when --selector/-l flag is not provided`)
f.BoolVar(&applyOptions.EnforceNeedsAreInstalled, "enforce-needs-are-installed", false, "enforce that all 'needs' dependencies are installable before applying changes")
f.BoolVar(&applyOptions.IncludeTransitiveNeeds, "include-transitive-needs", false, `like --include-needs, but also includes transitive needs (needs of needs). Does nothing when --selector/-l flag is not provided. Overrides exclusions of other selectors and conditions.`)
f.BoolVar(&applyOptions.SkipDiffOnInstall, "skip-diff-on-install", false, "Skips running helm-diff on releases being newly installed on this apply. Useful when the release manifests are too huge to be reviewed, or it's too time-consuming to diff at all")
f.BoolVar(&applyOptions.IncludeTests, "include-tests", false, "enable the diffing of the helm test hooks")

View File

@ -40,6 +40,7 @@ func NewDiffCmd(globalCfg *config.GlobalImpl) *cobra.Command {
f.BoolVar(&diffOptions.SkipNeeds, "skip-needs", true, `do not automatically include releases from the target release's "needs" when --selector/-l flag is provided. Does nothing when --selector/-l flag is not provided. Defaults to true when --include-needs or --include-transitive-needs is not provided`)
f.BoolVar(&diffOptions.IncludeTests, "include-tests", false, "enable the diffing of the helm test hooks")
f.BoolVar(&diffOptions.IncludeNeeds, "include-needs", false, `automatically include releases from the target release's "needs" when --selector/-l flag is provided. Does nothing when --selector/-l flag is not provided`)
f.BoolVar(&diffOptions.EnforceNeedsAreInstalled, "enforce-needs-are-installed", false, "enforce that all 'needs' dependencies are installable before applying changes")
f.BoolVar(&diffOptions.IncludeTransitiveNeeds, "include-transitive-needs", false, `like --include-needs, but also includes transitive needs (needs of needs). Does nothing when --selector/-l flag is not provided. Overrides exclusions of other selectors and conditions.`)
f.BoolVar(&diffOptions.SkipDiffOnInstall, "skip-diff-on-install", false, "Skips running helm-diff on releases being newly installed on this apply. Useful when the release manifests are too huge to be reviewed, or it's too time-consuming to diff at all")
f.BoolVar(&diffOptions.ShowSecrets, "show-secrets", false, "do not redact secret values in the output. should be used for debug purpose only")

View File

@ -41,6 +41,7 @@ func NewSyncCmd(globalCfg *config.GlobalImpl) *cobra.Command {
f.BoolVar(&syncOptions.SkipCRDs, "skip-crds", false, "if set, no CRDs will be installed on sync. By default, CRDs are installed if not already present")
f.BoolVar(&syncOptions.IncludeNeeds, "include-needs", false, `automatically include releases from the target release's "needs" when --selector/-l flag is provided. Does nothing when --selector/-l flag is not provided`)
f.BoolVar(&syncOptions.IncludeTransitiveNeeds, "include-transitive-needs", false, `like --include-needs, but also includes transitive needs (needs of needs). Does nothing when --selector/-l flag is not provided. Overrides exclusions of other selectors and conditions.`)
f.BoolVar(&syncOptions.EnforceNeedsAreInstalled, "enforce-needs-are-installed", false, "enforce that all 'needs' dependencies are installable before applying changes")
f.BoolVar(&syncOptions.HideNotes, "hide-notes", false, "add --hide-notes flag to helm")
f.BoolVar(&syncOptions.TakeOwnership, "take-ownership", false, `add --take-ownership flag to helm`)
f.BoolVar(&syncOptions.SyncReleaseLabels, "sync-release-labels", false, "sync release labels to the target release")

View File

@ -43,6 +43,7 @@ func NewTemplateCmd(globalCfg *config.GlobalImpl) *cobra.Command {
f.BoolVar(&templateOptions.SkipNeeds, "skip-needs", true, `do not automatically include releases from the target release's "needs" when --selector/-l flag is provided. Does nothing when --selector/-l flag is not provided. Defaults to true when --include-needs or --include-transitive-needs is not provided`)
f.BoolVar(&templateOptions.IncludeNeeds, "include-needs", false, `automatically include releases from the target release's "needs" when --selector/-l flag is provided. Does nothing when --selector/-l flag is not provided`)
f.BoolVar(&templateOptions.IncludeTransitiveNeeds, "include-transitive-needs", false, `like --include-needs, but also includes transitive needs (needs of needs). Does nothing when --selector/-l flag is not provided. Overrides exclusions of other selectors and conditions.`)
f.BoolVar(&templateOptions.EnforceNeedsAreInstalled, "enforce-needs-are-installed", false, "enforce that all 'needs' dependencies are installable before applying changes")
f.BoolVar(&templateOptions.SkipCleanup, "skip-cleanup", false, "Stop cleaning up temporary values generated by helmfile and helm-secrets. Useful for debugging. Don't use in production for security")
f.BoolVar(&templateOptions.NoHooks, "no-hooks", false, "do not template files made by hooks.")
f.StringVar(&templateOptions.PostRenderer, "post-renderer", "", `pass --post-renderer to "helm template" or "helm upgrade --install"`)

View File

@ -2144,13 +2144,19 @@ func (a *App) withNeeds(r *Run, c DAGConfig, includeDisabled bool, f func(*state
includeNeeds = true
}
batches, err := st.PlanReleases(state.PlanOptions{Reverse: false, SelectedReleases: selectedReleases, IncludeNeeds: includeNeeds, IncludeTransitiveNeeds: c.IncludeTransitiveNeeds(), SkipNeeds: c.SkipNeeds()})
batches, err := st.PlanReleases(state.PlanOptions{
Reverse: false,
SelectedReleases: selectedReleases,
IncludeNeeds: includeNeeds,
IncludeTransitiveNeeds: c.IncludeTransitiveNeeds(),
SkipNeeds: c.SkipNeeds(),
})
if err != nil {
return false, []error{err}
}
var selectedReleasesWithNeeds []state.ReleaseSpec
for _, rs := range batches {
for _, r := range rs {
selectedReleasesWithNeeds = append(selectedReleasesWithNeeds, r.ReleaseSpec)
@ -2185,6 +2191,15 @@ func (a *App) withNeeds(r *Run, c DAGConfig, includeDisabled bool, f func(*state
}
}
if c.EnforceNeedsAreInstalled() {
for _, r := range toRender {
visited := make(map[string]bool)
if depErr := st.HasTransitiveDependencyWithInstalledFalse(r, visited); depErr != nil {
return false, []error{fmt.Errorf("Release %s has a transitive dependency %s marked as installed=false", depErr.Release.Name, depErr.Dependency.Name)}
}
}
}
if includeDisabled {
for _, d := range releasesToUninstall {
rels = append(rels, d)

View File

@ -2195,11 +2195,12 @@ type configImpl struct {
skipSchemaValidation bool
skipRefresh bool
skipNeeds bool
includeNeeds bool
includeTransitiveNeeds bool
skipCharts bool
kubeVersion string
skipNeeds bool
includeNeeds bool
includeTransitiveNeeds bool
enforceNeedsAreInstalled bool
skipCharts bool
kubeVersion string
}
func (c configImpl) Selectors() []string {
@ -2306,51 +2307,56 @@ func (c configImpl) ShowOnly() []string {
return nil
}
func (c configImpl) EnforceNeedsAreInstalled() bool {
return c.enforceNeedsAreInstalled
}
type applyConfig struct {
args string
cascade string
values []string
set []string
validate bool
skipCleanup bool
skipCRDs bool
skipDeps bool
skipRefresh bool
skipNeeds bool
includeNeeds bool
includeTransitiveNeeds bool
includeTests bool
suppress []string
suppressSecrets bool
showSecrets bool
noHooks bool
suppressDiff bool
noColor bool
color bool
context int
diffOutput string
concurrency int
detailedExitcode bool
stripTrailingCR bool
interactive bool
skipDiffOnInstall bool
syncArgs string
diffArgs string
logger *zap.SugaredLogger
wait bool
waitRetries int
waitForJobs bool
reuseValues bool
postRenderer string
postRendererArgs []string
skipSchemaValidation bool
kubeVersion string
suppressOutputLineRegex []string
showOnly []string
hideNotes bool
takeOwnership bool
syncReleaseLabels bool
set []string
validate bool
skipCleanup bool
skipCRDs bool
skipDeps bool
skipRefresh bool
skipNeeds bool
includeNeeds bool
includeTransitiveNeeds bool
includeTests bool
suppress []string
suppressSecrets bool
showSecrets bool
noHooks bool
suppressDiff bool
noColor bool
color bool
context int
diffOutput string
concurrency int
detailedExitcode bool
stripTrailingCR bool
interactive bool
skipDiffOnInstall bool
syncArgs string
diffArgs string
logger *zap.SugaredLogger
wait bool
waitRetries int
waitForJobs bool
reuseValues bool
postRenderer string
postRendererArgs []string
skipSchemaValidation bool
kubeVersion string
suppressOutputLineRegex []string
showOnly []string
hideNotes bool
takeOwnership bool
syncReleaseLabels bool
enforceNeedsAreInstalled bool
// template-only options
includeCRDs, skipTests bool
@ -2409,6 +2415,10 @@ func (a applyConfig) SkipNeeds() bool {
return a.skipNeeds
}
func (a applyConfig) EnforceNeedsAreInstalled() bool {
return a.enforceNeedsAreInstalled
}
func (a applyConfig) IncludeNeeds() bool {
return a.includeNeeds || a.IncludeTransitiveNeeds()
}

View File

@ -242,6 +242,7 @@ type DAGConfig interface {
SkipNeeds() bool
IncludeNeeds() bool
IncludeTransitiveNeeds() bool
EnforceNeedsAreInstalled() bool
}
type WriteValuesConfigProvider interface {

View File

@ -16,37 +16,38 @@ import (
)
type diffConfig struct {
args string
diffArgs string
values []string
retainValuesFiles bool
set []string
validate bool
skipCRDs bool
skipDeps bool
skipRefresh bool
includeTests bool
skipNeeds bool
includeNeeds bool
includeTransitiveNeeds bool
suppress []string
suppressSecrets bool
showSecrets bool
noHooks bool
suppressDiff bool
suppressOutputLineRegex []string
noColor bool
context int
diffOutput string
concurrency int
detailedExitcode bool
stripTrailingCR bool
interactive bool
skipDiffOnInstall bool
skipSchemaValidation bool
reuseValues bool
logger *zap.SugaredLogger
takeOwnership bool
args string
diffArgs string
values []string
retainValuesFiles bool
set []string
validate bool
skipCRDs bool
skipDeps bool
skipRefresh bool
includeTests bool
skipNeeds bool
includeNeeds bool
includeTransitiveNeeds bool
suppress []string
suppressSecrets bool
showSecrets bool
noHooks bool
suppressDiff bool
suppressOutputLineRegex []string
noColor bool
context int
diffOutput string
concurrency int
detailedExitcode bool
stripTrailingCR bool
interactive bool
skipDiffOnInstall bool
skipSchemaValidation bool
reuseValues bool
logger *zap.SugaredLogger
takeOwnership bool
enforceNeedsAreInstalled bool
}
func (a diffConfig) Args() string {
@ -188,6 +189,10 @@ func (a diffConfig) TakeOwnership() bool {
return a.takeOwnership
}
func (a diffConfig) EnforceNeedsAreInstalled() bool {
return a.enforceNeedsAreInstalled
}
func TestDiff(t *testing.T) {
type flags struct {
skipNeeds bool

View File

@ -28,6 +28,8 @@ type ApplyOptions struct {
IncludeNeeds bool
// IncludeTransitiveNeeds is true if the transitive needs should be included
IncludeTransitiveNeeds bool
// EnforceNeedsAreInstalled is true if we should error if/when there are unmeetable dependencies
EnforceNeedsAreInstalled bool
// SkipDiffOnInstall is true if the diff should be skipped on install
SkipDiffOnInstall bool
// DiffArgs is the list of arguments to pass to the helm-diff.
@ -139,6 +141,11 @@ func (a *ApplyImpl) IncludeTransitiveNeeds() bool {
return a.ApplyOptions.IncludeTransitiveNeeds
}
// EnforceNeedsAreInstalled is true we should error if/when there are unmeetable dependencies
func (a *ApplyImpl) EnforceNeedsAreInstalled() bool {
return a.ApplyOptions.EnforceNeedsAreInstalled
}
// ShowSecrets returns the show secrets.
func (a *ApplyImpl) ShowSecrets() bool {
return a.ApplyOptions.ShowSecrets

View File

@ -18,6 +18,8 @@ type DiffOptions struct {
IncludeNeeds bool
// IncludeTransitiveNeeds is the include transitive needs flag
IncludeTransitiveNeeds bool
// EnforceNeedsAreInstalled is the enforce needs are installed flag
EnforceNeedsAreInstalled bool
// SkipDiffOnInstall is the skip diff on install flag
SkipDiffOnInstall bool
// ShowSecrets is the show secrets flag
@ -87,6 +89,11 @@ func (t *DiffImpl) IncludeTransitiveNeeds() bool {
return t.DiffOptions.IncludeTransitiveNeeds
}
// EnforceNeedsAreInstalled errors if the transitive dependencies are not installable
func (t *DiffImpl) EnforceNeedsAreInstalled() bool {
return t.DiffOptions.EnforceNeedsAreInstalled
}
// Set returns the Set
func (t *DiffImpl) Set() []string {
return t.DiffOptions.Set

View File

@ -14,6 +14,8 @@ type LintOptions struct {
IncludeNeeds bool
// IncludeTransitiveNeeds is the include transitive needs flag
IncludeTransitiveNeeds bool
// EnforceNeedsAreInstalled is the enforce needs are installed flag
EnforceNeedsAreInstalled bool
// SkipDeps is the skip deps flag
}
@ -66,6 +68,11 @@ func (l *LintImpl) IncludeTransitiveNeeds() bool {
return l.LintOptions.IncludeTransitiveNeeds
}
// EnforceNeedsAreInstalled errors if the transitive dependencies are not installable
func (l *LintImpl) EnforceNeedsAreInstalled() bool {
return l.LintOptions.EnforceNeedsAreInstalled
}
// SkipNeeds returns the skip needs
func (l *LintImpl) SkipNeeds() bool {
if !l.IncludeNeeds() {

View File

@ -16,6 +16,8 @@ type SyncOptions struct {
IncludeNeeds bool
// IncludeTransitiveNeeds is the include transitive needs flag
IncludeTransitiveNeeds bool
// EnforceNeedsAreInstalled indicates whether to error when there are unmeetable dependencies
EnforceNeedsAreInstalled bool
// SkipCrds is the skip crds flag
SkipCRDs bool
// Wait is the wait flag
@ -82,6 +84,11 @@ func (t *SyncImpl) IncludeTransitiveNeeds() bool {
return t.SyncOptions.IncludeTransitiveNeeds
}
// EnforceNeedsAreInstalled errors if the transitive dependencies are not installable
func (t *SyncImpl) EnforceNeedsAreInstalled() bool {
return t.SyncOptions.EnforceNeedsAreInstalled
}
// Set returns the Set
func (t *SyncImpl) Set() []string {
return t.SyncOptions.Set

View File

@ -30,6 +30,8 @@ type TemplateOptions struct {
IncludeNeeds bool
// IncludeTransitiveNeeds is the include transitive needs flag
IncludeTransitiveNeeds bool
// EnforceNeedsAreInstalled indicates whether to error when there are unmeetable dependencies
EnforceNeedsAreInstalled bool
// No-Hooks is the no hooks flag
NoHooks bool
// SkipCleanup is the skip cleanup flag
@ -158,3 +160,8 @@ func (t *TemplateImpl) KubeVersion() string {
func (t *TemplateImpl) ShowOnly() []string {
return t.TemplateOptions.ShowOnly
}
// EnforceNeedsAreInstalled errors if the transitive dependencies are not installable
func (t *TemplateImpl) EnforceNeedsAreInstalled() bool {
return t.TemplateOptions.EnforceNeedsAreInstalled
}

View File

@ -2738,6 +2738,34 @@ func (st *HelmState) UpdateDeps(helm helmexec.Interface, includeTransitiveNeeds
return nil
}
// DependencyError holds information about a release and its dependency that is not installed.
type DependencyError struct {
Release ReleaseSpec
Dependency ReleaseSpec
}
// hasTransitiveDependencyWithInstallFalse checks if a release has a transitive dependency with Install set to false.
func (st *HelmState) HasTransitiveDependencyWithInstalledFalse(release ReleaseSpec, visited map[string]bool) *DependencyError {
if visited[release.Name] {
return nil
}
visited[release.Name] = true
for _, dep := range release.Needs {
for _, r := range st.Releases {
if r.Name == dep {
if r.Installed != nil && !*r.Installed {
return &DependencyError{Release: release, Dependency: r}
}
if depErr := st.HasTransitiveDependencyWithInstalledFalse(r, visited); depErr != nil {
return depErr
}
}
}
}
return nil
}
// find "Chart.yaml"
func findChartDirectory(topLevelDir string) (string, error) {
var files []string