Exemple #1
0
        def test_delete_missing_rrset(self):
            """
            It is an error to attempt to delete an rrset which does not exist.
            """
            zone_name = u"{}.test_delete_missing_rrset.invalid.".format(
                uuid4())
            rrset = RRSet(
                label=Name(u"foo.{}".format(zone_name)),
                type=u"CNAME",
                ttl=60,
                records={CNAME(canonical_name=Name(u"bar.example.invalid."))},
            )

            client = get_client(self)
            d = client.create_hosted_zone(u"{}".format(time()), zone_name)

            def created_zone(zone):
                self.addCleanup(lambda: self._cleanup(client, zone.identifier))
                d = client.change_resource_record_sets(zone.identifier,
                                                       [delete_rrset(rrset)])
                self.assertFailure(d, Route53Error)
                return d

            d.addCallback(created_zone)

            def got_error(error):
                self.assertEqual(BAD_REQUEST, int(error.status))

            d.addCallback(got_error)

            return d
Exemple #2
0
 def create_hosted_zone(self, caller_reference, name):
     """
     @see: L{txaws.route53.client._Route53Client.create_hosted_zone}
     """
     zone = HostedZone(
         name=name,
         reference=caller_reference,
         identifier=self._state.next_id(),
         # Hosted zones start with SOA and NS rrsets.
         rrset_count=2,
     )
     self._state.zones = self._state.zones.append(zone)
     self.change_resource_record_sets(
         zone.identifier,
         [
             create_rrset(
                 RRSet(
                     label=Name(name),
                     type="SOA",
                     ttl=900,
                     records=self._state.soa_records,
                 ), ),
             create_rrset(
                 RRSet(
                     label=Name(name),
                     type="NS",
                     ttl=172800,
                     records=self._state.ns_records,
                 ), ),
         ],
     )
     return succeed(zone)
Exemple #3
0
        def test_list_resource_record_sets_maxitems(self):
            """
            If C{maxitems} is used to limit the number of records returned by
            C{list_resource_record_sets}, the records returned are those that
            sort first according to the rules given by
            U{http://docs.aws.amazon.com/Route53/latest/APIReference/API_ListResourceRecordSets.html#API_ListResourceRecordSets_RequestSyntax}.
            """
            zone_name = u"{}.example.invalid.".format(uuid4())
            client = get_client(self)

            # extra sorts _after_ expected according to the AWS Route53
            # ordering rules but it sorts _before_ according to more naive
            # (incorrect) string ordering rules.
            extra = RRSet(
                Name(u"a.z.{}".format(zone_name)),
                u"A",
                60,
                {A(IPv4Address(u"10.0.0.1"))},
            )
            expected = RRSet(
                Name(u"b.y.{}".format(zone_name)),
                u"A",
                60,
                {A(IPv4Address(u"10.0.0.2"))},
            )

            d = client.create_hosted_zone(u"{}".format(time()), zone_name)

            def created_zone(zone):
                self.addCleanup(lambda: self._cleanup(client, zone.identifier))
                d = client.change_resource_record_sets(zone.identifier, [
                    create_rrset(extra),
                    create_rrset(expected),
                ])
                d.addCallback(lambda ignored: zone)
                return d

            d.addCallback(created_zone)

            def created_rrsets(zone):
                return client.list_resource_record_sets(
                    zone.identifier,
                    name=Name(u"a.{}".format(zone_name)),
                    type=u"A",
                    maxitems=1,
                )

            d.addCallback(created_rrsets)

            def listed_rrsets(rrsets):
                self.assertEqual(
                    {RRSetKey(expected.label, expected.type): expected},
                    rrsets,
                )

            d.addCallback(listed_rrsets)
            return d
Exemple #4
0
 def created_rrsets(zone):
     return client.list_resource_record_sets(
         zone.identifier,
         name=Name(u"a.{}".format(zone_name)),
         type=u"A",
         maxitems=1,
     )
def _get_converge_inputs(config, subscriptions, k8s, aws):
    a = start_action(action_type=u"load-converge-inputs")
    with a.context():
        d = DeferredContext(
            gatherResults([
                get_active_subscriptions(subscriptions),
                get_customer_grid_configmaps(k8s, config.kubernetes_namespace),
                get_customer_grid_deployments(k8s, config.kubernetes_namespace),
                get_customer_grid_replicasets(k8s, config.kubernetes_namespace),
                get_customer_grid_pods(k8s, config.kubernetes_namespace),
                get_customer_grid_service(k8s, config.kubernetes_namespace),
                get_hosted_zone_by_name(aws.get_route53_client(), Name(config.domain)),
            ]),
        )
        d.addCallback(
            lambda state: _State(**dict(
                zip([
                    u"subscriptions",
                    u"configmaps",
                    u"deployments",
                    u"replicasets",
                    u"pods",
                    u"service",
                    u"zone",
                ], state,
                ),
            )),
        )
        return d.addActionFinish()
def _cname_for_subscription(domain):
    return CNAME(
        Name(
            u"introducer.{domain}".format(
                domain=domain,
            )
        )
    )
 def itersubscription_ids(self):
     for key in self.rrsets:
         if key.type == u"CNAME":
             subscription_part, rest = key.label.text.split(u".", 1)
             # XXX Ugh strings
             if Name(rest) == _introducer_domain(self.domain):
                 subscription_id = autopad_b32decode(subscription_part)
                 yield subscription_id
Exemple #8
0
 def test_soa(self):
     """
     L{SOA} can round-trip an I{SOA} record through XML.
     """
     self._test_roundtrip(
         SOA,
         SOA(
             mname=Name("ns-857.example.invalid."),
             rname=Name("awsdns-hostmaster.example.invalid."),
             serial=1,
             refresh=7200,
             retry=900,
             expire=1209600,
             minimum=86400,
         ),
         XML(self._soa_xml),
     )
Exemple #9
0
 def test_cname(self):
     """
     L{CNAME} can round-trip a I{CNAME} record through XML.
     """
     self._test_roundtrip(
         CNAME,
         CNAME(canonical_name=Name("sub.example.invalid.")),
         XML(self._cname_xml),
     )
Exemple #10
0
 def test_ns(self):
     """
     L{NS} can round-trip an I{NS} record through XML.
     """
     self._test_roundtrip(
         NS,
         NS(nameserver=Name("ns1.example.invalid.")),
         XML(self._ns_xml),
     )
Exemple #11
0
        def test_change_resource_record_sets_nonexistent_zone(self):
            """
            You cannot interact with resource record sets for a non-existent
            zone.
            """
            rrset = RRSet(
                label=Name(u"foo.example.invalid."),
                type=u"CNAME",
                ttl=60,
                records={CNAME(canonical_name=Name(u"bar.example.invalid."))},
            )
            client = get_client(self)
            d = client.change_resource_record_sets(u"abcdefg12345678",
                                                   [create_rrset(rrset)])
            self.assertFailure(d, Route53Error)

            def got_error(error):
                self.assertEqual(NOT_FOUND, int(error.status))

            d.addCallback(got_error)
            return d
 def filter_results(zones):
     Message.log(zone_names=list(zone.name for zone in zones))
     for zone in zones:
         # XXX Bleuch zone.name should be a Name!
         if Name(zone.name) == name:
             d = route53.list_resource_record_sets(zone_id=zone.identifier)
             d.addCallback(
                 lambda rrsets, zone=zone: _ZoneState(
                     zone=zone,
                     rrsets=rrsets,
                 ),
             )
             return d
     raise KeyError(name)
def _converge_route53_infrastructure(actual, config, subscriptions, k8s, aws):
    """
    Converge on the desired Route53 state relating to general S4
    infrastructure.

    Specifically, make sure there is an rrset for the ``introducer`` subdomain
    which points at the customer grid service's load balancer endpoint.
    """
    if actual.service is None or actual.service.status is None:
        # Cannot do anything without a v1.Service or one without a populated
        # v1.ServiceStatus field.
        return []

    if not actual.service.status.loadBalancer.ingress:
        # Also cannot do anything if we don't yet know what our ingress
        # address is.
        return []

    loadbalancer_hostname = actual.service.status.loadBalancer.ingress[0].hostname
    introducer_key = RRSetKey(label=_introducer_domain(config.domain), type=u"CNAME")
    desired_rrset = RRSet(
        label=introducer_key.label,
        type=introducer_key.type,
        ttl=60,
        records={
            CNAME(canonical_name=Name(loadbalancer_hostname)),
        },
    )

    actual_rrset = actual.zone.rrsets.get(introducer_key, None)
    if actual_rrset == desired_rrset:
        # Nothing to do.
        return []

    # Create it or change it to what we want.
    route53 = aws.get_route53_client()
    return [
        lambda: change_route53_rrsets(route53, actual.zone.zone, desired_rrset),
    ]
def _introducer_name_for_subscription(subscription_id, domain):
    return Name(configmap_public_host(subscription_id, domain))
def _introducer_domain(domain):
    """
    Construct a ``Name`` for the intermediate domain name that glues per-user
    domain names to the load balancer hostname/address for the grid service.
    """
    return Name(u"introducer.{}".format(domain))
Exemple #16
0
class Route53ClientState:
    """
    L{Route53ClientState} holds all of the Route53 state associated with a
    single account.  This allows multiple clients with the same credentials to
    share state while hiding one account's state from other accounts (just as
    AWS does).

    @ivar soa_records: The SOA records (1) which will be put in all newly
        created hosted zones.

    @ivar ns_records: The NS records which will be put in all newly created
        hosted zones.

    @ivar zones: A sequence of HostedZone instances representing the
        zones known to exist.
    @type zones: L{pyrsistent.PVector}

    @ivar rrsets: A mapping from zone identifiers to further mappings.
        The further mappings map an L{RRSetKey} instance to an L{RRSet}
        instance and represent the rrsets belonging to the
        corresponding zone.
    @type rrsets: L{pyrsistent.PMap}
    """
    soa_records = {
        SOA(
            mname=Name(text='ns-698.awsdns-23.net.example.invalid.'),
            rname=Name(text='awsdns-hostmaster.amazon.com.example.invalid.'),
            serial=1,
            refresh=7200,
            retry=900,
            expire=1209600,
            minimum=86400,
        ),
    }

    ns_records = {
        NS(nameserver=Name(text='ns-698.awsdns-23.net.example.invalid.')),
        NS(nameserver=Name(text='ns-1188.awsdns-20.org.examplie.invalid.')),
    }

    _id = attr.ib(default=attr.Factory(count), init=False)

    zones = attr.ib(default=pvector())
    rrsets = attr.ib(default=pmap())

    def next_id(self):
        """
        Assign and return a new, unique hosted zone identifier.

        @rtype: L{str}
        """
        return "/hostedzone/{:014d}".format(next(self._id))

    def get_rrsets(self, zone_id):
        """
        Retrieve all the rrsets that belong to the given zone.

        @param zone_id: The zone to inspect.
        @type zone_id: L{str}

        @return: L{None} if the zone is not found.  Otherwise, a L{PMap}
            mapping L{RRSetKey} to L{RRSet}.
        """
        if any(zone.identifier == zone_id for zone in self.zones):
            return self.rrsets.get(zone_id, pmap())
        # You cannot interact with rrsets unless a zone exists.
        return None

    def set_rrsets(self, zone_id, rrsets):
        """
        Specify all the rrsets that belong to the given zone.

        @param zone_id: The zone to modify.
        @type zone_id: L{str}

        @param rrsets: A L{PMap} mapping L{RRSetKey} to L{RRSet}.
        """
        self.rrsets = self.rrsets.set(zone_id, rrsets)
Exemple #17
0
        def test_resource_record_sets(self):
            zone_name = u"{}.example.invalid.".format(uuid4())
            cname = CNAME(canonical_name=Name(u"example.invalid."))
            client = get_client(self)

            zone = yield client.create_hosted_zone(u"{}".format(time()),
                                                   zone_name)

            # At least try to clean up, to be as nice as possible.
            # This might fail and someone else might have to do the
            # cleanup - but it might not!
            self.addCleanup(lambda: self._cleanup(client, zone.identifier))

            cname_label = Name(u"foo.\N{SNOWMAN}.{}".format(zone_name))
            create = create_rrset(
                RRSet(
                    label=cname_label,
                    type=u"CNAME",
                    ttl=60,
                    records={cname},
                ))
            yield client.change_resource_record_sets(zone.identifier, [create])
            initial = yield client.list_resource_record_sets(zone.identifier)

            key = RRSetKey(cname_label, u"CNAME")
            self.assertIn(key, initial)
            cname_rrset = initial[key]
            self.assertEqual(
                RRSet(label=cname_label,
                      type=u"CNAME",
                      ttl=60,
                      records={cname}),
                cname_rrset,
            )

            # Zones start with an SOA and some NS records.
            key = RRSetKey(Name(zone_name), u"SOA")
            self.assertIn(key, initial)
            soa = initial[key]
            self.assertEqual(
                len(soa.records), 1,
                "Expected one SOA record, got {}".format(soa.records))

            key = RRSetKey(Name(zone_name), u"NS")
            self.assertIn(key, initial)
            ns = initial[key]
            self.assertNotEqual(set(), ns.records,
                                "Expected some NS records, got none")

            # Unrecognized change type
            # XXX This depends on _ChangeRRSet using attrs.
            bogus = attr.assoc(create, action=u"BOGUS")
            d = client.change_resource_record_sets(zone.identifier, [bogus])
            error = yield self.assertFailure(d, Route53Error)
            self.assertEqual(BAD_REQUEST, int(error.status))

            created_a = A(IPv4Address(u"10.0.0.1"))
            upsert_label = Name(u"upsert.{}".format(zone_name))
            upsert_create = upsert_rrset(
                RRSet(
                    upsert_label,
                    u"A",
                    60,
                    {created_a},
                ))
            updated_a = A(IPv4Address(u"10.0.0.2"))
            upsert_update = upsert_rrset(
                RRSet(
                    upsert_create.rrset.label,
                    upsert_create.rrset.type,
                    upsert_create.rrset.ttl,
                    {updated_a},
                ))
            yield client.change_resource_record_sets(zone.identifier,
                                                     [upsert_create])
            rrsets = yield client.list_resource_record_sets(zone.identifier)
            self.assertEqual(rrsets[RRSetKey(upsert_label, u"A")].records,
                             {created_a})

            yield client.change_resource_record_sets(zone.identifier,
                                                     [upsert_update])
            rrsets = yield client.list_resource_record_sets(zone.identifier)
            self.assertEqual(rrsets[RRSetKey(upsert_label, u"A")].records,
                             {updated_a})

            # Use the name and maxitems parameters to select exactly one resource record.
            rrsets = yield client.list_resource_record_sets(
                zone.identifier,
                maxitems=1,
                name=upsert_label,
                type=u"A",
            )
            self.assertEqual(1, len(rrsets), "Expected 1 rrset")
            self.assertEqual({updated_a}, rrsets[RRSetKey(upsert_label,
                                                          u"A")].records)

            # It's invalid to specify type without name.
            d = client.list_resource_record_sets(zone.identifier, type=u"A")
            error = yield self.assertFailure(d, Route53Error)
            self.assertEqual(BAD_REQUEST, int(error.status))

            # It's invalid to delete the SOA record.
            d = client.change_resource_record_sets(
                zone.identifier,
                [delete_rrset(soa)],
            )
            error = yield self.assertFailure(d, Route53Error)
            self.assertEqual(BAD_REQUEST, int(error.status))

            # Likewise, the NS records.
            d = client.change_resource_record_sets(
                zone.identifier,
                [delete_rrset(ns)],
            )
            error = yield self.assertFailure(d, Route53Error)
            self.assertEqual(BAD_REQUEST, int(error.status))

            # Test deletion at the end so the zone is clean for the
            # naive cleanup logic.
            yield client.change_resource_record_sets(
                zone.identifier,
                [
                    delete_rrset(cname_rrset),
                    delete_rrset(upsert_update.rrset),
                ],
            )
            rrsets = yield client.list_resource_record_sets(zone.identifier)
            self.assertNotIn(cname_label, rrsets)
            self.assertNotIn(upsert_label, rrsets)