Package io.fathom.cloud.dns.backend.aws

Source Code of io.fathom.cloud.dns.backend.aws.AwsDnsBackend$UpdateRoute53

package io.fathom.cloud.dns.backend.aws;

import io.fathom.cloud.CloudException;
import io.fathom.cloud.dns.backend.DnsBackendBase;
import io.fathom.cloud.dns.model.DnsZone;
import io.fathom.cloud.dns.services.DnsSecrets;
import io.fathom.cloud.openstack.client.dns.model.Record;
import io.fathom.cloud.openstack.client.dns.model.Recordset;
import io.fathom.cloud.protobuf.DnsModel.BackendData;
import io.fathom.cloud.protobuf.DnsModel.BackendSecretData;
import io.fathom.cloud.protobuf.DnsModel.DnsBackendProviderType;
import io.fathom.cloud.protobuf.DnsModel.DnsSuffixData;
import io.fathom.cloud.server.model.Project;

import java.io.IOException;
import java.util.List;
import java.util.Map;
import java.util.Set;

import javax.inject.Inject;
import javax.inject.Singleton;

import org.slf4j.Logger;
import org.slf4j.LoggerFactory;

import com.amazonaws.services.route53.model.HostedZone;
import com.amazonaws.services.route53.model.ResourceRecord;
import com.amazonaws.services.route53.model.ResourceRecordSet;
import com.google.common.base.CharMatcher;
import com.google.common.base.Splitter;
import com.google.common.collect.Lists;
import com.google.common.collect.Maps;
import com.google.common.collect.Sets;

@Singleton
public class AwsDnsBackend extends DnsBackendBase {

    private static final Logger log = LoggerFactory.getLogger(AwsDnsBackend.class);

    @Inject
    DnsSecrets dnsSecrets;

    @Inject
    AwsExecutor awsExecutor;

    AwsRoute53Client client;

    public void init(BackendData backendData) {
        BackendSecretData secretData = dnsSecrets.getSecretData(backendData);

        String accessKey = secretData.getUsername();
        String secretKey = secretData.getPassword();

        client = new AwsRoute53Client(accessKey, secretKey);
    }

    @Override
    public void updateDomain(Project project, DnsZone zone) {
        UpdateRoute53 job = new UpdateRoute53(project, zone, client);

        awsExecutor.execute(job);
    }

    @Override
    public String createZone(Project project, String zone, String topZone, DnsSuffixData suffixData) {
        if (suffixData.getSharedDomain()) {
            log.info("Request to create {} under shared AWS Route 53 domain {}; will reuse", zone, suffixData.getKey());
            return null;
        } else {
            HostedZone hostedZone = client.createHostedZone(topZone);
            return hostedZone.getId();
        }
    }

    @Override
    public DnsBackendProviderType getType() {
        return DnsBackendProviderType.AWS_ROUTE53;
    }

    public class UpdateRoute53 extends UpdateDnsDomainBase {
        protected final AwsRoute53Client client;

        public UpdateRoute53(Project project, DnsZone zone, AwsRoute53Client client) {
            super(project, zone);
            this.client = client;
        }

        @Override
        public Void call() throws CloudException, IOException {
            log.debug("Updating domain: {}", zone.getName());

            List<Recordset> requested = readFromDatabase(false);

            HostedZone hostedZone = getHostedZone();

            List<Recordset> aws = readFromAws(hostedZone);

            Changes changes = computeChanges(aws, requested);

            List<ResourceRecordSet> create = mapToAws(hostedZone.getName(), changes.create);
            List<ResourceRecordSet> remove = mapToAws(hostedZone.getName(), changes.remove);
            client.changeRecords(hostedZone.getId(), create, remove);

            return null;
        }

        private List<ResourceRecordSet> mapToAws(String hostedZoneName, List<Recordset> list) {
            List<ResourceRecordSet> ret = Lists.newArrayList();
            for (Recordset r : list) {
                String name = r.name;
                if (!name.endsWith(".")) {
                    name += ".";
                }

                if ("SOA".equals(r.type)) {
                    // ignore if not at the root
                    if (!name.equals(hostedZoneName)) {
                        log.info("Ignoring SOA record not at root of AWS hosted zone: {}", name);
                        continue;
                    }
                }

                ResourceRecordSet rrs = new ResourceRecordSet();
                rrs.setName(name);
                rrs.setWeight(r.weight);

                Long ttl = r.ttl;
                if (ttl == null) {
                    ttl = 600L;
                }
                rrs.setTTL(ttl);

                rrs.setType(r.type);

                // rrs.setSetIdentifier("openstack:" + r.zone_id + ":" + r.id);

                List<ResourceRecord> resourceRecords = Lists.newArrayList();
                Set<String> values = Sets.newHashSet();

                for (Record record : r.records) {
                    String value = record.value;
                    // These get encoded into a string for route 53
                    if (record.port != null) {
                        throw new IllegalStateException();
                    }
                    if (record.priority != null) {
                        throw new IllegalStateException();
                    }
                    if (record.weight != null) {
                        throw new IllegalStateException();
                    }

                    if (values.contains(value)) {
                        log.debug("Skipping duplicate value: {}", value);
                        continue;
                    }
                    values.add(value);

                    ResourceRecord rr = new ResourceRecord();
                    rr.setValue(record.value);
                    resourceRecords.add(rr);
                }
                rrs.setResourceRecords(resourceRecords);

                ret.add(rrs);
            }
            return ret;
        }

        HostedZone getHostedZone() {
            Map<String, HostedZone> hostedZones = client.getHostedZones();

            String zoneName = zone.getName();
            if (!zoneName.endsWith(".")) {
                zoneName += ".";
            }

            String awsZoneName = client.getAwsZoneName(zoneName);
            HostedZone hostedZone = hostedZones.get(awsZoneName);
            if (hostedZone == null) {
                hostedZone = client.createHostedZone(awsZoneName);
            }
            return hostedZone;
        }

        private List<Recordset> readFromAws(HostedZone hostedZone) {
            log.debug("Reading AWS zone: {}", hostedZone.getName());

            List<Recordset> recordsets = Lists.newArrayList();

            String domainName = CharMatcher.is('.').trimTrailingFrom(hostedZone.getName());
            String awsZoneId = hostedZone.getId();

            // String tagPrefix = "openstack:" + domain.getId() + ":";

            List<ResourceRecordSet> awsRecordsets = client.getResourceRecords(awsZoneId);
            for (ResourceRecordSet awsRecordset : awsRecordsets) {
                boolean sameDomain = false;
                String awsRecordsetName = CharMatcher.is('.').trimTrailingFrom(awsRecordset.getName());
                if (awsRecordsetName.equals(domainName)) {
                    sameDomain = true;
                } else if (awsRecordsetName.endsWith("." + domainName)) {
                    sameDomain = true;
                    // // Don't match www.sub.domain.com against domain.com
                    // String prefix = rrsDomainName.substring(0,
                    // rrsDomainName.length() - (domainName.length() + 1));
                    // int dotIndex = prefix.indexOf('.');
                    // if (dotIndex == -1) {
                    // sameDomain = true;
                    // }
                }
                // String tag = awsRecordset.getSetIdentifier();
                // if (Strings.isNullOrEmpty(tag)) {
                // log.warn("Record did not have tag: {}",
                // awsRecordset.getName());
                // } else {
                // if (tag.startsWith(tagPrefix)) {
                // sameDomain = true;
                // }
                // }

                if (!sameDomain) {
                    log.debug("Domain name not part of domain: {}", awsRecordsetName);
                    continue;
                }

                Recordset recordset = new Recordset();

                recordset.type = awsRecordset.getType();
                recordset.name = awsRecordsetName;

                recordset.ttl = awsRecordset.getTTL();
                recordset.weight = awsRecordset.getWeight();
                recordset.records = Lists.newArrayList();
                for (ResourceRecord awsRR : awsRecordset.getResourceRecords()) {
                    Record rr = new Record();
                    rr.value = awsRR.getValue();
                    if (rr.value.contains(" ")) {
                        List<String> tokens = Splitter.on(' ').splitToList(rr.value);

                        if (recordset.type.equals("SOA") && tokens.size() == 7) {
                            // Ignore for now
                            // String ns
                            // ns-310.awsdns-38.com.
                            // awsdns-hostmaster.amazon.com. 1
                            // 7200 900 1209600 86400
                        } else {
                            throw new IllegalStateException("Cannot decode route 53 value: " + awsRR);
                        }
                    }
                    recordset.records.add(rr);
                }

                recordsets.add(recordset);
            }

            return recordsets;
        }

        @Override
        protected List<Recordset> readFromDatabase(boolean createSoa) throws CloudException {
            List<Recordset> recordsets = super.readFromDatabase(createSoa);

            Map<String, Recordset> unique = Maps.newHashMap();

            for (Recordset recordset : recordsets) {
                String key = recordset.type + ":" + recordset.name;
                if (recordset.weight != null) {
                    key += ":" + recordset.id;
                }
                Recordset existing = unique.get(key);
                if (existing == null) {
                    unique.put(key, recordset);
                } else {
                    Recordset merged = merge(existing, recordset);
                    unique.put(key, merged);
                }
            }
            return Lists.newArrayList(unique.values());
        }

        Recordset merge(Recordset a, Recordset b) {
            if (a.isDeleted()) {
                return b;
            }
            if (b.isDeleted()) {
                return a;
            }
            Recordset merged = new Recordset();
            merged.name = a.name;
            merged.type = a.type;
            merged.records = Lists.newArrayList();
            merged.records.addAll(a.records);
            merged.records.addAll(b.records);
            merged.ttl = a.ttl;
            merged.weight = a.weight;
            merged.zone_id = a.zone_id;
            merged.version = a.version;
            return merged;
        }
    }
}
TOP

Related Classes of io.fathom.cloud.dns.backend.aws.AwsDnsBackend$UpdateRoute53

TOP
Copyright © 2018 www.massapi.com. All rights reserved.
All source code are property of their respective owners. Java is a trademark of Sun Microsystems, Inc and owned by ORACLE Inc. Contact coftware#gmail.com.