Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

fake: Add new fake server for testing #112

Merged
merged 2 commits into from
Dec 17, 2024
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
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
}
80 changes: 80 additions & 0 deletions pkg/fake/server.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,80 @@
package fake

import (
"encoding/json"
"net/http"
"net/http/httptest"
"strings"
"testing"

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

type FakeServer struct {
server *httptest.Server
assert *assert.Assertions

Path string
StatusCode int
Group string
ID string
}

func NewFakeServer(t *testing.T, statusCode int, path string) *FakeServer {
s := &FakeServer{
assert: assert.New(t),
Path: path,
StatusCode: statusCode,
}

s.server = httptest.NewServer(http.HandlerFunc(s.handleRequest))

return s
}

func (s *FakeServer) handleRequest(rw http.ResponseWriter, req *http.Request) {
s.assert.Contains([]string{"/v1/pre-reboot", "/v1/steady-state"}, req.URL.String(), "Should request a valid url")
if s.Path != "" {
s.assert.Equal(s.Path, req.URL.String(), "Should use the specified URL")
}

s.assert.Equal(http.MethodPost, req.Method, "Should be POST request")
s.assert.Equal("true", strings.ToLower(req.Header.Get("fleet-lock-protocol")), "fleet-lock-protocol header should be set")

params, err := api.ParseRequest(req.Body)
s.assert.NoError(err, "Request should have the correct format")

if s.Group != "" {
s.assert.Equal(s.Group, params.Client.Group, "Should have expected group")
} else {
s.assert.NotEmpty(params.Client.Group, "Should have group set")
}
if s.ID != "" {
s.assert.Equal(s.ID, params.Client.ID, "Should have expected id")
} else {
s.assert.NotEmpty(params.Client.ID, "Should have id set")
}

rw.WriteHeader(s.StatusCode)
b, err := json.MarshalIndent(api.FleetLockResponse{
Kind: "ok",
Value: "Success",
}, "", " ")
if !s.assert.NoError(err, "Error in fake server: failed to prepare response") {
return
}

_, err = rw.Write(b)
s.assert.NoError(err, "Error in fake server: failed to send response")
}

func (s *FakeServer) URL() string {
return s.server.URL
}

func (s *FakeServer) Close() {
if s != nil && s.server != nil {
s.server.Close()
}
}
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
40 changes: 10 additions & 30 deletions pkg/server/client/client_test.go
Original file line number Diff line number Diff line change
@@ -1,12 +1,10 @@
package client

import (
"encoding/json"
"net/http"
"net/http/httptest"
"strings"
"testing"

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

Expand Down Expand Up @@ -130,35 +128,17 @@ func TestGetAndSet(t *testing.T) {
})
}

func NewFakeServer(t *testing.T, statusCode int, path string) (*FleetlockClient, *httptest.Server) {
assert := assert.New(t)
func NewFakeServer(t *testing.T, statusCode int, path string) (*FleetlockClient, *fake.FakeServer) {
testGroup, testID := "testGroup", "testID"

srv := fake.NewFakeServer(t, statusCode, path)
srv.Group = testGroup
srv.ID = testID

srv := httptest.NewServer(http.HandlerFunc(func(rw http.ResponseWriter, req *http.Request) {
assert.Equal(path, req.URL.String(), "Request use the correct request URL")
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)
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{
Kind: "ok",
Value: "Success",
}, "", " ")
if !assert.NoError(err, "Error in fake server: failed to prepare response") {
return
}

_, err = rw.Write(b)
assert.NoError(err, "Error in fake server: failed to send response")
}))
c := &FleetlockClient{
url: srv.URL,
group: "testGroup",
appID: "testID",
url: srv.URL(),
group: testGroup,
appID: testID,
}
return c, srv
}
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
Loading
Loading