Skip to content

Commit

Permalink
Add /v2/dqlite/remove endpoint (#55)
Browse files Browse the repository at this point in the history
  • Loading branch information
HomayoonAlimohammadi authored Sep 30, 2024
1 parent 664ceaa commit 5083ae8
Show file tree
Hide file tree
Showing 11 changed files with 231 additions and 49 deletions.
21 changes: 21 additions & 0 deletions pkg/api/v2/register.go
Original file line number Diff line number Diff line change
Expand Up @@ -53,4 +53,25 @@ func (a *API) RegisterServer(server *http.ServeMux, middleware func(f http.Handl
}
httputil.Response(w, map[string]string{"status": "OK"})
}))

// POST v2/dqlite/remove
server.HandleFunc(fmt.Sprintf("%s/dqlite/remove", HTTPPrefix), middleware(func(w http.ResponseWriter, r *http.Request) {
if r.Method != http.MethodPost {
w.WriteHeader(http.StatusMethodNotAllowed)
return
}

req := RemoveFromDqliteRequest{}
if err := httputil.UnmarshalJSON(r, &req); err != nil {
httputil.Error(w, http.StatusBadRequest, fmt.Errorf("failed to unmarshal JSON: %w", err))
return
}

if rc, err := a.RemoveFromDqlite(r.Context(), req); err != nil {
httputil.Error(w, rc, fmt.Errorf("failed to remove from dqlite: %w", err))
return
}

httputil.Response(w, nil)
}))
}
24 changes: 24 additions & 0 deletions pkg/api/v2/remove.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,24 @@
package v2

import (
"context"
"fmt"
"net/http"

snaputil "github.com/canonical/microk8s-cluster-agent/pkg/snap/util"
)

// RemoveFromDqliteRequest represents a request to remove a node from the dqlite cluster.
type RemoveFromDqliteRequest struct {
// RemoveEndpoint is the endpoint of the node to remove from the dqlite cluster.
RemoveEndpoint string `json:"removeEndpoint"`
}

// RemoveFromDqlite implements the "POST /v2/dqlite/remove" endpoint and removes a node from the dqlite cluster.
func (a *API) RemoveFromDqlite(ctx context.Context, req RemoveFromDqliteRequest) (int, error) {
if err := snaputil.RemoveNodeFromDqlite(ctx, a.Snap, req.RemoveEndpoint); err != nil {
return http.StatusInternalServerError, fmt.Errorf("failed to remove node from dqlite: %w", err)
}

return http.StatusOK, nil
}
42 changes: 42 additions & 0 deletions pkg/api/v2/remove_test.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,42 @@
package v2_test

import (
"context"
"errors"
"net/http"
"testing"

. "github.com/onsi/gomega"

v2 "github.com/canonical/microk8s-cluster-agent/pkg/api/v2"
"github.com/canonical/microk8s-cluster-agent/pkg/snap/mock"
)

func TestRemove(t *testing.T) {
t.Run("RemoveFails", func(t *testing.T) {
cmdErr := errors.New("failed to run command")
apiv2 := &v2.API{
Snap: &mock.Snap{
RunCommandErr: cmdErr,
},
}

rc, err := apiv2.RemoveFromDqlite(context.Background(), v2.RemoveFromDqliteRequest{RemoveEndpoint: "1.1.1.1:1234"})

g := NewWithT(t)
g.Expect(err).To(MatchError(cmdErr))
g.Expect(rc).To(Equal(http.StatusInternalServerError))
})

t.Run("RemovesSuccessfully", func(t *testing.T) {
apiv2 := &v2.API{
Snap: &mock.Snap{},
}

rc, err := apiv2.RemoveFromDqlite(context.Background(), v2.RemoveFromDqliteRequest{RemoveEndpoint: "1.1.1.1:1234"})

g := NewWithT(t)
g.Expect(err).ToNot(HaveOccurred())
g.Expect(rc).To(Equal(http.StatusOK))
})
}
10 changes: 10 additions & 0 deletions pkg/snap/interface.go
Original file line number Diff line number Diff line change
Expand Up @@ -7,6 +7,16 @@ import (

// Snap is how the cluster agent interacts with the snap.
type Snap interface {
// GetSnapPath returns the path to a file or directory in the snap directory.
GetSnapPath(parts ...string) string
// GetSnapDataPath returns the path to a file or directory in the snap's data directory.
GetSnapDataPath(parts ...string) string
// GetSnapCommonPath returns the path to a file or directory in the snap's common directory.
GetSnapCommonPath(parts ...string) string

// RunCommand runs a shell command.
RunCommand(ctx context.Context, commands ...string) error

// GetGroupName is the group microk8s is using.
// The group name is "microk8s" for classic snaps and "snap_microk8s" for strict snaps.
GetGroupName() string
Expand Down
34 changes: 34 additions & 0 deletions pkg/snap/mock/mock.go
Original file line number Diff line number Diff line change
Expand Up @@ -4,6 +4,7 @@ import (
"context"
"fmt"
"io"
"path/filepath"
"strings"

"github.com/canonical/microk8s-cluster-agent/pkg/snap"
Expand All @@ -23,8 +24,20 @@ type JoinClusterCall struct {
Worker bool
}

// RunCommandCall contains the arguments passed to a specific call of the RunCommand method.
type RunCommandCall struct {
Commands []string
}

// Snap is a generic mock for the snap.Snap interface.
type Snap struct {
SnapDir string
SnapDataDir string
SnapCommonDir string

RunCommandCalledWith []RunCommandCall
RunCommandErr error

GroupName string

EnableAddonCalledWith []string
Expand Down Expand Up @@ -88,6 +101,27 @@ type Snap struct {
EtcdCA, EtcdCert, EtcdKey string
}

// GetSnapPath is a mock implementation for the snap.Snap interface.
func (s *Snap) GetSnapPath(parts ...string) string {
return filepath.Join(append([]string{s.SnapDir}, parts...)...)
}

// GetSnapDataPath is a mock implementation for the snap.Snap interface.
func (s *Snap) GetSnapDataPath(parts ...string) string {
return filepath.Join(append([]string{s.SnapDataDir}, parts...)...)
}

// GetSnapCommonPath is a mock implementation for the snap.Snap interface.
func (s *Snap) GetSnapCommonPath(parts ...string) string {
return filepath.Join(append([]string{s.SnapCommonDir}, parts...)...)
}

// RunCommand is a mock implementation for the snap.Snap interface.
func (s *Snap) RunCommand(_ context.Context, commands ...string) error {
s.RunCommandCalledWith = append(s.RunCommandCalledWith, RunCommandCall{Commands: commands})
return s.RunCommandErr
}

// GetGroupName is a mock implementation for the snap.Snap interface.
func (s *Snap) GetGroupName() string {
return s.GroupName
Expand Down
Loading

0 comments on commit 5083ae8

Please sign in to comment.