def setup_cluster(dcos_url, username, password, ssl_verify=True, refresh_auth=False): """Setup a new connection to a DC/OS cluster. :returns: the cluster that was connected to :rtype: str """ url = util.normalize_url(dcos_url) # first see if it is already configured cluster = get_cluster(url) if cluster is not None: cluster.set_attached() if refresh_auth: auth.dcos_uid_password_auth(url, username, password) return cluster with setup_directory() as tempdir: set_attached(tempdir) # in python 2 this url NEEDS to be a str # otherwise for some reason toml messes up config.set_val("core.dcos_url", str(url)) config.set_val("core.ssl_verify", ssl_verify) auth.dcos_uid_password_auth(url, username, password) return setup_cluster_config(url, tempdir, False)
def _store_cluster_cert(cert, no_check): """Store cluster certificate bundle downloaded from cluster and store settings in core.ssl_verify :param cert: ca cert from cluster :type cert: str :param no_check: whether to verify downloaded cert :type no_check: bool :returns: whether or not we are storing the downloaded cert bundle :rtype: bool """ if not no_check: if not _user_cert_validation(cert): # we don't have a cert, but we still want to validate SSL config.set_val("core.ssl_verify", "true") return False with util.temptext() as temp_file: _, temp_path = temp_file with open(temp_path, 'w') as f: f.write(cert) cert_path = os.path.join( config.get_attached_cluster_path(), "dcos_ca.crt") util.sh_copy(temp_path, cert_path) config.set_val("core.ssl_verify", cert_path) return True
def _get_dcostoken_by_post_with_creds(dcos_url, creds): """ Get DC/OS Authentication token by POST to `acs/api/v1/auth/login` with specific credentials. Credentials can be uid/password for username/password authentication, OIDC ID token for implicit OIDC flow (used for open DC/OS), or uid/token for service accounts (where token is a jwt token encoded with private key). :param dcos_url: url to cluster :type dcos_url: str :param creds: credentials to login endpoint :type creds: {} :returns: DC/OS Authentication Token :rtype: str """ url = dcos_url.rstrip('/') + '/acs/api/v1/auth/login' response = http._request('post', url, json=creds) token = None if response.status_code == 200: token = response.json()['token'] config.set_val("core.dcos_acs_token", token) return token
def browser_prompt_auth(dcos_url, provider_info): """ Get DC/OS Authentication token by browser prompt :param dcos_url: url to cluster :type dcos_url: str :param provider_info: info about provider to auth with :param provider_info: str :rtype: None """ start_flow_url = provider_info["config"]["start_flow_url"].lstrip('/') if not urlparse(start_flow_url).netloc: start_flow_url = dcos_url.rstrip('/') + start_flow_url dcos_token = _prompt_user_for_token( start_flow_url, "DC/OS Authentication Token") # verify token endpoint = '/pkgpanda/active.buildinfo.full.json' url = urllib.parse.urljoin(dcos_url, endpoint) response = http._request('HEAD', url, auth=http.DCOSAcsAuth(dcos_token)) if response.status_code in [200, 403]: config.set_val("core.dcos_acs_token", dcos_token) else: raise DCOSException("Authentication failed")
def _store_cluster_cert(cert, no_check): """Store cluster certificate bundle downloaded from cluster and store settings in core.ssl_verify :param cert: ca cert from cluster :type cert: str :param no_check: whether to verify downloaded cert :type no_check: bool :returns: whether or not we are storing the downloaded cert bundle :rtype: bool """ if not no_check and not _user_cert_validation(cert): raise DCOSException("Couldn't get confirmation for the fingerprint.") with util.temptext() as temp_file: _, temp_path = temp_file with open(temp_path, 'w') as f: f.write(cert) cert_path = os.path.join( config.get_attached_cluster_path(), "dcos_ca.crt") util.sh_copy(temp_path, cert_path) config.set_val("core.ssl_verify", cert_path) return True
def test_setup_cluster_through_config_commands(acs_token, temp_dcos_dir): skip_if_env_missing([DCOS_TEST_URL_ENV]) returncode, _, stderr = exec_command([ 'dcos', 'config', 'set', 'core.dcos_url', os.environ.get(DCOS_TEST_URL_ENV) ]) assert returncode == 0 assert stderr == ( b"[core.dcos_url]: set to '%s'\n" b"Setting-up a cluster through this command is being deprecated. " b"To setup the CLI to talk to your cluster, please run " b"`dcos cluster setup <dcos_url>`.\n" % (bytes(os.environ.get(DCOS_TEST_URL_ENV), 'utf-8'), )) config.set_val('core.ssl_verify', "false") returncode, _, _ = exec_command( ['dcos', 'config', 'set', 'core.dcos_acs_token', acs_token]) assert returncode == 0 returncode, stdout, _ = exec_command(['dcos', 'cluster', 'list', '--json']) assert returncode == 0 cluster_list = json.loads(stdout.decode('utf-8')) assert len(cluster_list) == 1 assert cluster_list[0]['url'] == os.environ.get(DCOS_TEST_URL_ENV)
def _get_dcos_acs_auth(username, password, hostname): """Get authentication flow for dcos acs auth :param username: username user for authentication :type username: str :param password: password for authentication :type password: str :param hostname: hostname for credentials :type hostname: str :returns: DCOSAcsAuth :rtype: AuthBase """ toml_config = util.get_config() token = toml_config.get("core.dcos_acs_token") if token is None: dcos_url = toml_config.get("core.dcos_url") url = urllib.parse.urljoin(dcos_url, 'acs/api/v1/auth/login') if password is None: username, password = _get_auth_credentials(username, hostname) creds = {"uid": username, "password": password} # using private method here, so we don't retry on this request # error here will be bubbled up to _request_with_auth response = _request('post', url, json=creds) if response.status_code == 200: token = response.json()['token'] config.set_val("core.dcos_acs_token", token) return DCOSAcsAuth(token)
def browser_prompt_auth(dcos_url, provider_info): """ Get DC/OS Authentication token by browser prompt :param dcos_url: url to cluster :type dcos_url: str :param provider_info: info about provider to auth with :param provider_info: str :rtype: None """ start_flow_url = provider_info["config"]["start_flow_url"].lstrip('/') if not urlparse(start_flow_url).netloc: start_flow_url = dcos_url.rstrip('/') + start_flow_url dcos_token = _prompt_user_for_token(start_flow_url, "DC/OS Authentication Token") # verify token endpoint = '/pkgpanda/active.buildinfo.full.json' url = urllib.parse.urljoin(dcos_url, endpoint) response = http._request('HEAD', url, auth=http.DCOSAcsAuth(dcos_token)) if response.status_code in [200, 403]: config.set_val("core.dcos_acs_token", dcos_token) else: raise DCOSException("Authentication failed")
def _get_dcos_auth(auth_scheme, username, password, hostname): """Get authentication flow for dcos acs auth and dcos oauth :param auth_scheme: authentication_scheme :type auth_scheme: str :param username: username user for authentication :type username: str :param password: password for authentication :type password: str :param hostname: hostname for credentials :type hostname: str :returns: DCOSAcsAuth :rtype: AuthBase """ toml_config = config.get_config() token = config.get_config_val("core.dcos_acs_token", toml_config) if token is None: dcos_url = config.get_config_val("core.dcos_url", toml_config) if auth_scheme == "acsjwt": creds = _get_dcos_acs_auth_creds(username, password, hostname) else: creds = _get_dcos_oauth_creds(dcos_url) url = urllib.parse.urljoin(dcos_url, 'acs/api/v1/auth/login') response = _request('post', url, json=creds) if response.status_code == 200: token = response.json()['token'] config.set_val("core.dcos_acs_token", token) return DCOSAcsAuth(token)
def setup_cluster_config(dcos_url, temp_path, stored_cert): """ Create a cluster directory for cluster specified in "temp_path" directory. :param dcos_url: url to DC/OS cluster :type dcos_url: str :param temp_path: path to temporary config dir :type temp_path: str :param stored_cert: whether we stored cert bundle in 'setup' dir :type stored_cert: bool :returns: path to cluster specific directory :rtype: str """ try: # find cluster id cluster_url = dcos_url.rstrip('/') + '/metadata' res = http.get(cluster_url, timeout=1) cluster_id = res.json().get("CLUSTER_ID") except DCOSException as e: msg = ("Error trying to find cluster id: {}\n " "Please make sure the provided DC/OS URL is valid: {}".format( e, dcos_url)) raise DCOSException(msg) # create cluster id dir cluster_path = os.path.join(config.get_config_dir_path(), constants.DCOS_CLUSTERS_SUBDIR, cluster_id) if os.path.exists(cluster_path): raise DCOSException("Cluster [{}] is already setup".format(dcos_url)) util.ensure_dir_exists(cluster_path) # move contents of setup dir to new location for (path, dirnames, filenames) in os.walk(temp_path): for f in filenames: util.sh_copy(os.path.join(path, f), cluster_path) cluster = Cluster(cluster_id) config_path = cluster.get_config_path() if stored_cert: cert_path = os.path.join(cluster_path, "dcos_ca.crt") config.set_val("core.ssl_verify", cert_path, config_path=config_path) cluster_name = cluster_id try: url = dcos_url.rstrip('/') + '/mesos/state-summary' name_query = http.get(url, toml_config=cluster.get_config()) cluster_name = name_query.json().get("cluster") except DCOSException: pass config.set_val("cluster.name", cluster_name, config_path=config_path) return cluster_path
def update_marathon_client(): global client global toml_config_o toml_config_o = config.get_config() dcos_url = config.get_config_val('core.dcos_url', toml_config_o) marathon_url = urllib.parse.urljoin(dcos_url, 'service/marathon-user/') config.set_val('marathon.url', marathon_url) toml_config_m = config.get_config() client = marathon.create_client(toml_config_m)
def test_dcos_dir_env_with_acs_token(acs_token, temp_dcos_dir): _copy_config_to_dir('dcos.toml', temp_dcos_dir) config.set_val('core.dcos_acs_token', acs_token) returncode, stdout, _ = exec_command(['dcos', 'cluster', 'list', '--json']) assert returncode == 0 cluster_list = json.loads(stdout.decode('utf-8')) assert len(cluster_list) == 1 assert cluster_list[0]['url'] == "http://dcos.snakeoil.mesosphere.com"
def test_dcos_dir_env_with_acs_token(acs_token, temp_dcos_dir): skip_if_env_missing([DCOS_TEST_URL_ENV]) _copy_config_to_dir('dcos.toml', temp_dcos_dir) config.set_val('core.dcos_url', os.environ.get(DCOS_TEST_URL_ENV)) config.set_val('core.dcos_acs_token', acs_token) returncode, stdout, _ = exec_command(['dcos', 'cluster', 'list', '--json']) assert returncode == 0 cluster_list = json.loads(stdout.decode('utf-8')) assert len(cluster_list) == 1 assert cluster_list[0]['url'] == os.environ.get(DCOS_TEST_URL_ENV)
def _set(name, value): """ :returns: process status :rtype: int """ if name == "package.sources": notice = ("This config property has been deprecated. " "Please add your repositories with `dcos package repo add`") return DCOSException(notice) if name == "core.email": notice = "This config property has been deprecated." return DCOSException(notice) config.set_val(name, value) return 0
def marathon_on_marathon(name='marathon-user'): """ Context manager for altering the marathon client for MoM :param name: service name of MoM to use :type name: str """ toml_config_o = config.get_config() dcos_url = config.get_config_val('core.dcos_url', toml_config_o) service_name = 'service/{}/'.format(name) marathon_url = urllib.parse.urljoin(dcos_url, service_name) config.set_val('marathon.url', marathon_url) try: yield finally: # return config to previous state config.save(toml_config_o)
def test_dcos_config_env_with_acs_token(acs_token, temp_dcos_dir): with _temp_dcos_config('dcos.toml', temp_dcos_dir): config.set_val('core.dcos_acs_token', acs_token) exp_stderr = ( b"DCOS_CONFIG is deprecated, please consider using " b"`dcos cluster setup <dcos_url>`.\n" ) returncode, stdout, stderr = exec_command( ['dcos', 'cluster', 'list', '--json']) assert returncode == 0 assert exp_stderr == stderr cluster_list = json.loads(stdout.decode('utf-8')) assert len(cluster_list) == 1 assert cluster_list[0]['url'] == "http://dcos.snakeoil.mesosphere.com"
def _rename(name, new_name): """ :param name: name of cluster :type name: str :param new_name: new_name of cluster :type new_name: str :rtype: None """ c = cluster.get_cluster(name) other = cluster.get_cluster(new_name) if c is None: raise DCOSException("Cluster [{}] does not exist".format(name)) elif other and other != c: msg = "A cluster with name [{}] already exists" raise DCOSException(msg.format(new_name)) else: config.set_val("cluster.name", new_name, c.get_config_path())
def _set(name, value): """ :returns: process status :rtype: int """ toml, msg = config.set_val(name, value) emitter.publish(DefaultError(msg)) return 0
def test_dcos_config_env_with_acs_token(acs_token, temp_dcos_dir): skip_if_env_missing([DCOS_TEST_URL_ENV]) with _temp_dcos_config('dcos.toml', temp_dcos_dir): config.set_val('core.dcos_url', os.environ.get(DCOS_TEST_URL_ENV)) config.set_val('core.dcos_acs_token', acs_token) exp_stderr = (b"DCOS_CONFIG is deprecated, please consider using " b"`dcos cluster setup <dcos_url>`.\n") returncode, stdout, stderr = exec_command( ['dcos', 'cluster', 'list', '--json']) assert returncode == 0 assert exp_stderr == stderr cluster_list = json.loads(stdout.decode('utf-8')) assert len(cluster_list) == 1 assert cluster_list[0]['url'] == os.environ.get(DCOS_TEST_URL_ENV)
def _set(name, value): """ :returns: process status :rtype: int """ toml_config = config.set_val(name, value) if (name == 'core.reporting' is True) or (name == 'core.email'): analytics.segment_identify(toml_config) return 0
def _get_dcos_auth(auth_scheme, username, password, hostname): """Get authentication flow for dcos acs auth and dcos oauth :param auth_scheme: authentication_scheme :type auth_scheme: str :param username: username user for authentication :type username: str :param password: password for authentication :type password: str :param hostname: hostname for credentials :type hostname: str :returns: DCOSAcsAuth :rtype: AuthBase """ toml_config = config.get_config() token = config.get_config_val("core.dcos_acs_token", toml_config) if token is None: dcos_url = config.get_config_val("core.dcos_url", toml_config) if auth_scheme == "acsjwt": creds = _get_dcos_acs_auth_creds(username, password, hostname) else: creds = _get_dcos_oauth_creds(dcos_url) verify = _verify_ssl() # Silence 'Unverified HTTPS request' and 'SecurityWarning' for bad cert if verify is not None: silence_requests_warnings() url = urllib.parse.urljoin(dcos_url, 'acs/api/v1/auth/login') # using private method here, so we don't retry on this request # error here will be bubbled up to _request_with_auth response = _request('post', url, json=creds, verify=verify) if response.status_code == 200: token = response.json()['token'] config.set_val("core.dcos_acs_token", token) return DCOSAcsAuth(token)
def _set(name, value): """ :returns: process status :rtype: int """ if name == "core.dcos_url": return _cluster_setup(value) toml, msg = config.set_val(name, value) emitter.publish(DefaultError(msg)) return 0
def _set(name, value): """ :returns: process status :rtype: int """ if name == "package.sources": notice = ("This config property has been deprecated. " "Please add your repositories with `dcos package repo add`") return DCOSException(notice) toml_config = config.set_val(name, value) if (name == 'core.reporting' is True) or (name == 'core.email'): analytics.segment_identify(toml_config) return 0
def _set(name, value): """ :returns: process status :rtype: int """ toml, msg = config.set_val(name, value) emitter.publish(DefaultError(msg)) if name == "core.dcos_url" and config.uses_deprecated_config(): notice = ( "Setting-up a cluster through this command is being deprecated. " "To setup the CLI to talk to your cluster, please run " "`dcos cluster setup <dcos_url>`.") emitter.publish(DefaultError(notice)) return 0
def _set(name, value): """ :returns: process status :rtype: int """ if name == "package.sources": notice = ("This config property has been deprecated. " "Please add your repositories with `dcos package repo add`") return DCOSException(notice) if name == "core.email": notice = "This config property has been deprecated." return DCOSException(notice) toml, msg = config.set_val(name, value) emitter.publish(DefaultError(msg)) return 0
def setup(dcos_url, insecure=False, no_check=False, ca_certs=None, password_str=None, password_env=None, password_file=None, provider=None, username=None, key_path=None): """ Setup the CLI to talk to your DC/OS cluster. :param dcos_url: master ip of cluster :type dcos_url: str :param insecure: whether or not to verify ssl certs :type insecure: bool :param no_check: whether or not to verify downloaded ca cert :type no_check: bool :param ca_certs: path to root CA to verify requests :type ca_certs: str :param password_str: password :type password_str: str :param password_env: name of environment variable with password :type password_env: str :param password_file: path to file with password :type password_file: bool :param provider: name of provider to authentication with :type provider: str :param username: username :type username: str :param key_path: path to file with private key :type param: str :returns: process status :rtype: int """ with cluster.setup_directory() as temp_path: # set cluster as attached cluster.set_attached(temp_path) # authenticate config.set_val("core.dcos_url", dcos_url) # get validated dcos_url dcos_url = config.get_config_val("core.dcos_url") # configure ssl settings stored_cert = False if insecure: config.set_val("core.ssl_verify", "false") elif ca_certs: config.set_val("core.ssl_verify", ca_certs) else: cert = cluster.get_cluster_cert(dcos_url) # if we don't have a cert don't try to verify one if cert is False: config.set_val("core.ssl_verify", "false") else: stored_cert = _store_cluster_cert(cert, no_check) try: login(dcos_url, password_str, password_env, password_file, provider, username, key_path) except DCOSAuthenticationException: msg = ("Authentication failed. " "Please run `dcos cluster setup <dcos_url>`") raise DCOSException(msg) # configure cluster directory cluster.setup_cluster_config(dcos_url, temp_path, stored_cert) return 0
def setup(dcos_url, insecure=False, no_check=False, ca_certs=None, password_str=None, password_env=None, password_file=None, provider=None, username=None, key_path=None): """ Setup the CLI to talk to your DC/OS cluster. :param dcos_url: master ip of cluster :type dcos_url: str :param insecure: whether or not to verify ssl certs :type insecure: bool :param no_check: whether or not to verify downloaded ca cert :type no_check: bool :param ca_certs: path to root CA to verify requests :type ca_certs: str :param password_str: password :type password_str: str :param password_env: name of environment variable with password :type password_env: str :param password_file: path to file with password :type password_file: bool :param provider: name of provider to authentication with :type provider: str :param username: username :type username: str :param key_path: path to file with private key :type param: str :returns: process status :rtype: int """ with cluster.setup_directory() as temp_path: # set cluster as attached cluster.set_attached(temp_path) # Make sure to ignore any environment variable for the DCOS URL. # There is already a mandatory command argument for this. env_warning = ("Ignoring '{}' environment variable.\n") if "DCOS_URL" in os.environ: emitter.publish(DefaultError(env_warning.format('DCOS_URL'))) del os.environ["DCOS_URL"] if "DCOS_DCOS_URL" in os.environ: emitter.publish(DefaultError(env_warning.format('DCOS_DCOS_URL'))) del os.environ["DCOS_DCOS_URL"] # authenticate config.set_val("core.dcos_url", dcos_url) # get validated dcos_url dcos_url = config.get_config_val("core.dcos_url") # configure ssl settings stored_cert = False if insecure: config.set_val("core.ssl_verify", "false") elif ca_certs: config.set_val("core.ssl_verify", ca_certs) elif _needs_cluster_cert(dcos_url): cert = cluster.get_cluster_cert(dcos_url) if cert: stored_cert = _store_cluster_cert(cert, no_check) else: config.set_val("core.ssl_verify", "false") else: config.set_val("core.ssl_verify", "false") try: login(dcos_url, password_str, password_env, password_file, provider, username, key_path) except DCOSAuthenticationException: msg = ("Authentication failed. " "Please run `dcos cluster setup <dcos_url>`") raise DCOSException(msg) # configure cluster directory cluster.setup_cluster_config(dcos_url, temp_path, stored_cert) return 0
def set_app_id(app_id): config.set_val("spark.app_id", app_id)