From 9830dcd888bc57c533981fcc902cae78b22f3edb Mon Sep 17 00:00:00 2001 From: Tjev Date: Tue, 31 May 2022 15:29:32 +0300 Subject: [PATCH] Add servergroup policy and rules field support --- ...source_openstack_compute_servergroup_v2.go | 98 +++++++++- ...e_openstack_compute_servergroup_v2_test.go | 183 ++++++++++++++++++ .../r/compute_servergroup_v2.html.markdown | 28 ++- 3 files changed, 300 insertions(+), 9 deletions(-) diff --git a/openstack/resource_openstack_compute_servergroup_v2.go b/openstack/resource_openstack_compute_servergroup_v2.go index b80b3cf18..85afb8fab 100644 --- a/openstack/resource_openstack_compute_servergroup_v2.go +++ b/openstack/resource_openstack_compute_servergroup_v2.go @@ -6,6 +6,7 @@ import ( "github.com/hashicorp/terraform-plugin-sdk/v2/diag" "github.com/hashicorp/terraform-plugin-sdk/v2/helper/schema" + "github.com/hashicorp/terraform-plugin-sdk/v2/helper/validation" "github.com/gophercloud/gophercloud/openstack/compute/v2/extensions/servergroups" ) @@ -41,6 +42,29 @@ func resourceComputeServerGroupV2() *schema.Resource { Elem: &schema.Schema{Type: schema.TypeString}, }, + "policy": { + Type: schema.TypeString, + Optional: true, + ForceNew: true, + }, + + "rules": { + Type: schema.TypeList, + Optional: true, + ForceNew: true, + MaxItems: 1, + Elem: &schema.Resource{ + Schema: map[string]*schema.Schema{ + "max_server_per_host": { + Type: schema.TypeInt, + Optional: true, + ForceNew: true, + ValidateFunc: validation.IntAtLeast(1), + }, + }, + }, + }, + "members": { Type: schema.TypeList, Computed: true, @@ -68,12 +92,57 @@ func resourceComputeServerGroupV2Create(ctx context.Context, d *schema.ResourceD rawPolicies := d.Get("policies").([]interface{}) policies := expandComputeServerGroupV2Policies(computeClient, rawPolicies) - createOpts := ComputeServerGroupV2CreateOpts{ - servergroups.CreateOpts{ - Name: name, - Policies: policies, - }, - MapValueSpecs(d), + policy := d.Get("policy").(string) + rules_v, rules_set := d.GetOk("rules") + + var createOpts ComputeServerGroupV2CreateOpts + + // "policies" is replaced with "policy" and optional "rules" since microversion 2.64 + if len(policies) > 0 { + if policy != "" { + return diag.Errorf("Cannot create with both \"policies\" and \"policy\" field specified") + } + if rules_set { + return diag.Errorf("Cannot use \"policies\" field with \"rules\"" + + " - omit the \"rules\" or use \"policy\" instead") + } + createOpts = ComputeServerGroupV2CreateOpts{ + servergroups.CreateOpts{ + Name: name, + Policies: policies, + }, + MapValueSpecs(d), + } + } else { + computeClient.Microversion = "2.64" + + if policy == "anti-affinity" && rules_set { + rules := rules_v.([]map[string]interface{}) + + var max_server_per_host int + if v, ok := rules[0]["max_server_per_host"]; ok { + max_server_per_host = v.(int) + } + + createOpts = ComputeServerGroupV2CreateOpts{ + servergroups.CreateOpts{ + Name: name, + Policy: policy, + Rules: &servergroups.Rules{ + MaxServerPerHost: max_server_per_host, + }, + }, + MapValueSpecs(d), + } + } else { + createOpts = ComputeServerGroupV2CreateOpts{ + servergroups.CreateOpts{ + Name: name, + Policy: policy, + }, + MapValueSpecs(d), + } + } } log.Printf("[DEBUG] openstack_compute_servergroup_v2 create options: %#v", createOpts) @@ -102,11 +171,26 @@ func resourceComputeServerGroupV2Read(_ context.Context, d *schema.ResourceData, log.Printf("[DEBUG] Retrieved openstack_compute_servergroup_v2 %s: %#v", d.Id(), sg) d.Set("name", sg.Name) - d.Set("policies", sg.Policies) + + if len(sg.Policies) > 0 { + d.Set("policy", sg.Policies) + } + d.Set("members", sg.Members) d.Set("region", GetRegion(d, config)) + if sg.Policy != nil { + d.Set("policy", sg.Policy) + } + + if sg.Rules != nil { + rules := make(map[string]interface{}) + rules["max_server_per_host"] = sg.Rules.MaxServerPerHost + rules_l := []map[string]interface{}{rules} + d.Set("rules", rules_l) + } + return nil } diff --git a/openstack/resource_openstack_compute_servergroup_v2_test.go b/openstack/resource_openstack_compute_servergroup_v2_test.go index 9e6f09c8b..1a3b7e42e 100644 --- a/openstack/resource_openstack_compute_servergroup_v2_test.go +++ b/openstack/resource_openstack_compute_servergroup_v2_test.go @@ -2,6 +2,7 @@ package openstack import ( "fmt" + "regexp" "testing" "github.com/hashicorp/terraform-plugin-sdk/v2/helper/resource" @@ -36,6 +37,112 @@ func TestAccComputeV2ServerGroup_basic(t *testing.T) { }) } +func TestAccComputeV2ServerGroup_basic_v2_64(t *testing.T) { + var sg servergroups.ServerGroup + + resource.Test(t, resource.TestCase{ + PreCheck: func() { + testAccPreCheck(t) + testAccPreCheckNonAdminOnly(t) + }, + ProviderFactories: testAccProviders, + CheckDestroy: testAccCheckComputeV2ServerGroupDestroy, + Steps: []resource.TestStep{ + { + Config: testAccComputeV2ServerGroupV264Policy, + Check: resource.ComposeTestCheckFunc( + testAccCheckComputeV2ServerGroupExists("openstack_compute_servergroup_v2.sg_1", &sg), + resource.TestCheckResourceAttr( + "openstack_compute_servergroup_v2.sg_1", "policy", "affinity"), + ), + }, + }, + }) +} + +func TestAccComputeV2ServerGroup_v2_64_anti_affinity(t *testing.T) { + var sg servergroups.ServerGroup + + resource.Test(t, resource.TestCase{ + PreCheck: func() { + testAccPreCheck(t) + testAccPreCheckNonAdminOnly(t) + }, + ProviderFactories: testAccProviders, + CheckDestroy: testAccCheckComputeV2ServerGroupDestroy, + Steps: []resource.TestStep{ + { + Config: testAccComputeV2ServerGroupV264PolicyAntiAffinity, + Check: resource.ComposeTestCheckFunc( + testAccCheckComputeV2ServerGroupExists("openstack_compute_servergroup_v2.sg_1", &sg), + resource.TestCheckResourceAttr( + "openstack_compute_servergroup_v2.sg_1", "policy", "anti-affinity"), + ), + }, + }, + }) +} + +func TestAccComputeV2ServerGroup_v2_64_with_rules(t *testing.T) { + var sg servergroups.ServerGroup + + resource.Test(t, resource.TestCase{ + PreCheck: func() { + testAccPreCheck(t) + testAccPreCheckNonAdminOnly(t) + }, + ProviderFactories: testAccProviders, + CheckDestroy: testAccCheckComputeV2ServerGroupDestroy, + Steps: []resource.TestStep{ + { + Config: testAccComputeV2ServerGroupV264PolicyRules, + Check: resource.ComposeTestCheckFunc( + testAccCheckComputeV2ServerGroupExists("openstack_compute_servergroup_v2.sg_1", &sg), + resource.TestCheckResourceAttr( + "openstack_compute_servergroup_v2.sg_1", "policy", "anti-affinity"), + resource.TestCheckResourceAttr( + "openstack_compute_servergroup_v2.sg_1", "rules.max_server_per_host", "2"), + ), + }, + }, + }) +} + +func TestAccComputeV2ServerGroup_v2_64_with_invalid_rules(t *testing.T) { + resource.Test(t, resource.TestCase{ + PreCheck: func() { + testAccPreCheck(t) + testAccPreCheckNonAdminOnly(t) + }, + ProviderFactories: testAccProviders, + CheckDestroy: testAccCheckComputeV2ServerGroupDestroy, + Steps: []resource.TestStep{ + { + Config: testAccComputeV2ServerGroupV264PolicyRules, + ExpectError: regexp.MustCompile(`expected max_server_per_host to be at least 1, got .*`), + }, + }, + }) +} + +func TestAccComputeV2ServerGroup_policies_and_policy(t *testing.T) { + resource.Test(t, resource.TestCase{ + PreCheck: func() { + testAccPreCheck(t) + testAccPreCheckNonAdminOnly(t) + }, + ProviderFactories: testAccProviders, + CheckDestroy: testAccCheckComputeV2ServerGroupDestroy, + Steps: []resource.TestStep{ + { + Config: testAccComputeV2ServerGroupV264Policy, + ExpectError: regexp.MustCompile( + "Cannot create with both \"policies\" and \"policy\" field specified"), + }, + }, + }) +} + func TestAccComputeV2ServerGroup_affinity(t *testing.T) { var instance servers.Server var sg servergroups.ServerGroup @@ -64,6 +171,32 @@ func TestAccComputeV2ServerGroup_affinity(t *testing.T) { }) } +func TestAccComputeV2ServerGroup_affinity_v2_64(t *testing.T) { + var instance servers.Server + var sg servergroups.ServerGroup + + resource.Test(t, resource.TestCase{ + PreCheck: func() { + testAccPreCheck(t) + testAccPreCheckNonAdminOnly(t) + }, + ProviderFactories: testAccProviders, + CheckDestroy: testAccCheckComputeV2ServerGroupDestroy, + Steps: []resource.TestStep{ + { + Config: testAccComputeV2ServerGroupAffinityV264(), + Check: resource.ComposeTestCheckFunc( + testAccCheckComputeV2ServerGroupExists("openstack_compute_servergroup_v2.sg_1", &sg), + testAccCheckComputeV2InstanceExists("openstack_compute_instance_v2.instance_1", &instance), + testAccCheckComputeV2InstanceInServerGroup(&instance, &sg), + resource.TestCheckResourceAttr( + "openstack_compute_servergroup_v2.sg_1", "policy", "affinity"), + ), + }, + }, + }) +} + func TestAccComputeV2ServerGroup_soft_affinity(t *testing.T) { var instance servers.Server var sg servergroups.ServerGroup @@ -166,6 +299,36 @@ resource "openstack_compute_servergroup_v2" "sg_1" { } ` +const testAccComputeV2ServerGroupV264Policy = ` +resource "openstack_compute_servergroup_v2" "sg_1" { + name = "sg_1" + policy = "affinity" +` + +const testAccComputeV2ServerGroupV264PolicyAntiAffinity = ` +resource "openstack_compute_servergroup_v2" "sg_1" { + name = "sg_1" + policy = "anti-affinity" +` + +const testAccComputeV2ServerGroupV264PolicyRules = ` +resource "openstack_compute_servergroup_v2" "sg_1" { + name = "sg_1" + policy = "anti-affinity" + rules = { + max_server_per_host = 2 + } +` + +const testAccComputeV2ServerGroupV264InvalidPolicyRules = ` +resource "openstack_compute_servergroup_v2" "sg_1" { + name = "sg_1" + policy = "anti-affinity" + rules = { + max_server_per_host = -1 + } +` + func testAccComputeV2ServerGroupAffinity() string { return fmt.Sprintf(` resource "openstack_compute_servergroup_v2" "sg_1" { @@ -205,3 +368,23 @@ resource "openstack_compute_instance_v2" "instance_1" { } `, osNetworkID) } + +func testAccComputeV2ServerGroupAffinityV264() string { + return fmt.Sprintf(` +resource "openstack_compute_servergroup_v2" "sg_1" { + name = "sg_1" + policy = "affinity" +} + +resource "openstack_compute_instance_v2" "instance_1" { + name = "instance_1" + security_groups = ["default"] + scheduler_hints { + group = "${openstack_compute_servergroup_v2.sg_1.id}" + } + network { + uuid = "%s" + } +} +`, osNetworkID) +} diff --git a/website/docs/r/compute_servergroup_v2.html.markdown b/website/docs/r/compute_servergroup_v2.html.markdown index 3871dac60..a726cecb0 100644 --- a/website/docs/r/compute_servergroup_v2.html.markdown +++ b/website/docs/r/compute_servergroup_v2.html.markdown @@ -12,6 +12,8 @@ Manages a V2 Server Group resource within OpenStack. ## Example Usage +### Compute service API version 2.63 or below: + ```hcl resource "openstack_compute_servergroup_v2" "test-sg" { name = "my-sg" @@ -19,6 +21,18 @@ resource "openstack_compute_servergroup_v2" "test-sg" { } ``` +### Compute service API version 2.64 or above: + +```hcl +resource "openstack_compute_servergroup_v2" "test-sg" { + name = "my-sg" + policy = "anti-affinity" + rules { + max_server_per_host = 3 + } +} +``` + ## Argument Reference The following arguments are supported: @@ -30,12 +44,20 @@ The following arguments are supported: * `name` - (Required) A unique name for the server group. Changing this creates a new server group. -* `policies` - (Required) The set of policies for the server group. All policies +* `policies` - (Optional) The set of policies for the server group. All policies are mutually exclusive. See the Policies section for more information. - Changing this creates a new server group. + Changing this creates a new server group. This field should only be used + with Compute service API 2.63 or below. * `value_specs` - (Optional) Map of additional options. +* `policy` - (Optional) The policy to be applied to the server group. This argument + replaces the `policies` argument (the `policies` and `policy` fields are mutually + exclusive), and should only be used with Compute service API 2.64 or above. + +* `rules` - (Optional) The rules which are applied to specified `policy`. Currently, + only the `max_server_per_host` rule is supported for the `anti-affinity` policy. + ## Policies * `affinity` - All instances/servers launched in this group will be hosted on @@ -62,6 +84,8 @@ The following attributes are exported: * `name` - See Argument Reference above. * `policies` - See Argument Reference above. * `members` - The instances that are part of this server group. +* `policy` - See Argument Reference above. +* `rules` - See Argument Reference above. ## Import