diff --git a/Makefile b/Makefile index 8af8ae8..c7191fd 100644 --- a/Makefile +++ b/Makefile @@ -11,9 +11,11 @@ generate: deps mockgen --destination ./internal/mocks/hosts_service.go --package=mocks --source ./vendor/github.com/serverscom/serverscom-go-client/pkg/hosts.go mockgen --destination ./internal/mocks/ssh_service.go --package=mocks --source ./vendor/github.com/serverscom/serverscom-go-client/pkg/ssh_keys.go mockgen --destination ./internal/mocks/ssl_service.go --package=mocks --source ./vendor/github.com/serverscom/serverscom-go-client/pkg/ssl_certificates.go + mockgen --destination ./internal/mocks/load_balancers_service.go --package=mocks --source ./vendor/github.com/serverscom/serverscom-go-client/pkg/load_balancers.go sed -i '' 's|github.com/serverscom/srvctl/vendor/github.com/serverscom/serverscom-go-client/pkg|github.com/serverscom/serverscom-go-client/pkg|g' \ ./internal/mocks/ssh_service.go \ ./internal/mocks/hosts_service.go \ ./internal/mocks/ssl_service.go \ + ./internal/mocks/load_balancers_service.go \ ./internal/mocks/collection.go diff --git a/cmd/base/list.go b/cmd/base/list.go index 2974491..1c9ee3b 100644 --- a/cmd/base/list.go +++ b/cmd/base/list.go @@ -1,105 +1,15 @@ package base import ( - "log" - "strings" - serverscom "github.com/serverscom/serverscom-go-client/pkg" "github.com/spf13/cobra" ) -type ListOptions[T any] interface { - AddFlags(*cobra.Command) - ApplyToCollection(serverscom.Collection[T]) -} - -type AllPager interface { - AllPages() bool -} - -func NewListOptions[T any](opts ...ListOptions[T]) []ListOptions[T] { - return opts -} - // CollectionFactory is a function type that creates a typed resource collection // with configurable verbosity level // type CollectionFactory[T any] func(verbose bool) serverscom.Collection[T] type CollectionFactory[T any] func(verbose bool, args ...string) serverscom.Collection[T] -// BaseListOptions is a base options struct for list commands -type BaseListOptions[T any] struct { - perPage int - page int - sorting string - direction string - allPages bool -} - -// AddFlags adds common list flags to the command -func (o *BaseListOptions[T]) AddFlags(cmd *cobra.Command) { - flags := cmd.Flags() - flags.IntVar(&o.perPage, "per-page", 0, "Number of items per page") - flags.IntVar(&o.page, "page", 0, "Page number") - flags.StringVar(&o.sorting, "sorting", "", "Sort field") - flags.StringVar(&o.direction, "direction", "", "Sort direction (ASC or DESC)") - flags.BoolVarP(&o.allPages, "all", "A", false, "Get all pages of resources") - - flags.String("type", "", "") - if err := flags.MarkHidden("type"); err != nil { - log.Fatal(err) - } -} - -// ApplyToCollection applies the options to a collection -func (o *BaseListOptions[T]) ApplyToCollection(collection serverscom.Collection[T]) { - - if o.sorting != "" { - collection.SetParam("sort", o.sorting) - } - if o.direction != "" { - collection.SetParam("direction", strings.ToUpper(o.direction)) - } - if o.perPage > 0 { - collection.SetPerPage(o.perPage) - } - if o.page > 0 { - collection.SetPage(o.page) - } -} - -// AllPages returns true if all pages should be retrieved -func (o *BaseListOptions[T]) AllPages() bool { - return o.allPages -} - -type LabelSelectorOption[T any] struct { - labelSelector string -} - -func (o *LabelSelectorOption[T]) AddFlags(cmd *cobra.Command) { - cmd.Flags().StringVar(&o.labelSelector, "label-selector", "", "Filter results by labels") -} - -func (o *LabelSelectorOption[T]) ApplyToCollection(collection serverscom.Collection[T]) { - if o.labelSelector != "" { - collection.SetParam("label_selector", o.labelSelector) - } -} - -type SearchPatternOption[T any] struct { - searchPattern string -} - -func (o *SearchPatternOption[T]) AddFlags(cmd *cobra.Command) { - cmd.Flags().StringVar(&o.searchPattern, "search-pattern", "", "Return resources containing the parameter value in its name") -} - -func (o *SearchPatternOption[T]) ApplyToCollection(collection serverscom.Collection[T]) { - if o.searchPattern != "" { - collection.SetParam("search_pattern", o.searchPattern) - } -} - // NewListCmd base list command for different collections func NewListCmd[T any](use string, entityName string, colFactory CollectionFactory[T], cmdContext *CmdContext, opts ...ListOptions[T]) *cobra.Command { aliases := []string{} diff --git a/cmd/base/list_options.go b/cmd/base/list_options.go new file mode 100644 index 0000000..c1cdaa9 --- /dev/null +++ b/cmd/base/list_options.go @@ -0,0 +1,128 @@ +package base + +import ( + "log" + "strings" + + serverscom "github.com/serverscom/serverscom-go-client/pkg" + "github.com/spf13/cobra" +) + +type ListOptions[T any] interface { + AddFlags(*cobra.Command) + ApplyToCollection(serverscom.Collection[T]) +} + +type AllPager interface { + AllPages() bool +} + +func NewListOptions[T any](opts ...ListOptions[T]) []ListOptions[T] { + return opts +} + +// BaseListOptions is a base options struct for list commands +type BaseListOptions[T any] struct { + perPage int + page int + sorting string + direction string + allPages bool +} + +// AddFlags adds common list flags to the command +func (o *BaseListOptions[T]) AddFlags(cmd *cobra.Command) { + flags := cmd.Flags() + flags.IntVar(&o.perPage, "per-page", 0, "Number of items per page") + flags.IntVar(&o.page, "page", 0, "Page number") + flags.StringVar(&o.sorting, "sorting", "", "Sort field") + flags.StringVar(&o.direction, "direction", "", "Sort direction (ASC or DESC)") + flags.BoolVarP(&o.allPages, "all", "A", false, "Get all pages of resources") + + flags.String("type", "", "") + if err := flags.MarkHidden("type"); err != nil { + log.Fatal(err) + } +} + +// ApplyToCollection applies the options to a collection +func (o *BaseListOptions[T]) ApplyToCollection(collection serverscom.Collection[T]) { + + if o.sorting != "" { + collection.SetParam("sort", o.sorting) + } + if o.direction != "" { + collection.SetParam("direction", strings.ToUpper(o.direction)) + } + if o.perPage > 0 { + collection.SetPerPage(o.perPage) + } + if o.page > 0 { + collection.SetPage(o.page) + } +} + +// AllPages returns true if all pages should be retrieved +func (o *BaseListOptions[T]) AllPages() bool { + return o.allPages +} + +// label selector option +type LabelSelectorOption[T any] struct { + labelSelector string +} + +func (o *LabelSelectorOption[T]) AddFlags(cmd *cobra.Command) { + cmd.Flags().StringVar(&o.labelSelector, "label-selector", "", "Filter results by labels") +} + +func (o *LabelSelectorOption[T]) ApplyToCollection(collection serverscom.Collection[T]) { + if o.labelSelector != "" { + collection.SetParam("label_selector", o.labelSelector) + } +} + +// search pattern option +type SearchPatternOption[T any] struct { + searchPattern string +} + +func (o *SearchPatternOption[T]) AddFlags(cmd *cobra.Command) { + cmd.Flags().StringVar(&o.searchPattern, "search-pattern", "", "Return resources containing the parameter value in its name") +} + +func (o *SearchPatternOption[T]) ApplyToCollection(collection serverscom.Collection[T]) { + if o.searchPattern != "" { + collection.SetParam("search_pattern", o.searchPattern) + } +} + +// location id option +type LocationIDOption[T any] struct { + locationID string +} + +func (o *LocationIDOption[T]) AddFlags(cmd *cobra.Command) { + cmd.Flags().StringVar(&o.locationID, "location-id", "", "Filter results by location ID") +} + +func (o *LocationIDOption[T]) ApplyToCollection(collection serverscom.Collection[T]) { + if o.locationID != "" { + collection.SetParam("location_id", o.locationID) + } +} + +// cluster id option +type ClusterIDOption[T any] struct { + clusterID string +} + +func (o *ClusterIDOption[T]) AddFlags(cmd *cobra.Command) { + cmd.Flags().StringVar(&o.clusterID, "cluster-id", "", "Filter results by cluster ID") +} + +func (o *ClusterIDOption[T]) ApplyToCollection(collection serverscom.Collection[T]) { + if o.clusterID != "" { + collection.SetParam("cluster_id", o.clusterID) + } +} diff --git a/cmd/entities/hosts/hosts.go b/cmd/entities/hosts/hosts.go index 2a1224c..59d7b1a 100644 --- a/cmd/entities/hosts/hosts.go +++ b/cmd/entities/hosts/hosts.go @@ -112,9 +112,8 @@ func newHostTypeCmd(cmdContext *base.CmdContext, hostTypeCmd HostTypeCmd) *cobra hostCmd := &cobra.Command{ Use: hostTypeCmd.use, Short: hostTypeCmd.shortDesc, - RunE: func(cmd *cobra.Command, args []string) error { - return cmd.Help() - }, + Args: base.NoArgs, + Run: base.UsageRun, } hostCmd.AddCommand(newListCmd(cmdContext, &hostTypeCmd)) diff --git a/cmd/entities/load_balancers/add.go b/cmd/entities/load_balancers/add.go new file mode 100644 index 0000000..c7da11f --- /dev/null +++ b/cmd/entities/load_balancers/add.go @@ -0,0 +1,87 @@ +package loadbalancers + +import ( + "context" + "fmt" + "log" + + serverscom "github.com/serverscom/serverscom-go-client/pkg" + "github.com/serverscom/srvctl/cmd/base" + "github.com/spf13/cobra" +) + +type LBCreator interface { + Create(ctx context.Context, client *serverscom.Client, input any) (any, error) + NewCreateInput() any +} + +type LBL4CreateMgr struct{} + +func (c *LBL4CreateMgr) Create(ctx context.Context, client *serverscom.Client, input any) (any, error) { + lbInput, ok := input.(*serverscom.L4LoadBalancerCreateInput) + if !ok { + return nil, fmt.Errorf("invalid input type for L4 LB") + } + return client.LoadBalancers.CreateL4LoadBalancer(ctx, *lbInput) +} + +func (c *LBL4CreateMgr) NewCreateInput() any { + return &serverscom.L4LoadBalancerCreateInput{} +} + +type LBL7CreateMgr struct{} + +func (c *LBL7CreateMgr) Create(ctx context.Context, client *serverscom.Client, input any) (any, error) { + lbInput, ok := input.(*serverscom.L7LoadBalancerCreateInput) + if !ok { + return nil, fmt.Errorf("invalid input type for L7 LB") + } + return client.LoadBalancers.CreateL7LoadBalancer(ctx, *lbInput) +} + +func (c *LBL7CreateMgr) NewCreateInput() any { + return &serverscom.L7LoadBalancerCreateInput{} +} + +func newAddCmd(cmdContext *base.CmdContext, lbType *LBTypeCmd) *cobra.Command { + var path string + cmd := &cobra.Command{ + Use: "add --input ", + Short: fmt.Sprintf("Create %s", lbType.entityName), + Args: cobra.ExactArgs(0), + RunE: func(cmd *cobra.Command, args []string) error { + manager := cmdContext.GetManager() + ctx, cancel := base.SetupContext(cmd, manager) + defer cancel() + + base.SetupProxy(cmd, manager) + + input := lbType.managers.createMgr.NewCreateInput() + + if err := base.ReadInputJSON(path, cmd.InOrStdin(), input); err != nil { + return err + } + + scClient := cmdContext.GetClient().SetVerbose(manager.GetVerbose(cmd)).GetScClient() + + lb, err := lbType.managers.createMgr.Create(ctx, scClient, input) + if err != nil { + return err + } + + if lb != nil { + formatter := cmdContext.GetOrCreateFormatter(cmd) + return formatter.Format(lb) + } + + return nil + }, + } + + cmd.Flags().StringVarP(&path, "input", "i", "", "path to input file or '-' to read from stdin") + if err := cmd.MarkFlagRequired("input"); err != nil { + log.Fatal(err) + } + + return cmd +} diff --git a/cmd/entities/load_balancers/delete.go b/cmd/entities/load_balancers/delete.go new file mode 100644 index 0000000..430eb7d --- /dev/null +++ b/cmd/entities/load_balancers/delete.go @@ -0,0 +1,53 @@ +package loadbalancers + +import ( + "context" + + serverscom "github.com/serverscom/serverscom-go-client/pkg" + "github.com/serverscom/srvctl/cmd/base" + "github.com/spf13/cobra" +) + +type LBDeleter interface { + Delete(ctx context.Context, client *serverscom.Client, id string) error +} + +type LBL4DeleteMgr struct{} + +func (d *LBL4DeleteMgr) Delete(ctx context.Context, client *serverscom.Client, id string) error { + return client.LoadBalancers.DeleteL4LoadBalancer(ctx, id) +} + +type LBL7DeleteMgr struct{} + +func (d *LBL7DeleteMgr) Delete(ctx context.Context, client *serverscom.Client, id string) error { + return client.LoadBalancers.DeleteL7LoadBalancer(ctx, id) +} + +func newDeleteCmd(cmdContext *base.CmdContext, lbType *LBTypeCmd) *cobra.Command { + cmd := &cobra.Command{ + Use: "delete ", + Short: "Delete load balancer", + Long: "Delete load balancer by id", + Args: cobra.ExactArgs(1), + RunE: func(cmd *cobra.Command, args []string) error { + manager := cmdContext.GetManager() + + ctx, cancel := base.SetupContext(cmd, manager) + defer cancel() + + base.SetupProxy(cmd, manager) + + scClient := cmdContext.GetClient().SetVerbose(manager.GetVerbose(cmd)).GetScClient() + + id := args[0] + err := lbType.managers.deleteMgr.Delete(ctx, scClient, id) + if err != nil { + return err + } + + return nil + }, + } + return cmd +} diff --git a/cmd/entities/load_balancers/get.go b/cmd/entities/load_balancers/get.go new file mode 100644 index 0000000..18e03b0 --- /dev/null +++ b/cmd/entities/load_balancers/get.go @@ -0,0 +1,57 @@ +package loadbalancers + +import ( + "context" + + serverscom "github.com/serverscom/serverscom-go-client/pkg" + "github.com/serverscom/srvctl/cmd/base" + "github.com/spf13/cobra" +) + +type LBGetter interface { + Get(ctx context.Context, client *serverscom.Client, id string) (any, error) +} + +type LBL4GetMgr struct{} + +func (g *LBL4GetMgr) Get(ctx context.Context, client *serverscom.Client, id string) (any, error) { + return client.LoadBalancers.GetL4LoadBalancer(ctx, id) +} + +type LBL7GetMgr struct{} + +func (g *LBL7GetMgr) Get(ctx context.Context, client *serverscom.Client, id string) (any, error) { + return client.LoadBalancers.GetL7LoadBalancer(ctx, id) +} + +func newGetCmd(cmdContext *base.CmdContext, lbType *LBTypeCmd) *cobra.Command { + cmd := &cobra.Command{ + Use: "get ", + Short: "Get load balancer", + Long: "Get load balancer by id", + Args: cobra.ExactArgs(1), + RunE: func(cmd *cobra.Command, args []string) error { + manager := cmdContext.GetManager() + + ctx, cancel := base.SetupContext(cmd, manager) + defer cancel() + + base.SetupProxy(cmd, manager) + + scClient := cmdContext.GetClient().SetVerbose(manager.GetVerbose(cmd)).GetScClient() + + id := args[0] + lb, err := lbType.managers.getMgr.Get(ctx, scClient, id) + if err != nil { + return err + } + + if lb != nil { + formatter := cmdContext.GetOrCreateFormatter(cmd) + return formatter.Format(lb) + } + return nil + }, + } + return cmd +} diff --git a/cmd/entities/load_balancers/list.go b/cmd/entities/load_balancers/list.go new file mode 100644 index 0000000..6c8a1fd --- /dev/null +++ b/cmd/entities/load_balancers/list.go @@ -0,0 +1,35 @@ +package loadbalancers + +import ( + serverscom "github.com/serverscom/serverscom-go-client/pkg" + "github.com/serverscom/srvctl/cmd/base" + "github.com/spf13/cobra" +) + +func newListCmd(cmdContext *base.CmdContext, lbType *LBTypeCmd) *cobra.Command { + factory := func(verbose bool, args ...string) serverscom.Collection[serverscom.LoadBalancer] { + scClient := cmdContext.GetClient().SetVerbose(verbose).GetScClient() + collection := scClient.LoadBalancers.Collection() + + if lbType != nil && lbType.typeFlag != "" { + collection = collection.SetParam("type", lbType.typeFlag) + } + + return collection + } + + opts := base.NewListOptions( + &base.BaseListOptions[serverscom.LoadBalancer]{}, + &base.LabelSelectorOption[serverscom.LoadBalancer]{}, + &base.SearchPatternOption[serverscom.LoadBalancer]{}, + &base.LocationIDOption[serverscom.LoadBalancer]{}, + &base.ClusterIDOption[serverscom.LoadBalancer]{}, + ) + + entityName := "load balancers" + if lbType != nil { + entityName = lbType.entityName + } + + return base.NewListCmd("list", entityName, factory, cmdContext, opts...) +} diff --git a/cmd/entities/load_balancers/load_balancers.go b/cmd/entities/load_balancers/load_balancers.go new file mode 100644 index 0000000..f729ea3 --- /dev/null +++ b/cmd/entities/load_balancers/load_balancers.go @@ -0,0 +1,135 @@ +package loadbalancers + +import ( + "log" + + serverscom "github.com/serverscom/serverscom-go-client/pkg" + "github.com/serverscom/srvctl/cmd/base" + "github.com/serverscom/srvctl/internal/output/entities" + "github.com/spf13/cobra" +) + +type LBTypeCmd struct { + use string + shortDesc string + entityName string + typeFlag string + managers LBManagers + extraCmds []func(*base.CmdContext) *cobra.Command +} + +type LBManagers struct { + getMgr LBGetter + createMgr LBCreator + updateMgr LBUpdater + deleteMgr LBDeleter +} + +func NewCmd(cmdContext *base.CmdContext) *cobra.Command { + entitiesMap, err := getLBEntities() + if err != nil { + log.Fatal(err) + } + + LBTypeCmds := []LBTypeCmd{ + { + use: "l4", + shortDesc: "Manage L4 load balancers", + entityName: "L4 load balancers", + typeFlag: "l4", + managers: LBManagers{ + getMgr: &LBL4GetMgr{}, + createMgr: &LBL4CreateMgr{}, + updateMgr: &LBL4UpdateMgr{}, + deleteMgr: &LBL4DeleteMgr{}, + }, + }, + { + use: "l7", + shortDesc: "Manage L7 load balancers", + entityName: "L7 load balancers", + typeFlag: "l7", + managers: LBManagers{ + getMgr: &LBL7GetMgr{}, + createMgr: &LBL7CreateMgr{}, + updateMgr: &LBL7UpdateMgr{}, + deleteMgr: &LBL7DeleteMgr{}, + }, + }, + } + + cmd := &cobra.Command{ + Use: "lb", + Short: "Manage load balancers", + Long: "Manage load balancers of different types ( l4, l7 )", + PersistentPreRunE: base.CombinePreRunE( + base.CheckFormatterFlags(cmdContext, entitiesMap), + base.CheckEmptyContexts(cmdContext), + ), + Args: base.NoArgs, + Run: base.UsageRun, + } + + // lb list cmd + cmd.AddCommand(newListCmd(cmdContext, nil)) + + for _, st := range LBTypeCmds { + cmd.AddCommand(newLBTypeCmd(cmdContext, st)) + } + + base.AddFormatFlags(cmd) + + return cmd +} + +func newLBTypeCmd(cmdContext *base.CmdContext, lbType LBTypeCmd) *cobra.Command { + LBCmd := &cobra.Command{ + Use: lbType.use, + Short: lbType.shortDesc, + Args: base.NoArgs, + Run: base.UsageRun, + } + + LBCmd.AddCommand(newListCmd(cmdContext, &lbType)) + LBCmd.AddCommand(newGetCmd(cmdContext, &lbType)) + + if lbType.managers.createMgr != nil { + LBCmd.AddCommand(newAddCmd(cmdContext, &lbType)) + } + if lbType.managers.updateMgr != nil { + LBCmd.AddCommand(newUpdateCmd(cmdContext, &lbType)) + } + if lbType.managers.deleteMgr != nil { + LBCmd.AddCommand(newDeleteCmd(cmdContext, &lbType)) + } + + for _, cmdFunc := range lbType.extraCmds { + LBCmd.AddCommand(cmdFunc(cmdContext)) + } + + return LBCmd +} + +func getLBEntities() (map[string]entities.EntityInterface, error) { + result := make(map[string]entities.EntityInterface) + lbEntity, err := entities.Registry.GetEntityFromValue(serverscom.LoadBalancer{}) + if err != nil { + return nil, err + } + result["lb"] = lbEntity + result["list"] = lbEntity + + l4Entity, err := entities.Registry.GetEntityFromValue(serverscom.L4LoadBalancer{}) + if err != nil { + return nil, err + } + result["l4"] = l4Entity + + l7Entity, err := entities.Registry.GetEntityFromValue(serverscom.L7LoadBalancer{}) + if err != nil { + return nil, err + } + result["l7"] = l7Entity + + return result, nil +} diff --git a/cmd/entities/load_balancers/load_balancers_test.go b/cmd/entities/load_balancers/load_balancers_test.go new file mode 100644 index 0000000..9c05252 --- /dev/null +++ b/cmd/entities/load_balancers/load_balancers_test.go @@ -0,0 +1,787 @@ +package loadbalancers + +import ( + "errors" + "path/filepath" + "testing" + "time" + + . "github.com/onsi/gomega" + serverscom "github.com/serverscom/serverscom-go-client/pkg" + "github.com/serverscom/srvctl/cmd/testutils" + "github.com/serverscom/srvctl/internal/mocks" + "go.uber.org/mock/gomock" +) + +var ( + fixtureBasePath = filepath.Join("..", "..", "..", "testdata", "entities", "lb") + fixedTime = time.Date(2025, 1, 1, 12, 0, 0, 0, time.UTC) + testId = "testId" + testLB = serverscom.LoadBalancer{ + ID: testId, + Name: "test-l4-lb", + Type: "l4", + LocationID: 1, + ExternalAddresses: []string{"127.0.0.1"}, + Status: "active", + Labels: map[string]string{"foo": "bar"}, + Created: fixedTime, + Updated: fixedTime, + } + testL4LB = serverscom.L4LoadBalancer{ + ID: testId, + Name: "test-l4-lb", + Type: "l4", + LocationID: 1, + ExternalAddresses: []string{"127.0.0.1"}, + Status: "active", + Labels: map[string]string{"foo": "bar"}, + Created: fixedTime, + Updated: fixedTime, + } + testL7LB = serverscom.L7LoadBalancer{ + ID: testId, + Name: "test-l7-lb", + Type: "l7", + LocationID: 1, + ExternalAddresses: []string{"127.0.0.1"}, + Domains: []string{"test.com"}, + Status: "active", + Labels: map[string]string{"foo": "bar"}, + Created: fixedTime, + Updated: fixedTime, + } +) + +func TestAddL4LBCmd(t *testing.T) { + testCases := []struct { + name string + output string + args []string + configureMock func(*mocks.MockLoadBalancersService) + expectedOutput []byte + expectError bool + }{ + { + name: "create l4 lb", + output: "json", + expectedOutput: testutils.ReadFixture(filepath.Join(fixtureBasePath, "get_l4.json")), + args: []string{"--input", filepath.Join(fixtureBasePath, "create_l4.json")}, + configureMock: func(mock *mocks.MockLoadBalancersService) { + mock.EXPECT(). + CreateL4LoadBalancer(gomock.Any(), gomock.AssignableToTypeOf(serverscom.L4LoadBalancerCreateInput{})). + Return(&testL4LB, nil) + }, + }, + { + name: "with error", + expectError: true, + }, + } + + mockCtrl := gomock.NewController(t) + defer mockCtrl.Finish() + + lbServiceHandler := mocks.NewMockLoadBalancersService(mockCtrl) + + scClient := serverscom.NewClientWithEndpoint("", "") + scClient.LoadBalancers = lbServiceHandler + + for _, tc := range testCases { + t.Run(tc.name, func(t *testing.T) { + g := NewWithT(t) + + if tc.configureMock != nil { + tc.configureMock(lbServiceHandler) + } + + testCmdContext := testutils.NewTestCmdContext(scClient) + lbCmd := NewCmd(testCmdContext) + + args := []string{"lb", "l4", "add"} + if len(tc.args) > 0 { + args = append(args, tc.args...) + } + if tc.output != "" { + args = append(args, "--output", tc.output) + } + + builder := testutils.NewTestCommandBuilder(). + WithCommand(lbCmd). + WithArgs(args) + + cmd := builder.Build() + + err := cmd.Execute() + + if tc.expectError { + g.Expect(err).To(HaveOccurred()) + } else { + g.Expect(err).To(BeNil()) + g.Expect(builder.GetOutput()).To(BeEquivalentTo(string(tc.expectedOutput))) + } + }) + } +} + +func TestAddL7LBCmd(t *testing.T) { + testCases := []struct { + name string + output string + args []string + configureMock func(*mocks.MockLoadBalancersService) + expectedOutput []byte + expectError bool + }{ + { + name: "create l7 lb", + output: "json", + expectedOutput: testutils.ReadFixture(filepath.Join(fixtureBasePath, "get_l7.json")), + args: []string{"--input", filepath.Join(fixtureBasePath, "create_l7.json")}, + configureMock: func(mock *mocks.MockLoadBalancersService) { + mock.EXPECT(). + CreateL7LoadBalancer(gomock.Any(), gomock.AssignableToTypeOf(serverscom.L7LoadBalancerCreateInput{})). + Return(&testL7LB, nil) + }, + }, + { + name: "with error", + expectError: true, + }, + } + + mockCtrl := gomock.NewController(t) + defer mockCtrl.Finish() + + lbServiceHandler := mocks.NewMockLoadBalancersService(mockCtrl) + + scClient := serverscom.NewClientWithEndpoint("", "") + scClient.LoadBalancers = lbServiceHandler + + for _, tc := range testCases { + t.Run(tc.name, func(t *testing.T) { + g := NewWithT(t) + + if tc.configureMock != nil { + tc.configureMock(lbServiceHandler) + } + + testCmdContext := testutils.NewTestCmdContext(scClient) + lbCmd := NewCmd(testCmdContext) + + args := []string{"lb", "l7", "add"} + if len(tc.args) > 0 { + args = append(args, tc.args...) + } + if tc.output != "" { + args = append(args, "--output", tc.output) + } + + builder := testutils.NewTestCommandBuilder(). + WithCommand(lbCmd). + WithArgs(args) + + cmd := builder.Build() + + err := cmd.Execute() + + if tc.expectError { + g.Expect(err).To(HaveOccurred()) + } else { + g.Expect(err).To(BeNil()) + g.Expect(builder.GetOutput()).To(BeEquivalentTo(string(tc.expectedOutput))) + } + }) + } +} + +func TestGetL4LBCmd(t *testing.T) { + testCases := []struct { + name string + id string + output string + expectedOutput []byte + expectError bool + }{ + { + name: "get l4 lb in default format", + id: testId, + output: "", + expectedOutput: testutils.ReadFixture(filepath.Join(fixtureBasePath, "get_l4.txt")), + }, + { + name: "get l4 lb in JSON format", + id: testId, + output: "json", + expectedOutput: testutils.ReadFixture(filepath.Join(fixtureBasePath, "get_l4.json")), + }, + { + name: "get l4 lb in YAML format", + id: testId, + output: "yaml", + expectedOutput: testutils.ReadFixture(filepath.Join(fixtureBasePath, "get_l4.yaml")), + }, + { + name: "get l4 lb with error", + id: testId, + expectError: true, + }, + } + + mockCtrl := gomock.NewController(t) + defer mockCtrl.Finish() + + lbServiceHandler := mocks.NewMockLoadBalancersService(mockCtrl) + + scClient := serverscom.NewClientWithEndpoint("", "") + scClient.LoadBalancers = lbServiceHandler + + for _, tc := range testCases { + t.Run(tc.name, func(t *testing.T) { + g := NewWithT(t) + + var err error + if tc.expectError { + err = errors.New("some error") + } + lbServiceHandler.EXPECT(). + GetL4LoadBalancer(gomock.Any(), tc.id). + Return(&testL4LB, err) + + testCmdContext := testutils.NewTestCmdContext(scClient) + lbCmd := NewCmd(testCmdContext) + + args := []string{"lb", "l4", "get", tc.id} + if tc.output != "" { + args = append(args, "--output", tc.output) + } + + builder := testutils.NewTestCommandBuilder(). + WithCommand(lbCmd). + WithArgs(args) + + cmd := builder.Build() + + err = cmd.Execute() + + if tc.expectError { + g.Expect(err).To(HaveOccurred()) + } else { + g.Expect(err).To(BeNil()) + g.Expect(builder.GetOutput()).To(BeEquivalentTo(string(tc.expectedOutput))) + } + }) + } +} + +func TestGetL7LBCmd(t *testing.T) { + testCases := []struct { + name string + id string + output string + expectedOutput []byte + expectError bool + }{ + { + name: "get l7 lb in default format", + id: testId, + output: "", + expectedOutput: testutils.ReadFixture(filepath.Join(fixtureBasePath, "get_l7.txt")), + }, + { + name: "get l7 lb in JSON format", + id: testId, + output: "json", + expectedOutput: testutils.ReadFixture(filepath.Join(fixtureBasePath, "get_l7.json")), + }, + { + name: "get l7 lb in YAML format", + id: testId, + output: "yaml", + expectedOutput: testutils.ReadFixture(filepath.Join(fixtureBasePath, "get_l7.yaml")), + }, + { + name: "get l7 lb with error", + id: testId, + expectError: true, + }, + } + + mockCtrl := gomock.NewController(t) + defer mockCtrl.Finish() + + lbServiceHandler := mocks.NewMockLoadBalancersService(mockCtrl) + + scClient := serverscom.NewClientWithEndpoint("", "") + scClient.LoadBalancers = lbServiceHandler + + for _, tc := range testCases { + t.Run(tc.name, func(t *testing.T) { + g := NewWithT(t) + + var err error + if tc.expectError { + err = errors.New("some error") + } + lbServiceHandler.EXPECT(). + GetL7LoadBalancer(gomock.Any(), tc.id). + Return(&testL7LB, err) + + testCmdContext := testutils.NewTestCmdContext(scClient) + lbCmd := NewCmd(testCmdContext) + + args := []string{"lb", "l7", "get", tc.id} + if tc.output != "" { + args = append(args, "--output", tc.output) + } + + builder := testutils.NewTestCommandBuilder(). + WithCommand(lbCmd). + WithArgs(args) + + cmd := builder.Build() + + err = cmd.Execute() + + if tc.expectError { + g.Expect(err).To(HaveOccurred()) + } else { + g.Expect(err).To(BeNil()) + g.Expect(builder.GetOutput()).To(BeEquivalentTo(string(tc.expectedOutput))) + } + }) + } +} + +func TestListLBCmd(t *testing.T) { + lb1 := testLB + lb1.ID += "1" + lb2 := testLB + lb2.ID += "2" + lb2.Name = "test-l7-lb" + lb2.Type = "l7" + + testCases := []struct { + name string + output string + args []string + expectedOutput []byte + expectError bool + configureMock func(*mocks.MockCollection[serverscom.LoadBalancer]) + }{ + { + name: "list all load balancers", + output: "json", + args: []string{"-A"}, + expectedOutput: testutils.ReadFixture(filepath.Join(fixtureBasePath, "list_all.json")), + configureMock: func(mock *mocks.MockCollection[serverscom.LoadBalancer]) { + mock.EXPECT(). + Collect(gomock.Any()). + Return([]serverscom.LoadBalancer{ + lb1, + lb2, + }, nil) + }, + }, + { + name: "list l4 lb", + output: "json", + expectedOutput: testutils.ReadFixture(filepath.Join(fixtureBasePath, "list_l4.json")), + configureMock: func(mock *mocks.MockCollection[serverscom.LoadBalancer]) { + mock.EXPECT(). + List(gomock.Any()). + Return([]serverscom.LoadBalancer{ + lb1, + }, nil) + }, + }, + { + name: "list l7 lb", + output: "json", + expectedOutput: testutils.ReadFixture(filepath.Join(fixtureBasePath, "list_l7.json")), + configureMock: func(mock *mocks.MockCollection[serverscom.LoadBalancer]) { + mock.EXPECT(). + List(gomock.Any()). + Return([]serverscom.LoadBalancer{ + lb2, + }, nil) + }, + }, + { + name: "list lb with template", + args: []string{"--template", "{{range .}}Name: {{.Name}}\n{{end}}"}, + expectedOutput: testutils.ReadFixture(filepath.Join(fixtureBasePath, "list_template.txt")), + configureMock: func(mock *mocks.MockCollection[serverscom.LoadBalancer]) { + mock.EXPECT(). + List(gomock.Any()). + Return([]serverscom.LoadBalancer{ + lb1, + lb2, + }, nil) + }, + }, + { + name: "list lb with pageView", + args: []string{"--page-view"}, + expectedOutput: testutils.ReadFixture(filepath.Join(fixtureBasePath, "list_pageview.txt")), + configureMock: func(mock *mocks.MockCollection[serverscom.LoadBalancer]) { + mock.EXPECT(). + List(gomock.Any()). + Return([]serverscom.LoadBalancer{ + lb1, + lb2, + }, nil) + }, + }, + { + name: "list lb with error", + expectError: true, + configureMock: func(mock *mocks.MockCollection[serverscom.LoadBalancer]) { + mock.EXPECT(). + List(gomock.Any()). + Return(nil, errors.New("some error")) + }, + }, + } + + mockCtrl := gomock.NewController(t) + defer mockCtrl.Finish() + + lbServiceHandler := mocks.NewMockLoadBalancersService(mockCtrl) + collectionHandler := mocks.NewMockCollection[serverscom.LoadBalancer](mockCtrl) + + lbServiceHandler.EXPECT(). + Collection(). + Return(collectionHandler). + AnyTimes() + + collectionHandler.EXPECT(). + SetParam(gomock.Any(), gomock.Any()). + Return(collectionHandler). + AnyTimes() + + scClient := serverscom.NewClientWithEndpoint("", "") + scClient.LoadBalancers = lbServiceHandler + + for _, tc := range testCases { + t.Run(tc.name, func(t *testing.T) { + g := NewWithT(t) + + if tc.configureMock != nil { + tc.configureMock(collectionHandler) + } + + testCmdContext := testutils.NewTestCmdContext(scClient) + lbCmd := NewCmd(testCmdContext) + + args := []string{"lb", "list"} + if len(tc.args) > 0 { + args = append(args, tc.args...) + } + if tc.output != "" { + args = append(args, "--output", tc.output) + } + + builder := testutils.NewTestCommandBuilder(). + WithCommand(lbCmd). + WithArgs(args) + + cmd := builder.Build() + + err := cmd.Execute() + + if tc.expectError { + g.Expect(err).To(HaveOccurred()) + } else { + g.Expect(err).To(BeNil()) + g.Expect(builder.GetOutput()).To(BeEquivalentTo(string(tc.expectedOutput))) + } + }) + } +} + +func TestUpdateL4LBCmd(t *testing.T) { + updatedL4LB := testL4LB + updatedL4LB.Labels = map[string]string{"new": "label"} + + testCases := []struct { + name string + id string + output string + args []string + configureMock func(*mocks.MockLoadBalancersService) + expectedOutput []byte + expectError bool + }{ + { + name: "update l4 lb", + id: testId, + output: "json", + expectedOutput: testutils.ReadFixture(filepath.Join(fixtureBasePath, "update_l4.json")), + args: []string{"--input", filepath.Join(fixtureBasePath, "update_input_l4.json")}, + configureMock: func(mock *mocks.MockLoadBalancersService) { + mock.EXPECT(). + UpdateL4LoadBalancer(gomock.Any(), testId, serverscom.L4LoadBalancerUpdateInput{ + Labels: map[string]string{"new": "label"}, + }). + Return(&updatedL4LB, nil) + }, + }, + { + name: "update l4 lb with error", + id: testId, + args: []string{"--input", filepath.Join(fixtureBasePath, "update_input_l4.json")}, + configureMock: func(mock *mocks.MockLoadBalancersService) { + mock.EXPECT(). + UpdateL4LoadBalancer(gomock.Any(), testId, gomock.AssignableToTypeOf(serverscom.L4LoadBalancerUpdateInput{})). + Return(nil, errors.New("some error")) + }, + expectError: true, + }, + } + + mockCtrl := gomock.NewController(t) + defer mockCtrl.Finish() + + lbServiceHandler := mocks.NewMockLoadBalancersService(mockCtrl) + + scClient := serverscom.NewClientWithEndpoint("", "") + scClient.LoadBalancers = lbServiceHandler + + for _, tc := range testCases { + t.Run(tc.name, func(t *testing.T) { + g := NewWithT(t) + + if tc.configureMock != nil { + tc.configureMock(lbServiceHandler) + } + + testCmdContext := testutils.NewTestCmdContext(scClient) + lbCmd := NewCmd(testCmdContext) + + args := []string{"lb", "l4", "update", tc.id} + if len(tc.args) > 0 { + args = append(args, tc.args...) + } + if tc.output != "" { + args = append(args, "--output", tc.output) + } + + builder := testutils.NewTestCommandBuilder(). + WithCommand(lbCmd). + WithArgs(args) + + cmd := builder.Build() + + err := cmd.Execute() + + if tc.expectError { + g.Expect(err).To(HaveOccurred()) + } else { + g.Expect(err).To(BeNil()) + g.Expect(builder.GetOutput()).To(BeEquivalentTo(string(tc.expectedOutput))) + } + }) + } +} + +func TestUpdateL7LBCmd(t *testing.T) { + updatedL7LB := testL7LB + updatedL7LB.Labels = map[string]string{"new": "label"} + + testCases := []struct { + name string + id string + output string + args []string + configureMock func(*mocks.MockLoadBalancersService) + expectedOutput []byte + expectError bool + }{ + { + name: "update l7 lb", + id: testId, + output: "json", + expectedOutput: testutils.ReadFixture(filepath.Join(fixtureBasePath, "update_l7.json")), + args: []string{"--input", filepath.Join(fixtureBasePath, "update_input_l7.json")}, + configureMock: func(mock *mocks.MockLoadBalancersService) { + mock.EXPECT(). + UpdateL7LoadBalancer(gomock.Any(), testId, serverscom.L7LoadBalancerUpdateInput{ + Labels: map[string]string{"new": "label"}, + }). + Return(&updatedL7LB, nil) + }, + }, + { + name: "update l7 lb with error", + id: testId, + args: []string{"--input", filepath.Join(fixtureBasePath, "update_input_l7.json")}, + configureMock: func(mock *mocks.MockLoadBalancersService) { + mock.EXPECT(). + UpdateL7LoadBalancer(gomock.Any(), testId, gomock.AssignableToTypeOf(serverscom.L7LoadBalancerUpdateInput{})). + Return(nil, errors.New("some error")) + }, + expectError: true, + }, + } + + mockCtrl := gomock.NewController(t) + defer mockCtrl.Finish() + + lbServiceHandler := mocks.NewMockLoadBalancersService(mockCtrl) + + scClient := serverscom.NewClientWithEndpoint("", "") + scClient.LoadBalancers = lbServiceHandler + + for _, tc := range testCases { + t.Run(tc.name, func(t *testing.T) { + g := NewWithT(t) + + if tc.configureMock != nil { + tc.configureMock(lbServiceHandler) + } + + testCmdContext := testutils.NewTestCmdContext(scClient) + lbCmd := NewCmd(testCmdContext) + + args := []string{"lb", "l7", "update", tc.id} + if len(tc.args) > 0 { + args = append(args, tc.args...) + } + if tc.output != "" { + args = append(args, "--output", tc.output) + } + + builder := testutils.NewTestCommandBuilder(). + WithCommand(lbCmd). + WithArgs(args) + + cmd := builder.Build() + + err := cmd.Execute() + + if tc.expectError { + g.Expect(err).To(HaveOccurred()) + } else { + g.Expect(err).To(BeNil()) + g.Expect(builder.GetOutput()).To(BeEquivalentTo(string(tc.expectedOutput))) + } + }) + } +} + +func TestDeleteL4LBCmd(t *testing.T) { + testCases := []struct { + name string + id string + expectError bool + }{ + { + name: "delete l4 lb", + id: testId, + }, + { + name: "delete l4 lb with error", + id: testId, + expectError: true, + }, + } + + mockCtrl := gomock.NewController(t) + defer mockCtrl.Finish() + + lbServiceHandler := mocks.NewMockLoadBalancersService(mockCtrl) + + scClient := serverscom.NewClientWithEndpoint("", "") + scClient.LoadBalancers = lbServiceHandler + + for _, tc := range testCases { + t.Run(tc.name, func(t *testing.T) { + g := NewWithT(t) + + var err error + if tc.expectError { + err = errors.New("some error") + } + lbServiceHandler.EXPECT(). + DeleteL4LoadBalancer(gomock.Any(), testId). + Return(err) + + testCmdContext := testutils.NewTestCmdContext(scClient) + lbCmd := NewCmd(testCmdContext) + + args := []string{"lb", "l4", "delete", tc.id} + builder := testutils.NewTestCommandBuilder(). + WithCommand(lbCmd). + WithArgs(args) + + cmd := builder.Build() + + err = cmd.Execute() + + if tc.expectError { + g.Expect(err).To(HaveOccurred()) + } else { + g.Expect(err).To(BeNil()) + } + }) + } +} + +func TestDeleteL7LBCmd(t *testing.T) { + testCases := []struct { + name string + id string + expectError bool + }{ + { + name: "delete l7 lb", + id: testId, + }, + { + name: "delete l7 lb with error", + id: testId, + expectError: true, + }, + } + + mockCtrl := gomock.NewController(t) + defer mockCtrl.Finish() + + lbServiceHandler := mocks.NewMockLoadBalancersService(mockCtrl) + + scClient := serverscom.NewClientWithEndpoint("", "") + scClient.LoadBalancers = lbServiceHandler + + for _, tc := range testCases { + t.Run(tc.name, func(t *testing.T) { + g := NewWithT(t) + + var err error + if tc.expectError { + err = errors.New("some error") + } + lbServiceHandler.EXPECT(). + DeleteL7LoadBalancer(gomock.Any(), testId). + Return(err) + + testCmdContext := testutils.NewTestCmdContext(scClient) + lbCmd := NewCmd(testCmdContext) + + args := []string{"lb", "l7", "delete", tc.id} + builder := testutils.NewTestCommandBuilder(). + WithCommand(lbCmd). + WithArgs(args) + + cmd := builder.Build() + + err = cmd.Execute() + + if tc.expectError { + g.Expect(err).To(HaveOccurred()) + } else { + g.Expect(err).To(BeNil()) + } + }) + } +} diff --git a/cmd/entities/load_balancers/update.go b/cmd/entities/load_balancers/update.go new file mode 100644 index 0000000..689b8a3 --- /dev/null +++ b/cmd/entities/load_balancers/update.go @@ -0,0 +1,88 @@ +package loadbalancers + +import ( + "context" + "fmt" + "log" + + serverscom "github.com/serverscom/serverscom-go-client/pkg" + "github.com/serverscom/srvctl/cmd/base" + "github.com/spf13/cobra" +) + +type LBUpdater interface { + Update(ctx context.Context, client *serverscom.Client, id string, input any) (any, error) + NewUpdateInput() any +} + +type LBL4UpdateMgr struct{} + +func (c *LBL4UpdateMgr) Update(ctx context.Context, client *serverscom.Client, id string, input any) (any, error) { + lbInput, ok := input.(*serverscom.L4LoadBalancerUpdateInput) + if !ok { + return nil, fmt.Errorf("invalid input type for L4 LB") + } + return client.LoadBalancers.UpdateL4LoadBalancer(ctx, id, *lbInput) +} + +func (c *LBL4UpdateMgr) NewUpdateInput() any { + return &serverscom.L4LoadBalancerUpdateInput{} +} + +type LBL7UpdateMgr struct{} + +func (c *LBL7UpdateMgr) Update(ctx context.Context, client *serverscom.Client, id string, input any) (any, error) { + lbInput, ok := input.(*serverscom.L7LoadBalancerUpdateInput) + if !ok { + return nil, fmt.Errorf("invalid input type for L7 LB") + } + return client.LoadBalancers.UpdateL7LoadBalancer(ctx, id, *lbInput) +} + +func (c *LBL7UpdateMgr) NewUpdateInput() any { + return &serverscom.L7LoadBalancerUpdateInput{} +} + +func newUpdateCmd(cmdContext *base.CmdContext, lbType *LBTypeCmd) *cobra.Command { + var path string + cmd := &cobra.Command{ + Use: "update --input ", + Short: fmt.Sprintf("Update %s", lbType.entityName), + Args: cobra.ExactArgs(1), + RunE: func(cmd *cobra.Command, args []string) error { + manager := cmdContext.GetManager() + ctx, cancel := base.SetupContext(cmd, manager) + defer cancel() + + base.SetupProxy(cmd, manager) + + input := lbType.managers.updateMgr.NewUpdateInput() + + if err := base.ReadInputJSON(path, cmd.InOrStdin(), input); err != nil { + return err + } + + scClient := cmdContext.GetClient().SetVerbose(manager.GetVerbose(cmd)).GetScClient() + + id := args[0] + lb, err := lbType.managers.updateMgr.Update(ctx, scClient, id, input) + if err != nil { + return err + } + + if lb != nil { + formatter := cmdContext.GetOrCreateFormatter(cmd) + return formatter.Format(lb) + } + + return nil + }, + } + + cmd.Flags().StringVarP(&path, "input", "i", "", "path to input file or '-' to read from stdin") + if err := cmd.MarkFlagRequired("input"); err != nil { + log.Fatal(err) + } + + return cmd +} diff --git a/cmd/entities/ssl/add.go b/cmd/entities/ssl/add.go index 3c0ef17..bc93930 100644 --- a/cmd/entities/ssl/add.go +++ b/cmd/entities/ssl/add.go @@ -50,14 +50,14 @@ func newAddCmd(cmdContext *base.CmdContext, sslType *SSLTypeCmd) *cobra.Command scClient := cmdContext.GetClient().SetVerbose(manager.GetVerbose(cmd)).GetScClient() - server, err := sslType.managers.createMgr.Create(ctx, scClient, input) + sslCert, err := sslType.managers.createMgr.Create(ctx, scClient, input) if err != nil { return err } - if server != nil { + if sslCert != nil { formatter := cmdContext.GetOrCreateFormatter(cmd) - return formatter.Format(server) + return formatter.Format(sslCert) } return nil diff --git a/cmd/entities/ssl/ssl.go b/cmd/entities/ssl/ssl.go index 5f9b05e..fa49fdb 100644 --- a/cmd/entities/ssl/ssl.go +++ b/cmd/entities/ssl/ssl.go @@ -89,9 +89,8 @@ func newSSLTypeCmd(cmdContext *base.CmdContext, sslTypeCmd SSLTypeCmd) *cobra.Co sslCmd := &cobra.Command{ Use: sslTypeCmd.use, Short: sslTypeCmd.shortDesc, - RunE: func(cmd *cobra.Command, args []string) error { - return cmd.Help() - }, + Args: base.NoArgs, + Run: base.UsageRun, } sslCmd.AddCommand(newListCmd(cmdContext, &sslTypeCmd)) diff --git a/cmd/entities/ssl/update.go b/cmd/entities/ssl/update.go index 5c7127c..e05a4fa 100644 --- a/cmd/entities/ssl/update.go +++ b/cmd/entities/ssl/update.go @@ -12,7 +12,7 @@ func newUpdateCustomCmd(cmdContext *base.CmdContext) *cobra.Command { var labels []string cmd := &cobra.Command{ - Use: "update ", + Use: "update ", Short: "Update an ssl custom certificate", Long: "Update an ssl custom certificate by id", Args: cobra.ExactArgs(1), @@ -35,14 +35,14 @@ func newUpdateCustomCmd(cmdContext *base.CmdContext) *cobra.Command { scClient := cmdContext.GetClient().SetVerbose(manager.GetVerbose(cmd)).GetScClient() id := args[0] - sshKey, err := scClient.SSLCertificates.UpdateCustom(ctx, id, input) + sslCert, err := scClient.SSLCertificates.UpdateCustom(ctx, id, input) if err != nil { return err } - if sshKey != nil { + if sslCert != nil { formatter := cmdContext.GetOrCreateFormatter(cmd) - return formatter.Format(sshKey) + return formatter.Format(sslCert) } return nil }, @@ -80,14 +80,14 @@ func newUpdateLeCmd(cmdContext *base.CmdContext) *cobra.Command { scClient := cmdContext.GetClient().SetVerbose(manager.GetVerbose(cmd)).GetScClient() id := args[0] - sshKey, err := scClient.SSLCertificates.UpdateLE(ctx, id, input) + sslCert, err := scClient.SSLCertificates.UpdateLE(ctx, id, input) if err != nil { return err } - if sshKey != nil { + if sslCert != nil { formatter := cmdContext.GetOrCreateFormatter(cmd) - return formatter.Format(sshKey) + return formatter.Format(sslCert) } return nil }, diff --git a/cmd/root.go b/cmd/root.go index ee3c036..ab8051f 100644 --- a/cmd/root.go +++ b/cmd/root.go @@ -5,6 +5,7 @@ import ( "github.com/serverscom/srvctl/cmd/config" "github.com/serverscom/srvctl/cmd/context" "github.com/serverscom/srvctl/cmd/entities/hosts" + loadbalancers "github.com/serverscom/srvctl/cmd/entities/load_balancers" sshkeys "github.com/serverscom/srvctl/cmd/entities/ssh-keys" "github.com/serverscom/srvctl/cmd/entities/ssl" "github.com/serverscom/srvctl/cmd/login" @@ -40,6 +41,7 @@ func NewRootCmd(version string) *cobra.Command { cmd.AddCommand(sshkeys.NewCmd(cmdContext)) cmd.AddCommand(hosts.NewCmd(cmdContext)) cmd.AddCommand(ssl.NewCmd(cmdContext)) + cmd.AddCommand(loadbalancers.NewCmd(cmdContext)) return cmd } diff --git a/internal/mocks/load_balancers_service.go b/internal/mocks/load_balancers_service.go new file mode 100644 index 0000000..b5ed00f --- /dev/null +++ b/internal/mocks/load_balancers_service.go @@ -0,0 +1,174 @@ +// Code generated by MockGen. DO NOT EDIT. +// Source: ./vendor/github.com/serverscom/serverscom-go-client/pkg/load_balancers.go +// +// Generated by this command: +// +// mockgen --destination ./internal/mocks/load_balancers_service.go --package=mocks --source ./vendor/github.com/serverscom/serverscom-go-client/pkg/load_balancers.go +// + +// Package mocks is a generated GoMock package. +package mocks + +import ( + context "context" + reflect "reflect" + + serverscom "github.com/serverscom/serverscom-go-client/pkg" + gomock "go.uber.org/mock/gomock" +) + +// MockLoadBalancersService is a mock of LoadBalancersService interface. +type MockLoadBalancersService struct { + ctrl *gomock.Controller + recorder *MockLoadBalancersServiceMockRecorder + isgomock struct{} +} + +// MockLoadBalancersServiceMockRecorder is the mock recorder for MockLoadBalancersService. +type MockLoadBalancersServiceMockRecorder struct { + mock *MockLoadBalancersService +} + +// NewMockLoadBalancersService creates a new mock instance. +func NewMockLoadBalancersService(ctrl *gomock.Controller) *MockLoadBalancersService { + mock := &MockLoadBalancersService{ctrl: ctrl} + mock.recorder = &MockLoadBalancersServiceMockRecorder{mock} + return mock +} + +// EXPECT returns an object that allows the caller to indicate expected use. +func (m *MockLoadBalancersService) EXPECT() *MockLoadBalancersServiceMockRecorder { + return m.recorder +} + +// Collection mocks base method. +func (m *MockLoadBalancersService) Collection() serverscom.Collection[serverscom.LoadBalancer] { + m.ctrl.T.Helper() + ret := m.ctrl.Call(m, "Collection") + ret0, _ := ret[0].(serverscom.Collection[serverscom.LoadBalancer]) + return ret0 +} + +// Collection indicates an expected call of Collection. +func (mr *MockLoadBalancersServiceMockRecorder) Collection() *gomock.Call { + mr.mock.ctrl.T.Helper() + return mr.mock.ctrl.RecordCallWithMethodType(mr.mock, "Collection", reflect.TypeOf((*MockLoadBalancersService)(nil).Collection)) +} + +// CreateL4LoadBalancer mocks base method. +func (m *MockLoadBalancersService) CreateL4LoadBalancer(ctx context.Context, input serverscom.L4LoadBalancerCreateInput) (*serverscom.L4LoadBalancer, error) { + m.ctrl.T.Helper() + ret := m.ctrl.Call(m, "CreateL4LoadBalancer", ctx, input) + ret0, _ := ret[0].(*serverscom.L4LoadBalancer) + ret1, _ := ret[1].(error) + return ret0, ret1 +} + +// CreateL4LoadBalancer indicates an expected call of CreateL4LoadBalancer. +func (mr *MockLoadBalancersServiceMockRecorder) CreateL4LoadBalancer(ctx, input any) *gomock.Call { + mr.mock.ctrl.T.Helper() + return mr.mock.ctrl.RecordCallWithMethodType(mr.mock, "CreateL4LoadBalancer", reflect.TypeOf((*MockLoadBalancersService)(nil).CreateL4LoadBalancer), ctx, input) +} + +// CreateL7LoadBalancer mocks base method. +func (m *MockLoadBalancersService) CreateL7LoadBalancer(ctx context.Context, input serverscom.L7LoadBalancerCreateInput) (*serverscom.L7LoadBalancer, error) { + m.ctrl.T.Helper() + ret := m.ctrl.Call(m, "CreateL7LoadBalancer", ctx, input) + ret0, _ := ret[0].(*serverscom.L7LoadBalancer) + ret1, _ := ret[1].(error) + return ret0, ret1 +} + +// CreateL7LoadBalancer indicates an expected call of CreateL7LoadBalancer. +func (mr *MockLoadBalancersServiceMockRecorder) CreateL7LoadBalancer(ctx, input any) *gomock.Call { + mr.mock.ctrl.T.Helper() + return mr.mock.ctrl.RecordCallWithMethodType(mr.mock, "CreateL7LoadBalancer", reflect.TypeOf((*MockLoadBalancersService)(nil).CreateL7LoadBalancer), ctx, input) +} + +// DeleteL4LoadBalancer mocks base method. +func (m *MockLoadBalancersService) DeleteL4LoadBalancer(ctx context.Context, id string) error { + m.ctrl.T.Helper() + ret := m.ctrl.Call(m, "DeleteL4LoadBalancer", ctx, id) + ret0, _ := ret[0].(error) + return ret0 +} + +// DeleteL4LoadBalancer indicates an expected call of DeleteL4LoadBalancer. +func (mr *MockLoadBalancersServiceMockRecorder) DeleteL4LoadBalancer(ctx, id any) *gomock.Call { + mr.mock.ctrl.T.Helper() + return mr.mock.ctrl.RecordCallWithMethodType(mr.mock, "DeleteL4LoadBalancer", reflect.TypeOf((*MockLoadBalancersService)(nil).DeleteL4LoadBalancer), ctx, id) +} + +// DeleteL7LoadBalancer mocks base method. +func (m *MockLoadBalancersService) DeleteL7LoadBalancer(ctx context.Context, id string) error { + m.ctrl.T.Helper() + ret := m.ctrl.Call(m, "DeleteL7LoadBalancer", ctx, id) + ret0, _ := ret[0].(error) + return ret0 +} + +// DeleteL7LoadBalancer indicates an expected call of DeleteL7LoadBalancer. +func (mr *MockLoadBalancersServiceMockRecorder) DeleteL7LoadBalancer(ctx, id any) *gomock.Call { + mr.mock.ctrl.T.Helper() + return mr.mock.ctrl.RecordCallWithMethodType(mr.mock, "DeleteL7LoadBalancer", reflect.TypeOf((*MockLoadBalancersService)(nil).DeleteL7LoadBalancer), ctx, id) +} + +// GetL4LoadBalancer mocks base method. +func (m *MockLoadBalancersService) GetL4LoadBalancer(ctx context.Context, id string) (*serverscom.L4LoadBalancer, error) { + m.ctrl.T.Helper() + ret := m.ctrl.Call(m, "GetL4LoadBalancer", ctx, id) + ret0, _ := ret[0].(*serverscom.L4LoadBalancer) + ret1, _ := ret[1].(error) + return ret0, ret1 +} + +// GetL4LoadBalancer indicates an expected call of GetL4LoadBalancer. +func (mr *MockLoadBalancersServiceMockRecorder) GetL4LoadBalancer(ctx, id any) *gomock.Call { + mr.mock.ctrl.T.Helper() + return mr.mock.ctrl.RecordCallWithMethodType(mr.mock, "GetL4LoadBalancer", reflect.TypeOf((*MockLoadBalancersService)(nil).GetL4LoadBalancer), ctx, id) +} + +// GetL7LoadBalancer mocks base method. +func (m *MockLoadBalancersService) GetL7LoadBalancer(ctx context.Context, id string) (*serverscom.L7LoadBalancer, error) { + m.ctrl.T.Helper() + ret := m.ctrl.Call(m, "GetL7LoadBalancer", ctx, id) + ret0, _ := ret[0].(*serverscom.L7LoadBalancer) + ret1, _ := ret[1].(error) + return ret0, ret1 +} + +// GetL7LoadBalancer indicates an expected call of GetL7LoadBalancer. +func (mr *MockLoadBalancersServiceMockRecorder) GetL7LoadBalancer(ctx, id any) *gomock.Call { + mr.mock.ctrl.T.Helper() + return mr.mock.ctrl.RecordCallWithMethodType(mr.mock, "GetL7LoadBalancer", reflect.TypeOf((*MockLoadBalancersService)(nil).GetL7LoadBalancer), ctx, id) +} + +// UpdateL4LoadBalancer mocks base method. +func (m *MockLoadBalancersService) UpdateL4LoadBalancer(ctx context.Context, id string, input serverscom.L4LoadBalancerUpdateInput) (*serverscom.L4LoadBalancer, error) { + m.ctrl.T.Helper() + ret := m.ctrl.Call(m, "UpdateL4LoadBalancer", ctx, id, input) + ret0, _ := ret[0].(*serverscom.L4LoadBalancer) + ret1, _ := ret[1].(error) + return ret0, ret1 +} + +// UpdateL4LoadBalancer indicates an expected call of UpdateL4LoadBalancer. +func (mr *MockLoadBalancersServiceMockRecorder) UpdateL4LoadBalancer(ctx, id, input any) *gomock.Call { + mr.mock.ctrl.T.Helper() + return mr.mock.ctrl.RecordCallWithMethodType(mr.mock, "UpdateL4LoadBalancer", reflect.TypeOf((*MockLoadBalancersService)(nil).UpdateL4LoadBalancer), ctx, id, input) +} + +// UpdateL7LoadBalancer mocks base method. +func (m *MockLoadBalancersService) UpdateL7LoadBalancer(ctx context.Context, id string, input serverscom.L7LoadBalancerUpdateInput) (*serverscom.L7LoadBalancer, error) { + m.ctrl.T.Helper() + ret := m.ctrl.Call(m, "UpdateL7LoadBalancer", ctx, id, input) + ret0, _ := ret[0].(*serverscom.L7LoadBalancer) + ret1, _ := ret[1].(error) + return ret0, ret1 +} + +// UpdateL7LoadBalancer indicates an expected call of UpdateL7LoadBalancer. +func (mr *MockLoadBalancersServiceMockRecorder) UpdateL7LoadBalancer(ctx, id, input any) *gomock.Call { + mr.mock.ctrl.T.Helper() + return mr.mock.ctrl.RecordCallWithMethodType(mr.mock, "UpdateL7LoadBalancer", reflect.TypeOf((*MockLoadBalancersService)(nil).UpdateL7LoadBalancer), ctx, id, input) +} diff --git a/internal/output/entities/init.go b/internal/output/entities/init.go index 0762eea..13aaf3d 100644 --- a/internal/output/entities/init.go +++ b/internal/output/entities/init.go @@ -14,4 +14,5 @@ func init() { RegisterSSLCertDefinition() RegisterSSLCertCustomDefinition() RegisterSSLCertLeDefinition() + RegisterLoadBalancerDefinitions() } diff --git a/internal/output/entities/load_balancers.go b/internal/output/entities/load_balancers.go new file mode 100644 index 0000000..bcb9df4 --- /dev/null +++ b/internal/output/entities/load_balancers.go @@ -0,0 +1,91 @@ +package entities + +import ( + "log" + "reflect" + + serverscom "github.com/serverscom/serverscom-go-client/pkg" +) + +var ( + LoadBalancerType = reflect.TypeOf(serverscom.LoadBalancer{}) + L4LoadBalancerType = reflect.TypeOf(serverscom.L4LoadBalancer{}) + L7LoadBalancerType = reflect.TypeOf(serverscom.L7LoadBalancer{}) + LoadBalancerListDefaultFields = []string{"ID", "Name", "Type", "Status", "LocationID", "ClusterID"} +) + +func RegisterLoadBalancerDefinitions() { + // Register LoadBalancer + eLoadBalancer := &Entity{ + fields: []Field{ + {ID: "ID", Name: "ID", Path: "ID", ListHandlerFunc: stringHandler, PageViewHandlerFunc: stringHandler, Default: true}, + {ID: "Name", Name: "Name", Path: "Name", ListHandlerFunc: stringHandler, PageViewHandlerFunc: stringHandler, Default: true}, + {ID: "Type", Name: "Type", Path: "Type", ListHandlerFunc: stringHandler, PageViewHandlerFunc: stringHandler, Default: true}, + {ID: "Status", Name: "Status", Path: "Status", ListHandlerFunc: stringHandler, PageViewHandlerFunc: stringHandler, Default: true}, + {ID: "ExternalAddresses", Name: "ExternalAddresses", Path: "ExternalAddresses", PageViewHandlerFunc: slicePvHandler}, + {ID: "LocationID", Name: "LocationID", Path: "LocationID", ListHandlerFunc: stringHandler, PageViewHandlerFunc: stringHandler, Default: true}, + {ID: "ClusterID", Name: "ClusterID", Path: "ClusterID", ListHandlerFunc: stringHandler, PageViewHandlerFunc: stringHandler, Default: true}, + {ID: "Labels", Name: "Labels", Path: "Labels", PageViewHandlerFunc: mapPvHandler}, + {ID: "Created", Name: "Created", Path: "Created", ListHandlerFunc: timeHandler, PageViewHandlerFunc: timeHandler, Default: true}, + {ID: "Updated", Name: "Updated", Path: "Updated", ListHandlerFunc: timeHandler, PageViewHandlerFunc: timeHandler, Default: true}, + }, + cmdDefaultFields: map[string][]string{ + "list": LoadBalancerListDefaultFields, + }, + eType: LoadBalancerType, + } + if err := Registry.Register(eLoadBalancer); err != nil { + log.Fatal(err) + } + + // Register L4LoadBalancer + eL4LoadBalancer := &Entity{ + fields: []Field{ + {ID: "ID", Name: "ID", Path: "ID", ListHandlerFunc: stringHandler, PageViewHandlerFunc: stringHandler, Default: true}, + {ID: "Name", Name: "Name", Path: "Name", ListHandlerFunc: stringHandler, PageViewHandlerFunc: stringHandler, Default: true}, + {ID: "Type", Name: "Type", Path: "Type", ListHandlerFunc: stringHandler, PageViewHandlerFunc: stringHandler, Default: true}, + {ID: "Status", Name: "Status", Path: "Status", ListHandlerFunc: stringHandler, PageViewHandlerFunc: stringHandler, Default: true}, + {ID: "ExternalAddresses", Name: "ExternalAddresses", Path: "ExternalAddresses", PageViewHandlerFunc: slicePvHandler}, + {ID: "LocationID", Name: "LocationID", Path: "LocationID", ListHandlerFunc: stringHandler, PageViewHandlerFunc: stringHandler, Default: true}, + {ID: "StoreLogs", Name: "StoreLogs", Path: "StoreLogs", ListHandlerFunc: stringHandler, PageViewHandlerFunc: stringHandler}, + {ID: "ClusterID", Name: "ClusterID", Path: "ClusterID", ListHandlerFunc: stringHandler, PageViewHandlerFunc: stringHandler, Default: true}, + {ID: "Labels", Name: "Labels", Path: "Labels", PageViewHandlerFunc: mapPvHandler}, + {ID: "Created", Name: "Created", Path: "Created", ListHandlerFunc: timeHandler, PageViewHandlerFunc: timeHandler, Default: true}, + {ID: "Updated", Name: "Updated", Path: "Updated", ListHandlerFunc: timeHandler, PageViewHandlerFunc: timeHandler, Default: true}, + }, + cmdDefaultFields: map[string][]string{ + "list": LoadBalancerListDefaultFields, + }, + eType: L4LoadBalancerType, + } + if err := Registry.Register(eL4LoadBalancer); err != nil { + log.Fatal(err) + } + + // Register L7LoadBalancer + eL7LoadBalancer := &Entity{ + fields: []Field{ + {ID: "ID", Name: "ID", Path: "ID", ListHandlerFunc: stringHandler, PageViewHandlerFunc: stringHandler, Default: true}, + {ID: "Name", Name: "Name", Path: "Name", ListHandlerFunc: stringHandler, PageViewHandlerFunc: stringHandler, Default: true}, + {ID: "Type", Name: "Type", Path: "Type", ListHandlerFunc: stringHandler, PageViewHandlerFunc: stringHandler, Default: true}, + {ID: "Status", Name: "Status", Path: "Status", ListHandlerFunc: stringHandler, PageViewHandlerFunc: stringHandler, Default: true}, + {ID: "ExternalAddresses", Name: "ExternalAddresses", Path: "ExternalAddresses", PageViewHandlerFunc: slicePvHandler}, + {ID: "LocationID", Name: "LocationID", Path: "LocationID", ListHandlerFunc: stringHandler, PageViewHandlerFunc: stringHandler, Default: true}, + {ID: "Geoip", Name: "Geoip", Path: "Geoip", ListHandlerFunc: stringHandler, PageViewHandlerFunc: stringHandler}, + {ID: "StoreLogs", Name: "StoreLogs", Path: "StoreLogs", ListHandlerFunc: stringHandler, PageViewHandlerFunc: stringHandler}, + {ID: "StoreLogsRegionID", Name: "StoreLogsRegionID", Path: "StoreLogsRegionID", ListHandlerFunc: stringHandler, PageViewHandlerFunc: stringHandler}, + {ID: "Domains", Name: "Domains", Path: "Domains", PageViewHandlerFunc: slicePvHandler}, + {ID: "ClusterID", Name: "ClusterID", Path: "ClusterID", ListHandlerFunc: stringHandler, PageViewHandlerFunc: stringHandler, Default: true}, + {ID: "Labels", Name: "Labels", Path: "Labels", PageViewHandlerFunc: mapPvHandler}, + {ID: "Created", Name: "Created", Path: "Created", ListHandlerFunc: timeHandler, PageViewHandlerFunc: timeHandler, Default: true}, + {ID: "Updated", Name: "Updated", Path: "Updated", ListHandlerFunc: timeHandler, PageViewHandlerFunc: timeHandler, Default: true}, + }, + cmdDefaultFields: map[string][]string{ + "list": LoadBalancerListDefaultFields, + }, + eType: L7LoadBalancerType, + } + if err := Registry.Register(eL7LoadBalancer); err != nil { + log.Fatal(err) + } +} diff --git a/testdata/entities/lb/create_l4.json b/testdata/entities/lb/create_l4.json new file mode 100644 index 0000000..998d193 --- /dev/null +++ b/testdata/entities/lb/create_l4.json @@ -0,0 +1,32 @@ +{ + "name": "test-l4-lb", + "location_id": 1, + "cluster_id": null, + "vhost_zones": [ + { + "id": "test1", + "ports": [ + 80 + ], + "udp": true, + "proxy_protocol": true, + "upstream_id": "test2" + } + ], + "upstream_zones": [ + { + "id": "test2", + "udp": true, + "upstreams": [ + { + "ip": "10.253.115.4", + "port": 81, + "weight": 1 + } + ] + } + ], + "labels": { + "foo": "bar" + } +} \ No newline at end of file diff --git a/testdata/entities/lb/create_l7.json b/testdata/entities/lb/create_l7.json new file mode 100644 index 0000000..83202fc --- /dev/null +++ b/testdata/entities/lb/create_l7.json @@ -0,0 +1,32 @@ +{ + "name": "test-l7-lb", + "location_id": 1, + "cluster_id": null, + "vhost_zones": [ + { + "id": "test1", + "ports": [ + 80 + ], + "udp": true, + "proxy_protocol": true, + "upstream_id": "test2" + } + ], + "upstream_zones": [ + { + "id": "test2", + "udp": true, + "upstreams": [ + { + "ip": "10.253.115.4", + "port": 81, + "weight": 1 + } + ] + } + ], + "labels": { + "foo": "bar" + } +} \ No newline at end of file diff --git a/testdata/entities/lb/get_l4.json b/testdata/entities/lb/get_l4.json new file mode 100644 index 0000000..170ecfa --- /dev/null +++ b/testdata/entities/lb/get_l4.json @@ -0,0 +1,17 @@ +{ + "id": "testId", + "name": "test-l4-lb", + "type": "l4", + "status": "active", + "external_addresses": [ + "127.0.0.1" + ], + "location_id": 1, + "store_logs": false, + "cluster_id": null, + "labels": { + "foo": "bar" + }, + "created_at": "2025-01-01T12:00:00Z", + "updated_at": "2025-01-01T12:00:00Z" +} \ No newline at end of file diff --git a/testdata/entities/lb/get_l4.txt b/testdata/entities/lb/get_l4.txt new file mode 100644 index 0000000..1009384 --- /dev/null +++ b/testdata/entities/lb/get_l4.txt @@ -0,0 +1,2 @@ +ID Name Type Status LocationID ClusterID Created Updated +testId test-l4-lb l4 active 1 2025-01-01T12:00:00Z 2025-01-01T12:00:00Z diff --git a/testdata/entities/lb/get_l4.yaml b/testdata/entities/lb/get_l4.yaml new file mode 100644 index 0000000..2484054 --- /dev/null +++ b/testdata/entities/lb/get_l4.yaml @@ -0,0 +1,13 @@ +id: testId +name: test-l4-lb +type: l4 +status: active +externaladdresses: + - 127.0.0.1 +locationid: 1 +storelogs: false +clusterid: null +labels: + foo: bar +created: 2025-01-01T12:00:00Z +updated: 2025-01-01T12:00:00Z diff --git a/testdata/entities/lb/get_l7.json b/testdata/entities/lb/get_l7.json new file mode 100644 index 0000000..ef2c0c7 --- /dev/null +++ b/testdata/entities/lb/get_l7.json @@ -0,0 +1,22 @@ +{ + "id": "testId", + "name": "test-l7-lb", + "type": "l7", + "domains": [ + "test.com" + ], + "status": "active", + "external_addresses": [ + "127.0.0.1" + ], + "location_id": 1, + "geoip": false, + "store_logs": false, + "store_logs_region_id": 0, + "cluster_id": null, + "labels": { + "foo": "bar" + }, + "created_at": "2025-01-01T12:00:00Z", + "updated_at": "2025-01-01T12:00:00Z" +} \ No newline at end of file diff --git a/testdata/entities/lb/get_l7.txt b/testdata/entities/lb/get_l7.txt new file mode 100644 index 0000000..d7d2aa9 --- /dev/null +++ b/testdata/entities/lb/get_l7.txt @@ -0,0 +1,2 @@ +ID Name Type Status LocationID ClusterID Created Updated +testId test-l7-lb l7 active 1 2025-01-01T12:00:00Z 2025-01-01T12:00:00Z diff --git a/testdata/entities/lb/get_l7.yaml b/testdata/entities/lb/get_l7.yaml new file mode 100644 index 0000000..ce2b947 --- /dev/null +++ b/testdata/entities/lb/get_l7.yaml @@ -0,0 +1,17 @@ +id: testId +name: test-l7-lb +type: l7 +domains: + - test.com +status: active +externaladdresses: + - 127.0.0.1 +locationid: 1 +geoip: false +storelogs: false +storelogsregionid: 0 +clusterid: null +labels: + foo: bar +created: 2025-01-01T12:00:00Z +updated: 2025-01-01T12:00:00Z diff --git a/testdata/entities/lb/list_all.json b/testdata/entities/lb/list_all.json new file mode 100644 index 0000000..3aa590d --- /dev/null +++ b/testdata/entities/lb/list_all.json @@ -0,0 +1,34 @@ +[ + { + "id": "testId1", + "name": "test-l4-lb", + "type": "l4", + "status": "active", + "external_addresses": [ + "127.0.0.1" + ], + "location_id": 1, + "cluster_id": null, + "labels": { + "foo": "bar" + }, + "created_at": "2025-01-01T12:00:00Z", + "updated_at": "2025-01-01T12:00:00Z" + }, + { + "id": "testId2", + "name": "test-l7-lb", + "type": "l7", + "status": "active", + "external_addresses": [ + "127.0.0.1" + ], + "location_id": 1, + "cluster_id": null, + "labels": { + "foo": "bar" + }, + "created_at": "2025-01-01T12:00:00Z", + "updated_at": "2025-01-01T12:00:00Z" + } +] \ No newline at end of file diff --git a/testdata/entities/lb/list_l4.json b/testdata/entities/lb/list_l4.json new file mode 100644 index 0000000..98feab8 --- /dev/null +++ b/testdata/entities/lb/list_l4.json @@ -0,0 +1,18 @@ +[ + { + "id": "testId1", + "name": "test-l4-lb", + "type": "l4", + "status": "active", + "external_addresses": [ + "127.0.0.1" + ], + "location_id": 1, + "cluster_id": null, + "labels": { + "foo": "bar" + }, + "created_at": "2025-01-01T12:00:00Z", + "updated_at": "2025-01-01T12:00:00Z" + } +] \ No newline at end of file diff --git a/testdata/entities/lb/list_l7.json b/testdata/entities/lb/list_l7.json new file mode 100644 index 0000000..649cd84 --- /dev/null +++ b/testdata/entities/lb/list_l7.json @@ -0,0 +1,18 @@ +[ + { + "id": "testId2", + "name": "test-l7-lb", + "type": "l7", + "status": "active", + "external_addresses": [ + "127.0.0.1" + ], + "location_id": 1, + "cluster_id": null, + "labels": { + "foo": "bar" + }, + "created_at": "2025-01-01T12:00:00Z", + "updated_at": "2025-01-01T12:00:00Z" + } +] \ No newline at end of file diff --git a/testdata/entities/lb/list_pageview.txt b/testdata/entities/lb/list_pageview.txt new file mode 100644 index 0000000..17c6ac2 --- /dev/null +++ b/testdata/entities/lb/list_pageview.txt @@ -0,0 +1,21 @@ +ID: testId1 +Name: test-l4-lb +Type: l4 +Status: active +ExternalAddresses: 127.0.0.1 +LocationID: 1 +ClusterID: +Labels: foo=bar +Created: 2025-01-01T12:00:00Z +Updated: 2025-01-01T12:00:00Z +--- +ID: testId2 +Name: test-l7-lb +Type: l7 +Status: active +ExternalAddresses: 127.0.0.1 +LocationID: 1 +ClusterID: +Labels: foo=bar +Created: 2025-01-01T12:00:00Z +Updated: 2025-01-01T12:00:00Z diff --git a/testdata/entities/lb/list_template.txt b/testdata/entities/lb/list_template.txt new file mode 100644 index 0000000..1084882 --- /dev/null +++ b/testdata/entities/lb/list_template.txt @@ -0,0 +1,2 @@ +Name: test-l4-lb +Name: test-l7-lb diff --git a/testdata/entities/lb/update_input_l4.json b/testdata/entities/lb/update_input_l4.json new file mode 100644 index 0000000..a515d28 --- /dev/null +++ b/testdata/entities/lb/update_input_l4.json @@ -0,0 +1,5 @@ +{ + "labels": { + "new": "label" + } +} \ No newline at end of file diff --git a/testdata/entities/lb/update_input_l7.json b/testdata/entities/lb/update_input_l7.json new file mode 100644 index 0000000..a515d28 --- /dev/null +++ b/testdata/entities/lb/update_input_l7.json @@ -0,0 +1,5 @@ +{ + "labels": { + "new": "label" + } +} \ No newline at end of file diff --git a/testdata/entities/lb/update_l4.json b/testdata/entities/lb/update_l4.json new file mode 100644 index 0000000..dbca156 --- /dev/null +++ b/testdata/entities/lb/update_l4.json @@ -0,0 +1,17 @@ +{ + "id": "testId", + "name": "test-l4-lb", + "type": "l4", + "status": "active", + "external_addresses": [ + "127.0.0.1" + ], + "location_id": 1, + "store_logs": false, + "cluster_id": null, + "labels": { + "new": "label" + }, + "created_at": "2025-01-01T12:00:00Z", + "updated_at": "2025-01-01T12:00:00Z" +} \ No newline at end of file diff --git a/testdata/entities/lb/update_l7.json b/testdata/entities/lb/update_l7.json new file mode 100644 index 0000000..088d2ee --- /dev/null +++ b/testdata/entities/lb/update_l7.json @@ -0,0 +1,22 @@ +{ + "id": "testId", + "name": "test-l7-lb", + "type": "l7", + "domains": [ + "test.com" + ], + "status": "active", + "external_addresses": [ + "127.0.0.1" + ], + "location_id": 1, + "geoip": false, + "store_logs": false, + "store_logs_region_id": 0, + "cluster_id": null, + "labels": { + "new": "label" + }, + "created_at": "2025-01-01T12:00:00Z", + "updated_at": "2025-01-01T12:00:00Z" +} \ No newline at end of file