@@ -15,7 +15,6 @@ import org.cobbzilla.util.http.HttpRequestBean; | |||
import org.cobbzilla.util.http.HttpResponseBean; | |||
import org.cobbzilla.util.http.HttpUtil; | |||
import java.io.IOException; | |||
import java.util.*; | |||
import java.util.concurrent.atomic.AtomicReference; | |||
import java.util.stream.Collectors; | |||
@@ -32,6 +31,7 @@ import static org.cobbzilla.util.http.HttpMethods.PATCH; | |||
import static org.cobbzilla.util.http.HttpMethods.PUT; | |||
import static org.cobbzilla.util.json.JsonUtil.COMPACT_MAPPER; | |||
import static org.cobbzilla.util.json.JsonUtil.json; | |||
import static org.cobbzilla.wizard.resources.ResourceUtil.invalidEx; | |||
public class GoDaddyDnsDriver extends DnsDriverBase<GoDaddyDnsConfig> { | |||
@@ -141,10 +141,6 @@ public class GoDaddyDnsDriver extends DnsDriverBase<GoDaddyDnsConfig> { | |||
return response.isOk() ? response : die("remove: " + response); | |||
}, MAX_GODADDY_RETRIES); | |||
return record; | |||
} catch (IOException e) { | |||
return die("remove: "+e); | |||
} finally { | |||
if (lock != null && domain.get() != null) unlockDomain(domain.get().getUuid(), lock); | |||
} | |||
@@ -165,18 +161,21 @@ public class GoDaddyDnsDriver extends DnsDriverBase<GoDaddyDnsConfig> { | |||
if (domain == null) return emptyList(); | |||
// iterate over all records, return matches | |||
String url = config.getBaseUri()+domain.getName()+"/records"; | |||
if (matcher != null) { | |||
if (matcher.hasType()) { | |||
url += "/" + matcher.getType().name(); | |||
} | |||
if (matcher.hasFqdn()) { | |||
String fqdn = matcher.getFqdn(); | |||
fqdn = domain.dropDomainSuffix(fqdn); | |||
url += "/" + fqdn; | |||
final var url = new StringBuilder(config.getBaseUri()).append(domain.getName()).append("/records"); | |||
if (matcher != null && (matcher.hasType() || matcher.hasFqdn())) { | |||
if (!matcher.hasType() || !matcher.hasPattern()) { | |||
// as per GoDaddy's docs both type and fqdn must be set here | |||
// https://developer.godaddy.com/doc/endpoint/domains#/v1/recordGet | |||
throw invalidEx("err.request.invalid", "Both type and pattern are required"); | |||
} | |||
url.append("/").append(matcher.getType().name()); | |||
var fqdn = matcher.getPattern(); | |||
fqdn = domain.dropDomainSuffix(fqdn); | |||
url.append("/").append(fqdn); | |||
} | |||
return readRecords(domain, url, matcher); | |||
return readRecords(domain, url.toString(), matcher); | |||
} | |||
public Collection<DnsRecord> readRecords(BubbleDomain domain, String url, DnsRecordMatch matcher) { | |||
@@ -195,17 +194,17 @@ public class GoDaddyDnsDriver extends DnsDriverBase<GoDaddyDnsConfig> { | |||
private final Map<String, GoDaddyDnsRecord[]> listCache = new ExpirationMap<>(SECONDS.toMillis(10)); | |||
public GoDaddyDnsRecord[] listGoDaddyDnsRecords(String url) throws IOException { | |||
final HttpRequestBean request = auth(url); | |||
return listCache.computeIfAbsent(url, k -> { | |||
public GoDaddyDnsRecord[] listGoDaddyDnsRecords(final String goDaddyApiUrl) { | |||
return listCache.computeIfAbsent(goDaddyApiUrl, url -> { | |||
final var request = auth(url); | |||
final HttpResponseBean response; | |||
try { | |||
response = HttpUtil.getResponse(request); | |||
} catch (Exception e) { | |||
log.error("listGoDaddyDnsRecords("+url+"): "+e); | |||
log.error("listGoDaddyDnsRecords(" + url + "): " + e, e); | |||
return GoDaddyDnsRecord.EMPTY_ARRAY; | |||
} | |||
if (!response.isOk()) throw new IllegalStateException("readRecords: "+response); | |||
if (!response.isOk()) throw new IllegalStateException("listGoDaddyDnsRecords: " + response); | |||
return json(response.getEntityString(), GoDaddyDnsRecord[].class); | |||
}); | |||
} | |||
@@ -18,10 +18,7 @@ import org.cobbzilla.util.dns.DnsRecord; | |||
import org.cobbzilla.util.dns.DnsRecordMatch; | |||
import org.cobbzilla.util.dns.DnsType; | |||
import java.util.ArrayList; | |||
import java.util.Collection; | |||
import java.util.List; | |||
import java.util.Map; | |||
import java.util.*; | |||
import java.util.stream.Collectors; | |||
import static java.util.Collections.emptyList; | |||
@@ -57,15 +54,20 @@ public class Route53DnsDriver extends DnsDriverBase<Route53DnsConfig> { | |||
@Getter(lazy=true) private final Map<String, HostedZone> cachedZoneLookups = new ExpirationMap<>(); | |||
private HostedZone getHostedZone(BubbleDomain domain) { | |||
return getCachedZoneLookups().computeIfAbsent(domain.getName(), key -> { | |||
final var keyDot = key + "."; | |||
final Optional<HostedZone> found; | |||
try { | |||
final ListHostedZonesResult zones = getRoute53client().listHostedZones(new ListHostedZonesRequest().withMaxItems(MAX_ITEMS)); | |||
for (HostedZone z : zones.getHostedZones()) { | |||
if (z.getName().equalsIgnoreCase(key + ".")) return z; | |||
} | |||
return die("HostedZone with name '"+key+".' not found"); | |||
found = getRoute53client().listHostedZones(new ListHostedZonesRequest().withMaxItems(MAX_ITEMS)) | |||
.getHostedZones() | |||
.stream() | |||
.filter(z -> z.getName().equalsIgnoreCase(keyDot)) | |||
.findFirst(); | |||
} catch (Exception e) { | |||
return die("getHostedZone: "+e); | |||
return die("getHostedZone: " + e); | |||
} | |||
return found.isPresent() ? found.get() : die("getHostedZone: HostedZone not found with name: " + keyDot); | |||
}); | |||
} | |||
@@ -165,57 +165,36 @@ | |||
}, | |||
{ | |||
"comment": "list DNS for the network, should now see a DNS A record for new instance", | |||
"before": "await_url me/networks/<<network>>/dns/find?type=A&name=.<<network>>.<<domain>> 40m 10s await_json.length > 0", | |||
"request": { | |||
"uri": "me/networks/<<network>>/dns/find?type=A&name=.<<network>>.<<domain>>" | |||
}, | |||
"response": { | |||
"store": "dnsRecords", | |||
"check": [ | |||
{"condition": "json.length == 1"} | |||
] | |||
} | |||
}, | |||
{ | |||
"comment": "call API of deployed node, ensure it is running", | |||
"before": "await_url .bubble 40m 20s", | |||
"comment": "call API of deployed node after some grace period, ensure it is running", | |||
"before": "await_url .bubble 20m:40m 20s", | |||
"connection": { | |||
"name": "<<bubbleConnectionVar>>", | |||
"baseUri": "https://{{<<networkVar>>.host}}.<<network>>.<<domain>>:{{serverConfig.nginxPort}}/api" | |||
}, | |||
"request": { "uri" : ".bubble" }, | |||
"response": { | |||
"raw": true, | |||
"check": [ | |||
{"condition": "response.json == 'you are ok. the magic is ok too.'"} | |||
] | |||
} | |||
"response": { "raw": true, "check": [{ "condition": "response.json == 'you are ok. the magic is ok too.'" }] } | |||
}, | |||
// { | |||
// "comment": "verify new node has said hello, and requested another node", | |||
// "connection": { "name": "<<sageFqdn>>_connection" }, | |||
// "request": { | |||
// "session": "rootSession", | |||
// "uri": "me/notifications/inbox" | |||
// }, | |||
// "response": { | |||
// "check": [ | |||
// {"condition": "json.length >= 2"}, | |||
// {"condition": "_find(json, function (n) { n.getType().name() == 'upstream_hello' }) != null"}, | |||
// {"condition": "_find(json, function (n) { n.getType().name() == 'new_node' }) != null"} | |||
// ] | |||
// } | |||
// }, | |||
{ | |||
"comment": "now list DNS for the network, should now see a DNS A record for new instance", | |||
"connection": { "name": "<<sageConnectionVar>>" }, | |||
"request": { "uri": "me/networks/<<network>>/dns/find?type=A&name={{<<networkVar>>.host}}.<<network>>.<<domain>>" }, | |||
"response": { "store": "dnsRecords", "check": [{ "condition": "json.length == 1" }] } | |||
}, | |||
{ | |||
"comment": "login to deployed node", | |||
"comment": "check unauthorized access to debug mailbox required for this test (BUBBLE_TEST_MODE has to be true)", | |||
"connection": { "name": "<<bubbleConnectionVar>>" }, | |||
"request": { "uri": "debug/inbox/email/<<email>>?type=request&action=verify&target=network" }, | |||
"response": { "status": 200 }, // confirming status is not 401 here | |||
"after": "await_url debug/inbox/email/<<email>>?type=request&action=verify&target=network 10m 10s len(await_json) > 0" | |||
}, | |||
{ | |||
"comment": "activate and login to deployed node", | |||
"request": { | |||
"session": "new", | |||
"uri" : "auth/login", | |||
"uri" : "auth/login?k={{await_json.[0].ctx.message.data}}", | |||
"entity": { | |||
"name": "<<username>>", | |||
"password": "<<password>>" | |||
@@ -54,8 +54,7 @@ | |||
}, | |||
{ | |||
"comment": "add file to storage", | |||
"before": "await_url .bubble 40m 20s", | |||
"comment": "add test file to storage", | |||
"connection": { "name": "bubbleConnection" }, | |||
"request": { | |||
"session": "bubbleUserSession", | |||