feat: least frequent repository update (#356)
Prevents helmfile from consuming unnecessarily much time in running `helm repo update` over and over. helmfile now marks which repository was updated, and skip second and further `helm repo update` when all the `repositories` found in a helmfile.yaml was marked as already updated. Let's say you had two helmfiles, the first one with repositories `foo` and `bar`, an the second one with only `bar`. `helmfile repos` will run `helm update repo` for the first helmfile, marking `foo` and `bar` as already updated. The second helmfile.yaml contains only `bar`, which is marked as already updated. So helmfile won't run `helm repo update` for the second. This applies to all the helmfile command that results in `helm repo update`, like `repos`, `sync`, `diff` and so on. Resolves #335
This commit is contained in:
		
							parent
							
								
									f2b610afdf
								
							
						
					
					
						commit
						b94265122f
					
				
							
								
								
									
										91
									
								
								main.go
								
								
								
								
							
							
						
						
									
										91
									
								
								main.go
								
								
								
								
							|  | @ -114,13 +114,13 @@ func main() { | ||||||
| 				}, | 				}, | ||||||
| 			}, | 			}, | ||||||
| 			Action: func(c *cli.Context) error { | 			Action: func(c *cli.Context) error { | ||||||
| 				return visitAllDesiredStates(c, func(state *state.HelmState, helm helmexec.Interface) (bool, []error) { | 				return visitAllDesiredStates(c, func(state *state.HelmState, helm helmexec.Interface, ctx context) (bool, []error) { | ||||||
| 					args := args.GetArgs(c.String("args"), state) | 					args := args.GetArgs(c.String("args"), state) | ||||||
| 					if len(args) > 0 { | 					if len(args) > 0 { | ||||||
| 						helm.SetExtraArgs(args...) | 						helm.SetExtraArgs(args...) | ||||||
| 					} | 					} | ||||||
| 
 | 
 | ||||||
| 					errs := state.SyncRepos(helm) | 					errs := ctx.SyncReposOnce(state, helm) | ||||||
| 
 | 
 | ||||||
| 					ok := len(state.Repositories) > 0 && len(errs) == 0 | 					ok := len(state.Repositories) > 0 && len(errs) == 0 | ||||||
| 
 | 
 | ||||||
|  | @ -148,7 +148,7 @@ func main() { | ||||||
| 				}, | 				}, | ||||||
| 			}, | 			}, | ||||||
| 			Action: func(c *cli.Context) error { | 			Action: func(c *cli.Context) error { | ||||||
| 				return findAndIterateOverDesiredStatesUsingFlags(c, func(state *state.HelmState, helm helmexec.Interface) []error { | 				return findAndIterateOverDesiredStatesUsingFlags(c, func(state *state.HelmState, helm helmexec.Interface, _ context) []error { | ||||||
| 					return executeSyncCommand(c, state, helm) | 					return executeSyncCommand(c, state, helm) | ||||||
| 				}) | 				}) | ||||||
| 			}, | 			}, | ||||||
|  | @ -185,10 +185,12 @@ func main() { | ||||||
| 				}, | 				}, | ||||||
| 			}, | 			}, | ||||||
| 			Action: func(c *cli.Context) error { | 			Action: func(c *cli.Context) error { | ||||||
| 				return findAndIterateOverDesiredStatesUsingFlags(c, func(state *state.HelmState, helm helmexec.Interface) []error { | 				return findAndIterateOverDesiredStatesUsingFlags(c, func(state *state.HelmState, helm helmexec.Interface, ctx context) []error { | ||||||
| 					if errs := state.PrepareRelease(helm, "diff"); errs != nil && len(errs) > 0 { | 					if c.Bool("sync-repos") { | ||||||
|  | 						if errs := ctx.SyncReposOnce(state, helm); errs != nil && len(errs) > 0 { | ||||||
| 							return errs | 							return errs | ||||||
| 						} | 						} | ||||||
|  | 					} | ||||||
| 
 | 
 | ||||||
| 					_, errs := executeDiffCommand(c, state, helm, c.Bool("detailed-exitcode"), c.Bool("suppress-secrets")) | 					_, errs := executeDiffCommand(c, state, helm, c.Bool("detailed-exitcode"), c.Bool("suppress-secrets")) | ||||||
| 					return errs | 					return errs | ||||||
|  | @ -215,10 +217,17 @@ func main() { | ||||||
| 				}, | 				}, | ||||||
| 			}, | 			}, | ||||||
| 			Action: func(c *cli.Context) error { | 			Action: func(c *cli.Context) error { | ||||||
| 				return findAndIterateOverDesiredStatesUsingFlags(c, func(state *state.HelmState, helm helmexec.Interface) []error { | 				return findAndIterateOverDesiredStatesUsingFlags(c, func(state *state.HelmState, helm helmexec.Interface, ctx context) []error { | ||||||
| 					if errs := state.PrepareRelease(helm, "template"); errs != nil && len(errs) > 0 { | 					if errs := state.PrepareRelease(helm, "template"); errs != nil && len(errs) > 0 { | ||||||
| 						return errs | 						return errs | ||||||
| 					} | 					} | ||||||
|  | 					if errs := ctx.SyncReposOnce(state, helm); errs != nil && len(errs) > 0 { | ||||||
|  | 						return errs | ||||||
|  | 					} | ||||||
|  | 
 | ||||||
|  | 					if errs := state.UpdateDeps(helm); errs != nil && len(errs) > 0 { | ||||||
|  | 						return errs | ||||||
|  | 					} | ||||||
| 
 | 
 | ||||||
| 					return executeTemplateCommand(c, state, helm) | 					return executeTemplateCommand(c, state, helm) | ||||||
| 				}) | 				}) | ||||||
|  | @ -244,11 +253,11 @@ func main() { | ||||||
| 				}, | 				}, | ||||||
| 			}, | 			}, | ||||||
| 			Action: func(c *cli.Context) error { | 			Action: func(c *cli.Context) error { | ||||||
| 				return findAndIterateOverDesiredStatesUsingFlags(c, func(state *state.HelmState, helm helmexec.Interface) []error { | 				return findAndIterateOverDesiredStatesUsingFlags(c, func(state *state.HelmState, helm helmexec.Interface, ctx context) []error { | ||||||
| 					values := c.StringSlice("values") | 					values := c.StringSlice("values") | ||||||
| 					args := args.GetArgs(c.String("args"), state) | 					args := args.GetArgs(c.String("args"), state) | ||||||
| 					workers := c.Int("concurrency") | 					workers := c.Int("concurrency") | ||||||
| 					if errs := state.SyncRepos(helm); errs != nil && len(errs) > 0 { | 					if errs := ctx.SyncReposOnce(state, helm); errs != nil && len(errs) > 0 { | ||||||
| 						return errs | 						return errs | ||||||
| 					} | 					} | ||||||
| 					if errs := state.PrepareRelease(helm, "lint"); errs != nil && len(errs) > 0 { | 					if errs := state.PrepareRelease(helm, "lint"); errs != nil && len(errs) > 0 { | ||||||
|  | @ -278,8 +287,8 @@ func main() { | ||||||
| 				}, | 				}, | ||||||
| 			}, | 			}, | ||||||
| 			Action: func(c *cli.Context) error { | 			Action: func(c *cli.Context) error { | ||||||
| 				return findAndIterateOverDesiredStatesUsingFlags(c, func(state *state.HelmState, helm helmexec.Interface) []error { | 				return findAndIterateOverDesiredStatesUsingFlags(c, func(state *state.HelmState, helm helmexec.Interface, ctx context) []error { | ||||||
| 					if errs := state.SyncRepos(helm); errs != nil && len(errs) > 0 { | 					if errs := ctx.SyncReposOnce(state, helm); errs != nil && len(errs) > 0 { | ||||||
| 						return errs | 						return errs | ||||||
| 					} | 					} | ||||||
| 					if errs := state.PrepareRelease(helm, "sync"); errs != nil && len(errs) > 0 { | 					if errs := state.PrepareRelease(helm, "sync"); errs != nil && len(errs) > 0 { | ||||||
|  | @ -324,9 +333,9 @@ func main() { | ||||||
| 				}, | 				}, | ||||||
| 			}, | 			}, | ||||||
| 			Action: func(c *cli.Context) error { | 			Action: func(c *cli.Context) error { | ||||||
| 				return findAndIterateOverDesiredStatesUsingFlags(c, func(st *state.HelmState, helm helmexec.Interface) []error { | 				return findAndIterateOverDesiredStatesUsingFlags(c, func(st *state.HelmState, helm helmexec.Interface, ctx context) []error { | ||||||
| 					if !c.Bool("skip-repo-update") { | 					if !c.Bool("skip-repo-update") { | ||||||
| 						if errs := st.SyncRepos(helm); errs != nil && len(errs) > 0 { | 						if errs := ctx.SyncReposOnce(st, helm); errs != nil && len(errs) > 0 { | ||||||
| 							return errs | 							return errs | ||||||
| 						} | 						} | ||||||
| 					} | 					} | ||||||
|  | @ -413,7 +422,7 @@ Do you really want to apply? | ||||||
| 				}, | 				}, | ||||||
| 			}, | 			}, | ||||||
| 			Action: func(c *cli.Context) error { | 			Action: func(c *cli.Context) error { | ||||||
| 				return findAndIterateOverDesiredStatesUsingFlags(c, func(state *state.HelmState, helm helmexec.Interface) []error { | 				return findAndIterateOverDesiredStatesUsingFlags(c, func(state *state.HelmState, helm helmexec.Interface, _ context) []error { | ||||||
| 					workers := c.Int("concurrency") | 					workers := c.Int("concurrency") | ||||||
| 
 | 
 | ||||||
| 					args := args.GetArgs(c.String("args"), state) | 					args := args.GetArgs(c.String("args"), state) | ||||||
|  | @ -444,7 +453,7 @@ Do you really want to apply? | ||||||
| 				}, | 				}, | ||||||
| 			}, | 			}, | ||||||
| 			Action: func(c *cli.Context) error { | 			Action: func(c *cli.Context) error { | ||||||
| 				return findAndIterateOverDesiredStatesUsingFlagsWithReverse(c, true, func(state *state.HelmState, helm helmexec.Interface) []error { | 				return findAndIterateOverDesiredStatesUsingFlagsWithReverse(c, true, func(state *state.HelmState, helm helmexec.Interface, _ context) []error { | ||||||
| 					purge := c.Bool("purge") | 					purge := c.Bool("purge") | ||||||
| 
 | 
 | ||||||
| 					args := args.GetArgs(c.String("args"), state) | 					args := args.GetArgs(c.String("args"), state) | ||||||
|  | @ -492,7 +501,7 @@ Do you really want to delete? | ||||||
| 				}, | 				}, | ||||||
| 			}, | 			}, | ||||||
| 			Action: func(c *cli.Context) error { | 			Action: func(c *cli.Context) error { | ||||||
| 				return findAndIterateOverDesiredStatesUsingFlags(c, func(state *state.HelmState, helm helmexec.Interface) []error { | 				return findAndIterateOverDesiredStatesUsingFlags(c, func(state *state.HelmState, helm helmexec.Interface, _ context) []error { | ||||||
| 					cleanup := c.Bool("cleanup") | 					cleanup := c.Bool("cleanup") | ||||||
| 					timeout := c.Int("timeout") | 					timeout := c.Int("timeout") | ||||||
| 
 | 
 | ||||||
|  | @ -527,14 +536,6 @@ func executeSyncCommand(c *cli.Context, state *state.HelmState, helm helmexec.In | ||||||
| } | } | ||||||
| 
 | 
 | ||||||
| func executeTemplateCommand(c *cli.Context, state *state.HelmState, helm helmexec.Interface) []error { | func executeTemplateCommand(c *cli.Context, state *state.HelmState, helm helmexec.Interface) []error { | ||||||
| 	if errs := state.SyncRepos(helm); errs != nil && len(errs) > 0 { |  | ||||||
| 		return errs |  | ||||||
| 	} |  | ||||||
| 
 |  | ||||||
| 	if errs := state.UpdateDeps(helm); errs != nil && len(errs) > 0 { |  | ||||||
| 		return errs |  | ||||||
| 	} |  | ||||||
| 
 |  | ||||||
| 	args := args.GetArgs(c.String("args"), state) | 	args := args.GetArgs(c.String("args"), state) | ||||||
| 	values := c.StringSlice("values") | 	values := c.StringSlice("values") | ||||||
| 	workers := c.Int("concurrency") | 	workers := c.Int("concurrency") | ||||||
|  | @ -548,12 +549,6 @@ func executeDiffCommand(c *cli.Context, st *state.HelmState, helm helmexec.Inter | ||||||
| 		helm.SetExtraArgs(args...) | 		helm.SetExtraArgs(args...) | ||||||
| 	} | 	} | ||||||
| 
 | 
 | ||||||
| 	if c.Bool("sync-repos") { |  | ||||||
| 		if errs := st.SyncRepos(helm); errs != nil && len(errs) > 0 { |  | ||||||
| 			return []*state.ReleaseSpec{}, errs |  | ||||||
| 		} |  | ||||||
| 	} |  | ||||||
| 
 |  | ||||||
| 	values := c.StringSlice("values") | 	values := c.StringSlice("values") | ||||||
| 	workers := c.Int("concurrency") | 	workers := c.Int("concurrency") | ||||||
| 
 | 
 | ||||||
|  | @ -574,7 +569,7 @@ type app struct { | ||||||
| 	selectors         []string | 	selectors         []string | ||||||
| } | } | ||||||
| 
 | 
 | ||||||
| func findAndIterateOverDesiredStatesUsingFlags(c *cli.Context, converge func(*state.HelmState, helmexec.Interface) []error) error { | func findAndIterateOverDesiredStatesUsingFlags(c *cli.Context, converge func(*state.HelmState, helmexec.Interface, context) []error) error { | ||||||
| 	return findAndIterateOverDesiredStatesUsingFlagsWithReverse(c, false, converge) | 	return findAndIterateOverDesiredStatesUsingFlagsWithReverse(c, false, converge) | ||||||
| } | } | ||||||
| 
 | 
 | ||||||
|  | @ -612,17 +607,43 @@ func initAppEntry(c *cli.Context, reverse bool) (*app, string, error) { | ||||||
| 	return app, fileOrDir, nil | 	return app, fileOrDir, nil | ||||||
| } | } | ||||||
| 
 | 
 | ||||||
| func visitAllDesiredStates(c *cli.Context, converge func(*state.HelmState, helmexec.Interface) (bool, []error)) error { | type context struct { | ||||||
|  | 	updatedRepos map[string]struct{} | ||||||
|  | } | ||||||
|  | 
 | ||||||
|  | func (ctx context) SyncReposOnce(st *state.HelmState, helm state.RepoUpdater) []error { | ||||||
|  | 	var errs []error | ||||||
|  | 
 | ||||||
|  | 	allUpdated := true | ||||||
|  | 	for _, r := range st.Repositories { | ||||||
|  | 		_, exists := ctx.updatedRepos[r.Name] | ||||||
|  | 		allUpdated = allUpdated && exists | ||||||
|  | 	} | ||||||
|  | 
 | ||||||
|  | 	if !allUpdated { | ||||||
|  | 		errs = st.SyncRepos(helm) | ||||||
|  | 
 | ||||||
|  | 		for _, r := range st.Repositories { | ||||||
|  | 			ctx.updatedRepos[r.Name] = struct{}{} | ||||||
|  | 		} | ||||||
|  | 	} | ||||||
|  | 
 | ||||||
|  | 	return errs | ||||||
|  | } | ||||||
|  | 
 | ||||||
|  | func visitAllDesiredStates(c *cli.Context, converge func(*state.HelmState, helmexec.Interface, context) (bool, []error)) error { | ||||||
| 	app, fileOrDir, err := initAppEntry(c, false) | 	app, fileOrDir, err := initAppEntry(c, false) | ||||||
| 	if err != nil { | 	if err != nil { | ||||||
| 		return err | 		return err | ||||||
| 	} | 	} | ||||||
| 
 | 
 | ||||||
|  | 	ctx := context{} | ||||||
|  | 
 | ||||||
| 	convergeWithHelmBinary := func(st *state.HelmState, helm helmexec.Interface) (bool, []error) { | 	convergeWithHelmBinary := func(st *state.HelmState, helm helmexec.Interface) (bool, []error) { | ||||||
| 		if c.GlobalString("helm-binary") != "" { | 		if c.GlobalString("helm-binary") != "" { | ||||||
| 			helm.SetHelmBinary(c.GlobalString("helm-binary")) | 			helm.SetHelmBinary(c.GlobalString("helm-binary")) | ||||||
| 		} | 		} | ||||||
| 		return converge(st, helm) | 		return converge(st, helm, ctx) | ||||||
| 	} | 	} | ||||||
| 
 | 
 | ||||||
| 	err = app.VisitDesiredStates(fileOrDir, convergeWithHelmBinary) | 	err = app.VisitDesiredStates(fileOrDir, convergeWithHelmBinary) | ||||||
|  | @ -648,17 +669,19 @@ func toCliError(err error) error { | ||||||
| 	return err | 	return err | ||||||
| } | } | ||||||
| 
 | 
 | ||||||
| func findAndIterateOverDesiredStatesUsingFlagsWithReverse(c *cli.Context, reverse bool, converge func(*state.HelmState, helmexec.Interface) []error) error { | func findAndIterateOverDesiredStatesUsingFlagsWithReverse(c *cli.Context, reverse bool, converge func(*state.HelmState, helmexec.Interface, context) []error) error { | ||||||
| 	app, fileOrDir, err := initAppEntry(c, reverse) | 	app, fileOrDir, err := initAppEntry(c, reverse) | ||||||
| 	if err != nil { | 	if err != nil { | ||||||
| 		return err | 		return err | ||||||
| 	} | 	} | ||||||
| 
 | 
 | ||||||
|  | 	ctx := context{} | ||||||
|  | 
 | ||||||
| 	convergeWithHelmBinary := func(st *state.HelmState, helm helmexec.Interface) []error { | 	convergeWithHelmBinary := func(st *state.HelmState, helm helmexec.Interface) []error { | ||||||
| 		if c.GlobalString("helm-binary") != "" { | 		if c.GlobalString("helm-binary") != "" { | ||||||
| 			helm.SetHelmBinary(c.GlobalString("helm-binary")) | 			helm.SetHelmBinary(c.GlobalString("helm-binary")) | ||||||
| 		} | 		} | ||||||
| 		return converge(st, helm) | 		return converge(st, helm, ctx) | ||||||
| 	} | 	} | ||||||
| 
 | 
 | ||||||
| 	err = app.VisitDesiredStatesWithReleasesFiltered(fileOrDir, convergeWithHelmBinary) | 	err = app.VisitDesiredStatesWithReleasesFiltered(fileOrDir, convergeWithHelmBinary) | ||||||
|  |  | ||||||
|  | @ -128,8 +128,13 @@ func (state *HelmState) applyDefaultsTo(spec *ReleaseSpec) { | ||||||
| 	} | 	} | ||||||
| } | } | ||||||
| 
 | 
 | ||||||
|  | type RepoUpdater interface { | ||||||
|  | 	AddRepo(name, repository, certfile, keyfile, username, password string) error | ||||||
|  | 	UpdateRepo() error | ||||||
|  | } | ||||||
|  | 
 | ||||||
| // SyncRepos will update the given helm releases
 | // SyncRepos will update the given helm releases
 | ||||||
| func (state *HelmState) SyncRepos(helm helmexec.Interface) []error { | func (state *HelmState) SyncRepos(helm RepoUpdater) []error { | ||||||
| 	errs := []error{} | 	errs := []error{} | ||||||
| 
 | 
 | ||||||
| 	for _, repo := range state.Repositories { | 	for _, repo := range state.Repositories { | ||||||
|  | @ -828,6 +833,7 @@ func (state *HelmState) PrepareRelease(helm helmexec.Interface, helmfileCommand | ||||||
| 	for _, release := range state.Releases { | 	for _, release := range state.Releases { | ||||||
| 		if _, err := state.triggerPrepareEvent(&release, helmfileCommand); err != nil { | 		if _, err := state.triggerPrepareEvent(&release, helmfileCommand); err != nil { | ||||||
| 			errs = append(errs, &ReleaseError{&release, err}) | 			errs = append(errs, &ReleaseError{&release, err}) | ||||||
|  | 			continue | ||||||
| 		} | 		} | ||||||
| 	} | 	} | ||||||
| 	if len(errs) != 0 { | 	if len(errs) != 0 { | ||||||
|  |  | ||||||
		Loading…
	
		Reference in New Issue