Skip to content

Commit

Permalink
api: Move fleetlock api types to new package
Browse files Browse the repository at this point in the history
Signed-off-by: Heathcliff <[email protected]>
  • Loading branch information
heathcliff26 committed Dec 17, 2024
1 parent d432951 commit 1fc1ee4
Show file tree
Hide file tree
Showing 8 changed files with 86 additions and 78 deletions.
3 changes: 2 additions & 1 deletion pkg/server/client/types.go → pkg/api/types.go
Original file line number Diff line number Diff line change
@@ -1,4 +1,4 @@
package client
package api

type FleetLockRequest struct {
Client FleetLockRequestClient `json:"client_params"`
Expand All @@ -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"`
Expand Down
42 changes: 42 additions & 0 deletions pkg/api/utils.go
Original file line number Diff line number Diff line change
@@ -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
}
16 changes: 9 additions & 7 deletions pkg/server/client/client.go
Original file line number Diff line number Diff line change
Expand Up @@ -4,6 +4,8 @@ import (
"fmt"
"net/http"
"sync"

"github.com/heathcliff26/fleetlock/pkg/api"
)

type FleetlockClient struct {
Expand Down Expand Up @@ -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
Expand Down
5 changes: 3 additions & 2 deletions pkg/server/client/client_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -7,6 +7,7 @@ import (
"strings"
"testing"

"github.com/heathcliff26/fleetlock/pkg/api"
"github.com/stretchr/testify/assert"
)

Expand Down Expand Up @@ -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",
}, "", " ")
Expand Down
38 changes: 0 additions & 38 deletions pkg/server/client/utils.go
Original file line number Diff line number Diff line change
@@ -1,51 +1,13 @@
package client

import (
"bytes"
"encoding/json"
"io"
"log/slog"
"os"
"strings"

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")
Expand Down
22 changes: 11 additions & 11 deletions pkg/server/responses.go
Original file line number Diff line number Diff line change
@@ -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",
}
Expand Down
20 changes: 10 additions & 10 deletions pkg/server/server.go
Original file line number Diff line number Diff line change
Expand Up @@ -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.-]+$"
Expand Down Expand Up @@ -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
Expand Down Expand Up @@ -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)
Expand Down Expand Up @@ -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))
Expand All @@ -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
}
Expand All @@ -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
Expand Down Expand Up @@ -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
Expand All @@ -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))
Expand All @@ -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)
Expand Down
18 changes: 9 additions & 9 deletions pkg/server/server_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -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"
Expand Down Expand Up @@ -297,27 +297,27 @@ 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,
},
}
}

func createFleetLockRequest(group, id string) io.Reader {
body, _ := client.PrepareRequest(group, id)
body, _ := api.PrepareRequest(group, id)
return body
}

Expand All @@ -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
}
Expand Down

0 comments on commit 1fc1ee4

Please sign in to comment.