diff --git a/cmd/base/const.go b/cmd/base/const.go index 184f2eb..c462ac6 100644 --- a/cmd/base/const.go +++ b/cmd/base/const.go @@ -1,5 +1,6 @@ package base const ( - ENDPOINT = "https://api.servers.com/v1" + ENDPOINT = "https://api.servers.com/v1" + ErrNoContexts = "no contexts found, log in first: 'srvctl login ' or, if you don't want to create the context, you may pass SC_TOKEN every time you use srvctl" ) diff --git a/cmd/base/hooks.go b/cmd/base/hooks.go index 61f390d..d0bfc13 100644 --- a/cmd/base/hooks.go +++ b/cmd/base/hooks.go @@ -1,6 +1,7 @@ package base import ( + "errors" "fmt" "html/template" "os" @@ -131,7 +132,7 @@ func CheckEmptyContexts(cmdContext *CmdContext) func(cmd *cobra.Command, args [] manager := cmdContext.GetManager() if len(manager.GetContexts()) == 0 { - return fmt.Errorf("no contexts found, log in first: 'srvctl login '") + return errors.New(ErrNoContexts) } return nil } diff --git a/cmd/entities/hosts/drive-slots.go b/cmd/entities/hosts/drive-slots.go index c24b818..bbafae0 100644 --- a/cmd/entities/hosts/drive-slots.go +++ b/cmd/entities/hosts/drive-slots.go @@ -23,4 +23,20 @@ func newListDSDriveSlotsCmd(cmdContext *base.CmdContext) *cobra.Command { return base.NewListCmd("list-drive-slots ", "Dedicated server drive slots", factory, cmdContext, opts) } -// TODO add list drive slots for KBM after adding KBMDriveSlots in go client +func newListKBMDriveSlotsCmd(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-drive-slots ", "KBM node drive slots", factory, cmdContext, opts...) +} diff --git a/cmd/entities/hosts/hosts.go b/cmd/entities/hosts/hosts.go index 6b1b81b..b748096 100644 --- a/cmd/entities/hosts/hosts.go +++ b/cmd/entities/hosts/hosts.go @@ -71,6 +71,8 @@ func NewCmd(cmdContext *base.CmdContext) *cobra.Command { extraCmds: []func(*base.CmdContext) *cobra.Command{ newUpdateKBMCmd, newListKBMCmd, + newListKBMNetworksCmd, + newListKBMDriveSlotsCmd, }, }, { diff --git a/cmd/entities/hosts/hosts_test.go b/cmd/entities/hosts/hosts_test.go index dd46693..ae2b0e5 100644 --- a/cmd/entities/hosts/hosts_test.go +++ b/cmd/entities/hosts/hosts_test.go @@ -3,6 +3,7 @@ package hosts import ( "errors" "path/filepath" + "strings" "testing" "time" @@ -81,9 +82,9 @@ var ( Created: fixedTime, Updated: fixedTime, } - netTitle = "Some Net" - cidr = "100.0.8.0/29" - testDSNetwork = serverscom.Network{ + netTitle = "Some Net" + cidr = "100.0.8.0/29" + testNetwork = serverscom.Network{ ID: testNetworkId, Title: &netTitle, Status: "active", @@ -95,6 +96,24 @@ var ( 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 TestListHostsCmd(t *testing.T) { @@ -1622,7 +1641,7 @@ func TestGetDSNetworkCmd(t *testing.T) { } hostsServiceHandler.EXPECT(). GetDedicatedServerNetwork(gomock.Any(), tc.id, tc.networkID). - Return(&testDSNetwork, err) + Return(&testNetwork, err) testCmdContext := testutils.NewTestCmdContext(scClient) hostsCmd := NewCmd(testCmdContext) @@ -1653,7 +1672,7 @@ func TestGetDSNetworkCmd(t *testing.T) { } func TestListDSNetworksCmd(t *testing.T) { - testNetwork1 := testDSNetwork + testNetwork1 := testNetwork testNetwork1.ID = testNetworkId testNetwork2 := testNetwork1 testNetwork2.ID = "testNetId2" @@ -1820,7 +1839,7 @@ func TestAddDSNetworkCmd(t *testing.T) { configureMock: func(mock *mocks.MockHostsService) { mock.EXPECT(). AddDedicatedServerPublicIPv4Network(gomock.Any(), testId, expectedInputPublic). - Return(&testDSNetwork, nil) + Return(&testNetwork, nil) }, }, { @@ -1836,7 +1855,7 @@ func TestAddDSNetworkCmd(t *testing.T) { configureMock: func(mock *mocks.MockHostsService) { mock.EXPECT(). AddDedicatedServerPrivateIPv4Network(gomock.Any(), testId, expectedInputPrivate). - Return(&testDSNetwork, nil) + Return(&testNetwork, nil) }, }, { @@ -1935,7 +1954,7 @@ func TestDeleteDSNetworkCmd(t *testing.T) { } hostsServiceHandler.EXPECT(). DeleteDedicatedServerNetwork(gomock.Any(), tc.id, tc.networkID). - Return(&testDSNetwork, err) + Return(&testNetwork, err) testCmdContext := testutils.NewTestCmdContext(scClient) hostsCmd := NewCmd(testCmdContext) @@ -1961,3 +1980,488 @@ func TestDeleteDSNetworkCmd(t *testing.T) { }) } } + +func TestListKBMPowerFeedsCmd(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, "list_kbm_power_feeds.txt")), + }, + { + name: "get KBM node power_feeds", + id: testId, + output: "json", + expectedOutput: testutils.ReadFixture(filepath.Join(fixtureBasePath, "list_kbm_power_feeds.json")), + }, + { + name: "get KBM node power_feeds in YAML format", + id: testId, + output: "yaml", + expectedOutput: testutils.ReadFixture(filepath.Join(fixtureBasePath, "list_kbm_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{"hosts", "kbm", "list-power-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 TestListKBMNetworksCmd(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_kbm_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_kbm_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_kbm_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_kbm_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{"hosts", "kbm", "list-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 TestListKBMDriveSlotsCmd(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_kbm_drive_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_kbm_drive_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_kbm_drive_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_kbm_drive_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{"hosts", "kbm", "list-drive-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 TestKBMPowerCmd(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{"hosts", "kbm", "power", testId, "--command=on"}, + output: "json", + expectedOutput: testutils.ReadFixture(filepath.Join(fixtureBasePath, "set_kbm_power.json")), + }, + { + name: "power off kbm node", + id: testId, + args: []string{"hosts", "kbm", "power", testId, "--command=off"}, + output: "json", + expectedOutput: testutils.ReadFixture(filepath.Join(fixtureBasePath, "set_kbm_power.json")), + }, + { + name: "power cycle kbm node", + id: testId, + args: []string{"hosts", "kbm", "power", testId, "--command=cycle"}, + output: "json", + expectedOutput: testutils.ReadFixture(filepath.Join(fixtureBasePath, "set_kbm_power.json")), + }, + { + name: "power on kbm node with error", + id: testId, + args: []string{"hosts", "kbm", "power", testId, "--command=on"}, + expectError: true, + }, + { + name: "power on kbm node without flag error", + id: testId, + args: []string{"hosts", "kbm", "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], "--command=") + } + 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 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/hosts/networks.go b/cmd/entities/hosts/networks.go index bb2650c..7a31d72 100644 --- a/cmd/entities/hosts/networks.go +++ b/cmd/entities/hosts/networks.go @@ -156,3 +156,25 @@ func newDeleteDSNetworkCmd(cmdContext *base.CmdContext) *cobra.Command { return cmd } + +func newListKBMNetworksCmd(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-networks ", "KBM node networks", factory, cmdContext, opts...) +} diff --git a/cmd/entities/hosts/power.go b/cmd/entities/hosts/power.go index 0ab44a1..f86d4f4 100644 --- a/cmd/entities/hosts/power.go +++ b/cmd/entities/hosts/power.go @@ -49,8 +49,7 @@ func (p *KBMPowerMgr) PowerAction(ctx context.Context, client *serverscom.Client } func (p *KBMPowerMgr) ListPowerFeeds(ctx context.Context, client *serverscom.Client, id string) (any, error) { - // return client.Hosts.TODO(ctx, id) - return nil, nil + return client.Hosts.KubernetesBaremetalNodePowerFeeds(ctx, id) } type SBMPowerMgr struct{} diff --git a/cmd/entities/kbm/get_node.go b/cmd/entities/kbm/get_node.go deleted file mode 100644 index a1c3875..0000000 --- a/cmd/entities/kbm/get_node.go +++ /dev/null @@ -1,39 +0,0 @@ -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 deleted file mode 100644 index 6934161..0000000 --- a/cmd/entities/kbm/get_node_power_feeds.go +++ /dev/null @@ -1,40 +0,0 @@ -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 deleted file mode 100644 index 43e14cc..0000000 --- a/cmd/entities/kbm/kbm_node.go +++ /dev/null @@ -1,43 +0,0 @@ -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 deleted file mode 100644 index 13e79f2..0000000 --- a/cmd/entities/kbm/kbm_node_test.go +++ /dev/null @@ -1,867 +0,0 @@ -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 deleted file mode 100644 index 1c832c6..0000000 --- a/cmd/entities/kbm/list_node_networks.go +++ /dev/null @@ -1,30 +0,0 @@ -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 deleted file mode 100644 index 3736e03..0000000 --- a/cmd/entities/kbm/list_node_slots.go +++ /dev/null @@ -1,26 +0,0 @@ -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 deleted file mode 100644 index ed58d48..0000000 --- a/cmd/entities/kbm/list_nodes.go +++ /dev/null @@ -1,26 +0,0 @@ -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 deleted file mode 100644 index 96c90f1..0000000 --- a/cmd/entities/kbm/set_node_power.go +++ /dev/null @@ -1,60 +0,0 @@ -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 deleted file mode 100644 index e41413b..0000000 --- a/cmd/entities/kbm/update_node.go +++ /dev/null @@ -1,51 +0,0 @@ -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/cmd/login/login.go b/cmd/login/login.go index 5e83b2f..7d52f25 100644 --- a/cmd/login/login.go +++ b/cmd/login/login.go @@ -45,7 +45,7 @@ Example: srvctl login context-name`, } else { contextName = manager.GetDefaultContextName() if contextName == "" { - return fmt.Errorf("no contexts found, log in first: 'srvctl login '") + return errors.New(base.ErrNoContexts) } } diff --git a/testdata/entities/kbm/list_node_slots.json b/testdata/entities/hosts/list_kbm_drive_slots.json similarity index 100% rename from testdata/entities/kbm/list_node_slots.json rename to testdata/entities/hosts/list_kbm_drive_slots.json diff --git a/testdata/entities/kbm/list_node_slots_all.json b/testdata/entities/hosts/list_kbm_drive_slots_all.json similarity index 100% rename from testdata/entities/kbm/list_node_slots_all.json rename to testdata/entities/hosts/list_kbm_drive_slots_all.json diff --git a/testdata/entities/kbm/list_node_slots_pageview.txt b/testdata/entities/hosts/list_kbm_drive_slots_pageview.txt similarity index 100% rename from testdata/entities/kbm/list_node_slots_pageview.txt rename to testdata/entities/hosts/list_kbm_drive_slots_pageview.txt diff --git a/testdata/entities/kbm/list_node_slots_template.txt b/testdata/entities/hosts/list_kbm_drive_slots_template.txt similarity index 100% rename from testdata/entities/kbm/list_node_slots_template.txt rename to testdata/entities/hosts/list_kbm_drive_slots_template.txt diff --git a/testdata/entities/kbm/list_node_networks.json b/testdata/entities/hosts/list_kbm_networks.json similarity index 100% rename from testdata/entities/kbm/list_node_networks.json rename to testdata/entities/hosts/list_kbm_networks.json diff --git a/testdata/entities/kbm/list_node_networks_all.json b/testdata/entities/hosts/list_kbm_networks_all.json similarity index 100% rename from testdata/entities/kbm/list_node_networks_all.json rename to testdata/entities/hosts/list_kbm_networks_all.json diff --git a/testdata/entities/kbm/list_node_networks_pageview.txt b/testdata/entities/hosts/list_kbm_networks_pageview.txt similarity index 100% rename from testdata/entities/kbm/list_node_networks_pageview.txt rename to testdata/entities/hosts/list_kbm_networks_pageview.txt diff --git a/testdata/entities/kbm/list_node_networks_template.txt b/testdata/entities/hosts/list_kbm_networks_template.txt similarity index 100% rename from testdata/entities/kbm/list_node_networks_template.txt rename to testdata/entities/hosts/list_kbm_networks_template.txt diff --git a/testdata/entities/kbm/get_node_power_feeds.json b/testdata/entities/hosts/list_kbm_power_feeds.json similarity index 100% rename from testdata/entities/kbm/get_node_power_feeds.json rename to testdata/entities/hosts/list_kbm_power_feeds.json diff --git a/testdata/entities/kbm/get_node_power_feeds.txt b/testdata/entities/hosts/list_kbm_power_feeds.txt similarity index 100% rename from testdata/entities/kbm/get_node_power_feeds.txt rename to testdata/entities/hosts/list_kbm_power_feeds.txt diff --git a/testdata/entities/kbm/get_node_power_feeds.yaml b/testdata/entities/hosts/list_kbm_power_feeds.yaml similarity index 100% rename from testdata/entities/kbm/get_node_power_feeds.yaml rename to testdata/entities/hosts/list_kbm_power_feeds.yaml diff --git a/testdata/entities/kbm/set_node_power.json b/testdata/entities/hosts/set_kbm_power.json similarity index 100% rename from testdata/entities/kbm/set_node_power.json rename to testdata/entities/hosts/set_kbm_power.json diff --git a/testdata/entities/kbm/get_node.json b/testdata/entities/kbm/get_node.json deleted file mode 100644 index f24d9c8..0000000 --- a/testdata/entities/kbm/get_node.json +++ /dev/null @@ -1,36 +0,0 @@ -{ - "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 deleted file mode 100644 index 7a89f73..0000000 --- a/testdata/entities/kbm/get_node.txt +++ /dev/null @@ -1,2 +0,0 @@ -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 deleted file mode 100644 index d2fc0be..0000000 --- a/testdata/entities/kbm/get_node.yaml +++ /dev/null @@ -1,33 +0,0 @@ -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/list_nodes.json b/testdata/entities/kbm/list_nodes.json deleted file mode 100644 index 22dee12..0000000 --- a/testdata/entities/kbm/list_nodes.json +++ /dev/null @@ -1,38 +0,0 @@ -[ - { - "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 deleted file mode 100644 index 381f282..0000000 --- a/testdata/entities/kbm/list_nodes_all.json +++ /dev/null @@ -1,74 +0,0 @@ -[ - { - "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 deleted file mode 100644 index ecb35a2..0000000 --- a/testdata/entities/kbm/list_nodes_pageview.txt +++ /dev/null @@ -1,67 +0,0 @@ -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 deleted file mode 100644 index e8bd6d2..0000000 --- a/testdata/entities/kbm/list_nodes_template.txt +++ /dev/null @@ -1,2 +0,0 @@ -Title: example.aa Type: kubernetes_baremetal_node -Title: example.bb Type: kubernetes_baremetal_node diff --git a/testdata/entities/kbm/update_node.json b/testdata/entities/kbm/update_node.json deleted file mode 100644 index 5010e3d..0000000 --- a/testdata/entities/kbm/update_node.json +++ /dev/null @@ -1,38 +0,0 @@ -{ - "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