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

Added support for healthstate and healthreason for vpc dns bindings #5222

Merged
merged 2 commits into from
Mar 22, 2024
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
70 changes: 63 additions & 7 deletions ibm/service/vpc/data_source_ibm_is_vpc_dns_resolution_binding.go
Original file line number Diff line number Diff line change
Expand Up @@ -35,6 +35,35 @@ func DataSourceIBMIsVPCDnsResolutionBinding() *schema.Resource {
Computed: true,
Description: "The date and time that the DNS resolution binding was created.",
},
"health_reasons": &schema.Schema{
Type: schema.TypeList,
Computed: true,
Description: "The reasons for the current `health_state` (if any).The enumerated reason code values for this property will expand in the future. When processing this property, check for and log unknown values. Optionally halt processing and surface the error, or bypass the resource on which the unexpected reason code was encountered.",
Elem: &schema.Resource{
Schema: map[string]*schema.Schema{
"code": &schema.Schema{
Type: schema.TypeString,
Computed: true,
Description: "A snake case string succinctly identifying the reason for this health state.",
},
"message": &schema.Schema{
Type: schema.TypeString,
Computed: true,
Description: "An explanation of the reason for this health state.",
},
"more_info": &schema.Schema{
Type: schema.TypeString,
Computed: true,
Description: "Link to documentation about the reason for this health state.",
},
},
},
},
"health_state": &schema.Schema{
Type: schema.TypeString,
Computed: true,
Description: "The health of this resource.- `ok`: No abnormal behavior detected- `degraded`: Experiencing compromised performance, capacity, or connectivity- `faulted`: Completely unreachable, inoperative, or otherwise entirely incapacitated- `inapplicable`: The health state does not apply because of the current lifecycle state. A resource with a lifecycle state of `failed` or `deleting` will have a health state of `inapplicable`. A `pending` resource may also have this state.",
},
"endpoint_gateways": &schema.Schema{
Type: schema.TypeList,
Computed: true,
Expand Down Expand Up @@ -241,7 +270,7 @@ func dataSourceIBMIsVPCDnsResolutionBindingRead(context context.Context, d *sche
d.SetId(*vpcdnsResolutionBinding.ID)

if err = d.Set("created_at", flex.DateTimeToString(vpcdnsResolutionBinding.CreatedAt)); err != nil {
return diag.FromErr(fmt.Errorf("Error setting created_at: %s", err))
return diag.FromErr(fmt.Errorf("[ERROR] Error setting created_at: %s", err))
}

endpointGateways := []map[string]interface{}{}
Expand All @@ -255,23 +284,40 @@ func dataSourceIBMIsVPCDnsResolutionBindingRead(context context.Context, d *sche
}
}
if err = d.Set("endpoint_gateways", endpointGateways); err != nil {
return diag.FromErr(fmt.Errorf("Error setting endpoint_gateways %s", err))
return diag.FromErr(fmt.Errorf("[ERROR] Error setting endpoint_gateways %s", err))
}

if err = d.Set("href", vpcdnsResolutionBinding.Href); err != nil {
return diag.FromErr(fmt.Errorf("Error setting href: %s", err))
return diag.FromErr(fmt.Errorf("[ERROR] Error setting href: %s", err))
}

if err = d.Set("lifecycle_state", vpcdnsResolutionBinding.LifecycleState); err != nil {
return diag.FromErr(fmt.Errorf("Error setting lifecycle_state: %s", err))
return diag.FromErr(fmt.Errorf("[ERROR] Error setting lifecycle_state: %s", err))
}

if err = d.Set("name", vpcdnsResolutionBinding.Name); err != nil {
return diag.FromErr(fmt.Errorf("Error setting name: %s", err))
return diag.FromErr(fmt.Errorf("[ERROR] Error setting name: %s", err))
}

if err = d.Set("resource_type", vpcdnsResolutionBinding.ResourceType); err != nil {
return diag.FromErr(fmt.Errorf("Error setting resource_type: %s", err))
return diag.FromErr(fmt.Errorf("[ERROR] Error setting resource_type: %s", err))
}
healthReasons := []map[string]interface{}{}
if vpcdnsResolutionBinding.HealthReasons != nil {
for _, modelItem := range vpcdnsResolutionBinding.HealthReasons {
modelMap, err := dataSourceIBMIsVPCDnsResolutionBindingVpcdnsResolutionBindingHealthReasonToMap(&modelItem)
if err != nil {
return diag.FromErr(err)
}
healthReasons = append(healthReasons, modelMap)
}
}
if err = d.Set("health_reasons", healthReasons); err != nil {
return diag.FromErr(fmt.Errorf("[ERROR] Error setting health_reasons %s", err))
}

if err = d.Set("health_state", vpcdnsResolutionBinding.HealthState); err != nil {
return diag.FromErr(fmt.Errorf("[ERROR] Error setting health_state: %s", err))
}

vpc := []map[string]interface{}{}
Expand All @@ -283,7 +329,7 @@ func dataSourceIBMIsVPCDnsResolutionBindingRead(context context.Context, d *sche
vpc = append(vpc, modelMap)
}
if err = d.Set("vpc", vpc); err != nil {
return diag.FromErr(fmt.Errorf("Error setting vpc %s", err))
return diag.FromErr(fmt.Errorf("[ERROR] Error setting vpc %s", err))
}

return nil
Expand Down Expand Up @@ -374,3 +420,13 @@ func dataSourceIBMIsVPCDnsResolutionBindingVPCRemoteToMap(model *vpcv1.VPCRemote
}
return modelMap, nil
}

func dataSourceIBMIsVPCDnsResolutionBindingVpcdnsResolutionBindingHealthReasonToMap(model *vpcv1.VpcdnsResolutionBindingHealthReason) (map[string]interface{}, error) {
modelMap := make(map[string]interface{})
modelMap["code"] = model.Code
modelMap["message"] = model.Message
if model.MoreInfo != nil {
modelMap["more_info"] = model.MoreInfo
}
return modelMap, nil
}
Original file line number Diff line number Diff line change
Expand Up @@ -138,6 +138,35 @@ func DataSourceIBMIsVPCDnsResolutionBindings() *schema.Resource {
Computed: true,
Description: "The URL for this DNS resolution binding.",
},
"health_reasons": &schema.Schema{
Type: schema.TypeList,
Computed: true,
Description: "The reasons for the current `health_state` (if any).The enumerated reason code values for this property will expand in the future. When processing this property, check for and log unknown values. Optionally halt processing and surface the error, or bypass the resource on which the unexpected reason code was encountered.",
Elem: &schema.Resource{
Schema: map[string]*schema.Schema{
"code": &schema.Schema{
Type: schema.TypeString,
Computed: true,
Description: "A snake case string succinctly identifying the reason for this health state.",
},
"message": &schema.Schema{
Type: schema.TypeString,
Computed: true,
Description: "An explanation of the reason for this health state.",
},
"more_info": &schema.Schema{
Type: schema.TypeString,
Computed: true,
Description: "Link to documentation about the reason for this health state.",
},
},
},
},
"health_state": &schema.Schema{
Type: schema.TypeString,
Computed: true,
Description: "The health of this resource.- `ok`: No abnormal behavior detected- `degraded`: Experiencing compromised performance, capacity, or connectivity- `faulted`: Completely unreachable, inoperative, or otherwise entirely incapacitated- `inapplicable`: The health state does not apply because of the current lifecycle state. A resource with a lifecycle state of `failed` or `deleting` will have a health state of `inapplicable`. A `pending` resource may also have this state.",
},
isVPCDnsResolutionBindingLifecycleState: &schema.Schema{
Type: schema.TypeString,
Computed: true,
Expand Down Expand Up @@ -300,7 +329,18 @@ func dataSourceIBMIsVPCDnsResolutionBindingsRead(context context.Context, d *sch
l[isVPCDnsResolutionBindingHref] = dns.Href

l[isVPCDnsResolutionBindingResourceType] = dns.ResourceType

healthReasons := []map[string]interface{}{}
if dns.HealthReasons != nil {
for _, modelItem := range dns.HealthReasons {
modelMap, err := dataSourceIBMIsVPCDnsResolutionBindingVpcdnsResolutionBindingHealthReasonToMap(&modelItem)
if err != nil {
return diag.FromErr(err)
}
healthReasons = append(healthReasons, modelMap)
}
}
l["health_reasons"] = healthReasons
l["health_state"] = dns.HealthState
vpc := []map[string]interface{}{}
if dns.VPC != nil {
modelMap, err := dataSourceIBMIsVPCDnsResolutionBindingVPCReferenceRemoteToMap(dns.VPC)
Expand Down
151 changes: 147 additions & 4 deletions ibm/service/vpc/resource_ibm_is_vpc_dns_resolution_binding.go
Original file line number Diff line number Diff line change
Expand Up @@ -8,8 +8,10 @@ import (
"fmt"
"log"
"strings"
"time"

"github.com/hashicorp/terraform-plugin-sdk/v2/diag"
"github.com/hashicorp/terraform-plugin-sdk/v2/helper/resource"
"github.com/hashicorp/terraform-plugin-sdk/v2/helper/schema"

"github.com/IBM-Cloud/terraform-provider-ibm/ibm/flex"
Expand All @@ -23,6 +25,10 @@ func ResourceIBMIsVPCDnsResolutionBinding() *schema.Resource {
UpdateContext: resourceIBMIsVPCDnsResolutionBindingUpdate,
DeleteContext: resourceIBMIsVPCDnsResolutionBindingDelete,
Importer: &schema.ResourceImporter{},
Timeouts: &schema.ResourceTimeout{
Create: schema.DefaultTimeout(10 * time.Minute),
Delete: schema.DefaultTimeout(10 * time.Minute),
},

Schema: map[string]*schema.Schema{
"vpc_id": &schema.Schema{
Expand All @@ -35,6 +41,35 @@ func ResourceIBMIsVPCDnsResolutionBinding() *schema.Resource {
Computed: true,
Description: "The date and time that the DNS resolution binding was created.",
},
"health_reasons": &schema.Schema{
Type: schema.TypeList,
Computed: true,
Description: "The reasons for the current `health_state` (if any).The enumerated reason code values for this property will expand in the future. When processing this property, check for and log unknown values. Optionally halt processing and surface the error, or bypass the resource on which the unexpected reason code was encountered.",
Elem: &schema.Resource{
Schema: map[string]*schema.Schema{
"code": &schema.Schema{
Type: schema.TypeString,
Computed: true,
Description: "A snake case string succinctly identifying the reason for this health state.",
},
"message": &schema.Schema{
Type: schema.TypeString,
Computed: true,
Description: "An explanation of the reason for this health state.",
},
"more_info": &schema.Schema{
Type: schema.TypeString,
Computed: true,
Description: "Link to documentation about the reason for this health state.",
},
},
},
},
"health_state": &schema.Schema{
Type: schema.TypeString,
Computed: true,
Description: "The health of this resource.- `ok`: No abnormal behavior detected- `degraded`: Experiencing compromised performance, capacity, or connectivity- `faulted`: Completely unreachable, inoperative, or otherwise entirely incapacitated- `inapplicable`: The health state does not apply because of the current lifecycle state. A resource with a lifecycle state of `failed` or `deleting` will have a health state of `inapplicable`. A `pending` resource may also have this state.",
},
"endpoint_gateways": &schema.Schema{
Type: schema.TypeList,
Computed: true,
Expand Down Expand Up @@ -268,11 +303,20 @@ func resourceIBMIsVPCDnsResolutionBindingCreate(context context.Context, d *sche
return diag.FromErr(fmt.Errorf("CreateVPCDnsResolutionBindingWithContext failed %s\n%s", err, response))
}
d.SetId(MakeTerraformVPCDNSID(spokeVPCID, *vpcdnsResolutionBinding.ID))

err = resourceIBMIsVPCDnsResolutionBindingGet(vpcdnsResolutionBinding, d)
intf, err := isWaitForVpcDnsCreated(sess, spokeVPCID, *vpcdnsResolutionBinding.ID, d.Timeout(schema.TimeoutCreate))
if err != nil {
return diag.FromErr(err)
}

if vpcdnsResolutionBinding, ok := intf.(*vpcv1.VpcdnsResolutionBinding); ok {
err = resourceIBMIsVPCDnsResolutionBindingGet(vpcdnsResolutionBinding, d)
if err != nil {
return diag.FromErr(err)
}
} else {
return resourceIBMIsVPCDnsResolutionBindingRead(context, d, meta)
}

return nil
}
func resourceIBMIsVPCDnsResolutionBindingRead(context context.Context, d *schema.ResourceData, meta interface{}) diag.Diagnostics {
Expand Down Expand Up @@ -331,7 +375,20 @@ func resourceIBMIsVPCDnsResolutionBindingGet(vpcdnsResolutionBinding *vpcv1.Vpcd
if err := d.Set("lifecycle_state", vpcdnsResolutionBinding.LifecycleState); err != nil {
return fmt.Errorf("[ERROR] Error setting lifecycle_state: %s", err)
}

healthReasons := []map[string]interface{}{}
for _, healthReasonsItem := range vpcdnsResolutionBinding.HealthReasons {
healthReasonsItemMap, err := resourceIBMIsVPCDnsResolutionBindingVpcdnsResolutionBindingHealthReasonToMap(&healthReasonsItem)
if err != nil {
return err
}
healthReasons = append(healthReasons, healthReasonsItemMap)
}
if err := d.Set("health_reasons", healthReasons); err != nil {
return fmt.Errorf("[ERROR] Error setting health_reasons: %s", err)
}
if err := d.Set("health_state", vpcdnsResolutionBinding.HealthState); err != nil {
return fmt.Errorf("[ERROR] Error setting health_state: %s", err)
}
if err := d.Set("name", vpcdnsResolutionBinding.Name); err != nil {
return fmt.Errorf("[ERROR] Error setting name: %s", err)
}
Expand Down Expand Up @@ -404,13 +461,17 @@ func resourceIBMIsVPCDnsResolutionBindingDelete(context context.Context, d *sche
deleteVPCDnsResolutionBindingOptions.SetVPCID(vpcId)
deleteVPCDnsResolutionBindingOptions.SetID(id)

_, response, err := sess.DeleteVPCDnsResolutionBindingWithContext(context, deleteVPCDnsResolutionBindingOptions)
dns, response, err := sess.DeleteVPCDnsResolutionBindingWithContext(context, deleteVPCDnsResolutionBindingOptions)
if err != nil {
log.Printf("[DEBUG] DeleteVPCDnsResolutionBindingWithContext failed %s\n%s", err, response)
if response.StatusCode != 404 {
return diag.FromErr(fmt.Errorf("DeleteVPCDnsResolutionBindingWithContext failed %s\n%s", err, response))
}
}
_, err = isWaitForVpcDnsDeleted(sess, vpcId, id, d.Timeout(schema.TimeoutDelete), dns)
if err != nil {
return diag.FromErr(err)
}
d.SetId("")
return nil
}
Expand All @@ -430,3 +491,85 @@ func ParseVPCDNSTerraformID(s string) (string, string, error) {
}
return segments[0], segments[1], nil
}

func isWaitForVpcDnsDeleted(sess *vpcv1.VpcV1, vpcid, id string, timeout time.Duration, dns *vpcv1.VpcdnsResolutionBinding) (interface{}, error) {
log.Printf("Waiting for vpc dns (%s) to be deleted.", id)

stateConf := &resource.StateChangeConf{
Pending: []string{"deleting", "pending", "updating", "waiting"},
Target: []string{"stable", "failed", "suspended", ""},
Refresh: isVpcDnsDeleteRefreshFunc(sess, vpcid, id, dns),
Timeout: timeout,
Delay: 10 * time.Second,
MinTimeout: 10 * time.Second,
}

return stateConf.WaitForState()
}

func isVpcDnsDeleteRefreshFunc(sess *vpcv1.VpcV1, vpcid, id string, dns *vpcv1.VpcdnsResolutionBinding) resource.StateRefreshFunc {
return func() (interface{}, string, error) {
getVPCDnsResolutionBindingOptions := &vpcv1.GetVPCDnsResolutionBindingOptions{}

getVPCDnsResolutionBindingOptions.SetVPCID(vpcid)
getVPCDnsResolutionBindingOptions.SetID(id)

vpcdnsResolutionBinding, response, err := sess.GetVPCDnsResolutionBinding(getVPCDnsResolutionBindingOptions)
if vpcdnsResolutionBinding == nil {
vpcdnsResolutionBinding = dns
}
if err != nil {
if response != nil && response.StatusCode == 404 {
return vpcdnsResolutionBinding, "", nil
}
return vpcdnsResolutionBinding, "", fmt.Errorf("[ERROR] Error getting vpcdnsResolutionBinding: %s\n%s", err, response)
}
return vpcdnsResolutionBinding, *vpcdnsResolutionBinding.LifecycleState, err
}
}

func isWaitForVpcDnsCreated(sess *vpcv1.VpcV1, vpcid, id string, timeout time.Duration) (interface{}, error) {
log.Printf("Waiting for vpc dns (%s) to be created.", id)

stateConf := &resource.StateChangeConf{
Pending: []string{"deleting", "pending", "updating", "waiting"},
Target: []string{"stable", "failed", "suspended", ""},
Refresh: isVpcDnsCreateRefreshFunc(sess, vpcid, id),
Timeout: timeout,
Delay: 10 * time.Second,
MinTimeout: 10 * time.Second,
}

return stateConf.WaitForState()
}

func isVpcDnsCreateRefreshFunc(sess *vpcv1.VpcV1, vpcid, id string) resource.StateRefreshFunc {
return func() (interface{}, string, error) {
getVPCDnsResolutionBindingOptions := &vpcv1.GetVPCDnsResolutionBindingOptions{}

getVPCDnsResolutionBindingOptions.SetVPCID(vpcid)
getVPCDnsResolutionBindingOptions.SetID(id)

vpcdnsResolutionBinding, response, err := sess.GetVPCDnsResolutionBinding(getVPCDnsResolutionBindingOptions)
if err != nil {
if response != nil && response.StatusCode == 404 {
return vpcdnsResolutionBinding, "", nil
}
return vpcdnsResolutionBinding, "", fmt.Errorf("[ERROR] Error getting vpcdnsResolutionBinding: %s\n%s", err, response)
}
if *vpcdnsResolutionBinding.LifecycleState == "failed" || *vpcdnsResolutionBinding.LifecycleState == "suspended" {
return vpcdnsResolutionBinding, "", fmt.Errorf("[ERROR] DnsResolutionBinding in %s state", *vpcdnsResolutionBinding.LifecycleState)
}
return vpcdnsResolutionBinding, *vpcdnsResolutionBinding.LifecycleState, err
}
}

func resourceIBMIsVPCDnsResolutionBindingVpcdnsResolutionBindingHealthReasonToMap(model *vpcv1.VpcdnsResolutionBindingHealthReason) (map[string]interface{}, error) {
modelMap := make(map[string]interface{})
modelMap["code"] = model.Code
modelMap["message"] = model.Message
if model.MoreInfo != nil {
modelMap["more_info"] = model.MoreInfo
}
return modelMap, nil
}
1 change: 0 additions & 1 deletion website/docs/d/is_virtual_endpoint_gateway.html.markdown
Original file line number Diff line number Diff line change
Expand Up @@ -38,7 +38,6 @@ In addition to the argument reference list, you can access the following attribu

- `access_tags` - (List) Access management tags associated for the virtual endpoint gateway.

-> **NOTE:** `allow_dns_resolution_binding` attribute is a select location availability, invitation only feature. In other regions value might not be present.
- `allow_dns_resolution_binding` - (Bool) Indicates whether to allow this endpoint gateway to participate in DNS resolution bindings with a VPC that has dns.enable_hub set to true.
- `created_at` - (Timestamp) The created date and time of the endpoint gateway.
- `crn` - (String) The CRN for this endpoint gateway.
Expand Down
1 change: 0 additions & 1 deletion website/docs/d/is_virtual_endpoint_gateways.html.markdown
Original file line number Diff line number Diff line change
Expand Up @@ -42,7 +42,6 @@ In addition to the argument reference list, you can access the following attribu
Nested scheme for `virtual_endpoint_gateways`:
- `access_tags` - (List) Access management tags associated for the virtual endpoint gateway.

-> **NOTE:** `allow_dns_resolution_binding` attribute is a select location availability, invitation only feature. In other regions value might not be present.
- `allow_dns_resolution_binding` - (Bool) Indicates whether to allow this endpoint gateway to participate in DNS resolution bindings with a VPC that has dns.enable_hub set to true.
- `created_at` - (Timestamp) The created date and time of the endpoint gateway.
- `crn` - (String) The CRN for this endpoint gateway.
Expand Down
Loading
Loading