From 46d06a12f5ed0d0c33e9074cbaf8560a61ec104b Mon Sep 17 00:00:00 2001 From: Pieter Lexis Date: Tue, 1 Dec 2015 12:26:07 +0100 Subject: [PATCH] Move current API path to /api/v1 from / Closes #2612 --- docs/markdown/httpapi/README.md | 14 +-- docs/markdown/httpapi/api_spec.md | 56 ++++----- pdns/dnsdist-web.cc | 2 +- pdns/ws-api.cc | 6 +- pdns/ws-auth.cc | 30 ++--- pdns/ws-recursor.cc | 24 ++-- regression-tests.api/test_Basics.py | 4 +- regression-tests.api/test_RecursorConfig.py | 6 +- regression-tests.api/test_Servers.py | 12 +- regression-tests.api/test_Zones.py | 120 ++++++++++---------- 10 files changed, 137 insertions(+), 137 deletions(-) diff --git a/docs/markdown/httpapi/README.md b/docs/markdown/httpapi/README.md index bca389f7e..24c269fd8 100644 --- a/docs/markdown/httpapi/README.md +++ b/docs/markdown/httpapi/README.md @@ -30,16 +30,16 @@ Then configure as follows: After restarting `pdns_server`, the following examples should start working: # List zones - curl -H 'X-API-Key: changeme' http://127.0.0.1:8081/servers/localhost/zones | jq . + curl -H 'X-API-Key: changeme' http://127.0.0.1:8081/api/v1/servers/localhost/zones | jq . # Create new zone "example.org" with nameservers ns1.example.org, ns2.example.org - curl -X POST --data '{"name":"example.org", "kind": "Native", "masters": [], "nameservers": ["ns1.example.org", "ns2.example.org"]}' -v -H 'X-API-Key: changeme' http://127.0.0.1:8081/servers/localhost/zones | jq . + curl -X POST --data '{"name":"example.org", "kind": "Native", "masters": [], "nameservers": ["ns1.example.org", "ns2.example.org"]}' -v -H 'X-API-Key: changeme' http://127.0.0.1:8081/api/v1/servers/localhost/zones | jq . # Show the new zone - curl -H 'X-API-Key: changeme' http://127.0.0.1:8081/servers/localhost/zones/example.org | jq . + curl -H 'X-API-Key: changeme' http://127.0.0.1:8081/api/v1/servers/localhost/zones/example.org | jq . # Add a new record to the new zone (would replace any existing test.example.org/A records) - curl -X PATCH --data '{"rrsets": [ {"name": "test.example.org", "type": "A", "changetype": "REPLACE", "records": [ {"content": "192.0.5.4", "disabled": false, "name": "test.example.org", "ttl": 86400, "type": "A" } ] } ] }' -H 'X-API-Key: changeme' http://127.0.0.1:8081/servers/localhost/zones/example.org | jq . + curl -X PATCH --data '{"rrsets": [ {"name": "test.example.org", "type": "A", "changetype": "REPLACE", "records": [ {"content": "192.0.5.4", "disabled": false, "name": "test.example.org", "ttl": 86400, "type": "A" } ] } ] }' -H 'X-API-Key: changeme' http://127.0.0.1:8081/api/v1/servers/localhost/zones/example.org | jq . # Combined replacement of multiple RRsets curl -X PATCH --data '{"rrsets": [ @@ -53,7 +53,7 @@ After restarting `pdns_server`, the following examples should start working: "changetype": "REPLACE", "records": [ {"content": "2001:db8::6/32", "disabled": false, "name": "test.example.org", "ttl": 86400, "type": "AAAA" } ] } - ] }' -H 'X-API-Key: changeme' http://127.0.0.1:8081/servers/localhost/zones/example.org | jq . + ] }' -H 'X-API-Key: changeme' http://127.0.0.1:8081/api/v1/servers/localhost/zones/example.org | jq . `jq` is a highly recommended tool for pretty-printing JSON. If you don't have `jq`, try `json_pp` or `python -mjson.tool` instead. @@ -81,8 +81,8 @@ Install PowerDNS Recursor, configured as follows: After restarting `pdns_recursor`, the following examples should start working: - curl -v -H 'X-API-Key: changeme' http://127.0.0.1:8082/servers/localhost | jq . - curl -v -H 'X-API-Key: changeme' http://127.0.0.1:8082/servers/localhost/zones | jq . + curl -v -H 'X-API-Key: changeme' http://127.0.0.1:8082/api/v1/servers/localhost | jq . + curl -v -H 'X-API-Key: changeme' http://127.0.0.1:8082/api/v1/servers/localhost/zones | jq . API Specification diff --git a/docs/markdown/httpapi/api_spec.md b/docs/markdown/httpapi/api_spec.md index 184c20e21..3e0d19fb9 100644 --- a/docs/markdown/httpapi/api_spec.md +++ b/docs/markdown/httpapi/api_spec.md @@ -96,16 +96,16 @@ Common Error Causes 1. The client body was not a JSON document, or it could not be parsed, or the root element of the JSON document was not a hash. 2. The client did not send an `Accept:` header, or it was set to `*/*`. -3. For requests that operate on a zone, the `zone_id` URL part was invalid. To get a valid `zone_id`, list the zones with the `/servers/:server_id/zones` endpoint. +3. For requests that operate on a zone, the `zone_id` URL part was invalid. To get a valid `zone_id`, list the zones with the `/api/v1/servers/:server_id/zones` endpoint. -URL: / ------- +URL: /api/v1 +------------ Allowed methods: `GET` { - "server_url": "/servers{/server}", + "server_url": "/api/v1/servers{/server}", "api_features": [], } @@ -198,11 +198,11 @@ other servers. { "type": "Server", "id": "localhost", - "url": "/servers/localhost", + "url": "/api/v1/servers/localhost", "daemon_type": "recursor", "version": "VERSION", - "config_url": "/servers/localhost/config{/config_setting}", - "zones_url": "/servers/localhost/zones{/zone}", + "config_url": "/api/v1/servers/localhost/config{/config_setting}", + "zones_url": "/api/v1/servers/localhost/zones{/zone}", } Note: On a pdns server, the servers collection is read-only, and the only @@ -215,7 +215,7 @@ depend on the credentials you have supplied. May be one of `authoritative`, `recursor`. -URL: /servers +URL: /api/v1/servers ------------- Collection access. @@ -227,7 +227,7 @@ Allowed REST methods: * pdnscontrol: `GET`, `PUT`, `POST`, `DELETE` -URL: /servers/:server\_id +URL: /api/v1/servers/:server\_id ------------------------- Returns a single server_resource. @@ -248,7 +248,7 @@ config\_setting\_resource } -URL: /servers/:server\_id/config +URL: /api/v1/servers/:server\_id/config -------------------------------- Collection access. @@ -262,7 +262,7 @@ Creates a new config setting. This is useful for creating configuration for new **TODO**: Not yet implemented. -URL: /servers/:server\_id/config/:config\_setting\_name +URL: /api/v1/servers/:server\_id/config/:config\_setting\_name ------------------------------------------------------- Allowed REST methods: `GET`, `PUT` @@ -287,7 +287,7 @@ zone_collection "id": "", "name": "", "type": "Zone", - "url": "/servers/:server_id/zones/:id", + "url": "/api/v1/servers/:server_id/zones/:id", "kind": "", "serial": , "notified_serial": , @@ -384,7 +384,7 @@ When creating a slave zone, it is recommended to not set any of `nameservers`, `records`. -URL: /servers/:server\_id/zones +URL: /api/v1/servers/:server\_id/zones ------------------------------- Allowed REST methods: `GET`, `POST` @@ -405,7 +405,7 @@ rules before storing it. (Also applies to custom SOA records.) **TODO**: `dnssec`, `nsec3narrow`, `nsec3param`, `presigned` are not yet implemented. -URL: /servers/:server\_id/zones/:zone\_id +URL: /api/v1/servers/:server\_id/zones/:zone\_id ----------------------------------------- Allowed methods: `GET`, `PUT`, `DELETE`, `PATCH`. @@ -495,7 +495,7 @@ Allowed fields in client body: all except `id` and `url`. Changing `name` renames the zone, as expected. -URL: /servers/:server\_id/zones/:zone\_id/notify +URL: /api/v1/servers/:server\_id/zones/:zone\_id/notify ------------------------------------------------ Allowed methods: `PUT` @@ -510,7 +510,7 @@ Not supported for recursors. Clients MUST NOT send a body. -URL: /servers/:server\_id/zones/:zone\_id/axfr-retrieve +URL: /api/v1/servers/:server\_id/zones/:zone\_id/axfr-retrieve ------------------------------------------------------- Allowed methods: `PUT` @@ -525,7 +525,7 @@ Not supported for recursors. **Note**: Added in 3.4.2 -URL: /servers/:server\_id/zones/:zone\_id/export +URL: /api/v1/servers/:server\_id/zones/:zone\_id/export ------------------------------------------------------- Allowed methods: `GET` @@ -535,7 +535,7 @@ Returns the zone in AXFR format. Not supported for recursors. -URL: /servers/:server\_id/zones/:zone\_id/check +URL: /api/v1/servers/:server\_id/zones/:zone\_id/check ----------------------------------------------- Allowed methods: `GET` @@ -574,7 +574,7 @@ through this interface. The server SHOULD reject updates to these metadata. -URL: /servers/:server\_id/zones/:zone\_name/metadata +URL: /api/v1/servers/:server\_id/zones/:zone\_name/metadata ---------------------------------------------------- Collection access. @@ -583,7 +583,7 @@ Allowed methods: `GET`, `POST` **TODO**: Not yet implemented. -URL: /servers/:server\_id/zones/:zone\_name/metadata/:metadata\_kind +URL: /api/v1/servers/:server\_id/zones/:zone\_name/metadata/:metadata\_kind -------------------------------------------------------------------- Allowed methods: `GET`, `PUT`, `DELETE` @@ -621,7 +621,7 @@ both mutually exclusive. `ds`: an array with all dses for this key -URL: /servers/:server\_id/zones/:zone\_name/cryptokeys +URL: /api/v1/servers/:server\_id/zones/:zone\_name/cryptokeys ------------------------------------------------------ Allowed methods: `GET`, `POST` @@ -648,7 +648,7 @@ Where `` is one of the supported key algos in lowercase OR the numeric id, see [http://rtfm.powerdns.com/pdnsutil.html](http://rtfm.powerdns.com/pdnsutil.html) -URL: /servers/:server\_id/zones/:zone\_name/cryptokeys/:cryptokey\_id +URL: /api/v1/servers/:server\_id/zones/:zone\_name/cryptokeys/:cryptokey\_id --------------------------------------------------------------------- Allowed methods: `GET`, `PUT`, `DELETE` @@ -675,7 +675,7 @@ Cache Access Logging & Statistics ==================== -URL: /servers/:server\_id/search-log?q=:search\_term +URL: /api/v1/servers/:server\_id/search-log?q=:search\_term ---------------------------------------------------- Allowed methods: `GET` (Query) @@ -689,7 +689,7 @@ Query the log, filtered by `:search_term`. Response body: ... ] -URL: /servers/:server\_id/statistics +URL: /api/v1/servers/:server\_id/statistics ------------------------------------ Allowed methods: `GET` (Query) @@ -711,7 +711,7 @@ The statistic entries are dependent on the daemon type. Values are returned as strings. -URL: /servers/:server\_id/trace +URL: /api/v1/servers/:server\_id/trace ------------------------------- **TODO**: Not yet implemented. @@ -741,7 +741,7 @@ Retrieve query tracing log and current config. Response body: } -URL: /servers/:server\_id/failures +URL: /api/v1/servers/:server\_id/failures ---------------------------------- **TODO**: Not yet implemented. @@ -878,7 +878,7 @@ Clears recursively all cached data ("plain" DNS + DNSSEC) **TODO**: should this be stored? (for history) -URL: /servers/:server\_id/overrides +URL: /api/v1/servers/:server\_id/overrides ---------------------------------- **TODO**: Not yet implemented. @@ -887,7 +887,7 @@ Collection access. Allowed Methods: `GET`, `POST` -URL: /servers/:server\_id/overrides/:override\_id +URL: /api/v1/servers/:server\_id/overrides/:override\_id ------------------------------------------------- **TODO**: Not yet implemented. diff --git a/pdns/dnsdist-web.cc b/pdns/dnsdist-web.cc index b2c6e23d9..2e09fa37e 100644 --- a/pdns/dnsdist-web.cc +++ b/pdns/dnsdist-web.cc @@ -113,7 +113,7 @@ static void connectionThread(int sock, ComboAddress remote, string password) resp.headers["Content-Type"] = "application/json"; resp.body=my_json.dump(); } - else if(req.url.path=="/servers/localhost") { + else if(req.url.path=="/api/v1/servers/localhost") { resp.status=200; Json::array servers; diff --git a/pdns/ws-api.cc b/pdns/ws-api.cc index a03be1562..0d64d6782 100644 --- a/pdns/ws-api.cc +++ b/pdns/ws-api.cc @@ -86,12 +86,12 @@ static void fillServerDetail(Value& out, Value::AllocatorType& allocator) out.SetObject(); out.AddMember("type", "Server", allocator); out.AddMember("id", "localhost", allocator); - out.AddMember("url", "/servers/localhost", allocator); + out.AddMember("url", "/api/v1/servers/localhost", allocator); out.AddMember("daemon_type", jdaemonType, allocator); Value jversion(getPDNSVersion().c_str(), allocator); out.AddMember("version", jversion, allocator); - out.AddMember("config_url", "/servers/localhost/config{/config_setting}", allocator); - out.AddMember("zones_url", "/servers/localhost/zones{/zone}", allocator); + out.AddMember("config_url", "/api/v1/servers/localhost/config{/config_setting}", allocator); + out.AddMember("zones_url", "/api/v1/servers/localhost/zones{/zone}", allocator); } void apiServer(HttpRequest* req, HttpResponse* resp) { diff --git a/pdns/ws-auth.cc b/pdns/ws-auth.cc index 6942ea092..a12948390 100644 --- a/pdns/ws-auth.cc +++ b/pdns/ws-auth.cc @@ -296,7 +296,7 @@ static void fillZoneInfo(const DomainInfo& di, Value& jdi, Document& doc) { string zoneId = apiZoneNameToId(di.zone); Value jzoneId(zoneId.c_str(), doc.GetAllocator()); // copy jdi.AddMember("id", jzoneId, doc.GetAllocator()); - string url = "/servers/localhost/zones/" + zoneId; + string url = "api/v1/servers/localhost/zones/" + zoneId; Value jurl(url.c_str(), doc.GetAllocator()); // copy jdi.AddMember("url", jurl, doc.GetAllocator()); Value jname(di.zone.toString().c_str(), doc.GetAllocator()); // copy @@ -1226,20 +1226,20 @@ void AuthWebServer::webThread() { try { if(::arg().mustDo("json-interface")) { - d_ws->registerApiHandler("/servers/localhost/config", &apiServerConfig); - d_ws->registerApiHandler("/servers/localhost/flush-cache", &apiServerFlushCache); - d_ws->registerApiHandler("/servers/localhost/search-log", &apiServerSearchLog); - d_ws->registerApiHandler("/servers/localhost/search-data", &apiServerSearchData); - d_ws->registerApiHandler("/servers/localhost/statistics", &apiServerStatistics); - d_ws->registerApiHandler("/servers/localhost/zones//axfr-retrieve", &apiServerZoneAxfrRetrieve); - d_ws->registerApiHandler("/servers/localhost/zones//cryptokeys/", &apiZoneCryptokeys); - d_ws->registerApiHandler("/servers/localhost/zones//cryptokeys", &apiZoneCryptokeys); - d_ws->registerApiHandler("/servers/localhost/zones//export", &apiServerZoneExport); - d_ws->registerApiHandler("/servers/localhost/zones//notify", &apiServerZoneNotify); - d_ws->registerApiHandler("/servers/localhost/zones/", &apiServerZoneDetail); - d_ws->registerApiHandler("/servers/localhost/zones", &apiServerZones); - d_ws->registerApiHandler("/servers/localhost", &apiServerDetail); - d_ws->registerApiHandler("/servers", &apiServer); + d_ws->registerApiHandler("/api/v1/servers/localhost/config", &apiServerConfig); + d_ws->registerApiHandler("/api/v1/servers/localhost/flush-cache", &apiServerFlushCache); + d_ws->registerApiHandler("/api/v1/servers/localhost/search-log", &apiServerSearchLog); + d_ws->registerApiHandler("/api/v1/servers/localhost/search-data", &apiServerSearchData); + d_ws->registerApiHandler("/api/v1/servers/localhost/statistics", &apiServerStatistics); + d_ws->registerApiHandler("/api/v1/servers/localhost/zones//axfr-retrieve", &apiServerZoneAxfrRetrieve); + d_ws->registerApiHandler("/api/v1/servers/localhost/zones//cryptokeys/", &apiZoneCryptokeys); + d_ws->registerApiHandler("/api/v1/servers/localhost/zones//cryptokeys", &apiZoneCryptokeys); + d_ws->registerApiHandler("/api/v1/servers/localhost/zones//export", &apiServerZoneExport); + d_ws->registerApiHandler("/api/v1/servers/localhost/zones//notify", &apiServerZoneNotify); + d_ws->registerApiHandler("/api/v1/servers/localhost/zones/", &apiServerZoneDetail); + d_ws->registerApiHandler("/api/v1/servers/localhost/zones", &apiServerZones); + d_ws->registerApiHandler("/api/v1/servers/localhost", &apiServerDetail); + d_ws->registerApiHandler("/api/v1/servers", &apiServer); } d_ws->registerWebHandler("/style.css", boost::bind(&AuthWebServer::cssfunction, this, _1, _2)); d_ws->registerWebHandler("/", boost::bind(&AuthWebServer::indexfunction, this, _1, _2)); diff --git a/pdns/ws-recursor.cc b/pdns/ws-recursor.cc index f923f6e0f..7b3318f14 100644 --- a/pdns/ws-recursor.cc +++ b/pdns/ws-recursor.cc @@ -146,7 +146,7 @@ static void fillZone(const DNSName& zonename, HttpResponse* resp) string zoneId = apiZoneNameToId(iter->first); Value jzoneid(zoneId.c_str(), doc.GetAllocator()); // copy doc.AddMember("id", jzoneid, doc.GetAllocator()); - string url = "/servers/localhost/zones/" + zoneId; + string url = "/api/v1/servers/localhost/zones/" + zoneId; Value jurl(url.c_str(), doc.GetAllocator()); // copy doc.AddMember("url", jurl, doc.GetAllocator()); Value jname(iter->first.toString().c_str(), doc.GetAllocator()); // copy @@ -308,7 +308,7 @@ static void apiServerZones(HttpRequest* req, HttpResponse* resp) string zoneId = apiZoneNameToId(val.first); Value jzoneid(zoneId.c_str(), doc.GetAllocator()); // copy jdi.AddMember("id", jzoneid, doc.GetAllocator()); - string url = "/servers/localhost/zones/" + zoneId; + string url = "/api/v1/servers/localhost/zones/" + zoneId; Value jurl(url.c_str(), doc.GetAllocator()); // copy jdi.AddMember("url", jurl, doc.GetAllocator()); jdi.AddMember("name", val.first.toString().c_str(), doc.GetAllocator()); @@ -435,16 +435,16 @@ RecursorWebServer::RecursorWebServer(FDMultiplexer* fdm) // legacy dispatch d_ws->registerApiHandler("/jsonstat", boost::bind(&RecursorWebServer::jsonstat, this, _1, _2)); - d_ws->registerApiHandler("/servers/localhost/flush-cache", &apiServerFlushCache); - d_ws->registerApiHandler("/servers/localhost/config/allow-from", &apiServerConfigAllowFrom); - d_ws->registerApiHandler("/servers/localhost/config", &apiServerConfig); - d_ws->registerApiHandler("/servers/localhost/search-log", &apiServerSearchLog); - d_ws->registerApiHandler("/servers/localhost/search-data", &apiServerSearchData); - d_ws->registerApiHandler("/servers/localhost/statistics", &apiServerStatistics); - d_ws->registerApiHandler("/servers/localhost/zones/", &apiServerZoneDetail); - d_ws->registerApiHandler("/servers/localhost/zones", &apiServerZones); - d_ws->registerApiHandler("/servers/localhost", &apiServerDetail); - d_ws->registerApiHandler("/servers", &apiServer); + d_ws->registerApiHandler("/api/v1/servers/localhost/flush-cache", &apiServerFlushCache); + d_ws->registerApiHandler("/api/v1/servers/localhost/config/allow-from", &apiServerConfigAllowFrom); + d_ws->registerApiHandler("/api/v1/servers/localhost/config", &apiServerConfig); + d_ws->registerApiHandler("/api/v1/servers/localhost/search-log", &apiServerSearchLog); + d_ws->registerApiHandler("/api/v1/servers/localhost/search-data", &apiServerSearchData); + d_ws->registerApiHandler("/api/v1/servers/localhost/statistics", &apiServerStatistics); + d_ws->registerApiHandler("/api/v1/servers/localhost/zones/", &apiServerZoneDetail); + d_ws->registerApiHandler("/api/v1/servers/localhost/zones", &apiServerZones); + d_ws->registerApiHandler("/api/v1/servers/localhost", &apiServerDetail); + d_ws->registerApiHandler("/api/v1/servers", &apiServer); d_ws->go(); } diff --git a/regression-tests.api/test_Basics.py b/regression-tests.api/test_Basics.py index 79c3d9ecf..a3d4d8136 100644 --- a/regression-tests.api/test_Basics.py +++ b/regression-tests.api/test_Basics.py @@ -7,7 +7,7 @@ from test_helper import ApiTestCase class TestBasics(ApiTestCase): def test_unauth(self): - r = requests.get(self.url("/servers/localhost")) + r = requests.get(self.url("/api/v1/servers/localhost")) self.assertEquals(r.status_code, requests.codes.unauthorized) def test_split_request(self): @@ -33,7 +33,7 @@ class TestBasics(ApiTestCase): raise Exception('Got unwanted response: %s' % status) def test_cors(self): - r = self.session.options(self.url("/servers/localhost")) + r = self.session.options(self.url("/api/v1/servers/localhost")) # look for CORS headers self.assertEquals(r.status_code, requests.codes.ok) diff --git a/regression-tests.api/test_RecursorConfig.py b/regression-tests.api/test_RecursorConfig.py index eeaa8a650..2a3c1f119 100644 --- a/regression-tests.api/test_RecursorConfig.py +++ b/regression-tests.api/test_RecursorConfig.py @@ -7,13 +7,13 @@ from test_helper import ApiTestCase, is_recursor class RecursorConfig(ApiTestCase): def test_config_allow_from_get(self): - r = self.session.get(self.url("/servers/localhost/config/allow-from")) + r = self.session.get(self.url("/api/v1/servers/localhost/config/allow-from")) self.assert_success_json(r) def test_config_allow_from_replace(self): payload = {'value': ["127.0.0.1"]} r = self.session.put( - self.url("/servers/localhost/config/allow-from"), + self.url("/api/v1/servers/localhost/config/allow-from"), data=json.dumps(payload), headers={'content-type': 'application/json'}) self.assert_success_json(r) @@ -24,7 +24,7 @@ class RecursorConfig(ApiTestCase): """Test the error case, should return 422.""" payload = {'value': ["abcdefgh"]} r = self.session.put( - self.url("/servers/localhost/config/allow-from"), + self.url("/api/v1/servers/localhost/config/allow-from"), data=json.dumps(payload), headers={'content-type': 'application/json'}) self.assertEquals(r.status_code, 422) diff --git a/regression-tests.api/test_Servers.py b/regression-tests.api/test_Servers.py index 36ef586f9..28067858f 100644 --- a/regression-tests.api/test_Servers.py +++ b/regression-tests.api/test_Servers.py @@ -4,7 +4,7 @@ from test_helper import ApiTestCase, is_auth, is_recursor class Servers(ApiTestCase): def test_list_servers(self): - r = self.session.get(self.url("/servers")) + r = self.session.get(self.url("/api/v1/servers")) self.assert_success_json(r) lst = r.json() self.assertEquals(len(lst), 1) # only localhost allowed in there @@ -14,7 +14,7 @@ class Servers(ApiTestCase): self.assertEquals(data['id'], 'localhost') def test_servers_localhost(self): - r = self.session.get(self.url("/servers/localhost")) + r = self.session.get(self.url("/api/v1/servers/localhost")) self.assert_success_json(r) data = r.json() for k in ('id', 'type', 'version', 'daemon_type', 'url', 'zones_url', 'config_url'): @@ -31,25 +31,25 @@ class Servers(ApiTestCase): self.assertEquals(data['daemon_type'], daemon_type) def test_read_config(self): - r = self.session.get(self.url("/servers/localhost/config")) + r = self.session.get(self.url("/api/v1/servers/localhost/config")) self.assert_success_json(r) data = dict([(r['name'], r['value']) for r in r.json()]) self.assertIn('daemon', data) def test_read_statistics(self): - r = self.session.get(self.url("/servers/localhost/statistics")) + r = self.session.get(self.url("/api/v1/servers/localhost/statistics")) self.assert_success_json(r) data = dict([(r['name'], r['value']) for r in r.json()]) self.assertIn('uptime', data) def test_flush_cache(self): - r = self.session.put(self.url("/servers/localhost/flush-cache?domain=example.org.")) + r = self.session.put(self.url("/api/v1/servers/localhost/flush-cache?domain=example.org.")) self.assert_success_json(r) data = r.json() self.assertIn('count', data) def test_flush_complete_cache(self): - r = self.session.put(self.url("/servers/localhost/flush-cache")) + r = self.session.put(self.url("/api/v1/servers/localhost/flush-cache")) self.assert_success_json(r) data = r.json() self.assertIn('count', data) diff --git a/regression-tests.api/test_Zones.py b/regression-tests.api/test_Zones.py index 26f62e421..e7aabbe30 100644 --- a/regression-tests.api/test_Zones.py +++ b/regression-tests.api/test_Zones.py @@ -7,7 +7,7 @@ from test_helper import ApiTestCase, unique_zone_name, is_auth, is_recursor class Zones(ApiTestCase): def test_list_zones(self): - r = self.session.get(self.url("/servers/localhost/zones")) + r = self.session.get(self.url("/api/v1/servers/localhost/zones")) self.assert_success_json(r) domains = r.json() example_com = [domain for domain in domains if domain['name'] in ('example.com', 'example.com.')] @@ -38,7 +38,7 @@ class AuthZonesHelperMixin(object): payload[k] = v print payload r = self.session.post( - self.url("/servers/localhost/zones"), + self.url("/api/v1/servers/localhost/zones"), data=json.dumps(payload), headers={'content-type': 'application/json'}) self.assert_success_json(r) @@ -159,7 +159,7 @@ class AuthZones(ApiTestCase, AuthZonesHelperMixin): } print payload r = self.session.post( - self.url("/servers/localhost/zones"), + self.url("/api/v1/servers/localhost/zones"), data=json.dumps(payload), headers={'content-type': 'application/json'}) self.assertEquals(r.status_code, 422) @@ -173,12 +173,12 @@ class AuthZones(ApiTestCase, AuthZonesHelperMixin): print "payload:", payload print "data:", data # Because slave zones don't get a SOA, we need to test that they'll show up in the zone list. - r = self.session.get(self.url("/servers/localhost/zones")) + r = self.session.get(self.url("/api/v1/servers/localhost/zones")) zonelist = r.json() print "zonelist:", zonelist self.assertIn(payload['name'], [zone['name'] for zone in zonelist]) # Also test that fetching the zone works. - r = self.session.get(self.url("/servers/localhost/zones/" + data['id'])) + r = self.session.get(self.url("/api/v1/servers/localhost/zones/" + data['id'])) data = r.json() print "zone (fetched):", data for k in ('name', 'masters', 'kind'): @@ -189,14 +189,14 @@ class AuthZones(ApiTestCase, AuthZonesHelperMixin): def test_delete_slave_zone(self): payload, data = self.create_zone(kind='Slave', nameservers=None, masters=['127.0.0.2']) - r = self.session.delete(self.url("/servers/localhost/zones/" + data['id'])) + r = self.session.delete(self.url("/api/v1/servers/localhost/zones/" + data['id'])) r.raise_for_status() def test_retrieve_slave_zone(self): payload, data = self.create_zone(kind='Slave', nameservers=None, masters=['127.0.0.2']) print "payload:", payload print "data:", data - r = self.session.put(self.url("/servers/localhost/zones/" + data['id'] + "/axfr-retrieve")) + r = self.session.put(self.url("/api/v1/servers/localhost/zones/" + data['id'] + "/axfr-retrieve")) data = r.json() print "status for axfr-retrieve:", data self.assertEqual(data['result'], u'Added retrieval request for \'' + payload['name'] + @@ -206,7 +206,7 @@ class AuthZones(ApiTestCase, AuthZonesHelperMixin): payload, data = self.create_zone(kind='Master') print "payload:", payload print "data:", data - r = self.session.put(self.url("/servers/localhost/zones/" + data['id'] + "/notify")) + r = self.session.put(self.url("/api/v1/servers/localhost/zones/" + data['id'] + "/notify")) data = r.json() print "status for notify:", data self.assertEqual(data['result'], 'Notification queued') @@ -215,7 +215,7 @@ class AuthZones(ApiTestCase, AuthZonesHelperMixin): payload, data = self.create_zone(name='foo/bar.'+unique_zone_name()) name = payload['name'] zone_id = (name.replace('/', '=2F')) + '.' - r = self.session.get(self.url("/servers/localhost/zones/" + zone_id)) + r = self.session.get(self.url("/api/v1/servers/localhost/zones/" + zone_id)) data = r.json() for k in ('id', 'url', 'name', 'masters', 'kind', 'last_check', 'notified_serial', 'serial', 'dnssec'): self.assertIn(k, data) @@ -223,10 +223,10 @@ class AuthZones(ApiTestCase, AuthZonesHelperMixin): self.assertEquals(data[k], payload[k]) def test_get_zone(self): - r = self.session.get(self.url("/servers/localhost/zones")) + r = self.session.get(self.url("/api/v1/servers/localhost/zones")) domains = r.json() example_com = [domain for domain in domains if domain['name'] == u'example.com'][0] - r = self.session.get(self.url("/servers/localhost/zones/" + example_com['id'])) + r = self.session.get(self.url("/api/v1/servers/localhost/zones/" + example_com['id'])) self.assert_success_json(r) data = r.json() for k in ('id', 'url', 'name', 'masters', 'kind', 'last_check', 'notified_serial', 'serial'): @@ -258,7 +258,7 @@ powerdns-broken.com. 86400 IN SOA powerdnssec1.ds9a.nl. ahu payload['kind'] = 'Master' payload['nameservers'] = [] r = self.session.post( - self.url("/servers/localhost/zones"), + self.url("/api/v1/servers/localhost/zones"), data=json.dumps(payload), headers={'content-type': 'application/json'}) self.assertEquals(r.status_code, 422) @@ -288,7 +288,7 @@ powerdns.com. 86400 IN SOA powerdnssec1.ds9a.nl. ahu.ds9a.n payload['kind'] = 'Master' payload['nameservers'] = [] r = self.session.post( - self.url("/servers/localhost/zones"), + self.url("/api/v1/servers/localhost/zones"), data=json.dumps(payload), headers={'content-type': 'application/json'}) self.assert_success_json(r) @@ -351,7 +351,7 @@ fred IN A 192.168.0.4 payload['kind'] = 'Master' payload['nameservers'] = [] r = self.session.post( - self.url("/servers/localhost/zones"), + self.url("/api/v1/servers/localhost/zones"), data=json.dumps(payload), headers={'content-type': 'application/json'}) self.assert_success_json(r) @@ -394,7 +394,7 @@ fred IN A 192.168.0.4 name = payload['name'] # export it r = self.session.get( - self.url("/servers/localhost/zones/" + name + "/export"), + self.url("/api/v1/servers/localhost/zones/" + name + "/export"), headers={'accept': 'application/json;q=0.9,*/*;q=0.8'} ) self.assert_success_json(r) @@ -411,7 +411,7 @@ fred IN A 192.168.0.4 name = payload['name'] # export it r = self.session.get( - self.url("/servers/localhost/zones/" + name + "/export"), + self.url("/api/v1/servers/localhost/zones/" + name + "/export"), headers={'accept': '*/*'} ) data = r.text.strip().split("\n") @@ -432,7 +432,7 @@ fred IN A 192.168.0.4 'soa_edit': 'EPOCH' } r = self.session.put( - self.url("/servers/localhost/zones/" + name), + self.url("/api/v1/servers/localhost/zones/" + name), data=json.dumps(payload), headers={'content-type': 'application/json'}) self.assert_success_json(r) @@ -447,7 +447,7 @@ fred IN A 192.168.0.4 'soa_edit': '' } r = self.session.put( - self.url("/servers/localhost/zones/" + name), + self.url("/api/v1/servers/localhost/zones/" + name), data=json.dumps(payload), headers={'content-type': 'application/json'}) self.assert_success_json(r) @@ -483,12 +483,12 @@ fred IN A 192.168.0.4 } payload = {'rrsets': [rrset]} r = self.session.patch( - self.url("/servers/localhost/zones/" + name), + self.url("/api/v1/servers/localhost/zones/" + name), data=json.dumps(payload), headers={'content-type': 'application/json'}) self.assert_success_json(r) # verify that (only) the new record is there - r = self.session.get(self.url("/servers/localhost/zones/" + name)) + r = self.session.get(self.url("/api/v1/servers/localhost/zones/" + name)) rrset['type'] = rrset['type'].upper() data = r.json()['records'] recs = [rec for rec in data if rec['type'] == rrset['type'] and rec['name'] == rrset['name']] @@ -515,12 +515,12 @@ fred IN A 192.168.0.4 } payload = {'rrsets': [rrset]} r = self.session.patch( - self.url("/servers/localhost/zones/" + name), + self.url("/api/v1/servers/localhost/zones/" + name), data=json.dumps(payload), headers={'content-type': 'application/json'}) self.assert_success_json(r) # verify that (only) the new record is there - r = self.session.get(self.url("/servers/localhost/zones/" + name)) + r = self.session.get(self.url("/api/v1/servers/localhost/zones/" + name)) data = r.json()['records'] recs = [rec for rec in data if rec['type'] == rrset['type'] and rec['name'] == rrset['name']] self.assertEquals(recs, rrset['records']) @@ -558,12 +558,12 @@ fred IN A 192.168.0.4 } payload = {'rrsets': [rrset1, rrset2]} r = self.session.patch( - self.url("/servers/localhost/zones/" + name), + self.url("/api/v1/servers/localhost/zones/" + name), data=json.dumps(payload), headers={'content-type': 'application/json'}) self.assert_success_json(r) # verify that all rrsets have been updated - r = self.session.get(self.url("/servers/localhost/zones/" + name)) + r = self.session.get(self.url("/api/v1/servers/localhost/zones/" + name)) data = r.json()['records'] recs1 = [rec for rec in data if rec['type'] == rrset1['type'] and rec['name'] == rrset1['name']] self.assertEquals(recs1, rrset1['records']) @@ -581,12 +581,12 @@ fred IN A 192.168.0.4 } payload = {'rrsets': [rrset]} r = self.session.patch( - self.url("/servers/localhost/zones/" + name), + self.url("/api/v1/servers/localhost/zones/" + name), data=json.dumps(payload), headers={'content-type': 'application/json'}) self.assert_success_json(r) # verify that the records are gone - r = self.session.get(self.url("/servers/localhost/zones/" + name)) + r = self.session.get(self.url("/api/v1/servers/localhost/zones/" + name)) data = r.json()['records'] recs = [rec for rec in data if rec['type'] == rrset['type'] and rec['name'] == rrset['name']] self.assertEquals(recs, []) @@ -612,7 +612,7 @@ fred IN A 192.168.0.4 } payload = {'rrsets': [rrset]} r = self.session.patch( - self.url("/servers/localhost/zones/" + name), + self.url("/api/v1/servers/localhost/zones/" + name), data=json.dumps(payload), headers={'content-type': 'application/json'}) self.assert_success_json(r) @@ -621,7 +621,7 @@ fred IN A 192.168.0.4 soa_serial1 = [rec for rec in r.json()['records'] if rec['type'] == 'SOA'][0]['content'].split()[2] self.assertNotEquals(soa_serial1, '1') # make sure domain is still in zone list (disabled SOA!) - r = self.session.get(self.url("/servers/localhost/zones")) + r = self.session.get(self.url("/api/v1/servers/localhost/zones")) domains = r.json() self.assertEquals(len([domain for domain in domains if domain['name'] == name]), 1) # sleep 1sec to ensure the EPOCH value changes for the next request @@ -630,7 +630,7 @@ fred IN A 192.168.0.4 rrset['records'][0]['disabled'] = False payload = {'rrsets': [rrset]} r = self.session.patch( - self.url("/servers/localhost/zones/" + name), + self.url("/api/v1/servers/localhost/zones/" + name), data=json.dumps(payload), headers={'content-type': 'application/json'}) self.assert_success_json(r) @@ -660,7 +660,7 @@ fred IN A 192.168.0.4 } payload = {'rrsets': [rrset]} r = self.session.patch( - self.url("/servers/localhost/zones/" + name), + self.url("/api/v1/servers/localhost/zones/" + name), data=json.dumps(payload), headers={'content-type': 'application/json'}) self.assertEquals(r.status_code, 422) @@ -685,7 +685,7 @@ fred IN A 192.168.0.4 } payload = {'rrsets': [rrset]} r = self.session.patch( - self.url("/servers/localhost/zones/" + name), + self.url("/api/v1/servers/localhost/zones/" + name), data=json.dumps(payload), headers={'content-type': 'application/json'}) self.assertEquals(r.status_code, 422) @@ -710,7 +710,7 @@ fred IN A 192.168.0.4 } payload = {'rrsets': [rrset]} r = self.session.patch( - self.url("/servers/localhost/zones/" + name), + self.url("/api/v1/servers/localhost/zones/" + name), data=json.dumps(payload), headers={'content-type': 'application/json'}) self.assertEquals(r.status_code, 422) @@ -734,7 +734,7 @@ fred IN A 192.168.0.4 ] } payload = {'rrsets': [rrset]} - r = self.session.patch(self.url("/servers/localhost/zones/" + name), data=json.dumps(payload), + r = self.session.patch(self.url("/api/v1/servers/localhost/zones/" + name), data=json.dumps(payload), headers={'content-type': 'application/json'}) self.assertEquals(r.status_code, 422) self.assertIn('unknown type', r.json()['error']) @@ -758,7 +758,7 @@ fred IN A 192.168.0.4 ] } payload = {'rrsets': [rrset]} - r = self.session.patch(self.url("/servers/localhost/zones/" + name), data=json.dumps(payload), + r = self.session.patch(self.url("/api/v1/servers/localhost/zones/" + name), data=json.dumps(payload), headers={'content-type': 'application/json'}) self.assertEquals(r.status_code, 422) self.assertIn('Not in expected format', r.json()['error']) @@ -773,7 +773,7 @@ fred IN A 192.168.0.4 } payload = {'rrsets': [rrset]} r = self.session.patch( - self.url("/servers/localhost/zones/" + name), + self.url("/api/v1/servers/localhost/zones/" + name), data=json.dumps(payload), headers={'content-type': 'application/json'}) print r.content @@ -782,7 +782,7 @@ fred IN A 192.168.0.4 def test_zone_delete(self): payload, zone = self.create_zone() name = payload['name'] - r = self.session.delete(self.url("/servers/localhost/zones/" + name)) + r = self.session.delete(self.url("/api/v1/servers/localhost/zones/" + name)) self.assertEquals(r.status_code, 204) self.assertNotIn('Content-Type', r.headers) @@ -806,13 +806,13 @@ fred IN A 192.168.0.4 } payload = {'rrsets': [rrset]} r = self.session.patch( - self.url("/servers/localhost/zones/" + name), + self.url("/api/v1/servers/localhost/zones/" + name), data=json.dumps(payload), headers={'content-type': 'application/json'}) self.assert_success_json(r) # make sure the comments have been set, and that the NS # records are still present - r = self.session.get(self.url("/servers/localhost/zones/" + name)) + r = self.session.get(self.url("/api/v1/servers/localhost/zones/" + name)) data = r.json() print data self.assertNotEquals([r for r in data['records'] if r['type'] == 'NS'], []) @@ -832,12 +832,12 @@ fred IN A 192.168.0.4 } payload = {'rrsets': [rrset]} r = self.session.patch( - self.url("/servers/localhost/zones/" + name), + self.url("/api/v1/servers/localhost/zones/" + name), data=json.dumps(payload), headers={'content-type': 'application/json'}) self.assert_success_json(r) # make sure the NS records are still present - r = self.session.get(self.url("/servers/localhost/zones/" + name)) + r = self.session.get(self.url("/api/v1/servers/localhost/zones/" + name)) data = r.json() print data self.assertNotEquals([r for r in data['records'] if r['type'] == 'NS'], []) @@ -862,7 +862,7 @@ fred IN A 192.168.0.4 } payload = {'rrsets': [rrset]} r = self.session.patch( - self.url("/servers/localhost/zones/" + name), + self.url("/api/v1/servers/localhost/zones/" + name), data=json.dumps(payload), headers={'content-type': 'application/json'}) self.assert_success_json(r) @@ -883,12 +883,12 @@ fred IN A 192.168.0.4 } payload2 = {'rrsets': [rrset2]} r = self.session.patch( - self.url("/servers/localhost/zones/" + name), + self.url("/api/v1/servers/localhost/zones/" + name), data=json.dumps(payload2), headers={'content-type': 'application/json'}) self.assert_success_json(r) # make sure the comments still exist - r = self.session.get(self.url("/servers/localhost/zones/" + name)) + r = self.session.get(self.url("/api/v1/servers/localhost/zones/" + name)) data = r.json() print data # fix up input data for comparison with assertEquals. @@ -923,11 +923,11 @@ fred IN A 192.168.0.4 } payload = {'rrsets': [rrset]} r = self.session.patch( - self.url("/servers/localhost/zones/" + name), + self.url("/api/v1/servers/localhost/zones/" + name), data=json.dumps(payload), headers={'content-type': 'application/json'}) self.assert_success_json(r) - r = self.session.get(self.url("/servers/localhost/zones/" + revzone)) + r = self.session.get(self.url("/api/v1/servers/localhost/zones/" + revzone)) recs = r.json()['records'] print recs revrec = [rec for rec in recs if rec['type'] == 'PTR'] @@ -963,11 +963,11 @@ fred IN A 192.168.0.4 } payload = {'rrsets': [rrset]} r = self.session.patch( - self.url("/servers/localhost/zones/" + name), + self.url("/api/v1/servers/localhost/zones/" + name), data=json.dumps(payload), headers={'content-type': 'application/json'}) self.assert_success_json(r) - r = self.session.get(self.url("/servers/localhost/zones/" + revzone)) + r = self.session.get(self.url("/api/v1/servers/localhost/zones/" + revzone)) recs = r.json()['records'] print recs revrec = [rec for rec in recs if rec['type'] == 'PTR'] @@ -982,7 +982,7 @@ fred IN A 192.168.0.4 def test_search_rr_exact_zone(self): name = unique_zone_name() self.create_zone(name=name) - r = self.session.get(self.url("/servers/localhost/search-data?q=" + name)) + r = self.session.get(self.url("/api/v1/servers/localhost/search-data?q=" + name)) self.assert_success_json(r) print r.json() self.assertEquals(r.json(), [{u'object_type': u'zone', u'name': name, u'zone_id': name+'.'}]) @@ -990,7 +990,7 @@ fred IN A 192.168.0.4 def test_search_rr_substring(self): name = 'search-rr-zone.name' self.create_zone(name=name) - r = self.session.get(self.url("/servers/localhost/search-data?q=*rr-zone*")) + r = self.session.get(self.url("/api/v1/servers/localhost/search-data?q=*rr-zone*")) self.assert_success_json(r) print r.json() # should return zone, SOA, ns1, ns2 @@ -999,7 +999,7 @@ fred IN A 192.168.0.4 def test_search_rr_case_insensitive(self): name = 'search-rr-insenszone.name' self.create_zone(name=name) - r = self.session.get(self.url("/servers/localhost/search-data?q=*rr-insensZONE*")) + r = self.session.get(self.url("/api/v1/servers/localhost/search-data?q=*rr-insensZONE*")) self.assert_success_json(r) print r.json() # should return zone, SOA, ns1, ns2 @@ -1012,7 +1012,7 @@ class AuthRootZone(ApiTestCase, AuthZonesHelperMixin): def setUp(self): super(AuthRootZone, self).setUp() # zone name is not unique, so delete the zone before each individual test. - self.session.delete(self.url("/servers/localhost/zones/=2E")) + self.session.delete(self.url("/api/v1/servers/localhost/zones/=2E")) def test_create_zone(self): payload, data = self.create_zone(name='', serial=22, soa_edit_api='') @@ -1028,13 +1028,13 @@ class AuthRootZone(ApiTestCase, AuthZonesHelperMixin): " 10800 3600 604800 3600" ) # Regression test: verify zone list works - zonelist = self.session.get(self.url("/servers/localhost/zones")).json() + zonelist = self.session.get(self.url("/api/v1/servers/localhost/zones")).json() print "zonelist:", zonelist self.assertIn(payload['name'], [zone['name'] for zone in zonelist]) # Also test that fetching the zone works. print "id:", data['id'] self.assertEquals(data['id'], '=2E') - data = self.session.get(self.url("/servers/localhost/zones/" + data['id'])).json() + data = self.session.get(self.url("/api/v1/servers/localhost/zones/" + data['id'])).json() print "zone (fetched):", data for k in ('name', 'kind'): self.assertIn(k, data) @@ -1053,7 +1053,7 @@ class AuthRootZone(ApiTestCase, AuthZonesHelperMixin): 'soa_edit': 'EPOCH' } r = self.session.put( - self.url("/servers/localhost/zones/" + zone_id), + self.url("/api/v1/servers/localhost/zones/" + zone_id), data=json.dumps(payload), headers={'content-type': 'application/json'}) self.assert_success_json(r) @@ -1068,7 +1068,7 @@ class AuthRootZone(ApiTestCase, AuthZonesHelperMixin): 'soa_edit': '' } r = self.session.put( - self.url("/servers/localhost/zones/" + zone_id), + self.url("/api/v1/servers/localhost/zones/" + zone_id), data=json.dumps(payload), headers={'content-type': 'application/json'}) self.assert_success_json(r) @@ -1093,7 +1093,7 @@ class RecursorZones(ApiTestCase): 'recursion_desired': rd } r = self.session.post( - self.url("/servers/localhost/zones"), + self.url("/api/v1/servers/localhost/zones"), data=json.dumps(payload), headers={'content-type': 'application/json'}) self.assert_success_json(r) @@ -1141,7 +1141,7 @@ class RecursorZones(ApiTestCase): 'recursion_desired': False } r = self.session.put( - self.url("/servers/localhost/zones/" + name), + self.url("/api/v1/servers/localhost/zones/" + name), data=json.dumps(payload), headers={'content-type': 'application/json'}) self.assert_success_json(r) @@ -1152,14 +1152,14 @@ class RecursorZones(ApiTestCase): def test_zone_delete(self): payload, zone = self.create_zone(kind='Native') name = payload['name'] - r = self.session.delete(self.url("/servers/localhost/zones/" + name)) + r = self.session.delete(self.url("/api/v1/servers/localhost/zones/" + name)) self.assertEquals(r.status_code, 204) self.assertNotIn('Content-Type', r.headers) def test_search_rr_exact_zone(self): name = unique_zone_name() + '.' self.create_zone(name=name, kind='Native') - r = self.session.get(self.url("/servers/localhost/search-data?q=" + name)) + r = self.session.get(self.url("/api/v1/servers/localhost/search-data?q=" + name)) self.assert_success_json(r) print r.json() self.assertEquals(r.json(), [{u'type': u'zone', u'name': name, u'zone_id': name}]) @@ -1167,7 +1167,7 @@ class RecursorZones(ApiTestCase): def test_search_rr_substring(self): name = 'search-rr-zone.name' self.create_zone(name=name, kind='Native') - r = self.session.get(self.url("/servers/localhost/search-data?q=rr-zone")) + r = self.session.get(self.url("/api/v1/servers/localhost/search-data?q=rr-zone")) self.assert_success_json(r) print r.json() # should return zone, SOA -- 2.40.0