Skip to content

Commit

Permalink
feat #198: added failure reason for verbose stop mode
Browse files Browse the repository at this point in the history
  • Loading branch information
F1bonacc1 committed Aug 10, 2024
1 parent 69f28a0 commit 592c713
Show file tree
Hide file tree
Showing 6 changed files with 181 additions and 33 deletions.
2 changes: 1 addition & 1 deletion src/app/project_interface.go
Original file line number Diff line number Diff line change
Expand Up @@ -23,7 +23,7 @@ type IProject interface {
GetProcessState(name string) (*types.ProcessState, error)
GetProcessesState() (*types.ProcessesState, error)
StopProcess(name string) error
StopProcesses(names []string) ([]string, error)
StopProcesses(names []string) (map[string]string, error)
StartProcess(name string) error
RestartProcess(name string) error
ScaleProcess(name string, scale int) error
Expand Down
14 changes: 9 additions & 5 deletions src/app/project_runner.go
Original file line number Diff line number Diff line change
Expand Up @@ -291,16 +291,20 @@ func (p *ProjectRunner) StopProcess(name string) error {
return err
}

func (p *ProjectRunner) StopProcesses(names []string) ([]string, error) {
stopped := make([]string, 0)
func (p *ProjectRunner) StopProcesses(names []string) (map[string]string, error) {
stopped := make(map[string]string)
successes := 0
for _, name := range names {
if err := p.StopProcess(name); err == nil {
stopped = append(stopped, name)
stopped[name] = "ok"
successes++
} else {
stopped[name] = err.Error()
}
}

if len(stopped) != len(names) {
if len(stopped) == 0 {
if successes != len(names) {
if successes == 0 {
return stopped, fmt.Errorf("no such processes or not running: %v", names)
}
return stopped, fmt.Errorf("failed to stop some processes")
Expand Down
2 changes: 1 addition & 1 deletion src/client/client.go
Original file line number Diff line number Diff line change
Expand Up @@ -113,7 +113,7 @@ func (p *PcClient) StopProcess(name string) error {
return p.stopProcess(name)
}

func (p *PcClient) StopProcesses(names []string) ([]string, error) {
func (p *PcClient) StopProcesses(names []string) (map[string]string, error) {
return p.stopProcesses(names)
}

Expand Down
4 changes: 2 additions & 2 deletions src/client/stop.go
Original file line number Diff line number Diff line change
Expand Up @@ -31,7 +31,7 @@ func (p *PcClient) stopProcess(name string) error {
return fmt.Errorf(respErr.Error)
}

func (p *PcClient) stopProcesses(names []string) ([]string, error) {
func (p *PcClient) stopProcesses(names []string) (map[string]string, error) {
url := fmt.Sprintf("http://%s/processes/stop", p.address)
jsonPayload, err := json.Marshal(names)
if err != nil {
Expand All @@ -48,7 +48,7 @@ func (p *PcClient) stopProcesses(names []string) ([]string, error) {
}
defer resp.Body.Close()
if resp.StatusCode == http.StatusOK || resp.StatusCode == http.StatusMultiStatus {
stopped := []string{}
stopped := map[string]string{}
if err = json.NewDecoder(resp.Body).Decode(&stopped); err != nil {
log.Err(err).Msgf("failed to decode stop processes %v", names)
return stopped, err
Expand Down
67 changes: 43 additions & 24 deletions src/cmd/stop.go
Original file line number Diff line number Diff line change
Expand Up @@ -23,38 +23,57 @@ var stopCmd = &cobra.Command{
if err != nil {
log.Fatal().Err(err).Msgf("failed to stop processes %v", args)
}
missing := findMissingProcesses(args, stopped)
if len(missing) != 0 && !verboseOutput {
fmt.Printf("Successfully stopped some processes but encountered failures for: %s\n",
"'"+strings.Join(missing, `', '`)+`'`)
os.Exit(1)
}
for _, name := range stopped {
fmt.Printf("%s Successfully stopped %s\n", color.GreenString("✓"), name)
}
if len(missing) > 0 {
log.Error().Msgf("failed to stop some processes: %v", missing)
for _, name := range missing {
fmt.Printf("%s Failed to stop %s\n", color.RedString("✘"), name)
}
os.Exit(1)

if verboseOutput {
status, exitCode := prepareVerboseOutput(stopped, args)
fmt.Print(status)
os.Exit(exitCode)
} else {
status, exitCode := prepareConciseOutput(stopped, args)
fmt.Print(status)
os.Exit(exitCode)
}
},
}

func findMissingProcesses(requested, stopped []string) []string {
bMap := make(map[string]bool)
for _, str := range stopped {
bMap[str] = true
func prepareVerboseOutput(stopped map[string]string, processes []string) (output string, exitCode int) {
for _, name := range processes {
status, ok := stopped[name]
if !ok {
log.Error().Msgf("Process %s does not exist", name)
output += fmt.Sprintf("%s Unknown status for process %s\n", color.RedString("✘"), name)
exitCode = 1
continue
}
if status == "ok" {
output += fmt.Sprintf("%s Successfully stopped %s\n", color.GreenString("✓"), name)
} else {
output += fmt.Sprintf("%s Failed to stop %s: %s\n", color.RedString("✘"), name, status)
exitCode = 1
}
}
return output, exitCode
}

missing := []string{}
for _, str := range requested {
if !bMap[str] {
missing = append(missing, str)
func prepareConciseOutput(stopped map[string]string, processes []string) (string, int) {
failed := make([]string, 0)
pass := make([]string, 0)
for _, name := range processes {
if stopped[name] != "ok" {
failed = append(failed, name)
} else {
pass = append(pass, name)
}
}
return missing
if len(pass) == 0 && len(failed) != 0 {
return fmt.Sprintf("Failed to stop: %s\n",
"'"+strings.Join(failed, `', '`)+`'`), 1
} else if len(failed) != 0 && len(pass) != 0 {
return fmt.Sprintf("Successfully stopped %s but encountered failures for: %s\n",
"'"+strings.Join(pass, `', '`)+`'`,
"'"+strings.Join(failed, `', '`)+`'`), 1
}
return fmt.Sprintf("Successfully stopped: %s\n", "'"+strings.Join(pass, `', '`)+`'`), 0
}

func init() {
Expand Down
125 changes: 125 additions & 0 deletions src/cmd/stop_test.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,125 @@
package cmd

import "testing"

func Test_prepareVerboseOutput(t *testing.T) {
type args struct {
status map[string]string
processes []string
}
tests := []struct {
name string
args args
wantOutput string
wantExitCode int
}{
{
name: "success",
args: args{
status: map[string]string{"a": "ok", "b": "ok"},
processes: []string{"a", "b"},
},
wantOutput: "✓ Successfully stopped a\n✓ Successfully stopped b\n",
wantExitCode: 0,
},
{
name: "fail",
args: args{
status: map[string]string{"a": "fail", "b": "fail"},
processes: []string{"a", "b"},
},
wantOutput: "✘ Failed to stop a: fail\n✘ Failed to stop b: fail\n",
wantExitCode: 1,
},
{
name: "fail and success",
args: args{
status: map[string]string{"a": "ok", "b": "fail"},
processes: []string{"a", "b"},
},
wantOutput: "✓ Successfully stopped a\n✘ Failed to stop b: fail\n",
wantExitCode: 1,
},
{
name: "empty status",
args: args{
status: map[string]string{},
processes: []string{"a", "b"},
},
wantOutput: "✘ Unknown status for process a\n✘ Unknown status for process b\n",
wantExitCode: 1,
},
}
for _, tt := range tests {
t.Run(tt.name, func(t *testing.T) {
gotOutput, gotExitCode := prepareVerboseOutput(tt.args.status, tt.args.processes)
if gotOutput != tt.wantOutput {
t.Errorf("prepareVerboseOutput() gotOutput = %v, want %v", gotOutput, tt.wantOutput)
}
if gotExitCode != tt.wantExitCode {
t.Errorf("prepareVerboseOutput() gotExitCode = %v, want %v", gotExitCode, tt.wantExitCode)
}
})
}
}

func Test_prepareConciseOutput(t *testing.T) {
type args struct {
stopped map[string]string
processes []string
}
tests := []struct {
name string
args args
want string
want1 int
}{
{
name: "success",
args: args{
stopped: map[string]string{"a": "ok", "b": "ok"},
processes: []string{"a", "b"},
},
want: "Successfully stopped: 'a', 'b'\n",
want1: 0,
},
{
name: "fail",
args: args{
stopped: map[string]string{"a": "fail", "b": "fail"},
processes: []string{"a", "b"},
},
want: "Failed to stop: 'a', 'b'\n",
want1: 1,
},
{
name: "fail and success",
args: args{
stopped: map[string]string{"a": "ok", "b": "fail"},
processes: []string{"a", "b"},
},
want: "Successfully stopped 'a' but encountered failures for: 'b'\n",
want1: 1,
},
{
name: "empty status",
args: args{
stopped: map[string]string{},
processes: []string{"a", "b"},
},
want: "Failed to stop: 'a', 'b'\n",
want1: 1,
},
}
for _, tt := range tests {
t.Run(tt.name, func(t *testing.T) {
got, got1 := prepareConciseOutput(tt.args.stopped, tt.args.processes)
if got != tt.want {
t.Errorf("prepareConciseOutput() got = %v, want %v", got, tt.want)
}
if got1 != tt.want1 {
t.Errorf("prepareConciseOutput() got1 = %v, want %v", got1, tt.want1)
}
})
}
}

0 comments on commit 592c713

Please sign in to comment.