Skip to content

Commit

Permalink
feat: add MicroVM cli command
Browse files Browse the repository at this point in the history
  • Loading branch information
evaldas-kazakas committed Feb 4, 2025
1 parent 844fba6 commit 26b1f56
Show file tree
Hide file tree
Showing 16 changed files with 273 additions and 16 deletions.
20 changes: 20 additions & 0 deletions client.go
Original file line number Diff line number Diff line change
Expand Up @@ -294,3 +294,23 @@ func (c *Client) Reload(ctx context.Context) (*Response, error) {

return c.do(req, nil)
}

// ListMicroVMs returns a list of MicroVM(s) inside the specified pool.
func (c *Client) ListMicroVMs(ctx context.Context, pool string) (*MicroVMs, *Response, error) {
req, err := c.newRequestWithContext(ctx, "GET", fmt.Sprintf("/api/v1/pools/%s/microvms", pool), nil)
if err != nil {
return nil, nil, err
}

type Root struct {
MicroVMs MicroVMs `json:"micro_vms"`
}

var root Root
rsp, err := c.do(req, &root)
if err != nil {
return nil, rsp, err
}

return &root.MicroVMs, rsp, nil
}
4 changes: 4 additions & 0 deletions commands/cmd.go
Original file line number Diff line number Diff line change
Expand Up @@ -21,6 +21,7 @@ type fireactionsClient interface {
ResumePool(ctx context.Context, name string) (*fireactions.Response, error)
ScalePool(ctx context.Context, name string) (*fireactions.Response, error)
Reload(ctx context.Context) (*fireactions.Response, error)
ListMicroVMs(ctx context.Context, pool string) (*fireactions.MicroVMs, *fireactions.Response, error)
}

// New returns a new root-level command.
Expand Down Expand Up @@ -60,6 +61,9 @@ func New() *cobra.Command {
cmd.AddCommand(newPoolsPauseCmd())
cmd.AddCommand(newPoolsScaleCmd())

cmd.AddGroup(&cobra.Group{ID: "microvm", Title: "MicroVM management commands:"})
cmd.AddCommand(newMicrovmsCmd())

cmd.PersistentFlags().SortFlags = false
cmd.PersistentFlags().StringVarP(&endpoint, "endpoint", "e", "http://127.0.0.1:8080", "Endpoint to use for communicating with the Fireactions API.")
cmd.PersistentFlags().StringVarP(&username, "username", "u", "", "Username to use for authenticating with the Fireactions API.")
Expand Down
2 changes: 1 addition & 1 deletion commands/cmd_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -28,5 +28,5 @@ func TestNew(t *testing.T) {
assert.NotNil(t, cmd.PersistentFlags().Lookup("password"))

assert.NotNil(t, cmd.Commands())
assert.Len(t, cmd.Commands(), 8) // 8 subcommands added
assert.Len(t, cmd.Commands(), 9) // 9 subcommands added
}
56 changes: 56 additions & 0 deletions commands/microvms.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,56 @@
package commands

import (
"context"
"fmt"

"github.com/hostinger/fireactions/helper/printer"
"github.com/spf13/cobra"
)

// newMicrovmsCmd returns the parent command for managing MicroVMs.
func newMicrovmsCmd() *cobra.Command {
cmd := &cobra.Command{
Use: "microvm",
Short: "Manage MicroVMs within a pool",
GroupID: "microvm",
}

cmd.AddCommand(newMicrovmsListCmd())

return cmd
}

func newMicrovmsListCmd() *cobra.Command {
cmd := &cobra.Command{
Use: "list --pool <pool-name>",
Short: "List all MicroVMs in the specified pool",
Args: cobra.NoArgs,
Aliases: []string{"ls"},
RunE: func(cmd *cobra.Command, args []string) error {
poolName, err := cmd.Flags().GetString("pool")
if err != nil {
return fmt.Errorf("error retrieving pool flag: %w", err)
}
return runMicrovmsListCmd(cmd, poolName)
},
}

cmd.Flags().String("pool", "", "Pool name (required)")
if err := cmd.MarkFlagRequired("pool"); err != nil {
fmt.Fprintf(cmd.ErrOrStderr(), "failed to mark 'pool' flag as required: %v\n", err)
}

return cmd
}

func runMicrovmsListCmd(cmd *cobra.Command, poolName string) error {
microvms, _, err := client.ListMicroVMs(context.Background(), poolName)
if err != nil {
return fmt.Errorf("failed to list MicroVMs: %w", err)
}

printer.PrintText(microvms, cmd.OutOrStdout(), nil)
return nil

}
52 changes: 52 additions & 0 deletions commands/microvms_test.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,52 @@
package commands

import (
"fmt"
"testing"

"github.com/golang/mock/gomock"
"github.com/hostinger/fireactions"
"github.com/hostinger/fireactions/commands/mocks"
"github.com/stretchr/testify/assert"
)

func TestMicrovmsList_Success(t *testing.T) {
ctrl := gomock.NewController(t)
defer ctrl.Finish()

mockClient := mocks.NewClient(ctrl)
mockResponse := &fireactions.Response{}
mockMicroVMs := &fireactions.MicroVMs{}

mockClient.EXPECT().ListMicroVMs(gomock.Any(), gomock.Any()).Return(mockMicroVMs, mockResponse, nil)

client = mockClient

cmd := newMicrovmsListCmd()
if err := cmd.Flags().Set("pool", "pool-name"); err != nil {
t.Fatalf("failed to set flag: %v", err)
}

err := cmd.RunE(cmd, []string{})
assert.Nil(t, err)
}

func TestMicrovmsList_Failure(t *testing.T) {
ctrl := gomock.NewController(t)
defer ctrl.Finish()

mockClient := mocks.NewClient(ctrl)

mockClient.EXPECT().ListMicroVMs(gomock.Any(), gomock.Any()).Return(nil, nil, fmt.Errorf("mock API failure"))

client = mockClient

cmd := newMicrovmsListCmd()

if err := cmd.Flags().Set("pool", "pool-name"); err != nil {
t.Fatalf("failed to set flag: %v", err)
}

err := cmd.RunE(cmd, []string{})
assert.ErrorContains(t, err, "mock API failure")
}
36 changes: 24 additions & 12 deletions commands/mocks/client.go

Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.

2 changes: 1 addition & 1 deletion commands/pools_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -4,10 +4,10 @@ import (
"errors"
"testing"

"github.com/golang/mock/gomock"
"github.com/hostinger/fireactions"
"github.com/hostinger/fireactions/commands/mocks"
"github.com/stretchr/testify/assert"
"go.uber.org/mock/gomock"
)

func TestPoolsPauseCommand_Success(t *testing.T) {
Expand Down
2 changes: 1 addition & 1 deletion commands/reload_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -3,10 +3,10 @@ package commands
import (
"testing"

"github.com/golang/mock/gomock"
"github.com/hostinger/fireactions/commands/mocks"
"github.com/spf13/cobra"
"github.com/stretchr/testify/assert"
"go.uber.org/mock/gomock"
)

func TestRunReloadCmd_Success(t *testing.T) {
Expand Down
9 changes: 9 additions & 0 deletions docs/cli/index.md
Original file line number Diff line number Diff line change
Expand Up @@ -21,6 +21,9 @@ Pool management commands:
show Retrieve a specific pool by name
list List all pools

MicroVM management commands:
microvm Manage MicroVMs within a pool

Additional Commands:
reload Reload the server with the latest configuration (no downtime)

Expand Down Expand Up @@ -71,3 +74,9 @@ List all pools.
### `reload`

Reload the server with the latest configuration (no downtime).

## `microvm`

### `list --pool <POOL-NAME>`

List all MicroVMs in the specified pool
1 change: 1 addition & 0 deletions go.mod
Original file line number Diff line number Diff line change
Expand Up @@ -120,6 +120,7 @@ require (
github.com/go-playground/universal-translator v0.18.1 // indirect
github.com/go-playground/validator/v10 v10.24.0
github.com/goccy/go-json v0.10.4 // indirect
github.com/golang/mock v1.6.0
github.com/google/uuid v1.6.0 // indirect
github.com/json-iterator/go v1.1.12 // indirect
github.com/klauspost/cpuid/v2 v2.2.9 // indirect
Expand Down
8 changes: 8 additions & 0 deletions go.sum
Original file line number Diff line number Diff line change
Expand Up @@ -448,6 +448,8 @@ github.com/golang/mock v1.2.0/go.mod h1:oTYuIxOrZwtPieC+H1uAHpcLFnEyAGVDL/k47Jfb
github.com/golang/mock v1.3.1/go.mod h1:sBzyDLLjw3U8JLTeZvSv8jJB+tU5PVekmnlKIyFUx0Y=
github.com/golang/mock v1.4.0/go.mod h1:UOMv5ysSaYNkG+OFQykRIcU/QvvxJf3p21QfJ2Bt3cw=
github.com/golang/mock v1.4.1/go.mod h1:UOMv5ysSaYNkG+OFQykRIcU/QvvxJf3p21QfJ2Bt3cw=
github.com/golang/mock v1.6.0 h1:ErTB+efbowRARo13NNdxyJji2egdxLGQhRaY+DUumQc=
github.com/golang/mock v1.6.0/go.mod h1:p6yTPP+5HYm5mzsMV8JkE6ZKdX+/wYM6Hr+LicevLPs=
github.com/golang/protobuf v1.2.0/go.mod h1:6lQm79b+lXiMfvg/cZm0SGofjICqVBUtrP5yJMmIC1U=
github.com/golang/protobuf v1.3.1/go.mod h1:6lQm79b+lXiMfvg/cZm0SGofjICqVBUtrP5yJMmIC1U=
github.com/golang/protobuf v1.3.2/go.mod h1:6lQm79b+lXiMfvg/cZm0SGofjICqVBUtrP5yJMmIC1U=
Expand Down Expand Up @@ -890,6 +892,7 @@ github.com/xordataexchange/crypt v0.0.3-0.20170626215501-b2862e3d0a77/go.mod h1:
github.com/youmark/pkcs8 v0.0.0-20181117223130-1be2e3e5546d/go.mod h1:rHwXgn7JulP+udvsHwJoVG1YGAP6VLg4y9I5dyZdqmA=
github.com/yuin/goldmark v1.1.27/go.mod h1:3hX8gzYuyVAZsxl0MRgGTJEmQBFcNTphYh9decYSb74=
github.com/yuin/goldmark v1.2.1/go.mod h1:3hX8gzYuyVAZsxl0MRgGTJEmQBFcNTphYh9decYSb74=
github.com/yuin/goldmark v1.3.5/go.mod h1:mwnBkeHKe2W/ZEtQ+71ViKU8L12m81fl3OWwC1Zlc8k=
github.com/yvasiyarov/go-metrics v0.0.0-20140926110328-57bccd1ccd43/go.mod h1:aX5oPXxHm3bOH+xeAttToC8pqch2ScQN/JoXYupl6xs=
github.com/yvasiyarov/gorelic v0.0.0-20141212073537-a9bba5b9ab50/go.mod h1:NUSPSUX/bi6SeDMUh6brw0nXpxHnc96TguQh0+r/ssA=
github.com/yvasiyarov/newrelic_platform_go v0.0.0-20140908184405-b21fdbd4370f/go.mod h1:GlGEuHIJweS1mbCqG+7vt2nvWLzLLnRHbXz5JKd/Qbg=
Expand Down Expand Up @@ -976,6 +979,7 @@ golang.org/x/mod v0.1.1-0.20191105210325-c90efee705ee/go.mod h1:QqPTAvyqsEbceGzB
golang.org/x/mod v0.1.1-0.20191107180719-034126e5016b/go.mod h1:QqPTAvyqsEbceGzBzNggFXnrqF1CaUcvgkdR5Ot7KZg=
golang.org/x/mod v0.2.0/go.mod h1:s0Qsj1ACt9ePp/hMypM3fl4fZqREWJwdYDEqhRiZZUA=
golang.org/x/mod v0.3.0/go.mod h1:s0Qsj1ACt9ePp/hMypM3fl4fZqREWJwdYDEqhRiZZUA=
golang.org/x/mod v0.4.2/go.mod h1:s0Qsj1ACt9ePp/hMypM3fl4fZqREWJwdYDEqhRiZZUA=
golang.org/x/net v0.0.0-20180724234803-3673e40ba225/go.mod h1:mL1N/T3taQHkDXs73rZJwtUhF3w3ftmwwsq0BUmARs4=
golang.org/x/net v0.0.0-20180826012351-8a410e7b638d/go.mod h1:mL1N/T3taQHkDXs73rZJwtUhF3w3ftmwwsq0BUmARs4=
golang.org/x/net v0.0.0-20180906233101-161cd47e91fd/go.mod h1:mL1N/T3taQHkDXs73rZJwtUhF3w3ftmwwsq0BUmARs4=
Expand Down Expand Up @@ -1012,6 +1016,7 @@ golang.org/x/net v0.0.0-20201110031124-69a78807bb2b/go.mod h1:sp8m0HH+o8qH0wwXwY
golang.org/x/net v0.0.0-20201224014010-6772e930b67b/go.mod h1:m0MpNAwzfU5UDzcl9v0D8zg8gWTRqZa9RBIspLL5mdg=
golang.org/x/net v0.0.0-20210226172049-e18ecbb05110/go.mod h1:m0MpNAwzfU5UDzcl9v0D8zg8gWTRqZa9RBIspLL5mdg=
golang.org/x/net v0.0.0-20210316092652-d523dce5a7f4/go.mod h1:RBQZq4jEuRlivfhVLdyRGr576XBO4/greRjx4P4O3yc=
golang.org/x/net v0.0.0-20210405180319-a5a99cb37ef4/go.mod h1:p54w0d4576C0XHj96bSt6lcn1PtDYWL6XObtHCRCNQM=
golang.org/x/net v0.0.0-20210421230115-4e50805a0758/go.mod h1:72T/g9IO56b78aLF+1Kcs5dz7/ng1VjMUvfKvpfy+jM=
golang.org/x/net v0.0.0-20210428140749-89ef3d95e781/go.mod h1:OJAsFXCWl8Ukc7SiCT/9KSuxbyM7479/AVlXFRxuMCk=
golang.org/x/net v0.0.0-20211112202133-69e39bad7dc2/go.mod h1:9nx3DQGgdP8bBQD5qxJ1jj9UTztislL4KSBs9R2vV5Y=
Expand Down Expand Up @@ -1101,8 +1106,10 @@ golang.org/x/sys v0.0.0-20210112080510-489259a85091/go.mod h1:h1NjWce9XRLGQEsW7w
golang.org/x/sys v0.0.0-20210124154548-22da62e12c0c/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
golang.org/x/sys v0.0.0-20210315160823-c6e025ad8005/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
golang.org/x/sys v0.0.0-20210324051608-47abb6519492/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
golang.org/x/sys v0.0.0-20210330210617-4fbd30eecc44/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
golang.org/x/sys v0.0.0-20210420072515-93ed5bcd2bfe/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
golang.org/x/sys v0.0.0-20210423082822-04245dca01da/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
golang.org/x/sys v0.0.0-20210510120138-977fb7262007/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg=
golang.org/x/sys v0.0.0-20210615035016-665e8c7367d1/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg=
golang.org/x/sys v0.0.0-20210809222454-d867a43fc93e/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg=
golang.org/x/sys v0.0.0-20210927094055-39ccf1dd6fa6/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg=
Expand Down Expand Up @@ -1177,6 +1184,7 @@ golang.org/x/tools v0.0.0-20200304193943-95d2e580d8eb/go.mod h1:o4KQGtdN14AW+yjs
golang.org/x/tools v0.0.0-20200619180055-7c47624df98f/go.mod h1:EkVYQZoAsY45+roYkvgYkIh4xh/qjgUK9TdY2XT94GE=
golang.org/x/tools v0.0.0-20201224043029-2b0845dc783e/go.mod h1:emZCQorbCU4vsT4fOWvOPXz4eW1wZW4PmDk9uLelYpA=
golang.org/x/tools v0.0.0-20210106214847-113979e3529a/go.mod h1:emZCQorbCU4vsT4fOWvOPXz4eW1wZW4PmDk9uLelYpA=
golang.org/x/tools v0.1.1/go.mod h1:o0xws9oXOQQZyjljx8fwUC0k7L1pTE6eaCbjGeHmOkk=
golang.org/x/tools v0.22.0 h1:gqSGLZqv+AI9lIQzniJ0nZDRG5GBPsSi+DRNHWNz6yA=
golang.org/x/tools v0.22.0/go.mod h1:aCwcsjqvq7Yqt6TNyX7QMU2enbQ/Gt0bo6krSeEri+c=
golang.org/x/xerrors v0.0.0-20190717185122-a985d3407aa7/go.mod h1:I/5z698sn9Ka8TeJc9MKroUUfqBBauWjQqLJ2OPfmY0=
Expand Down
19 changes: 19 additions & 0 deletions server/handlers.go
Original file line number Diff line number Diff line change
Expand Up @@ -106,3 +106,22 @@ func reloadHandler(p PoolManager) gin.HandlerFunc {

return f
}

func listMicroVMsHandler(m MicroVMManager) gin.HandlerFunc {
f := func(ctx *gin.Context) {
poolID := ctx.Param("id")

microVMs, err := m.ListMicroVMs(ctx, poolID)
if err != nil {
ctx.JSON(http.StatusInternalServerError, gin.H{"error": err.Error()})
return
}
if microVMs == nil {
microVMs = []*MicroVM{}
}

ctx.JSON(http.StatusOK, gin.H{"micro_vms": microVMs})
}

return f
}
Loading

0 comments on commit 26b1f56

Please sign in to comment.