running update dependencies for local charts
This commit is contained in:
parent
cef2d26c06
commit
1b302db7f8
|
|
@ -101,7 +101,7 @@ COMMANDS:
|
||||||
repos sync repositories from state file (helm repo add && helm repo update)
|
repos sync repositories from state file (helm repo add && helm repo update)
|
||||||
charts sync charts from state file (helm upgrade --install)
|
charts sync charts from state file (helm upgrade --install)
|
||||||
diff diff charts from state file against env (helm diff)
|
diff diff charts from state file against env (helm diff)
|
||||||
sync sync all resources from state file (repos && charts)
|
sync sync all resources from state file (repos, charts and local chart deps)
|
||||||
delete delete charts from state file (helm delete)
|
delete delete charts from state file (helm delete)
|
||||||
|
|
||||||
GLOBAL OPTIONS:
|
GLOBAL OPTIONS:
|
||||||
|
|
@ -119,9 +119,10 @@ GLOBAL OPTIONS:
|
||||||
|
|
||||||
### sync
|
### sync
|
||||||
|
|
||||||
The `helmfile sync` sub-command sync your cluster state as desired in your `helmfile`. The default helmfile is `helmfile.yaml`, but any yaml file can be passed by specifying a `--file path/to/your/yaml/file` flag.
|
The `helmfile sync` sub-command sync your cluster state as described in your `helmfile`. The default helmfile is `helmfile.yaml`, but any yaml file can be passed by specifying a `--file path/to/your/yaml/file` flag.
|
||||||
|
|
||||||
Under the covers, Helmfile executes `helm upgrade --install` for each `release` declared in the manifest, by optionally decrypting [secrets](#secrets) to be consumed as helm chart values.
|
Under the covers, Helmfile executes `helm upgrade --install` for each `release` declared in the manifest, by optionally decrypting [secrets](#secrets) to be consumed as helm chart values. It also updates specified chart repositories and updates the
|
||||||
|
dependencies of any referenced local charts.
|
||||||
|
|
||||||
### diff
|
### diff
|
||||||
|
|
||||||
|
|
|
||||||
|
|
@ -46,6 +46,12 @@ func (helm *execer) UpdateRepo() error {
|
||||||
return err
|
return err
|
||||||
}
|
}
|
||||||
|
|
||||||
|
func (helm *execer) UpdateDeps(chart string) error {
|
||||||
|
out, err := helm.exec("dependency", "update", chart)
|
||||||
|
helm.write(out)
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
|
||||||
func (helm *execer) SyncRelease(name, chart string, flags ...string) error {
|
func (helm *execer) SyncRelease(name, chart string, flags ...string) error {
|
||||||
out, err := helm.exec(append([]string{"upgrade", "--install", name, chart}, flags...)...)
|
out, err := helm.exec(append([]string{"upgrade", "--install", name, chart}, flags...)...)
|
||||||
helm.write(out)
|
helm.write(out)
|
||||||
|
|
@ -86,4 +92,4 @@ func (helm *execer) write(out []byte) {
|
||||||
if helm.writer != nil {
|
if helm.writer != nil {
|
||||||
helm.writer.Write(out)
|
helm.writer.Write(out)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
||||||
|
|
@ -100,6 +100,24 @@ func Test_SyncRelease(t *testing.T) {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
func Test_UpdateDeps(t *testing.T) {
|
||||||
|
var buffer bytes.Buffer
|
||||||
|
helm := MockExecer(&buffer, "dev")
|
||||||
|
helm.UpdateDeps("./chart/foo")
|
||||||
|
expected := "exec: helm dependency update ./chart/foo --kube-context dev\n"
|
||||||
|
if buffer.String() != expected {
|
||||||
|
t.Errorf("helmexec.SyncRelease()\nactual = %v\nexpect = %v", buffer.String(), expected)
|
||||||
|
}
|
||||||
|
|
||||||
|
buffer.Reset()
|
||||||
|
helm.SetExtraArgs("--verify")
|
||||||
|
helm.UpdateDeps("./chart/foo")
|
||||||
|
expected = "exec: helm dependency update ./chart/foo --verify --kube-context dev\n"
|
||||||
|
if buffer.String() != expected {
|
||||||
|
t.Errorf("helmexec.AddRepo()\nactual = %v\nexpect = %v", buffer.String(), expected)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
func Test_DecryptSecret(t *testing.T) {
|
func Test_DecryptSecret(t *testing.T) {
|
||||||
var buffer bytes.Buffer
|
var buffer bytes.Buffer
|
||||||
helm := MockExecer(&buffer, "dev")
|
helm := MockExecer(&buffer, "dev")
|
||||||
|
|
|
||||||
|
|
@ -5,7 +5,7 @@ type Interface interface {
|
||||||
|
|
||||||
AddRepo(name, repository, certfile, keyfile string) error
|
AddRepo(name, repository, certfile, keyfile string) error
|
||||||
UpdateRepo() error
|
UpdateRepo() error
|
||||||
|
UpdateDeps(chart string) error
|
||||||
SyncRelease(name, chart string, flags ...string) error
|
SyncRelease(name, chart string, flags ...string) error
|
||||||
DiffRelease(name, chart string, flags ...string) error
|
DiffRelease(name, chart string, flags ...string) error
|
||||||
DeleteRelease(name string) error
|
DeleteRelease(name string) error
|
||||||
|
|
|
||||||
15
main.go
15
main.go
|
|
@ -163,7 +163,7 @@ func main() {
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
Name: "sync",
|
Name: "sync",
|
||||||
Usage: "sync all resources from state file (repos && charts)",
|
Usage: "sync all resources from state file (repos, charts and local chart deps)",
|
||||||
Flags: []cli.Flag{
|
Flags: []cli.Flag{
|
||||||
cli.StringSliceFlag{
|
cli.StringSliceFlag{
|
||||||
Name: "values",
|
Name: "values",
|
||||||
|
|
@ -193,14 +193,21 @@ func main() {
|
||||||
os.Exit(1)
|
os.Exit(1)
|
||||||
}
|
}
|
||||||
|
|
||||||
values := c.StringSlice("values")
|
|
||||||
workers := c.Int("concurrency")
|
|
||||||
|
|
||||||
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, " ")...)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
if errs := state.UpdateDeps(helm); errs != nil && len(errs) > 0 {
|
||||||
|
for _, err := range errs {
|
||||||
|
fmt.Printf("err: %s\n", err.Error())
|
||||||
|
}
|
||||||
|
os.Exit(1)
|
||||||
|
}
|
||||||
|
|
||||||
|
values := c.StringSlice("values")
|
||||||
|
workers := c.Int("concurrency")
|
||||||
|
|
||||||
errs := state.SyncReleases(helm, values, workers)
|
errs := state.SyncReleases(helm, values, workers)
|
||||||
return clean(state, errs)
|
return clean(state, errs)
|
||||||
},
|
},
|
||||||
|
|
|
||||||
|
|
@ -184,7 +184,8 @@ func (state *HelmState) SyncReleases(helm helmexec.Interface, additionalValues [
|
||||||
continue
|
continue
|
||||||
}
|
}
|
||||||
|
|
||||||
if err := helm.SyncRelease(release.Name, normalizeChart(state.BaseChartPath, release.Chart), flags...); err != nil {
|
chart := normalizeChart(state.BaseChartPath, release.Chart)
|
||||||
|
if err := helm.SyncRelease(release.Name, chart, flags...); err != nil {
|
||||||
errQueue <- err
|
errQueue <- err
|
||||||
}
|
}
|
||||||
doneQueue <- true
|
doneQueue <- true
|
||||||
|
|
@ -331,18 +332,38 @@ func (state *HelmState) FilterReleases(labels []string) error {
|
||||||
return nil
|
return nil
|
||||||
}
|
}
|
||||||
|
|
||||||
|
func (state *HelmState) UpdateDeps(helm helmexec.Interface) []error {
|
||||||
|
errs := []error{}
|
||||||
|
|
||||||
|
for _, release := range state.Releases {
|
||||||
|
if isLocalChart(release.Chart) {
|
||||||
|
if err := helm.UpdateDeps(normalizeChart(state.BaseChartPath, release.Chart)); err != nil {
|
||||||
|
errs = append(errs, err)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
if len(errs) != 0 {
|
||||||
|
return errs
|
||||||
|
}
|
||||||
|
return nil
|
||||||
|
}
|
||||||
|
|
||||||
// normalizeChart allows for the distinction between a file path reference and repository references.
|
// normalizeChart allows for the distinction between a file path reference and repository references.
|
||||||
// - Any single (or double character) followed by a `/` will be considered a local file reference and
|
// - Any single (or double character) followed by a `/` will be considered a local file reference and
|
||||||
// be constructed relative to the `base path`.
|
// be constructed relative to the `base path`.
|
||||||
// - Everything else is assumed to be an absolute path or an actual <repository>/<chart> reference.
|
// - Everything else is assumed to be an absolute path or an actual <repository>/<chart> reference.
|
||||||
func normalizeChart(basePath, chart string) string {
|
func normalizeChart(basePath, chart string) string {
|
||||||
regex, _ := regexp.Compile("^[.]?./")
|
if !isLocalChart(chart) {
|
||||||
if !regex.MatchString(chart) {
|
|
||||||
return chart
|
return chart
|
||||||
}
|
}
|
||||||
return filepath.Join(basePath, chart)
|
return filepath.Join(basePath, chart)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
func isLocalChart(chart string) bool {
|
||||||
|
regex, _ := regexp.Compile("^[.]?./")
|
||||||
|
return regex.MatchString(chart)
|
||||||
|
}
|
||||||
|
|
||||||
func flagsForRelease(helm helmexec.Interface, basePath string, release *ReleaseSpec) ([]string, error) {
|
func flagsForRelease(helm helmexec.Interface, basePath string, release *ReleaseSpec) ([]string, error) {
|
||||||
flags := []string{}
|
flags := []string{}
|
||||||
if release.Version != "" {
|
if release.Version != "" {
|
||||||
|
|
|
||||||
|
|
@ -3,6 +3,10 @@ package state
|
||||||
import (
|
import (
|
||||||
"reflect"
|
"reflect"
|
||||||
"testing"
|
"testing"
|
||||||
|
|
||||||
|
"errors"
|
||||||
|
"fmt"
|
||||||
|
"strings"
|
||||||
)
|
)
|
||||||
|
|
||||||
func TestReadFromYaml(t *testing.T) {
|
func TestReadFromYaml(t *testing.T) {
|
||||||
|
|
@ -218,3 +222,180 @@ func TestHelmState_applyDefaultsTo(t *testing.T) {
|
||||||
})
|
})
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
func Test_isLocalChart(t *testing.T) {
|
||||||
|
type args struct {
|
||||||
|
chart string
|
||||||
|
}
|
||||||
|
tests := []struct {
|
||||||
|
name string
|
||||||
|
args args
|
||||||
|
want bool
|
||||||
|
}{
|
||||||
|
{
|
||||||
|
name: "local chart",
|
||||||
|
args: args{
|
||||||
|
chart: "./charts/nonstop",
|
||||||
|
},
|
||||||
|
want: true,
|
||||||
|
},
|
||||||
|
{
|
||||||
|
name: "repo chart",
|
||||||
|
args: args{
|
||||||
|
chart: "stable/genius",
|
||||||
|
},
|
||||||
|
want: false,
|
||||||
|
},
|
||||||
|
{
|
||||||
|
name: "empty",
|
||||||
|
args: args{
|
||||||
|
chart: "",
|
||||||
|
},
|
||||||
|
want: false,
|
||||||
|
},
|
||||||
|
{
|
||||||
|
name: "parent local path",
|
||||||
|
args: args{
|
||||||
|
chart: "../../dotty",
|
||||||
|
},
|
||||||
|
want: true,
|
||||||
|
},
|
||||||
|
{
|
||||||
|
name: "parent-parent local path",
|
||||||
|
args: args{
|
||||||
|
chart: "../../dotty",
|
||||||
|
},
|
||||||
|
want: true,
|
||||||
|
},
|
||||||
|
}
|
||||||
|
for _, tt := range tests {
|
||||||
|
t.Run(tt.name, func(t *testing.T) {
|
||||||
|
if got := isLocalChart(tt.args.chart); got != tt.want {
|
||||||
|
t.Errorf("isLocalChart() = %v, want %v", got, tt.want)
|
||||||
|
}
|
||||||
|
})
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
func Test_normalizeChart(t *testing.T) {
|
||||||
|
type args struct {
|
||||||
|
basePath string
|
||||||
|
chart string
|
||||||
|
}
|
||||||
|
tests := []struct {
|
||||||
|
name string
|
||||||
|
args args
|
||||||
|
want string
|
||||||
|
}{
|
||||||
|
{
|
||||||
|
name: "construct local chart path",
|
||||||
|
args: args{
|
||||||
|
basePath: "/Users/jane/code/deploy/charts",
|
||||||
|
chart: "./app",
|
||||||
|
},
|
||||||
|
want: "/Users/jane/code/deploy/charts/app",
|
||||||
|
},
|
||||||
|
{
|
||||||
|
name: "repo path",
|
||||||
|
args: args{
|
||||||
|
basePath: "/Users/jane/code/deploy/charts",
|
||||||
|
chart: "remote/app",
|
||||||
|
},
|
||||||
|
want: "remote/app",
|
||||||
|
},
|
||||||
|
{
|
||||||
|
name: "construct local chart path, parent dir",
|
||||||
|
args: args{
|
||||||
|
basePath: "/Users/jane/code/deploy/charts",
|
||||||
|
chart: "../app",
|
||||||
|
},
|
||||||
|
want: "/Users/jane/code/deploy/app",
|
||||||
|
},
|
||||||
|
{
|
||||||
|
name: "too much parent levels",
|
||||||
|
args: args{
|
||||||
|
basePath: "/src",
|
||||||
|
chart: "../../app",
|
||||||
|
},
|
||||||
|
want: "/app",
|
||||||
|
},
|
||||||
|
}
|
||||||
|
for _, tt := range tests {
|
||||||
|
t.Run(tt.name, func(t *testing.T) {
|
||||||
|
if got := normalizeChart(tt.args.basePath, tt.args.chart); got != tt.want {
|
||||||
|
t.Errorf("normalizeChart() = %v, want %v", got, tt.want)
|
||||||
|
}
|
||||||
|
})
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// mocking helmexec.Interface
|
||||||
|
|
||||||
|
type mockHelmExec struct {
|
||||||
|
charts []string
|
||||||
|
}
|
||||||
|
|
||||||
|
func (helm *mockHelmExec) UpdateDeps(chart string) error {
|
||||||
|
fmt.Println(chart)
|
||||||
|
if strings.Contains(chart, "error") {
|
||||||
|
return errors.New("error")
|
||||||
|
}
|
||||||
|
helm.charts = append(helm.charts, chart)
|
||||||
|
return nil
|
||||||
|
}
|
||||||
|
func (helm *mockHelmExec) SetExtraArgs(args ...string) {
|
||||||
|
return
|
||||||
|
}
|
||||||
|
func (helm *mockHelmExec) AddRepo(name, repository, certfile, keyfile string) error {
|
||||||
|
return nil
|
||||||
|
}
|
||||||
|
func (helm *mockHelmExec) UpdateRepo() error {
|
||||||
|
return nil
|
||||||
|
}
|
||||||
|
func (helm *mockHelmExec) SyncRelease(name, chart string, flags ...string) error {
|
||||||
|
return nil
|
||||||
|
}
|
||||||
|
func (helm *mockHelmExec) DiffRelease(name, chart string, flags ...string) error {
|
||||||
|
return nil
|
||||||
|
}
|
||||||
|
func (helm *mockHelmExec) DeleteRelease(name string) error {
|
||||||
|
return nil
|
||||||
|
}
|
||||||
|
func (helm *mockHelmExec) DecryptSecret(name string) (string, error) {
|
||||||
|
return "", nil
|
||||||
|
}
|
||||||
|
|
||||||
|
func TestHelmState_UpdateDeps(t *testing.T) {
|
||||||
|
state := &HelmState{
|
||||||
|
BaseChartPath: "/src",
|
||||||
|
Releases: []ReleaseSpec{
|
||||||
|
{
|
||||||
|
Chart: "./local",
|
||||||
|
},
|
||||||
|
{
|
||||||
|
Chart: "../local",
|
||||||
|
},
|
||||||
|
{
|
||||||
|
Chart: "../../local",
|
||||||
|
},
|
||||||
|
{
|
||||||
|
Chart: "published",
|
||||||
|
},
|
||||||
|
{
|
||||||
|
Chart: "published/deeper",
|
||||||
|
},
|
||||||
|
{
|
||||||
|
Chart: "./error",
|
||||||
|
},
|
||||||
|
},
|
||||||
|
}
|
||||||
|
want := []string{"/src/local", "/local", "/local"}
|
||||||
|
helm := &mockHelmExec{}
|
||||||
|
errs := state.UpdateDeps(helm)
|
||||||
|
if !reflect.DeepEqual(helm.charts, want) {
|
||||||
|
t.Errorf("HelmState.UpdateDeps() = %v, want %v", helm.charts, want)
|
||||||
|
}
|
||||||
|
if len(errs) != 1 {
|
||||||
|
t.Errorf("HelmState.UpdateDeps() - expected an error, but got: %v", len(errs))
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
|
||||||
Loading…
Reference in New Issue