Skip to content

Commit

Permalink
Merge pull request #352 from Netflix/adrian.zone-ttl-email
Browse files Browse the repository at this point in the history
Adds Zone.ttl, email and builder
  • Loading branch information
adriancole committed Mar 28, 2015
2 parents d435178 + ac47b7f commit 2d0e711
Show file tree
Hide file tree
Showing 43 changed files with 660 additions and 431 deletions.
2 changes: 2 additions & 0 deletions CHANGELOG.md
Original file line number Diff line number Diff line change
@@ -1,4 +1,6 @@
### Version 4.5
* Adds `Zone.email()`, `Zone.ttl()` and `Zone.builder()`
* Adds email and ttl to CLI zone list output
* Adds `ZoneApi.iterateByName()` to support lookups
* Adds `-n` parameter to CLI zone list
* Refines zone identifiers and handling of zones with the same name
Expand Down
3 changes: 2 additions & 1 deletion README.md
Original file line number Diff line number Diff line change
Expand Up @@ -27,8 +27,9 @@ Advanced usage, including ec2 hooks are covered in the [readme](https://github.c

If you just want to fool around, you can use the `mock` provider.
```bash
# first column is the zone id, which isn't always its name!
$ denominator -p mock zone list
denominator.io.
denominator.io. denominator.io. admin.denominator.io. 86400
$ denominator -p mock record -z denominator.io. list
denominator.io. SOA 3600 ns1.denominator.io. admin.denominator.io. 1 3600 600 604800 60
denominator.io. NS 86400 ns1.denominator.io.
Expand Down
21 changes: 10 additions & 11 deletions cli/README.md
Original file line number Diff line number Diff line change
Expand Up @@ -72,8 +72,9 @@ denominator will print out a help statement, but here's the gist.

If you just want to fool around, you can use the `mock` provider.
```bash
# first column is the zone id, which isn't always its name!
$ denominator -p mock zone list
denominator.io.
denominator.io. denominator.io. admin.denominator.io. 86400
$ denominator -p mock -z denominator.io. record list
denominator.io. SOA 3600 ns1.denominator.io. admin.denominator.io. 1 3600 600 604800 60
denominator.io. NS 86400 ns1.denominator.io.
Expand Down Expand Up @@ -130,18 +131,16 @@ email.netflix.com. A 3600 69.53.237.168
--snip--
```

### Zone Identifiers
When the provider doesn't use name-based zone identification, the last column zone list is the `id`.
This can be used to disambiguate zones with the same name, or to improve performance by eliminating a network call.
If you wish to use a zone's identifier, simply pass it instead of the zone name in record commands.
### Zone ID
The first column in the zone list is the `id`. This can be used to disambiguate zones with the same
name, or to improve performance by eliminating a network call. If you wish to use a zone's
identifier, simply pass it instead of the zone name in record commands.

```bash
$ denominator -p route53 -c my_access_key -c my_secret_key zone list -n denominator.io.
[Route53#listHostedZonesByName] ---> GET https://route53.amazonaws.com/2013-04-01/hostedzonesbyname?dnsname=denominator.io. HTTP/1.1
[Route53#listHostedZonesByName] <--- HTTP/1.1 200 OK (678ms)
denominator.io. 63CEB242-9E3E-327D-9351-2EFD02493E18 Z2ZEEJCUZCVG56
denominator.io. 022DFF2F-2E0B-F0A2-A581-19339A7BA1F1 Z3OQLQGABCU3T
$ denominator -p route53 -c my_access_key -c my_secret_key record -z Z3OQLQGABCU3T list
$ denominator -q -p route53 -c my_access_key -c my_secret_key zone list -n denominator.io.
Z2ZEEJCUZCVG56 denominator.io. 63CEB242-9E3E-327D-9351-2EFD02493E18 awsdns-hostmaster.amazon.com. 86400
Z3OQLQGABCU3T denominator.io. 022DFF2F-2E0B-F0A2-A581-19339A7BA1F1 awsdns-hostmaster.amazon.com. 86400
$ denominator -q -p route53 -c my_access_key -c my_secret_key record -z Z3OQLQGABCU3T list
--snip--
denominator.io. NS 172800 ns-1312.awsdns-36.org.
--snip--
Expand Down
17 changes: 4 additions & 13 deletions cli/src/main/java/denominator/cli/Denominator.java
Original file line number Diff line number Diff line change
Expand Up @@ -372,7 +372,7 @@ void overrideFromEnv(Map<String, String> env) {
protected abstract Iterator<String> doRun(DNSApiManager mgr);
}

@Command(name = "list", description = "Lists the zones present in this provider. If more than one column is present, the last is the zone id.")
@Command(name = "list", description = "Lists the zones present in this provider. The zone id is the first column.")
public static class ZoneList extends DenominatorCommand {

@Option(type = OptionType.COMMAND, name = {"-n",
Expand All @@ -383,20 +383,11 @@ public Iterator<String> doRun(final DNSApiManager mgr) {
Iterator<Zone> zones =
name == null ? mgr.api().zones().iterator() : mgr.api().zones().iterateByName(name);
return Iterators.transform(zones, new Function<Zone, String>() {

@Override
public String apply(Zone input) {
Identification zoneId = mgr.provider().zoneIdentification();
switch (zoneId) {
case NAME:
return input.name();
case OPAQUE:
return format("%-36s %s", input.name(), input.id());
case QUALIFIED:
return format("%-36s %-19s %s", input.name(), input.qualifier(), input.id());
default:
throw new UnsupportedOperationException("unsupported zone identification: " + zoneId);
}
return format("%-24s %-36s %-19s %-36s %d", input.id(), input.name(),
input.qualifier() != null ? input.qualifier() : "", input.email(),
input.ttl());
}
});
}
Expand Down
6 changes: 4 additions & 2 deletions cli/src/test/java/denominator/cli/DenominatorTest.java
Original file line number Diff line number Diff line change
Expand Up @@ -74,7 +74,9 @@ public void listsAllProvidersWithCredentials() {

@Test // denominator -p mock zone list
public void testZoneList() {
assertThat(new ZoneList().doRun(mgr)).containsExactly("denominator.io.");
assertThat(new ZoneList().doRun(mgr)).containsExactly(
"denominator.io. denominator.io. admin.denominator.io. 86400"
);
}

@Test // denominator -p mock zone list -n denominator.com.
Expand Down Expand Up @@ -224,7 +226,7 @@ public void testResourceRecordSetList() {
assertThat(command.doRun(mgr)).containsExactly(
"a.denominator.io. A alazona null 192.0.2.1",
"denominator.io. NS 86400 ns1.denominator.io.",
"denominator.io. SOA 3600 ns1.denominator.io. admin.denominator.io. 1 3600 600 604800 60",
"denominator.io. SOA 3600 ns1.denominator.io. admin.denominator.io. 1 3600 600 604800 86400",
"server1.denominator.io. CERT 3600 12345 1 1 B33F",
"server1.denominator.io. SRV 3600 0 1 80 www.denominator.io.",
"www.geo.denominator.io. CNAME alazona 86400 a.denominator.io.",
Expand Down
89 changes: 50 additions & 39 deletions clouddns/src/main/java/denominator/clouddns/CloudDNSZoneApi.java
Original file line number Diff line number Diff line change
@@ -1,28 +1,18 @@
package denominator.clouddns;

import java.net.URI;
import java.util.Iterator;

import javax.inject.Inject;

import denominator.clouddns.RackspaceApis.CloudDNS;
import denominator.clouddns.RackspaceApis.ListWithNext;
import denominator.clouddns.RackspaceApis.Pager;
import denominator.model.Zone;

import static denominator.clouddns.RackspaceApis.emptyOn404;
import static denominator.common.Util.singletonIterator;

class CloudDNSZoneApi implements denominator.ZoneApi {

private final CloudDNS api;
private final Pager<Zone> zonePager = new Pager<Zone>() {

@Override
public ListWithNext<Zone> apply(URI nullOrNext) {
return nullOrNext == null ? api.domains() : api.domains(nullOrNext);
}

};

@Inject
CloudDNSZoneApi(CloudDNS api) {
Expand All @@ -31,38 +21,59 @@ public ListWithNext<Zone> apply(URI nullOrNext) {

@Override
public Iterator<Zone> iterator() {
final ListWithNext<Zone> first = emptyOn404(zonePager, null);
if (first.next == null) {
return first.iterator();
return new ZipWithDomain(api.domains());
}

@Override
public Iterator<Zone> iterateByName(String name) {
ListWithNext<Zone> zones = api.domainsByName(name);
if (zones.isEmpty()) {
return singletonIterator(null);
}
return new Iterator<Zone>() {
Iterator<Zone> current = first.iterator();
URI next = first.next;

@Override
public boolean hasNext() {
while (!current.hasNext() && next != null) {
ListWithNext<Zone> nextPage = emptyOn404(zonePager, next);
current = nextPage.iterator();
next = nextPage.next;
}
return current.hasNext();
}
return singletonIterator(zipWithDomain(zones.get(0)));
}

@Override
public Zone next() {
return current.next();
}
/**
* CloudDNS only exposes a domain's ttl in the show api.
*/
private Zone zipWithDomain(Zone next) {
int ttl = api.domain(next.id()).ttl();
return Zone.builder()
.name(next.name())
.id(next.id())
.email(next.email())
.ttl(ttl).build();
}

class ZipWithDomain implements Iterator<Zone> {

@Override
public void remove() {
throw new UnsupportedOperationException();
ListWithNext<Zone> list;
int i = 0;
int length;

ZipWithDomain(ListWithNext<Zone> list) {
this.list = list;
this.length = list.size();
}

@Override
public boolean hasNext() {
while (i == length && list.next != null) {
list = api.domains(list.next);
length = list.size();
i = 0;
}
};
}
return i < length;
}

@Override
public Iterator<Zone> iterateByName(String name) {
return api.domainsByName(name).iterator();
@Override
public Zone next() {
return zipWithDomain(list.get(i++));
}

@Override
public void remove() {
throw new UnsupportedOperationException();
}
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -66,19 +66,22 @@ protected String jsonKey() {
}

protected Zone build(JsonReader reader) throws IOException {
String name = null;
String id = null;
Zone.Builder result = new Zone.Builder();
while (reader.hasNext()) {
String nextName = reader.nextName();
if (nextName.equals("name")) {
name = reader.nextString();
result.name(reader.nextString());
} else if (nextName.equals("id")) {
id = reader.nextString();
result.id(reader.nextString());
} else if (nextName.equals("emailAddress")) {
result.email(reader.nextString());
} else if (nextName.equals("ttl")) {
result.ttl(reader.nextInt());
} else {
reader.skipValue();
}
}
return Zone.create(name, id);
return result.build();
}
}

Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -25,7 +25,7 @@ static <X> ListWithNext<X> emptyOn404(Pager<X> pagingFunction, URI nullOrNext) {
}

@Headers("Content-Type: application/json")
static interface CloudIdentity {
interface CloudIdentity {

@RequestLine("POST /tokens")
@Body("%7B\"auth\":%7B\"RAX-KSKEY:apiKeyCredentials\":%7B\"username\":\"{username}\",\"apiKey\":\"{apiKey}\"%7D%7D%7D")
Expand All @@ -38,7 +38,7 @@ TokenIdAndPublicURL passwordAuth(URI endpoint, @Param("username") String usernam
@Param("password") String password);
}

static interface CloudDNS {
interface CloudDNS {

@RequestLine("GET /limits")
Map<String, Object> limits();
Expand All @@ -59,6 +59,9 @@ static interface CloudDNS {
@RequestLine("GET /domains")
ListWithNext<Zone> domains();

@RequestLine("GET /domains/{domainId}?showRecords=false&showSubdomains=false")
Zone domain(@Param("domainId") String id);

@RequestLine("GET")
ListWithNext<Record> records(URI href);

Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -25,8 +25,7 @@ public class CloudDNSProviderDynamicUpdateMockTest {
public void dynamicEndpointUpdates() throws Exception {
final AtomicReference<String> url = new AtomicReference<String>(server.url());
server.enqueueAuthResponse();
server.enqueue(new MockResponse().setResponseCode(404).setBody(
"{\"message\":\"Not Found\",\"code\":404,\"details\":\"\"}"));
server.enqueue(new MockResponse().setBody("{ \"domains\": [] }"));

DNSApi api = Denominator.create(new CloudDNSProvider() {
@Override
Expand Down Expand Up @@ -54,8 +53,7 @@ public String url() {
@Test
public void dynamicCredentialUpdates() throws Exception {
server.enqueueAuthResponse();
server.enqueue(new MockResponse().setResponseCode(404).setBody(
"{\"message\":\"Not Found\",\"code\":404,\"details\":\"\"}"));
server.enqueue(new MockResponse().setBody("{ \"domains\": [] }"));

AtomicReference<Credentials>
dynamicCredentials =
Expand All @@ -74,8 +72,7 @@ public void dynamicCredentialUpdates() throws Exception {

server.credentials("jclouds-bob", "comeon");
server.enqueueAuthResponse();
server.enqueue(new MockResponse().setResponseCode(404).setBody(
"{\"message\":\"Not Found\",\"code\":404,\"details\":\"\"}"));
server.enqueue(new MockResponse().setBody("{ \"domains\": [] }"));

api.zones().iterator();

Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -5,15 +5,12 @@
import org.junit.Rule;
import org.junit.Test;

import java.util.Iterator;

import denominator.ZoneApi;
import denominator.model.Zone;

import static denominator.assertj.ModelAssertions.assertThat;
import static denominator.clouddns.RackspaceApisTest.domainId;
import static denominator.clouddns.RackspaceApisTest.domainResponse;
import static denominator.clouddns.RackspaceApisTest.domainsResponse;
import static org.junit.Assert.assertFalse;

public class CloudDNSZoneApiMockTest {

Expand All @@ -24,16 +21,19 @@ public class CloudDNSZoneApiMockTest {
public void iteratorWhenPresent() throws Exception {
server.enqueueAuthResponse();
server.enqueue(new MockResponse().setBody(domainsResponse));
server.enqueue(new MockResponse().setBody(domainResponse));

ZoneApi api = server.connect().api().zones();
Iterator<Zone> domains = api.iterator();

assertThat(domains.next())
.hasName("denominator.io")
.hasId(String.valueOf(domainId));
assertThat(api.iterator()).containsExactly(
Zone.builder().name("denominator.io").id("1234").email("admin@denominator.io").ttl(3600)
.build()
);

server.assertAuthRequest();
server.assertRequest().hasPath("/v1.0/123123/domains");
server.assertRequest()
.hasPath("/v1.0/123123/domains/1234?showRecords=false&showSubdomains=false");
}

@Test
Expand All @@ -42,7 +42,7 @@ public void iteratorWhenAbsent() throws Exception {
server.enqueue(new MockResponse().setBody("{ \"domains\": [] }"));

ZoneApi api = server.connect().api().zones();
assertFalse(api.iterator().hasNext());
assertThat(api.iterator()).isEmpty();

server.assertAuthRequest();
server.assertRequest().hasPath("/v1.0/123123/domains");
Expand All @@ -51,17 +51,20 @@ public void iteratorWhenAbsent() throws Exception {
@Test
public void iteratorByNameWhenPresent() throws Exception {
server.enqueueAuthResponse();
server.enqueue(new MockResponse().setBody(
"{\"domains\":[{\"name\":\"denominator.io\",\"id\":1234,\"emailAddress\":\"fake@denominator.io\",\"updated\":\"2015-03-22T18:21:33.000+0000\",\"created\":\"2015-03-22T18:21:33.000+0000\"}],\"totalEntries\":1}"));
server.enqueue(new MockResponse().setBody(domainsResponse));
server.enqueue(new MockResponse().setBody(domainResponse));

ZoneApi api = server.connect().api().zones();

assertThat(api.iterateByName("denominator.io").next())
.hasName("denominator.io")
.hasId(String.valueOf(domainId));
assertThat(api.iterateByName("denominator.io")).containsExactly(
Zone.builder().name("denominator.io").id("1234").email("admin@denominator.io").ttl(3600)
.build()
);

server.assertAuthRequest();
server.assertRequest().hasPath("/v1.0/123123/domains?name=denominator.io");
server.assertRequest()
.hasPath("/v1.0/123123/domains/1234?showRecords=false&showSubdomains=false");
}

@Test
Expand Down
Loading

0 comments on commit 2d0e711

Please sign in to comment.