Skip to content

Commit d1cabbe

Browse files
authored
feat(kubernetes): add support for control-plane IP filtering (#245)
1 parent 4d431c5 commit d1cabbe

11 files changed

Lines changed: 208 additions & 17 deletions

File tree

CHANGELOG.md

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

1010
### Added
1111
- 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+
- Add `kubernetes modify` command for modifying IP addresses that are allowed to access cluster's Kubernetes API.
13+
- Add `--kubernetes-api-allow-ip` argument to `kubernetes create` command.
14+
- Add `Kubernetes API allowed IPs` field to `kubernetes show` output.
1215

1316
### Changed
1417
- In human readable output of `kubernetes show` command, show node-groups as table. Node-group datails are available with `kubernetes nodegroup show` command.

go.mod

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -4,7 +4,7 @@ go 1.20
44

55
require (
66
github.com/UpCloudLtd/progress v1.0.1
7-
github.com/UpCloudLtd/upcloud-go-api/v6 v6.5.0
7+
github.com/UpCloudLtd/upcloud-go-api/v6 v6.6.0
88
github.com/adrg/xdg v0.3.2
99
github.com/asaskevich/govalidator v0.0.0-20210307081110-f21760c49a8d
1010
github.com/gemalto/flume v0.12.0

go.sum

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -40,8 +40,8 @@ github.com/PuerkitoBio/purell v1.1.1 h1:WEQqlqaGbrPkxLJWfBwQmfEAE1Z7ONdDLqrN38tN
4040
github.com/PuerkitoBio/urlesc v0.0.0-20170810143723-de5bf2ad4578 h1:d+Bc7a5rLufV/sSk/8dngufqelfh6jnri85riMAaF/M=
4141
github.com/UpCloudLtd/progress v1.0.1 h1:e0ptyD2oOGa3udRcLzgRemIN9enGx4Bc9GQ0sZ/1/EY=
4242
github.com/UpCloudLtd/progress v1.0.1/go.mod h1:hKsRsvlCffcYt/s0krpWvOFozOjpfUYjSkL6CzZztoI=
43-
github.com/UpCloudLtd/upcloud-go-api/v6 v6.5.0 h1:bOZY2kNRZo7J+9vgG37Z7S36Wb1SSoTNmg+RPaL6eJs=
44-
github.com/UpCloudLtd/upcloud-go-api/v6 v6.5.0/go.mod h1:I8rWmBBl+OhiY3AGzKbrobiE5TsLCLNYkCQxE4eJcTg=
43+
github.com/UpCloudLtd/upcloud-go-api/v6 v6.6.0 h1:Fc9a083OBzl8i4pDV2KXCAfxo4gCjJFHgRuPvRnroBY=
44+
github.com/UpCloudLtd/upcloud-go-api/v6 v6.6.0/go.mod h1:I8rWmBBl+OhiY3AGzKbrobiE5TsLCLNYkCQxE4eJcTg=
4545
github.com/adrg/xdg v0.3.2 h1:GUSGQ5pHdev83AYhDSS1A/CX+0JIsxbiWtow2DSA+RU=
4646
github.com/adrg/xdg v0.3.2/go.mod h1:7I2hH/IT30IsupOpKZ5ue7/qNi3CoKzD6tL3HwpaRMQ=
4747
github.com/alecthomas/template v0.0.0-20160405071501-a0175ee3bccc/go.mod h1:LOuyumcjzFXgccqObfd/Ljyb9UuFJ6TxHnclSeseNhc=

internal/commands/all/all.go

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -152,6 +152,7 @@ func BuildCommands(rootCmd *cobra.Command, conf *config.Config) {
152152
// Kubernetes
153153
kubernetesCommand := commands.BuildCommand(kubernetes.BaseKubernetesCommand(), rootCmd, conf)
154154
commands.BuildCommand(kubernetes.CreateCommand(), kubernetesCommand.Cobra(), conf)
155+
commands.BuildCommand(kubernetes.ModifyCommand(), kubernetesCommand.Cobra(), conf)
155156
commands.BuildCommand(kubernetes.ConfigCommand(), kubernetesCommand.Cobra(), conf)
156157
commands.BuildCommand(kubernetes.DeleteCommand(), kubernetesCommand.Cobra(), conf)
157158
commands.BuildCommand(kubernetes.ListCommand(), kubernetesCommand.Cobra(), conf)

internal/commands/kubernetes/create.go

Lines changed: 6 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -125,6 +125,12 @@ func (c *createCommand) InitCommand() {
125125
"taint=\"env=dev:NoSchedule\","+
126126
"taint=\"env=dev2:NoSchedule\"`",
127127
)
128+
fs.StringArrayVar(
129+
&c.params.ControlPlaneIPFilter,
130+
"kubernetes-api-allow-ip",
131+
[]string{},
132+
"Allow cluster's Kubernetes API to be accessed from an IP address or a network CIDR, multiple can be declared.",
133+
)
128134
config.AddToggleFlag(fs, &c.params.privateNodeGroups, "private-node-groups", false, "Do not assign public IPs to worker nodes. If set, the attached network should have a NAT gateway configured to provide internet access to the worker nodes.")
129135
fs.StringVar(&c.params.Zone, "zone", "", namedargs.ZoneDescription("cluster"))
130136
config.AddToggleFlag(fs, &c.params.wait, "wait", false, "Wait for cluster to be in running state before returning.")

internal/commands/kubernetes/create_test.go

Lines changed: 4 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -32,9 +32,10 @@ func TestCreateKubernetes(t *testing.T) {
3232
}
3333
}
3434
nodeGroupRequest := request.CreateKubernetesClusterRequest{
35-
Name: "my-cluster",
36-
Network: "aa39e313-d908-418a-a959-459699bdc83a",
37-
NetworkCIDR: "172.16.1.0/24",
35+
ControlPlaneIPFilter: []string{},
36+
Name: "my-cluster",
37+
Network: "aa39e313-d908-418a-a959-459699bdc83a",
38+
NetworkCIDR: "172.16.1.0/24",
3839
NodeGroups: []request.KubernetesNodeGroup{
3940
{
4041
Count: 2,
Lines changed: 62 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,62 @@
1+
package kubernetes
2+
3+
import (
4+
"fmt"
5+
6+
"github.com/UpCloudLtd/upcloud-cli/v2/internal/commands"
7+
"github.com/UpCloudLtd/upcloud-cli/v2/internal/completion"
8+
"github.com/UpCloudLtd/upcloud-cli/v2/internal/output"
9+
"github.com/UpCloudLtd/upcloud-cli/v2/internal/resolver"
10+
11+
"github.com/UpCloudLtd/upcloud-go-api/v6/upcloud/request"
12+
"github.com/spf13/pflag"
13+
)
14+
15+
// ModifyCommand creates the "kubernetes modify" command
16+
func ModifyCommand() commands.Command {
17+
return &modifyCommand{
18+
BaseCommand: commands.New(
19+
"modify",
20+
"Modifiy an existing cluster",
21+
"upctl cluster modify 00bb4617-c592-4b32-b869-35a60b323b18 --plan 1xCPU-1GB",
22+
),
23+
}
24+
}
25+
26+
type modifyCommand struct {
27+
*commands.BaseCommand
28+
resolver.CachingKubernetes
29+
completion.Kubernetes
30+
params request.ModifyKubernetesClusterRequest
31+
}
32+
33+
// InitCommand implements Command.InitCommand
34+
func (c *modifyCommand) InitCommand() {
35+
fs := &pflag.FlagSet{}
36+
fs.StringArrayVar(
37+
&c.params.Cluster.ControlPlaneIPFilter,
38+
"kubernetes-api-allow-ip",
39+
[]string{},
40+
"Allow cluster's Kubernetes API to be accessed from an IP address or a network CIDR, multiple can be declared.",
41+
)
42+
43+
c.AddFlags(fs)
44+
}
45+
46+
// Execute implements commands.MultipleArgumentCommand
47+
func (c *modifyCommand) Execute(exec commands.Executor, arg string) (output.Output, error) {
48+
msg := fmt.Sprintf("Modifying Kubernetes cluster %v", arg)
49+
exec.PushProgressStarted(msg)
50+
51+
req := c.params
52+
req.ClusterUUID = arg
53+
54+
res, err := exec.All().ModifyKubernetesCluster(exec.Context(), &req)
55+
if err != nil {
56+
return commands.HandleError(exec, msg, err)
57+
}
58+
59+
exec.PushProgressSuccess(msg)
60+
61+
return output.OnlyMarshaled{Value: res}, nil
62+
}
Lines changed: 75 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,75 @@
1+
package kubernetes
2+
3+
import (
4+
"testing"
5+
6+
"github.com/UpCloudLtd/upcloud-cli/v2/internal/commands"
7+
"github.com/UpCloudLtd/upcloud-cli/v2/internal/config"
8+
smock "github.com/UpCloudLtd/upcloud-cli/v2/internal/mock"
9+
"github.com/UpCloudLtd/upcloud-cli/v2/internal/mockexecute"
10+
11+
"github.com/UpCloudLtd/upcloud-go-api/v6/upcloud"
12+
"github.com/UpCloudLtd/upcloud-go-api/v6/upcloud/request"
13+
"github.com/stretchr/testify/assert"
14+
"github.com/stretchr/testify/require"
15+
)
16+
17+
func TestModifyKubernetesCluster(t *testing.T) {
18+
clusterUUID := "28c80353-98fd-4221-85e0-82d7603756ba"
19+
20+
for _, test := range []struct {
21+
name string
22+
args []string
23+
expected request.ModifyKubernetesClusterRequest
24+
errorMsg string
25+
}{
26+
{
27+
name: "no args",
28+
args: []string{clusterUUID},
29+
expected: request.ModifyKubernetesClusterRequest{ClusterUUID: clusterUUID, Cluster: request.ModifyKubernetesCluster{ControlPlaneIPFilter: []string{}}},
30+
},
31+
{
32+
name: "one IP",
33+
args: []string{
34+
clusterUUID,
35+
"--kubernetes-api-allow-ip", "10.144.1.100",
36+
},
37+
expected: request.ModifyKubernetesClusterRequest{
38+
ClusterUUID: clusterUUID,
39+
Cluster: request.ModifyKubernetesCluster{ControlPlaneIPFilter: []string{"10.144.1.100"}},
40+
},
41+
},
42+
{
43+
name: "IP and CIDR",
44+
args: []string{
45+
clusterUUID,
46+
"--kubernetes-api-allow-ip", "10.144.1.100",
47+
"--kubernetes-api-allow-ip", "10.144.2.0/24",
48+
},
49+
expected: request.ModifyKubernetesClusterRequest{
50+
ClusterUUID: clusterUUID,
51+
Cluster: request.ModifyKubernetesCluster{ControlPlaneIPFilter: []string{"10.144.1.100", "10.144.2.0/24"}},
52+
},
53+
},
54+
} {
55+
t.Run(test.name, func(t *testing.T) {
56+
conf := config.New()
57+
testCmd := ModifyCommand()
58+
mService := new(smock.Service)
59+
60+
mService.On("ModifyKubernetesCluster", &test.expected).Return(&upcloud.KubernetesCluster{}, nil)
61+
62+
c := commands.BuildCommand(testCmd, nil, conf)
63+
64+
c.Cobra().SetArgs(test.args)
65+
_, err := mockexecute.MockExecute(c, mService, conf)
66+
67+
if test.errorMsg != "" {
68+
assert.EqualError(t, err, test.errorMsg)
69+
} else {
70+
require.NoError(t, err)
71+
mService.AssertNumberOfCalls(t, "ModifyKubernetesCluster", 1)
72+
}
73+
})
74+
}
75+
}

internal/commands/kubernetes/show.go

Lines changed: 32 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1,12 +1,16 @@
11
package kubernetes
22

33
import (
4+
"fmt"
5+
"strings"
6+
47
"github.com/UpCloudLtd/upcloud-cli/v2/internal/commands"
58
"github.com/UpCloudLtd/upcloud-cli/v2/internal/completion"
69
"github.com/UpCloudLtd/upcloud-cli/v2/internal/format"
710
"github.com/UpCloudLtd/upcloud-cli/v2/internal/output"
811
"github.com/UpCloudLtd/upcloud-cli/v2/internal/resolver"
912
"github.com/UpCloudLtd/upcloud-cli/v2/internal/ui"
13+
"github.com/jedib0t/go-pretty/v6/text"
1014

1115
"github.com/UpCloudLtd/upcloud-go-api/v6/upcloud/request"
1216
)
@@ -80,6 +84,7 @@ func (s *showCommand) Execute(exec commands.Executor, uuid string) (output.Outpu
8084
{Title: "Network UUID:", Value: cluster.Network, Colour: ui.DefaultUUUIDColours},
8185
{Title: "Network name:", Value: networkName, Format: format.PossiblyUnknownString},
8286
{Title: "Network CIDR:", Value: cluster.NetworkCIDR, Colour: ui.DefaultAddressColours},
87+
{Title: "Kubernetes API allowed IPs:", Value: cluster.ControlPlaneIPFilter, Format: formatIPFilter},
8388
{Title: "Private node groups:", Value: cluster.PrivateNodeGroups, Format: format.Boolean},
8489
{Title: "Zone:", Value: cluster.Zone},
8590
{Title: "Operational state:", Value: cluster.State, Format: format.KubernetesClusterState},
@@ -100,3 +105,30 @@ func (s *showCommand) Execute(exec commands.Executor, uuid string) (output.Outpu
100105
},
101106
}, nil
102107
}
108+
109+
func formatIPFilter(val interface{}) (text.Colors, string, error) {
110+
addresses, ok := val.([]string)
111+
if !ok {
112+
return nil, "", fmt.Errorf("cannot parse IP addresses from %T, expected []string", val)
113+
}
114+
115+
allowAll := false
116+
var strs []string
117+
for _, ipa := range addresses {
118+
if ipa == "0.0.0.0/0" {
119+
allowAll = true
120+
}
121+
122+
strs = append(strs, ui.DefaultAddressColours.Sprint(ipa))
123+
}
124+
125+
if addresses == nil || allowAll {
126+
return nil, "all", nil
127+
}
128+
129+
if addresses == nil || allowAll {
130+
return nil, text.FgHiBlack.Sprint("none"), nil
131+
}
132+
133+
return nil, strings.Join(strs, ",\n"), nil
134+
}

internal/commands/kubernetes/show_test.go

Lines changed: 14 additions & 11 deletions
Original file line numberDiff line numberDiff line change
@@ -18,9 +18,10 @@ import (
1818
func TestShowCommand(t *testing.T) {
1919
text.DisableColors()
2020
cluster1 := upcloud.KubernetesCluster{
21-
Name: "upcloud-go-sdk-unit-test",
22-
Network: "03a98be3-7daa-443f-bb25-4bc6854b396c",
23-
NetworkCIDR: "172.16.1.0/24",
21+
ControlPlaneIPFilter: []string{"10.144.1.100", "10.144.2.0/24"},
22+
Name: "upcloud-go-sdk-unit-test",
23+
Network: "03a98be3-7daa-443f-bb25-4bc6854b396c",
24+
NetworkCIDR: "172.16.1.0/24",
2425
NodeGroups: []upcloud.KubernetesNodeGroup{
2526
{
2627
Count: 4,
@@ -103,14 +104,16 @@ func TestShowCommand(t *testing.T) {
103104

104105
expected := `
105106
Overview:
106-
UUID: 0ddab8f4-97c0-4222-91ba-85a4fff7499b
107-
Name: upcloud-go-sdk-unit-test
108-
Network UUID: 03a98be3-7daa-443f-bb25-4bc6854b396c
109-
Network name: Test network
110-
Network CIDR: 172.16.1.0/24
111-
Private node groups: no
112-
Zone: de-fra1
113-
Operational state: running
107+
UUID: 0ddab8f4-97c0-4222-91ba-85a4fff7499b
108+
Name: upcloud-go-sdk-unit-test
109+
Network UUID: 03a98be3-7daa-443f-bb25-4bc6854b396c
110+
Network name: Test network
111+
Network CIDR: 172.16.1.0/24
112+
Kubernetes API allowed IPs: 10.144.1.100,
113+
10.144.2.0/24
114+
Private node groups: no
115+
Zone: de-fra1
116+
Operational state: running
114117
115118
Node groups:
116119

0 commit comments

Comments
 (0)