From 1fc1ee4c7a72afd39c86c6df46bc39f85f6aafe7 Mon Sep 17 00:00:00 2001 From: Heathcliff Date: Tue, 17 Dec 2024 12:26:31 +0100 Subject: [PATCH] api: Move fleetlock api types to new package Signed-off-by: Heathcliff --- pkg/{server/client => api}/types.go | 3 ++- pkg/api/utils.go | 42 +++++++++++++++++++++++++++++ pkg/server/client/client.go | 16 ++++++----- pkg/server/client/client_test.go | 5 ++-- pkg/server/client/utils.go | 38 -------------------------- pkg/server/responses.go | 22 +++++++-------- pkg/server/server.go | 20 +++++++------- pkg/server/server_test.go | 18 ++++++------- 8 files changed, 86 insertions(+), 78 deletions(-) rename pkg/{server/client => api}/types.go (87%) create mode 100644 pkg/api/utils.go diff --git a/pkg/server/client/types.go b/pkg/api/types.go similarity index 87% rename from pkg/server/client/types.go rename to pkg/api/types.go index 8e4aa24f..5a09ca28 100644 --- a/pkg/server/client/types.go +++ b/pkg/api/types.go @@ -1,4 +1,4 @@ -package client +package api type FleetLockRequest struct { Client FleetLockRequestClient `json:"client_params"` @@ -14,6 +14,7 @@ type FleetLockResponse struct { Value string `json:"value"` } +// Not part of the actual api specification type FleetlockHealthResponse struct { Status string `json:"status"` Error string `json:"error"` diff --git a/pkg/api/utils.go b/pkg/api/utils.go new file mode 100644 index 00000000..fad0a979 --- /dev/null +++ b/pkg/api/utils.go @@ -0,0 +1,42 @@ +package api + +import ( + "bytes" + "encoding/json" + "io" +) + +// Parse an http request body and extract the parameters +func ParseRequest(body io.ReadCloser) (FleetLockRequest, error) { + var res FleetLockRequest + err := json.NewDecoder(body).Decode(&res) + if err != nil { + return FleetLockRequest{}, err + } + return res, nil +} + +// Parse an http response body and extract the parameters +func ParseResponse(body io.ReadCloser) (FleetLockResponse, error) { + var res FleetLockResponse + err := json.NewDecoder(body).Decode(&res) + if err != nil { + return FleetLockResponse{}, err + } + return res, nil +} + +// Create a new http request pody based on the provided parameters +func PrepareRequest(group, id string) (io.Reader, error) { + req := FleetLockRequest{ + Client: FleetLockRequestClient{ + ID: id, + Group: group, + }, + } + body, err := json.Marshal(req) + if err != nil { + return nil, err + } + return bytes.NewReader(body), nil +} diff --git a/pkg/server/client/client.go b/pkg/server/client/client.go index 9938f54f..469cc834 100644 --- a/pkg/server/client/client.go +++ b/pkg/server/client/client.go @@ -4,6 +4,8 @@ import ( "fmt" "net/http" "sync" + + "github.com/heathcliff26/fleetlock/pkg/api" ) type FleetlockClient struct { @@ -68,29 +70,29 @@ func (c *FleetlockClient) Release() error { return fmt.Errorf("failed to release lock kind=\"%s\" reason=\"%s\"", res.Kind, res.Value) } -func (c *FleetlockClient) doRequest(path string) (bool, FleetLockResponse, error) { +func (c *FleetlockClient) doRequest(path string) (bool, api.FleetLockResponse, error) { c.mutex.RLock() defer c.mutex.RUnlock() - body, err := PrepareRequest(c.group, c.appID) + body, err := api.PrepareRequest(c.group, c.appID) if err != nil { - return false, FleetLockResponse{}, fmt.Errorf("failed to prepare request body: %v", err) + return false, api.FleetLockResponse{}, fmt.Errorf("failed to prepare request body: %v", err) } req, err := http.NewRequest(http.MethodPost, c.url+path, body) if err != nil { - return false, FleetLockResponse{}, fmt.Errorf("failed to create http post request: %v", err) + return false, api.FleetLockResponse{}, fmt.Errorf("failed to create http post request: %v", err) } req.Header.Set("fleet-lock-protocol", "true") req.Header.Set("Content-Type", "application/json") res, err := http.DefaultClient.Do(req) if err != nil { - return false, FleetLockResponse{}, fmt.Errorf("failed to send request to server: %v", err) + return false, api.FleetLockResponse{}, fmt.Errorf("failed to send request to server: %v", err) } - resBody, err := ParseResponse(res.Body) + resBody, err := api.ParseResponse(res.Body) if err != nil { - return false, FleetLockResponse{}, fmt.Errorf("failed to prepare response body: %v", err) + return false, api.FleetLockResponse{}, fmt.Errorf("failed to prepare response body: %v", err) } return res.StatusCode == http.StatusOK, resBody, nil diff --git a/pkg/server/client/client_test.go b/pkg/server/client/client_test.go index 61da7cdd..2de87e57 100644 --- a/pkg/server/client/client_test.go +++ b/pkg/server/client/client_test.go @@ -7,6 +7,7 @@ import ( "strings" "testing" + "github.com/heathcliff26/fleetlock/pkg/api" "github.com/stretchr/testify/assert" ) @@ -138,13 +139,13 @@ func NewFakeServer(t *testing.T, statusCode int, path string) (*FleetlockClient, assert.Equal(http.MethodPost, req.Method, "Should be POST request") assert.Equal("true", strings.ToLower(req.Header.Get("fleet-lock-protocol")), "fleet-lock-protocol header should be set") - params, err := ParseRequest(req.Body) + params, err := api.ParseRequest(req.Body) assert.NoError(err, "Request should have the correct format") assert.Equal("testGroup", params.Client.Group, "Should have Group set") assert.Equal("testID", params.Client.ID, "Should have ID set") rw.WriteHeader(statusCode) - b, err := json.MarshalIndent(FleetLockResponse{ + b, err := json.MarshalIndent(api.FleetLockResponse{ Kind: "ok", Value: "Success", }, "", " ") diff --git a/pkg/server/client/utils.go b/pkg/server/client/utils.go index 84b8f898..b8b575f1 100644 --- a/pkg/server/client/utils.go +++ b/pkg/server/client/utils.go @@ -1,9 +1,6 @@ package client import ( - "bytes" - "encoding/json" - "io" "log/slog" "os" "strings" @@ -11,41 +8,6 @@ import ( systemdutils "github.com/heathcliff26/fleetlock/pkg/systemd-utils" ) -// Parse an http request body and extract the parameters -func ParseRequest(body io.ReadCloser) (FleetLockRequest, error) { - var res FleetLockRequest - err := json.NewDecoder(body).Decode(&res) - if err != nil { - return FleetLockRequest{}, err - } - return res, nil -} - -// Parse an http response body and extract the parameters -func ParseResponse(body io.ReadCloser) (FleetLockResponse, error) { - var res FleetLockResponse - err := json.NewDecoder(body).Decode(&res) - if err != nil { - return FleetLockResponse{}, err - } - return res, nil -} - -// Create a new http request pody based on the provided parameters -func PrepareRequest(group, id string) (io.Reader, error) { - req := FleetLockRequest{ - Client: FleetLockRequestClient{ - ID: id, - Group: group, - }, - } - body, err := json.Marshal(req) - if err != nil { - return nil, err - } - return bytes.NewReader(body), nil -} - // Read the machine-id from /etc/machine-id func GetMachineID() (string, error) { b, err := os.ReadFile("/etc/machine-id") diff --git a/pkg/server/responses.go b/pkg/server/responses.go index 6396cef4..b5dd2a2a 100644 --- a/pkg/server/responses.go +++ b/pkg/server/responses.go @@ -1,45 +1,45 @@ package server -import "github.com/heathcliff26/fleetlock/pkg/server/client" +import "github.com/heathcliff26/fleetlock/pkg/api" var ( - msgNotFound = client.FleetLockResponse{ + msgNotFound = api.FleetLockResponse{ Kind: "not_found", Value: "The requested url is not found on this server", } - msgWrongMethod = client.FleetLockResponse{ + msgWrongMethod = api.FleetLockResponse{ Kind: "bad_request", Value: "Only accepts POST request", } - msgMissingFleetLockHeader = client.FleetLockResponse{ + msgMissingFleetLockHeader = api.FleetLockResponse{ Kind: "missing_fleetlock_header", Value: "The header fleet-lock-protocol must be set to true", } - msgRequestParseFailed = client.FleetLockResponse{ + msgRequestParseFailed = api.FleetLockResponse{ Kind: "bad_request", Value: "The request json could not be parsed", } - msgInvalidGroupValue = client.FleetLockResponse{ + msgInvalidGroupValue = api.FleetLockResponse{ Kind: "bad_request", Value: "The value of group is invalid or empty. It must conform to \"" + groupValidationPattern + "\"", } - msgEmptyID = client.FleetLockResponse{ + msgEmptyID = api.FleetLockResponse{ Kind: "bad_request", Value: "The value of id is empty", } - msgUnexpectedError = client.FleetLockResponse{ + msgUnexpectedError = api.FleetLockResponse{ Kind: "error", Value: "An unexpected error occured", } - msgSuccess = client.FleetLockResponse{ + msgSuccess = api.FleetLockResponse{ Kind: "success", Value: "The operation was succesfull", } - msgSlotsFull = client.FleetLockResponse{ + msgSlotsFull = api.FleetLockResponse{ Kind: "all_slots_full", Value: "Could not reserve a slot as all slots in the group are currently locked already", } - msgWaitingForNodeDrain = client.FleetLockResponse{ + msgWaitingForNodeDrain = api.FleetLockResponse{ Kind: "waiting_for_node_drain", Value: "The Slot has been reserved, but the node is not yet drained", } diff --git a/pkg/server/server.go b/pkg/server/server.go index 6ed5feb7..88f23b80 100644 --- a/pkg/server/server.go +++ b/pkg/server/server.go @@ -7,9 +7,9 @@ import ( "regexp" "strings" + "github.com/heathcliff26/fleetlock/pkg/api" "github.com/heathcliff26/fleetlock/pkg/k8s" lockmanager "github.com/heathcliff26/fleetlock/pkg/lock-manager" - "github.com/heathcliff26/fleetlock/pkg/server/client" ) const groupValidationPattern = "^[a-zA-Z0-9.-]+$" @@ -44,7 +44,7 @@ func NewServer(cfg *ServerConfig, groups lockmanager.Groups, storageCfg lockmana func (s *Server) requestHandler(rw http.ResponseWriter, req *http.Request) { slog.Debug("Received request", slog.String("method", req.Method), slog.String("uri", req.RequestURI), slog.String("remote", ReadUserIP(req))) - var handleFunc func(http.ResponseWriter, client.FleetLockRequest) + var handleFunc func(http.ResponseWriter, api.FleetLockRequest) switch req.URL.String() { case "/v1/pre-reboot": handleFunc = s.handleReserve @@ -73,7 +73,7 @@ func (s *Server) requestHandler(rw http.ResponseWriter, req *http.Request) { return } - params, err := client.ParseRequest(req.Body) + params, err := api.ParseRequest(req.Body) if err != nil { slog.Debug("Failed to parse request", "error", err, slog.String("remote", ReadUserIP(req))) rw.WriteHeader(http.StatusBadRequest) @@ -101,7 +101,7 @@ func (s *Server) requestHandler(rw http.ResponseWriter, req *http.Request) { // Handle requests to reserve a slot // // URL: /v1/pre-reboot -func (s *Server) handleReserve(rw http.ResponseWriter, params client.FleetLockRequest) { +func (s *Server) handleReserve(rw http.ResponseWriter, params api.FleetLockRequest) { ok, err := s.lm.Reserve(params.Client.Group, params.Client.ID) if err != nil { slog.Error("Failed to reserve slot", "error", err, slog.String("group", params.Client.Group), slog.String("id", params.Client.ID)) @@ -126,7 +126,7 @@ func (s *Server) handleReserve(rw http.ResponseWriter, params client.FleetLockRe // Handle requests to release a slot // // URL: /v1/steady-state -func (s *Server) handleRelease(rw http.ResponseWriter, params client.FleetLockRequest) { +func (s *Server) handleRelease(rw http.ResponseWriter, params api.FleetLockRequest) { if s.k8s != nil && !s.uncordonNode(rw, params) { return } @@ -142,9 +142,9 @@ func (s *Server) handleRelease(rw http.ResponseWriter, params client.FleetLockRe sendResponse(rw, msgSuccess) } -// Drain the node after reservation and before sending success to client. +// Drain the node after reservation and before sending success to api. // Requires k8s client to be non-nil. -func (s *Server) drainNode(rw http.ResponseWriter, params client.FleetLockRequest) bool { +func (s *Server) drainNode(rw http.ResponseWriter, params api.FleetLockRequest) bool { node, ok := s.matchNodeToId(rw, params) if node == "" { return ok @@ -178,7 +178,7 @@ func (s *Server) drainNode(rw http.ResponseWriter, params client.FleetLockReques // Uncordon the node before release. // Requires k8s client to be non-nil. -func (s *Server) uncordonNode(rw http.ResponseWriter, params client.FleetLockRequest) bool { +func (s *Server) uncordonNode(rw http.ResponseWriter, params api.FleetLockRequest) bool { node, ok := s.matchNodeToId(rw, params) if node == "" { return ok @@ -195,7 +195,7 @@ func (s *Server) uncordonNode(rw http.ResponseWriter, params client.FleetLockReq return true } -func (s *Server) matchNodeToId(rw http.ResponseWriter, params client.FleetLockRequest) (string, bool) { +func (s *Server) matchNodeToId(rw http.ResponseWriter, params api.FleetLockRequest) (string, bool) { node, err := s.k8s.FindNodeByZincatiID(params.Client.ID) if err != nil { slog.Error("An error occured when matching client id to node", "error", err, slog.String("group", params.Client.Group), slog.String("id", params.Client.ID)) @@ -215,7 +215,7 @@ func (s *Server) matchNodeToId(rw http.ResponseWriter, params client.FleetLockRe // URL: /healthz func (s *Server) handleHealthCheck(rw http.ResponseWriter, _ *http.Request) { rw.Header().Set("Content-Type", "application/json") - status := client.FleetlockHealthResponse{ + status := api.FleetlockHealthResponse{ Status: "ok", } sendResponse(rw, status) diff --git a/pkg/server/server_test.go b/pkg/server/server_test.go index 946cef31..770fa2d2 100644 --- a/pkg/server/server_test.go +++ b/pkg/server/server_test.go @@ -11,10 +11,10 @@ import ( "testing" "time" + "github.com/heathcliff26/fleetlock/pkg/api" "github.com/heathcliff26/fleetlock/pkg/k8s" lockmanager "github.com/heathcliff26/fleetlock/pkg/lock-manager" "github.com/heathcliff26/fleetlock/pkg/lock-manager/storage/memory" - "github.com/heathcliff26/fleetlock/pkg/server/client" "github.com/stretchr/testify/assert" v1 "k8s.io/api/core/v1" @@ -297,19 +297,19 @@ func TestHealthCheck(t *testing.T) { assert.Equal(http.StatusOK, rr.Result().StatusCode, "Health Check should return with 200") assert.Equal("application/json", rr.Header().Get("Content-Type"), "Content type should be json") - var res client.FleetlockHealthResponse + var res api.FleetlockHealthResponse err := json.NewDecoder(rr.Result().Body).Decode(&res) assert.NoError(err, "Response should be parsable") - expectedRes := client.FleetlockHealthResponse{ + expectedRes := api.FleetlockHealthResponse{ Status: "ok", } assert.Equal(expectedRes, res, "Response should match") } -func newFleetlockRequest(group, id string) client.FleetLockRequest { - return client.FleetLockRequest{ - Client: client.FleetLockRequestClient{ +func newFleetlockRequest(group, id string) api.FleetLockRequest { + return api.FleetLockRequest{ + Client: api.FleetLockRequestClient{ ID: id, Group: group, }, @@ -317,7 +317,7 @@ func newFleetlockRequest(group, id string) client.FleetLockRequest { } func createFleetLockRequest(group, id string) io.Reader { - body, _ := client.PrepareRequest(group, id) + body, _ := api.PrepareRequest(group, id) return body } @@ -329,10 +329,10 @@ func createRequest(target, group, id string) *http.Request { return req } -func parseResponse(rr *httptest.ResponseRecorder) (*http.Response, client.FleetLockResponse, error) { +func parseResponse(rr *httptest.ResponseRecorder) (*http.Response, api.FleetLockResponse, error) { res := rr.Result() - response, err := client.ParseResponse(res.Body) + response, err := api.ParseResponse(res.Body) return res, response, err }