Skip to content

Commit

Permalink
feat: add instance actions
Browse files Browse the repository at this point in the history
* refactor(rapi client): remove http client abstraction
* refactor: extract instance details into component

Co-authored-by: Julian Geywitz <[email protected]>
Co-authored-by: Rudolph Bott <[email protected]>
  • Loading branch information
geigi and rbott authored Feb 4, 2022
1 parent ac85a2e commit 2b3937a
Show file tree
Hide file tree
Showing 31 changed files with 1,164 additions and 274 deletions.
2 changes: 1 addition & 1 deletion .git-hooks/pre-push
Original file line number Diff line number Diff line change
Expand Up @@ -14,4 +14,4 @@ echo "Executing golang tests..."
cd "$WORK_DIR/api" && go test ./...

echo "Executing yarn linting..."
cd "$WORK_DIR/web" && yarn lint
cd "$WORK_DIR/web" && yarn lint && yarn test --watchAll=false
1 change: 1 addition & 0 deletions .github/workflows/build.yml
Original file line number Diff line number Diff line change
Expand Up @@ -19,6 +19,7 @@ jobs:
run: |
cd web
yarn install
yarn test --watchAll=false
yarn build
- name: Set up Go 1.x
Expand Down
45 changes: 45 additions & 0 deletions api/actions/instance.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,45 @@
package actions

import (
"fmt"
"gnt-cc/rapi_client"
"strconv"
"strings"
)

type rapiActionToMethodMap map[string](func(string, string, interface{}) (rapi_client.Response, error))

type InstanceActions struct {
RAPIClient rapi_client.Client
}

func (actions *InstanceActions) PerformSimpleInstanceAction(clusterName string, instanceName string, rapiAction string) (int, error) {
rapiActionToMethodMapping := rapiActionToMethodMap{
"startup": actions.RAPIClient.Put,
"reboot": actions.RAPIClient.Post,
"shutdown": actions.RAPIClient.Put,
"migrate": actions.RAPIClient.Put,
"failover": actions.RAPIClient.Put,
}

rapiMethod, exists := rapiActionToMethodMapping[rapiAction]

if !exists {
return 0, fmt.Errorf("cannot find rapiClient function for action '%s'", rapiAction)
}

slug := fmt.Sprintf("/2/instances/%s/%s", instanceName, rapiAction)
response, err := rapiMethod(clusterName, slug, nil)

if err != nil {
return 0, err
}

jobID, err := strconv.Atoi(strings.TrimSpace(response.Body))

if err != nil {
return 0, fmt.Errorf("cannot parse RAPI response")
}

return jobID, nil
}
57 changes: 57 additions & 0 deletions api/actions/instance_test.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,57 @@
package actions_test

import (
"errors"
"gnt-cc/actions"
"gnt-cc/mocking"
"gnt-cc/rapi_client"
"testing"

"github.com/stretchr/testify/assert"
"github.com/stretchr/testify/mock"
)

var rapiActions = []string{
"startup",
"reboot",
"shutdown",
}

func TestInstanceMethodReturnsError_WhenRAPIReturnsError(t *testing.T) {
client := mocking.NewRAPIClient()
client.On("Post", "testClusterName", mock.Anything, nil).
Return(rapi_client.Response{}, errors.New("expected error"))
client.On("Put", "testClusterName", mock.Anything, nil).
Return(rapi_client.Response{}, errors.New("expected error"))

actions := actions.InstanceActions{RAPIClient: client}

for _, rapiAction := range rapiActions {
_, err := actions.PerformSimpleInstanceAction("testClusterName", "testInstanceName", rapiAction)
assert.EqualError(t, err, "expected error")
}
}

func TestRAPIEndpointIsCalled_WhenInvokingInstanceMethod(t *testing.T) {
client := mocking.NewRAPIClient()
client.On("Put", "testClusterName", "/2/instances/testInstanceName/startup", nil).
Once().Return(rapi_client.Response{
Body: "423458",
}, nil)
client.On("Post", "testClusterName", "/2/instances/testInstanceName/reboot", nil).
Once().Return(rapi_client.Response{
Body: "423458",
}, nil)
client.On("Put", "testClusterName", "/2/instances/testInstanceName/shutdown", nil).
Once().Return(rapi_client.Response{
Body: "423458",
}, nil)

actions := actions.InstanceActions{RAPIClient: client}

for _, rapiAction := range rapiActions {
jobId, err := actions.PerformSimpleInstanceAction("testClusterName", "testInstanceName", rapiAction)
assert.Nil(t, err)
assert.Equal(t, jobId, 423458)
}
}
67 changes: 67 additions & 0 deletions api/controllers/instance.go
Original file line number Diff line number Diff line change
Expand Up @@ -10,6 +10,7 @@ import (

type InstanceController struct {
Repository instanceRepository
Actions instanceActions
}

// GetAll godoc
Expand Down Expand Up @@ -37,6 +38,72 @@ func (controller *InstanceController) GetAll(c *gin.Context) {
})
}

// Start godoc
// @Summary Start an instance in a given cluster
// @Description ...
// @Produce json
// @Success 200 {object} model.JobIDResponse
// @Router /clusters/{cluster}/instances/{instance}/start [post]
func (controller *InstanceController) Start(c *gin.Context) {
controller.SimpleAction(c, "startup")
}

// Restart godoc
// @Summary Restart an instance in a given cluster
// @Description ...
// @Produce json
// @Success 200 {object} model.JobIDResponse
// @Router /clusters/{cluster}/instances/{instance}/restart [post]
func (controller *InstanceController) Restart(c *gin.Context) {
controller.SimpleAction(c, "reboot")
}

// Shutdown godoc
// @Summary Shutdown an instance in a given cluster
// @Description ...
// @Produce json
// @Success 200 {object} model.JobIDResponse
// @Router /clusters/{cluster}/instances/{instance}/shutdown [post]
func (controller *InstanceController) Shutdown(c *gin.Context) {
controller.SimpleAction(c, "shutdown")
}

// Migrate godoc
// @Summary Migrate an instance in a given cluster
// @Description ...
// @Produce json
// @Success 200 {object} model.JobIDResponse
// @Router /clusters/{cluster}/instances/{instance}/migrate [post]
func (controller *InstanceController) Migrate(c *gin.Context) {
controller.SimpleAction(c, "migrate")
}

// Failover godoc
// @Summary Failover an instance in a given cluster
// @Description ...
// @Produce json
// @Success 200 {object} model.JobIDResponse
// @Router /clusters/{cluster}/instances/{instance}/failover [post]
func (controller *InstanceController) Failover(c *gin.Context) {
controller.SimpleAction(c, "failover")
}

func (controller *InstanceController) SimpleAction(c *gin.Context, action string) {
clusterName := c.Param("cluster")
instanceName := c.Param("instance")

jobID, err := controller.Actions.PerformSimpleInstanceAction(clusterName, instanceName, action)

if err != nil {
abortWithInternalServerError(c, err)
return
}

c.JSON(200, model.JobIDResponse{
JobID: jobID,
})
}

// Get godoc
// @Summary Get an instance in a given cluster
// @Description ...
Expand Down
4 changes: 4 additions & 0 deletions api/controllers/types.go
Original file line number Diff line number Diff line change
Expand Up @@ -17,4 +17,8 @@ type (
GetAll(clusterName string) ([]model.GntJob, error)
Get(clusterName, jobID string) (model.JobResult, error)
}

instanceActions interface {
PerformSimpleInstanceAction(clusterName string, instanceName string, rapiAction string) (int, error)
}
)
5 changes: 4 additions & 1 deletion api/go.mod
Original file line number Diff line number Diff line change
Expand Up @@ -7,14 +7,17 @@ require (
github.com/appleboy/gin-jwt/v2 v2.7.0
github.com/gin-contrib/cors v1.3.1
github.com/gin-gonic/gin v1.7.4
github.com/gofrs/uuid v4.0.0+incompatible // indirect
github.com/gorilla/websocket v1.4.2
github.com/jarcoal/httpmock v1.0.8
github.com/jtblin/go-ldap-client v0.0.0-20170223121919-b73f66626b33
github.com/shopspring/decimal v1.2.0 // indirect
github.com/sirupsen/logrus v1.8.1
github.com/spf13/viper v1.8.1
github.com/stretchr/testify v1.7.0
github.com/swaggo/files v0.0.0-20190704085106-630677cd5c14
github.com/swaggo/gin-swagger v1.3.1
github.com/swaggo/swag v1.7.4
github.com/swaggo/swag v1.7.9
gopkg.in/asn1-ber.v1 v1.0.0-20181015200546-f715ec2f112d // indirect
gopkg.in/ldap.v2 v2.5.1 // indirect
)
Loading

0 comments on commit 2b3937a

Please sign in to comment.