From 155ad617949305e2ad6db2feeb23672cc32c298a Mon Sep 17 00:00:00 2001 From: jan Date: Sun, 26 Dec 2021 00:16:53 +0100 Subject: [PATCH 1/8] Add service group resource --- .gitignore | 1 + client/v3/v3_service.go | 115 ++++++++ client/v3/v3_structs.go | 30 ++ nutanix/provider.go | 1 + nutanix/resource_nutanix_service_group.go | 338 ++++++++++++++++++++++ 5 files changed, 485 insertions(+) create mode 100644 nutanix/resource_nutanix_service_group.go diff --git a/.gitignore b/.gitignore index 422d55c8d..9e0c2d51d 100644 --- a/.gitignore +++ b/.gitignore @@ -27,3 +27,4 @@ log.* # autogenerated fies *.autogenerated.* +.idea/ diff --git a/client/v3/v3_service.go b/client/v3/v3_service.go index 52c373c69..e7a4e0a18 100644 --- a/client/v3/v3_service.go +++ b/client/v3/v3_service.go @@ -108,6 +108,12 @@ type Service interface { CreateRecoveryPlan(request *RecoveryPlanInput) (*RecoveryPlanResponse, error) UpdateRecoveryPlan(uuid string, body *RecoveryPlanInput) (*RecoveryPlanResponse, error) DeleteRecoveryPlan(uuid string) (*DeleteResponse, error) + GetServiceGroup(uuid string) (*ServiceGroupResponse, error) + ListServiceGroups(getEntitiesRequest *DSMetadata) (*ServiceGroupListResponse, error) + ListAllServiceGroups(filter string) (*ServiceGroupListResponse, error) + CreateServiceGroup(request *ServiceGroupInput) (*Reference, error) + UpdateServiceGroup(uuid string, body *ServiceGroupInput) error + DeleteServiceGroup(uuid string) error } /*CreateVM Creates a VM @@ -2203,3 +2209,112 @@ func (op Operations) DeleteRecoveryPlan(uuid string) (*DeleteResponse, error) { return deleteResponse, op.client.Do(ctx, req, deleteResponse) } + +func (op Operations) GetServiceGroup(uuid string) (*ServiceGroupResponse, error) { + ctx := context.TODO() + + path := fmt.Sprintf("/service_groups/%s", uuid) + ServiceGroup := new(ServiceGroupResponse) + + req, err := op.client.NewRequest(ctx, http.MethodGet, path, nil) + if err != nil { + return nil, err + } + + return ServiceGroup, op.client.Do(ctx, req, ServiceGroup) +} + +func (op Operations) CreateServiceGroup(request *ServiceGroupInput) (*Reference, error) { + ctx := context.TODO() + + req, err := op.client.NewRequest(ctx, http.MethodPost, "/service_groups", request) + ServiceGroup := new(Reference) + + if err != nil { + return nil, err + } + + return ServiceGroup, op.client.Do(ctx, req, ServiceGroup) +} + +func (op Operations) DeleteServiceGroup(uuid string) error { + ctx := context.TODO() + + path := fmt.Sprintf("/service_groups/%s", uuid) + + req, err := op.client.NewRequest(ctx, http.MethodDelete, path, nil) + + if err != nil { + return err + } + + return op.client.Do(ctx, req, nil) +} + +func (op Operations) ListAllServiceGroups(filter string) (*ServiceGroupListResponse, error) { + entities := make([]*ServiceGroupListEntry, 0) + + resp, err := op.ListServiceGroups(&DSMetadata{ + Filter: &filter, + Kind: utils.StringPtr("service_group"), + Length: utils.Int64Ptr(itemsPerPage), + }) + if err != nil { + return nil, err + } + + totalEntities := utils.Int64Value(resp.Metadata.TotalMatches) + remaining := totalEntities + offset := utils.Int64Value(resp.Metadata.Offset) + + if totalEntities > itemsPerPage { + for hasNext(&remaining) { + resp, err = op.ListServiceGroups(&DSMetadata{ + Filter: &filter, + Kind: utils.StringPtr("service_group"), + Length: utils.Int64Ptr(itemsPerPage), + Offset: utils.Int64Ptr(offset), + }) + + if err != nil { + return nil, err + } + + entities = append(entities, resp.Entities...) + + offset += itemsPerPage + log.Printf("[Debug] total=%d, remaining=%d, offset=%d len(entities)=%d\n", totalEntities, remaining, offset, len(entities)) + } + + resp.Entities = entities + } + + return resp, nil +} + +func (op Operations) ListServiceGroups(getEntitiesRequest *DSMetadata) (*ServiceGroupListResponse, error) { + ctx := context.TODO() + path := "/service_groups/list" + + list := new(ServiceGroupListResponse) + + req, err := op.client.NewRequest(ctx, http.MethodPost, path, getEntitiesRequest) + if err != nil { + return nil, err + } + + return list, op.client.Do(ctx, req, list) +} + +func (op Operations) UpdateServiceGroup(uuid string, body *ServiceGroupInput) error { + ctx := context.TODO() + + path := fmt.Sprintf("/service_groups/%s", uuid) + req, err := op.client.NewRequest(ctx, http.MethodPut, path, body) + + if err != nil { + return err + } + + return op.client.Do(ctx, req, nil) +} diff --git a/client/v3/v3_structs.go b/client/v3/v3_structs.go index f543d3d74..41c04a230 100644 --- a/client/v3/v3_structs.go +++ b/client/v3/v3_structs.go @@ -2517,3 +2517,33 @@ type RecoveryPlanInput struct { Metadata *Metadata `json:"metadata,omitempty"` Spec *RecoveryPlanSpec `json:"spec,omitempty"` } + +type ServiceListEntry struct { + Protocol *string `json:"protocol,omitempty"` + TCPPortRangeList []*PortRange `json:"tcp_port_range_list,omitempty"` + UDPPortRangeList []*PortRange `json:"udp_port_range_list,omitempty"` + IcmpTypeCodeList []*NetworkRuleIcmpTypeCodeList `json:"icmp_type_code_list,omitempty"` +} + +type ServiceGroupListEntry struct { + UUID *string `json:"uuid,omitempty"` + ServiceGroup *ServiceGroupInput `json:"service_group,omitempty"` + AssociatedPoliciesList []*Reference `json:"associated_policies_list,omitempty"` +} + +type ServiceGroupInput struct { + Name *string `json:"name,omitempty"` + Description *string `json:"description,omitempty"` + ServiceList []*ServiceListEntry `json:"service_list,omitempty"` + SystemDefined *bool `json:"is_system_defined,omitempty"` +} + +type ServiceGroupListResponse struct { + Metadata *ListMetadataOutput `json:"metadata,omitempty"` + Entities []*ServiceGroupListEntry `json:"entities,omitempty"` +} + +type ServiceGroupResponse struct { + ServiceGroup *ServiceGroupInput `json:"service_group,omitempty"` + UUID *string `json:"uuid,omitempty"` +} diff --git a/nutanix/provider.go b/nutanix/provider.go index 3e9c3d260..f52a60ac2 100644 --- a/nutanix/provider.go +++ b/nutanix/provider.go @@ -133,6 +133,7 @@ func Provider() terraform.ResourceProvider { "nutanix_karbon_private_registry": resourceNutanixKarbonPrivateRegistry(), "nutanix_protection_rule": resourceNutanixProtectionRule(), "nutanix_recovery_plan": resourceNutanixRecoveryPlan(), + "nutanix_service_group": resourceNutanixServiceGroup(), }, ConfigureFunc: providerConfigure, } diff --git a/nutanix/resource_nutanix_service_group.go b/nutanix/resource_nutanix_service_group.go new file mode 100644 index 000000000..587c530dc --- /dev/null +++ b/nutanix/resource_nutanix_service_group.go @@ -0,0 +1,338 @@ +package nutanix + +import ( + "encoding/json" + "fmt" + "github.com/hashicorp/terraform-plugin-sdk/helper/schema" + v3 "github.com/terraform-providers/terraform-provider-nutanix/client/v3" + "github.com/terraform-providers/terraform-provider-nutanix/utils" + "log" + "strconv" + "strings" +) + +func resourceNutanixServiceGroup() *schema.Resource { + return &schema.Resource{ + Create: resourceNutanixServicGroupCreate, + Read: resourceNutanixServicGroupRead, + Delete: resourceNutanixServicGroupDelete, + Update: resourceNutanixServicGroupUpdate, + Importer: &schema.ResourceImporter{ + State: schema.ImportStatePassthrough, + }, + Schema: map[string]*schema.Schema{ + "system_defined": { + Type: schema.TypeBool, + Computed: true, + Optional: false, + }, + "description": { + Type: schema.TypeString, + Optional: true, + }, + "service_list": { + Type: schema.TypeList, + Required: true, + Elem: &schema.Resource{ + Schema: map[string]*schema.Schema{ + "protocol": { + Type: schema.TypeString, + Optional: true, + }, + "icmp_type_code_list": { + Type: schema.TypeList, + + Optional: true, + Elem: &schema.Resource{ + Schema: map[string]*schema.Schema{ + "code": { + Type: schema.TypeString, + Optional: true, + }, + "type": { + Type: schema.TypeString, + Optional: true, + }, + }, + }, + }, + "tcp_port_range_list": { + Type: schema.TypeList, + Optional: true, + Elem: &schema.Resource{ + Schema: map[string]*schema.Schema{ + "end_port": { + Type: schema.TypeInt, + Optional: true, + }, + "start_port": { + Type: schema.TypeInt, + Optional: true, + }, + }, + }, + }, + "udp_port_range_list": { + Type: schema.TypeList, + Optional: true, + Elem: &schema.Resource{ + Schema: map[string]*schema.Schema{ + "end_port": { + Type: schema.TypeInt, + Optional: true, + }, + "start_port": { + Type: schema.TypeInt, + Optional: true, + }, + }, + }, + }, + }, + }, + }, + "name": { + Type: schema.TypeString, + Required: true, + }, + }, + } +} + +func IsValidProtocol(category string) bool { + switch category { + case + "ALL", + "ICMP", + "TCP", + "UDP": + return true + } + return false +} + +func resourceNutanixServicGroupUpdate(d *schema.ResourceData, meta interface{}) error { + conn := meta.(*Client).API + id := d.Id() + response, err := conn.V3.GetServiceGroup(id) + + request := &v3.ServiceGroupInput{} + + if err != nil { + if strings.Contains(fmt.Sprint(err), "ENTITY_NOT_FOUND") { + d.SetId("") + } + return fmt.Errorf("error retrieving for access control policy id (%s) :%+v", id, err) + } + + group := response.ServiceGroup + + if d.HasChange("name") { + group.Name = utils.StringPtr(d.Get("name").(string)) + } + + if d.HasChange("description") { + group.Description = utils.StringPtr(d.Get("description").(string)) + } + + if d.HasChange("system_defined") { + group.SystemDefined = utils.BoolPtr(d.Get("system_defined").(bool)) + } + + if d.HasChange("service_list") { + serviceList, err := expandServiceEntry(d) + + if err != nil { + return err + } + + group.ServiceList = serviceList + } + + request.SystemDefined = group.SystemDefined + request.Name = group.Name + request.Description = group.Description + request.ServiceList = group.ServiceList + + errUpdate := conn.V3.UpdateServiceGroup(d.Id(), request) + if errUpdate != nil { + return fmt.Errorf("error updating service group id %s): %s", d.Id(), errUpdate) + } + + return resourceNutanixServicGroupRead(d, meta) +} + +func flattenServiceEntry(group *v3.ServiceGroupInput) []map[string]interface{} { + groupList := make([]map[string]interface{}, 0) + + for _, v := range group.ServiceList { + groupItem := make(map[string]interface{}) + groupItem["protocol"] = utils.StringValue(v.Protocol) + + if v.TCPPortRangeList != nil { + tcpprl := v.TCPPortRangeList + tcpprList := make([]map[string]interface{}, len(tcpprl)) + for i, tcp := range tcpprl { + tcpItem := make(map[string]interface{}) + tcpItem["end_port"] = utils.Int64Value(tcp.EndPort) + tcpItem["start_port"] = utils.Int64Value(tcp.StartPort) + tcpprList[i] = tcpItem + } + groupItem["tcp_port_range_list"] = tcpprList + } + + if v.UDPPortRangeList != nil { + udpprl := v.UDPPortRangeList + udpprList := make([]map[string]interface{}, len(udpprl)) + for i, udp := range udpprl { + udpItem := make(map[string]interface{}) + udpItem["end_port"] = utils.Int64Value(udp.EndPort) + udpItem["start_port"] = utils.Int64Value(udp.StartPort) + udpprList[i] = udpItem + } + groupItem["udp_port_range_list"] = udpprList + } + + if v.IcmpTypeCodeList != nil { + icmptcl := v.IcmpTypeCodeList + icmptcList := make([]map[string]interface{}, len(icmptcl)) + for i, icmp := range icmptcl { + icmpItem := make(map[string]interface{}) + icmpItem["code"] = strconv.FormatInt(utils.Int64Value(icmp.Code), 10) + icmpItem["type"] = strconv.FormatInt(utils.Int64Value(icmp.Type), 10) + icmptcList[i] = icmpItem + } + groupItem["icmp_type_code_list"] = icmptcList + } + + groupList = append(groupList, groupItem) + } + return groupList +} + +func resourceNutanixServicGroupRead(d *schema.ResourceData, meta interface{}) error { + log.Printf("[DEBUG] Reading ServiceGroup: %s", d.Get("name").(string)) + + // Get client connection + conn := meta.(*Client).API + + // Make request to the API + resp, err := conn.V3.GetServiceGroup(d.Id()) + + if err != nil { + if strings.Contains(fmt.Sprint(err), "ENTITY_NOT_FOUND") { + d.SetId("") + return nil + } + return err + } + + if err := d.Set("service_list", flattenServiceEntry(resp.ServiceGroup)); err != nil { + return err + } + + d.Set("name", utils.StringValue(resp.ServiceGroup.Name)) + d.Set("description", utils.StringValue(resp.ServiceGroup.Description)) + + return d.Set("system_defined", utils.BoolValue(resp.ServiceGroup.SystemDefined)) +} + +func expandServiceEntry(d *schema.ResourceData) ([]*v3.ServiceListEntry, error) { + if services, ok := d.GetOk("service_list"); ok { + set := services.([]interface{}) + outbound := make([]*v3.ServiceListEntry, len(set)) + + for k, v := range set { + service := &v3.ServiceListEntry{} + + entry := v.(map[string]interface{}) + + if proto, pok := entry["protocol"]; pok && proto.(string) != "" { + if !IsValidProtocol(proto.(string)) { + return nil, fmt.Errorf("protocol needs to be one of 'ALL', 'ICMP', 'TCP', 'UDP'") + } + service.Protocol = utils.StringPtr(proto.(string)) + } + + if t, tcpok := entry["tcp_port_range_list"]; tcpok { + service.TCPPortRangeList = expandPortRangeList(t) + } + + if u, udpok := entry["udp_port_range_list"]; udpok { + service.UDPPortRangeList = expandPortRangeList(u) + } + + if icmp, icmpok := entry["icmp_type_code_list"]; icmpok { + service.IcmpTypeCodeList = expandIcmpTypeCodeList(icmp) + } + + outbound[k] = service + } + + return outbound, nil + } + return nil, nil +} + +func resourceNutanixServicGroupCreate(d *schema.ResourceData, meta interface{}) error { + conn := meta.(*Client).API + + request := &v3.ServiceGroupInput{} + request.ServiceList = make([]*v3.ServiceListEntry, 0) + + name, nameOK := d.GetOk("name") + + // Read Arguments and set request values + if desc, ok := d.GetOk("description"); ok { + request.Description = utils.StringPtr(desc.(string)) + } + + // validate required fields + if !nameOK { + return fmt.Errorf("please provide the required attribute name") + } + + request.Name = utils.StringPtr(name.(string)) + + serviceList, err := expandServiceEntry(d) + + if err != nil { + return err + } + + request.ServiceList = serviceList + + requestEnc, err := json.Marshal(request) + + if err != nil { + return err + } + + log.Printf("[DEBUG] %s", requestEnc) + + resp, err := conn.V3.CreateServiceGroup(request) + + if err != nil { + return err + } + + n := *resp.UUID + + // set terraform state + d.SetId(n) + + return resourceNutanixServicGroupRead(d, meta) +} + +func resourceNutanixServicGroupDelete(d *schema.ResourceData, meta interface{}) error { + conn := meta.(*Client).API + + log.Printf("[Debug] Destroying the service group with the ID %s", d.Id()) + + if err := conn.V3.DeleteServiceGroup(d.Id()); err != nil { + return err + } + + d.SetId("") + return nil +} From 8f36b91c399fb1c10eb92b29f6c6572bf682ccd4 Mon Sep 17 00:00:00 2001 From: jan Date: Sun, 26 Dec 2021 02:24:53 +0100 Subject: [PATCH 2/8] Add address groups to the API client --- client/v3/v3_service.go | 114 ++++++++++++++++++++++++++++++++++++++++ client/v3/v3_structs.go | 27 ++++++++++ 2 files changed, 141 insertions(+) diff --git a/client/v3/v3_service.go b/client/v3/v3_service.go index e7a4e0a18..2070a2645 100644 --- a/client/v3/v3_service.go +++ b/client/v3/v3_service.go @@ -114,6 +114,12 @@ type Service interface { CreateServiceGroup(request *ServiceGroupInput) (*Reference, error) UpdateServiceGroup(uuid string, body *ServiceGroupInput) error DeleteServiceGroup(uuid string) error + GetAddressGroup(uuid string) (*AddressGroupResponse, error) + ListAddressGroups(getEntitiesRequest *DSMetadata) (*AddressGroupListResponse, error) + ListAllAddressGroups(filter string) (*AddressGroupListResponse, error) + DeleteAddressGroup(uuid string) error + CreateAddressGroup(request *AddressGroupInput) (*Reference, error) + UpdateAddressGroup(uuid string, body *AddressGroupInput) error } /*CreateVM Creates a VM @@ -2318,3 +2324,111 @@ func (op Operations) UpdateServiceGroup(uuid string, body *ServiceGroupInput) er return op.client.Do(ctx, req, nil) } + +func (op Operations) GetAddressGroup(uuid string) (*AddressGroupResponse, error) { + ctx := context.TODO() + + path := fmt.Sprintf("/address_groups/%s", uuid) + AddressGroup := new(AddressGroupResponse) + + req, err := op.client.NewRequest(ctx, http.MethodGet, path, nil) + if err != nil { + return nil, err + } + + return AddressGroup, op.client.Do(ctx, req, AddressGroup) +} + +func (op Operations) ListAllAddressGroups(filter string) (*AddressGroupListResponse, error) { + entities := make([]*AddressGroupListEntry, 0) + + resp, err := op.ListAddressGroups(&DSMetadata{ + Filter: &filter, + Kind: utils.StringPtr("address_group"), + Length: utils.Int64Ptr(itemsPerPage), + }) + if err != nil { + return nil, err + } + + totalEntities := utils.Int64Value(resp.Metadata.TotalMatches) + remaining := totalEntities + offset := utils.Int64Value(resp.Metadata.Offset) + + if totalEntities > itemsPerPage { + for hasNext(&remaining) { + resp, err = op.ListAddressGroups(&DSMetadata{ + Filter: &filter, + Kind: utils.StringPtr("address_group"), + Length: utils.Int64Ptr(itemsPerPage), + Offset: utils.Int64Ptr(offset), + }) + + if err != nil { + return nil, err + } + + entities = append(entities, resp.Entities...) + + offset += itemsPerPage + log.Printf("[Debug] total=%d, remaining=%d, offset=%d len(entities)=%d\n", totalEntities, remaining, offset, len(entities)) + } + + resp.Entities = entities + } + + return resp, nil +} + +func (op Operations) ListAddressGroups(getEntitiesRequest *DSMetadata) (*AddressGroupListResponse, error) { + ctx := context.TODO() + path := "/address_groups/list" + + list := new(AddressGroupListResponse) + + req, err := op.client.NewRequest(ctx, http.MethodPost, path, getEntitiesRequest) + if err != nil { + return nil, err + } + + return list, op.client.Do(ctx, req, list) +} + +func (op Operations) DeleteAddressGroup(uuid string) error { + ctx := context.TODO() + + path := fmt.Sprintf("/address_groups/%s", uuid) + + req, err := op.client.NewRequest(ctx, http.MethodDelete, path, nil) + + if err != nil { + return err + } + + return op.client.Do(ctx, req, nil) +} + +func (op Operations) CreateAddressGroup(request *AddressGroupInput) (*Reference, error) { + ctx := context.TODO() + + req, err := op.client.NewRequest(ctx, http.MethodPost, "/address_groups", request) + AddressGroup := new(Reference) + + if err != nil { + return nil, err + } + + return AddressGroup, op.client.Do(ctx, req, AddressGroup) +} +func (op Operations) UpdateAddressGroup(uuid string, body *AddressGroupInput) error { + ctx := context.TODO() + + path := fmt.Sprintf("/address_groups/%s", uuid) + req, err := op.client.NewRequest(ctx, http.MethodPut, path, body) + + if err != nil { + return err + } + + return op.client.Do(ctx, req, nil) +} diff --git a/client/v3/v3_structs.go b/client/v3/v3_structs.go index 41c04a230..e2cc8308e 100644 --- a/client/v3/v3_structs.go +++ b/client/v3/v3_structs.go @@ -2547,3 +2547,30 @@ type ServiceGroupResponse struct { ServiceGroup *ServiceGroupInput `json:"service_group,omitempty"` UUID *string `json:"uuid,omitempty"` } + +type IPAddressBlock struct { + IPAddress *string `json:"ip,omitempty"` + PrefixLength *int64 `json:"prefix_length,omitempty"` +} + +type AddressGroupInput struct { + Name *string `json:"name,omitempty"` + Description *string `json:"description,omitempty"` + BlockList []*IPAddressBlock `json:"ip_address_block_list,omitempty"` + AddressGroupString *string `json:"address_group_string,omitempty"` +} + +type AddressGroupResponse struct { + UUID *string `json:"uuid,omitempty"` + AddressGroup *AddressGroupInput `json:"address_group,omitempty"` +} + +type AddressGroupListEntry struct { + AddressGroup *AddressGroupInput `json:"address_group,omitempty"` + AssociatedPoliciesList []*Reference `json:"associated_policies_list,omitempty"` +} + +type AddressGroupListResponse struct { + Metadata *ListMetadataOutput `json:"metadata,omitempty"` + Entities []*AddressGroupListEntry `json:"entities,omitempty"` +} From a73033a2497459b0de6a7f2ac7144b5806561da7 Mon Sep 17 00:00:00 2001 From: jan Date: Sun, 26 Dec 2021 22:22:08 +0100 Subject: [PATCH 3/8] Add address group resource --- nutanix/provider.go | 1 + nutanix/resource_nutanix_address_group.go | 217 ++++++++++++++++++++++ nutanix/resource_nutanix_service_group.go | 20 +- 3 files changed, 228 insertions(+), 10 deletions(-) create mode 100644 nutanix/resource_nutanix_address_group.go diff --git a/nutanix/provider.go b/nutanix/provider.go index f52a60ac2..dbb17f345 100644 --- a/nutanix/provider.go +++ b/nutanix/provider.go @@ -134,6 +134,7 @@ func Provider() terraform.ResourceProvider { "nutanix_protection_rule": resourceNutanixProtectionRule(), "nutanix_recovery_plan": resourceNutanixRecoveryPlan(), "nutanix_service_group": resourceNutanixServiceGroup(), + "nutanix_address_group": resourceNutanixAddressGroup(), }, ConfigureFunc: providerConfigure, } diff --git a/nutanix/resource_nutanix_address_group.go b/nutanix/resource_nutanix_address_group.go new file mode 100644 index 000000000..2e4e7e21e --- /dev/null +++ b/nutanix/resource_nutanix_address_group.go @@ -0,0 +1,217 @@ +package nutanix + +import ( + "fmt" + "github.com/hashicorp/terraform-plugin-sdk/helper/schema" + v3 "github.com/terraform-providers/terraform-provider-nutanix/client/v3" + "github.com/terraform-providers/terraform-provider-nutanix/utils" + "log" + "strings" +) + +func resourceNutanixAddressGroup() *schema.Resource { + return &schema.Resource{ + Create: resourceNutanixAddressGroupCreate, + Read: resourceNutanixAddressGroupRead, + Delete: resourceNutanixAddressGroupDelete, + Update: resourceNutanixAddressGroupUpdate, + Importer: &schema.ResourceImporter{ + State: schema.ImportStatePassthrough, + }, + Schema: map[string]*schema.Schema{ + "name": { + Type: schema.TypeString, + Required: true, + }, + "description": { + Type: schema.TypeString, + Optional: true, + }, + "ip_address_block_list": { + Type: schema.TypeList, + Required: true, + Elem: &schema.Resource{ + Schema: map[string]*schema.Schema{ + "ip": { + Type: schema.TypeString, + Required: true, + }, + "prefix_length": { + Type: schema.TypeInt, + Required: true, + }, + }, + }, + }, + "address_group_string": { + Type: schema.TypeString, + Computed: true, + }, + }, + } + +} + +func resourceNutanixAddressGroupUpdate(d *schema.ResourceData, meta interface{}) error { + conn := meta.(*Client).API + id := d.Id() + response, err := conn.V3.GetAddressGroup(id) + + request := &v3.AddressGroupInput{} + + if err != nil { + if strings.Contains(fmt.Sprint(err), "ENTITY_NOT_FOUND") { + d.SetId("") + } + return fmt.Errorf("error retrieving for address group id (%s) :%+v", id, err) + } + + group := response.AddressGroup + + if d.HasChange("name") { + group.Name = utils.StringPtr(d.Get("name").(string)) + } + + if d.HasChange("description") { + group.Description = utils.StringPtr(d.Get("description").(string)) + } + + if d.HasChange("ip_address_block_list") { + blockList, err := expandAddressEntry(d) + + if err != nil { + return err + } + + group.BlockList = blockList + } + + request.Name = group.Name + request.Description = group.Description + request.BlockList = group.BlockList + + errUpdate := conn.V3.UpdateAddressGroup(d.Id(), request) + if errUpdate != nil { + return fmt.Errorf("error updating address group id %s): %s", d.Id(), errUpdate) + } + + return resourceNutanixAddressGroupRead(d, meta) + +} + +func resourceNutanixAddressGroupDelete(d *schema.ResourceData, meta interface{}) error { + conn := meta.(*Client).API + + log.Printf("[Debug] Destroying the address group with the ID %s", d.Id()) + + if err := conn.V3.DeleteAddressGroup(d.Id()); err != nil { + return err + } + + d.SetId("") + return nil +} + +func resourceNutanixAddressGroupRead(d *schema.ResourceData, meta interface{}) error { + log.Printf("[DEBUG] Reading AddressGroup: %s", d.Get("name").(string)) + + // Get client connection + conn := meta.(*Client).API + + // Make request to the API + resp, err := conn.V3.GetAddressGroup(d.Id()) + + if err != nil { + if strings.Contains(fmt.Sprint(err), "ENTITY_NOT_FOUND") { + d.SetId("") + return nil + } + return err + } + + if err := d.Set("ip_address_block_list", flattenAddressEntry(resp.AddressGroup)); err != nil { + return err + } + + d.Set("name", utils.StringValue(resp.AddressGroup.Name)) + + return d.Set("description", utils.StringValue(resp.AddressGroup.Description)) +} + +func flattenAddressEntry(group *v3.AddressGroupInput) []map[string]interface{} { + groupList := make([]map[string]interface{}, 0) + for _, v := range group.BlockList { + groupItem := make(map[string]interface{}) + groupItem["ip"] = v.IPAddress + groupItem["prefix_length"] = v.PrefixLength + groupList = append(groupList, groupItem) + } + + return groupList +} + +func resourceNutanixAddressGroupCreate(d *schema.ResourceData, meta interface{}) error { + conn := meta.(*Client).API + + request := &v3.AddressGroupInput{} + request.BlockList = make([]*v3.IPAddressBlock, 0) + + // Read Arguments and set request values + + if name, ok := d.GetOk("name"); ok { + request.Name = utils.StringPtr(name.(string)) + } + + if desc, ok := d.GetOk("description"); ok { + request.Description = utils.StringPtr(desc.(string)) + } + addressList, err := expandAddressEntry(d) + + if err != nil { + return err + } + + request.BlockList = addressList + + resp, err := conn.V3.CreateAddressGroup(request) + + if err != nil { + return err + } + + n := *resp.UUID + + // set terraform state + d.SetId(n) + + return resourceNutanixAddressGroupRead(d, meta) +} + +func expandAddressEntry(d *schema.ResourceData) ([]*v3.IPAddressBlock, error) { + if groups, ok := d.GetOk("ip_address_block_list"); ok { + set := groups.([]interface{}) + outbound := make([]*v3.IPAddressBlock, len(set)) + + for k, v := range set { + entry := v.(map[string]interface{}) + + block := &v3.IPAddressBlock{} + if ip, ipok := entry["ip"]; ipok { + block.IPAddress = utils.StringPtr(ip.(string)) + } else { + return nil, fmt.Errorf("error updating address group id %s): ip missing", d.Id()) + } + + if length, lengthok := entry["prefix_length"]; lengthok { + block.PrefixLength = utils.Int64Ptr(int64(length.(int))) + } else { + return nil, fmt.Errorf("error updating address group id %s): prefix_length missing", d.Id()) + } + + outbound[k] = block + } + return outbound, nil + } + + return nil, nil +} diff --git a/nutanix/resource_nutanix_service_group.go b/nutanix/resource_nutanix_service_group.go index 587c530dc..7b52c390f 100644 --- a/nutanix/resource_nutanix_service_group.go +++ b/nutanix/resource_nutanix_service_group.go @@ -13,10 +13,10 @@ import ( func resourceNutanixServiceGroup() *schema.Resource { return &schema.Resource{ - Create: resourceNutanixServicGroupCreate, - Read: resourceNutanixServicGroupRead, - Delete: resourceNutanixServicGroupDelete, - Update: resourceNutanixServicGroupUpdate, + Create: resourceNutanixServiceGroupCreate, + Read: resourceNutanixServiceGroupRead, + Delete: resourceNutanixServiceGroupDelete, + Update: resourceNutanixServiceGroupUpdate, Importer: &schema.ResourceImporter{ State: schema.ImportStatePassthrough, }, @@ -111,7 +111,7 @@ func IsValidProtocol(category string) bool { return false } -func resourceNutanixServicGroupUpdate(d *schema.ResourceData, meta interface{}) error { +func resourceNutanixServiceGroupUpdate(d *schema.ResourceData, meta interface{}) error { conn := meta.(*Client).API id := d.Id() response, err := conn.V3.GetServiceGroup(id) @@ -159,7 +159,7 @@ func resourceNutanixServicGroupUpdate(d *schema.ResourceData, meta interface{}) return fmt.Errorf("error updating service group id %s): %s", d.Id(), errUpdate) } - return resourceNutanixServicGroupRead(d, meta) + return resourceNutanixServiceGroupRead(d, meta) } func flattenServiceEntry(group *v3.ServiceGroupInput) []map[string]interface{} { @@ -210,7 +210,7 @@ func flattenServiceEntry(group *v3.ServiceGroupInput) []map[string]interface{} { return groupList } -func resourceNutanixServicGroupRead(d *schema.ResourceData, meta interface{}) error { +func resourceNutanixServiceGroupRead(d *schema.ResourceData, meta interface{}) error { log.Printf("[DEBUG] Reading ServiceGroup: %s", d.Get("name").(string)) // Get client connection @@ -274,7 +274,7 @@ func expandServiceEntry(d *schema.ResourceData) ([]*v3.ServiceListEntry, error) return nil, nil } -func resourceNutanixServicGroupCreate(d *schema.ResourceData, meta interface{}) error { +func resourceNutanixServiceGroupCreate(d *schema.ResourceData, meta interface{}) error { conn := meta.(*Client).API request := &v3.ServiceGroupInput{} @@ -321,10 +321,10 @@ func resourceNutanixServicGroupCreate(d *schema.ResourceData, meta interface{}) // set terraform state d.SetId(n) - return resourceNutanixServicGroupRead(d, meta) + return resourceNutanixServiceGroupRead(d, meta) } -func resourceNutanixServicGroupDelete(d *schema.ResourceData, meta interface{}) error { +func resourceNutanixServiceGroupDelete(d *schema.ResourceData, meta interface{}) error { conn := meta.(*Client).API log.Printf("[Debug] Destroying the service group with the ID %s", d.Id()) From 9efaae5759ed46f89186f911fd9cc8367471a133 Mon Sep 17 00:00:00 2001 From: jan Date: Sun, 26 Dec 2021 23:19:30 +0100 Subject: [PATCH 4/8] Add address group data sources --- client/v3/v3_structs.go | 2 +- nutanix/data_source_address_group.go | 40 +++++ nutanix/data_source_nutanix_address_groups.go | 162 ++++++++++++++++++ nutanix/provider.go | 2 + nutanix/resource_nutanix_address_group.go | 6 +- 5 files changed, 208 insertions(+), 4 deletions(-) create mode 100644 nutanix/data_source_address_group.go create mode 100644 nutanix/data_source_nutanix_address_groups.go diff --git a/client/v3/v3_structs.go b/client/v3/v3_structs.go index e2cc8308e..143917b21 100644 --- a/client/v3/v3_structs.go +++ b/client/v3/v3_structs.go @@ -2567,7 +2567,7 @@ type AddressGroupResponse struct { type AddressGroupListEntry struct { AddressGroup *AddressGroupInput `json:"address_group,omitempty"` - AssociatedPoliciesList []*Reference `json:"associated_policies_list,omitempty"` + AssociatedPoliciesList []*ReferenceValues `json:"associated_policies_list,omitempty"` } type AddressGroupListResponse struct { diff --git a/nutanix/data_source_address_group.go b/nutanix/data_source_address_group.go new file mode 100644 index 000000000..e1e9a2a8b --- /dev/null +++ b/nutanix/data_source_address_group.go @@ -0,0 +1,40 @@ +package nutanix + +import "github.com/hashicorp/terraform-plugin-sdk/helper/schema" + +func dataSourceNutanixAddressGroup() *schema.Resource { + return &schema.Resource{ + Read: resourceNutanixAddressGroupRead, + Schema: map[string]*schema.Schema{ + "name": { + Type: schema.TypeString, + Required: true, + }, + "description": { + Type: schema.TypeString, + Optional: true, + }, + "ip_address_block_list": { + Type: schema.TypeList, + Required: true, + Elem: &schema.Resource{ + Schema: map[string]*schema.Schema{ + "ip": { + Type: schema.TypeString, + Required: true, + }, + "prefix_length": { + Type: schema.TypeInt, + Required: true, + }, + }, + }, + }, + "address_group_string": { + Type: schema.TypeString, + Computed: true, + }, + }, + } + +} diff --git a/nutanix/data_source_nutanix_address_groups.go b/nutanix/data_source_nutanix_address_groups.go new file mode 100644 index 000000000..42964af4b --- /dev/null +++ b/nutanix/data_source_nutanix_address_groups.go @@ -0,0 +1,162 @@ +package nutanix + +import ( + "github.com/hashicorp/terraform-plugin-sdk/helper/resource" + "github.com/hashicorp/terraform-plugin-sdk/helper/schema" + v3 "github.com/terraform-providers/terraform-provider-nutanix/client/v3" + "github.com/terraform-providers/terraform-provider-nutanix/utils" +) + +func dataSourceNutanixAddressGroups() *schema.Resource { + return &schema.Resource{ + Read: dataSourceNutanixAddressGroupsRead, + Schema: map[string]*schema.Schema{ + "metadata": { + Type: schema.TypeSet, + Optional: true, + Computed: true, + MaxItems: 1, + Elem: &schema.Resource{ + Schema: map[string]*schema.Schema{ + "filter": { + Type: schema.TypeString, + Optional: true, + Computed: true, + }, + "kind": { + Type: schema.TypeString, + Optional: true, + Computed: true, + }, + "sort_order": { + Type: schema.TypeString, + Optional: true, + Computed: true, + }, + "offset": { + Type: schema.TypeInt, + Optional: true, + Computed: true, + }, + "length": { + Type: schema.TypeInt, + Optional: true, + Computed: true, + }, + "sort_attribute": { + Type: schema.TypeString, + Optional: true, + Computed: true, + }, + }, + }, + }, + "entities": { + Type: schema.TypeList, + Computed: true, + Elem: &schema.Resource{ + Schema: map[string]*schema.Schema{ + "address_group": { + Type: schema.TypeMap, + Computed: true, + Elem: &schema.Resource{ + Schema: map[string]*schema.Schema{ + "name": { + Type: schema.TypeString, + Required: true, + }, + "description": { + Type: schema.TypeString, + Optional: true, + }, + "ip_address_block_list": { + Type: schema.TypeList, + Required: true, + Elem: &schema.Resource{ + Schema: map[string]*schema.Schema{ + "ip": { + Type: schema.TypeString, + Required: true, + }, + "prefix_length": { + Type: schema.TypeInt, + Required: true, + }, + }, + }, + }, + "address_group_string": { + Type: schema.TypeString, + Computed: true, + }, + }, + }, + }, + "associated_policies_list": { + Type: schema.TypeMap, + Computed: true, + Elem: &schema.Resource{ + Schema: map[string]*schema.Schema{ + "kind": { + Type: schema.TypeString, + Computed: true, + }, + "uuid": { + Type: schema.TypeString, + Computed: true, + }, + "name": { + Type: schema.TypeString, + Computed: true, + }, + }, + }, + }, + }, + }, + }, + }, + } +} + +func dataSourceNutanixAddressGroupsRead(d *schema.ResourceData, meta interface{}) error { + // Get client connection + conn := meta.(*Client).API + req := &v3.DSMetadata{} + + metadata, filtersOk := d.GetOk("metadata") + if filtersOk { + req = buildDataSourceListMetadata(metadata.(*schema.Set)) + } + + resp, err := conn.V3.ListAllAddressGroups(utils.StringValue(req.Filter)) + if err != nil { + return err + } + + if err := d.Set("entities", flattenAddressGroup(resp.Entities)); err != nil { + return err + } + + d.SetId(resource.UniqueId()) + return nil +} + +func flattenAddressGroup(entries []*v3.AddressGroupListEntry) interface{} { + entities := make([]map[string]interface{}, len(entries)) + + for i, entry := range entries { + + entities[i] = map[string]interface{}{ + "address_group": map[string]interface{}{ + "name": entry.AddressGroup.Name, + "description": entry.AddressGroup.Description, + "address_group_string": entry.AddressGroup.AddressGroupString, + "ip_address_block_list": flattenAddressEntry(entry.AddressGroup.BlockList), + }, + "associated_policies_list": flattenReferenceList(entry.AssociatedPoliciesList), + } + } + + return entities +} diff --git a/nutanix/provider.go b/nutanix/provider.go index dbb17f345..eac97925b 100644 --- a/nutanix/provider.go +++ b/nutanix/provider.go @@ -117,6 +117,8 @@ func Provider() terraform.ResourceProvider { "nutanix_protection_rules": dataSourceNutanixProtectionRules(), "nutanix_recovery_plan": dataSourceNutanixRecoveryPlan(), "nutanix_recovery_plans": dataSourceNutanixRecoveryPlans(), + "nutanix_address_groups": dataSourceNutanixAddressGroups(), + "nutanix_address_group": dataSourceNutanixAddressGroup(), }, ResourcesMap: map[string]*schema.Resource{ "nutanix_virtual_machine": resourceNutanixVirtualMachine(), diff --git a/nutanix/resource_nutanix_address_group.go b/nutanix/resource_nutanix_address_group.go index 2e4e7e21e..817201a8d 100644 --- a/nutanix/resource_nutanix_address_group.go +++ b/nutanix/resource_nutanix_address_group.go @@ -129,7 +129,7 @@ func resourceNutanixAddressGroupRead(d *schema.ResourceData, meta interface{}) e return err } - if err := d.Set("ip_address_block_list", flattenAddressEntry(resp.AddressGroup)); err != nil { + if err := d.Set("ip_address_block_list", flattenAddressEntry(resp.AddressGroup.BlockList)); err != nil { return err } @@ -138,9 +138,9 @@ func resourceNutanixAddressGroupRead(d *schema.ResourceData, meta interface{}) e return d.Set("description", utils.StringValue(resp.AddressGroup.Description)) } -func flattenAddressEntry(group *v3.AddressGroupInput) []map[string]interface{} { +func flattenAddressEntry(group []*v3.IPAddressBlock) []map[string]interface{} { groupList := make([]map[string]interface{}, 0) - for _, v := range group.BlockList { + for _, v := range group { groupItem := make(map[string]interface{}) groupItem["ip"] = v.IPAddress groupItem["prefix_length"] = v.PrefixLength From 6de6e452411c443e37f71983bf1edb4b4a3e3718 Mon Sep 17 00:00:00 2001 From: jan Date: Mon, 27 Dec 2021 15:48:53 +0100 Subject: [PATCH 5/8] Add workaround for missing UUID parameter for data source --- nutanix/data_source_address_group.go | 40 --------- nutanix/data_source_nutanix_address_group.go | 84 +++++++++++++++++++ nutanix/data_source_nutanix_address_groups.go | 1 - nutanix/resource_nutanix_address_group.go | 7 +- nutanix/resource_nutanix_service_group.go | 7 +- 5 files changed, 91 insertions(+), 48 deletions(-) delete mode 100644 nutanix/data_source_address_group.go create mode 100644 nutanix/data_source_nutanix_address_group.go diff --git a/nutanix/data_source_address_group.go b/nutanix/data_source_address_group.go deleted file mode 100644 index e1e9a2a8b..000000000 --- a/nutanix/data_source_address_group.go +++ /dev/null @@ -1,40 +0,0 @@ -package nutanix - -import "github.com/hashicorp/terraform-plugin-sdk/helper/schema" - -func dataSourceNutanixAddressGroup() *schema.Resource { - return &schema.Resource{ - Read: resourceNutanixAddressGroupRead, - Schema: map[string]*schema.Schema{ - "name": { - Type: schema.TypeString, - Required: true, - }, - "description": { - Type: schema.TypeString, - Optional: true, - }, - "ip_address_block_list": { - Type: schema.TypeList, - Required: true, - Elem: &schema.Resource{ - Schema: map[string]*schema.Schema{ - "ip": { - Type: schema.TypeString, - Required: true, - }, - "prefix_length": { - Type: schema.TypeInt, - Required: true, - }, - }, - }, - }, - "address_group_string": { - Type: schema.TypeString, - Computed: true, - }, - }, - } - -} diff --git a/nutanix/data_source_nutanix_address_group.go b/nutanix/data_source_nutanix_address_group.go new file mode 100644 index 000000000..b0e1adcaf --- /dev/null +++ b/nutanix/data_source_nutanix_address_group.go @@ -0,0 +1,84 @@ +package nutanix + +import ( + "fmt" + "strings" + + "github.com/hashicorp/terraform-plugin-sdk/helper/schema" +) + +func dataSourceNutanixAddressGroup() *schema.Resource { + return &schema.Resource{ + Read: dataSourceNutanixAddressGroupRead, + Schema: map[string]*schema.Schema{ + "uuid": { + Type: schema.TypeString, + Required: true, + }, + "name": { + Type: schema.TypeString, + Computed: true, + }, + "description": { + Type: schema.TypeString, + Optional: true, + }, + "ip_address_block_list": { + Type: schema.TypeList, + Required: true, + Elem: &schema.Resource{ + Schema: map[string]*schema.Schema{ + "ip": { + Type: schema.TypeString, + Required: true, + }, + "prefix_length": { + Type: schema.TypeInt, + Required: true, + }, + }, + }, + }, + "address_group_string": { + Type: schema.TypeString, + Computed: true, + }, + }, + } +} + +func dataSourceNutanixAddressGroupRead(d *schema.ResourceData, meta interface{}) error { + conn := meta.(*Client).API + + if uuid, uuidOk := d.GetOk("uuid"); uuidOk { + group, reqErr := conn.V3.GetAddressGroup(uuid.(string)) + + if reqErr != nil { + if strings.Contains(fmt.Sprint(reqErr), "ENTITY_NOT_FOUND") { + d.SetId("") + } + return fmt.Errorf("error reading user with error %s", reqErr) + } + + if name, nameOk := d.GetOk("name"); nameOk { + d.Set("name", name.(string)) + } + + if desc, descOk := d.GetOk("description"); descOk { + d.Set("description", desc.(string)) + } + + if str, strOk := d.GetOk("address_group_string"); strOk { + d.Set("address_group_string", str.(string)) + } + + if err := d.Set("ip_address_block_list", flattenAddressEntry(group.AddressGroup.BlockList)); err != nil { + return err + } + + d.SetId(uuid.(string)) + } else { + return fmt.Errorf("please provide `uuid`") + } + return nil +} diff --git a/nutanix/data_source_nutanix_address_groups.go b/nutanix/data_source_nutanix_address_groups.go index 42964af4b..7bad40f2a 100644 --- a/nutanix/data_source_nutanix_address_groups.go +++ b/nutanix/data_source_nutanix_address_groups.go @@ -146,7 +146,6 @@ func flattenAddressGroup(entries []*v3.AddressGroupListEntry) interface{} { entities := make([]map[string]interface{}, len(entries)) for i, entry := range entries { - entities[i] = map[string]interface{}{ "address_group": map[string]interface{}{ "name": entry.AddressGroup.Name, diff --git a/nutanix/resource_nutanix_address_group.go b/nutanix/resource_nutanix_address_group.go index 817201a8d..aa4cc485d 100644 --- a/nutanix/resource_nutanix_address_group.go +++ b/nutanix/resource_nutanix_address_group.go @@ -2,11 +2,12 @@ package nutanix import ( "fmt" + "log" + "strings" + "github.com/hashicorp/terraform-plugin-sdk/helper/schema" v3 "github.com/terraform-providers/terraform-provider-nutanix/client/v3" "github.com/terraform-providers/terraform-provider-nutanix/utils" - "log" - "strings" ) func resourceNutanixAddressGroup() *schema.Resource { @@ -49,7 +50,6 @@ func resourceNutanixAddressGroup() *schema.Resource { }, }, } - } func resourceNutanixAddressGroupUpdate(d *schema.ResourceData, meta interface{}) error { @@ -96,7 +96,6 @@ func resourceNutanixAddressGroupUpdate(d *schema.ResourceData, meta interface{}) } return resourceNutanixAddressGroupRead(d, meta) - } func resourceNutanixAddressGroupDelete(d *schema.ResourceData, meta interface{}) error { diff --git a/nutanix/resource_nutanix_service_group.go b/nutanix/resource_nutanix_service_group.go index 7b52c390f..215e6cc26 100644 --- a/nutanix/resource_nutanix_service_group.go +++ b/nutanix/resource_nutanix_service_group.go @@ -3,12 +3,13 @@ package nutanix import ( "encoding/json" "fmt" - "github.com/hashicorp/terraform-plugin-sdk/helper/schema" - v3 "github.com/terraform-providers/terraform-provider-nutanix/client/v3" - "github.com/terraform-providers/terraform-provider-nutanix/utils" "log" "strconv" "strings" + + "github.com/hashicorp/terraform-plugin-sdk/helper/schema" + v3 "github.com/terraform-providers/terraform-provider-nutanix/client/v3" + "github.com/terraform-providers/terraform-provider-nutanix/utils" ) func resourceNutanixServiceGroup() *schema.Resource { From 8c5ab2fe483a827f66a68d58607c10975afe5596 Mon Sep 17 00:00:00 2001 From: Siddharth Kulshrestha Date: Wed, 9 Feb 2022 21:10:20 +0530 Subject: [PATCH 6/8] Fix in data_source_nutanix_address_groups schema. --- nutanix/data_source_nutanix_address_groups.go | 18 ++++++++++-------- 1 file changed, 10 insertions(+), 8 deletions(-) diff --git a/nutanix/data_source_nutanix_address_groups.go b/nutanix/data_source_nutanix_address_groups.go index 7bad40f2a..e45f76aea 100644 --- a/nutanix/data_source_nutanix_address_groups.go +++ b/nutanix/data_source_nutanix_address_groups.go @@ -57,8 +57,9 @@ func dataSourceNutanixAddressGroups() *schema.Resource { Elem: &schema.Resource{ Schema: map[string]*schema.Schema{ "address_group": { - Type: schema.TypeMap, + Type: schema.TypeList, Computed: true, + MaxItems: 1, Elem: &schema.Resource{ Schema: map[string]*schema.Schema{ "name": { @@ -93,7 +94,7 @@ func dataSourceNutanixAddressGroups() *schema.Resource { }, }, "associated_policies_list": { - Type: schema.TypeMap, + Type: schema.TypeList, Computed: true, Elem: &schema.Resource{ Schema: map[string]*schema.Schema{ @@ -147,15 +148,16 @@ func flattenAddressGroup(entries []*v3.AddressGroupListEntry) interface{} { for i, entry := range entries { entities[i] = map[string]interface{}{ - "address_group": map[string]interface{}{ - "name": entry.AddressGroup.Name, - "description": entry.AddressGroup.Description, - "address_group_string": entry.AddressGroup.AddressGroupString, - "ip_address_block_list": flattenAddressEntry(entry.AddressGroup.BlockList), + "address_group": []map[string]interface{}{ + { + "name": entry.AddressGroup.Name, + "description": entry.AddressGroup.Description, + "address_group_string": entry.AddressGroup.AddressGroupString, + "ip_address_block_list": flattenAddressEntry(entry.AddressGroup.BlockList), + }, }, "associated_policies_list": flattenReferenceList(entry.AssociatedPoliciesList), } } - return entities } From af8c43a65520b4763ee7b29778e9181ec65efa93 Mon Sep 17 00:00:00 2001 From: Siddharth Kulshrestha Date: Thu, 10 Feb 2022 12:15:29 +0530 Subject: [PATCH 7/8] Fix: data_source_nutanix_address_group marked name and description as computed and set values from backend API instead of asking from user. --- nutanix/data_source_nutanix_address_group.go | 17 +++++++++-------- 1 file changed, 9 insertions(+), 8 deletions(-) diff --git a/nutanix/data_source_nutanix_address_group.go b/nutanix/data_source_nutanix_address_group.go index b0e1adcaf..0ed8cf97d 100644 --- a/nutanix/data_source_nutanix_address_group.go +++ b/nutanix/data_source_nutanix_address_group.go @@ -5,6 +5,7 @@ import ( "strings" "github.com/hashicorp/terraform-plugin-sdk/helper/schema" + "github.com/terraform-providers/terraform-provider-nutanix/utils" ) func dataSourceNutanixAddressGroup() *schema.Resource { @@ -21,11 +22,11 @@ func dataSourceNutanixAddressGroup() *schema.Resource { }, "description": { Type: schema.TypeString, - Optional: true, + Computed: true, }, "ip_address_block_list": { Type: schema.TypeList, - Required: true, + Computed: true, Elem: &schema.Resource{ Schema: map[string]*schema.Schema{ "ip": { @@ -60,16 +61,16 @@ func dataSourceNutanixAddressGroupRead(d *schema.ResourceData, meta interface{}) return fmt.Errorf("error reading user with error %s", reqErr) } - if name, nameOk := d.GetOk("name"); nameOk { - d.Set("name", name.(string)) + if err := d.Set("name", utils.StringValue(group.AddressGroup.Name)); err != nil { + return err } - if desc, descOk := d.GetOk("description"); descOk { - d.Set("description", desc.(string)) + if err := d.Set("description", utils.StringValue(group.AddressGroup.Description)); err != nil { + return err } - if str, strOk := d.GetOk("address_group_string"); strOk { - d.Set("address_group_string", str.(string)) + if err := d.Set("address_group_string", utils.StringValue(group.AddressGroup.AddressGroupString)); err != nil { + return err } if err := d.Set("ip_address_block_list", flattenAddressEntry(group.AddressGroup.BlockList)); err != nil { From 3040cc0ea63ff17b20c8d6335db01be2725c654b Mon Sep 17 00:00:00 2001 From: Abhishek Date: Thu, 10 Feb 2022 20:22:00 +0530 Subject: [PATCH 8/8] Adding service group and Address group testcases --- .../data_source_nutanix_address_group_test.go | 45 ++++++++++ ...data_source_nutanix_address_groups_test.go | 49 +++++++++++ .../resource_nutanix_address_group_test.go | 76 +++++++++++++++++ .../resource_nutanix_service_group_test.go | 84 +++++++++++++++++++ 4 files changed, 254 insertions(+) create mode 100644 nutanix/data_source_nutanix_address_group_test.go create mode 100644 nutanix/data_source_nutanix_address_groups_test.go create mode 100644 nutanix/resource_nutanix_address_group_test.go create mode 100644 nutanix/resource_nutanix_service_group_test.go diff --git a/nutanix/data_source_nutanix_address_group_test.go b/nutanix/data_source_nutanix_address_group_test.go new file mode 100644 index 000000000..12c9a0acd --- /dev/null +++ b/nutanix/data_source_nutanix_address_group_test.go @@ -0,0 +1,45 @@ +package nutanix + +import ( + "fmt" + "testing" + + "github.com/hashicorp/terraform-plugin-sdk/helper/acctest" + "github.com/hashicorp/terraform-plugin-sdk/helper/resource" +) + +func TestAccNutanixAddressGroupDataSource_basic(t *testing.T) { + rInt := acctest.RandInt() + resource.Test(t, resource.TestCase{ + PreCheck: func() { testAccPreCheck(t) }, + Providers: testAccProviders, + Steps: []resource.TestStep{ + { + Config: testAccAddressGroupDataSourceConfig(rInt), + Check: resource.ComposeTestCheckFunc( + resource.TestCheckResourceAttr("data.nutanix_address_group.addr_group", "ip_address_block_list.#", "1"), + resource.TestCheckResourceAttr("data.nutanix_address_group.addr_group", "ip_address_block_list.0.prefix_length", "24"), + resource.TestCheckResourceAttr("data.nutanix_address_group.addr_group", "description", "test address group resource"), + ), + }, + }, + }) +} + +func testAccAddressGroupDataSourceConfig(r int) string { + return fmt.Sprintf(` + resource "nutanix_address_group" "test_address" { + name = "test-%[1]d" + description = "test address group resource" + + ip_address_block_list { + ip = "10.0.0.0" + prefix_length = 24 + } + } + + data "nutanix_address_group" "addr_group" { + uuid = "${nutanix_address_group.test_address.id}" + } + `, r) +} diff --git a/nutanix/data_source_nutanix_address_groups_test.go b/nutanix/data_source_nutanix_address_groups_test.go new file mode 100644 index 000000000..940e2e970 --- /dev/null +++ b/nutanix/data_source_nutanix_address_groups_test.go @@ -0,0 +1,49 @@ +package nutanix + +import ( + "fmt" + "testing" + + "github.com/hashicorp/terraform-plugin-sdk/helper/acctest" + "github.com/hashicorp/terraform-plugin-sdk/helper/resource" +) + +func TestAccNutanixAddressGroupsDataSource_basic(t *testing.T) { + rInt := acctest.RandInt() + + resource.Test(t, resource.TestCase{ + PreCheck: func() { testAccPreCheck(t) }, + Providers: testAccProviders, + Steps: []resource.TestStep{ + { + Config: testAccAddressGroupsDataSourceConfig(rInt), + Check: resource.ComposeTestCheckFunc( + resource.TestCheckResourceAttr("data.nutanix_address_group.addr_group", "ip_address_block_list.#", "1"), + resource.TestCheckResourceAttr("data.nutanix_address_group.addr_group", "description", "test address groups resource"), + resource.TestCheckResourceAttr("data.nutanix_address_groups.addr_groups", "entities.#", "1"), + resource.TestCheckResourceAttr("data.nutanix_address_groups.addr_groups", "entities.0.address_group.#", "1"), + ), + }, + }, + }) +} + +func testAccAddressGroupsDataSourceConfig(r int) string { + return fmt.Sprintf(` + resource "nutanix_address_group" "test_address" { + name = "test-%[1]d" + description = "test address groups resource" + + ip_address_block_list { + ip = "10.0.0.0" + prefix_length = 24 + } + } + + data "nutanix_address_group" "addr_group" { + uuid = nutanix_address_group.test_address.id + } + + data "nutanix_address_groups" "addr_groups" {} + `, r) +} diff --git a/nutanix/resource_nutanix_address_group_test.go b/nutanix/resource_nutanix_address_group_test.go new file mode 100644 index 000000000..bbc33b77f --- /dev/null +++ b/nutanix/resource_nutanix_address_group_test.go @@ -0,0 +1,76 @@ +package nutanix + +import ( + "fmt" + "strings" + "testing" + "time" + + "github.com/hashicorp/terraform-plugin-sdk/helper/acctest" + "github.com/hashicorp/terraform-plugin-sdk/helper/resource" + "github.com/hashicorp/terraform-plugin-sdk/terraform" +) + +const resourcesAddressGroup = "nutanix_address_group.test" + +func TestAccNutanixAddressGroup(t *testing.T) { + name := acctest.RandomWithPrefix("nutanix_address_gr") + description := "this is nutanix address group" + + resource.Test(t, resource.TestCase{ + PreCheck: func() { testAccPreCheck(t) }, + Providers: testAccProviders, + CheckDestroy: testAccCheckNutanixAddressGroupDestroy, + Steps: []resource.TestStep{ + { + Config: testAccNutanixAddressGroupConfig(name, description), + Check: resource.ComposeTestCheckFunc( + resource.TestCheckResourceAttr(resourcesAddressGroup, "name", name), + resource.TestCheckResourceAttr(resourcesAddressGroup, "description", description), + resource.TestCheckResourceAttr(resourcesAddressGroup, "ip_address_block_list.#", "1"), + resource.TestCheckResourceAttr(resourcesAddressGroup, "ip_address_block_list.0.prefix_length", "24"), + ), + }, + { + ResourceName: resourcesAddressGroup, + ImportState: true, + ImportStateVerify: true, + }, + }, + }) +} + +func testAccCheckNutanixAddressGroupDestroy(s *terraform.State) error { + conn := testAccProvider.Meta().(*Client) + + for _, rs := range s.RootModule().Resources { + if rs.Type != "nutanix_address_grp" { + continue + } + for { + _, err := conn.API.V3.GetVM(rs.Primary.ID) + if err != nil { + if strings.Contains(fmt.Sprint(err), "ENTITY_NOT_FOUND") { + return nil + } + return err + } + time.Sleep(3000 * time.Millisecond) + } + } + + return nil +} + +func testAccNutanixAddressGroupConfig(name, description string) string { + return fmt.Sprintf(` + resource "nutanix_address_group" "test" { + name = "%[1]s" + description = "%[2]s" + ip_address_block_list { + ip = "10.0.0.0" + prefix_length = 24 + } + } +`, name, description) +} diff --git a/nutanix/resource_nutanix_service_group_test.go b/nutanix/resource_nutanix_service_group_test.go new file mode 100644 index 000000000..ef17d203a --- /dev/null +++ b/nutanix/resource_nutanix_service_group_test.go @@ -0,0 +1,84 @@ +package nutanix + +import ( + "fmt" + "strings" + "testing" + "time" + + "github.com/hashicorp/terraform-plugin-sdk/helper/acctest" + "github.com/hashicorp/terraform-plugin-sdk/helper/resource" + "github.com/hashicorp/terraform-plugin-sdk/terraform" +) + +const resourceServiceGroup = "nutanix_service_group.test" + +func TestAccNutanixServiceGroup(t *testing.T) { + name := acctest.RandomWithPrefix("nutanix_service_gr") + description := "this is nutanix service group" + + resource.Test(t, resource.TestCase{ + PreCheck: func() { testAccPreCheck(t) }, + Providers: testAccProviders, + CheckDestroy: testAccCheckNutanixServiceGroupDestroy, + Steps: []resource.TestStep{ + { + Config: testAccNutanixServiceGroupConfig(name, description), + Check: resource.ComposeTestCheckFunc( + resource.TestCheckResourceAttr(resourceServiceGroup, "name", name), + resource.TestCheckResourceAttr(resourceServiceGroup, "description", description), + resource.TestCheckResourceAttr(resourceServiceGroup, "service_list.#", "1"), + resource.TestCheckResourceAttr(resourceServiceGroup, "service_list.0.protocol", "TCP"), + ), + }, + { + ResourceName: resourceServiceGroup, + ImportState: true, + ImportStateVerify: true, + }, + }, + }) +} + +func testAccCheckNutanixServiceGroupDestroy(s *terraform.State) error { + conn := testAccProvider.Meta().(*Client) + + for _, rs := range s.RootModule().Resources { + if rs.Type != "nutanix_service_grp" { + continue + } + for { + _, err := conn.API.V3.GetVM(rs.Primary.ID) + if err != nil { + if strings.Contains(fmt.Sprint(err), "ENTITY_NOT_FOUND") { + return nil + } + return err + } + time.Sleep(3000 * time.Millisecond) + } + } + + return nil +} + +func testAccNutanixServiceGroupConfig(name, description string) string { + return fmt.Sprintf(` + resource "nutanix_service_group" "test" { + name = "%[1]s" + description = "%[2]s" + + service_list { + protocol = "TCP" + tcp_port_range_list { + start_port = 22 + end_port = 22 + } + tcp_port_range_list { + start_port = 2222 + end_port = 2222 + } + } + } +`, name, description) +}