github.com/vtorhonen/terraform@v0.9.0-beta2.0.20170307220345-5d894e4ffda7/builtin/providers/ultradns/resource_ultradns_tcpool.go (about)

     1  package ultradns
     2  
     3  import (
     4  	"fmt"
     5  	"log"
     6  	"strings"
     7  
     8  	"github.com/Ensighten/udnssdk"
     9  	"github.com/hashicorp/terraform/helper/schema"
    10  )
    11  
    12  func resourceUltradnsTcpool() *schema.Resource {
    13  	return &schema.Resource{
    14  		Create: resourceUltradnsTcpoolCreate,
    15  		Read:   resourceUltradnsTcpoolRead,
    16  		Update: resourceUltradnsTcpoolUpdate,
    17  		Delete: resourceUltradnsTcpoolDelete,
    18  
    19  		Schema: map[string]*schema.Schema{
    20  			// Required
    21  			"zone": &schema.Schema{
    22  				Type:     schema.TypeString,
    23  				Required: true,
    24  				ForceNew: true,
    25  			},
    26  			"name": &schema.Schema{
    27  				Type:     schema.TypeString,
    28  				Required: true,
    29  				ForceNew: true,
    30  			},
    31  			"description": &schema.Schema{
    32  				Type:     schema.TypeString,
    33  				Required: true,
    34  				// 0-255 char
    35  			},
    36  			"rdata": &schema.Schema{
    37  				Type:     schema.TypeSet,
    38  				Set:      hashRdatas,
    39  				Required: true,
    40  				// Valid: len(rdataInfo) == len(rdata)
    41  				Elem: &schema.Resource{
    42  					Schema: map[string]*schema.Schema{
    43  						// Required
    44  						"host": &schema.Schema{
    45  							Type:     schema.TypeString,
    46  							Required: true,
    47  						},
    48  						// Optional
    49  						"failover_delay": &schema.Schema{
    50  							Type:     schema.TypeInt,
    51  							Optional: true,
    52  							Default:  0,
    53  							// Valid: 0-30
    54  							// Units: Minutes
    55  						},
    56  						"priority": &schema.Schema{
    57  							Type:     schema.TypeInt,
    58  							Optional: true,
    59  							Default:  1,
    60  						},
    61  						"run_probes": &schema.Schema{
    62  							Type:     schema.TypeBool,
    63  							Optional: true,
    64  							Default:  true,
    65  						},
    66  						"state": &schema.Schema{
    67  							Type:     schema.TypeString,
    68  							Optional: true,
    69  							Default:  "NORMAL",
    70  						},
    71  						"threshold": &schema.Schema{
    72  							Type:     schema.TypeInt,
    73  							Optional: true,
    74  							Default:  1,
    75  						},
    76  						"weight": &schema.Schema{
    77  							Type:     schema.TypeInt,
    78  							Optional: true,
    79  							Default:  2,
    80  							// Valid: i%2 == 0 && 2 <= i <= 100
    81  						},
    82  					},
    83  				},
    84  			},
    85  			// Optional
    86  			"ttl": &schema.Schema{
    87  				Type:     schema.TypeInt,
    88  				Optional: true,
    89  				Default:  3600,
    90  			},
    91  			"run_probes": &schema.Schema{
    92  				Type:     schema.TypeBool,
    93  				Optional: true,
    94  				Default:  true,
    95  			},
    96  			"act_on_probes": &schema.Schema{
    97  				Type:     schema.TypeBool,
    98  				Optional: true,
    99  				Default:  true,
   100  			},
   101  			"max_to_lb": &schema.Schema{
   102  				Type:     schema.TypeInt,
   103  				Optional: true,
   104  				// Valid: 0 <= i <= len(rdata)
   105  			},
   106  			"backup_record_rdata": &schema.Schema{
   107  				Type:     schema.TypeString,
   108  				Optional: true,
   109  				// Valid: IPv4 address or CNAME
   110  			},
   111  			"backup_record_failover_delay": &schema.Schema{
   112  				Type:     schema.TypeInt,
   113  				Optional: true,
   114  				// Valid: 0-30
   115  				// Units: Minutes
   116  			},
   117  			// Computed
   118  			"hostname": &schema.Schema{
   119  				Type:     schema.TypeString,
   120  				Computed: true,
   121  			},
   122  		},
   123  	}
   124  }
   125  
   126  // CRUD Operations
   127  
   128  func resourceUltradnsTcpoolCreate(d *schema.ResourceData, meta interface{}) error {
   129  	client := meta.(*udnssdk.Client)
   130  
   131  	r, err := newRRSetResourceFromTcpool(d)
   132  	if err != nil {
   133  		return err
   134  	}
   135  
   136  	log.Printf("[INFO] ultradns_tcpool create: %#v", r)
   137  	_, err = client.RRSets.Create(r.RRSetKey(), r.RRSet())
   138  	if err != nil {
   139  		return fmt.Errorf("create failed: %#v -> %v", r, err)
   140  	}
   141  
   142  	d.SetId(r.ID())
   143  	log.Printf("[INFO] ultradns_tcpool.id: %v", d.Id())
   144  
   145  	return resourceUltradnsTcpoolRead(d, meta)
   146  }
   147  
   148  func resourceUltradnsTcpoolRead(d *schema.ResourceData, meta interface{}) error {
   149  	client := meta.(*udnssdk.Client)
   150  
   151  	rr, err := newRRSetResourceFromTcpool(d)
   152  	if err != nil {
   153  		return err
   154  	}
   155  
   156  	rrsets, err := client.RRSets.Select(rr.RRSetKey())
   157  	if err != nil {
   158  		uderr, ok := err.(*udnssdk.ErrorResponseList)
   159  		if ok {
   160  			for _, resps := range uderr.Responses {
   161  				// 70002 means Records Not Found
   162  				if resps.ErrorCode == 70002 {
   163  					d.SetId("")
   164  					return nil
   165  				}
   166  				return fmt.Errorf("resource not found: %v", err)
   167  			}
   168  		}
   169  		return fmt.Errorf("resource not found: %v", err)
   170  	}
   171  
   172  	r := rrsets[0]
   173  
   174  	zone := d.Get("zone")
   175  	// ttl
   176  	d.Set("ttl", r.TTL)
   177  	// hostname
   178  	if r.OwnerName == "" {
   179  		d.Set("hostname", zone)
   180  	} else {
   181  		if strings.HasSuffix(r.OwnerName, ".") {
   182  			d.Set("hostname", r.OwnerName)
   183  		} else {
   184  			d.Set("hostname", fmt.Sprintf("%s.%s", r.OwnerName, zone))
   185  		}
   186  	}
   187  
   188  	// And now... the Profile!
   189  	if r.Profile == nil {
   190  		return fmt.Errorf("RRSet.profile missing: invalid TCPool schema in: %#v", r)
   191  	}
   192  	p, err := r.Profile.TCPoolProfile()
   193  	if err != nil {
   194  		return fmt.Errorf("RRSet.profile could not be unmarshalled: %v\n", err)
   195  	}
   196  
   197  	// Set simple values
   198  	d.Set("description", p.Description)
   199  	d.Set("run_probes", p.RunProbes)
   200  	d.Set("act_on_probes", p.ActOnProbes)
   201  	d.Set("max_to_lb", p.MaxToLB)
   202  	if p.BackupRecord != nil {
   203  		d.Set("backup_record_rdata", p.BackupRecord.RData)
   204  		d.Set("backup_record_failover_delay", p.BackupRecord.FailoverDelay)
   205  	}
   206  
   207  	// TODO: rigorously test this to see if we can remove the error handling
   208  	err = d.Set("rdata", makeSetFromRdata(r.RData, p.RDataInfo))
   209  	if err != nil {
   210  		return fmt.Errorf("rdata set failed: %#v", err)
   211  	}
   212  	return nil
   213  }
   214  
   215  func resourceUltradnsTcpoolUpdate(d *schema.ResourceData, meta interface{}) error {
   216  	client := meta.(*udnssdk.Client)
   217  
   218  	r, err := newRRSetResourceFromTcpool(d)
   219  	if err != nil {
   220  		return err
   221  	}
   222  
   223  	log.Printf("[INFO] ultradns_tcpool update: %+v", r)
   224  	_, err = client.RRSets.Update(r.RRSetKey(), r.RRSet())
   225  	if err != nil {
   226  		return fmt.Errorf("resource update failed: %v", err)
   227  	}
   228  
   229  	return resourceUltradnsTcpoolRead(d, meta)
   230  }
   231  
   232  func resourceUltradnsTcpoolDelete(d *schema.ResourceData, meta interface{}) error {
   233  	client := meta.(*udnssdk.Client)
   234  
   235  	r, err := newRRSetResourceFromTcpool(d)
   236  	if err != nil {
   237  		return err
   238  	}
   239  
   240  	log.Printf("[INFO] ultradns_tcpool delete: %+v", r)
   241  	_, err = client.RRSets.Delete(r.RRSetKey())
   242  	if err != nil {
   243  		return fmt.Errorf("resource delete failed: %v", err)
   244  	}
   245  
   246  	return nil
   247  }
   248  
   249  // Resource Helpers
   250  
   251  func newRRSetResourceFromTcpool(d *schema.ResourceData) (rRSetResource, error) {
   252  	rDataRaw := d.Get("rdata").(*schema.Set).List()
   253  	r := rRSetResource{
   254  		// "The only valid rrtype value for SiteBacker or Traffic Controller pools is A"
   255  		// per https://portal.ultradns.com/static/docs/REST-API_User_Guide.pdf
   256  		RRType:    "A",
   257  		Zone:      d.Get("zone").(string),
   258  		OwnerName: d.Get("name").(string),
   259  		TTL:       d.Get("ttl").(int),
   260  		RData:     unzipRdataHosts(rDataRaw),
   261  	}
   262  
   263  	profile := udnssdk.TCPoolProfile{
   264  		Context:     udnssdk.TCPoolSchema,
   265  		ActOnProbes: d.Get("act_on_probes").(bool),
   266  		Description: d.Get("description").(string),
   267  		MaxToLB:     d.Get("max_to_lb").(int),
   268  		RunProbes:   d.Get("run_probes").(bool),
   269  		RDataInfo:   unzipRdataInfos(rDataRaw),
   270  	}
   271  
   272  	// Only send BackupRecord if present
   273  	br := d.Get("backup_record_rdata").(string)
   274  	if br != "" {
   275  		profile.BackupRecord = &udnssdk.BackupRecord{
   276  			RData:         d.Get("backup_record_rdata").(string),
   277  			FailoverDelay: d.Get("backup_record_failover_delay").(int),
   278  		}
   279  	}
   280  
   281  	rp := profile.RawProfile()
   282  	r.Profile = rp
   283  
   284  	return r, nil
   285  }
   286  
   287  func unzipRdataInfos(configured []interface{}) []udnssdk.SBRDataInfo {
   288  	rdataInfos := make([]udnssdk.SBRDataInfo, 0, len(configured))
   289  	for _, rRaw := range configured {
   290  		data := rRaw.(map[string]interface{})
   291  		r := udnssdk.SBRDataInfo{
   292  			FailoverDelay: data["failover_delay"].(int),
   293  			Priority:      data["priority"].(int),
   294  			RunProbes:     data["run_probes"].(bool),
   295  			State:         data["state"].(string),
   296  			Threshold:     data["threshold"].(int),
   297  			Weight:        data["weight"].(int),
   298  		}
   299  		rdataInfos = append(rdataInfos, r)
   300  	}
   301  	return rdataInfos
   302  }
   303  
   304  // collate and zip RData and RDataInfo into []map[string]interface{}
   305  func zipRData(rds []string, rdis []udnssdk.SBRDataInfo) []map[string]interface{} {
   306  	result := make([]map[string]interface{}, 0, len(rds))
   307  	for i, rdi := range rdis {
   308  		r := map[string]interface{}{
   309  			"host":           rds[i],
   310  			"failover_delay": rdi.FailoverDelay,
   311  			"priority":       rdi.Priority,
   312  			"run_probes":     rdi.RunProbes,
   313  			"state":          rdi.State,
   314  			"threshold":      rdi.Threshold,
   315  			"weight":         rdi.Weight,
   316  		}
   317  		result = append(result, r)
   318  	}
   319  	return result
   320  }
   321  
   322  // makeSetFromRdatas encodes an array of Rdata into a
   323  // *schema.Set in the appropriate structure for the schema
   324  func makeSetFromRdata(rds []string, rdis []udnssdk.SBRDataInfo) *schema.Set {
   325  	s := &schema.Set{F: hashRdatas}
   326  	rs := zipRData(rds, rdis)
   327  	for _, r := range rs {
   328  		s.Add(r)
   329  	}
   330  	return s
   331  }