// setTags is a helper to set the tags for a resource. It expects the
// tags field to be named "tags"
func setTagsR53(conn *route53.Route53, d *schema.ResourceData, resourceType string) error {
	if d.HasChange("tags") {
		oraw, nraw := d.GetChange("tags")
		o := oraw.(map[string]interface{})
		n := nraw.(map[string]interface{})
		create, remove := diffTagsR53(tagsFromMapR53(o), tagsFromMapR53(n))

		// Set tags
		r := make([]*string, len(remove))
		for i, t := range remove {
			r[i] = t.Key
		}
		log.Printf("[DEBUG] Changing tags: \n\tadding: %#v\n\tremoving:%#v", create, remove)
		req := &route53.ChangeTagsForResourceInput{
			ResourceId:   aws.String(d.Id()),
			ResourceType: aws.String(resourceType),
		}

		if len(create) > 0 {
			req.AddTags = create
		}
		if len(r) > 0 {
			req.RemoveTagKeys = r
		}

		_, err := conn.ChangeTagsForResource(req)
		if err != nil {
			return err
		}
	}

	return nil
}
Example #2
0
func cleanupDomain(r53 *route53.Route53, id string) {
	// delete all non-default SOA/NS records
	rrsets, err := cli53.ListAllRecordSets(r53, id)
	fatalIfErr(err)
	changes := []*route53.Change{}
	for _, rrset := range rrsets {
		if *rrset.Type != "NS" && *rrset.Type != "SOA" {
			change := &route53.Change{
				Action:            aws.String("DELETE"),
				ResourceRecordSet: rrset,
			}
			changes = append(changes, change)
		}
	}

	if len(changes) > 0 {
		req2 := route53.ChangeResourceRecordSetsInput{
			HostedZoneId: &id,
			ChangeBatch: &route53.ChangeBatch{
				Changes: changes,
			},
		}
		_, err = r53.ChangeResourceRecordSets(&req2)
		if err != nil {
			fmt.Printf("Warning: cleanup failed - %s\n", err)
		}
	}

	req3 := route53.DeleteHostedZoneInput{Id: &id}
	_, err = r53.DeleteHostedZone(&req3)
	if err != nil {
		fmt.Printf("Warning: cleanup failed - %s\n", err)
	}
}
func deleteRoute53RecordSet(conn *route53.Route53, input *route53.ChangeResourceRecordSetsInput) (interface{}, error) {
	wait := resource.StateChangeConf{
		Pending:    []string{"rejected"},
		Target:     []string{"accepted"},
		Timeout:    5 * time.Minute,
		MinTimeout: 1 * time.Second,
		Refresh: func() (interface{}, string, error) {
			resp, err := conn.ChangeResourceRecordSets(input)
			if err != nil {
				if r53err, ok := err.(awserr.Error); ok {
					if r53err.Code() == "PriorRequestNotComplete" {
						// There is some pending operation, so just retry
						// in a bit.
						return 42, "rejected", nil
					}

					if r53err.Code() == "InvalidChangeBatch" {
						// This means that the record is already gone.
						return resp, "accepted", nil
					}
				}

				return 42, "failure", err
			}

			return resp, "accepted", nil
		},
	}

	return wait.WaitForState()
}
Example #4
0
// Paginate request to get all record sets.
func ListAllRecordSets(r53 *route53.Route53, id string) (rrsets []*route53.ResourceRecordSet, err error) {
	req := route53.ListResourceRecordSetsInput{
		HostedZoneId: &id,
	}

	for {
		var resp *route53.ListResourceRecordSetsOutput
		resp, err = r53.ListResourceRecordSets(&req)
		if err != nil {
			return
		} else {
			rrsets = append(rrsets, resp.ResourceRecordSets...)
			if *resp.IsTruncated {
				req.StartRecordName = resp.NextRecordName
				req.StartRecordType = resp.NextRecordType
				req.StartRecordIdentifier = resp.NextRecordIdentifier
			} else {
				break
			}
		}
	}

	// unescape wildcards
	for _, rrset := range rrsets {
		rrset.Name = aws.String(unescaper.Replace(*rrset.Name))
	}

	return
}
Example #5
0
func cleanupReusableDelegationSet(r53 *route53.Route53, id string) {
	req := route53.DeleteReusableDelegationSetInput{Id: &id}
	_, err := r53.DeleteReusableDelegationSet(&req)
	if err != nil {
		fmt.Printf("Warning: cleanup failed - %s\n", err)
	}
}
func resourceAwsGoRoute53Wait(r53 *route53.Route53, ref *route53.GetChangeInput) (result interface{}, state string, err error) {

	status, err := r53.GetChange(ref)
	if err != nil {
		return nil, "UNKNOWN", err
	}
	return true, *status.ChangeInfo.Status, nil
}
func deleteAllRecordsInHostedZoneId(hostedZoneId, hostedZoneName string, conn *route53.Route53) error {
	input := &route53.ListResourceRecordSetsInput{
		HostedZoneId: aws.String(hostedZoneId),
	}

	var lastDeleteErr, lastErrorFromWaiter error
	var pageNum = 0
	err := conn.ListResourceRecordSetsPages(input, func(page *route53.ListResourceRecordSetsOutput, isLastPage bool) bool {
		sets := page.ResourceRecordSets
		pageNum += 1

		changes := make([]*route53.Change, 0)
		// 100 items per page returned by default
		for _, set := range sets {
			if *set.Name == hostedZoneName+"." && (*set.Type == "NS" || *set.Type == "SOA") {
				// Zone NS & SOA records cannot be deleted
				continue
			}
			changes = append(changes, &route53.Change{
				Action:            aws.String("DELETE"),
				ResourceRecordSet: set,
			})
		}
		log.Printf("[DEBUG] Deleting %d records (page %d) from %s",
			len(changes), pageNum, hostedZoneId)

		req := &route53.ChangeResourceRecordSetsInput{
			HostedZoneId: aws.String(hostedZoneId),
			ChangeBatch: &route53.ChangeBatch{
				Comment: aws.String("Deleted by Terraform"),
				Changes: changes,
			},
		}

		var resp interface{}
		resp, lastDeleteErr = deleteRoute53RecordSet(conn, req)
		if out, ok := resp.(*route53.ChangeResourceRecordSetsOutput); ok {
			log.Printf("[DEBUG] Waiting for change batch to become INSYNC: %#v", out)
			if out.ChangeInfo != nil && out.ChangeInfo.Id != nil {
				lastErrorFromWaiter = waitForRoute53RecordSetToSync(conn, cleanChangeID(*out.ChangeInfo.Id))
			} else {
				log.Printf("[DEBUG] Change info was empty")
			}
		} else {
			log.Printf("[DEBUG] Unable to wait for change batch because of an error: %s", lastDeleteErr)
		}

		return !isLastPage
	})
	if err != nil {
		return fmt.Errorf("Failed listing/deleting record sets: %s\nLast error from deletion: %s\nLast error from waiter: %s",
			err, lastDeleteErr, lastErrorFromWaiter)
	}

	return nil
}
func createRoute53Delete(r53Api *route53.Route53, s serviceStatus) (*route53.ChangeResourceRecordSetsInput, error) {
	domainParts := strings.Split(s.dnsName, ".")
	segments := len(domainParts)
	tld := strings.Join(domainParts[segments-2:], ".")
	subdomain := strings.Join(domainParts[:segments-2], ".")

	listHostedZoneInput := route53.ListHostedZonesByNameInput{
		DNSName: &tld,
	}
	hzOut, err := r53Api.ListHostedZonesByName(&listHostedZoneInput)
	if err != nil {
		glog.Warningf("No zone found for %s: %v", tld, err)
		return nil, err
	}
	zones := hzOut.HostedZones
	if len(zones) < 1 {
		glog.Warningf("No zone found for %s", tld)
		return nil, err
	}
	// The AWS API may return more than one zone, the first zone should be the relevant one
	tldWithDot := fmt.Sprint(tld, ".")
	if *zones[0].Name != tldWithDot {
		glog.Warningf("Zone found %s does not match tld given %s", *zones[0].Name, tld)
		return nil, err
	}
	zoneId := *zones[0].ID
	zoneParts := strings.Split(zoneId, "/")
	zoneId = zoneParts[len(zoneParts)-1]

	at := route53.AliasTarget{
		DNSName:              &s.lb.Hostname,
		EvaluateTargetHealth: aws.Boolean(false),
		HostedZoneID:         &s.hzId,
	}
	rrs := route53.ResourceRecordSet{
		AliasTarget: &at,
		Name:        &s.dnsName,
		Type:        aws.String("A"),
	}
	change := route53.Change{
		Action:            aws.String("DELETE"),
		ResourceRecordSet: &rrs,
	}
	batch := route53.ChangeBatch{
		Changes: []*route53.Change{&change},
		Comment: aws.String("Kubernetes Update to Service"),
	}
	crrsInput := route53.ChangeResourceRecordSetsInput{
		ChangeBatch:  &batch,
		HostedZoneID: &zoneId,
	}
	glog.Infof("Created dns delete record set: tld=%s, subdomain=%s, zoneId=%s", tld, subdomain, zoneId)

	return &crrsInput, nil
}
Example #9
0
func getHostedZone(service *route53.Route53, domain string) (*route53.HostedZone, error) {
	params := &route53.ListHostedZonesByNameInput{
		DNSName:  aws.String(domain),
		MaxItems: aws.String("1"),
	}
	resp, err := service.ListHostedZonesByName(params)
	if err != nil {
		return nil, err
	}

	zone := resp.HostedZones[0]
	return zone, nil
}
Example #10
0
func getHostedZoneID(fqdn string, client *route53.Route53) (string, error) {
	authZone, err := acme.FindZoneByFqdn(fqdn, acme.RecursiveNameservers)
	if err != nil {
		return "", err
	}

	// .DNSName should not have a trailing dot
	reqParams := &route53.ListHostedZonesByNameInput{
		DNSName: aws.String(acme.UnFqdn(authZone)),
	}
	resp, err := client.ListHostedZonesByName(reqParams)
	if err != nil {
		return "", err
	}

	var hostedZoneID string
	for _, hostedZone := range resp.HostedZones {
		// .Name has a trailing dot
		if !*hostedZone.Config.PrivateZone && *hostedZone.Name == authZone {
			hostedZoneID = *hostedZone.Id
			break
		}
	}

	if len(hostedZoneID) == 0 {
		return "", fmt.Errorf("Zone %s not found in Route 53 for domain %s", authZone, fqdn)
	}

	if strings.HasPrefix(hostedZoneID, "/hostedzone/") {
		hostedZoneID = strings.TrimPrefix(hostedZoneID, "/hostedzone/")
	}

	return hostedZoneID, nil
}
func getNameServers(zoneId string, zoneName string, r53 *route53.Route53) ([]string, error) {
	resp, err := r53.ListResourceRecordSets(&route53.ListResourceRecordSetsInput{
		HostedZoneId:    aws.String(zoneId),
		StartRecordName: aws.String(zoneName),
		StartRecordType: aws.String("NS"),
	})
	if err != nil {
		return nil, err
	}
	if len(resp.ResourceRecordSets) == 0 {
		return nil, nil
	}
	ns := make([]string, len(resp.ResourceRecordSets[0].ResourceRecords))
	for i := range resp.ResourceRecordSets[0].ResourceRecords {
		ns[i] = *resp.ResourceRecordSets[0].ResourceRecords[i].Value
	}
	sort.Strings(ns)
	return ns, nil
}
Example #12
0
// Paginate request to get all record sets.
func ListAllRecordSets(r53 *route53.Route53, id string) (rrsets []*route53.ResourceRecordSet, err error) {
	req := route53.ListResourceRecordSetsInput{
		HostedZoneId: &id,
	}

	for {
		var resp *route53.ListResourceRecordSetsOutput
		resp, err = r53.ListResourceRecordSets(&req)
		if err != nil {
			return
		} else {
			rrsets = append(rrsets, resp.ResourceRecordSets...)
			if *resp.IsTruncated {
				req.StartRecordName = resp.NextRecordName
				req.StartRecordType = resp.NextRecordType
				req.StartRecordIdentifier = resp.NextRecordIdentifier
			} else {
				break
			}
		}
	}
	return
}
func upsertRecordOnRoute53(ipAddress string, fqdn string, svc *route53.Route53, verbose bool) {
	// Extract the domain from the fully qualified domain name
	r, _ := regexp.Compile("^([^\x2E]*)\x2E(.*)$")
	toks := r.FindStringSubmatch(fqdn)
	domain := toks[2]

	// http://docs.aws.amazon.com/sdk-for-go/api/service/route53/Route53.html#ListHostedZonesByName-instance_method
	resources, err := svc.ListHostedZonesByName(&route53.ListHostedZonesByNameInput{
		DNSName:  aws.String(domain + "."),
		MaxItems: aws.String("1"),
	})

	if err != nil {
		fmt.Println("ERR: err.Error()", err)
		os.Exit(1)
	}
	if len(resources.HostedZones) != 1 {
		fmt.Printf("ERR: Could not find the domain %s\n", domain)
		os.Exit(1)
	}
	if *resources.DNSName != domain+"." {
		fmt.Printf("ERR: Could not find the domain %s - %s \n", domain, *resources.DNSName)
		os.Exit(1)
	}

	zoneIDToks := strings.Split(*resources.HostedZones[0].Id, "/")
	zoneID := zoneIDToks[len(zoneIDToks)-1]

	resp, err := svc.ListResourceRecordSets(&route53.ListResourceRecordSetsInput{
		StartRecordName: aws.String(fqdn),
		StartRecordType: aws.String("A"),
		HostedZoneId:    aws.String(zoneID),
		MaxItems:        aws.String("1"),
	})
	if err != nil {
		fmt.Println(err.Error())
		os.Exit(1)
	}
	var foundResource bool
	if len(resp.ResourceRecordSets) != 1 {
		foundResource = false
	} else {
		foundResource = *resp.ResourceRecordSets[0].Name == fqdn+"."
		if foundResource {
			for _, record := range resp.ResourceRecordSets[0].ResourceRecords {
				if *record.Value == ipAddress {
					if verbose {
						fmt.Printf("INFO: %s already registered in route53 as %s\n", ipAddress, fqdn)
					}
					return
				}
			}
		}
	}
	// Make an A-record:
	resourceRecordSet := &route53.ResourceRecordSet{
		Name: aws.String(fqdn + "."),
		Type: aws.String("A"),
		ResourceRecords: []*route53.ResourceRecord{
			&route53.ResourceRecord{
				Value: aws.String(ipAddress),
			},
		},
		TTL: aws.Int64(300),
	}

	// Wrap it as an UPSERT
	upsert := []*route53.Change{&route53.Change{
		Action:            aws.String("UPSERT"),
		ResourceRecordSet: resourceRecordSet,
	}}

	// Put it into a pretty envelope with a stamp for route53#zoneId and change ticket
	params := route53.ChangeResourceRecordSetsInput{
		ChangeBatch: &route53.ChangeBatch{
			Changes: upsert,
		},
		HostedZoneId: aws.String(zoneID),
	}

	// Post it
	changeResp, err := svc.ChangeResourceRecordSets(&params)

	if err != nil {
		fmt.Println("ERR: ", err.Error(), changeResp)
		os.Exit(1)
	}

	// Done
	if verbose {
		fmt.Printf("INFO: Submitted change for zoneId %s to register %s as %s\n", zoneID, ipAddress, fqdn)
	}
}
func createRoute53Upsert(r53Api *route53.Route53, elbApi *elb.ELB, domain string, hn string) (*route53.ChangeResourceRecordSetsInput, error) {
	domainParts := strings.Split(domain, ".")
	segments := len(domainParts)
	tld := strings.Join(domainParts[segments-2:], ".")
	//	subdomain := strings.Join(domainParts[:segments-2], ".")

	elbName := strings.Split(hn, "-")[0]
	lbInput := &elb.DescribeLoadBalancersInput{
		LoadBalancerNames: []*string{
			&elbName,
		},
	}
	resp, err := elbApi.DescribeLoadBalancers(lbInput)
	if err != nil {
		glog.Warningf("Could not describe load balancer: %v", err)
		return nil, err
	}
	descs := resp.LoadBalancerDescriptions
	if len(descs) < 1 {
		glog.Warningf("No lb found for %s: %v", tld, err)
		return nil, err
	}
	if len(descs) > 1 {
		glog.Warningf("Multiple lbs found for %s: %v", tld, err)
		return nil, err
	}
	hzId := descs[0].CanonicalHostedZoneNameID

	listHostedZoneInput := route53.ListHostedZonesByNameInput{
		DNSName: &tld,
	}
	hzOut, err := r53Api.ListHostedZonesByName(&listHostedZoneInput)
	if err != nil {
		glog.Warningf("No zone found for %s: %v", tld, err)
		return nil, err
	}
	zones := hzOut.HostedZones
	if len(zones) < 1 {
		glog.Warningf("No zone found for %s", tld)
		return nil, err
	}
	// The AWS API may return more than one zone, the first zone should be the relevant one
	tldWithDot := fmt.Sprint(tld, ".")
	if *zones[0].Name != tldWithDot {
		glog.Warningf("Zone found %s does not match tld given %s", *zones[0].Name, tld)
		return nil, err
	}
	zoneId := *zones[0].ID
	zoneParts := strings.Split(zoneId, "/")
	zoneId = zoneParts[len(zoneParts)-1]

	at := route53.AliasTarget{
		DNSName:              &hn,
		EvaluateTargetHealth: aws.Boolean(false),
		HostedZoneID:         hzId,
	}
	rrs := route53.ResourceRecordSet{
		AliasTarget: &at,
		Name:        &domain,
		Type:        aws.String("A"),
	}
	change := route53.Change{
		Action:            aws.String("UPSERT"),
		ResourceRecordSet: &rrs,
	}
	batch := route53.ChangeBatch{
		Changes: []*route53.Change{&change},
		Comment: aws.String("Kubernetes Update to Service"),
	}
	crrsInput := route53.ChangeResourceRecordSetsInput{
		ChangeBatch:  &batch,
		HostedZoneID: &zoneId,
	}
	//glog.Infof("Created dns record set: tld=%s, subdomain=%s, zoneId=%s", tld, subdomain, zoneId)

	return &crrsInput, nil
}