diff --git a/src/composer/cli/blueprints.py b/src/composer/cli/blueprints.py index d4fc33b7..1872e2fe 100644 --- a/src/composer/cli/blueprints.py +++ b/src/composer/cli/blueprints.py @@ -72,7 +72,7 @@ def blueprints_list(socket_path, api_version, args, show_json=False): blueprints list """ api_route = client.api_url(api_version, "/blueprints/list") - result = client.get_url_json(socket_path, api_route) + result = client.get_url_json_unlimited(socket_path, api_route) (rc, exit_now) = handle_api_result(result, show_json) if exit_now: return rc @@ -119,7 +119,7 @@ def blueprints_changes(socket_path, api_version, args, show_json=False): blueprints changes Display the changes for each blueprint. """ api_route = client.api_url(api_version, "/blueprints/changes/%s" % (",".join(argify(args)))) - result = client.get_url_json(socket_path, api_route) + result = client.get_url_json_unlimited(socket_path, api_route) (rc, exit_now) = handle_api_result(result, show_json) if exit_now: return rc diff --git a/src/composer/cli/modules.py b/src/composer/cli/modules.py index cd377838..0a775738 100644 --- a/src/composer/cli/modules.py +++ b/src/composer/cli/modules.py @@ -37,7 +37,7 @@ def modules_cmd(opts): return 1 api_route = client.api_url(opts.api_version, "/modules/list") - result = client.get_url_json(opts.socket, api_route) + result = client.get_url_json_unlimited(opts.socket, api_route) (rc, exit_now) = handle_api_result(result, opts.json) if exit_now: return rc diff --git a/src/composer/cli/projects.py b/src/composer/cli/projects.py index 39c56085..f2a5b683 100644 --- a/src/composer/cli/projects.py +++ b/src/composer/cli/projects.py @@ -59,7 +59,7 @@ def projects_list(socket_path, api_version, args, show_json=False): projects list """ api_route = client.api_url(api_version, "/projects/list") - result = client.get_url_json(socket_path, api_route) + result = client.get_url_json_unlimited(socket_path, api_route) (rc, exit_now) = handle_api_result(result, show_json) if exit_now: return rc diff --git a/src/composer/http_client.py b/src/composer/http_client.py index ebbac2af..ff356bbf 100644 --- a/src/composer/http_client.py +++ b/src/composer/http_client.py @@ -20,6 +20,7 @@ log = logging.getLogger("composer-cli") import os import sys import json +from urlparse import urlparse, urlunparse from composer.unix_socket import UnixHTTPConnectionPool @@ -35,6 +36,29 @@ def api_url(api_version, url): """ return os.path.normpath("/api/v%s/%s" % (api_version, url)) +def append_query(url, query): + """Add a query argument to a URL + + The query should be of the form "param1=what¶m2=ever", i.e., no + leading '?'. The new query data will be appended to any existing + query string. + + :param url: The original URL + :type url: str + :param query: The query to append + :type query: str + :returns: The new URL with the query argument included + :rtype: str + """ + + url_parts = urlparse(url) + if url_parts.query: + new_query = url_parts.query + "&" + query + else: + new_query = query + return urlunparse([url_parts[0], url_parts[1], url_parts[2], + url_parts[3], new_query, url_parts[5]]) + def get_url_raw(socket_path, url): """Return the raw results of a GET request @@ -69,6 +93,31 @@ def get_url_json(socket_path, url): r = http.request("GET", url) return json.loads(r.data.decode('utf-8')) +def get_url_json_unlimited(socket_path, url): + """Return the JSON results of a GET request + + For URLs that use offset/limit arguments, this command will + fetch all results for the given request. + + :param socket_path: Path to the Unix socket to use for API communication + :type socket_path: str + :param url: URL to request + :type url: str + :returns: The json response from the server + :rtype: dict + """ + http = UnixHTTPConnectionPool(socket_path) + + # Start with limit=0 to just get the number of objects + total_url = append_query(url, "limit=0") + r_total = http.request("GET", total_url) + json_total = json.loads(r_total.data.decode('utf-8')) + + # Add the "total" returned by limit=0 as the new limit + unlimited_url = append_query(url, "limit=%d" % json_total["total"]) + r_unlimited = http.request("GET", unlimited_url) + return json.loads(r_unlimited.data.decode('utf-8')) + def delete_url_json(socket_path, url): """Send a DELETE request to the url and return JSON response