feat: add service start/stop via daemon api

This commit is contained in:
Marc 2024-10-02 23:47:33 -04:00
parent 01df6c4dd5
commit d51031a875
Signed by: marc
GPG key ID: 048E042F22B5DC79
2 changed files with 116 additions and 14 deletions

View file

@ -2,28 +2,58 @@ package daemon
import ( import (
"context" "context"
"encoding/json"
"net/http" "net/http"
service "spud/service"
service_definition "spud/service_definition"
) )
func GetApiRoutes() map[string]HandlerFuncWithContext { func GetApiRoutes() map[string]HandlerFuncWithContext {
return map[string]HandlerFuncWithContext{ return map[string]HandlerFuncWithContext{
"/service": ServiceList, "/service/": ServiceList,
"/service/{serviceName}/": ServiceDetails,
} }
} }
type ServiceListPayload struct {
Definition service_definition.ServiceDefinition `json:"definition"`
}
func handleServiceListPost(w http.ResponseWriter, r *http.Request, c context.Context) {
client := c.Value("client").(service.ServiceClient)
var p ServiceListPayload
json.NewDecoder(r.Body).Decode(&p)
client.Create(p.Definition)
w.WriteHeader(201)
json.NewEncoder(w).Encode(p.Definition)
}
func handleServiceDetailDelete(w http.ResponseWriter, r *http.Request, c context.Context) {
client := c.Value("client").(service.ServiceClient)
serviceName := r.PathValue("serviceName")
client.Stop(serviceName)
w.WriteHeader(204)
}
func handleNotImplemented(w http.ResponseWriter) {
w.WriteHeader(501)
}
func ServiceList(w http.ResponseWriter, r *http.Request, c context.Context) { func ServiceList(w http.ResponseWriter, r *http.Request, c context.Context) {
var returnPayload []byte
var returnCode int
switch r.Method { switch r.Method {
case "POST": case http.MethodPost:
returnPayload = []byte("Create service: Not implemented") handleServiceListPost(w, r, c)
returnCode = 501
default: default:
returnPayload = []byte("Not implemented") handleNotImplemented(w)
returnCode = 501 }
}
func ServiceDetails(w http.ResponseWriter, r *http.Request, c context.Context) {
switch r.Method {
case http.MethodDelete:
handleServiceDetailDelete(w, r, c)
default:
handleNotImplemented(w)
} }
w.WriteHeader(returnCode)
w.Write(returnPayload)
} }

View file

@ -4,13 +4,48 @@ import (
"context" "context"
"net/http" "net/http"
"net/http/httptest" "net/http/httptest"
service_definition "spud/service_definition"
"testing" "testing"
) )
type MockClient struct {
calls struct {
Create []service_definition.ServiceDefinition
Stop []string
}
}
func (c *MockClient) Create(s service_definition.ServiceDefinition) {
c.calls.Create = append(c.calls.Create, s)
}
func (c *MockClient) Stop(n string) {
c.calls.Stop = append(c.calls.Stop, n)
}
func TestServiceListPostCreatesService(t *testing.T) {
mockClient := &MockClient{}
daemonContext := context.WithValue(context.Background(), "client", mockClient)
req := httptest.NewRequest(http.MethodPost, "/service/", nil)
resp := httptest.NewRecorder()
ServiceList(resp, req, daemonContext)
response := resp.Result()
if response.StatusCode != 201 {
t.Errorf("Expected status 201, got %d", response.StatusCode)
}
if len(mockClient.calls.Create) != 1 {
t.Error("Expected a call to Create")
}
}
func TestServiceListUnsupportedMethods(t *testing.T) { func TestServiceListUnsupportedMethods(t *testing.T) {
for _, method := range []string{http.MethodGet, http.MethodPut, http.MethodHead} { for _, method := range []string{http.MethodGet, http.MethodPut, http.MethodHead, http.MethodDelete} {
t.Run(method, func(t *testing.T) { t.Run(method, func(t *testing.T) {
req := httptest.NewRequest(method, "/service", nil) req := httptest.NewRequest(method, "/service/", nil)
resp := httptest.NewRecorder() resp := httptest.NewRecorder()
ServiceList(resp, req, context.Background()) ServiceList(resp, req, context.Background())
@ -24,3 +59,40 @@ func TestServiceListUnsupportedMethods(t *testing.T) {
} }
} }
func TestServiceDetailsDeleteStopsService(t *testing.T) {
mockClient := &MockClient{}
daemonContext := context.WithValue(context.Background(), "client", mockClient)
req := httptest.NewRequest(http.MethodDelete, "/service/service-name/", nil)
resp := httptest.NewRecorder()
ServiceDetails(resp, req, daemonContext)
response := resp.Result()
if response.StatusCode != 204 {
t.Errorf("Expected status 204, got %d", response.StatusCode)
}
if len(mockClient.calls.Stop) != 1 {
t.Error("Expected a call to Stop")
}
}
func TestServiceDetailUnsupportedMethods(t *testing.T) {
for _, method := range []string{http.MethodGet, http.MethodPost, http.MethodPut, http.MethodHead} {
t.Run(method, func(t *testing.T) {
req := httptest.NewRequest(method, "/service/service-name/", nil)
resp := httptest.NewRecorder()
ServiceDetails(resp, req, context.Background())
response := resp.Result()
if response.StatusCode != 501 {
t.Errorf("Expected status 501, got %d.", response.StatusCode)
}
})
}
}