github.com/armen/terraform@v0.5.2-0.20150529052519-caa8117a08f1/builtin/providers/aws/resource_aws_route53_zone.go (about)

     1  package aws
     2  
     3  import (
     4  	"fmt"
     5  	"log"
     6  	"sort"
     7  	"strings"
     8  	"time"
     9  
    10  	"github.com/hashicorp/terraform/helper/resource"
    11  	"github.com/hashicorp/terraform/helper/schema"
    12  
    13  	"github.com/awslabs/aws-sdk-go/aws"
    14  	"github.com/awslabs/aws-sdk-go/aws/awserr"
    15  	"github.com/awslabs/aws-sdk-go/service/route53"
    16  )
    17  
    18  func resourceAwsRoute53Zone() *schema.Resource {
    19  	return &schema.Resource{
    20  		Create: resourceAwsRoute53ZoneCreate,
    21  		Read:   resourceAwsRoute53ZoneRead,
    22  		Update: resourceAwsRoute53ZoneUpdate,
    23  		Delete: resourceAwsRoute53ZoneDelete,
    24  
    25  		Schema: map[string]*schema.Schema{
    26  			"name": &schema.Schema{
    27  				Type:     schema.TypeString,
    28  				Required: true,
    29  				ForceNew: true,
    30  			},
    31  
    32  			"comment": &schema.Schema{
    33  				Type:     schema.TypeString,
    34  				Optional: true,
    35  				Default: "Managed by Terraform",
    36  			},
    37  
    38  			"vpc_id": &schema.Schema{
    39  				Type:     schema.TypeString,
    40  				Optional: true,
    41  				ForceNew: true,
    42  			},
    43  
    44  			"vpc_region": &schema.Schema{
    45  				Type:     schema.TypeString,
    46  				Optional: true,
    47  				ForceNew: true,
    48  				Computed: true,
    49  			},
    50  
    51  			"zone_id": &schema.Schema{
    52  				Type:     schema.TypeString,
    53  				Computed: true,
    54  			},
    55  
    56  			"name_servers": &schema.Schema{
    57  				Type:     schema.TypeList,
    58  				Elem:     &schema.Schema{Type: schema.TypeString},
    59  				Computed: true,
    60  			},
    61  
    62  			"tags": tagsSchema(),
    63  		},
    64  	}
    65  }
    66  
    67  func resourceAwsRoute53ZoneCreate(d *schema.ResourceData, meta interface{}) error {
    68  	r53 := meta.(*AWSClient).r53conn
    69  
    70  	req := &route53.CreateHostedZoneInput{
    71  		Name:             aws.String(d.Get("name").(string)),
    72  		HostedZoneConfig: &route53.HostedZoneConfig{Comment: aws.String(d.Get("comment").(string))},
    73  		CallerReference:  aws.String(time.Now().Format(time.RFC3339Nano)),
    74  	}
    75  	if v := d.Get("vpc_id"); v != "" {
    76  		req.VPC = &route53.VPC{
    77  			VPCID:     aws.String(v.(string)),
    78  			VPCRegion: aws.String(meta.(*AWSClient).region),
    79  		}
    80  		if w := d.Get("vpc_region"); w != "" {
    81  			req.VPC.VPCRegion = aws.String(w.(string))
    82  		}
    83  		d.Set("vpc_region", req.VPC.VPCRegion)
    84  	}
    85  
    86  	log.Printf("[DEBUG] Creating Route53 hosted zone: %s", *req.Name)
    87  	var err error
    88  	resp, err := r53.CreateHostedZone(req)
    89  	if err != nil {
    90  		return err
    91  	}
    92  
    93  	// Store the zone_id
    94  	zone := cleanZoneID(*resp.HostedZone.ID)
    95  	d.Set("zone_id", zone)
    96  	d.SetId(zone)
    97  
    98  	// Wait until we are done initializing
    99  	wait := resource.StateChangeConf{
   100  		Delay:      30 * time.Second,
   101  		Pending:    []string{"PENDING"},
   102  		Target:     "INSYNC",
   103  		Timeout:    10 * time.Minute,
   104  		MinTimeout: 2 * time.Second,
   105  		Refresh: func() (result interface{}, state string, err error) {
   106  			changeRequest := &route53.GetChangeInput{
   107  				ID: aws.String(cleanChangeID(*resp.ChangeInfo.ID)),
   108  			}
   109  			return resourceAwsGoRoute53Wait(r53, changeRequest)
   110  		},
   111  	}
   112  	_, err = wait.WaitForState()
   113  	if err != nil {
   114  		return err
   115  	}
   116  	return resourceAwsRoute53ZoneUpdate(d, meta)
   117  }
   118  
   119  func resourceAwsRoute53ZoneRead(d *schema.ResourceData, meta interface{}) error {
   120  	r53 := meta.(*AWSClient).r53conn
   121  	zone, err := r53.GetHostedZone(&route53.GetHostedZoneInput{ID: aws.String(d.Id())})
   122  	if err != nil {
   123  		// Handle a deleted zone
   124  		if r53err, ok := err.(awserr.Error); ok && r53err.Code() == "NoSuchHostedZone" {
   125  			d.SetId("")
   126  			return nil
   127  		}
   128  		return err
   129  	}
   130  
   131  	if !*zone.HostedZone.Config.PrivateZone {
   132  		ns := make([]string, len(zone.DelegationSet.NameServers))
   133  		for i := range zone.DelegationSet.NameServers {
   134  			ns[i] = *zone.DelegationSet.NameServers[i]
   135  		}
   136  		sort.Strings(ns)
   137  		if err := d.Set("name_servers", ns); err != nil {
   138  			return fmt.Errorf("[DEBUG] Error setting name servers for: %s, error: %#v", d.Id(), err)
   139  		}
   140  	} else {
   141  		ns, err := getNameServers(d.Id(), d.Get("name").(string), r53)
   142  		if err != nil {
   143  			return err
   144  		}
   145  		if err := d.Set("name_servers", ns); err != nil {
   146  			return fmt.Errorf("[DEBUG] Error setting name servers for: %s, error: %#v", d.Id(), err)
   147  		}
   148  
   149  		var associatedVPC *route53.VPC
   150  		for _, vpc := range zone.VPCs {
   151  			if *vpc.VPCID == d.Get("vpc_id") {
   152  				associatedVPC = vpc
   153  			}
   154  		}
   155  		if associatedVPC == nil {
   156  			return fmt.Errorf("[DEBUG] VPC: %v is not associated with Zone: %v", d.Get("vpc_id"), d.Id())
   157  		}
   158  	}
   159  
   160  	// get tags
   161  	req := &route53.ListTagsForResourceInput{
   162  		ResourceID:   aws.String(d.Id()),
   163  		ResourceType: aws.String("hostedzone"),
   164  	}
   165  
   166  	resp, err := r53.ListTagsForResource(req)
   167  	if err != nil {
   168  		return err
   169  	}
   170  
   171  	var tags []*route53.Tag
   172  	if resp.ResourceTagSet != nil {
   173  		tags = resp.ResourceTagSet.Tags
   174  	}
   175  
   176  	if err := d.Set("tags", tagsToMapR53(tags)); err != nil {
   177  		return err
   178  	}
   179  
   180  	return nil
   181  }
   182  
   183  func resourceAwsRoute53ZoneUpdate(d *schema.ResourceData, meta interface{}) error {
   184  	conn := meta.(*AWSClient).r53conn
   185  
   186  	if err := setTagsR53(conn, d); err != nil {
   187  		return err
   188  	} else {
   189  		d.SetPartial("tags")
   190  	}
   191  
   192  	return resourceAwsRoute53ZoneRead(d, meta)
   193  }
   194  
   195  func resourceAwsRoute53ZoneDelete(d *schema.ResourceData, meta interface{}) error {
   196  	r53 := meta.(*AWSClient).r53conn
   197  
   198  	log.Printf("[DEBUG] Deleting Route53 hosted zone: %s (ID: %s)",
   199  		d.Get("name").(string), d.Id())
   200  	_, err := r53.DeleteHostedZone(&route53.DeleteHostedZoneInput{ID: aws.String(d.Id())})
   201  	if err != nil {
   202  		return err
   203  	}
   204  
   205  	return nil
   206  }
   207  
   208  func resourceAwsGoRoute53Wait(r53 *route53.Route53, ref *route53.GetChangeInput) (result interface{}, state string, err error) {
   209  
   210  	status, err := r53.GetChange(ref)
   211  	if err != nil {
   212  		return nil, "UNKNOWN", err
   213  	}
   214  	return true, *status.ChangeInfo.Status, nil
   215  }
   216  
   217  // cleanChangeID is used to remove the leading /change/
   218  func cleanChangeID(ID string) string {
   219  	return cleanPrefix(ID, "/change/")
   220  }
   221  
   222  // cleanZoneID is used to remove the leading /hostedzone/
   223  func cleanZoneID(ID string) string {
   224  	return cleanPrefix(ID, "/hostedzone/")
   225  }
   226  
   227  // cleanPrefix removes a string prefix from an ID
   228  func cleanPrefix(ID, prefix string) string {
   229  	if strings.HasPrefix(ID, prefix) {
   230  		ID = strings.TrimPrefix(ID, prefix)
   231  	}
   232  	return ID
   233  }
   234  
   235  func getNameServers(zoneId string, zoneName string, r53 *route53.Route53) ([]string, error) {
   236  	resp, err := r53.ListResourceRecordSets(&route53.ListResourceRecordSetsInput{
   237  		HostedZoneID:    aws.String(zoneId),
   238  		StartRecordName: aws.String(zoneName),
   239  		StartRecordType: aws.String("NS"),
   240  	})
   241  	if err != nil {
   242  		return nil, err
   243  	}
   244  	if len(resp.ResourceRecordSets) == 0 {
   245  		return nil, nil
   246  	}
   247  	ns := make([]string, len(resp.ResourceRecordSets[0].ResourceRecords))
   248  	for i := range resp.ResourceRecordSets[0].ResourceRecords {
   249  		ns[i] = *resp.ResourceRecordSets[0].ResourceRecords[i].Value
   250  	}
   251  	sort.Strings(ns)
   252  	return ns, nil
   253  }