Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

feat(kubernetes): add support to configure control plane firewall #1310

Merged
merged 1 commit into from
Jan 29, 2025
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
34 changes: 34 additions & 0 deletions digitalocean/kubernetes/kubernetes.go
Original file line number Diff line number Diff line change
Expand Up @@ -240,6 +240,29 @@ func expandMaintPolicyOpts(config []interface{}) (*godo.KubernetesMaintenancePol
return maintPolicy, nil
}

func expandControlPlaneFirewallOpts(raw []interface{}) *godo.KubernetesControlPlaneFirewall {
if len(raw) == 0 || raw[0] == nil {
return &godo.KubernetesControlPlaneFirewall{}
}
controlPlaneFirewallConfig := raw[0].(map[string]interface{})

controlPlaneFirewall := &godo.KubernetesControlPlaneFirewall{
Enabled: godo.PtrTo(controlPlaneFirewallConfig["enabled"].(bool)),
AllowedAddresses: expandAllowedAddresses(controlPlaneFirewallConfig["allowed_addresses"].([]interface{})),
}
return controlPlaneFirewall
}

func expandAllowedAddresses(addrs []interface{}) []string {
var expandedAddrs []string
for _, item := range addrs {
if str, ok := item.(string); ok {
expandedAddrs = append(expandedAddrs, str)
}
}
return expandedAddrs
}

func flattenMaintPolicyOpts(opts *godo.KubernetesMaintenancePolicy) []map[string]interface{} {
result := make([]map[string]interface{}, 0)
item := make(map[string]interface{})
Expand All @@ -252,6 +275,17 @@ func flattenMaintPolicyOpts(opts *godo.KubernetesMaintenancePolicy) []map[string
return result
}

func flattenControlPlaneFirewallOpts(opts *godo.KubernetesControlPlaneFirewall) []map[string]interface{} {
result := make([]map[string]interface{}, 0)
item := make(map[string]interface{})

item["enabled"] = opts.Enabled
item["allowed_addresses"] = opts.AllowedAddresses
result = append(result, item)

return result
}

func flattenNodePool(d *schema.ResourceData, keyPrefix string, pool *godo.KubernetesNodePool, parentTags ...string) []interface{} {
rawPool := map[string]interface{}{
"id": pool.ID,
Expand Down
47 changes: 41 additions & 6 deletions digitalocean/kubernetes/resource_kubernetes_cluster.go
Original file line number Diff line number Diff line change
Expand Up @@ -23,6 +23,10 @@ var (
MultipleNodePoolImportError = fmt.Errorf("Cluster contains multiple node pools. Manually add the `%s` tag to the pool that should be used as the default. Additional pools must be imported separately as 'digitalocean_kubernetes_node_pool' resources.", DigitaloceanKubernetesDefaultNodePoolTag)
)

const (
controlPlaneFirewallField = "control_plane_firewall"
)

func ResourceDigitalOceanKubernetesCluster() *schema.Resource {
return &schema.Resource{
CreateContext: resourceDigitalOceanKubernetesClusterCreate,
Expand Down Expand Up @@ -197,6 +201,28 @@ func ResourceDigitalOceanKubernetesCluster() *schema.Resource {
Optional: true,
ValidateFunc: validation.IntAtLeast(0),
},

controlPlaneFirewallField: {
Type: schema.TypeList,
Optional: true,
Computed: true,
MaxItems: 1,
Elem: &schema.Resource{
Schema: map[string]*schema.Schema{
"enabled": {
Type: schema.TypeBool,
Required: true,
},
"allowed_addresses": {
Type: schema.TypeList,
Required: true,
Elem: &schema.Schema{
Type: schema.TypeString,
},
},
},
},
},
},

Timeouts: &schema.ResourceTimeout{
Expand Down Expand Up @@ -326,6 +352,10 @@ func resourceDigitalOceanKubernetesClusterCreate(ctx context.Context, d *schema.
opts.AutoUpgrade = autoUpgrade.(bool)
}

if controlPlaneFirewall, ok := d.GetOk(controlPlaneFirewallField); ok {
opts.ControlPlaneFirewall = expandControlPlaneFirewallOpts(controlPlaneFirewall.([]interface{}))
}

cluster, _, err := client.Kubernetes.Create(context.Background(), opts)
if err != nil {
return diag.Errorf("Error creating Kubernetes cluster: %s", err)
Expand Down Expand Up @@ -389,6 +419,10 @@ func digitaloceanKubernetesClusterRead(
d.Set("auto_upgrade", cluster.AutoUpgrade)
d.Set("urn", cluster.URN())

if err := d.Set(controlPlaneFirewallField, flattenControlPlaneFirewallOpts(cluster.ControlPlaneFirewall)); err != nil {
return diag.Errorf("[DEBUG] Error setting %s - error: %#v", controlPlaneFirewallField, err)
}

if err := d.Set("maintenance_policy", flattenMaintPolicyOpts(cluster.MaintenancePolicy)); err != nil {
return diag.Errorf("[DEBUG] Error setting maintenance_policy - error: %#v", err)
}
Expand Down Expand Up @@ -447,14 +481,15 @@ func resourceDigitalOceanKubernetesClusterUpdate(ctx context.Context, d *schema.
client := meta.(*config.CombinedConfig).GodoClient()

// Figure out the changes and then call the appropriate API methods
if d.HasChanges("name", "tags", "auto_upgrade", "surge_upgrade", "maintenance_policy", "ha") {
if d.HasChanges("name", "tags", "auto_upgrade", "surge_upgrade", "maintenance_policy", "ha", controlPlaneFirewallField) {

opts := &godo.KubernetesClusterUpdateRequest{
Name: d.Get("name").(string),
Tags: tag.ExpandTags(d.Get("tags").(*schema.Set).List()),
AutoUpgrade: godo.PtrTo(d.Get("auto_upgrade").(bool)),
SurgeUpgrade: d.Get("surge_upgrade").(bool),
HA: godo.PtrTo(d.Get("ha").(bool)),
Name: d.Get("name").(string),
Tags: tag.ExpandTags(d.Get("tags").(*schema.Set).List()),
AutoUpgrade: godo.PtrTo(d.Get("auto_upgrade").(bool)),
SurgeUpgrade: d.Get("surge_upgrade").(bool),
HA: godo.PtrTo(d.Get("ha").(bool)),
ControlPlaneFirewall: expandControlPlaneFirewallOpts(d.Get(controlPlaneFirewallField).([]interface{})),
}

if maint, ok := d.GetOk("maintenance_policy"); ok {
Expand Down
76 changes: 76 additions & 0 deletions digitalocean/kubernetes/resource_kubernetes_cluster_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -365,6 +365,52 @@ func TestAccDigitalOceanKubernetesCluster_MaintenancePolicy(t *testing.T) {
})
}

func TestAccDigitalOceanKubernetesCluster_ControlPlaneFirewall(t *testing.T) {
rName := acceptance.RandomTestName()
var k8s godo.KubernetesCluster

firewall := `
control_plane_firewall {
enabled = true
allowed_addresses = ["1.2.3.4/16"]
}
`

firewallUpdate := `
control_plane_firewall {
enabled = false
allowed_addresses = ["1.2.3.4/16", "5.6.7.8/16"]
}
`

resource.ParallelTest(t, resource.TestCase{
PreCheck: func() { acceptance.TestAccPreCheck(t) },
ProviderFactories: acceptance.TestAccProviderFactories,
CheckDestroy: testAccCheckDigitalOceanKubernetesClusterDestroy,
Steps: []resource.TestStep{
{
Config: testAccDigitalOceanKubernetesConfigControlPlaneFirewall(testClusterVersionLatest, rName, firewall),
Check: resource.ComposeAggregateTestCheckFunc(
testAccCheckDigitalOceanKubernetesClusterExists("digitalocean_kubernetes_cluster.foobar", &k8s),
resource.TestCheckResourceAttr("digitalocean_kubernetes_cluster.foobar", "name", rName),
resource.TestCheckResourceAttr("digitalocean_kubernetes_cluster.foobar", "control_plane_firewall.0.enabled", "true"),
resource.TestCheckResourceAttr("digitalocean_kubernetes_cluster.foobar", "control_plane_firewall.0.allowed_addresses.0", "1.2.3.4/16"),
),
},
{
Config: testAccDigitalOceanKubernetesConfigControlPlaneFirewall(testClusterVersionLatest, rName, firewallUpdate),
Check: resource.ComposeAggregateTestCheckFunc(
testAccCheckDigitalOceanKubernetesClusterExists("digitalocean_kubernetes_cluster.foobar", &k8s),
resource.TestCheckResourceAttr("digitalocean_kubernetes_cluster.foobar", "name", rName),
resource.TestCheckResourceAttr("digitalocean_kubernetes_cluster.foobar", "control_plane_firewall.0.enabled", "false"),
resource.TestCheckResourceAttr("digitalocean_kubernetes_cluster.foobar", "control_plane_firewall.0.allowed_addresses.0", "1.2.3.4/16"),
resource.TestCheckResourceAttr("digitalocean_kubernetes_cluster.foobar", "control_plane_firewall.0.allowed_addresses.1", "5.6.7.8/16"),
),
},
},
})
}

func TestAccDigitalOceanKubernetesCluster_UpdatePoolDetails(t *testing.T) {
rName := acceptance.RandomTestName()
var k8s godo.KubernetesCluster
Expand Down Expand Up @@ -840,6 +886,36 @@ resource "digitalocean_kubernetes_cluster" "foobar" {
`, testClusterVersion, rName, policy)
}

func testAccDigitalOceanKubernetesConfigControlPlaneFirewall(testClusterVersion string, rName string, controlPlaneFirewall string) string {
return fmt.Sprintf(`%s

resource "digitalocean_kubernetes_cluster" "foobar" {
name = "%s"
region = "lon1"
version = data.digitalocean_kubernetes_versions.test.latest_version
surge_upgrade = true
tags = ["foo", "bar", "one"]

%s

node_pool {
name = "default"
size = "s-1vcpu-2gb"
node_count = 1
tags = ["one", "two"]
labels = {
priority = "high"
}
taint {
key = "key1"
value = "val1"
effect = "PreferNoSchedule"
}
}
}
`, testClusterVersion, rName, controlPlaneFirewall)
}

func testAccDigitalOceanKubernetesConfigBasic2(testClusterVersion string, rName string) string {
return fmt.Sprintf(`%s

Expand Down