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

Add External IDs Support for Group #733

Merged
merged 2 commits into from
May 9, 2022
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
79 changes: 79 additions & 0 deletions nsxt/resource_nsxt_policy_group.go
Original file line number Diff line number Diff line change
Expand Up @@ -44,6 +44,13 @@ var conjunctionOperatorValues = []string{
model.ConjunctionOperator_CONJUNCTION_OPERATOR_AND,
}

var externalMemberTypeValues = []string{
model.ExternalIDExpression_MEMBER_TYPE_VIRTUALMACHINE,
model.ExternalIDExpression_MEMBER_TYPE_VIRTUALNETWORKINTERFACE,
model.ExternalIDExpression_MEMBER_TYPE_CLOUDNATIVESERVICEINSTANCE,
model.ExternalIDExpression_MEMBER_TYPE_PHYSICALSERVER,
}

func resourceNsxtPolicyGroup() *schema.Resource {
return &schema.Resource{
Create: resourceNsxtPolicyGroupCreate,
Expand Down Expand Up @@ -117,6 +124,28 @@ func getMACAddressExpressionSchema() *schema.Resource {
}
}

func getExternalIDExpressionSchema() *schema.Resource {
return &schema.Resource{
Schema: map[string]*schema.Schema{
"external_ids": {
Type: schema.TypeSet,
Required: true,
Description: "List of external IDs",
Elem: &schema.Schema{
Type: schema.TypeString,
},
},
"member_type": {
Type: schema.TypeString,
Optional: true,
Description: "External ID member type, default to virtual machine if not specified",
ValidateFunc: validation.StringInSlice(externalMemberTypeValues, false),
Default: model.ExternalIDExpression_MEMBER_TYPE_VIRTUALMACHINE,
},
},
}
}

func getPathExpressionSchema() *schema.Resource {
return &schema.Resource{
Schema: map[string]*schema.Schema{
Expand Down Expand Up @@ -226,6 +255,12 @@ func getCriteriaSetSchema() *schema.Resource {
Optional: true,
MaxItems: 1,
},
"external_id_expression": {
Type: schema.TypeList,
Description: "External ID expression specifying additional members in the Group",
Elem: getExternalIDExpressionSchema(),
Optional: true,
},
},
}
}
Expand Down Expand Up @@ -423,6 +458,29 @@ func buildGroupMacAddressData(ipaddr interface{}) (*data.StructValue, error) {
return dataValue.(*data.StructValue), nil
}

func buildGroupExternalIDExpressionData(externalID interface{}) (*data.StructValue, error) {
idMap := externalID.(map[string]interface{})
memberType := idMap["member_type"].(string)
var extIDs []string

for _, extID := range idMap["external_ids"].(*schema.Set).List() {
extIDs = append(extIDs, extID.(string))
}
extIDStruct := model.ExternalIDExpression{
MemberType: &memberType,
ExternalIds: extIDs,
ResourceType: model.ExternalIDExpression__TYPE_IDENTIFIER,
}

converter := bindings.NewTypeConverter()
converter.SetMode(bindings.REST)
dataValue, errors := converter.ConvertToVapi(extIDStruct, model.ExternalIDExpressionBindingType())
if errors != nil {
return nil, errors[0]
}
return dataValue.(*data.StructValue), nil
}

func buildGroupMemberPathData(paths interface{}) (*data.StructValue, error) {
pathMap := paths.(map[string]interface{})
var pathList []string
Expand Down Expand Up @@ -496,6 +554,12 @@ func buildGroupExpressionDataFromType(expressionType string, datum interface{})
return nil, err
}
return data, nil
} else if expressionType == "external_id_expression" {
data, err := buildGroupExternalIDExpressionData(datum)
if err != nil {
return nil, err
}
return data, nil
}
return nil, fmt.Errorf("Unknown expression type: %v", expressionType)
}
Expand Down Expand Up @@ -643,6 +707,21 @@ func fromGroupExpressionData(expressions []*data.StructValue) ([]map[string]inte
addrList = append(addrList, addrMap)
macMap["macaddress_expression"] = addrList
parsedCriteria = append(parsedCriteria, macMap)
} else if expStruct.ResourceType == model.ExternalIDExpression__TYPE_IDENTIFIER {
log.Printf("[DEBUG] Parsing external id expression")
extIDData, errors := converter.ConvertToGolang(expression, model.ExternalIDExpressionBindingType())
if len(errors) > 0 {
return nil, nil, errors[0]
}
extIDStruct := extIDData.(model.ExternalIDExpression)
var idList []map[string]interface{}
var extIDMap = make(map[string]interface{})
extIDMap["member_type"] = extIDStruct.MemberType
extIDMap["external_ids"] = extIDStruct.ExternalIds
var idMap = make(map[string]interface{})
idList = append(idList, extIDMap)
idMap["external_id_expression"] = idList
parsedCriteria = append(parsedCriteria, idMap)
} else if expStruct.ResourceType == model.Condition__TYPE_IDENTIFIER {
log.Printf("[DEBUG] Parsing condition")
condMap, err := groupConditionDataToMap(expression)
Expand Down
99 changes: 99 additions & 0 deletions nsxt/resource_nsxt_policy_group_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -131,6 +131,52 @@ func TestAccResourceNsxtGlobalPolicyGroup_singleIPAddressCriteria(t *testing.T)
})
}

func TestAccResourceNsxtGlobalPolicyGroup_externalIDCriteria(t *testing.T) {
name := getAccTestResourceName()
updatedName := getAccTestResourceName()
testResourceName := "nsxt_policy_group.test"

resource.ParallelTest(t, resource.TestCase{
PreCheck: func() {
testAccPreCheck(t)
},
Providers: testAccProviders,
CheckDestroy: func(state *terraform.State) error {
return testAccNsxtPolicyGroupCheckDestroy(state, updatedName, defaultDomain)
},
Steps: []resource.TestStep{
{
Config: testAccNsxtPolicyGroupExternalIDCreateTemplate(name, defaultDomain),
Check: resource.ComposeTestCheckFunc(
testAccNsxtPolicyGroupExists(testResourceName, defaultDomain),
resource.TestCheckResourceAttr(testResourceName, "display_name", name),
resource.TestCheckResourceAttr(testResourceName, "description", "Acceptance Test"),
resource.TestCheckResourceAttr(testResourceName, "domain", defaultDomain),
resource.TestCheckResourceAttrSet(testResourceName, "path"),
resource.TestCheckResourceAttrSet(testResourceName, "revision"),
resource.TestCheckResourceAttr(testResourceName, "conjunction.#", "0"),
resource.TestCheckResourceAttr(testResourceName, "tag.#", "2"),
resource.TestCheckResourceAttr(testResourceName, "criteria.#", "1"),
),
},
{
Config: testAccNsxtPolicyGroupExternalIDUpdateTemplate(updatedName, defaultDomain),
Check: resource.ComposeTestCheckFunc(
testAccNsxtPolicyGroupExists(testResourceName, defaultDomain),
resource.TestCheckResourceAttr(testResourceName, "display_name", updatedName),
resource.TestCheckResourceAttr(testResourceName, "description", "Acceptance Test"),
resource.TestCheckResourceAttr(testResourceName, "domain", defaultDomain),
resource.TestCheckResourceAttrSet(testResourceName, "path"),
resource.TestCheckResourceAttrSet(testResourceName, "revision"),
resource.TestCheckResourceAttr(testResourceName, "conjunction.#", "1"),
resource.TestCheckResourceAttr(testResourceName, "tag.#", "0"),
resource.TestCheckResourceAttr(testResourceName, "criteria.#", "2"),
),
},
},
})
}

func TestAccResourceNsxtGlobalPolicyGroup_withDomain(t *testing.T) {
name := "test-nsx-global-policy-group-domain"
testResourceName := "nsxt_policy_group.test"
Expand Down Expand Up @@ -778,6 +824,59 @@ resource "nsxt_policy_group" "test" {
`, name)
}

func testAccNsxtPolicyGroupExternalIDCreateTemplate(name string, siteName string) string {
return fmt.Sprintf(`
resource "nsxt_policy_group" "test" {
display_name = "%s"
description = "Acceptance Test"

criteria {
external_id_expression {
member_type = "VirtualMachine"
external_ids = ["520ba7b0-d9f8-87b1-6f44-15bbeb7935c7", "52748a9e-d61d-e29b-d54b-07f169ff0ee8-4000"]
}
}

tag {
scope = "scope1"
tag = "tag1"
}

tag {
scope = "scope2"
tag = "tag2"
}
}
`, name)
}

func testAccNsxtPolicyGroupExternalIDUpdateTemplate(name string, siteName string) string {
return fmt.Sprintf(`
resource "nsxt_policy_group" "test" {
display_name = "%s"
description = "Acceptance Test"

criteria {
external_id_expression {
member_type = "VirtualMachine"
external_ids = ["520ba7b0-d9f8-87b1-6f44-15bbeb7935c7", "52748a9e-d61d-e29b-d54b-07f169ff0ee8-4000"]
}
}

conjunction {
operator = "OR"
}

criteria {
external_id_expression {
member_type = "VirtualNetworkInterface"
external_ids = ["520ba7b0-d9f8-87b1-6f44-15bbeb7935c7", "52748a9e-d61d-e29b-d54b-07f169ff0ee8-4000"]
}
}
}
`, name)
}

func testAccNsxtPolicyGroupPathsPrerequisites() string {
var preRequisites string
if testAccIsGlobalManager() {
Expand Down
14 changes: 14 additions & 0 deletions website/docs/r/policy_group.html.markdown
Original file line number Diff line number Diff line change
Expand Up @@ -53,6 +53,17 @@ resource "nsxt_policy_group" "group1" {
}
}

conjunction {
operator = "OR"
}

criteria {
external_id_expression {
member_type = "VirtualMachine"
external_ids = ["520ba7b0-d9f8-87b1-6f44-15bbeb7935c7", "52748a9e-d61d-e29b-d54b-07f169ff0ee8-4000"]
}
}

extended_criteria {
identity_group {
distinguished_name = "cn=u1,ou=users,dc=example,dc=local"
Expand Down Expand Up @@ -158,6 +169,9 @@ The following arguments are supported:
* `mac_addresses` - (Required) List of MAC addresses.
* `path_expression` - (Optional) An expression block to specify direct group members by policy path.
* `member_paths` - (Required) List of policy paths for direct members for this Group (such as Segments, Segment ports, Groups etc).
* `external_id_expression` - (Optional) An expression block to specify external IDs for the specified member type for this Group.
* `member_type` - (Optional) External ID member type. Must be one of: `VirtualMachine`, `VirtualNetworkInterface`, `CloudNativeServiceInstance`, or `PhysicalServer`. Defaults to `VirtualMachine`.
* `external_ids` - (Required) List of external IDs for the specified member type.
* `condition` (Optional) A repeatable condition block to select this Group's members. When multiple `condition` blocks are used in a single `criteria` they form a nested expression that's implicitly ANDed together and each nested condition must used the same `member_type`.
* `key` (Required) Specifies the attribute to query. Must be one of: `Tag`, `ComputerName`, `OSName` or `Name`. For a `member_type` other than `VirtualMachine`, only the `Tag` key is supported.
* `member_type` (Required) Specifies the type of resource to query. Must be one of: `IPSet`, `LogicalPort`, `LogicalSwitch`, `Segment`, `SegmentPort` or `VirtualMachine`.
Expand Down