diff --git a/client/v3/v3_structs.go b/client/v3/v3_structs.go index a0f452cd9..ca66de343 100644 --- a/client/v3/v3_structs.go +++ b/client/v3/v3_structs.go @@ -2044,9 +2044,9 @@ type LeftHandSide struct { // RightHandSide ... type RightHandSide struct { - Collection *string `json:"collection,omitempty"` - Categories map[string]string `json:"categories,omitempty"` - UUIDList []string `json:"uuid_list,omitempty"` + Collection *string `json:"collection,omitempty"` + Categories map[string][]string `json:"categories,omitempty"` + UUIDList []string `json:"uuid_list,omitempty"` } // AccessControlPolicyStatus ... diff --git a/nutanix/data_source_nutanix_access_control_policies.go b/nutanix/data_source_nutanix_access_control_policies.go index 47b377b97..a2202c0f4 100644 --- a/nutanix/data_source_nutanix_access_control_policies.go +++ b/nutanix/data_source_nutanix_access_control_policies.go @@ -206,7 +206,24 @@ func dataSourceNutanixAccessControlPolicies() *schema.Resource { Type: schema.TypeString, Computed: true, }, - "categories": categoriesSchema(), + "categories": { + Type: schema.TypeList, + MaxItems: 1, + Computed: true, + Elem: &schema.Resource{ + Schema: map[string]*schema.Schema{ + "name": { + Type: schema.TypeString, + Computed: true, + }, + "value": { + Type: schema.TypeList, + Computed: true, + Elem: &schema.Schema{Type: schema.TypeString}, + }, + }, + }, + }, "uuid_list": { Type: schema.TypeSet, Computed: true, @@ -241,7 +258,24 @@ func dataSourceNutanixAccessControlPolicies() *schema.Resource { Type: schema.TypeString, Computed: true, }, - "categories": categoriesSchema(), + "categories": { + Type: schema.TypeList, + MaxItems: 1, + Computed: true, + Elem: &schema.Resource{ + Schema: map[string]*schema.Schema{ + "name": { + Type: schema.TypeString, + Computed: true, + }, + "value": { + Type: schema.TypeList, + Computed: true, + Elem: &schema.Schema{Type: schema.TypeString}, + }, + }, + }, + }, "uuid_list": { Type: schema.TypeSet, Computed: true, diff --git a/nutanix/data_source_nutanix_access_control_policy.go b/nutanix/data_source_nutanix_access_control_policy.go index f17703b0f..ec2138186 100644 --- a/nutanix/data_source_nutanix_access_control_policy.go +++ b/nutanix/data_source_nutanix_access_control_policy.go @@ -13,8 +13,14 @@ func dataSourceNutanixAccessControlPolicy() *schema.Resource { Read: dataSourceNutanixAccessControlPolicyRead, Schema: map[string]*schema.Schema{ "access_control_policy_id": { - Type: schema.TypeString, - Required: true, + Type: schema.TypeString, + Optional: true, + ConflictsWith: []string{"access_control_policy_name"}, + }, + "access_control_policy_name": { + Type: schema.TypeString, + Optional: true, + ConflictsWith: []string{"access_control_policy_id"}, }, "api_version": { Type: schema.TypeString, @@ -198,7 +204,24 @@ func dataSourceNutanixAccessControlPolicy() *schema.Resource { Type: schema.TypeString, Computed: true, }, - "categories": categoriesSchema(), + "categories": { + Type: schema.TypeList, + MaxItems: 1, + Computed: true, + Elem: &schema.Resource{ + Schema: map[string]*schema.Schema{ + "name": { + Type: schema.TypeString, + Computed: true, + }, + "value": { + Type: schema.TypeList, + Computed: true, + Elem: &schema.Schema{Type: schema.TypeString}, + }, + }, + }, + }, "uuid_list": { Type: schema.TypeSet, Computed: true, @@ -233,7 +256,24 @@ func dataSourceNutanixAccessControlPolicy() *schema.Resource { Type: schema.TypeString, Computed: true, }, - "categories": categoriesSchema(), + "categories": { + Type: schema.TypeList, + MaxItems: 1, + Computed: true, + Elem: &schema.Resource{ + Schema: map[string]*schema.Schema{ + "name": { + Type: schema.TypeString, + Computed: true, + }, + "value": { + Type: schema.TypeList, + Computed: true, + Elem: &schema.Schema{Type: schema.TypeString}, + }, + }, + }, + }, "uuid_list": { Type: schema.TypeSet, Computed: true, @@ -256,16 +296,22 @@ func dataSourceNutanixAccessControlPolicyRead(d *schema.ResourceData, meta inter // Get client connection conn := meta.(*Client).API - accessID, iok := d.GetOk("access_control_policy_id") + id, iok := d.GetOk("access_control_policy_id") + name, nOk := d.GetOk("access_control_policy_name") - if !iok { - return fmt.Errorf("please provide `access_control_policy_id`") + if !iok && !nOk { + return fmt.Errorf("please provide `access_control_policy_id` or `access_control_policy_name`") } var reqErr error var resp *v3.AccessControlPolicy - resp, reqErr = conn.V3.GetAccessControlPolicy(accessID.(string)) + if iok { + resp, reqErr = conn.V3.GetAccessControlPolicy(id.(string)) + } + if nOk { + resp, reqErr = findACPByName(conn, name.(string)) + } if reqErr != nil { return reqErr @@ -321,3 +367,30 @@ func dataSourceNutanixAccessControlPolicyRead(d *schema.ResourceData, meta inter return nil } + +func findACPByName(conn *v3.Client, name string) (*v3.AccessControlPolicy, error) { + filter := fmt.Sprintf("name==%s", name) + resp, err := conn.V3.ListAllAccessControlPolicy(filter) + if err != nil { + return nil, err + } + + entities := resp.Entities + + found := make([]*v3.AccessControlPolicy, 0) + for _, v := range entities { + if *v.Spec.Name == name { + found = append(found, v) + } + } + + if len(found) > 1 { + return nil, fmt.Errorf("your query returned more than one result. Please use access_control_policy_id argument instead") + } + + if len(found) == 0 { + return nil, fmt.Errorf("access control policy with the given name, not found") + } + + return found[0], nil +} diff --git a/nutanix/data_source_nutanix_access_control_policy_test.go b/nutanix/data_source_nutanix_access_control_policy_test.go index 28cd4cdde..c1a34b396 100644 --- a/nutanix/data_source_nutanix_access_control_policy_test.go +++ b/nutanix/data_source_nutanix_access_control_policy_test.go @@ -9,7 +9,7 @@ import ( "github.com/hashicorp/terraform-plugin-sdk/helper/resource" ) -func TestAccNutanixAccessControlPolicyDataSource_basic(t *testing.T) { +func TestAccNutanixAccessControlPolicyDataSourceByID_basic(t *testing.T) { name := acctest.RandomWithPrefix("accest-access-policy") description := "Description of my access control policy" @@ -18,7 +18,7 @@ func TestAccNutanixAccessControlPolicyDataSource_basic(t *testing.T) { Providers: testAccProviders, Steps: []resource.TestStep{ { - Config: testAccAccessControlPolicyDataSourceConfig(name, description), + Config: testAccAccessControlPolicyDataSourceByIDConfig(name, description), Check: resource.ComposeTestCheckFunc( resource.TestCheckResourceAttr( "data.nutanix_access_control_policy.test", "name", name), @@ -31,7 +31,29 @@ func TestAccNutanixAccessControlPolicyDataSource_basic(t *testing.T) { }) } -func testAccAccessControlPolicyDataSourceConfig(name, description string) string { +func TestAccNutanixAccessControlPolicyDataSourceByName_basic(t *testing.T) { + name := acctest.RandomWithPrefix("accest-access-policy") + description := "Description of my access control policy" + + resource.Test(t, resource.TestCase{ + PreCheck: func() { testAccPreCheck(t) }, + Providers: testAccProviders, + Steps: []resource.TestStep{ + { + Config: testAccAccessControlPolicyDataSourceByNameConfig(name, description), + Check: resource.ComposeTestCheckFunc( + resource.TestCheckResourceAttr( + "data.nutanix_access_control_policy.test", "name", name), + resource.TestCheckResourceAttr( + "data.nutanix_access_control_policy.test", "description", description), + resource.TestCheckResourceAttrSet("data.nutanix_access_control_policy.test", "name"), + ), + }, + }, + }) +} + +func testAccAccessControlPolicyDataSourceByIDConfig(name, description string) string { return fmt.Sprintf(` resource "nutanix_role" "test" { name = "test role" @@ -55,3 +77,28 @@ data "nutanix_access_control_policy" "test" { } `, name, description) } + +func testAccAccessControlPolicyDataSourceByNameConfig(name, description string) string { + return fmt.Sprintf(` +resource "nutanix_role" "test" { + name = "test role 2" + description = "description role" + permission_reference_list { + kind = "permission" + uuid = "2e9988df-47ae-44ae-9114-ada346657b90" + } +} +resource "nutanix_access_control_policy" "test" { + name = "%[1]s" + description = "%[2]s" + role_reference{ + kind = "role" + uuid = nutanix_role.test.id + } +} + +data "nutanix_access_control_policy" "test" { + access_control_policy_name = nutanix_access_control_policy.test.name +} +`, name, description) +} diff --git a/nutanix/resource_nutanix_access_control_policy.go b/nutanix/resource_nutanix_access_control_policy.go index 5916432f1..2cc3eafb0 100644 --- a/nutanix/resource_nutanix_access_control_policy.go +++ b/nutanix/resource_nutanix_access_control_policy.go @@ -207,7 +207,27 @@ func resourceNutanixAccessControlPolicy() *schema.Resource { Computed: true, ValidateFunc: validation.StringInSlice([]string{"ALL"}, false), }, - "categories": categoriesSchema(), + "categories": { + Type: schema.TypeList, + MaxItems: 1, + Optional: true, + Computed: true, + Elem: &schema.Resource{ + Schema: map[string]*schema.Schema{ + "name": { + Type: schema.TypeString, + Optional: true, + Computed: true, + }, + "value": { + Type: schema.TypeSet, + Optional: true, + Computed: true, + Elem: &schema.Schema{Type: schema.TypeString}, + }, + }, + }, + }, "uuid_list": { Type: schema.TypeSet, Optional: true, @@ -248,7 +268,27 @@ func resourceNutanixAccessControlPolicy() *schema.Resource { Computed: true, ValidateFunc: validation.StringInSlice([]string{"ALL", "SELF_OWNED"}, false), }, - "categories": categoriesSchema(), + "categories": { + Type: schema.TypeList, + MaxItems: 1, + Optional: true, + Computed: true, + Elem: &schema.Resource{ + Schema: map[string]*schema.Schema{ + "name": { + Type: schema.TypeString, + Optional: true, + Computed: true, + }, + "value": { + Type: schema.TypeSet, + Optional: true, + Computed: true, + Elem: &schema.Schema{Type: schema.TypeString}, + }, + }, + }, + }, "uuid_list": { Type: schema.TypeSet, Optional: true, @@ -630,7 +670,7 @@ func expandRightHandSide(side map[string]interface{}) v3.RightHandSide { } } if v5, ok := rhd["categories"]; ok { - right.Categories = expandCategories(v5) + right.Categories = expandRightHandsideCategories(v5.([]interface{})) } if v5, ok := rhd["uuid_list"]; ok { right.UUIDList = cast.ToStringSlice(v5.(*schema.Set).List()) @@ -691,9 +731,33 @@ func flattenRightHandSide(right v3.RightHandSide) []interface{} { r := make(map[string]interface{}) r["collection"] = utils.StringValue(right.Collection) r["uuid_list"] = right.UUIDList - r["categories"] = flattenCategories(right.Categories) + r["categories"] = flattenTightHandsideCategories(right.Categories) rightHand = append(rightHand, r) return rightHand } + +func expandRightHandsideCategories(categoriesSet []interface{}) map[string][]string { + output := make(map[string][]string) + + for _, v := range categoriesSet { + category := v.(map[string]interface{}) + output[category["name"].(string)] = cast.ToStringSlice(category["value"].(*schema.Set).List()) + } + + return output +} + +func flattenTightHandsideCategories(categories map[string][]string) []interface{} { + c := make([]interface{}, 0) + + for name, value := range categories { + c = append(c, map[string]interface{}{ + "name": name, + "value": value, + }) + } + + return c +} diff --git a/nutanix/resource_nutanix_access_control_policy_test.go b/nutanix/resource_nutanix_access_control_policy_test.go index 0db6642fc..e268ba08f 100644 --- a/nutanix/resource_nutanix_access_control_policy_test.go +++ b/nutanix/resource_nutanix_access_control_policy_test.go @@ -53,8 +53,6 @@ func TestAccNutanixAccessControlPolicy_basic(t *testing.T) { func TestAccNutanixAccessControlPolicy_WithUser(t *testing.T) { name := acctest.RandomWithPrefix("accest-access-policy") description := "Description of my access control policy" - nameUpdated := acctest.RandomWithPrefix("accest-access-policy") - descriptionUpdated := "Description of my access control policy updated" resource.Test(t, resource.TestCase{ PreCheck: func() { testAccPreCheck(t) }, @@ -69,14 +67,6 @@ func TestAccNutanixAccessControlPolicy_WithUser(t *testing.T) { resource.TestCheckResourceAttr(resourceAccessPolicy, "description", description), ), }, - { - Config: testAccNutanixAccessControlPolicyConfigWithUser(nameUpdated, descriptionUpdated), - Check: resource.ComposeTestCheckFunc( - testAccCheckNutanixAccessControlPolicyExists(), - resource.TestCheckResourceAttr(resourceAccessPolicy, "name", nameUpdated), - resource.TestCheckResourceAttr(resourceAccessPolicy, "description", descriptionUpdated), - ), - }, { ResourceName: resourceAccessPolicy, ImportState: true, @@ -214,7 +204,7 @@ resource "nutanix_access_control_policy" "test" { func testAccNutanixAccessControlPolicyConfigWithUser(name, description string) string { return fmt.Sprintf(` resource "nutanix_role" "test" { - name = "test role" + name = "test role 2" description = "description role" permission_reference_list { kind = "permission" @@ -238,7 +228,10 @@ resource "nutanix_access_control_policy" "test" { operator = "IN" left_hand_side = "PROJECT" right_hand_side { - uuid_list = ["6b004b04-b88d-4aae-8b39-4a8f090200d3"] + categories { + name = "Environment" + value = ["Dev", "Dev1"] + } } } entity_filter_expression_list{