Skip to content

Commit

Permalink
GCLOUD2-13658 Returned security groups (#123)
Browse files Browse the repository at this point in the history
  • Loading branch information
volvofixthis authored Sep 5, 2024
1 parent c165a61 commit 9a1806b
Showing 1 changed file with 264 additions and 1 deletion.
265 changes: 264 additions & 1 deletion gcore/resource_gcore_k8sv2.go
Original file line number Diff line number Diff line change
Expand Up @@ -12,9 +12,12 @@ import (
gcorecloud "github.com/G-Core/gcorelabscloud-go"
"github.com/G-Core/gcorelabscloud-go/gcore/k8s/v2/clusters"
"github.com/G-Core/gcorelabscloud-go/gcore/k8s/v2/pools"
"github.com/G-Core/gcorelabscloud-go/gcore/securitygroup/v1/securitygroups"
"github.com/G-Core/gcorelabscloud-go/gcore/securitygroup/v1/types"
"github.com/G-Core/gcorelabscloud-go/gcore/servergroup/v1/servergroups"
"github.com/G-Core/gcorelabscloud-go/gcore/task/v1/tasks"
"github.com/G-Core/gcorelabscloud-go/gcore/volume/v1/volumes"
"github.com/hashicorp/go-cty/cty"
"github.com/hashicorp/terraform-plugin-sdk/v2/diag"
"github.com/hashicorp/terraform-plugin-sdk/v2/helper/customdiff"
"github.com/hashicorp/terraform-plugin-sdk/v2/helper/schema"
Expand All @@ -24,6 +27,8 @@ const (
K8sPoint = "k8s/clusters"
tasksPoint = "tasks"
K8sCreateTimeout = 3600

k8sSgMetadataKey = "gcloud_cluster_name"
)

var k8sCreateTimeout = time.Second * time.Duration(K8sCreateTimeout)
Expand Down Expand Up @@ -435,6 +440,138 @@ func resourceK8sV2() *schema.Resource {
},
},
},
"security_group_rules_internal": {
Type: schema.TypeList,
Computed: true,
Description: "Default readonly security group rules. It is used for internal purposes only.",
Elem: &schema.Resource{
Schema: map[string]*schema.Schema{
"id": {
Type: schema.TypeString,
Computed: true,
},
"direction": {
Type: schema.TypeString,
Computed: true,
Description: fmt.Sprintf("Available value is '%s', '%s'", types.RuleDirectionIngress, types.RuleDirectionEgress),
},
"ethertype": {
Type: schema.TypeString,
Computed: true,
Description: fmt.Sprintf("Available value is '%s', '%s'", types.EtherTypeIPv4, types.EtherTypeIPv6),
},
"protocol": {
Type: schema.TypeString,
Computed: true,
Description: fmt.Sprintf("Available value is %s", strings.Join(types.Protocol("").StringList(), ",")),
},
"port_range_min": {
Type: schema.TypeInt,
Computed: true,
},
"port_range_max": {
Type: schema.TypeInt,
Computed: true,
},
"description": {
Type: schema.TypeString,
Computed: true,
},
"remote_ip_prefix": {
Type: schema.TypeString,
Computed: true,
},
"updated_at": {
Type: schema.TypeString,
Computed: true,
},
"created_at": {
Type: schema.TypeString,
Computed: true,
},
},
},
},
"security_group_rules": {
Type: schema.TypeSet,
Optional: true,
Description: "Firewall rules control what inbound(ingress) and outbound(egress) traffic is allowed to enter or leave a Instance. At least one 'egress' rule should be set",
Set: secGroupUniqueID,
Elem: &schema.Resource{
Schema: map[string]*schema.Schema{
"id": {
Type: schema.TypeString,
Computed: true,
},
"direction": {
Type: schema.TypeString,
Required: true,
Description: fmt.Sprintf("Available value is '%s', '%s'", types.RuleDirectionIngress, types.RuleDirectionEgress),
ValidateDiagFunc: func(v interface{}, path cty.Path) diag.Diagnostics {
val := v.(string)
switch types.RuleDirection(val) {
case types.RuleDirectionIngress, types.RuleDirectionEgress:
return nil
}
return diag.Errorf("wrong direction '%s', available value is '%s', '%s'", val, types.RuleDirectionIngress, types.RuleDirectionEgress)
},
},
"ethertype": {
Type: schema.TypeString,
Required: true,
Description: fmt.Sprintf("Available value is '%s', '%s'", types.EtherTypeIPv4, types.EtherTypeIPv6),
ValidateDiagFunc: func(v interface{}, path cty.Path) diag.Diagnostics {
val := v.(string)
switch types.EtherType(val) {
case types.EtherTypeIPv4, types.EtherTypeIPv6:
return nil
}
return diag.Errorf("wrong ethertype '%s', available value is '%s', '%s'", val, types.EtherTypeIPv4, types.EtherTypeIPv6)
},
},
"protocol": {
Type: schema.TypeString,
Required: true,
Description: fmt.Sprintf("Available value is %s", strings.Join(types.Protocol("").StringList(), ",")),
},
"port_range_min": {
Type: schema.TypeInt,
Optional: true,
Default: 0,
ValidateDiagFunc: validatePortRange,
},
"port_range_max": {
Type: schema.TypeInt,
Optional: true,
Default: 0,
ValidateDiagFunc: validatePortRange,
},
"description": {
Type: schema.TypeString,
Optional: true,
Default: "",
},
"remote_ip_prefix": {
Type: schema.TypeString,
Optional: true,
Default: "",
},
"updated_at": {
Type: schema.TypeString,
Computed: true,
},
"created_at": {
Type: schema.TypeString,
Computed: true,
},
},
},
},
"security_group_id": {
Type: schema.TypeString,
Computed: true,
Description: "Security group ID.",
},
"status": {
Type: schema.TypeString,
Description: "Cluster status.",
Expand Down Expand Up @@ -657,12 +794,51 @@ func resourceK8sV2Create(ctx context.Context, d *schema.ResourceData, m interfac
}

d.SetId(clusterName.(string))
resourceK8sV2Read(ctx, d, m)

sgClient, err := CreateClient(provider, d, securityGroupPoint, versionPointV1)
if err != nil {
return diag.FromErr(err)
}

sgs, err := securitygroups.ListAll(
sgClient, securitygroups.ListOpts{MetadataKV: map[string]string{k8sSgMetadataKey: clusterName.(string)}},
)
sg := getSuitableSecurityGroup(sgs, clusterName.(string), d.Get("project_id").(int), d.Get("region_id").(int))
if len(d.Get("security_group_rules_internal").([]interface{})) == 0 {
rules := convertSecurityGroupRules(sg.SecurityGroupRules)
if err := d.Set("security_group_rules_internal", rules); err != nil {
return diag.FromErr(err)
}
}
if sg != nil {
rawRules := d.Get("security_group_rules").(*schema.Set).List()
if len(rawRules) != 0 {
usersRules := convertToSecurityGroupRules(rawRules)

for _, rule := range usersRules {
_, err = securitygroups.AddRule(sgClient, sg.ID, rule).Extract()
if err != nil {
log.Println("[ERROR] Cannot add rule to security group", err)
}
}
}
}

resourceK8sV2Read(ctx, d, m)
log.Printf("[DEBUG] Finish k8s cluster creating (%s)", clusterName)
return diags
}

func getSuitableSecurityGroup(sgs []securitygroups.SecurityGroup, name string, projectID, regionID int) *securitygroups.SecurityGroup {
sgName := fmt.Sprintf("%s-%d-%d-worker", name, regionID, projectID)
for _, sg := range sgs {
if sg.Name == sgName {
return &sg
}
}
return nil
}

func resourceK8sV2Read(ctx context.Context, d *schema.ResourceData, m interface{}) diag.Diagnostics {
log.Println("[DEBUG] Start k8s cluster reading")
var diags diag.Diagnostics
Expand All @@ -677,6 +853,12 @@ func resourceK8sV2Read(ctx context.Context, d *schema.ResourceData, m interface{
clusterName := d.Get("name").(string)
cluster, err := clusters.Get(client, clusterName).Extract()
if err != nil {
switch err.(type) {
case gcorecloud.ErrDefault404:
d.SetId("")
log.Printf("[WARNING] k8s cluster not found, removing from state")
return nil
}
return diag.FromErr(err)
}

Expand Down Expand Up @@ -773,6 +955,36 @@ func resourceK8sV2Read(ctx context.Context, d *schema.ResourceData, m interface{
return diag.FromErr(err)
}

// get cluster's security group
sgClient, err := CreateClient(provider, d, securityGroupPoint, versionPointV1)
if err != nil {
return diag.FromErr(err)
}

sgs, err := securitygroups.ListAll(
sgClient, securitygroups.ListOpts{MetadataKV: map[string]string{k8sSgMetadataKey: clusterName}},
)
sg := getSuitableSecurityGroup(sgs, clusterName, d.Get("project_id").(int), d.Get("region_id").(int))
if err == nil && sg != nil {
if len(d.Get("security_group_rules_internal").([]interface{})) == 0 {
rules := convertSecurityGroupRules(sg.SecurityGroupRules)
if err := d.Set("security_group_rules_internal", rules); err != nil {
return diag.FromErr(err)
}
}

d.Set("security_group_id", sg.ID)
// todo read security group for the cluster
rulesRaw := convertSecurityGroupRules(sg.SecurityGroupRules)
actualRules := schema.NewSet(secGroupUniqueID, rulesRaw)
readOnlyRules := schema.NewSet(secGroupUniqueID, d.Get("security_group_rules_internal").([]interface{}))
resultRules := actualRules.Difference(readOnlyRules)

if err := d.Set("security_group_rules", resultRules); err != nil {
return diag.FromErr(err)
}
}

log.Println("[DEBUG] Finish k8s cluster reading")
return diags
}
Expand Down Expand Up @@ -859,6 +1071,57 @@ func resourceK8sV2Update(ctx context.Context, d *schema.ResourceData, m interfac
}
}

if d.HasChange("security_group_rules") {
o, n := d.GetChange("security_group_rules")
newUsersRules := n.(*schema.Set)
oldUsersRules := o.(*schema.Set)

sgClient, err := CreateClient(provider, d, securityGroupPoint, versionPointV1)
if err != nil {
return diag.FromErr(err)
}

updateRequest := securitygroups.UpdateOpts{}
for _, rule := range oldUsersRules.List() {
r := rule.(map[string]interface{})
updateRequest.ChangedRules = append(updateRequest.ChangedRules, securitygroups.UpdateSecurityGroupRuleOpts{
Action: types.ActionDelete,
SecurityGroupRuleID: r["id"].(string),
})
}

for _, rule := range newUsersRules.List() {
r := rule.(map[string]interface{})
changedRules := securitygroups.UpdateSecurityGroupRuleOpts{
Action: types.ActionCreate,
Direction: types.RuleDirection(r["direction"].(string)),
EtherType: types.EtherType(r["ethertype"].(string)),
Protocol: types.Protocol(r["protocol"].(string)),
}

if port := r["port_range_max"].(int); port != 0 {
changedRules.PortRangeMax = &port
}
if port := r["port_range_min"].(int); port != 0 {
changedRules.PortRangeMin = &port
}
if descr := r["description"].(string); descr != "" {
changedRules.Description = &descr
}
if remoteIPPrefix := r["remote_ip_prefix"].(string); remoteIPPrefix != "" {
changedRules.RemoteIPPrefix = &remoteIPPrefix
}

updateRequest.ChangedRules = append(updateRequest.ChangedRules, changedRules)
}

_, err = securitygroups.Update(sgClient, d.Get("security_group_id").(string), updateRequest).Extract()
if err != nil {
return diag.FromErr(err)
}

}

diags := resourceK8sV2Read(ctx, d, m)
log.Printf("[DEBUG] Finish k8s cluster updating (%s)", clusterName)
return diags
Expand Down

0 comments on commit 9a1806b

Please sign in to comment.