Skip to content

Commit a38de77

Browse files
authored
feat(kubernetes): add labels support (#286)
1 parent 50afd69 commit a38de77

7 files changed

Lines changed: 118 additions & 6 deletions

File tree

CHANGELOG.md

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

88
## [Unreleased]
99

10+
### Added
11+
12+
- Support Kubernetes cluster labels: list labels with `show` commands and manage them with `create` and `modify` commands
13+
1014
## [3.5.0] - 2024-02-29
1115

1216
### Added

internal/commands/kubernetes/create.go

Lines changed: 12 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -7,6 +7,7 @@ import (
77
"github.com/UpCloudLtd/upcloud-cli/v3/internal/commands/kubernetes/nodegroup"
88
"github.com/UpCloudLtd/upcloud-cli/v3/internal/completion"
99
"github.com/UpCloudLtd/upcloud-cli/v3/internal/config"
10+
"github.com/UpCloudLtd/upcloud-cli/v3/internal/labels"
1011
"github.com/UpCloudLtd/upcloud-cli/v3/internal/namedargs"
1112
"github.com/UpCloudLtd/upcloud-cli/v3/internal/output"
1213
"github.com/UpCloudLtd/upcloud-cli/v3/internal/ui"
@@ -39,15 +40,24 @@ func CreateCommand() commands.Command {
3940

4041
type createParams struct {
4142
request.CreateKubernetesClusterRequest
43+
labels []string
4244
networkArg string
4345
nodeGroups []string
4446
privateNodeGroups config.OptionalBoolean
4547
wait config.OptionalBoolean
4648
}
4749

4850
func (p *createParams) processParams(exec commands.Executor) error {
49-
ngs := make([]request.KubernetesNodeGroup, 0)
51+
if len(p.labels) > 0 {
52+
labelSlice, err := labels.StringsToSliceOfLabels(p.labels)
53+
if err != nil {
54+
return err
55+
}
5056

57+
p.Labels = labelSlice
58+
}
59+
60+
ngs := make([]request.KubernetesNodeGroup, 0)
5161
for _, v := range p.nodeGroups {
5262
ng, err := processNodeGroup(v)
5363
if err != nil {
@@ -105,6 +115,7 @@ func (c *createCommand) InitCommand() {
105115
c.params = createParams{CreateKubernetesClusterRequest: request.CreateKubernetesClusterRequest{}}
106116

107117
fs.StringVar(&c.params.Name, "name", "", "Kubernetes cluster name.")
118+
fs.StringArrayVar(&c.params.labels, "label", nil, "Labels to describe the cluster in `key=value` format, multiple can be declared.")
108119
fs.StringVar(&c.params.Plan, "plan", "development", "Plan to use for the cluster. Run `upctl kubernetes plans` to list all available plans.")
109120
fs.StringVar(&c.params.Version, "version", "", "Identifier of the version of Kubernetes to use when creating the cluster. Run `upctl kubernetes versions` to list all available versions.")
110121
fs.StringVar(&c.params.networkArg, "network", "", "Network to use. The value should be name or UUID of a private network.")

internal/commands/kubernetes/create_test.go

Lines changed: 16 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -100,6 +100,16 @@ func TestCreateKubernetes(t *testing.T) {
100100
privateNodeGroupsRequest := nodeGroupRequest
101101
privateNodeGroupsRequest.PrivateNodeGroups = true
102102

103+
labelsArg := []string{
104+
"--label", "tool=upctl",
105+
"--label", "test=unittest",
106+
}
107+
labelsRequest := nodeGroupRequest
108+
labelsRequest.Labels = []upcloud.Label{
109+
{Key: "tool", Value: "upctl"},
110+
{Key: "test", Value: "unittest"},
111+
}
112+
103113
versionArg := []string{"--version", "0.99"}
104114
versionRequest := nodeGroupRequest
105115
versionRequest.Version = "0.99"
@@ -140,6 +150,12 @@ func TestCreateKubernetes(t *testing.T) {
140150
request: versionRequest,
141151
wantErr: false,
142152
},
153+
{
154+
name: "with labels",
155+
args: append(nodeGroupArgs(network.Name), labelsArg...),
156+
request: labelsRequest,
157+
wantErr: false,
158+
},
143159
} {
144160
t.Run(test.name, func(t *testing.T) {
145161
conf := config.New()

internal/commands/kubernetes/modify.go

Lines changed: 22 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -5,9 +5,12 @@ import (
55

66
"github.com/UpCloudLtd/upcloud-cli/v3/internal/commands"
77
"github.com/UpCloudLtd/upcloud-cli/v3/internal/completion"
8+
"github.com/UpCloudLtd/upcloud-cli/v3/internal/config"
9+
"github.com/UpCloudLtd/upcloud-cli/v3/internal/labels"
810
"github.com/UpCloudLtd/upcloud-cli/v3/internal/output"
911
"github.com/UpCloudLtd/upcloud-cli/v3/internal/resolver"
1012

13+
"github.com/UpCloudLtd/upcloud-go-api/v8/upcloud"
1114
"github.com/UpCloudLtd/upcloud-go-api/v8/upcloud/request"
1215
"github.com/spf13/pflag"
1316
)
@@ -27,7 +30,10 @@ type modifyCommand struct {
2730
*commands.BaseCommand
2831
resolver.CachingKubernetes
2932
completion.Kubernetes
33+
3034
controlPlaneIPFilter []string
35+
labels []string
36+
clearLabels config.OptionalBoolean
3137
}
3238

3339
// InitCommand implements Command.InitCommand
@@ -39,8 +45,11 @@ func (c *modifyCommand) InitCommand() {
3945
[]string{},
4046
"Allow cluster's Kubernetes API to be accessed from an IP address or a network CIDR, multiple can be declared.",
4147
)
48+
fs.StringArrayVar(&c.labels, "label", nil, "Labels to describe the cluster in `key=value` format, multiple can be declared.")
49+
config.AddToggleFlag(fs, &c.clearLabels, "clear-labels", false, "Clear all labels from to given cluster.")
4250

4351
c.AddFlags(fs)
52+
c.Cobra().MarkFlagsMutuallyExclusive("label", "clear-labels")
4453
}
4554

4655
// Execute implements commands.MultipleArgumentCommand
@@ -57,6 +66,19 @@ func (c *modifyCommand) Execute(exec commands.Executor, arg string) (output.Outp
5766
req.Cluster.ControlPlaneIPFilter = &c.controlPlaneIPFilter
5867
}
5968

69+
if c.clearLabels.Value() {
70+
req.Cluster.Labels = &[]upcloud.Label{}
71+
}
72+
73+
if len(c.labels) > 0 {
74+
labelSlice, err := labels.StringsToSliceOfLabels(c.labels)
75+
if err != nil {
76+
return nil, err
77+
}
78+
79+
req.Cluster.Labels = &labelSlice
80+
}
81+
6082
res, err := exec.All().ModifyKubernetesCluster(exec.Context(), &req)
6183
if err != nil {
6284
return commands.HandleError(exec, msg, err)

internal/commands/kubernetes/modify_test.go

Lines changed: 50 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -36,7 +36,10 @@ func TestModifyKubernetesCluster(t *testing.T) {
3636
},
3737
expected: request.ModifyKubernetesClusterRequest{
3838
ClusterUUID: clusterUUID,
39-
Cluster: request.ModifyKubernetesCluster{ControlPlaneIPFilter: &[]string{"10.144.1.100"}},
39+
Cluster: request.ModifyKubernetesCluster{
40+
ControlPlaneIPFilter: &[]string{"10.144.1.100"},
41+
Labels: nil,
42+
},
4043
},
4144
},
4245
{
@@ -48,9 +51,54 @@ func TestModifyKubernetesCluster(t *testing.T) {
4851
},
4952
expected: request.ModifyKubernetesClusterRequest{
5053
ClusterUUID: clusterUUID,
51-
Cluster: request.ModifyKubernetesCluster{ControlPlaneIPFilter: &[]string{"10.144.1.100", "10.144.2.0/24"}},
54+
Cluster: request.ModifyKubernetesCluster{
55+
ControlPlaneIPFilter: &[]string{"10.144.1.100", "10.144.2.0/24"},
56+
Labels: nil,
57+
},
5258
},
5359
},
60+
{
61+
name: "labels",
62+
args: []string{
63+
clusterUUID,
64+
"--label", "tool=upctl",
65+
"--label", "test=unittest",
66+
},
67+
expected: request.ModifyKubernetesClusterRequest{
68+
ClusterUUID: clusterUUID,
69+
Cluster: request.ModifyKubernetesCluster{
70+
ControlPlaneIPFilter: nil,
71+
Labels: &[]upcloud.Label{
72+
{Key: "tool", Value: "upctl"},
73+
{Key: "test", Value: "unittest"},
74+
},
75+
},
76+
},
77+
},
78+
{
79+
name: "clear-labels",
80+
args: []string{
81+
clusterUUID,
82+
"--clear-labels",
83+
},
84+
expected: request.ModifyKubernetesClusterRequest{
85+
ClusterUUID: clusterUUID,
86+
Cluster: request.ModifyKubernetesCluster{
87+
ControlPlaneIPFilter: nil,
88+
Labels: &[]upcloud.Label{},
89+
},
90+
},
91+
},
92+
{
93+
name: "labels and clear-labels",
94+
args: []string{
95+
clusterUUID,
96+
"--label", "tool=upctl",
97+
"--label", "test=unittest",
98+
"--clear-labels",
99+
},
100+
errorMsg: "if any flags in the group [label clear-labels] are set none of the others can be; [clear-labels label] were all set",
101+
},
54102
} {
55103
t.Run(test.name, func(t *testing.T) {
56104
conf := config.New()

internal/commands/kubernetes/show.go

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -7,6 +7,7 @@ import (
77
"github.com/UpCloudLtd/upcloud-cli/v3/internal/commands"
88
"github.com/UpCloudLtd/upcloud-cli/v3/internal/completion"
99
"github.com/UpCloudLtd/upcloud-cli/v3/internal/format"
10+
"github.com/UpCloudLtd/upcloud-cli/v3/internal/labels"
1011
"github.com/UpCloudLtd/upcloud-cli/v3/internal/output"
1112
"github.com/UpCloudLtd/upcloud-cli/v3/internal/resolver"
1213
"github.com/UpCloudLtd/upcloud-cli/v3/internal/ui"
@@ -94,6 +95,7 @@ func (s *showCommand) Execute(exec commands.Executor, uuid string) (output.Outpu
9495
},
9596
},
9697
},
98+
labels.GetLabelsSectionWithResourceType(cluster.Labels, "cluster"),
9799
output.CombinedSection{
98100
Key: "node_groups",
99101
Title: "Node groups:",

internal/commands/kubernetes/show_test.go

Lines changed: 12 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -17,9 +17,12 @@ import (
1717

1818
var testCluster = upcloud.KubernetesCluster{
1919
ControlPlaneIPFilter: []string{"10.144.1.100", "10.144.2.0/24"},
20-
Name: "upcloud-upctl-unit-test",
21-
Network: "03a98be3-7daa-443f-bb25-4bc6854b396c",
22-
NetworkCIDR: "172.16.1.0/24",
20+
Labels: []upcloud.Label{
21+
{Key: "test", Value: "upctl-unittest"},
22+
},
23+
Name: "upcloud-upctl-unit-test",
24+
Network: "03a98be3-7daa-443f-bb25-4bc6854b396c",
25+
NetworkCIDR: "172.16.1.0/24",
2326
NodeGroups: []upcloud.KubernetesNodeGroup{
2427
{
2528
Count: 4,
@@ -118,6 +121,12 @@ func TestShowCommand(t *testing.T) {
118121
Zone: de-fra1
119122
Operational state: running
120123
124+
Labels:
125+
126+
Key Value
127+
────── ────────────────
128+
test upctl-unittest
129+
121130
Node groups:
122131
123132
Name Count Plan Anti affinity Utility network access State

0 commit comments

Comments
 (0)