feat: add stop-remote to cli

This commit is contained in:
Marc 2024-10-04 20:28:09 -04:00
parent a6c937b565
commit 4cad5e875d
Signed by: marc
GPG key ID: 048E042F22B5DC79
4 changed files with 110 additions and 6 deletions

View file

@ -4,25 +4,63 @@
package cli package cli
import ( import (
"context"
"fmt" "fmt"
"github.com/spf13/cobra" "github.com/spf13/cobra"
service "spud/service" service "spud/service"
webclient "spud/webclient"
) )
func getStopCommand() *cobra.Command { func getStopCommand() *cobra.Command {
type ParsedFlags struct {
serviceName string
daemonHost string
daemonPort int
}
stop := &cobra.Command{ stop := &cobra.Command{
Use: "stop [service-name]", Use: "stop [service-name]",
Short: "Stops a running service and all of its containers.", Short: "Stops a running service and all of its containers.",
RunE: func(cmd *cobra.Command, args []string) error { PersistentPreRunE: func(cmd *cobra.Command, args []string) error {
if len(args) == 0 { var serviceName, host string
return fmt.Errorf("Must specify a service using '--service'") var port int
var err error
if host, err = cmd.PersistentFlags().GetString("host"); err != nil {
return err
} }
serviceName := args[0] if port, err = cmd.PersistentFlags().GetInt("port"); err != nil {
return err
}
return service.NewPodmanServiceManager().Stop(serviceName) if host != "" && port == 0 || host == "" && port != 0 {
return fmt.Errorf("Invalid flags: host and port must be defined together or not at all.")
}
if len(args) == 0 {
return fmt.Errorf("Must provide a service name.")
}
serviceName = args[0]
cmd.SetContext(context.WithValue(cmd.Context(), "flags", ParsedFlags{serviceName: serviceName, daemonHost: host, daemonPort: port}))
return nil
},
RunE: func(cmd *cobra.Command, args []string) error {
flags := cmd.Context().Value("flags").(ParsedFlags)
if flags.daemonHost != "" && flags.daemonPort != 0 {
webclient.NewWebClient(flags.daemonHost, flags.daemonPort).StopService(flags.serviceName)
return nil
}
return service.NewPodmanServiceManager().Stop(flags.serviceName)
}, },
} }
stop.PersistentFlags().StringP("host", "H", "", "If specified, host where the daemon lives.")
stop.PersistentFlags().IntP("port", "p", 0, "Port on the daemon host.")
return stop return stop
} }

View file

@ -1,10 +1,12 @@
package cli package cli
import ( import (
"fmt"
"github.com/spf13/cobra"
"testing" "testing"
) )
func TestCliStopRequiresAServiceName(t *testing.T) { func Test_StopServiceCli_StopRequiresAServiceName(t *testing.T) {
cli := GetCli() cli := GetCli()
cli.SetArgs([]string{"stop"}) cli.SetArgs([]string{"stop"})
@ -15,3 +17,36 @@ func TestCliStopRequiresAServiceName(t *testing.T) {
t.Error("Expected error, got nil.") t.Error("Expected error, got nil.")
} }
} }
func Test_StopServiceCli_ErrorIfHostAndPortNotProvidedTogether(t *testing.T) {
inputs := [][]string{
{"stop", "-H", "host"},
{"stop", "-p", "9999"},
}
for _, input := range inputs {
t.Run(fmt.Sprintf("%+v", input), func(t *testing.T) {
cli := GetCli()
cli.SetArgs(input)
stopCommand, _, _ := cli.Find([]string{"stop"})
previousPreRun := stopCommand.PersistentPreRunE
stopCommand.PersistentPreRunE = func(cmd *cobra.Command, args []string) error {
if err := previousPreRun(cmd, args); err == nil {
t.Errorf("Expected error, got nil.")
}
return nil
}
stopCommand.RunE = func(cmd *cobra.Command, args []string) error {
return nil
}
cli.Execute()
})
}
}

View file

@ -26,6 +26,11 @@ func (d *DummyHttpClient) Post(url string, contentType string, data io.Reader) (
return nil, nil return nil, nil
} }
func (d *DummyHttpClient) Do(request *http.Request) (*http.Response, error) {
d.requests = append(d.requests, RecordedRequest{method: request.Method, url: request.URL.String(), contentType: "", data: request.Body})
return nil, nil
}
func Test_WebClient_GetBaseUrlGetsUrlFromHostPort(t *testing.T) { func Test_WebClient_GetBaseUrlGetsUrlFromHostPort(t *testing.T) {
c := NewWebClient("http://host", 9999) c := NewWebClient("http://host", 9999)
@ -71,3 +76,21 @@ func Test_WebClient_CreateServiceSendsDefinition(t *testing.T) {
t.Errorf("Unexpected data: %s != %s", actualDef.String(), string(expectedDef)) t.Errorf("Unexpected data: %s != %s", actualDef.String(), string(expectedDef))
} }
} }
func Test_WebClient_StopServiceDeletesToDaemon(t *testing.T) {
c := NewWebClient("http://host", 9999)
httpClient := &DummyHttpClient{}
c.httpClient = httpClient
serviceName := "test-service"
c.StopService(serviceName)
if len(httpClient.requests) != 1 || httpClient.requests[0].method != http.MethodDelete {
t.Errorf("Expected one DELETE request, got none.")
}
expected := c.getBaseUrl() + "/service/" + serviceName + "/"
if httpClient.requests[0].url != expected {
t.Errorf("Expected url to be %s, got %s.", expected, httpClient.requests[0].url)
}
}

View file

@ -12,6 +12,7 @@ import (
type HttpClient interface { type HttpClient interface {
Post(string, string, io.Reader) (*http.Response, error) Post(string, string, io.Reader) (*http.Response, error)
Do(*http.Request) (*http.Response, error)
} }
type WebClient struct { type WebClient struct {
@ -43,3 +44,10 @@ func (c WebClient) CreateService(def service_definition.ServiceDefinition) error
return e return e
} }
func (c WebClient) StopService(name string) error {
req, _ := http.NewRequest(http.MethodDelete, c.getBaseUrl()+"/service/"+name+"/", nil)
_, e := c.httpClient.Do(req)
return e
}