Skip to content

Commit

Permalink
added support for health reasons and health state in vpc dns bindings
Browse files Browse the repository at this point in the history
  • Loading branch information
uibm committed Mar 22, 2024
1 parent dc2f51a commit a5c7042
Show file tree
Hide file tree
Showing 14 changed files with 277 additions and 25 deletions.
2 changes: 1 addition & 1 deletion go.mod
Original file line number Diff line number Diff line change
Expand Up @@ -31,7 +31,7 @@ require (
github.com/IBM/schematics-go-sdk v0.2.3
github.com/IBM/secrets-manager-go-sdk/v2 v2.0.4
github.com/IBM/vpc-beta-go-sdk v0.6.0
github.com/IBM/vpc-go-sdk v0.48.0
github.com/IBM/vpc-go-sdk v0.49.1
github.com/ScaleFT/sshkeys v0.0.0-20200327173127-6142f742bca5
github.com/akamai/AkamaiOPEN-edgegrid-golang v1.2.2
github.com/akamai/AkamaiOPEN-edgegrid-golang/v5 v5.0.0
Expand Down
4 changes: 2 additions & 2 deletions go.sum
Original file line number Diff line number Diff line change
Expand Up @@ -174,8 +174,8 @@ github.com/IBM/secrets-manager-go-sdk/v2 v2.0.4 h1:xa9e+POVqaXxXHXkSMCOVAbKdUNEu
github.com/IBM/secrets-manager-go-sdk/v2 v2.0.4/go.mod h1:5gq8D8uWOIbqOm1uztay6lpOysgJaxxEsaVZLWGWb40=
github.com/IBM/vpc-beta-go-sdk v0.6.0 h1:wfM3AcW3zOM3xsRtZ+EA6+sESlGUjQ6Yf4n5QQyz4uc=
github.com/IBM/vpc-beta-go-sdk v0.6.0/go.mod h1:fzHDAQIqH/5yJmYsKodKHLcqxMDT+yfH6vZjdiw8CQA=
github.com/IBM/vpc-go-sdk v0.48.0 h1:4yeSxVX9mizsIW2F0rsVI47rZoNKBrZ1QK9RwwRas9Q=
github.com/IBM/vpc-go-sdk v0.48.0/go.mod h1:FDJpiokOmaYG2hNYDfqKVxUPe8mm/dPi3mdz8Zt4o/Q=
github.com/IBM/vpc-go-sdk v0.49.1 h1:VIkZ8iJMBHqBulUXcPtN0ifxsa0xwlBtaLslU2V9HsY=
github.com/IBM/vpc-go-sdk v0.49.1/go.mod h1:iBg9UJY1y/XpkweyP6YH7G6guzKPV8BYDoBMTdPupH4=
github.com/Jeffail/gabs v1.1.1 h1:V0uzR08Hj22EX8+8QMhyI9sX2hwRu+/RJhJUmnwda/E=
github.com/Jeffail/gabs v1.1.1/go.mod h1:6xMvQMK4k33lb7GUUpaAPh6nKMmemQeg5d4gn7/bOXc=
github.com/Logicalis/asn1 v0.0.0-20190312173541-d60463189a56 h1:vuquMR410psHNax14XKNWa0Ae/kYgWJcXi0IFuX60N0=
Expand Down
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
}
Loading

0 comments on commit a5c7042

Please sign in to comment.