Add support for transitive dependencies. (#1983)

Co-authored-by: Peter Aichinger <petera@topdesk.com>
This commit is contained in:
pjotre86
2021-10-20 17:55:08 +09:00
committed by GitHub
co-authored by Peter Aichinger
parent 9a0ce53608
commit 77e6268bcb
14 changed files with 583 additions and 177 deletions
+72 -2
View File
@@ -1,8 +1,9 @@
package state
import (
"github.com/google/go-cmp/cmp"
"testing"
"github.com/google/go-cmp/cmp"
)
func TestSelectReleasesWithOverrides(t *testing.T) {
@@ -79,7 +80,76 @@ func TestSelectReleasesWithOverrides(t *testing.T) {
for _, tc := range testcases {
state.Selectors = tc.selector
rs, err := state.GetSelectedReleasesWithOverrides()
rs, err := state.GetSelectedReleasesWithOverrides(false)
if err != nil {
t.Fatalf("%s %s: %v", tc.selector, tc.subject, err)
}
var got []string
for _, r := range rs {
got = append(got, r.Name)
}
if d := cmp.Diff(tc.want, got); d != "" {
t.Errorf("%s %s: %s", tc.selector, tc.subject, d)
}
}
}
func TestSelectReleasesWithOverridesWithIncludedTransitives(t *testing.T) {
type testcase struct {
subject string
selector []string
want []string
includeTransitiveNeeds bool
}
testcases := []testcase{
{
subject: "include transitives",
selector: []string{"name=serviceA"},
want: []string{"serviceA"},
includeTransitiveNeeds: false,
},
{
subject: "include transitives",
selector: []string{"name=serviceA"},
want: []string{"serviceA", "serviceB", "serviceC"},
includeTransitiveNeeds: true,
},
}
example := []byte(`releases:
- name: serviceA
namespace: default
chart: stable/testchart
needs:
- serviceB
- name: serviceB
namespace: default
chart: stable/testchart
needs:
- serviceC
- name: serviceC
namespace: default
chart: stable/testchart
- name: serviceD
namespace: default
chart: stable/testchart
`)
state := stateTestEnv{
Files: map[string]string{
"/helmfile.yaml": string(example),
},
WorkDir: "/",
}.MustLoadState(t, "/helmfile.yaml", "default")
for _, tc := range testcases {
state.Selectors = tc.selector
rs, err := state.GetSelectedReleasesWithOverrides(tc.includeTransitiveNeeds)
if err != nil {
t.Fatalf("%s %s: %v", tc.selector, tc.subject, err)
}
+57 -46
View File
@@ -398,36 +398,6 @@ type RepoUpdater interface {
RegistryLogin(name string, username string, password string) error
}
// getRepositoriesToSync returns the names of repositories to be updated
func (st *HelmState) getRepositoriesToSync() (map[string]bool, error) {
releases, err := st.GetSelectedReleasesWithOverrides()
if err != nil {
return nil, err
}
repositoriesToUpdate := map[string]bool{}
if len(releases) == 0 {
for _, repo := range st.Repositories {
repositoriesToUpdate[repo.Name] = true
}
return repositoriesToUpdate, nil
}
for _, release := range releases {
if release.Installed == nil || *release.Installed {
chart := strings.Split(release.Chart, "/")
if len(chart) == 1 {
continue
}
repositoriesToUpdate[chart[0]] = true
}
}
return repositoriesToUpdate, nil
}
func (st *HelmState) SyncRepos(helm RepoUpdater, shouldSkip map[string]bool) ([]string, error) {
var updated []string
@@ -968,11 +938,12 @@ type ChartPrepareOptions struct {
SkipCleanup bool
// Validate is a helm-3-only option. When it is set to true, it configures chartify to pass --validate to helm-template run by it.
// It's required when one of your chart relies on Capabilities.APIVersions in a template
Validate bool
IncludeCRDs *bool
Wait bool
WaitForJobs bool
OutputDir string
Validate bool
IncludeCRDs *bool
Wait bool
WaitForJobs bool
OutputDir string
IncludeTransitiveNeeds bool
}
type chartPrepareResult struct {
@@ -1026,7 +997,7 @@ func (st *HelmState) PrepareCharts(helm helmexec.Interface, dir string, concurre
// This and releasesNeedCharts ensures that we run operations like helm-dep-build and prepare-hook calls only on
// releases that are (1) selected by the selectors and (2) to be installed.
selected, err = st.GetSelectedReleasesWithOverrides()
selected, err = st.GetSelectedReleasesWithOverrides(opts.IncludeTransitiveNeeds)
if err != nil {
return nil, []error{err}
}
@@ -2047,16 +2018,16 @@ func (st *HelmState) GetReleasesWithOverrides() []ReleaseSpec {
return rs
}
func (st *HelmState) SelectReleasesWithOverrides() ([]Release, error) {
func (st *HelmState) SelectReleasesWithOverrides(includeTransitiveNeeds bool) ([]Release, error) {
values := st.Values()
rs, err := markExcludedReleases(st.GetReleasesWithOverrides(), st.Selectors, st.CommonLabels, values)
rs, err := markExcludedReleases(st.GetReleasesWithOverrides(), st.Selectors, st.CommonLabels, values, includeTransitiveNeeds)
if err != nil {
return nil, err
}
return rs, nil
}
func markExcludedReleases(releases []ReleaseSpec, selectors []string, commonLabels map[string]string, values map[string]interface{}) ([]Release, error) {
func markExcludedReleases(releases []ReleaseSpec, selectors []string, commonLabels map[string]string, values map[string]interface{}, includeTransitiveNeeds bool) ([]Release, error) {
var filteredReleases []Release
filters := []ReleaseFilter{}
for _, label := range selectors {
@@ -2113,12 +2084,52 @@ func markExcludedReleases(releases []ReleaseSpec, selectors []string, commonLabe
}
filteredReleases = append(filteredReleases, res)
}
if includeTransitiveNeeds {
unmarkNeedsAndTransitives(filteredReleases, releases)
}
return filteredReleases, nil
}
func (st *HelmState) GetSelectedReleasesWithOverrides() ([]ReleaseSpec, error) {
filteredReleases, err := st.SelectReleasesWithOverrides()
func unmarkNeedsAndTransitives(filteredReleases []Release, allReleases []ReleaseSpec) {
needsWithTranstives := collectAllNeedsWithTransitives(filteredReleases, allReleases)
unmarkReleases(needsWithTranstives, filteredReleases)
}
func collectAllNeedsWithTransitives(filteredReleases []Release, allReleases []ReleaseSpec) map[string]struct{} {
needsWithTranstives := map[string]struct{}{}
for _, r := range filteredReleases {
if !r.Filtered {
collectNeedsWithTransitives(r.ReleaseSpec, allReleases, needsWithTranstives)
}
}
return needsWithTranstives
}
func unmarkReleases(toUnmark map[string]struct{}, releases []Release) {
for i, r := range releases {
if _, ok := toUnmark[ReleaseToID(&r.ReleaseSpec)]; ok {
releases[i].Filtered = false
}
}
}
func collectNeedsWithTransitives(release ReleaseSpec, allReleases []ReleaseSpec, needsWithTranstives map[string]struct{}) {
for _, id := range release.Needs {
if _, exists := needsWithTranstives[id]; !exists {
needsWithTranstives[id] = struct{}{}
releaseParts := strings.Split(id, "/")
releaseName := releaseParts[len(releaseParts)-1]
for _, r := range allReleases {
if r.Name == releaseName {
collectNeedsWithTransitives(r, allReleases, needsWithTranstives)
}
}
}
}
}
func (st *HelmState) GetSelectedReleasesWithOverrides(includeTransitiveNeeds bool) ([]ReleaseSpec, error) {
filteredReleases, err := st.SelectReleasesWithOverrides(includeTransitiveNeeds)
if err != nil {
return nil, err
}
@@ -2133,8 +2144,8 @@ func (st *HelmState) GetSelectedReleasesWithOverrides() ([]ReleaseSpec, error) {
}
// FilterReleases allows for the execution of helm commands against a subset of the releases in the helmfile.
func (st *HelmState) FilterReleases() error {
releases, err := st.GetSelectedReleasesWithOverrides()
func (st *HelmState) FilterReleases(includeTransitiveNeeds bool) error {
releases, err := st.GetSelectedReleasesWithOverrides(includeTransitiveNeeds)
if err != nil {
return err
}
@@ -2209,7 +2220,7 @@ func (st *HelmState) ResolveDeps() (*HelmState, error) {
}
// UpdateDeps wrapper for updating dependencies on the releases
func (st *HelmState) UpdateDeps(helm helmexec.Interface) []error {
func (st *HelmState) UpdateDeps(helm helmexec.Interface, includeTransitiveNeeds bool) []error {
var selected []ReleaseSpec
if len(st.Selectors) > 0 {
@@ -2217,7 +2228,7 @@ func (st *HelmState) UpdateDeps(helm helmexec.Interface) []error {
// This and releasesNeedCharts ensures that we run operations like helm-dep-build and prepare-hook calls only on
// releases that are (1) selected by the selectors and (2) to be installed.
selected, err = st.GetSelectedReleasesWithOverrides()
selected, err = st.GetSelectedReleasesWithOverrides(includeTransitiveNeeds)
if err != nil {
return []error{err}
}
+6 -5
View File
@@ -100,14 +100,15 @@ func (st *HelmState) iterateOnReleases(helm helmexec.Interface, concurrency int,
}
type PlanOptions struct {
Reverse bool
IncludeNeeds bool
SkipNeeds bool
SelectedReleases []ReleaseSpec
Reverse bool
IncludeNeeds bool
IncludeTransitiveNeeds bool
SkipNeeds bool
SelectedReleases []ReleaseSpec
}
func (st *HelmState) PlanReleases(opts PlanOptions) ([][]Release, error) {
marked, err := st.SelectReleasesWithOverrides()
marked, err := st.SelectReleasesWithOverrides(opts.IncludeTransitiveNeeds)
if err != nil {
return nil, err
}
+2 -2
View File
@@ -1848,7 +1848,7 @@ generated: 2019-05-16T15:42:45.50486+09:00
})
fs.Cwd = basePath
state = injectFs(state, fs)
errs := state.UpdateDeps(helm)
errs := state.UpdateDeps(helm, false)
want := []string{"/example", "./example", generatedDir}
if !reflect.DeepEqual(helm.Charts, want) {
@@ -2141,7 +2141,7 @@ func TestHelmState_NoReleaseMatched(t *testing.T) {
RenderedValues: map[string]interface{}{},
}
state.Selectors = []string{tt.labels}
errs := state.FilterReleases()
errs := state.FilterReleases(false)
if (errs != nil) != tt.wantErr {
t.Errorf("ReleaseStatuses() for %s error = %v, wantErr %v", tt.name, errs, tt.wantErr)
return