fix: update dependencies whose installed version does not satisfy the Chart.yaml constraint

`CheckDependencies` matched dependencies only by name, so a chart that had
previously downloaded a dependency (charts/<name>) was considered satisfied
even when its installed version no longer satisfied the version constraint in
Chart.yaml. With --dependency-update, `helm install` / `helm template` therefore
silently kept the outdated dependency instead of updating it.

Compare the installed chart version against the requested constraint using
IsCompatibleRange, and treat the dependency as present only when the name
matches and either no constraint is declared or the installed version satisfies
it. Expose Version() on the chart/dependency accessors to make this possible.

Fixes #31455

Signed-off-by: waterWang <672684719@qq.com>
pull/32588/head
waterWang 2 weeks ago
parent d62bee21c2
commit f7f1c74dc0

@ -844,7 +844,7 @@ OUTER:
if err != nil { if err != nil {
return err return err
} }
if dac.Name() == rac.Name() { if dac.Name() == rac.Name() && (rac.Version() == "" || chartutil.IsCompatibleRange(rac.Version(), dac.Version())) {
continue OUTER continue OUTER
} }
} }

@ -1196,6 +1196,27 @@ func TestCheckDependencies_MissingDependency(t *testing.T) {
assert.ErrorContains(t, CheckDependencies(mockChart, []ci.Dependency{&dependency}), "missing in charts") assert.ErrorContains(t, CheckDependencies(mockChart, []ci.Dependency{&dependency}), "missing in charts")
} }
func TestCheckDependencies_MatchingVersion(t *testing.T) {
dependency := chart.Dependency{Name: "hello", Version: "0.1.0"}
mockChart := buildChart(withDependency())
assert.NoError(t, CheckDependencies(mockChart, []ci.Dependency{&dependency}))
}
func TestCheckDependencies_MismatchedVersion(t *testing.T) {
mismatched := chart.Dependency{Name: "hello", Version: "0.2.0"}
mockChart := buildChart(withDependency())
assert.ErrorContains(t, CheckDependencies(mockChart, []ci.Dependency{&mismatched}), "missing in charts")
}
func TestCheckDependencies_WithoutVersionConstraint(t *testing.T) {
dependency := chart.Dependency{Name: "hello"}
mockChart := buildChart(withDependency())
assert.NoError(t, CheckDependencies(mockChart, []ci.Dependency{&dependency}))
}
func TestInstallCRDs_CheckNilErrors(t *testing.T) { func TestInstallCRDs_CheckNilErrors(t *testing.T) {
tests := []struct { tests := []struct {
name string name string

@ -51,6 +51,10 @@ func (r *v2Accessor) Name() string {
return r.chrt.Metadata.Name return r.chrt.Metadata.Name
} }
func (r *v2Accessor) Version() string {
return r.chrt.Metadata.Version
}
func (r *v2Accessor) IsRoot() bool { func (r *v2Accessor) IsRoot() bool {
return r.chrt.IsRoot() return r.chrt.IsRoot()
} }
@ -120,6 +124,10 @@ func (r *v3Accessor) Name() string {
return r.chrt.Metadata.Name return r.chrt.Metadata.Name
} }
func (r *v3Accessor) Version() string {
return r.chrt.Metadata.Version
}
func (r *v3Accessor) IsRoot() bool { func (r *v3Accessor) IsRoot() bool {
return r.chrt.IsRoot() return r.chrt.IsRoot()
} }

@ -47,6 +47,10 @@ func (r *v2DependencyAccessor) Name() string {
return r.dep.Name return r.dep.Name
} }
func (r *v2DependencyAccessor) Version() string {
return r.dep.Version
}
func (r *v2DependencyAccessor) Alias() string { func (r *v2DependencyAccessor) Alias() string {
return r.dep.Alias return r.dep.Alias
} }
@ -59,6 +63,10 @@ func (r *v3DependencyAccessor) Name() string {
return r.dep.Name return r.dep.Name
} }
func (r *v3DependencyAccessor) Version() string {
return r.dep.Version
}
func (r *v3DependencyAccessor) Alias() string { func (r *v3DependencyAccessor) Alias() string {
return r.dep.Alias return r.dep.Alias
} }

@ -25,6 +25,7 @@ type Dependency any
type Accessor interface { type Accessor interface {
Name() string Name() string
Version() string
IsRoot() bool IsRoot() bool
MetadataAsMap() map[string]any MetadataAsMap() map[string]any
Files() []*common.File Files() []*common.File
@ -40,5 +41,6 @@ type Accessor interface {
type DependencyAccessor interface { type DependencyAccessor interface {
Name() string Name() string
Version() string
Alias() string Alias() string
} }

Loading…
Cancel
Save