diff --git a/.gitignore b/.gitignore index 3e7ae8e..174db53 100644 --- a/.gitignore +++ b/.gitignore @@ -1,3 +1,6 @@ .vscode vendor dist +.idea/ +cp.out +.DS_Store diff --git a/cmd/entities/kbm/get_node.go b/cmd/entities/kbm/get_node.go new file mode 100644 index 0000000..a1c3875 --- /dev/null +++ b/cmd/entities/kbm/get_node.go @@ -0,0 +1,39 @@ +package kbm + +import ( + "github.com/serverscom/srvctl/cmd/base" + "github.com/spf13/cobra" +) + +func newGetCmd(cmdContext *base.CmdContext) *cobra.Command { + cmd := &cobra.Command{ + Use: "get-node ", + Short: "Get a KBM node", + Long: "Get a KBM node 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] + node, err := scClient.Hosts.GetKubernetesBaremetalNode(ctx, id) + if err != nil { + return err + } + + if node != nil { + formatter := cmdContext.GetOrCreateFormatter(cmd) + return formatter.Format(node) + } + return nil + }, + } + + return cmd +} diff --git a/cmd/entities/kbm/get_node_power_feeds.go b/cmd/entities/kbm/get_node_power_feeds.go new file mode 100644 index 0000000..6934161 --- /dev/null +++ b/cmd/entities/kbm/get_node_power_feeds.go @@ -0,0 +1,40 @@ +package kbm + +import ( + "github.com/serverscom/srvctl/cmd/base" + "github.com/spf13/cobra" +) + +func newGetPowerFeedsCmd(cmdContext *base.CmdContext) *cobra.Command { + cmd := &cobra.Command{ + Use: "list-node-feeds ", + Short: "List power feeds for a KBM node", + Long: "List power feeds for a KBM node 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] + + powerFeeds, err := scClient.Hosts.KubernetesBaremetalNodePowerFeeds(ctx, id) + if err != nil { + return err + } + + if powerFeeds != nil { + formatter := cmdContext.GetOrCreateFormatter(cmd) + return formatter.Format(powerFeeds) + } + return nil + }, + } + + return cmd +} diff --git a/cmd/entities/kbm/kbm_node.go b/cmd/entities/kbm/kbm_node.go new file mode 100644 index 0000000..43e14cc --- /dev/null +++ b/cmd/entities/kbm/kbm_node.go @@ -0,0 +1,43 @@ +package kbm + +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 { + kbmEntity, err := entities.Registry.GetEntityFromValue(serverscom.KubernetesBaremetalNode{}) + if err != nil { + log.Fatal(err) + } + entitiesMap := make(map[string]entities.EntityInterface) + entitiesMap["kbm"] = kbmEntity + cmd := &cobra.Command{ + Use: "kbm", + Short: "Manage kubernetes baremetal nodes", + PersistentPreRunE: base.CombinePreRunE( + base.CheckFormatterFlags(cmdContext, entitiesMap), + base.CheckEmptyContexts(cmdContext), + ), + Args: base.NoArgs, + Run: base.UsageRun, + } + + cmd.AddCommand( + newListCmd(cmdContext), + newGetCmd(cmdContext), + newGetPowerFeedsCmd(cmdContext), + newUpdateCmd(cmdContext), + newPowerCmd(cmdContext), + newListNetworksCmd(cmdContext), + newListSlotsCmd(cmdContext), + ) + + base.AddFormatFlags(cmd) + + return cmd +} diff --git a/cmd/entities/kbm/kbm_node_test.go b/cmd/entities/kbm/kbm_node_test.go new file mode 100644 index 0000000..13e79f2 --- /dev/null +++ b/cmd/entities/kbm/kbm_node_test.go @@ -0,0 +1,867 @@ +package kbm + +import ( + "errors" + . "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" + "path/filepath" + "strings" + "testing" + "time" +) + +var ( + testId = "testId" + testNetworkId = "testNetId" + fixtureBasePath = filepath.Join("..", "..", "..", "testdata", "entities", "kbm") + fixedTime = time.Date(2025, 1, 1, 12, 0, 0, 0, time.UTC) + testPublicIP = "1.2.3.4" + testLocationCode = "test" + testConfigDetails = serverscom.ConfigurationDetails{ + RAMSize: 2, + ServerModelID: testutils.PtrInt64(1), + ServerModelName: testutils.PtrString("server-model-123"), + PublicUplinkID: testutils.PtrInt64(2), + PublicUplinkName: testutils.PtrString("Public 1 Gbps without redundancy"), + PrivateUplinkID: testutils.PtrInt64(3), + PrivateUplinkName: testutils.PtrString("Private 1 Gbps without redundancy"), + BandwidthID: testutils.PtrInt64(4), + BandwidthName: testutils.PtrString("20000 GB"), + OperatingSystemID: testutils.PtrInt64(5), + OperatingSystemFullName: testutils.PtrString("CentOS 7 x86_64"), + } + testKBM = serverscom.KubernetesBaremetalNode{ + ID: testId, + RackID: testId, + KubernetesClusterID: testId, + KubernetesClusterNodeID: testId, + KubernetesClusterNodeNumber: 1, + Type: "kubernetes_baremetal_node", + Title: "example.aa", + Status: "active", + LocationCode: testLocationCode, + PublicIPv4Address: &testPublicIP, + ConfigurationDetails: testConfigDetails, + Created: fixedTime, + Updated: fixedTime, + } + netTitle = "Some Net" + cidr = "100.0.8.0/29" + testNetwork = serverscom.Network{ + ID: testNetworkId, + Title: &netTitle, + Status: "active", + Cidr: &cidr, + Family: "ipv4", + InterfaceType: "public", + DistributionMethod: "gateway", + Additional: false, + Created: fixedTime, + Updated: fixedTime, + } + testDriveModel = serverscom.DriveModel{ + ID: int64(10), + Name: "ssd-model-749", + Capacity: 100, + Interface: "SATA3", + FormFactor: "2.5", + MediaType: "SSD", + } + testDriveSlot = serverscom.HostDriveSlot{ + Position: 1, + Interface: "SAS", + FormFactor: "2.5\"", + DriveModel: &testDriveModel, + } + testPowerFeed = serverscom.HostPowerFeed{ + Name: "testPowerFeed", + Status: "on", + } +) + +func TestNewUpdateCmd(t *testing.T) { + newServer := testKBM + newServer.Labels = map[string]string{"new": "label"} + + testCases := []struct { + name string + id string + output string + args []string + configureMock func(*mocks.MockHostsService) + expectedOutput []byte + expectError bool + }{ + { + name: "update KBM node", + id: testId, + output: "json", + expectedOutput: testutils.ReadFixture(filepath.Join(fixtureBasePath, "update_node.json")), + args: []string{"--label", "new=label"}, + configureMock: func(mock *mocks.MockHostsService) { + mock.EXPECT(). + UpdateKubernetesBaremetalNode(gomock.Any(), testId, serverscom.KubernetesBaremetalNodeUpdateInput{ + Labels: map[string]string{"new": "label"}, + }). + Return(&newServer, nil) + }, + }, + { + name: "update KBM node with error", + id: testId, + configureMock: func(mock *mocks.MockHostsService) { + mock.EXPECT(). + UpdateKubernetesBaremetalNode(gomock.Any(), testId, serverscom.KubernetesBaremetalNodeUpdateInput{ + Labels: make(map[string]string), + }). + Return(nil, errors.New("some error")) + }, + expectError: true, + }, + } + + mockCtrl := gomock.NewController(t) + defer mockCtrl.Finish() + + hostsServiceHandler := mocks.NewMockHostsService(mockCtrl) + + scClient := serverscom.NewClientWithEndpoint("", "") + scClient.Hosts = hostsServiceHandler + + for _, tc := range testCases { + t.Run(tc.name, func(t *testing.T) { + g := NewWithT(t) + + if tc.configureMock != nil { + tc.configureMock(hostsServiceHandler) + } + + testCmdContext := testutils.NewTestCmdContext(scClient) + sshCmd := NewCmd(testCmdContext) + + args := []string{"kbm", "update-node", 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(sshCmd). + 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 TestNewListCmd(t *testing.T) { + testServer1 := testKBM + testServer2 := testServer1 + testServer2.ID = "testId2" + testServer2.Title = "example.bb" + + testCases := []struct { + name string + output string + args []string + expectedOutput []byte + expectError bool + configureMock func(*mocks.MockCollection[serverscom.KubernetesBaremetalNode]) + }{ + { + name: "list all KMB nodes", + output: "json", + args: []string{"-A"}, + expectedOutput: testutils.ReadFixture(filepath.Join(fixtureBasePath, "list_nodes_all.json")), + configureMock: func(mock *mocks.MockCollection[serverscom.KubernetesBaremetalNode]) { + mock.EXPECT(). + Collect(gomock.Any()). + Return([]serverscom.KubernetesBaremetalNode{ + testServer1, + testServer2, + }, nil) + }, + }, + { + name: "list KBM nodes", + output: "json", + expectedOutput: testutils.ReadFixture(filepath.Join(fixtureBasePath, "list_nodes.json")), + configureMock: func(mock *mocks.MockCollection[serverscom.KubernetesBaremetalNode]) { + mock.EXPECT(). + List(gomock.Any()). + Return([]serverscom.KubernetesBaremetalNode{ + testServer1, + }, nil) + }, + }, + { + name: "list KBM nodes with template", + args: []string{"--template", "{{range .}}Title: {{.Title}} Type: {{.Type}}\n{{end}}"}, + expectedOutput: testutils.ReadFixture(filepath.Join(fixtureBasePath, "list_nodes_template.txt")), + configureMock: func(mock *mocks.MockCollection[serverscom.KubernetesBaremetalNode]) { + mock.EXPECT(). + List(gomock.Any()). + Return([]serverscom.KubernetesBaremetalNode{ + testServer1, + testServer2, + }, nil) + }, + }, + { + name: "list KBM nodes with pageView", + args: []string{"--page-view"}, + expectedOutput: testutils.ReadFixture(filepath.Join(fixtureBasePath, "list_nodes_pageview.txt")), + configureMock: func(mock *mocks.MockCollection[serverscom.KubernetesBaremetalNode]) { + mock.EXPECT(). + List(gomock.Any()). + Return([]serverscom.KubernetesBaremetalNode{ + testServer1, + testServer2, + }, nil) + }, + }, + { + name: "list KBM nodes with error", + expectError: true, + configureMock: func(mock *mocks.MockCollection[serverscom.KubernetesBaremetalNode]) { + mock.EXPECT(). + List(gomock.Any()). + Return(nil, errors.New("some error")) + }, + }, + } + + mockCtrl := gomock.NewController(t) + defer mockCtrl.Finish() + + hostsServiceHandler := mocks.NewMockHostsService(mockCtrl) + collectionHandler := mocks.NewMockCollection[serverscom.KubernetesBaremetalNode](mockCtrl) + + hostsServiceHandler.EXPECT(). + ListKubernetesBaremetalNodes(). + Return(collectionHandler). + AnyTimes() + + collectionHandler.EXPECT(). + SetParam(gomock.Any(), gomock.Any()). + Return(collectionHandler). + AnyTimes() + + scClient := serverscom.NewClientWithEndpoint("", "") + scClient.Hosts = hostsServiceHandler + + 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) + hostsCmd := NewCmd(testCmdContext) + + args := []string{"kbm", "list-nodes"} + if len(tc.args) > 0 { + args = append(args, tc.args...) + } + if tc.output != "" { + args = append(args, "--output", tc.output) + } + + builder := testutils.NewTestCommandBuilder(). + WithCommand(hostsCmd). + 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 TestNewPowerCmd(t *testing.T) { + testServer := testKBM + testServer.Labels = map[string]string{"new": "label"} + + testCases := []struct { + name string + id string + output string + args []string + expectedOutput []byte + expectError bool + }{ + { + name: "power on kbm node", + id: testId, + args: []string{"kbm", "set-node-power", testId, "--power=on"}, + output: "json", + expectedOutput: testutils.ReadFixture(filepath.Join(fixtureBasePath, "set_node_power.json")), + }, + { + name: "power off kbm node", + id: testId, + args: []string{"kbm", "set-node-power", testId, "--power=off"}, + output: "json", + expectedOutput: testutils.ReadFixture(filepath.Join(fixtureBasePath, "set_node_power.json")), + }, + { + name: "power cycle kbm node", + id: testId, + args: []string{"kbm", "set-node-power", testId, "--power=cycle"}, + output: "json", + expectedOutput: testutils.ReadFixture(filepath.Join(fixtureBasePath, "set_node_power.json")), + }, + { + name: "power on kbm node with error", + id: testId, + args: []string{"kbm", "set-node-power", testId, "--power=on"}, + expectError: true, + }, + { + name: "power on kbm node without flag error", + id: testId, + args: []string{"kbm", "set-node-power", testId}, + expectError: true, + }, + } + + mockCtrl := gomock.NewController(t) + defer mockCtrl.Finish() + + hostsServiceHandler := mocks.NewMockHostsService(mockCtrl) + + scClient := serverscom.NewClientWithEndpoint("", "") + scClient.Hosts = hostsServiceHandler + + 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") + } + + var powerVal string + if tc.args != nil { + powerVal = strings.TrimPrefix(tc.args[len(tc.args)-1], "--power=") + } + expectPowerCall(hostsServiceHandler, powerVal, testId, &testServer, err) + + testCmdContext := testutils.NewTestCmdContext(scClient) + hostsCmd := NewCmd(testCmdContext) + + if tc.output != "" { + tc.args = append(tc.args, "--output", tc.output) + } + + builder := testutils.NewTestCommandBuilder(). + WithCommand(hostsCmd). + WithArgs(tc.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 TestNewGetCmd(t *testing.T) { + testServer := testKBM + testCases := []struct { + name string + id string + output string + expectedOutput []byte + expectError bool + }{ + { + name: "get KBM node in default format", + id: testId, + output: "", + expectedOutput: testutils.ReadFixture(filepath.Join(fixtureBasePath, "get_node.txt")), + }, + { + name: "get KBM node in JSON format", + id: testId, + output: "json", + expectedOutput: testutils.ReadFixture(filepath.Join(fixtureBasePath, "get_node.json")), + }, + { + name: "get KBM node in YAML format", + id: testId, + output: "yaml", + expectedOutput: testutils.ReadFixture(filepath.Join(fixtureBasePath, "get_node.yaml")), + }, + { + name: "get KBM node with error", + id: testId, + expectError: true, + }, + } + + mockCtrl := gomock.NewController(t) + defer mockCtrl.Finish() + + hostsServiceHandler := mocks.NewMockHostsService(mockCtrl) + + scClient := serverscom.NewClientWithEndpoint("", "") + scClient.Hosts = hostsServiceHandler + + 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") + } + hostsServiceHandler.EXPECT(). + GetKubernetesBaremetalNode(gomock.Any(), testId). + Return(&testServer, err) + + testCmdContext := testutils.NewTestCmdContext(scClient) + hostsCmd := NewCmd(testCmdContext) + + args := []string{"kbm", "get-node", tc.id} + if tc.output != "" { + args = append(args, "--output", tc.output) + } + + builder := testutils.NewTestCommandBuilder(). + WithCommand(hostsCmd). + 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 TestNewListNetworksCmd(t *testing.T) { + testNetwork1 := testNetwork + testNetwork1.ID = testNetworkId + testNetwork2 := testNetwork1 + testNetwork2.ID = "testNetId2" + netTitle2 := "Another Net" + testNetwork2.Title = &netTitle2 + + testCases := []struct { + name string + output string + args []string + expectedOutput []byte + expectError bool + configureMock func(*mocks.MockCollection[serverscom.Network]) + }{ + { + name: "list KBM node all networks", + output: "json", + args: []string{"testServerId", "-A"}, + expectedOutput: testutils.ReadFixture(filepath.Join(fixtureBasePath, "list_node_networks_all.json")), + configureMock: func(mock *mocks.MockCollection[serverscom.Network]) { + mock.EXPECT(). + Collect(gomock.Any()). + Return([]serverscom.Network{ + testNetwork1, + testNetwork2, + }, nil) + }, + }, + { + name: "list KBM node networks", + output: "json", + args: []string{"testServerId"}, + expectedOutput: testutils.ReadFixture(filepath.Join(fixtureBasePath, "list_node_networks.json")), + configureMock: func(mock *mocks.MockCollection[serverscom.Network]) { + mock.EXPECT(). + List(gomock.Any()). + Return([]serverscom.Network{ + testNetwork1, + }, nil) + }, + }, + { + name: "list DS networks with template", + args: []string{"testServerId", "--template", "{{range .}}Network: {{.ID}} Title: {{.Title}}\n{{end}}"}, + expectedOutput: testutils.ReadFixture(filepath.Join(fixtureBasePath, "list_node_networks_template.txt")), + configureMock: func(mock *mocks.MockCollection[serverscom.Network]) { + mock.EXPECT(). + List(gomock.Any()). + Return([]serverscom.Network{ + testNetwork1, + testNetwork2, + }, nil) + }, + }, + { + name: "list DS networks with pageView", + args: []string{"testServerId", "--page-view"}, + expectedOutput: testutils.ReadFixture(filepath.Join(fixtureBasePath, "list_node_networks_pageview.txt")), + configureMock: func(mock *mocks.MockCollection[serverscom.Network]) { + mock.EXPECT(). + List(gomock.Any()). + Return([]serverscom.Network{ + testNetwork1, + testNetwork2, + }, nil) + }, + }, + { + name: "list DS networks with error", + args: []string{"testServerId"}, + expectError: true, + configureMock: func(mock *mocks.MockCollection[serverscom.Network]) { + mock.EXPECT(). + List(gomock.Any()). + Return(nil, errors.New("some error")) + }, + }, + } + + mockCtrl := gomock.NewController(t) + defer mockCtrl.Finish() + + hostsServiceHandler := mocks.NewMockHostsService(mockCtrl) + collectionHandler := mocks.NewMockCollection[serverscom.Network](mockCtrl) + + hostsServiceHandler.EXPECT(). + KubernetesBaremetalNodeNetworks(gomock.Any()). + Return(collectionHandler). + AnyTimes() + + collectionHandler.EXPECT(). + SetParam(gomock.Any(), gomock.Any()). + Return(collectionHandler). + AnyTimes() + + scClient := serverscom.NewClientWithEndpoint("", "") + scClient.Hosts = hostsServiceHandler + + 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) + hostsCmd := NewCmd(testCmdContext) + + args := []string{"kbm", "list-node-networks"} + if len(tc.args) > 0 { + args = append(args, tc.args...) + } + if tc.output != "" { + args = append(args, "--output", tc.output) + } + + builder := testutils.NewTestCommandBuilder(). + WithCommand(hostsCmd). + 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 TestNewListSlotsCmd(t *testing.T) { + testDriveSlot1 := testDriveSlot + testDriveSlot2 := testDriveSlot1 + testDriveSlot2.Position = 2 + + testCases := []struct { + name string + output string + args []string + expectedOutput []byte + expectError bool + configureMock func(*mocks.MockCollection[serverscom.HostDriveSlot]) + }{ + { + name: "list KBM node all slots", + output: "json", + args: []string{"testServerId", "-A"}, + expectedOutput: testutils.ReadFixture(filepath.Join(fixtureBasePath, "list_node_slots_all.json")), + configureMock: func(mock *mocks.MockCollection[serverscom.HostDriveSlot]) { + mock.EXPECT(). + Collect(gomock.Any()). + Return([]serverscom.HostDriveSlot{ + testDriveSlot1, + testDriveSlot2, + }, nil) + }, + }, + { + name: "list KBM node slots", + output: "json", + args: []string{"testServerId"}, + expectedOutput: testutils.ReadFixture(filepath.Join(fixtureBasePath, "list_node_slots.json")), + configureMock: func(mock *mocks.MockCollection[serverscom.HostDriveSlot]) { + mock.EXPECT(). + List(gomock.Any()). + Return([]serverscom.HostDriveSlot{ + testDriveSlot1, + }, nil) + }, + }, + { + name: "list KBM node slots with template", + args: []string{"testServerId", "--template", "{{range .}}Position: {{.Position}} Interface: {{.Interface}}\n{{end}}"}, + expectedOutput: testutils.ReadFixture(filepath.Join(fixtureBasePath, "list_node_slots_template.txt")), + configureMock: func(mock *mocks.MockCollection[serverscom.HostDriveSlot]) { + mock.EXPECT(). + List(gomock.Any()). + Return([]serverscom.HostDriveSlot{ + testDriveSlot1, + testDriveSlot2, + }, nil) + }, + }, + { + name: "list KBM node slots with pageView", + args: []string{"testServerId", "--page-view"}, + expectedOutput: testutils.ReadFixture(filepath.Join(fixtureBasePath, "list_node_slots_pageview.txt")), + configureMock: func(mock *mocks.MockCollection[serverscom.HostDriveSlot]) { + mock.EXPECT(). + List(gomock.Any()). + Return([]serverscom.HostDriveSlot{ + testDriveSlot1, + testDriveSlot2, + }, nil) + }, + }, + { + name: "list KBM node slots with error", + args: []string{"testServerId"}, + expectError: true, + configureMock: func(mock *mocks.MockCollection[serverscom.HostDriveSlot]) { + mock.EXPECT(). + List(gomock.Any()). + Return(nil, errors.New("some error")) + }, + }, + } + + mockCtrl := gomock.NewController(t) + defer mockCtrl.Finish() + + hostsServiceHandler := mocks.NewMockHostsService(mockCtrl) + collectionHandler := mocks.NewMockCollection[serverscom.HostDriveSlot](mockCtrl) + + hostsServiceHandler.EXPECT(). + KubernetesBaremetalNodeDriveSlots(gomock.Any()). + Return(collectionHandler). + AnyTimes() + + collectionHandler.EXPECT(). + SetParam(gomock.Any(), gomock.Any()). + Return(collectionHandler). + AnyTimes() + + scClient := serverscom.NewClientWithEndpoint("", "") + scClient.Hosts = hostsServiceHandler + + 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) + hostsCmd := NewCmd(testCmdContext) + + args := []string{"kbm", "list-node-slots"} + if len(tc.args) > 0 { + args = append(args, tc.args...) + } + if tc.output != "" { + args = append(args, "--output", tc.output) + } + + builder := testutils.NewTestCommandBuilder(). + WithCommand(hostsCmd). + 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 TestNewGetPowerFeedsCmd(t *testing.T) { + testPowerFeed1 := testPowerFeed + testPowerFeed2 := testPowerFeed1 + + testPowerFeed2.Name = "testPowerFeed2" + testPowerFeed2.Status = "off" + + testCases := []struct { + name string + id string + output string + args []string + expectedOutput []byte + expectError bool + configureMock func(*mocks.MockCollection[serverscom.HostPowerFeed]) + }{ + { + name: "get KBM node power_feeds in default format", + id: testId, + output: "", + expectedOutput: testutils.ReadFixture(filepath.Join(fixtureBasePath, "get_node_power_feeds.txt")), + }, + { + name: "get KBM node power_feeds", + id: testId, + output: "json", + expectedOutput: testutils.ReadFixture(filepath.Join(fixtureBasePath, "get_node_power_feeds.json")), + }, + { + name: "get KBM node power_feeds in YAML format", + id: testId, + output: "yaml", + expectedOutput: testutils.ReadFixture(filepath.Join(fixtureBasePath, "get_node_power_feeds.yaml")), + }, + { + name: "get KBM node power_feeds with error", + id: testId, + expectError: true, + }, + } + + mockCtrl := gomock.NewController(t) + defer mockCtrl.Finish() + + hostsServiceHandler := mocks.NewMockHostsService(mockCtrl) + + scClient := serverscom.NewClientWithEndpoint("", "") + scClient.Hosts = hostsServiceHandler + + 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") + } + hostsServiceHandler.EXPECT(). + KubernetesBaremetalNodePowerFeeds(gomock.Any(), testId). + Return([]serverscom.HostPowerFeed{testPowerFeed1, testPowerFeed2}, err) + + testCmdContext := testutils.NewTestCmdContext(scClient) + hostsCmd := NewCmd(testCmdContext) + + args := []string{"kbm", "list-node-feeds", tc.id} + if tc.output != "" { + args = append(args, "--output", tc.output) + } + + builder := testutils.NewTestCommandBuilder(). + WithCommand(hostsCmd). + 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 expectPowerCall(m *mocks.MockHostsService, action string, id string, s *serverscom.KubernetesBaremetalNode, err error) { + calls := map[string]func() *gomock.Call{ + "on": func() *gomock.Call { return m.EXPECT().PowerOnKubernetesBaremetalNode(gomock.Any(), id) }, + "off": func() *gomock.Call { return m.EXPECT().PowerOffKubernetesBaremetalNode(gomock.Any(), id) }, + "cycle": func() *gomock.Call { return m.EXPECT().PowerCycleKubernetesBaremetalNode(gomock.Any(), id) }, + } + + if action == "" { + m.EXPECT(). + PowerOnKubernetesBaremetalNode(gomock.Any(), id). + Times(0) + m.EXPECT(). + PowerOffKubernetesBaremetalNode(gomock.Any(), id). + Times(0) + m.EXPECT(). + PowerCycleKubernetesBaremetalNode(gomock.Any(), id). + Times(0) + + gomock.InOrder( + calls["on"]().Times(0), + calls["off"]().Times(0), + calls["cycle"]().Times(0), + ) + + return + } + + if h, ok := calls[action]; ok { + h().Return(s, err).Times(1) + } +} diff --git a/cmd/entities/kbm/list_node_networks.go b/cmd/entities/kbm/list_node_networks.go new file mode 100644 index 0000000..1c832c6 --- /dev/null +++ b/cmd/entities/kbm/list_node_networks.go @@ -0,0 +1,30 @@ +package kbm + +import ( + serverscom "github.com/serverscom/serverscom-go-client/pkg" + "github.com/serverscom/srvctl/cmd/base" + "github.com/spf13/cobra" + "log" +) + +func newListNetworksCmd(cmdContext *base.CmdContext) *cobra.Command { + factory := func(verbose bool, args ...string) serverscom.Collection[serverscom.Network] { + scClient := cmdContext.GetClient().SetVerbose(verbose).GetScClient() + if len(args) == 0 { + log.Fatal("Missing KBM node ID") + } + id := args[0] + return scClient.Hosts.KubernetesBaremetalNodeNetworks(id) + } + + opts := base.NewListOptions( + &base.BaseListOptions[serverscom.Network]{}, + &base.SearchPatternOption[serverscom.Network]{}, + &base.FamilyOption[serverscom.Network]{}, + &base.InterfaceTypeOption[serverscom.Network]{}, + &base.DistributionMethodOption[serverscom.Network]{}, + &base.AdditionalOption[serverscom.Network]{}, + ) + + return base.NewListCmd("list-node-networks ", "KBM node networks", factory, cmdContext, opts...) +} diff --git a/cmd/entities/kbm/list_node_slots.go b/cmd/entities/kbm/list_node_slots.go new file mode 100644 index 0000000..3736e03 --- /dev/null +++ b/cmd/entities/kbm/list_node_slots.go @@ -0,0 +1,26 @@ +package kbm + +import ( + serverscom "github.com/serverscom/serverscom-go-client/pkg" + "github.com/serverscom/srvctl/cmd/base" + "github.com/spf13/cobra" + "log" +) + +func newListSlotsCmd(cmdContext *base.CmdContext) *cobra.Command { + factory := func(verbose bool, args ...string) serverscom.Collection[serverscom.HostDriveSlot] { + scClient := cmdContext.GetClient().SetVerbose(verbose).GetScClient() + if len(args) == 0 { + log.Fatal("Missing KBM node ID") + } + id := args[0] + return scClient.Hosts.KubernetesBaremetalNodeDriveSlots(id) + } + + opts := base.NewListOptions( + &base.BaseListOptions[serverscom.HostDriveSlot]{}, + &base.SearchPatternOption[serverscom.HostDriveSlot]{}, + ) + + return base.NewListCmd("list-node-slots ", "KBM node drive slots", factory, cmdContext, opts...) +} diff --git a/cmd/entities/kbm/list_nodes.go b/cmd/entities/kbm/list_nodes.go new file mode 100644 index 0000000..ed58d48 --- /dev/null +++ b/cmd/entities/kbm/list_nodes.go @@ -0,0 +1,26 @@ +package kbm + +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.KubernetesBaremetalNode] { + scClient := cmdContext.GetClient().SetVerbose(verbose).GetScClient() + collection := scClient.Hosts.ListKubernetesBaremetalNodes() + + return collection + } + + opts := base.NewListOptions( + &base.BaseListOptions[serverscom.KubernetesBaremetalNode]{}, + &base.LocationIDOption[serverscom.KubernetesBaremetalNode]{}, + &base.RackIDOption[serverscom.KubernetesBaremetalNode]{}, + &base.LabelSelectorOption[serverscom.KubernetesBaremetalNode]{}, + &base.SearchPatternOption[serverscom.KubernetesBaremetalNode]{}, + ) + + return base.NewListCmd("list-nodes", "kubernetes baremetal nodes", factory, cmdContext, opts...) +} diff --git a/cmd/entities/kbm/set_node_power.go b/cmd/entities/kbm/set_node_power.go new file mode 100644 index 0000000..96c90f1 --- /dev/null +++ b/cmd/entities/kbm/set_node_power.go @@ -0,0 +1,60 @@ +package kbm + +import ( + "context" + "fmt" + serverscom "github.com/serverscom/serverscom-go-client/pkg" + "github.com/serverscom/srvctl/cmd/base" + "github.com/spf13/cobra" +) + +func newPowerCmd(cmdContext *base.CmdContext) *cobra.Command { + var powerFlag string + + cmd := &cobra.Command{ + Use: "set-node-power ", + Short: "Power on/off/cycle KBM nodes", + Long: "Power on/off/cycle KBM nodes 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] + + server, err := powerAction(ctx, scClient, id, powerFlag) + if err != nil { + return err + } + + if server != nil { + formatter := cmdContext.GetOrCreateFormatter(cmd) + return formatter.Format(server) + } + return nil + }, + } + + cmd.Flags().StringVar(&powerFlag, "power", "", "power command: on|off|cycle (required)") + + return cmd +} + +func powerAction(ctx context.Context, client *serverscom.Client, id string, action string) (any, error) { + switch action { + case "on": + return client.Hosts.PowerOnKubernetesBaremetalNode(ctx, id) + case "off": + return client.Hosts.PowerOffKubernetesBaremetalNode(ctx, id) + case "cycle": + return client.Hosts.PowerCycleKubernetesBaremetalNode(ctx, id) + default: + return nil, fmt.Errorf("unsupported power action: %q", action) + } +} diff --git a/cmd/entities/kbm/update_node.go b/cmd/entities/kbm/update_node.go new file mode 100644 index 0000000..e41413b --- /dev/null +++ b/cmd/entities/kbm/update_node.go @@ -0,0 +1,51 @@ +package kbm + +import ( + serverscom "github.com/serverscom/serverscom-go-client/pkg" + "github.com/serverscom/srvctl/cmd/base" + "github.com/spf13/cobra" + "log" +) + +func newUpdateCmd(cmdContext *base.CmdContext) *cobra.Command { + var labels []string + + cmd := &cobra.Command{ + Use: "update-node ", + Short: "Update a KBM node", + Long: "Update a KBM node 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.KubernetesBaremetalNodeUpdateInput{ + Labels: labelsMap, + } + + scClient := cmdContext.GetClient().SetVerbose(manager.GetVerbose(cmd)).GetScClient() + id := args[0] + + server, err := scClient.Hosts.UpdateKubernetesBaremetalNode(ctx, id, input) + if err != nil { + return err + } + + if server != nil { + formatter := cmdContext.GetOrCreateFormatter(cmd) + return formatter.Format(server) + } + return nil + }, + } + + cmd.Flags().StringArrayVarP(&labels, "label", "l", []string{}, "string in key=value format") + + return cmd +} diff --git a/go.mod b/go.mod index c5fe7f1..872059d 100644 --- a/go.mod +++ b/go.mod @@ -6,7 +6,7 @@ require ( github.com/creack/pty v1.1.24 github.com/jmespath/go-jmespath v0.4.0 github.com/onsi/gomega v1.38.0 - github.com/serverscom/serverscom-go-client v1.0.20 + github.com/serverscom/serverscom-go-client v1.0.21 github.com/spf13/cobra v1.9.1 github.com/spf13/pflag v1.0.7 github.com/spf13/viper v1.20.1 diff --git a/go.sum b/go.sum index 271a58b..e5b5143 100644 --- a/go.sum +++ b/go.sum @@ -43,8 +43,8 @@ github.com/rogpeppe/go-internal v1.9.0/go.mod h1:WtVeX8xhTBvf0smdhujwtBcq4Qrzq/f github.com/russross/blackfriday/v2 v2.1.0/go.mod h1:+Rmxgy9KzJVeS9/2gXHxylqXiyQDYRxCVz55jmeOWTM= github.com/sagikazarmark/locafero v0.10.0 h1:FM8Cv6j2KqIhM2ZK7HZjm4mpj9NBktLgowT1aN9q5Cc= github.com/sagikazarmark/locafero v0.10.0/go.mod h1:Ieo3EUsjifvQu4NZwV5sPd4dwvu0OCgEQV7vjc9yDjw= -github.com/serverscom/serverscom-go-client v1.0.20 h1:a7LwhBvJJivyxnxbzDO7i3OVYe904oYHV6lcQwdoXYg= -github.com/serverscom/serverscom-go-client v1.0.20/go.mod h1:/Nf+XygKOxm19Sl2gvMzT55O4X+tWDkj/UM4mjzfKgM= +github.com/serverscom/serverscom-go-client v1.0.21 h1:7L3Mo56M9yprj33iCxeP/iaxnQfCfiV0OwOqnCqIt1I= +github.com/serverscom/serverscom-go-client v1.0.21/go.mod h1:/Nf+XygKOxm19Sl2gvMzT55O4X+tWDkj/UM4mjzfKgM= github.com/sourcegraph/conc v0.3.1-0.20240121214520-5f936abd7ae8 h1:+jumHNA0Wrelhe64i8F6HNlS8pkoyMv5sreGx2Ry5Rw= github.com/sourcegraph/conc v0.3.1-0.20240121214520-5f936abd7ae8/go.mod h1:3n1Cwaq1E1/1lhQhtRK2ts/ZwZEhjcQeJQ1RuC6Q/8U= github.com/spf13/afero v1.14.0 h1:9tH6MapGnn/j0eb0yIXiLjERO8RB6xIVZRDCX7PtqWA= diff --git a/internal/mocks/hosts_service.go b/internal/mocks/hosts_service.go index 80b9f12..8602898 100644 --- a/internal/mocks/hosts_service.go +++ b/internal/mocks/hosts_service.go @@ -335,6 +335,49 @@ func (mr *MockHostsServiceMockRecorder) GetSBMServer(ctx, id any) *gomock.Call { return mr.mock.ctrl.RecordCallWithMethodType(mr.mock, "GetSBMServer", reflect.TypeOf((*MockHostsService)(nil).GetSBMServer), ctx, id) } +// KubernetesBaremetalNodeDriveSlots mocks base method. +func (m *MockHostsService) KubernetesBaremetalNodeDriveSlots(id string) serverscom.Collection[serverscom.HostDriveSlot] { + m.ctrl.T.Helper() + ret := m.ctrl.Call(m, "KubernetesBaremetalNodeDriveSlots", id) + ret0, _ := ret[0].(serverscom.Collection[serverscom.HostDriveSlot]) + return ret0 +} + +// KubernetesBaremetalNodeDriveSlots indicates an expected call of KubernetesBaremetalNodeDriveSlots. +func (mr *MockHostsServiceMockRecorder) KubernetesBaremetalNodeDriveSlots(id any) *gomock.Call { + mr.mock.ctrl.T.Helper() + return mr.mock.ctrl.RecordCallWithMethodType(mr.mock, "KubernetesBaremetalNodeDriveSlots", reflect.TypeOf((*MockHostsService)(nil).KubernetesBaremetalNodeDriveSlots), id) +} + +// KubernetesBaremetalNodeNetworks mocks base method. +func (m *MockHostsService) KubernetesBaremetalNodeNetworks(id string) serverscom.Collection[serverscom.Network] { + m.ctrl.T.Helper() + ret := m.ctrl.Call(m, "KubernetesBaremetalNodeNetworks", id) + ret0, _ := ret[0].(serverscom.Collection[serverscom.Network]) + return ret0 +} + +// KubernetesBaremetalNodeNetworks indicates an expected call of KubernetesBaremetalNodeNetworks. +func (mr *MockHostsServiceMockRecorder) KubernetesBaremetalNodeNetworks(id any) *gomock.Call { + mr.mock.ctrl.T.Helper() + return mr.mock.ctrl.RecordCallWithMethodType(mr.mock, "KubernetesBaremetalNodeNetworks", reflect.TypeOf((*MockHostsService)(nil).KubernetesBaremetalNodeNetworks), id) +} + +// KubernetesBaremetalNodePowerFeeds mocks base method. +func (m *MockHostsService) KubernetesBaremetalNodePowerFeeds(ctx context.Context, id string) ([]serverscom.HostPowerFeed, error) { + m.ctrl.T.Helper() + ret := m.ctrl.Call(m, "KubernetesBaremetalNodePowerFeeds", ctx, id) + ret0, _ := ret[0].([]serverscom.HostPowerFeed) + ret1, _ := ret[1].(error) + return ret0, ret1 +} + +// KubernetesBaremetalNodePowerFeeds indicates an expected call of KubernetesBaremetalNodePowerFeeds. +func (mr *MockHostsServiceMockRecorder) KubernetesBaremetalNodePowerFeeds(ctx, id any) *gomock.Call { + mr.mock.ctrl.T.Helper() + return mr.mock.ctrl.RecordCallWithMethodType(mr.mock, "KubernetesBaremetalNodePowerFeeds", reflect.TypeOf((*MockHostsService)(nil).KubernetesBaremetalNodePowerFeeds), ctx, id) +} + // ListDedicatedServers mocks base method. func (m *MockHostsService) ListDedicatedServers() serverscom.Collection[serverscom.DedicatedServer] { m.ctrl.T.Helper() diff --git a/testdata/entities/kbm/get_node.json b/testdata/entities/kbm/get_node.json new file mode 100644 index 0000000..f24d9c8 --- /dev/null +++ b/testdata/entities/kbm/get_node.json @@ -0,0 +1,36 @@ +{ + "id": "testId", + "kubernetes_cluster_id": "testId", + "kubernetes_cluster_node_id": "testId", + "kubernetes_cluster_node_number": 1, + "rack_id": "testId", + "type": "kubernetes_baremetal_node", + "title": "example.aa", + "location_id": 0, + "location_code": "test", + "status": "active", + "operational_status": "", + "power_status": "", + "configuration": "", + "private_ipv4_address": null, + "public_ipv4_address": "1.2.3.4", + "lease_start_at": "", + "scheduled_release_at": null, + "oob_ipv4_address": "", + "configuration_details": { + "ram_size": 2, + "server_model_id": 1, + "server_model_name": "server-model-123", + "public_uplink_id": 2, + "public_uplink_name": "Public 1 Gbps without redundancy", + "private_uplink_id": 3, + "private_uplink_name": "Private 1 Gbps without redundancy", + "bandwidth_id": 4, + "bandwidth_name": "20000 GB", + "operating_system_id": 5, + "operating_system_full_name": "CentOS 7 x86_64" + }, + "labels": null, + "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/kbm/get_node.txt b/testdata/entities/kbm/get_node.txt new file mode 100644 index 0000000..7a89f73 --- /dev/null +++ b/testdata/entities/kbm/get_node.txt @@ -0,0 +1,2 @@ +ID RackID Title LocationCode Status PublicIPv4Address Created Updated +testId testId example.aa test active 1.2.3.4 2025-01-01T12:00:00Z 2025-01-01T12:00:00Z diff --git a/testdata/entities/kbm/get_node.yaml b/testdata/entities/kbm/get_node.yaml new file mode 100644 index 0000000..d2fc0be --- /dev/null +++ b/testdata/entities/kbm/get_node.yaml @@ -0,0 +1,33 @@ +id: testId +kubernetesclusterid: testId +kubernetesclusternodeid: testId +kubernetesclusternodenumber: 1 +rackid: testId +type: kubernetes_baremetal_node +title: example.aa +locationid: 0 +locationcode: test +status: active +operationalstatus: "" +powerstatus: "" +configuration: "" +privateipv4address: null +publicipv4address: 1.2.3.4 +leasestart: "" +scheduledrelease: null +oobipv4address: "" +configurationdetails: + ramsize: 2 + servermodelid: 1 + servermodelname: server-model-123 + publicuplinkid: 2 + publicuplinkname: Public 1 Gbps without redundancy + privateuplinkid: 3 + privateuplinkname: Private 1 Gbps without redundancy + bandwidthid: 4 + bandwidthname: 20000 GB + operatingsystemid: 5 + operatingsystemfullname: CentOS 7 x86_64 +labels: {} +created: 2025-01-01T12:00:00Z +updated: 2025-01-01T12:00:00Z diff --git a/testdata/entities/kbm/get_node_power_feeds.json b/testdata/entities/kbm/get_node_power_feeds.json new file mode 100644 index 0000000..13fcc72 --- /dev/null +++ b/testdata/entities/kbm/get_node_power_feeds.json @@ -0,0 +1,10 @@ +[ + { + "name": "testPowerFeed", + "status": "on" + }, + { + "name": "testPowerFeed2", + "status": "off" + } +] \ No newline at end of file diff --git a/testdata/entities/kbm/get_node_power_feeds.txt b/testdata/entities/kbm/get_node_power_feeds.txt new file mode 100644 index 0000000..2ce5d47 --- /dev/null +++ b/testdata/entities/kbm/get_node_power_feeds.txt @@ -0,0 +1,3 @@ +Name Status +testPowerFeed on +testPowerFeed2 off diff --git a/testdata/entities/kbm/get_node_power_feeds.yaml b/testdata/entities/kbm/get_node_power_feeds.yaml new file mode 100644 index 0000000..6956d1b --- /dev/null +++ b/testdata/entities/kbm/get_node_power_feeds.yaml @@ -0,0 +1,4 @@ +- name: testPowerFeed + status: "on" +- name: testPowerFeed2 + status: "off" diff --git a/testdata/entities/kbm/list_node_networks.json b/testdata/entities/kbm/list_node_networks.json new file mode 100644 index 0000000..b024e7f --- /dev/null +++ b/testdata/entities/kbm/list_node_networks.json @@ -0,0 +1,15 @@ +[ + { + "id": "testNetId", + "title": "Some Net", + "status": "active", + "cidr": "100.0.8.0/29", + "family": "ipv4", + "interface_type": "public", + "distribution_method": "gateway", + "additional": false, + "created_at": "2025-01-01T12:00:00Z", + "updated_at": "2025-01-01T12:00:00Z", + "state": "" + } +] \ No newline at end of file diff --git a/testdata/entities/kbm/list_node_networks_all.json b/testdata/entities/kbm/list_node_networks_all.json new file mode 100644 index 0000000..8dab390 --- /dev/null +++ b/testdata/entities/kbm/list_node_networks_all.json @@ -0,0 +1,28 @@ +[ + { + "id": "testNetId", + "title": "Some Net", + "status": "active", + "cidr": "100.0.8.0/29", + "family": "ipv4", + "interface_type": "public", + "distribution_method": "gateway", + "additional": false, + "created_at": "2025-01-01T12:00:00Z", + "updated_at": "2025-01-01T12:00:00Z", + "state": "" + }, + { + "id": "testNetId2", + "title": "Another Net", + "status": "active", + "cidr": "100.0.8.0/29", + "family": "ipv4", + "interface_type": "public", + "distribution_method": "gateway", + "additional": false, + "created_at": "2025-01-01T12:00:00Z", + "updated_at": "2025-01-01T12:00:00Z", + "state": "" + } +] \ No newline at end of file diff --git a/testdata/entities/kbm/list_node_networks_pageview.txt b/testdata/entities/kbm/list_node_networks_pageview.txt new file mode 100644 index 0000000..a98985d --- /dev/null +++ b/testdata/entities/kbm/list_node_networks_pageview.txt @@ -0,0 +1,21 @@ +ID: testNetId +Title: Some Net +Status: active +CIDR: 100.0.8.0/29 +Family: ipv4 +InterfaceType: public +DistributionMethod: gateway +Additional: false +Created: 2025-01-01T12:00:00Z +Updated: 2025-01-01T12:00:00Z +--- +ID: testNetId2 +Title: Another Net +Status: active +CIDR: 100.0.8.0/29 +Family: ipv4 +InterfaceType: public +DistributionMethod: gateway +Additional: false +Created: 2025-01-01T12:00:00Z +Updated: 2025-01-01T12:00:00Z diff --git a/testdata/entities/kbm/list_node_networks_template.txt b/testdata/entities/kbm/list_node_networks_template.txt new file mode 100644 index 0000000..8f58214 --- /dev/null +++ b/testdata/entities/kbm/list_node_networks_template.txt @@ -0,0 +1,2 @@ +Network: testNetId Title: Some Net +Network: testNetId2 Title: Another Net diff --git a/testdata/entities/kbm/list_node_slots.json b/testdata/entities/kbm/list_node_slots.json new file mode 100644 index 0000000..f200c38 --- /dev/null +++ b/testdata/entities/kbm/list_node_slots.json @@ -0,0 +1,15 @@ +[ + { + "position": 1, + "interface": "SAS", + "form_factor": "2.5\"", + "drive_model": { + "id": 10, + "name": "ssd-model-749", + "capacity": 100, + "interface": "SATA3", + "form_factor": "2.5", + "media_type": "SSD" + } + } +] \ No newline at end of file diff --git a/testdata/entities/kbm/list_node_slots_all.json b/testdata/entities/kbm/list_node_slots_all.json new file mode 100644 index 0000000..6f75e39 --- /dev/null +++ b/testdata/entities/kbm/list_node_slots_all.json @@ -0,0 +1,28 @@ +[ + { + "position": 1, + "interface": "SAS", + "form_factor": "2.5\"", + "drive_model": { + "id": 10, + "name": "ssd-model-749", + "capacity": 100, + "interface": "SATA3", + "form_factor": "2.5", + "media_type": "SSD" + } + }, + { + "position": 2, + "interface": "SAS", + "form_factor": "2.5\"", + "drive_model": { + "id": 10, + "name": "ssd-model-749", + "capacity": 100, + "interface": "SATA3", + "form_factor": "2.5", + "media_type": "SSD" + } + } +] \ No newline at end of file diff --git a/testdata/entities/kbm/list_node_slots_pageview.txt b/testdata/entities/kbm/list_node_slots_pageview.txt new file mode 100644 index 0000000..4ecce6b --- /dev/null +++ b/testdata/entities/kbm/list_node_slots_pageview.txt @@ -0,0 +1,21 @@ +Position: 1 +Interface: SAS +FormFactor: 2.5" +DriveModel: + ID: 10 + Name: ssd-model-749 + Capacity: 100 + Interface: SATA3 + FormFactor: 2.5 + MediaType: SSD +--- +Position: 2 +Interface: SAS +FormFactor: 2.5" +DriveModel: + ID: 10 + Name: ssd-model-749 + Capacity: 100 + Interface: SATA3 + FormFactor: 2.5 + MediaType: SSD diff --git a/testdata/entities/kbm/list_node_slots_template.txt b/testdata/entities/kbm/list_node_slots_template.txt new file mode 100644 index 0000000..8cda1a9 --- /dev/null +++ b/testdata/entities/kbm/list_node_slots_template.txt @@ -0,0 +1,2 @@ +Position: 1 Interface: SAS +Position: 2 Interface: SAS diff --git a/testdata/entities/kbm/list_nodes.json b/testdata/entities/kbm/list_nodes.json new file mode 100644 index 0000000..22dee12 --- /dev/null +++ b/testdata/entities/kbm/list_nodes.json @@ -0,0 +1,38 @@ +[ + { + "id": "testId", + "kubernetes_cluster_id": "testId", + "kubernetes_cluster_node_id": "testId", + "kubernetes_cluster_node_number": 1, + "rack_id": "testId", + "type": "kubernetes_baremetal_node", + "title": "example.aa", + "location_id": 0, + "location_code": "test", + "status": "active", + "operational_status": "", + "power_status": "", + "configuration": "", + "private_ipv4_address": null, + "public_ipv4_address": "1.2.3.4", + "lease_start_at": "", + "scheduled_release_at": null, + "oob_ipv4_address": "", + "configuration_details": { + "ram_size": 2, + "server_model_id": 1, + "server_model_name": "server-model-123", + "public_uplink_id": 2, + "public_uplink_name": "Public 1 Gbps without redundancy", + "private_uplink_id": 3, + "private_uplink_name": "Private 1 Gbps without redundancy", + "bandwidth_id": 4, + "bandwidth_name": "20000 GB", + "operating_system_id": 5, + "operating_system_full_name": "CentOS 7 x86_64" + }, + "labels": null, + "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/kbm/list_nodes_all.json b/testdata/entities/kbm/list_nodes_all.json new file mode 100644 index 0000000..381f282 --- /dev/null +++ b/testdata/entities/kbm/list_nodes_all.json @@ -0,0 +1,74 @@ +[ + { + "id": "testId", + "kubernetes_cluster_id": "testId", + "kubernetes_cluster_node_id": "testId", + "kubernetes_cluster_node_number": 1, + "rack_id": "testId", + "type": "kubernetes_baremetal_node", + "title": "example.aa", + "location_id": 0, + "location_code": "test", + "status": "active", + "operational_status": "", + "power_status": "", + "configuration": "", + "private_ipv4_address": null, + "public_ipv4_address": "1.2.3.4", + "lease_start_at": "", + "scheduled_release_at": null, + "oob_ipv4_address": "", + "configuration_details": { + "ram_size": 2, + "server_model_id": 1, + "server_model_name": "server-model-123", + "public_uplink_id": 2, + "public_uplink_name": "Public 1 Gbps without redundancy", + "private_uplink_id": 3, + "private_uplink_name": "Private 1 Gbps without redundancy", + "bandwidth_id": 4, + "bandwidth_name": "20000 GB", + "operating_system_id": 5, + "operating_system_full_name": "CentOS 7 x86_64" + }, + "labels": null, + "created_at": "2025-01-01T12:00:00Z", + "updated_at": "2025-01-01T12:00:00Z" + }, + { + "id": "testId2", + "kubernetes_cluster_id": "testId", + "kubernetes_cluster_node_id": "testId", + "kubernetes_cluster_node_number": 1, + "rack_id": "testId", + "type": "kubernetes_baremetal_node", + "title": "example.bb", + "location_id": 0, + "location_code": "test", + "status": "active", + "operational_status": "", + "power_status": "", + "configuration": "", + "private_ipv4_address": null, + "public_ipv4_address": "1.2.3.4", + "lease_start_at": "", + "scheduled_release_at": null, + "oob_ipv4_address": "", + "configuration_details": { + "ram_size": 2, + "server_model_id": 1, + "server_model_name": "server-model-123", + "public_uplink_id": 2, + "public_uplink_name": "Public 1 Gbps without redundancy", + "private_uplink_id": 3, + "private_uplink_name": "Private 1 Gbps without redundancy", + "bandwidth_id": 4, + "bandwidth_name": "20000 GB", + "operating_system_id": 5, + "operating_system_full_name": "CentOS 7 x86_64" + }, + "labels": null, + "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/kbm/list_nodes_pageview.txt b/testdata/entities/kbm/list_nodes_pageview.txt new file mode 100644 index 0000000..ecb35a2 --- /dev/null +++ b/testdata/entities/kbm/list_nodes_pageview.txt @@ -0,0 +1,67 @@ +ID: testId +KubernetesClusterId: +KubernetesClusterNodeId: +KubernetesClusterNodeNumber: 1 +RackID: testId +Type: kubernetes_baremetal_node +Title: example.aa +LocationID: 0 +LocationCode: test +Status: active +OperationalStatus: +PowerStatus: +Configuration: +PrivateIPv4Address: +PublicIPv4Address: 1.2.3.4 +LeaseStart: +ScheduledRelease: +OobIPv4Address: +Labels: +Created: 2025-01-01T12:00:00Z +Updated: 2025-01-01T12:00:00Z +ConfigurationDetails: + RAMSize: 2 + ServerModelID: 1 + ServerModelName: server-model-123 + PublicUplinkID: 2 + PublicUplinkName: Public 1 Gbps without redundancy + PrivateUplinkID: 3 + PrivateUplinkName: Private 1 Gbps without redundancy + BandwidthID: 4 + BandwidthName: 20000 GB + OperatingSystemID: 5 + OperatingSystemFullName: CentOS 7 x86_64 +--- +ID: testId2 +KubernetesClusterId: +KubernetesClusterNodeId: +KubernetesClusterNodeNumber: 1 +RackID: testId +Type: kubernetes_baremetal_node +Title: example.bb +LocationID: 0 +LocationCode: test +Status: active +OperationalStatus: +PowerStatus: +Configuration: +PrivateIPv4Address: +PublicIPv4Address: 1.2.3.4 +LeaseStart: +ScheduledRelease: +OobIPv4Address: +Labels: +Created: 2025-01-01T12:00:00Z +Updated: 2025-01-01T12:00:00Z +ConfigurationDetails: + RAMSize: 2 + ServerModelID: 1 + ServerModelName: server-model-123 + PublicUplinkID: 2 + PublicUplinkName: Public 1 Gbps without redundancy + PrivateUplinkID: 3 + PrivateUplinkName: Private 1 Gbps without redundancy + BandwidthID: 4 + BandwidthName: 20000 GB + OperatingSystemID: 5 + OperatingSystemFullName: CentOS 7 x86_64 diff --git a/testdata/entities/kbm/list_nodes_template.txt b/testdata/entities/kbm/list_nodes_template.txt new file mode 100644 index 0000000..e8bd6d2 --- /dev/null +++ b/testdata/entities/kbm/list_nodes_template.txt @@ -0,0 +1,2 @@ +Title: example.aa Type: kubernetes_baremetal_node +Title: example.bb Type: kubernetes_baremetal_node diff --git a/testdata/entities/kbm/set_node_power.json b/testdata/entities/kbm/set_node_power.json new file mode 100644 index 0000000..5010e3d --- /dev/null +++ b/testdata/entities/kbm/set_node_power.json @@ -0,0 +1,38 @@ +{ + "id": "testId", + "kubernetes_cluster_id": "testId", + "kubernetes_cluster_node_id": "testId", + "kubernetes_cluster_node_number": 1, + "rack_id": "testId", + "type": "kubernetes_baremetal_node", + "title": "example.aa", + "location_id": 0, + "location_code": "test", + "status": "active", + "operational_status": "", + "power_status": "", + "configuration": "", + "private_ipv4_address": null, + "public_ipv4_address": "1.2.3.4", + "lease_start_at": "", + "scheduled_release_at": null, + "oob_ipv4_address": "", + "configuration_details": { + "ram_size": 2, + "server_model_id": 1, + "server_model_name": "server-model-123", + "public_uplink_id": 2, + "public_uplink_name": "Public 1 Gbps without redundancy", + "private_uplink_id": 3, + "private_uplink_name": "Private 1 Gbps without redundancy", + "bandwidth_id": 4, + "bandwidth_name": "20000 GB", + "operating_system_id": 5, + "operating_system_full_name": "CentOS 7 x86_64" + }, + "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/kbm/update_node.json b/testdata/entities/kbm/update_node.json new file mode 100644 index 0000000..5010e3d --- /dev/null +++ b/testdata/entities/kbm/update_node.json @@ -0,0 +1,38 @@ +{ + "id": "testId", + "kubernetes_cluster_id": "testId", + "kubernetes_cluster_node_id": "testId", + "kubernetes_cluster_node_number": 1, + "rack_id": "testId", + "type": "kubernetes_baremetal_node", + "title": "example.aa", + "location_id": 0, + "location_code": "test", + "status": "active", + "operational_status": "", + "power_status": "", + "configuration": "", + "private_ipv4_address": null, + "public_ipv4_address": "1.2.3.4", + "lease_start_at": "", + "scheduled_release_at": null, + "oob_ipv4_address": "", + "configuration_details": { + "ram_size": 2, + "server_model_id": 1, + "server_model_name": "server-model-123", + "public_uplink_id": 2, + "public_uplink_name": "Public 1 Gbps without redundancy", + "private_uplink_id": 3, + "private_uplink_name": "Private 1 Gbps without redundancy", + "bandwidth_id": 4, + "bandwidth_name": "20000 GB", + "operating_system_id": 5, + "operating_system_full_name": "CentOS 7 x86_64" + }, + "labels": { + "new": "label" + }, + "created_at": "2025-01-01T12:00:00Z", + "updated_at": "2025-01-01T12:00:00Z" +} \ No newline at end of file