def create_tls_artifacts(cn: str, marathon_task: str) -> str: pub_path = "{}_pub.crt".format(cn) priv_path = "{}_priv.key".format(cn) log.info("Generating certificate. cn={}, task={}".format(cn, marathon_task)) output = sdk_cmd.marathon_task_exec( marathon_task, "openssl req -nodes -newkey rsa:2048 -keyout {} -out request.csr " '-subj "/C=US/ST=CA/L=SF/O=Mesosphere/OU=Mesosphere/CN={}"'.format(priv_path, cn), ) assert output[0] == 0 rc, raw_csr, _ = sdk_cmd.marathon_task_exec(marathon_task, "cat request.csr") assert rc == 0 request = {"certificate_request": raw_csr} output = sdk_cmd.marathon_task_exec( marathon_task, "curl --insecure -L -X POST " "-H 'Authorization: token={}' " "leader.mesos/ca/api/v2/sign " "-d '{}'".format(sdk_utils.dcos_token(), json.dumps(request)), ) assert output[0] == 0 # Write the public cert to the client certificate = json.loads(output[1])["result"]["certificate"] output = sdk_cmd.marathon_task_exec( marathon_task, "bash -c \"echo '{}' > {}\"".format(certificate, pub_path) ) assert output[0] == 0 _create_keystore_truststore(cn, marathon_task) return "CN={},OU=Mesosphere,O=Mesosphere,L=SF,ST=CA,C=US".format(cn)
def _retried_run_janitor(service_name): cmd_list = [ "docker", "run", "mesosphere/janitor", "/janitor.py", "-r", sdk_utils.get_role(service_name), "-p", service_name + "-principal", "-z", sdk_utils.get_zk_path(service_name), "--auth_token={}".format(sdk_utils.dcos_token()), ] rc, _, _ = sdk_cmd.master_ssh(" ".join(cmd_list)) assert rc == 0, "Janitor command failed"
def create_tls_artifacts(cn: str, marathon_task: str) -> str: pub_path = "{}_pub.crt".format(cn) priv_path = "{}_priv.key".format(cn) log.info("Generating certificate. cn={}, task={}".format( cn, marathon_task)) output = sdk_cmd.marathon_task_exec( marathon_task, "openssl req -nodes -newkey rsa:2048 -keyout {} -out request.csr " '-subj "/C=US/ST=CA/L=SF/O=Mesosphere/OU=Mesosphere/CN={}"'.format( priv_path, cn), ) assert output[0] == 0 rc, raw_csr, _ = sdk_cmd.marathon_task_exec(marathon_task, "cat request.csr") assert rc == 0 request = {"certificate_request": raw_csr} output = sdk_cmd.marathon_task_exec( marathon_task, "curl --insecure -L -X POST " "-H 'Authorization: token={}' " "leader.mesos/ca/api/v2/sign " "-d '{}'".format(sdk_utils.dcos_token(), json.dumps(request)), ) assert output[0] == 0 # Write the public cert to the client certificate = json.loads(output[1])["result"]["certificate"] output = sdk_cmd.marathon_task_exec( marathon_task, "bash -c \"echo '{}' > {}\"".format(certificate, pub_path)) assert output[0] == 0 _create_keystore_truststore(cn, marathon_task) return "CN={},OU=Mesosphere,O=Mesosphere,L=SF,ST=CA,C=US".format(cn)
def check_kibana_adminrouter_integration(path: str) -> bool: curl_cmd = 'curl -L -I -k -H "Authorization: token={}" -s -X GET {}/{}'.format( sdk_utils.dcos_token(), sdk_utils.dcos_url().rstrip("/"), path.lstrip("/")) rc, stdout, _ = sdk_cmd.master_ssh(curl_cmd) return bool(rc == 0 and stdout and "HTTP/1.1 200" in stdout)
def check_kibana_adminrouter_integration(path: str) -> bool: curl_cmd = 'curl -L -I -k -H "Authorization: token={}" -s {}/{}'.format( sdk_utils.dcos_token(), sdk_utils.dcos_url().rstrip("/"), path.lstrip("/") ) rc, stdout, _ = sdk_cmd.master_ssh(curl_cmd) return bool(rc == 0 and stdout and "HTTP/1.1 200" in stdout)
def cluster_request( method: str, cluster_path: str, retry: bool = True, raise_on_error: bool = True, log_args: bool = True, log_response: bool = False, timeout_seconds: int = 60, **kwargs: Any, ) -> requests.Response: """Queries the provided cluster HTTP path using the provided method, with the following handy features: - The DCOS cluster's URL is automatically applied to the provided path. - Auth headers are automatically added. - If the response code is >= 400, optionally retries and / or raises a `requests.exceptions.HTTPError`. : param method: Method to use for the query, such as `GET`, `POST`, `DELETE`, or `PUT`. : param cluster_path: HTTP path to be queried on the cluster, e.g. `/ marathon / v2 / apps`. Leading slash is optional. : param retry: Whether to retry the request automatically if an HTTP error (>= 400) is returned. : param raise_on_error: Whether to raise a `requests.exceptions.HTTPError` if the response code is >= 400. Disabling this effectively implies `retry = False` where HTTP status is concerned. : param log_args: Whether to log the contents of `kwargs`. Can be disabled to reduce noise. : param log_response: Whether to always log the response content. Otherwise responses are only logged if the response code is >= 400. : param kwargs: Additional arguments to requests.request(), such as `json = {"example": "content"}` or `params = {"example": "param"}`. : rtype: requests.Response """ url = urllib.parse.urljoin(sdk_utils.dcos_url(), cluster_path) # consistently include slash prefix for clearer logging below cluster_path = "/" + cluster_path.lstrip("/") # Wrap token in callback for requests library to invoke: class AuthHeader(requests.auth.AuthBase): def __init__(self, token: str) -> None: self._token = token def __call__(self, r: requests.Request) -> requests.Request: r.headers["Authorization"] = "token={}".format(self._token) return r auth = AuthHeader(sdk_utils.dcos_token()) def _cluster_request() -> requests.Response: start = time.time() # check if we have verify key already exists. if kwargs is not None and kwargs.get("verify") is not None: kwargs["verify"] = False response = requests.request(method, url, auth=auth, timeout=timeout_seconds, **kwargs) else: response = requests.request( method, url, auth=auth, verify=False, timeout=timeout_seconds, **kwargs ) end = time.time() log_msg = "(HTTP {}) {}".format(method.upper(), cluster_path) if kwargs: # log arg content (or just arg names, with hack to avoid 'dict_keys([...])') if present log_msg += " (args: {})".format(kwargs if log_args else [e for e in kwargs.keys()]) log_msg += " => {} ({})".format( response.status_code, sdk_utils.pretty_duration(end - start) ) log.info(log_msg) if log_response or not response.ok: # Response logging enabled, or query failed (>= 400). Before (potentially) throwing, # print response payload which may include additional error details. response_text = response.text if response_text: log.info( "Response content ({} bytes):\n{}".format(len(response_text), response_text) ) else: log.info("No response content") if raise_on_error: response.raise_for_status() return response if retry: # Use wrapper to implement retry: @retrying.retry(wait_fixed=1000, stop_max_delay=timeout_seconds * 1000) def retry_fn() -> requests.Response: return _cluster_request() response = retry_fn() assert isinstance(response, requests.Response) return response else: # No retry, invoke directly: return _cluster_request()
def cluster_request( method: str, cluster_path: str, retry: bool = True, raise_on_error: bool = True, log_args: bool = True, log_response: bool = False, timeout_seconds: int = 60, **kwargs: Any, ) -> requests.Response: """Queries the provided cluster HTTP path using the provided method, with the following handy features: - The DCOS cluster's URL is automatically applied to the provided path. - Auth headers are automatically added. - If the response code is >= 400, optionally retries and / or raises a `requests.exceptions.HTTPError`. : param method: Method to use for the query, such as `GET`, `POST`, `DELETE`, or `PUT`. : param cluster_path: HTTP path to be queried on the cluster, e.g. `/ marathon / v2 / apps`. Leading slash is optional. : param retry: Whether to retry the request automatically if an HTTP error (>= 400) is returned. : param raise_on_error: Whether to raise a `requests.exceptions.HTTPError` if the response code is >= 400. Disabling this effectively implies `retry = False` where HTTP status is concerned. : param log_args: Whether to log the contents of `kwargs`. Can be disabled to reduce noise. : param log_response: Whether to always log the response content. Otherwise responses are only logged if the response code is >= 400. : param kwargs: Additional arguments to requests.request(), such as `json = {"example": "content"}` or `params = {"example": "param"}`. : rtype: requests.Response """ url = urllib.parse.urljoin(sdk_utils.dcos_url(), cluster_path) # consistently include slash prefix for clearer logging below cluster_path = "/" + cluster_path.lstrip("/") # Wrap token in callback for requests library to invoke: class AuthHeader(requests.auth.AuthBase): def __init__(self, token: str) -> None: self._token = token def __call__(self, r: requests.Request) -> requests.Request: r.headers["Authorization"] = "token={}".format(self._token) return r auth = AuthHeader(sdk_utils.dcos_token()) def _cluster_request() -> requests.Response: start = time.time() # check if we have verify key already exists. if kwargs is not None and kwargs.get("verify") is not None: kwargs["verify"] = False response = requests.request(method, url, auth=auth, timeout=timeout_seconds, **kwargs) else: response = requests.request(method, url, auth=auth, verify=False, timeout=timeout_seconds, **kwargs) end = time.time() log_msg = "(HTTP {}) {}".format(method.upper(), cluster_path) if kwargs: # log arg content (or just arg names, with hack to avoid 'dict_keys([...])') if present log_msg += " (args: {})".format( kwargs if log_args else [e for e in kwargs.keys()]) log_msg += " => {} ({})".format(response.status_code, sdk_utils.pretty_duration(end - start)) log.info(log_msg) if log_response or not response.ok: # Response logging enabled, or query failed (>= 400). Before (potentially) throwing, # print response payload which may include additional error details. response_text = response.text if response_text: log.info("Response content ({} bytes):\n{}".format( len(response_text), response_text)) else: log.info("No response content") if raise_on_error: response.raise_for_status() return response if retry: # Use wrapper to implement retry: @retrying.retry(wait_fixed=1000, stop_max_delay=timeout_seconds * 1000) def retry_fn() -> requests.Response: return _cluster_request() response = retry_fn() assert isinstance(response, requests.Response) return response else: # No retry, invoke directly: return _cluster_request()