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)
diff diff charts from state file against env (helm diff)
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)
GLOBAL OPTIONS:

View File

@ -59,6 +59,14 @@ func (helm *execer) SyncRelease(name, chart string, flags ...string) error {
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) {
out, err := helm.exec(append([]string{"secrets", "dec", name})...)
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) {
var buffer bytes.Buffer
helm := MockExecer(&buffer, "")

View File

@ -9,6 +9,7 @@ type Interface interface {
UpdateDeps(chart string) error
SyncRelease(name, chart string, flags ...string) error
DiffRelease(name, chart string, flags ...string) error
ReleaseStatus(name string) error
DeleteRelease(name string) error
DecryptSecret(name string) (string, error)

32
main.go
View File

@ -218,6 +218,38 @@ func main() {
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",
Usage: "delete charts from state file (helm delete)",

View File

@ -298,6 +298,49 @@ func (state *HelmState) DiffReleases(helm helmexec.Interface, additionalValues [
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
func (state *HelmState) DeleteReleases(helm helmexec.Interface) []error {
var wg sync.WaitGroup

View File

@ -470,6 +470,7 @@ func Test_normalizeChart(t *testing.T) {
type mockHelmExec struct {
charts []string
repo []string
releases []string
}
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 {
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 {
return nil
}
@ -591,3 +599,50 @@ func TestHelmState_UpdateDeps(t *testing.T) {
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)
}
}