Merge pull request #70 from cmeury/status-command

status command retrieves release status
This commit is contained in:
KUOKA Yusuke 2018-04-27 22:09:12 +09:00 committed by GitHub
commit 44241f675f
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
7 changed files with 152 additions and 2 deletions

View File

@ -134,6 +134,7 @@ COMMANDS:
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 and local chart deps) sync sync all resources from state file (repos, charts and local chart deps)
status retrieve status of releases in state file
delete delete charts from state file (helm delete) delete delete charts from state file (helm delete)
GLOBAL OPTIONS: GLOBAL OPTIONS:

View File

@ -59,6 +59,14 @@ func (helm *execer) SyncRelease(name, chart string, flags ...string) error {
return err return err
} }
func (helm *execer) ReleaseStatus(name string) error {
out, err := helm.exec(append([]string{"status", name})...)
if helm.writer != nil {
helm.writer.Write(out)
}
return err
}
func (helm *execer) DecryptSecret(name string) (string, error) { func (helm *execer) DecryptSecret(name string) (string, error) {
out, err := helm.exec(append([]string{"secrets", "dec", name})...) out, err := helm.exec(append([]string{"secrets", "dec", name})...)
helm.write(out) helm.write(out)

View File

@ -155,6 +155,16 @@ func Test_DeleteRelease(t *testing.T) {
} }
} }
func Test_ReleaseStatus(t *testing.T) {
var buffer bytes.Buffer
helm := MockExecer(&buffer, "dev")
helm.ReleaseStatus("myRelease")
expected := "exec: helm status myRelease --kube-context dev\n"
if buffer.String() != expected {
t.Errorf("helmexec.ReleaseStatus()\nactual = %v\nexpect = %v", buffer.String(), expected)
}
}
func Test_exec(t *testing.T) { func Test_exec(t *testing.T) {
var buffer bytes.Buffer var buffer bytes.Buffer
helm := MockExecer(&buffer, "") helm := MockExecer(&buffer, "")

View File

@ -9,6 +9,7 @@ type Interface interface {
UpdateDeps(chart string) 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
ReleaseStatus(name string) error
DeleteRelease(name string) error DeleteRelease(name string) error
DecryptSecret(name string) (string, error) DecryptSecret(name string) (string, error)

32
main.go
View File

@ -218,6 +218,38 @@ func main() {
return clean(state, errs) return clean(state, errs)
}, },
}, },
{
Name: "status",
Usage: "retrieve status of releases in state file",
Flags: []cli.Flag{
cli.IntFlag{
Name: "concurrency",
Value: 0,
Usage: "maximum number of concurrent helm processes to run, 0 is unlimited",
},
cli.StringFlag{
Name: "args",
Value: "",
Usage: "pass args to helm exec",
},
},
Action: func(c *cli.Context) error {
state, helm, err := before(c)
if err != nil {
return err
}
workers := c.Int("concurrency")
args := c.String("args")
if len(args) > 0 {
helm.SetExtraArgs(strings.Split(args, " ")...)
}
errs := state.ReleaseStatuses(helm, workers)
return clean(state, errs)
},
},
{ {
Name: "delete", Name: "delete",
Usage: "delete charts from state file (helm delete)", Usage: "delete charts from state file (helm delete)",

View File

@ -298,6 +298,49 @@ func (state *HelmState) DiffReleases(helm helmexec.Interface, additionalValues [
return nil return nil
} }
func (state *HelmState) ReleaseStatuses(helm helmexec.Interface, workerLimit int) []error {
var errs []error
jobQueue := make(chan ReleaseSpec)
doneQueue := make(chan bool)
errQueue := make(chan error)
if workerLimit < 1 {
workerLimit = len(state.Releases)
}
for w := 1; w <= workerLimit; w++ {
go func() {
for release := range jobQueue {
if err := helm.ReleaseStatus(release.Name); err != nil {
errQueue <- err
}
doneQueue <- true
}
}()
}
go func() {
for _, release := range state.Releases {
jobQueue <- release
}
close(jobQueue)
}()
for i := 0; i < len(state.Releases); {
select {
case err := <-errQueue:
errs = append(errs, err)
case <-doneQueue:
i++
}
}
if len(errs) != 0 {
return errs
}
return nil
}
// DeleteReleases wrapper for executing helm delete on the releases // DeleteReleases wrapper for executing helm delete on the releases
func (state *HelmState) DeleteReleases(helm helmexec.Interface) []error { func (state *HelmState) DeleteReleases(helm helmexec.Interface) []error {
var wg sync.WaitGroup var wg sync.WaitGroup

View File

@ -468,8 +468,9 @@ func Test_normalizeChart(t *testing.T) {
// mocking helmexec.Interface // mocking helmexec.Interface
type mockHelmExec struct { type mockHelmExec struct {
charts []string charts []string
repo []string repo []string
releases []string
} }
func (helm *mockHelmExec) UpdateDeps(chart string) error { func (helm *mockHelmExec) UpdateDeps(chart string) error {
@ -496,6 +497,13 @@ func (helm *mockHelmExec) SyncRelease(name, chart string, flags ...string) error
func (helm *mockHelmExec) DiffRelease(name, chart string, flags ...string) error { func (helm *mockHelmExec) DiffRelease(name, chart string, flags ...string) error {
return nil return nil
} }
func (helm *mockHelmExec) ReleaseStatus(release string) error {
if strings.Contains(release, "error") {
return errors.New("error")
}
helm.releases = append(helm.releases, release)
return nil
}
func (helm *mockHelmExec) DeleteRelease(name string) error { func (helm *mockHelmExec) DeleteRelease(name string) error {
return nil return nil
} }
@ -591,3 +599,50 @@ func TestHelmState_UpdateDeps(t *testing.T) {
t.Errorf("HelmState.UpdateDeps() - no errors, but got: %v", len(errs)) t.Errorf("HelmState.UpdateDeps() - no errors, but got: %v", len(errs))
} }
} }
func TestHelmState_ReleaseStatuses(t *testing.T) {
tests := []struct {
name string
releases []ReleaseSpec
helm *mockHelmExec
want []string
wantErr bool
}{
{
name: "happy path",
releases: []ReleaseSpec{
{
Name: "releaseA",
},
},
helm: &mockHelmExec{},
want: []string{"releaseA"},
},
{
name: "happy path",
releases: []ReleaseSpec{
{
Name: "error",
},
},
helm: &mockHelmExec{},
wantErr: true,
},
}
for _, tt := range tests {
i := func(t *testing.T) {
state := &HelmState{
Releases: tt.releases,
}
errs := state.ReleaseStatuses(tt.helm, 1)
if (errs != nil) != tt.wantErr {
t.Errorf("ReleaseStatuses() for %s error = %v, wantErr %v", tt.name, errs, tt.wantErr)
return
}
if !reflect.DeepEqual(tt.helm.releases, tt.want) {
t.Errorf("HelmState.ReleaseStatuses() for [%s] = %v, want %v", tt.name, tt.helm.releases, tt.want)
}
}
t.Run(tt.name, i)
}
}