diff --git a/Makefile b/Makefile index 92e7787..11a99f4 100644 --- a/Makefile +++ b/Makefile @@ -18,6 +18,7 @@ generate: deps mockgen --destination ./internal/mocks/locations_service.go --package=mocks --source ./vendor/github.com/serverscom/serverscom-go-client/pkg/locations.go mockgen --destination ./internal/mocks/kubernetes_clusters_service.go --package=mocks --source ./vendor/github.com/serverscom/serverscom-go-client/pkg/kubernetes_clusters.go mockgen --destination ./internal/mocks/l2_segment_service.go --package=mocks --source ./vendor/github.com/serverscom/serverscom-go-client/pkg/l2_segments.go + mockgen --destination ./internal/mocks/network_pool_service.go --package=mocks --source ./vendor/github.com/serverscom/serverscom-go-client/pkg/network_pools.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 \ @@ -29,4 +30,5 @@ generate: deps ./internal/mocks/collection.go \ ./internal/mocks/locations_service.go \ ./internal/mocks/kubernetes_clusters_service.go \ - ./internal/mocks/l2_segment_service.go \ No newline at end of file + ./internal/mocks/l2_segment_service.go \ + ./internal/mocks/network_pool_service.go \ No newline at end of file diff --git a/cmd/base/list_options.go b/cmd/base/list_options.go index b486631..a54f213 100644 --- a/cmd/base/list_options.go +++ b/cmd/base/list_options.go @@ -416,3 +416,32 @@ func (o *L2SegmentGroupTypeOption[T]) ApplyToCollection(collection serverscom.Co collection.SetParam("group_type", o.group) } } + +// network pool type option +type NetworkPoolTypeOption[T any] struct { + poolType string +} + +func (o *NetworkPoolTypeOption[T]) AddFlags(cmd *cobra.Command) { + cmd.Flags().StringVar(&o.poolType, "type", "", "Filter network pools by type (public, private)") +} + +func (o *NetworkPoolTypeOption[T]) ApplyToCollection(collection serverscom.Collection[T]) { + if o.poolType != "" { + collection.SetParam("type", o.poolType) + } +} + +type AttachedSubnetworksOption[T any] struct { + attached bool +} + +func (o *AttachedSubnetworksOption[T]) AddFlags(cmd *cobra.Command) { + cmd.Flags().BoolVar(&o.attached, "attached", false, "Filter only subnetworks that are attached to a dedicated server") +} + +func (o *AttachedSubnetworksOption[T]) ApplyToCollection(collection serverscom.Collection[T]) { + if o.attached { + collection.SetParam("attached", "true") + } +} diff --git a/cmd/entities/network-pools/add.go b/cmd/entities/network-pools/add.go new file mode 100644 index 0000000..ce289bb --- /dev/null +++ b/cmd/entities/network-pools/add.go @@ -0,0 +1,59 @@ +package networkpools + +import ( + serverscom "github.com/serverscom/serverscom-go-client/pkg" + "github.com/serverscom/srvctl/cmd/base" + "github.com/spf13/cobra" +) + +func newCreateSubnetCmd(cmdContext *base.CmdContext) *cobra.Command { + var title string + var cidr string + var mask int + + cmd := &cobra.Command{ + Use: "add-subnet ", + Short: "Create a subnetwork", + Long: "Create a subnetwork for a network pool", + 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 := serverscom.SubnetworkCreateInput{} + if cmd.Flags().Changed("title") { + input.Title = &title + } + if cmd.Flags().Changed("cidr") { + input.CIDR = &cidr + } + if cmd.Flags().Changed("mask") { + input.Mask = &mask + } + + scClient := cmdContext.GetClient().SetVerbose(manager.GetVerbose(cmd)).GetScClient() + + networkPoolID := args[0] + sshKey, err := scClient.NetworkPools.CreateSubnetwork(ctx, networkPoolID, input) + if err != nil { + return err + } + + if sshKey != nil { + formatter := cmdContext.GetOrCreateFormatter(cmd) + return formatter.Format(sshKey) + } + return nil + }, + } + + cmd.Flags().StringVar(&title, "title", "", "Subnetwork name") + cmd.Flags().StringVar(&cidr, "cidr", "", "If this parameter is filled in, a subnetwork will be created according to the specified Classless Inter-Domain Routing pattern: x.x.x.x/x") + cmd.Flags().IntVar(&mask, "mask", 0, "Alternative parameter to CIDR. If it's specified, a subnetwork will be allocated from a pool by the mask") + + return cmd +} diff --git a/cmd/entities/network-pools/delete.go b/cmd/entities/network-pools/delete.go new file mode 100644 index 0000000..c29b7cd --- /dev/null +++ b/cmd/entities/network-pools/delete.go @@ -0,0 +1,35 @@ +package networkpools + +import ( + "github.com/serverscom/srvctl/cmd/base" + "github.com/spf13/cobra" +) + +func newDeleteSubnetCmd(cmdContext *base.CmdContext) *cobra.Command { + var subnetID string + + cmd := &cobra.Command{ + Use: "delete ", + Short: "Delete a subnetwork for a network pool", + Long: "Delete a subnetwork for a network pool 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() + + networkPoolID := args[0] + return scClient.NetworkPools.DeleteSubnetwork(ctx, networkPoolID, subnetID) + }, + } + + cmd.Flags().StringVar(&subnetID, "network-id", "", "Subnetwork id (string, required)") + _ = cmd.MarkFlagRequired("network-id") + + return cmd +} diff --git a/cmd/entities/network-pools/get.go b/cmd/entities/network-pools/get.go new file mode 100644 index 0000000..48c87de --- /dev/null +++ b/cmd/entities/network-pools/get.go @@ -0,0 +1,77 @@ +package networkpools + +import ( + "github.com/serverscom/srvctl/cmd/base" + "github.com/spf13/cobra" +) + +func newGetCmd(cmdContext *base.CmdContext) *cobra.Command { + cmd := &cobra.Command{ + Use: "get ", + Short: "Get a network pool", + Long: "Get a network pool 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() + + networkPoolID := args[0] + model, err := scClient.NetworkPools.Get(ctx, networkPoolID) + if err != nil { + return err + } + + if model != nil { + formatter := cmdContext.GetOrCreateFormatter(cmd) + return formatter.Format(model) + } + return nil + }, + } + + return cmd +} + +func newGetSubnetCmd(cmdContext *base.CmdContext) *cobra.Command { + var subnetID string + + cmd := &cobra.Command{ + Use: "get-subnet ", + Short: "Get a subnetwork", + Long: "Get a subnetwork for a network pool", + 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() + + networkPoolID := args[0] + model, err := scClient.NetworkPools.GetSubnetwork(ctx, networkPoolID, subnetID) + if err != nil { + return err + } + + if model != nil { + formatter := cmdContext.GetOrCreateFormatter(cmd) + return formatter.Format(model) + } + return nil + }, + } + + cmd.Flags().StringVar(&subnetID, "network-id", "", "Subnetwork id (string, required)") + _ = cmd.MarkFlagRequired("network-id") + + return cmd +} diff --git a/cmd/entities/network-pools/list.go b/cmd/entities/network-pools/list.go new file mode 100644 index 0000000..7a3eec8 --- /dev/null +++ b/cmd/entities/network-pools/list.go @@ -0,0 +1,39 @@ +package networkpools + +import ( + serverscom "github.com/serverscom/serverscom-go-client/pkg" + "github.com/serverscom/srvctl/cmd/base" + "github.com/spf13/cobra" +) + +func newListCmd(cmdContext *base.CmdContext) *cobra.Command { + factory := func(verbose bool, args ...string) serverscom.Collection[serverscom.NetworkPool] { + scClient := cmdContext.GetClient().SetVerbose(verbose).GetScClient() + return scClient.NetworkPools.Collection() + } + + opts := base.NewListOptions( + &base.BaseListOptions[serverscom.NetworkPool]{}, + &base.SearchPatternOption[serverscom.NetworkPool]{}, + &base.LabelSelectorOption[serverscom.NetworkPool]{}, + &base.LocationIDOption[serverscom.NetworkPool]{}, + &base.NetworkPoolTypeOption[serverscom.NetworkPool]{}, + ) + + return base.NewListCmd("list", "network pools", factory, cmdContext, opts...) +} + +func newListSubnetsCmd(cmdContext *base.CmdContext) *cobra.Command { + factory := func(verbose bool, args ...string) serverscom.Collection[serverscom.Subnetwork] { + scClient := cmdContext.GetClient().SetVerbose(verbose).GetScClient() + return scClient.NetworkPools.Subnetworks(args[0]) + } + + opts := base.NewListOptions( + &base.BaseListOptions[serverscom.Subnetwork]{}, + &base.SearchPatternOption[serverscom.Subnetwork]{}, + &base.AttachedSubnetworksOption[serverscom.Subnetwork]{}, + ) + + return base.NewListCmd("list-subnets", "subnets for a network pool", factory, cmdContext, opts...) +} diff --git a/cmd/entities/network-pools/network_pools.go b/cmd/entities/network-pools/network_pools.go new file mode 100644 index 0000000..c9309d4 --- /dev/null +++ b/cmd/entities/network-pools/network_pools.go @@ -0,0 +1,44 @@ +package networkpools + +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" +) + +func NewCmd(cmdContext *base.CmdContext) *cobra.Command { + networkPoolEntity, err := entities.Registry.GetEntityFromValue(serverscom.NetworkPool{}) + if err != nil { + log.Fatal(err) + } + entitiesMap := make(map[string]entities.EntityInterface) + entitiesMap["network-pools"] = networkPoolEntity + cmd := &cobra.Command{ + Use: "network-pools", + Short: "Manage network pools", + PersistentPreRunE: base.CombinePreRunE( + base.CheckFormatterFlags(cmdContext, entitiesMap), + base.CheckEmptyContexts(cmdContext), + ), + Args: base.NoArgs, + Run: base.UsageRun, + } + + cmd.AddCommand( + newListCmd(cmdContext), + newListSubnetsCmd(cmdContext), + newGetCmd(cmdContext), + newGetSubnetCmd(cmdContext), + newCreateSubnetCmd(cmdContext), + newUpdateCmd(cmdContext), + newUpdateSubnetCmd(cmdContext), + newDeleteSubnetCmd(cmdContext), + ) + + base.AddFormatFlags(cmd) + + return cmd +} diff --git a/cmd/entities/network-pools/network_pools_test.go b/cmd/entities/network-pools/network_pools_test.go new file mode 100644 index 0000000..96e5b63 --- /dev/null +++ b/cmd/entities/network-pools/network_pools_test.go @@ -0,0 +1,690 @@ +package networkpools + +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", "network-pools") + testID = "testId" + testSubnetID = "testSubnetId" + fixedTime = time.Date(2025, 1, 1, 12, 0, 0, 0, time.UTC) + + networkTitle = "testNetworkPool" + subnetTitle = "testSubnet" + cidr = "192.168.0.0/24" + + testNetworkPool = serverscom.NetworkPool{ + ID: testID, + Title: &networkTitle, + CIDR: cidr, + Type: "private", + LocationIDs: []int{1, 2}, + Labels: map[string]string{ + "environment": "testing", + }, + Created: fixedTime, + Updated: fixedTime, + } + + testSubnet = serverscom.Subnetwork{ + ID: testSubnetID, + NetworkPoolID: testID, + Title: &subnetTitle, + CIDR: cidr, + Attached: false, + InterfaceType: "private", + Created: fixedTime, + Updated: fixedTime, + } +) + +func TestGetNetworkPoolCmd(t *testing.T) { + testCases := []struct { + name string + id string + output string + expectedOutput []byte + expectError bool + }{ + { + name: "get network pool default format", + id: testID, + expectedOutput: testutils.ReadFixture(filepath.Join(fixtureBasePath, "get.txt")), + }, + { + name: "get network pool JSON", + id: testID, + output: "json", + expectedOutput: testutils.ReadFixture(filepath.Join(fixtureBasePath, "get.json")), + }, + { + name: "get network pool YAML", + id: testID, + output: "yaml", + expectedOutput: testutils.ReadFixture(filepath.Join(fixtureBasePath, "get.yaml")), + }, + { + name: "get network pool with error", + id: testID, + expectError: true, + }, + } + + mockCtrl := gomock.NewController(t) + defer mockCtrl.Finish() + poolService := mocks.NewMockNetworkPoolsService(mockCtrl) + + scClient := serverscom.NewClientWithEndpoint("", "") + scClient.NetworkPools = poolService + + 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") + } + + poolService.EXPECT(). + Get(gomock.Any(), tc.id). + Return(&testNetworkPool, err) + + testCmdContext := testutils.NewTestCmdContext(scClient) + cmd := NewCmd(testCmdContext) + + args := []string{"network-pools", "get", tc.id} + if tc.output != "" { + args = append(args, "--output", tc.output) + } + + builder := testutils.NewTestCommandBuilder(). + WithCommand(cmd). + WithArgs(args) + c := builder.Build() + err = c.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 TestGetSubnetCmd(t *testing.T) { + testCases := []struct { + name string + networkID string + subnetID string + output string + expectedOutput []byte + expectError bool + }{ + { + name: "get subnet default", + networkID: testID, + subnetID: testSubnetID, + expectedOutput: testutils.ReadFixture(filepath.Join(fixtureBasePath, "get_subnet.txt")), + }, + { + name: "get subnet json", + networkID: testID, + subnetID: testSubnetID, + output: "json", + expectedOutput: testutils.ReadFixture(filepath.Join(fixtureBasePath, "get_subnet.json")), + }, + { + name: "get subnet YAML", + networkID: testID, + subnetID: testSubnetID, + output: "yaml", + expectedOutput: testutils.ReadFixture(filepath.Join(fixtureBasePath, "get_subnet.yaml")), + }, + { + name: "get subnet with error", + networkID: testID, + subnetID: testSubnetID, + expectError: true, + }, + } + + mockCtrl := gomock.NewController(t) + defer mockCtrl.Finish() + poolService := mocks.NewMockNetworkPoolsService(mockCtrl) + + scClient := serverscom.NewClientWithEndpoint("", "") + scClient.NetworkPools = poolService + + 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") + } + poolService.EXPECT(). + GetSubnetwork(gomock.Any(), tc.networkID, tc.subnetID). + Return(&testSubnet, err) + + testCmdContext := testutils.NewTestCmdContext(scClient) + cmd := NewCmd(testCmdContext) + + args := []string{"network-pools", "get-subnet", tc.networkID, "--network-id", tc.subnetID} + if tc.output != "" { + args = append(args, "--output", tc.output) + } + builder := testutils.NewTestCommandBuilder(). + WithCommand(cmd). + WithArgs(args) + c := builder.Build() + err = c.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 TestListNetworkPoolsCmd(t *testing.T) { + title := "testNetworkPool2" + p1 := testNetworkPool + p2 := testNetworkPool + p2.ID += "2" + p2.Title = &title + + testCases := []struct { + name string + output string + args []string + expectedOutput []byte + expectError bool + configureMock func(*mocks.MockCollection[serverscom.NetworkPool]) + }{ + { + name: "list all network pools", + output: "json", + args: []string{"-A"}, + expectedOutput: testutils.ReadFixture(filepath.Join(fixtureBasePath, "list_all.json")), + configureMock: func(mock *mocks.MockCollection[serverscom.NetworkPool]) { + mock.EXPECT(). + Collect(gomock.Any()). + Return([]serverscom.NetworkPool{p1, p2}, nil) + }, + }, + { + name: "list network pools", + output: "json", + expectedOutput: testutils.ReadFixture(filepath.Join(fixtureBasePath, "list.json")), + configureMock: func(mock *mocks.MockCollection[serverscom.NetworkPool]) { + mock.EXPECT(). + List(gomock.Any()). + Return([]serverscom.NetworkPool{p1}, nil) + }, + }, + { + name: "list network pools with template", + args: []string{"--template", "{{range .}}ID: {{.ID}} Title: {{.Title}}\n{{end}}"}, + expectedOutput: testutils.ReadFixture(filepath.Join(fixtureBasePath, "list_template.txt")), + configureMock: func(mock *mocks.MockCollection[serverscom.NetworkPool]) { + mock.EXPECT(). + List(gomock.Any()). + Return([]serverscom.NetworkPool{p1, p2}, nil) + }, + }, + { + name: "list network pools with pageView", + args: []string{"--page-view"}, + expectedOutput: testutils.ReadFixture(filepath.Join(fixtureBasePath, "list_pageview.txt")), + configureMock: func(mock *mocks.MockCollection[serverscom.NetworkPool]) { + mock.EXPECT(). + List(gomock.Any()). + Return([]serverscom.NetworkPool{p1, p2}, nil) + }, + }, + { + name: "list network pools error", + expectError: true, + configureMock: func(mock *mocks.MockCollection[serverscom.NetworkPool]) { + mock.EXPECT(). + List(gomock.Any()). + Return(nil, errors.New("some error")) + }, + }, + } + + mockCtrl := gomock.NewController(t) + defer mockCtrl.Finish() + poolService := mocks.NewMockNetworkPoolsService(mockCtrl) + collection := mocks.NewMockCollection[serverscom.NetworkPool](mockCtrl) + + poolService.EXPECT().Collection().Return(collection).AnyTimes() + collection.EXPECT().SetParam(gomock.Any(), gomock.Any()).Return(collection).AnyTimes() + + scClient := serverscom.NewClientWithEndpoint("", "") + scClient.NetworkPools = poolService + + for _, tc := range testCases { + t.Run(tc.name, func(t *testing.T) { + g := NewWithT(t) + if tc.configureMock != nil { + tc.configureMock(collection) + } + testCmdContext := testutils.NewTestCmdContext(scClient) + cmd := NewCmd(testCmdContext) + + args := append([]string{"network-pools", "list"}, tc.args...) + if tc.output != "" { + args = append(args, "--output", tc.output) + } + builder := testutils.NewTestCommandBuilder(). + WithCommand(cmd). + WithArgs(args) + + c := builder.Build() + err := c.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 TestListSubnetsCmd(t *testing.T) { + title := "testSubnet2" + s1 := testSubnet + s2 := testSubnet + s2.ID += "2" + s2.Title = &title + + testCases := []struct { + name string + networkID string + output string + expectedOutput []byte + args []string + expectError bool + configureMock func(*mocks.MockCollection[serverscom.Subnetwork]) + }{ + { + name: "list all subnets", + output: "json", + args: []string{"-A"}, + expectedOutput: testutils.ReadFixture(filepath.Join(fixtureBasePath, "list_subnets_all.json")), + configureMock: func(mock *mocks.MockCollection[serverscom.Subnetwork]) { + mock.EXPECT(). + Collect(gomock.Any()). + Return([]serverscom.Subnetwork{s1, s2}, nil) + }, + }, + { + name: "list subnets", + networkID: testID, + output: "json", + expectedOutput: testutils.ReadFixture(filepath.Join(fixtureBasePath, "list_subnets.json")), + configureMock: func(mock *mocks.MockCollection[serverscom.Subnetwork]) { + mock.EXPECT(). + List(gomock.Any()). + Return([]serverscom.Subnetwork{s1}, nil) + }, + }, + { + name: "list subnets with template", + args: []string{"--template", "{{range .}}ID: {{.ID}} Title: {{.Title}}\n{{end}}"}, + expectedOutput: testutils.ReadFixture(filepath.Join(fixtureBasePath, "list_subnets_template.txt")), + configureMock: func(mock *mocks.MockCollection[serverscom.Subnetwork]) { + mock.EXPECT(). + List(gomock.Any()). + Return([]serverscom.Subnetwork{s1, s2}, nil) + }, + }, + { + name: "list subnets with pageView", + args: []string{"--page-view"}, + expectedOutput: testutils.ReadFixture(filepath.Join(fixtureBasePath, "list_subnets_pageview.txt")), + configureMock: func(mock *mocks.MockCollection[serverscom.Subnetwork]) { + mock.EXPECT(). + List(gomock.Any()). + Return([]serverscom.Subnetwork{s1, s2}, nil) + }, + }, + { + name: "list subnets error", + networkID: testID, + expectError: true, + configureMock: func(mock *mocks.MockCollection[serverscom.Subnetwork]) { + mock.EXPECT(). + List(gomock.Any()). + Return(nil, errors.New("some error")) + }, + }, + } + + mockCtrl := gomock.NewController(t) + defer mockCtrl.Finish() + poolService := mocks.NewMockNetworkPoolsService(mockCtrl) + collection := mocks.NewMockCollection[serverscom.Subnetwork](mockCtrl) + + poolService.EXPECT().Subnetworks(gomock.Any()).Return(collection).AnyTimes() + collection.EXPECT().SetParam(gomock.Any(), gomock.Any()).Return(collection).AnyTimes() + + scClient := serverscom.NewClientWithEndpoint("", "") + scClient.NetworkPools = poolService + + for _, tc := range testCases { + t.Run(tc.name, func(t *testing.T) { + g := NewWithT(t) + if tc.configureMock != nil { + tc.configureMock(collection) + } + testCmdContext := testutils.NewTestCmdContext(scClient) + cmd := NewCmd(testCmdContext) + + args := append([]string{"network-pools", "list-subnets", tc.networkID}, tc.args...) + if tc.output != "" { + args = append(args, "--output", tc.output) + } + builder := testutils.NewTestCommandBuilder(). + WithCommand(cmd). + WithArgs(args) + c := builder.Build() + err := c.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 TestCreateSubnetCmd(t *testing.T) { + testCases := []struct { + name string + args []string + output string + expectedOutput []byte + configureMock func(*mocks.MockNetworkPoolsService) + expectError bool + }{ + { + name: "create subnet", + args: []string{testID, "--title", subnetTitle, "--cidr", cidr}, + output: "json", + expectedOutput: testutils.ReadFixture(filepath.Join(fixtureBasePath, "get_subnet.json")), + configureMock: func(mock *mocks.MockNetworkPoolsService) { + mock.EXPECT(). + CreateSubnetwork(gomock.Any(), testID, gomock.AssignableToTypeOf(serverscom.SubnetworkCreateInput{})). + Return(&testSubnet, nil) + }, + }, + { + name: "create subnet error", + args: []string{testID, "--title", subnetTitle, "--cidr", cidr}, + expectError: true, + configureMock: func(mock *mocks.MockNetworkPoolsService) { + mock.EXPECT(). + CreateSubnetwork(gomock.Any(), testID, gomock.AssignableToTypeOf(serverscom.SubnetworkCreateInput{})). + Return(nil, errors.New("some error")) + }, + }, + } + + mockCtrl := gomock.NewController(t) + defer mockCtrl.Finish() + poolService := mocks.NewMockNetworkPoolsService(mockCtrl) + scClient := serverscom.NewClientWithEndpoint("", "") + scClient.NetworkPools = poolService + + for _, tc := range testCases { + t.Run(tc.name, func(t *testing.T) { + g := NewWithT(t) + if tc.configureMock != nil { + tc.configureMock(poolService) + } + ctx := testutils.NewTestCmdContext(scClient) + cmd := NewCmd(ctx) + + args := append([]string{"network-pools", "add-subnet"}, tc.args...) + if tc.output != "" { + args = append(args, "--output", tc.output) + } + + builder := testutils.NewTestCommandBuilder(). + WithCommand(cmd). + WithArgs(args) + + c := builder.Build() + err := c.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 TestUpdateNetworkPoolCmd(t *testing.T) { + updatedPool := testNetworkPool + title := "updated" + updatedPool.Title = &title + testCases := []struct { + name string + id string + output string + args []string + configureMock func(*mocks.MockNetworkPoolsService) + expectedOutput []byte + expectError bool + }{ + { + name: "update network pool", + id: testID, + output: "json", + expectedOutput: testutils.ReadFixture(filepath.Join(fixtureBasePath, "update.json")), + args: []string{"--title", "updated"}, + configureMock: func(mock *mocks.MockNetworkPoolsService) { + mock.EXPECT(). + Update(gomock.Any(), testID, gomock.AssignableToTypeOf(serverscom.NetworkPoolInput{})). + Return(&updatedPool, nil) + }, + }, + { + name: "update network pool error", + id: testID, + args: []string{"--title", "updated"}, + configureMock: func(mock *mocks.MockNetworkPoolsService) { + mock.EXPECT(). + Update(gomock.Any(), testID, gomock.AssignableToTypeOf(serverscom.NetworkPoolInput{})). + Return(nil, errors.New("some error")) + }, + expectError: true, + }, + } + + mockCtrl := gomock.NewController(t) + defer mockCtrl.Finish() + poolService := mocks.NewMockNetworkPoolsService(mockCtrl) + scClient := serverscom.NewClientWithEndpoint("", "") + scClient.NetworkPools = poolService + + for _, tc := range testCases { + t.Run(tc.name, func(t *testing.T) { + g := NewWithT(t) + if tc.configureMock != nil { + tc.configureMock(poolService) + } + testCmdContext := testutils.NewTestCmdContext(scClient) + cmd := NewCmd(testCmdContext) + + args := append([]string{"network-pools", "update", tc.id}, tc.args...) + if tc.output != "" { + args = append(args, "--output", tc.output) + } + + builder := testutils.NewTestCommandBuilder(). + WithCommand(cmd). + WithArgs(args) + c := builder.Build() + err := c.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 TestUpdateSubnetCmd(t *testing.T) { + updatedSubnet := testSubnet + title := "updated" + updatedSubnet.Title = &title + testCases := []struct { + name string + networkID string + subnetID string + args []string + output string + configureMock func(*mocks.MockNetworkPoolsService) + expectedOutput []byte + expectError bool + }{ + { + name: "update subnet", + networkID: testID, + subnetID: testSubnetID, + output: "json", + args: []string{"--network-id", testSubnetID, "--title", subnetTitle}, + expectedOutput: testutils.ReadFixture(filepath.Join(fixtureBasePath, "update_subnet.json")), + configureMock: func(mock *mocks.MockNetworkPoolsService) { + mock.EXPECT(). + UpdateSubnetwork(gomock.Any(), testID, testSubnetID, gomock.AssignableToTypeOf(serverscom.SubnetworkUpdateInput{})). + Return(&updatedSubnet, nil) + }, + }, + { + name: "update subnet error", + networkID: testID, + subnetID: testSubnetID, + args: []string{"--network-id", testSubnetID, "--title", subnetTitle}, + configureMock: func(mock *mocks.MockNetworkPoolsService) { + mock.EXPECT(). + UpdateSubnetwork(gomock.Any(), testID, testSubnetID, gomock.AssignableToTypeOf(serverscom.SubnetworkUpdateInput{})). + Return(nil, errors.New("some error")) + }, + expectError: true, + }, + } + + mockCtrl := gomock.NewController(t) + defer mockCtrl.Finish() + poolService := mocks.NewMockNetworkPoolsService(mockCtrl) + scClient := serverscom.NewClientWithEndpoint("", "") + scClient.NetworkPools = poolService + + for _, tc := range testCases { + t.Run(tc.name, func(t *testing.T) { + g := NewWithT(t) + if tc.configureMock != nil { + tc.configureMock(poolService) + } + ctx := testutils.NewTestCmdContext(scClient) + cmd := NewCmd(ctx) + + args := append([]string{"network-pools", "update-subnet", tc.networkID}, tc.args...) + if tc.output != "" { + args = append(args, "--output", tc.output) + } + + builder := testutils.NewTestCommandBuilder(). + WithCommand(cmd). + WithArgs(args) + + c := builder.Build() + err := c.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 TestDeleteSubnetCmd(t *testing.T) { + testCases := []struct { + name string + networkID string + subnetID string + expectError bool + }{ + { + name: "delete subnet", + networkID: testID, + subnetID: testSubnetID, + }, + { + name: "delete subnet error", + networkID: testID, + subnetID: testSubnetID, + expectError: true, + }, + } + mockCtrl := gomock.NewController(t) + defer mockCtrl.Finish() + poolService := mocks.NewMockNetworkPoolsService(mockCtrl) + scClient := serverscom.NewClientWithEndpoint("", "") + scClient.NetworkPools = poolService + + 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") + } + poolService.EXPECT(). + DeleteSubnetwork(gomock.Any(), tc.networkID, tc.subnetID). + Return(err) + + testCmdContext := testutils.NewTestCmdContext(scClient) + cmd := NewCmd(testCmdContext) + + args := []string{"network-pools", "delete", tc.networkID, "--network-id", tc.subnetID} + builder := testutils.NewTestCommandBuilder(). + WithCommand(cmd). + WithArgs(args) + c := builder.Build() + err = c.Execute() + if tc.expectError { + g.Expect(err).To(HaveOccurred()) + } else { + g.Expect(err).To(BeNil()) + } + }) + } +} diff --git a/cmd/entities/network-pools/update.go b/cmd/entities/network-pools/update.go new file mode 100644 index 0000000..07a14fd --- /dev/null +++ b/cmd/entities/network-pools/update.go @@ -0,0 +1,104 @@ +package networkpools + +import ( + "log" + + serverscom "github.com/serverscom/serverscom-go-client/pkg" + "github.com/serverscom/srvctl/cmd/base" + "github.com/spf13/cobra" +) + +func newUpdateCmd(cmdContext *base.CmdContext) *cobra.Command { + var title string + var labels []string + + cmd := &cobra.Command{ + Use: "update ", + Short: "Update a network pool", + Long: "Update a network pool 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) + + labelsMap, err := base.ParseLabels(labels) + if err != nil { + log.Fatal(err) + } + input := serverscom.NetworkPoolInput{ + Labels: labelsMap, + } + if cmd.Flags().Changed("title") { + input.Title = &title + } + + scClient := cmdContext.GetClient().SetVerbose(manager.GetVerbose(cmd)).GetScClient() + + networkPoolID := args[0] + sshKey, err := scClient.NetworkPools.Update(ctx, networkPoolID, input) + if err != nil { + return err + } + + if sshKey != nil { + formatter := cmdContext.GetOrCreateFormatter(cmd) + return formatter.Format(sshKey) + } + return nil + }, + } + + cmd.Flags().StringVar(&title, "title", "", "string") + cmd.Flags().StringArrayVarP(&labels, "label", "l", []string{}, "string in key=value format") + + return cmd +} + +func newUpdateSubnetCmd(cmdContext *base.CmdContext) *cobra.Command { + var title string + var subnetID string + + cmd := &cobra.Command{ + Use: "update-subnet ", + Short: "Update a subnetwork for a network pool", + Long: "Update a subnetwork for a network pool 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) + + input := serverscom.SubnetworkUpdateInput{} + if cmd.Flags().Changed("title") { + input.Title = &title + } + + scClient := cmdContext.GetClient().SetVerbose(manager.GetVerbose(cmd)).GetScClient() + + networkPoolID := args[0] + sshKey, err := scClient.NetworkPools.UpdateSubnetwork(ctx, networkPoolID, subnetID, input) + if err != nil { + return err + } + + if sshKey != nil { + formatter := cmdContext.GetOrCreateFormatter(cmd) + return formatter.Format(sshKey) + } + return nil + }, + } + + cmd.Flags().StringVar(&title, "title", "", "Subnetwork name") + cmd.Flags().StringVar(&subnetID, "network-id", "", "A unique identifier of a pool") + _ = cmd.MarkFlagRequired("network-id") + + return cmd +} diff --git a/cmd/entities/ssh-keys/update.go b/cmd/entities/ssh-keys/update.go index fd4d859..44b67a4 100644 --- a/cmd/entities/ssh-keys/update.go +++ b/cmd/entities/ssh-keys/update.go @@ -50,7 +50,7 @@ func newUpdateCmd(cmdContext *base.CmdContext) *cobra.Command { }, } - cmd.Flags().StringVarP(&name, "name", "n", "", "string") + cmd.Flags().StringVarP(&name, "name", "n", "", "A Name of an SSH key") cmd.Flags().StringArrayVarP(&labels, "label", "l", []string{}, "string in key=value format") return cmd diff --git a/cmd/root.go b/cmd/root.go index 8b5a62e..6c2ed63 100644 --- a/cmd/root.go +++ b/cmd/root.go @@ -12,6 +12,7 @@ import ( l2segments "github.com/serverscom/srvctl/cmd/entities/l2_segments" loadbalancers "github.com/serverscom/srvctl/cmd/entities/load_balancers" "github.com/serverscom/srvctl/cmd/entities/locations" + networkpools "github.com/serverscom/srvctl/cmd/entities/network-pools" "github.com/serverscom/srvctl/cmd/entities/racks" sbmmodels "github.com/serverscom/srvctl/cmd/entities/sbm_models" sbmosoptions "github.com/serverscom/srvctl/cmd/entities/sbm_os_options" @@ -70,6 +71,7 @@ func NewRootCmd(version string) *cobra.Command { cmd.AddCommand(sbmosoptions.NewCmd(cmdContext)) cmd.AddCommand(sbmmodels.NewCmd(cmdContext)) cmd.AddCommand(l2segments.NewCmd(cmdContext)) + cmd.AddCommand(networkpools.NewCmd(cmdContext)) return cmd } diff --git a/internal/mocks/network_pool_service.go b/internal/mocks/network_pool_service.go new file mode 100644 index 0000000..ba601f5 --- /dev/null +++ b/internal/mocks/network_pool_service.go @@ -0,0 +1,159 @@ +// Code generated by MockGen. DO NOT EDIT. +// Source: ./vendor/github.com/serverscom/serverscom-go-client/pkg/network_pools.go +// +// Generated by this command: +// +// mockgen --destination ./internal/mocks/network_pool_service.go --package=mocks --source ./vendor/github.com/serverscom/serverscom-go-client/pkg/network_pools.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" +) + +// MockNetworkPoolsService is a mock of NetworkPoolsService interface. +type MockNetworkPoolsService struct { + ctrl *gomock.Controller + recorder *MockNetworkPoolsServiceMockRecorder + isgomock struct{} +} + +// MockNetworkPoolsServiceMockRecorder is the mock recorder for MockNetworkPoolsService. +type MockNetworkPoolsServiceMockRecorder struct { + mock *MockNetworkPoolsService +} + +// NewMockNetworkPoolsService creates a new mock instance. +func NewMockNetworkPoolsService(ctrl *gomock.Controller) *MockNetworkPoolsService { + mock := &MockNetworkPoolsService{ctrl: ctrl} + mock.recorder = &MockNetworkPoolsServiceMockRecorder{mock} + return mock +} + +// EXPECT returns an object that allows the caller to indicate expected use. +func (m *MockNetworkPoolsService) EXPECT() *MockNetworkPoolsServiceMockRecorder { + return m.recorder +} + +// Collection mocks base method. +func (m *MockNetworkPoolsService) Collection() serverscom.Collection[serverscom.NetworkPool] { + m.ctrl.T.Helper() + ret := m.ctrl.Call(m, "Collection") + ret0, _ := ret[0].(serverscom.Collection[serverscom.NetworkPool]) + return ret0 +} + +// Collection indicates an expected call of Collection. +func (mr *MockNetworkPoolsServiceMockRecorder) Collection() *gomock.Call { + mr.mock.ctrl.T.Helper() + return mr.mock.ctrl.RecordCallWithMethodType(mr.mock, "Collection", reflect.TypeOf((*MockNetworkPoolsService)(nil).Collection)) +} + +// CreateSubnetwork mocks base method. +func (m *MockNetworkPoolsService) CreateSubnetwork(ctx context.Context, networkPoolID string, input serverscom.SubnetworkCreateInput) (*serverscom.Subnetwork, error) { + m.ctrl.T.Helper() + ret := m.ctrl.Call(m, "CreateSubnetwork", ctx, networkPoolID, input) + ret0, _ := ret[0].(*serverscom.Subnetwork) + ret1, _ := ret[1].(error) + return ret0, ret1 +} + +// CreateSubnetwork indicates an expected call of CreateSubnetwork. +func (mr *MockNetworkPoolsServiceMockRecorder) CreateSubnetwork(ctx, networkPoolID, input any) *gomock.Call { + mr.mock.ctrl.T.Helper() + return mr.mock.ctrl.RecordCallWithMethodType(mr.mock, "CreateSubnetwork", reflect.TypeOf((*MockNetworkPoolsService)(nil).CreateSubnetwork), ctx, networkPoolID, input) +} + +// DeleteSubnetwork mocks base method. +func (m *MockNetworkPoolsService) DeleteSubnetwork(ctx context.Context, networkPoolID, subnetworkID string) error { + m.ctrl.T.Helper() + ret := m.ctrl.Call(m, "DeleteSubnetwork", ctx, networkPoolID, subnetworkID) + ret0, _ := ret[0].(error) + return ret0 +} + +// DeleteSubnetwork indicates an expected call of DeleteSubnetwork. +func (mr *MockNetworkPoolsServiceMockRecorder) DeleteSubnetwork(ctx, networkPoolID, subnetworkID any) *gomock.Call { + mr.mock.ctrl.T.Helper() + return mr.mock.ctrl.RecordCallWithMethodType(mr.mock, "DeleteSubnetwork", reflect.TypeOf((*MockNetworkPoolsService)(nil).DeleteSubnetwork), ctx, networkPoolID, subnetworkID) +} + +// Get mocks base method. +func (m *MockNetworkPoolsService) Get(ctx context.Context, id string) (*serverscom.NetworkPool, error) { + m.ctrl.T.Helper() + ret := m.ctrl.Call(m, "Get", ctx, id) + ret0, _ := ret[0].(*serverscom.NetworkPool) + ret1, _ := ret[1].(error) + return ret0, ret1 +} + +// Get indicates an expected call of Get. +func (mr *MockNetworkPoolsServiceMockRecorder) Get(ctx, id any) *gomock.Call { + mr.mock.ctrl.T.Helper() + return mr.mock.ctrl.RecordCallWithMethodType(mr.mock, "Get", reflect.TypeOf((*MockNetworkPoolsService)(nil).Get), ctx, id) +} + +// GetSubnetwork mocks base method. +func (m *MockNetworkPoolsService) GetSubnetwork(ctx context.Context, networkPoolID, subnetworkID string) (*serverscom.Subnetwork, error) { + m.ctrl.T.Helper() + ret := m.ctrl.Call(m, "GetSubnetwork", ctx, networkPoolID, subnetworkID) + ret0, _ := ret[0].(*serverscom.Subnetwork) + ret1, _ := ret[1].(error) + return ret0, ret1 +} + +// GetSubnetwork indicates an expected call of GetSubnetwork. +func (mr *MockNetworkPoolsServiceMockRecorder) GetSubnetwork(ctx, networkPoolID, subnetworkID any) *gomock.Call { + mr.mock.ctrl.T.Helper() + return mr.mock.ctrl.RecordCallWithMethodType(mr.mock, "GetSubnetwork", reflect.TypeOf((*MockNetworkPoolsService)(nil).GetSubnetwork), ctx, networkPoolID, subnetworkID) +} + +// Subnetworks mocks base method. +func (m *MockNetworkPoolsService) Subnetworks(networkPoolID string) serverscom.Collection[serverscom.Subnetwork] { + m.ctrl.T.Helper() + ret := m.ctrl.Call(m, "Subnetworks", networkPoolID) + ret0, _ := ret[0].(serverscom.Collection[serverscom.Subnetwork]) + return ret0 +} + +// Subnetworks indicates an expected call of Subnetworks. +func (mr *MockNetworkPoolsServiceMockRecorder) Subnetworks(networkPoolID any) *gomock.Call { + mr.mock.ctrl.T.Helper() + return mr.mock.ctrl.RecordCallWithMethodType(mr.mock, "Subnetworks", reflect.TypeOf((*MockNetworkPoolsService)(nil).Subnetworks), networkPoolID) +} + +// Update mocks base method. +func (m *MockNetworkPoolsService) Update(ctx context.Context, id string, input serverscom.NetworkPoolInput) (*serverscom.NetworkPool, error) { + m.ctrl.T.Helper() + ret := m.ctrl.Call(m, "Update", ctx, id, input) + ret0, _ := ret[0].(*serverscom.NetworkPool) + ret1, _ := ret[1].(error) + return ret0, ret1 +} + +// Update indicates an expected call of Update. +func (mr *MockNetworkPoolsServiceMockRecorder) Update(ctx, id, input any) *gomock.Call { + mr.mock.ctrl.T.Helper() + return mr.mock.ctrl.RecordCallWithMethodType(mr.mock, "Update", reflect.TypeOf((*MockNetworkPoolsService)(nil).Update), ctx, id, input) +} + +// UpdateSubnetwork mocks base method. +func (m *MockNetworkPoolsService) UpdateSubnetwork(ctx context.Context, networkPoolID, subnetworkID string, input serverscom.SubnetworkUpdateInput) (*serverscom.Subnetwork, error) { + m.ctrl.T.Helper() + ret := m.ctrl.Call(m, "UpdateSubnetwork", ctx, networkPoolID, subnetworkID, input) + ret0, _ := ret[0].(*serverscom.Subnetwork) + ret1, _ := ret[1].(error) + return ret0, ret1 +} + +// UpdateSubnetwork indicates an expected call of UpdateSubnetwork. +func (mr *MockNetworkPoolsServiceMockRecorder) UpdateSubnetwork(ctx, networkPoolID, subnetworkID, input any) *gomock.Call { + mr.mock.ctrl.T.Helper() + return mr.mock.ctrl.RecordCallWithMethodType(mr.mock, "UpdateSubnetwork", reflect.TypeOf((*MockNetworkPoolsService)(nil).UpdateSubnetwork), ctx, networkPoolID, subnetworkID, input) +} diff --git a/internal/output/entities/init.go b/internal/output/entities/init.go index d16e4ea..8246b46 100644 --- a/internal/output/entities/init.go +++ b/internal/output/entities/init.go @@ -30,4 +30,5 @@ func init() { RegisterOperatingSystemOptionDefinition() RegisterRAMOptionDefinition() RegisterL2SegmentDefinitions() + RegisterNetworkPoolDefinitions() } diff --git a/internal/output/entities/network_pools.go b/internal/output/entities/network_pools.go new file mode 100644 index 0000000..6c0c0bb --- /dev/null +++ b/internal/output/entities/network_pools.go @@ -0,0 +1,60 @@ +package entities + +import ( + "log" + "reflect" + + serverscom "github.com/serverscom/serverscom-go-client/pkg" +) + +var ( + NetworkPoolType = reflect.TypeOf(serverscom.NetworkPool{}) + NetworkPoolListDefaultFields = []string{"ID", "Title", "CIDR", "Type"} + + SubnetworkType = reflect.TypeOf(serverscom.Subnetwork{}) + SubnetworkListDefaultFields = []string{"ID", "Title", "CIDR", "Attached", "InterfaceType"} +) + +func RegisterNetworkPoolDefinitions() { + networkPoolEntity := &Entity{ + fields: []Field{ + {ID: "ID", Name: "ID", Path: "ID", ListHandlerFunc: stringHandler, PageViewHandlerFunc: stringHandler, Default: true}, + {ID: "Title", Name: "Title", Path: "Title", ListHandlerFunc: stringHandler, PageViewHandlerFunc: stringHandler, Default: true}, + {ID: "CIDR", Name: "CIDR", Path: "CIDR", ListHandlerFunc: stringHandler, PageViewHandlerFunc: stringHandler, Default: true}, + {ID: "Type", Name: "Type", Path: "Type", ListHandlerFunc: stringHandler, PageViewHandlerFunc: stringHandler, Default: true}, + {ID: "LocationIDs", Name: "LocationIDs", Path: "LocationIDs", PageViewHandlerFunc: slicePvHandler}, + {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": NetworkPoolListDefaultFields, + }, + eType: NetworkPoolType, + } + if err := Registry.Register(networkPoolEntity); err != nil { + log.Fatal(err) + } + + subnetworkEntity := &Entity{ + fields: []Field{ + {ID: "ID", Name: "ID", Path: "ID", ListHandlerFunc: stringHandler, PageViewHandlerFunc: stringHandler, Default: true}, + {ID: "NetworkPoolID", Name: "NetworkPoolID", Path: "NetworkPoolID", ListHandlerFunc: stringHandler, PageViewHandlerFunc: stringHandler}, + {ID: "Title", Name: "Title", Path: "Title", ListHandlerFunc: stringHandler, PageViewHandlerFunc: stringHandler, Default: true}, + {ID: "CIDR", Name: "CIDR", Path: "CIDR", ListHandlerFunc: stringHandler, PageViewHandlerFunc: stringHandler, Default: true}, + {ID: "Attached", Name: "Attached", Path: "Attached", ListHandlerFunc: stringHandler, PageViewHandlerFunc: stringHandler, Default: true}, + {ID: "InterfaceType", Name: "InterfaceType", Path: "InterfaceType", ListHandlerFunc: stringHandler, PageViewHandlerFunc: stringHandler, Default: true}, + {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": SubnetworkListDefaultFields, + }, + eType: SubnetworkType, + } + if err := Registry.Register(subnetworkEntity); err != nil { + log.Fatal(err) + } +} diff --git a/internal/output/entities/servermodels.go b/internal/output/entities/server_models.go similarity index 100% rename from internal/output/entities/servermodels.go rename to internal/output/entities/server_models.go diff --git a/internal/output/entities/uplinkbandwidths.go b/internal/output/entities/uplink_bandwidths.go similarity index 100% rename from internal/output/entities/uplinkbandwidths.go rename to internal/output/entities/uplink_bandwidths.go diff --git a/internal/output/entities/uplinkmodels.go b/internal/output/entities/uplink_models.go similarity index 100% rename from internal/output/entities/uplinkmodels.go rename to internal/output/entities/uplink_models.go diff --git a/testdata/entities/network-pools/get.json b/testdata/entities/network-pools/get.json new file mode 100644 index 0000000..d148d10 --- /dev/null +++ b/testdata/entities/network-pools/get.json @@ -0,0 +1,15 @@ +{ + "id": "testId", + "title": "testNetworkPool", + "cidr": "192.168.0.0/24", + "type": "private", + "location_ids": [ + 1, + 2 + ], + "labels": { + "environment": "testing" + }, + "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/network-pools/get.txt b/testdata/entities/network-pools/get.txt new file mode 100644 index 0000000..f15fc03 --- /dev/null +++ b/testdata/entities/network-pools/get.txt @@ -0,0 +1,2 @@ +ID Title CIDR Type Created Updated +testId testNetworkPool 192.168.0.0/24 private 2025-01-01T12:00:00Z 2025-01-01T12:00:00Z diff --git a/testdata/entities/network-pools/get.yaml b/testdata/entities/network-pools/get.yaml new file mode 100644 index 0000000..189be0d --- /dev/null +++ b/testdata/entities/network-pools/get.yaml @@ -0,0 +1,11 @@ +id: testId +title: testNetworkPool +cidr: 192.168.0.0/24 +type: private +locationids: + - 1 + - 2 +labels: + environment: testing +created: 2025-01-01T12:00:00Z +updated: 2025-01-01T12:00:00Z diff --git a/testdata/entities/network-pools/get_subnet.json b/testdata/entities/network-pools/get_subnet.json new file mode 100644 index 0000000..e3d7974 --- /dev/null +++ b/testdata/entities/network-pools/get_subnet.json @@ -0,0 +1,10 @@ +{ + "id": "testSubnetId", + "network_pool_id": "testId", + "title": "testSubnet", + "cidr": "192.168.0.0/24", + "attached": false, + "interface_type": "private", + "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/network-pools/get_subnet.txt b/testdata/entities/network-pools/get_subnet.txt new file mode 100644 index 0000000..d82d34f --- /dev/null +++ b/testdata/entities/network-pools/get_subnet.txt @@ -0,0 +1,2 @@ +ID Title CIDR Attached InterfaceType Created Updated +testSubnetId testSubnet 192.168.0.0/24 false private 2025-01-01T12:00:00Z 2025-01-01T12:00:00Z diff --git a/testdata/entities/network-pools/get_subnet.yaml b/testdata/entities/network-pools/get_subnet.yaml new file mode 100644 index 0000000..d00ac66 --- /dev/null +++ b/testdata/entities/network-pools/get_subnet.yaml @@ -0,0 +1,8 @@ +id: testSubnetId +networkpoolid: testId +title: testSubnet +cidr: 192.168.0.0/24 +attached: false +interfacetype: private +created: 2025-01-01T12:00:00Z +updated: 2025-01-01T12:00:00Z diff --git a/testdata/entities/network-pools/list.json b/testdata/entities/network-pools/list.json new file mode 100644 index 0000000..f6b7576 --- /dev/null +++ b/testdata/entities/network-pools/list.json @@ -0,0 +1,17 @@ +[ + { + "id": "testId", + "title": "testNetworkPool", + "cidr": "192.168.0.0/24", + "type": "private", + "location_ids": [ + 1, + 2 + ], + "labels": { + "environment": "testing" + }, + "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/network-pools/list_all.json b/testdata/entities/network-pools/list_all.json new file mode 100644 index 0000000..b212705 --- /dev/null +++ b/testdata/entities/network-pools/list_all.json @@ -0,0 +1,32 @@ +[ + { + "id": "testId", + "title": "testNetworkPool", + "cidr": "192.168.0.0/24", + "type": "private", + "location_ids": [ + 1, + 2 + ], + "labels": { + "environment": "testing" + }, + "created_at": "2025-01-01T12:00:00Z", + "updated_at": "2025-01-01T12:00:00Z" + }, + { + "id": "testId2", + "title": "testNetworkPool2", + "cidr": "192.168.0.0/24", + "type": "private", + "location_ids": [ + 1, + 2 + ], + "labels": { + "environment": "testing" + }, + "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/network-pools/list_pageview.txt b/testdata/entities/network-pools/list_pageview.txt new file mode 100644 index 0000000..a8ff417 --- /dev/null +++ b/testdata/entities/network-pools/list_pageview.txt @@ -0,0 +1,19 @@ +ID: testId +Title: testNetworkPool +CIDR: 192.168.0.0/24 +Type: private +LocationIDs: 1 + 2 +Labels: environment=testing +Created: 2025-01-01T12:00:00Z +Updated: 2025-01-01T12:00:00Z +--- +ID: testId2 +Title: testNetworkPool2 +CIDR: 192.168.0.0/24 +Type: private +LocationIDs: 1 + 2 +Labels: environment=testing +Created: 2025-01-01T12:00:00Z +Updated: 2025-01-01T12:00:00Z diff --git a/testdata/entities/network-pools/list_subnets.json b/testdata/entities/network-pools/list_subnets.json new file mode 100644 index 0000000..22dab25 --- /dev/null +++ b/testdata/entities/network-pools/list_subnets.json @@ -0,0 +1,12 @@ +[ + { + "id": "testSubnetId", + "network_pool_id": "testId", + "title": "testSubnet", + "cidr": "192.168.0.0/24", + "attached": false, + "interface_type": "private", + "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/network-pools/list_subnets_all.json b/testdata/entities/network-pools/list_subnets_all.json new file mode 100644 index 0000000..62c04f8 --- /dev/null +++ b/testdata/entities/network-pools/list_subnets_all.json @@ -0,0 +1,22 @@ +[ + { + "id": "testSubnetId", + "network_pool_id": "testId", + "title": "testSubnet", + "cidr": "192.168.0.0/24", + "attached": false, + "interface_type": "private", + "created_at": "2025-01-01T12:00:00Z", + "updated_at": "2025-01-01T12:00:00Z" + }, + { + "id": "testSubnetId2", + "network_pool_id": "testId", + "title": "testSubnet2", + "cidr": "192.168.0.0/24", + "attached": false, + "interface_type": "private", + "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/network-pools/list_subnets_pageview.txt b/testdata/entities/network-pools/list_subnets_pageview.txt new file mode 100644 index 0000000..2f38006 --- /dev/null +++ b/testdata/entities/network-pools/list_subnets_pageview.txt @@ -0,0 +1,17 @@ +ID: testSubnetId +NetworkPoolID: testId +Title: testSubnet +CIDR: 192.168.0.0/24 +Attached: false +InterfaceType: private +Created: 2025-01-01T12:00:00Z +Updated: 2025-01-01T12:00:00Z +--- +ID: testSubnetId2 +NetworkPoolID: testId +Title: testSubnet2 +CIDR: 192.168.0.0/24 +Attached: false +InterfaceType: private +Created: 2025-01-01T12:00:00Z +Updated: 2025-01-01T12:00:00Z diff --git a/testdata/entities/network-pools/list_subnets_template.txt b/testdata/entities/network-pools/list_subnets_template.txt new file mode 100644 index 0000000..6b88ff0 --- /dev/null +++ b/testdata/entities/network-pools/list_subnets_template.txt @@ -0,0 +1,2 @@ +ID: testSubnetId Title: testSubnet +ID: testSubnetId2 Title: testSubnet2 diff --git a/testdata/entities/network-pools/list_template.txt b/testdata/entities/network-pools/list_template.txt new file mode 100644 index 0000000..e6cb4aa --- /dev/null +++ b/testdata/entities/network-pools/list_template.txt @@ -0,0 +1,2 @@ +ID: testId Title: testNetworkPool +ID: testId2 Title: testNetworkPool2 diff --git a/testdata/entities/network-pools/update.json b/testdata/entities/network-pools/update.json new file mode 100644 index 0000000..2867a9e --- /dev/null +++ b/testdata/entities/network-pools/update.json @@ -0,0 +1,15 @@ +{ + "id": "testId", + "title": "updated", + "cidr": "192.168.0.0/24", + "type": "private", + "location_ids": [ + 1, + 2 + ], + "labels": { + "environment": "testing" + }, + "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/network-pools/update_subnet.json b/testdata/entities/network-pools/update_subnet.json new file mode 100644 index 0000000..2a9dfc2 --- /dev/null +++ b/testdata/entities/network-pools/update_subnet.json @@ -0,0 +1,10 @@ +{ + "id": "testSubnetId", + "network_pool_id": "testId", + "title": "updated", + "cidr": "192.168.0.0/24", + "attached": false, + "interface_type": "private", + "created_at": "2025-01-01T12:00:00Z", + "updated_at": "2025-01-01T12:00:00Z" +} \ No newline at end of file