Skip to content

Commit d45b601

Browse files
authored
feat(kubernetes): add kubernetes nodegroup show command (#242)
1 parent 328357f commit d45b601

14 files changed

Lines changed: 433 additions & 100 deletions

File tree

CHANGELOG.md

Lines changed: 6 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -7,6 +7,12 @@ and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0
77

88
## [Unreleased]
99

10+
### Added
11+
- Add `kubernetes nodegroup show` for displaying node-group details. This also adds _Nodes_ table and _Anti-affinity_ field that were not available in previous `kubernetes show` output.
12+
13+
### Changed
14+
- In human readable output of `kubernetes show` command, show node-groups as table. Node-group datails are available with `kubernetes nodegroup show` command.
15+
1016
## [2.10.0] - 2023-07-17
1117
### Added
1218
- Add `--disable-utility-network-access` for `kubernetes nodegroup create` command

README.md

Lines changed: 2 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -28,11 +28,12 @@ completion Generate the autocompletion script for the specified shell
2828
database Manage databases
2929
help Help about any command
3030
ip-address Manage IP addresses
31-
kubernetes Manage Kubernetes clusters (EXPERIMENTAL)
31+
kubernetes Manage Kubernetes clusters
3232
loadbalancer Manage load balancers
3333
network Manage networks
3434
router Manage routers
3535
server Manage servers
36+
servergroup Manage server groups
3637
storage Manage storages
3738
version Display software information
3839
zone Display zone information

internal/commands/all/all.go

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -163,6 +163,7 @@ func BuildCommands(rootCmd *cobra.Command, conf *config.Config) {
163163
nodeGroupCommand := commands.BuildCommand(nodegroup.BaseNodeGroupCommand(), kubernetesCommand.Cobra(), conf)
164164
commands.BuildCommand(nodegroup.CreateCommand(), nodeGroupCommand.Cobra(), conf)
165165
commands.BuildCommand(nodegroup.ScaleCommand(), nodeGroupCommand.Cobra(), conf)
166+
commands.BuildCommand(nodegroup.ShowCommand(), nodeGroupCommand.Cobra(), conf)
166167
commands.BuildCommand(nodegroup.DeleteCommand(), nodeGroupCommand.Cobra(), conf)
167168

168169
// Server group operations

internal/commands/kubernetes/kubernetes.go

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -7,7 +7,7 @@ import (
77
// BaseKubernetesCommand creates the base "kubernetes" command
88
func BaseKubernetesCommand() commands.Command {
99
return &kubernetesCommand{
10-
commands.New("kubernetes", "Manage Kubernetes clusters (EXPERIMENTAL)"),
10+
commands.New("kubernetes", "Manage Kubernetes clusters"),
1111
}
1212
}
1313

internal/commands/kubernetes/nodegroup/delete.go

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -38,8 +38,8 @@ func (s *deleteCommand) InitCommand() {
3838
_ = s.Cobra().MarkFlagRequired("name")
3939
}
4040

41-
// Execute implements commands.MultipleArgumentCommand
42-
func (s *deleteCommand) Execute(exec commands.Executor, arg string) (output.Output, error) {
41+
// ExecuteSingleArgument implements commands.SingleArgumentCommand
42+
func (s *deleteCommand) ExecuteSingleArgument(exec commands.Executor, arg string) (output.Output, error) {
4343
msg := fmt.Sprintf("Deleting node group %s from cluster %v", s.name, arg)
4444
exec.PushProgressStarted(msg)
4545

internal/commands/kubernetes/nodegroup/nodegroup.go

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -7,7 +7,7 @@ import (
77
// BaseNodeGroupCommand creates the base "kubernetes nodegroups" command
88
func BaseNodeGroupCommand() commands.Command {
99
return &nodegroupCommand{
10-
commands.New("nodegroup", "Manage cluster node-groups (EXPERIMENTAL)"),
10+
commands.New("nodegroup", "Manage cluster node-groups"),
1111
}
1212
}
1313

internal/commands/kubernetes/nodegroup/scale.go

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -41,8 +41,8 @@ func (s *scaleCommand) InitCommand() {
4141
_ = s.Cobra().MarkFlagRequired("count")
4242
}
4343

44-
// Execute implements commands.MultipleArgumentCommand
45-
func (s *scaleCommand) Execute(exec commands.Executor, arg string) (output.Output, error) {
44+
// ExecuteSingleArgument implements commands.SingleArgumentCommand
45+
func (s *scaleCommand) ExecuteSingleArgument(exec commands.Executor, arg string) (output.Output, error) {
4646
msg := fmt.Sprintf("Scaling node group %s of cluster %v", s.name, arg)
4747
exec.PushProgressStarted(msg)
4848

Lines changed: 151 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,151 @@
1+
package nodegroup
2+
3+
import (
4+
"github.com/UpCloudLtd/upcloud-cli/v2/internal/commands"
5+
"github.com/UpCloudLtd/upcloud-cli/v2/internal/completion"
6+
"github.com/UpCloudLtd/upcloud-cli/v2/internal/format"
7+
"github.com/UpCloudLtd/upcloud-cli/v2/internal/labels"
8+
"github.com/UpCloudLtd/upcloud-cli/v2/internal/output"
9+
"github.com/UpCloudLtd/upcloud-cli/v2/internal/resolver"
10+
"github.com/UpCloudLtd/upcloud-cli/v2/internal/ui"
11+
"github.com/spf13/pflag"
12+
13+
"github.com/UpCloudLtd/upcloud-go-api/v6/upcloud/request"
14+
)
15+
16+
// ShowCommand creates the "kubernetes nodegroup show" command
17+
func ShowCommand() commands.Command {
18+
return &showCommand{
19+
BaseCommand: commands.New(
20+
"show",
21+
"Show node group details",
22+
"upctl kubernetes nodegroup show 55199a44-4751-4e27-9394-7c7661910be3 --name default",
23+
),
24+
}
25+
}
26+
27+
type showCommand struct {
28+
*commands.BaseCommand
29+
name string
30+
resolver.CachingKubernetes
31+
completion.Kubernetes
32+
}
33+
34+
// InitCommand implements Command.InitCommand
35+
func (s *showCommand) InitCommand() {
36+
flagSet := &pflag.FlagSet{}
37+
flagSet.StringVar(&s.name, "name", "", "Node group name")
38+
s.AddFlags(flagSet)
39+
40+
_ = s.Cobra().MarkFlagRequired("name")
41+
}
42+
43+
// ExecuteSingleArgument implements commands.SingleArgumentCommand
44+
func (s *showCommand) ExecuteSingleArgument(exec commands.Executor, uuid string) (output.Output, error) {
45+
svc := exec.All()
46+
nodeGroup, err := svc.GetKubernetesNodeGroup(exec.Context(), &request.GetKubernetesNodeGroupRequest{ClusterUUID: uuid, Name: s.name})
47+
if err != nil {
48+
return nil, err
49+
}
50+
51+
taintColumns := []output.TableColumn{
52+
{Key: "key", Header: "Key"},
53+
{Key: "value", Header: "Value"},
54+
{Key: "effect", Header: "Effect"},
55+
}
56+
57+
taintRows := []output.TableRow{}
58+
for _, taint := range nodeGroup.Taints {
59+
taintRows = append(taintRows, output.TableRow{
60+
taint.Key,
61+
taint.Value,
62+
taint.Effect,
63+
})
64+
}
65+
66+
kubeletArgColumns := taintColumns[0:2]
67+
68+
kubeletArgRows := []output.TableRow{}
69+
for _, kubeletArg := range nodeGroup.KubeletArgs {
70+
kubeletArgRows = append(kubeletArgRows, output.TableRow{
71+
kubeletArg.Key,
72+
kubeletArg.Value,
73+
})
74+
}
75+
76+
nodeRows := []output.TableRow{}
77+
for _, node := range nodeGroup.Nodes {
78+
nodeRows = append(nodeRows, output.TableRow{
79+
node.UUID,
80+
node.Name,
81+
node.State,
82+
})
83+
}
84+
85+
nodeColumns := []output.TableColumn{
86+
{Key: "uuid", Header: "UUID", Colour: ui.DefaultUUUIDColours},
87+
{Key: "name", Header: "Name"},
88+
{Key: "state", Header: "State", Format: format.KubernetesNodeState},
89+
}
90+
91+
var storageName string
92+
if storage, err := svc.GetStorageDetails(exec.Context(), &request.GetStorageDetailsRequest{UUID: nodeGroup.Storage}); err != nil {
93+
storageName = ""
94+
} else {
95+
storageName = storage.Title
96+
}
97+
98+
// For JSON and YAML output, passthrough API response
99+
return output.MarshaledWithHumanOutput{
100+
Value: nodeGroup,
101+
Output: output.Combined{
102+
output.CombinedSection{
103+
Contents: output.Details{
104+
Sections: []output.DetailSection{
105+
{
106+
Title: "Overview",
107+
Rows: []output.DetailRow{
108+
{Title: "Name:", Value: nodeGroup.Name},
109+
{Title: "Count:", Value: nodeGroup.Count},
110+
{Title: "Plan:", Value: nodeGroup.Plan},
111+
{Title: "State:", Value: nodeGroup.State, Format: format.KubernetesNodeGroupState},
112+
{Title: "Storage UUID:", Value: nodeGroup.Storage, Colour: ui.DefaultUUUIDColours},
113+
{Title: "Storage name:", Value: storageName, Format: format.PossiblyUnknownString},
114+
{Title: "Anti-affinity:", Value: nodeGroup.AntiAffinity, Format: format.Boolean},
115+
{Title: "Utility network access:", Value: nodeGroup.UtilityNetworkAccess, Format: format.Boolean},
116+
},
117+
},
118+
},
119+
},
120+
},
121+
labels.GetLabelsSectionWithResourceType(nodeGroup.Labels, "node group"),
122+
output.CombinedSection{
123+
Key: "taints",
124+
Title: "Taints:",
125+
Contents: output.Table{
126+
Columns: taintColumns,
127+
Rows: taintRows,
128+
EmptyMessage: "No taints defined for this node group.",
129+
},
130+
},
131+
output.CombinedSection{
132+
Key: "kubelet_args",
133+
Title: "Kubelet arguments:",
134+
Contents: output.Table{
135+
Columns: kubeletArgColumns,
136+
Rows: kubeletArgRows,
137+
EmptyMessage: "No kubelet arguments defined for this node group.",
138+
},
139+
},
140+
output.CombinedSection{
141+
Key: "nodes",
142+
Title: "Nodes:",
143+
Contents: output.Table{
144+
Columns: nodeColumns,
145+
Rows: nodeRows,
146+
EmptyMessage: "No nodes found for this node group.",
147+
},
148+
},
149+
},
150+
}, nil
151+
}

0 commit comments

Comments
 (0)