Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Ordered helmfile(s) from a directory #160

Merged
merged 3 commits into from
Jun 14, 2018
Merged
Changes from 1 commit
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
243 changes: 139 additions & 104 deletions main.go
Original file line number Diff line number Diff line change
Expand Up @@ -12,11 +12,14 @@ import (
"github.com/roboll/helmfile/helmexec"
"github.com/roboll/helmfile/state"
"github.com/urfave/cli"
"path/filepath"
"sort"
)

const (
DefaultHelmfile = "helmfile.yaml"
DeprecatedHelmfile = "charts.yaml"
DefaultHelmfile = "helmfile.yaml"
DeprecatedHelmfile = "charts.yaml"
DefaultHelmfileDirectory = "helmfile.d"
)

var Version string
Expand Down Expand Up @@ -66,18 +69,14 @@ func main() {
},
},
Action: func(c *cli.Context) error {
state, helm, err := before(c)
if err != nil {
return err
}

args := c.String("args")
if len(args) > 0 {
helm.SetExtraArgs(strings.Split(args, " ")...)
}

errs := state.SyncRepos(helm)
return clean(state, errs)
return eachDesiredStateDo(c, func(state *state.HelmState, helm helmexec.Interface) []error {
args := c.String("args")
if len(args) > 0 {
helm.SetExtraArgs(strings.Split(args, " ")...)
}

return state.SyncRepos(helm)
})
},
},
{
Expand All @@ -100,21 +99,17 @@ func main() {
},
},
Action: func(c *cli.Context) error {
state, helm, err := before(c)
if err != nil {
return err
}

args := c.String("args")
if len(args) > 0 {
helm.SetExtraArgs(strings.Split(args, " ")...)
}
return eachDesiredStateDo(c, func(state *state.HelmState, helm helmexec.Interface) []error {
args := c.String("args")
if len(args) > 0 {
helm.SetExtraArgs(strings.Split(args, " ")...)
}

values := c.StringSlice("values")
workers := c.Int("concurrency")
values := c.StringSlice("values")
workers := c.Int("concurrency")

errs := state.SyncReleases(helm, values, workers)
return clean(state, errs)
return state.SyncReleases(helm, values, workers)
})
},
},
{
Expand All @@ -141,30 +136,23 @@ func main() {
},
},
Action: func(c *cli.Context) error {
state, helm, err := before(c)
if err != nil {
return err
}

args := c.String("args")
if len(args) > 0 {
helm.SetExtraArgs(strings.Split(args, " ")...)
}
return eachDesiredStateDo(c, func(state *state.HelmState, helm helmexec.Interface) []error {
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())
if c.Bool("sync-repos") {
if errs := state.SyncRepos(helm); errs != nil && len(errs) > 0 {
return errs
}
os.Exit(1)
}
}

values := c.StringSlice("values")
workers := c.Int("concurrency")
values := c.StringSlice("values")
workers := c.Int("concurrency")

errs := state.DiffReleases(helm, values, workers)
return clean(state, errs)
return state.DiffReleases(helm, values, workers)
})
},
},
{
Expand All @@ -187,35 +175,25 @@ func main() {
},
},
Action: func(c *cli.Context) error {
state, helm, err := before(c)
if err != nil {
return err
}

if errs := state.SyncRepos(helm); errs != nil && len(errs) > 0 {
for _, err := range errs {
fmt.Printf("err: %s\n", err.Error())
return eachDesiredStateDo(c, func(state *state.HelmState, helm helmexec.Interface) []error {
if errs := state.SyncRepos(helm); errs != nil && len(errs) > 0 {
return errs
}
os.Exit(1)
}

if errs := state.UpdateDeps(helm); errs != nil && len(errs) > 0 {
for _, err := range errs {
fmt.Printf("err: %s\n", err.Error())
if errs := state.UpdateDeps(helm); errs != nil && len(errs) > 0 {
return errs
}
os.Exit(1)
}

args := c.String("args")
if len(args) > 0 {
helm.SetExtraArgs(strings.Split(args, " ")...)
}
args := c.String("args")
if len(args) > 0 {
helm.SetExtraArgs(strings.Split(args, " ")...)
}

values := c.StringSlice("values")
workers := c.Int("concurrency")
values := c.StringSlice("values")
workers := c.Int("concurrency")

errs := state.SyncReleases(helm, values, workers)
return clean(state, errs)
return state.SyncReleases(helm, values, workers)
})
},
},
{
Expand All @@ -234,20 +212,16 @@ func main() {
},
},
Action: func(c *cli.Context) error {
state, helm, err := before(c)
if err != nil {
return err
}
return eachDesiredStateDo(c, func(state *state.HelmState, helm helmexec.Interface) []error {
workers := c.Int("concurrency")

workers := c.Int("concurrency")

args := c.String("args")
if len(args) > 0 {
helm.SetExtraArgs(strings.Split(args, " ")...)
}
args := c.String("args")
if len(args) > 0 {
helm.SetExtraArgs(strings.Split(args, " ")...)
}

errs := state.ReleaseStatuses(helm, workers)
return clean(state, errs)
return state.ReleaseStatuses(helm, workers)
})
},
},
{
Expand All @@ -260,15 +234,11 @@ func main() {
},
},
Action: func(c *cli.Context) error {
state, helm, err := before(c)
if err != nil {
return err
}

purge := c.Bool("purge")
return eachDesiredStateDo(c, func(state *state.HelmState, helm helmexec.Interface) []error {
purge := c.Bool("purge")

errs := state.DeleteReleases(helm, purge)
return clean(state, errs)
return state.DeleteReleases(helm, purge)
})
},
},
{
Expand All @@ -291,21 +261,17 @@ func main() {
},
},
Action: func(c *cli.Context) error {
state, helm, err := before(c)
if err != nil {
return err
}
return eachDesiredStateDo(c, func(state *state.HelmState, helm helmexec.Interface) []error {
cleanup := c.Bool("cleanup")
timeout := c.Int("timeout")

cleanup := c.Bool("cleanup")
timeout := c.Int("timeout")

args := c.String("args")
if len(args) > 0 {
helm.SetExtraArgs(strings.Split(args, " ")...)
}
args := c.String("args")
if len(args) > 0 {
helm.SetExtraArgs(strings.Split(args, " ")...)
}

errs := state.TestReleases(helm, cleanup, timeout)
return clean(state, errs)
return state.TestReleases(helm, cleanup, timeout)
})
},
},
}
Expand All @@ -317,8 +283,77 @@ func main() {
}
}

func before(c *cli.Context) (*state.HelmState, helmexec.Interface, error) {
file := c.GlobalString("file")
func eachDesiredStateDo(c *cli.Context, converge func(*state.HelmState, helmexec.Interface) []error) error {
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(fileOrDirPath string) ([]string, error) {
var dir string

if fileOrDirPath != "" {
if !fileExists(fileOrDirPath) {
return []string{}, fmt.Errorf("state file named %s is not found", fileOrDirPath)
}

if !directoryExists(fileOrDirPath) {
return []string{fileOrDirPath}, nil
}

dir = fileOrDirPath
}

if dir == "" && fileExists(DefaultHelmfileDirectory) && directoryExists(DefaultHelmfileDirectory) {
dir = DefaultHelmfileDirectory
}

if dir != "" {
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I think this is clearer

if dir != "" { 
...
else if  fileExists(DefaultHelmfileDirectory) && directoryExists(DefaultHelmfileDirectory) {
...
}

Copy link
Collaborator Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

What would be included in if and else if bodies respectively? We need dir to be set for use in https://github.com/roboll/helmfile/pull/160/files#diff-7ddfb3e035b42cd70649cc33393fe32cR325.

Copy link
Collaborator Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Would it look clearer now?

files, err := filepath.Glob(filepath.Join(dir, "*.yaml"))
if err != nil {
return []string{}, err
}
sort.Slice(files, func(i, j int) bool {
return files[i] < files[j]
})
return files, nil
}

if fileExists(DefaultHelmfile) {
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Should we complain if we have both a helmfile dir and a helmfile? Or should we load both? Currently it would ignore the helmfile and use the helmdir.

Copy link

@gtaylor gtaylor Jun 6, 2018

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

That's a sneaky kind of behavior, so whatever we do, I hope we make it explicit and/or noisy. Maybe a warning + using the helmdir? Or an error and immediate exit if we want to really prevent confusion.

Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Typically with apps that have a conf.d structure, they also support a main config file. That main config file then will then have an option to point to a conf.d folder. For example systemd. Now, I'm not advocating adding this to this PR. As far as I'm concerned, executing one or the other (or even both) is fine by me.

Copy link
Collaborator Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Thanks for the feedbacks! Let me go forward with the immediate exit for now. I want to make it very explicit.

Copy link
Collaborator Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Addressed in the last commit

return []string{DefaultHelmfile}, nil
}

if fileExists(DeprecatedHelmfile) {
return []string{DeprecatedHelmfile}, nil
}

return []string{}, fmt.Errorf("no state file found. It must be named %s or %s, or otherwise specified with the --file flag", DefaultHelmfile, DeprecatedHelmfile)
}

func fileExists(filename string) bool {
_, err := os.Stat(filename)
return err == nil
}

func directoryExists(path string) bool {
fileInfo, err := os.Stat(path)
return err == nil && fileInfo != nil && fileInfo.IsDir()
}

func loadDesiredStateFromFile(c *cli.Context, file string) (*state.HelmState, helmexec.Interface, error) {
quiet := c.GlobalBool("quiet")
kubeContext := c.GlobalString("kube-context")
namespace := c.GlobalString("namespace")
Expand Down