Ordered helmfile(s) from a directory (#160)
Resolves #151 #137 Notes: - You can have either helmfile.d or helmfile.yaml/charts.yaml, not both
This commit is contained in:
		
							parent
							
								
									df264e2736
								
							
						
					
					
						commit
						2fba241122
					
				
							
								
								
									
										262
									
								
								main.go
								
								
								
								
							
							
						
						
									
										262
									
								
								main.go
								
								
								
								
							|  | @ -12,11 +12,14 @@ import ( | ||||||
| 	"github.com/roboll/helmfile/helmexec" | 	"github.com/roboll/helmfile/helmexec" | ||||||
| 	"github.com/roboll/helmfile/state" | 	"github.com/roboll/helmfile/state" | ||||||
| 	"github.com/urfave/cli" | 	"github.com/urfave/cli" | ||||||
|  | 	"path/filepath" | ||||||
|  | 	"sort" | ||||||
| ) | ) | ||||||
| 
 | 
 | ||||||
| const ( | const ( | ||||||
| 	DefaultHelmfile    = "helmfile.yaml" | 	DefaultHelmfile          = "helmfile.yaml" | ||||||
| 	DeprecatedHelmfile = "charts.yaml" | 	DeprecatedHelmfile       = "charts.yaml" | ||||||
|  | 	DefaultHelmfileDirectory = "helmfile.d" | ||||||
| ) | ) | ||||||
| 
 | 
 | ||||||
| var Version string | var Version string | ||||||
|  | @ -66,18 +69,14 @@ func main() { | ||||||
| 				}, | 				}, | ||||||
| 			}, | 			}, | ||||||
| 			Action: func(c *cli.Context) error { | 			Action: func(c *cli.Context) error { | ||||||
| 				state, helm, err := before(c) | 				return eachDesiredStateDo(c, func(state *state.HelmState, helm helmexec.Interface) []error { | ||||||
| 				if err != nil { | 					args := c.String("args") | ||||||
| 					return err | 					if len(args) > 0 { | ||||||
| 				} | 						helm.SetExtraArgs(strings.Split(args, " ")...) | ||||||
|  | 					} | ||||||
| 
 | 
 | ||||||
| 				args := c.String("args") | 					return state.SyncRepos(helm) | ||||||
| 				if len(args) > 0 { | 				}) | ||||||
| 					helm.SetExtraArgs(strings.Split(args, " ")...) |  | ||||||
| 				} |  | ||||||
| 
 |  | ||||||
| 				errs := state.SyncRepos(helm) |  | ||||||
| 				return clean(state, errs) |  | ||||||
| 			}, | 			}, | ||||||
| 		}, | 		}, | ||||||
| 		{ | 		{ | ||||||
|  | @ -100,21 +99,17 @@ func main() { | ||||||
| 				}, | 				}, | ||||||
| 			}, | 			}, | ||||||
| 			Action: func(c *cli.Context) error { | 			Action: func(c *cli.Context) error { | ||||||
| 				state, helm, err := before(c) | 				return eachDesiredStateDo(c, func(state *state.HelmState, helm helmexec.Interface) []error { | ||||||
| 				if err != nil { | 					args := c.String("args") | ||||||
| 					return err | 					if len(args) > 0 { | ||||||
| 				} | 						helm.SetExtraArgs(strings.Split(args, " ")...) | ||||||
|  | 					} | ||||||
| 
 | 
 | ||||||
| 				args := c.String("args") | 					values := c.StringSlice("values") | ||||||
| 				if len(args) > 0 { | 					workers := c.Int("concurrency") | ||||||
| 					helm.SetExtraArgs(strings.Split(args, " ")...) |  | ||||||
| 				} |  | ||||||
| 
 | 
 | ||||||
| 				values := c.StringSlice("values") | 					return state.SyncReleases(helm, values, workers) | ||||||
| 				workers := c.Int("concurrency") | 				}) | ||||||
| 
 |  | ||||||
| 				errs := state.SyncReleases(helm, values, workers) |  | ||||||
| 				return clean(state, errs) |  | ||||||
| 			}, | 			}, | ||||||
| 		}, | 		}, | ||||||
| 		{ | 		{ | ||||||
|  | @ -141,30 +136,23 @@ func main() { | ||||||
| 				}, | 				}, | ||||||
| 			}, | 			}, | ||||||
| 			Action: func(c *cli.Context) error { | 			Action: func(c *cli.Context) error { | ||||||
| 				state, helm, err := before(c) | 				return eachDesiredStateDo(c, func(state *state.HelmState, helm helmexec.Interface) []error { | ||||||
| 				if err != nil { | 					args := c.String("args") | ||||||
| 					return err | 					if len(args) > 0 { | ||||||
| 				} | 						helm.SetExtraArgs(strings.Split(args, " ")...) | ||||||
| 
 |  | ||||||
| 				args := c.String("args") |  | ||||||
| 				if len(args) > 0 { |  | ||||||
| 					helm.SetExtraArgs(strings.Split(args, " ")...) |  | ||||||
| 				} |  | ||||||
| 
 |  | ||||||
| 				if c.Bool("sync-repos") { |  | ||||||
| 					if errs := state.SyncRepos(helm); errs != nil && len(errs) > 0 { |  | ||||||
| 						for _, err := range errs { |  | ||||||
| 							fmt.Printf("err: %s\n", err.Error()) |  | ||||||
| 						} |  | ||||||
| 						os.Exit(1) |  | ||||||
| 					} | 					} | ||||||
| 				} |  | ||||||
| 
 | 
 | ||||||
| 				values := c.StringSlice("values") | 					if c.Bool("sync-repos") { | ||||||
| 				workers := c.Int("concurrency") | 						if errs := state.SyncRepos(helm); errs != nil && len(errs) > 0 { | ||||||
|  | 							return errs | ||||||
|  | 						} | ||||||
|  | 					} | ||||||
| 
 | 
 | ||||||
| 				errs := state.DiffReleases(helm, values, workers) | 					values := c.StringSlice("values") | ||||||
| 				return clean(state, errs) | 					workers := c.Int("concurrency") | ||||||
|  | 
 | ||||||
|  | 					return state.DiffReleases(helm, values, workers) | ||||||
|  | 				}) | ||||||
| 			}, | 			}, | ||||||
| 		}, | 		}, | ||||||
| 		{ | 		{ | ||||||
|  | @ -187,35 +175,25 @@ func main() { | ||||||
| 				}, | 				}, | ||||||
| 			}, | 			}, | ||||||
| 			Action: func(c *cli.Context) error { | 			Action: func(c *cli.Context) error { | ||||||
| 				state, helm, err := before(c) | 				return eachDesiredStateDo(c, func(state *state.HelmState, helm helmexec.Interface) []error { | ||||||
| 				if err != nil { | 					if errs := state.SyncRepos(helm); errs != nil && len(errs) > 0 { | ||||||
| 					return err | 						return errs | ||||||
| 				} |  | ||||||
| 
 |  | ||||||
| 				if errs := state.SyncRepos(helm); errs != nil && len(errs) > 0 { |  | ||||||
| 					for _, err := range errs { |  | ||||||
| 						fmt.Printf("err: %s\n", err.Error()) |  | ||||||
| 					} | 					} | ||||||
| 					os.Exit(1) |  | ||||||
| 				} |  | ||||||
| 
 | 
 | ||||||
| 				if errs := state.UpdateDeps(helm); errs != nil && len(errs) > 0 { | 					if errs := state.UpdateDeps(helm); errs != nil && len(errs) > 0 { | ||||||
| 					for _, err := range errs { | 						return errs | ||||||
| 						fmt.Printf("err: %s\n", err.Error()) |  | ||||||
| 					} | 					} | ||||||
| 					os.Exit(1) |  | ||||||
| 				} |  | ||||||
| 
 | 
 | ||||||
| 				args := c.String("args") | 					args := c.String("args") | ||||||
| 				if len(args) > 0 { | 					if len(args) > 0 { | ||||||
| 					helm.SetExtraArgs(strings.Split(args, " ")...) | 						helm.SetExtraArgs(strings.Split(args, " ")...) | ||||||
| 				} | 					} | ||||||
| 
 | 
 | ||||||
| 				values := c.StringSlice("values") | 					values := c.StringSlice("values") | ||||||
| 				workers := c.Int("concurrency") | 					workers := c.Int("concurrency") | ||||||
| 
 | 
 | ||||||
| 				errs := state.SyncReleases(helm, values, workers) | 					return state.SyncReleases(helm, values, workers) | ||||||
| 				return clean(state, errs) | 				}) | ||||||
| 			}, | 			}, | ||||||
| 		}, | 		}, | ||||||
| 		{ | 		{ | ||||||
|  | @ -234,20 +212,16 @@ func main() { | ||||||
| 				}, | 				}, | ||||||
| 			}, | 			}, | ||||||
| 			Action: func(c *cli.Context) error { | 			Action: func(c *cli.Context) error { | ||||||
| 				state, helm, err := before(c) | 				return eachDesiredStateDo(c, func(state *state.HelmState, helm helmexec.Interface) []error { | ||||||
| 				if err != nil { | 					workers := c.Int("concurrency") | ||||||
| 					return err |  | ||||||
| 				} |  | ||||||
| 
 | 
 | ||||||
| 				workers := c.Int("concurrency") | 					args := c.String("args") | ||||||
|  | 					if len(args) > 0 { | ||||||
|  | 						helm.SetExtraArgs(strings.Split(args, " ")...) | ||||||
|  | 					} | ||||||
| 
 | 
 | ||||||
| 				args := c.String("args") | 					return state.ReleaseStatuses(helm, workers) | ||||||
| 				if len(args) > 0 { | 				}) | ||||||
| 					helm.SetExtraArgs(strings.Split(args, " ")...) |  | ||||||
| 				} |  | ||||||
| 
 |  | ||||||
| 				errs := state.ReleaseStatuses(helm, workers) |  | ||||||
| 				return clean(state, errs) |  | ||||||
| 			}, | 			}, | ||||||
| 		}, | 		}, | ||||||
| 		{ | 		{ | ||||||
|  | @ -260,15 +234,11 @@ func main() { | ||||||
| 				}, | 				}, | ||||||
| 			}, | 			}, | ||||||
| 			Action: func(c *cli.Context) error { | 			Action: func(c *cli.Context) error { | ||||||
| 				state, helm, err := before(c) | 				return eachDesiredStateDo(c, func(state *state.HelmState, helm helmexec.Interface) []error { | ||||||
| 				if err != nil { | 					purge := c.Bool("purge") | ||||||
| 					return err |  | ||||||
| 				} |  | ||||||
| 
 | 
 | ||||||
| 				purge := c.Bool("purge") | 					return state.DeleteReleases(helm, purge) | ||||||
| 
 | 				}) | ||||||
| 				errs := state.DeleteReleases(helm, purge) |  | ||||||
| 				return clean(state, errs) |  | ||||||
| 			}, | 			}, | ||||||
| 		}, | 		}, | ||||||
| 		{ | 		{ | ||||||
|  | @ -291,21 +261,17 @@ func main() { | ||||||
| 				}, | 				}, | ||||||
| 			}, | 			}, | ||||||
| 			Action: func(c *cli.Context) error { | 			Action: func(c *cli.Context) error { | ||||||
| 				state, helm, err := before(c) | 				return eachDesiredStateDo(c, func(state *state.HelmState, helm helmexec.Interface) []error { | ||||||
| 				if err != nil { | 					cleanup := c.Bool("cleanup") | ||||||
| 					return err | 					timeout := c.Int("timeout") | ||||||
| 				} |  | ||||||
| 
 | 
 | ||||||
| 				cleanup := c.Bool("cleanup") | 					args := c.String("args") | ||||||
| 				timeout := c.Int("timeout") | 					if len(args) > 0 { | ||||||
|  | 						helm.SetExtraArgs(strings.Split(args, " ")...) | ||||||
|  | 					} | ||||||
| 
 | 
 | ||||||
| 				args := c.String("args") | 					return state.TestReleases(helm, cleanup, timeout) | ||||||
| 				if len(args) > 0 { | 				}) | ||||||
| 					helm.SetExtraArgs(strings.Split(args, " ")...) |  | ||||||
| 				} |  | ||||||
| 
 |  | ||||||
| 				errs := state.TestReleases(helm, cleanup, timeout) |  | ||||||
| 				return clean(state, errs) |  | ||||||
| 			}, | 			}, | ||||||
| 		}, | 		}, | ||||||
| 	} | 	} | ||||||
|  | @ -317,8 +283,83 @@ func main() { | ||||||
| 	} | 	} | ||||||
| } | } | ||||||
| 
 | 
 | ||||||
| func before(c *cli.Context) (*state.HelmState, helmexec.Interface, error) { | func eachDesiredStateDo(c *cli.Context, converge func(*state.HelmState, helmexec.Interface) []error) error { | ||||||
| 	file := c.GlobalString("file") | 	fileOrDirPath := c.GlobalString("file") | ||||||
|  | 	desiredStateFiles, err := findDesiredStateFiles(fileOrDirPath) | ||||||
|  | 	if err != nil { | ||||||
|  | 		return err | ||||||
|  | 	} | ||||||
|  | 	for _, f := range desiredStateFiles { | ||||||
|  | 		state, helm, err := loadDesiredStateFromFile(c, f) | ||||||
|  | 		if err != nil { | ||||||
|  | 			return err | ||||||
|  | 		} | ||||||
|  | 		errs := converge(state, helm) | ||||||
|  | 		if err := clean(state, errs); err != nil { | ||||||
|  | 			return err | ||||||
|  | 		} | ||||||
|  | 	} | ||||||
|  | 	return nil | ||||||
|  | } | ||||||
|  | 
 | ||||||
|  | func findDesiredStateFiles(specifiedPath string) ([]string, error) { | ||||||
|  | 	var helmfileDir string | ||||||
|  | 	if specifiedPath != "" { | ||||||
|  | 		if fileExistsAt(specifiedPath) { | ||||||
|  | 			return []string{specifiedPath}, nil | ||||||
|  | 		} else if directoryExistsAt(specifiedPath) { | ||||||
|  | 			helmfileDir = specifiedPath | ||||||
|  | 		} else { | ||||||
|  | 			return []string{}, fmt.Errorf("specified state file %s is not found", specifiedPath) | ||||||
|  | 		} | ||||||
|  | 	} else { | ||||||
|  | 		var defaultFile string | ||||||
|  | 		if fileExistsAt(DefaultHelmfile) { | ||||||
|  | 			defaultFile = DefaultHelmfile | ||||||
|  | 		} else if fileExistsAt(DeprecatedHelmfile) { | ||||||
|  | 			log.Printf( | ||||||
|  | 				"warn: %s is being loaded: %s is deprecated in favor of %s. See https://github.com/roboll/helmfile/issues/25 for more information", | ||||||
|  | 				DeprecatedHelmfile, | ||||||
|  | 				DeprecatedHelmfile, | ||||||
|  | 				DefaultHelmfile, | ||||||
|  | 			) | ||||||
|  | 			defaultFile = DeprecatedHelmfile | ||||||
|  | 		} | ||||||
|  | 
 | ||||||
|  | 		if directoryExistsAt(DefaultHelmfileDirectory) { | ||||||
|  | 			if defaultFile != "" { | ||||||
|  | 				return []string{}, fmt.Errorf("configuration conlict error: you can have either %s or %s, but not both", defaultFile, DefaultHelmfileDirectory) | ||||||
|  | 			} | ||||||
|  | 
 | ||||||
|  | 			helmfileDir = DefaultHelmfileDirectory | ||||||
|  | 		} else if defaultFile != "" { | ||||||
|  | 			return []string{defaultFile}, nil | ||||||
|  | 		} else { | ||||||
|  | 			return []string{}, fmt.Errorf("no state file found. It must be named %s/*.yaml, %s, or %s, or otherwise specified with the --file flag", DefaultHelmfileDirectory, DefaultHelmfile, DeprecatedHelmfile) | ||||||
|  | 		} | ||||||
|  | 	} | ||||||
|  | 
 | ||||||
|  | 	files, err := filepath.Glob(filepath.Join(helmfileDir, "*.yaml")) | ||||||
|  | 	if err != nil { | ||||||
|  | 		return []string{}, err | ||||||
|  | 	} | ||||||
|  | 	sort.Slice(files, func(i, j int) bool { | ||||||
|  | 		return files[i] < files[j] | ||||||
|  | 	}) | ||||||
|  | 	return files, nil | ||||||
|  | } | ||||||
|  | 
 | ||||||
|  | func fileExistsAt(path string) bool { | ||||||
|  | 	fileInfo, err := os.Stat(path) | ||||||
|  | 	return err == nil && fileInfo.Mode().IsRegular() | ||||||
|  | } | ||||||
|  | 
 | ||||||
|  | func directoryExistsAt(path string) bool { | ||||||
|  | 	fileInfo, err := os.Stat(path) | ||||||
|  | 	return err == nil && fileInfo.Mode().IsDir() | ||||||
|  | } | ||||||
|  | 
 | ||||||
|  | func loadDesiredStateFromFile(c *cli.Context, file string) (*state.HelmState, helmexec.Interface, error) { | ||||||
| 	quiet := c.GlobalBool("quiet") | 	quiet := c.GlobalBool("quiet") | ||||||
| 	kubeContext := c.GlobalString("kube-context") | 	kubeContext := c.GlobalString("kube-context") | ||||||
| 	namespace := c.GlobalString("namespace") | 	namespace := c.GlobalString("namespace") | ||||||
|  | @ -326,16 +367,7 @@ func before(c *cli.Context) (*state.HelmState, helmexec.Interface, error) { | ||||||
| 
 | 
 | ||||||
| 	st, err := state.ReadFromFile(file) | 	st, err := state.ReadFromFile(file) | ||||||
| 	if err != nil { | 	if err != nil { | ||||||
| 		if strings.Contains(err.Error(), fmt.Sprintf("open %s:", DefaultHelmfile)) { | 		return nil, nil, fmt.Errorf("failed to read %s: %v", file, err) | ||||||
| 			var fallbackErr error |  | ||||||
| 			st, fallbackErr = state.ReadFromFile(DeprecatedHelmfile) |  | ||||||
| 			if fallbackErr != nil { |  | ||||||
| 				return nil, nil, fmt.Errorf("failed to read %s and %s: %v", file, DeprecatedHelmfile, err) |  | ||||||
| 			} |  | ||||||
| 			log.Printf("warn: charts.yaml is loaded: charts.yaml is deprecated in favor of helmfile.yaml. See https://github.com/roboll/helmfile/issues/25 for more information") |  | ||||||
| 		} else { |  | ||||||
| 			return nil, nil, fmt.Errorf("failed to read %s: %v", file, err) |  | ||||||
| 		} |  | ||||||
| 	} | 	} | ||||||
| 
 | 
 | ||||||
| 	if st.Context != "" { | 	if st.Context != "" { | ||||||
|  |  | ||||||
		Loading…
	
		Reference in New Issue