Ejemplo n.º 1
0
def _log_service(follow, lines, service, file_):
    """Prints the contents of the logs for a given service.  Used for
    non-marathon services.

    :param follow: same as unix tail's -f
    :type follow: bool
    :param lines: number of lines to print
    :type lines: int
    :param service: service name
    :type service: str
    :param file_: file path to read
    :type file_: str
    :returns: process return code
    :rtype: int
    """

    if file_ is None:
        file_ = 'stdout'

    task = _get_service_task(service)
    try:
        if 'id' not in task:
            raise DCOSException('Missing `id` in task. {}'.format(task))

        task_id = task['id']
        task_main._log(follow, False, lines, task_id, file_)
        return 0
    except (DCOSAuthenticationException, DCOSAuthorizationException):
        raise
    except DCOSException as e:
        emitter.publish(DefaultError(e))
        emitter.publish(DefaultError('Falling back to files API...'))

    task = _get_service_task(service)
    return _log_task(task['id'], follow, lines, file_)
Ejemplo n.º 2
0
def _ssh(leader, slave, option, config_file, user, master_proxy, proxy_ip,
         private_ip, command):
    """SSH into a DC/OS node using the IP addresses found in master's
       state.json

    :param leader: True if the user has opted to SSH into the leading
                   master
    :type leader: bool | None
    :param slave: The slave ID if the user has opted to SSH into a slave
    :type slave: str | None
    :param option: SSH option
    :type option: [str]
    :param config_file: SSH config file
    :type config_file: str | None
    :param user: SSH user
    :type user: str | None
    :param master_proxy: If True, SSH-hop from a master
    :type master_proxy: bool | None
    :param proxy_ip: If set, SSH-hop from this IP address
    :type proxy_ip: str | None
    :param private_ip: The private IP address of the node we want to SSH to.
    :type private_ip: str | None
    :param command: Command to run on the node
    :type command: str | None
    :rtype: int
    :returns: process return code
    """

    dcos_client = mesos.DCOSClient()

    if leader:
        host = mesos.MesosDNSClient().leader()[0]['ip']
    elif private_ip:
        host = private_ip
    else:
        summary = dcos_client.get_state_summary()
        slave_obj = next(
            (slave_ for slave_ in summary['slaves'] if slave_['id'] == slave),
            None)
        if slave_obj:
            host = mesos.parse_pid(slave_obj['pid'])[1]
        else:
            raise DCOSException('No slave found with ID [{}]'.format(slave))

    if command is None:
        command = ''

    ssh_options = ssh_util.get_ssh_options(config_file, option, user, proxy_ip,
                                           master_proxy)
    cmd = "ssh {0} {1} -- {2}".format(ssh_options, host, command)

    emitter.publish(DefaultError("Running `{}`".format(cmd)))
    if not master_proxy and not proxy_ip:
        emitter.publish(
            DefaultError("If you are running this command from a separate "
                         "network than DC/OS, consider using "
                         "`--master-proxy` or `--proxy-ip`"))

    return subprocess.Subproc().call(cmd, shell=True)
Ejemplo n.º 3
0
def _log_marathon(follow, lines, ssh_config_file):
    """Prints the contents of the marathon logs. Proxy through the master
    because marathon only runs on the master.

    :param follow: same as unix tail's -f
    :type follow: bool
    :param lines: number of lines to print
    :type lines: int
    :param ssh_config_file: SSH config file.
    :type ssh_config_file: str | None
    ;:returns: process return code
    :rtype: int
    """

    journalctl_args = ''
    if follow:
        journalctl_args += '-f '
    if lines:
        journalctl_args += '-n {} '.format(lines)

    leader_ip = marathon.create_client().get_leader().split(':')[0]
    service = 'dcos-marathon'

    ssh_options = ssh_util.get_ssh_options(ssh_config_file, master_proxy=True)
    cmd = "ssh {0} {1} -- journalctl {2}-u {3}".format(ssh_options, leader_ip,
                                                       journalctl_args,
                                                       service)

    emitter.publish(DefaultError("Running `{}`".format(cmd)))

    return subprocess.Subproc().call(cmd, shell=True)
Ejemplo n.º 4
0
def _log_marathon(follow, lines, ssh_config_file):
    """Prints the contents of the marathon logs.

    :param follow: same as unix tail's -f
    :type follow: bool
    :param lines: number of lines to print
    :type lines: int
    :param ssh_config_file: SSH config file.
    :type ssh_config_file: str | None
    ;:returns: process return code
    :rtype: int
    """

    ssh_options = util.get_ssh_options(ssh_config_file, [])

    journalctl_args = ''
    if follow:
        journalctl_args += '-f '
    if lines:
        journalctl_args += '-n {} '.format(lines)

    leader_ip = marathon.create_client().get_leader().split(':')[0]

    user_string = 'core@'
    if ssh_config_file:
        user_string = ''

    cmd = ("ssh {0}{1}{2} " + "journalctl {3}-u dcos-marathon").format(
        ssh_options, user_string, leader_ip, journalctl_args)

    emitter.publish(DefaultError("Running `{}`".format(cmd)))

    return subprocess.Subproc().call(cmd, shell=True)
Ejemplo n.º 5
0
def _log(follow, lines, leader, slave, component, filters):
    """ Prints the contents of leader and slave logs.

    :param follow: same as unix tail's -f
    :type follow: bool
    :param lines: number of lines to print
    :type lines: int
    :param leader: whether to print the leading master's log
    :type leader: bool
    :param slave: the slave ID to print
    :type slave: str | None
    :param component: DC/OS component name
    :type component: string
    :param filters: a list of filters ["key:value", ...]
    :type filters: list
    :returns: process return code
    :rtype: int
    """

    if not (leader or slave) or (leader and slave):
        raise DCOSException(
            'You must choose one of --leader or --mesos-id.')

    if lines is None:
        lines = 10

    lines = util.parse_int(lines)

    try:
        _dcos_log(follow, lines, leader, slave, component, filters)
        return 0
    except (DCOSAuthenticationException,
            DCOSAuthorizationException):
            raise
    except DCOSException as e:
        emitter.publish(DefaultError(e))
        emitter.publish(DefaultError('Falling back to files API...'))

    if component or filters:
        raise DCOSException('--component or --filter is not '
                            'supported by files API')

    # fail back to mesos files API.
    mesos_files = _mesos_files(leader, slave)
    log.log_files(mesos_files, follow, lines)
    return 0
Ejemplo n.º 6
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
Ejemplo n.º 7
0
def _unset(name):
    """
    :returns: process status
    :rtype: int
    """

    msg = config.unset(name)
    emitter.publish(DefaultError(msg))

    return 0
Ejemplo n.º 8
0
def _decommission(mesos_id):
    try:
        mesos.DCOSClient().mark_agent_gone(mesos_id)
    except errors.DCOSException as e:
        emitter.publish(
            DefaultError("Couldn't mark agent {} as gone :\n\n{}".format(
                mesos_id, e)))
        return 1

    emitter.publish("Agent {} has been marked as gone.".format(mesos_id))
Ejemplo n.º 9
0
def _set(name, value):
    """
    :returns: process status
    :rtype: int
    """

    toml, msg = config.set_val(name, value)
    emitter.publish(DefaultError(msg))

    return 0
Ejemplo n.º 10
0
def _cluster_setup(dcos_url):
    """
    Setup a cluster using "cluster" directory instead "global" directory, until
    we deprecate "global" config command: `dcos config set core.dcos_url x`
    """

    notice = ("This config property is being deprecated. "
              "To setup the CLI to talk to your cluster, please run "
              "`dcos cluster setup <dcos_url>`.")
    emitter.publish(DefaultError(notice))

    return setup(dcos_url)
Ejemplo n.º 11
0
def login(dcos_url, password_str, password_env, password_file, provider,
          username, key_path):
    """
    :param dcos_url: URL of DC/OS cluster
    :type dcos_url: 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
    :rtype: int
    """

    password = _get_password(password_str, password_env, password_file)
    if provider is None:
        if username and password:
            auth.dcos_uid_password_auth(dcos_url, username, password)
        elif username and key_path:
            auth.servicecred_auth(dcos_url, username, key_path)
        else:
            try:
                providers = auth.get_providers()
                # Let users know if they have non-default providers configured
                # This is a weak check, we should check default versions per
                # DC/OS version since defaults will change. jj
                if len(providers) > 2:
                    msg = ("\nYour cluster has several authentication "
                           "providers enabled. Run `dcos auth "
                           "list-providers` to see all providers and `dcos "
                           "auth login --provider <provider-id>` to "
                           "authenticate with a specific provider\n")
                    emitter.publish(DefaultError(msg))
            except DCOSException:
                pass
            finally:
                auth.header_challenge_auth(dcos_url)
    else:
        providers = auth.get_providers()
        if providers.get(provider):
            _trigger_client_method(provider, providers[provider], username,
                                   password, key_path)
        else:
            msg = "Provider [{}] not configured on your cluster"
            raise DCOSException(msg.format(provider))

    return 0
Ejemplo n.º 12
0
    def uninstall_app(self, package_name, remove_all, app_id, manager_id):
        """Uninstalls an app.

        :param package_name: The package to uninstall
        :type package_name: str
        :param remove_all: Whether to remove all instances of the named app
        :type remove_all: boolean
        :param app_id: App ID of the app instance to uninstall
        :type app_id: str
        :returns: whether uninstall was successful or not
        :rtype: bool
        :param manager_id: the custom manager to forward this request to
        :type manager_id: str
        """

        params = {"packageName": package_name, "managerId": manager_id}
        if remove_all is True:
            params["all"] = True
        if app_id is not None:
            params["appId"] = app_id

        response = self.cosmos_post("uninstall", params)
        results = response.json().get("results")

        uninstalled_versions = []
        for res in results:
            version = res.get("packageVersion")
            if version not in uninstalled_versions:
                emitter.publish(
                    DefaultError(
                        'Uninstalled package [{}] version [{}]'.format(
                            res.get("packageName"),
                            res.get("packageVersion"))))
                uninstalled_versions += [res.get("packageVersion")]

                if res.get("postUninstallNotes") is not None:
                    emitter.publish(DefaultError(
                        res.get("postUninstallNotes")))

        return True
Ejemplo n.º 13
0
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
Ejemplo n.º 14
0
def _main(argv):

    for i, arg in enumerate(argv):
        if arg == '--show-failures':
            argv[i] = '--failures'
            warning = ("'--show-failures' is deprecated, "
                       "please use '--failures' instead.\n")
            emitter.publish(DefaultError(warning))

    args = docopt.docopt(default_doc("job"),
                         argv=argv,
                         version='dcos-job version {}'.format(dcoscli.version))

    return cmds.execute(_cmds(), args)
Ejemplo n.º 15
0
def set_val(name, value):
    """
    :param name: name of paramater
    :type name: str
    :param value: value to set to paramater `name`
    :type param: str
    :returns: Toml config
    :rtype: Toml
    """

    toml_config = util.get_config(True)

    section, subkey = split_key(name)

    config_schema = get_config_schema(section)

    new_value = jsonitem.parse_json_value(subkey, value, config_schema)

    toml_config_pre = copy.deepcopy(toml_config)
    if section not in toml_config_pre._dictionary:
        toml_config_pre._dictionary[section] = {}

    value_exists = name in toml_config
    old_value = toml_config.get(name)

    toml_config[name] = new_value

    check_config(toml_config_pre, toml_config)

    save(toml_config)

    msg = "[{}]: ".format(name)
    if name == "core.dcos_acs_token":
        if not value_exists:
            msg += "set"
        elif old_value == new_value:
            msg += "already set to that value"
        else:
            msg += "changed"
    elif not value_exists:
        msg += "set to '{}'".format(new_value)
    elif old_value == new_value:
        msg += "already set to '{}'".format(old_value)
    else:
        msg += "changed from '{}' to '{}'".format(old_value, new_value)
    emitter.publish(DefaultError(msg))

    return toml_config
Ejemplo n.º 16
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
Ejemplo n.º 17
0
def _log_marathon(follow, lines, ssh_config_file):
    """Prints the contents of the marathon logs. Proxy through the master
    because marathon only runs on the master.

    :param follow: same as unix tail's -f
    :type follow: bool
    :param lines: number of lines to print
    :type lines: int
    :param ssh_config_file: SSH config file.
    :type ssh_config_file: str | None
    ;:returns: process return code
    :rtype: int
    """

    ssh_options = util.get_ssh_options(ssh_config_file, [])

    journalctl_args = ''
    if follow:
        journalctl_args += '-f '
    if lines:
        journalctl_args += '-n {} '.format(lines)

    leader_ip = marathon.create_client().get_leader().split(':')[0]
    user_string = 'core@'
    if ssh_config_file:
        user_string = ''

    dcos_client = mesos.DCOSClient()
    master_public_ip = dcos_client.metadata().get('PUBLIC_IPV4')
    service = 'dcos-marathon'

    cmd = "ssh -At {0}{1}{2} ssh -At {0}{1}{3} journalctl {4}-u {5}".format(
        ssh_options,
        user_string,
        master_public_ip,
        leader_ip,
        journalctl_args,
        service)

    emitter.publish(DefaultError("Running `{}`".format(cmd)))

    return subprocess.Subproc().call(cmd, shell=True)
Ejemplo n.º 18
0
def _ssh(master, slave, option, config_file, user):
    """SSH into a DCOS node using the IP addresses found in master's
       state.json

    :param master: True if the user has opted to SSH into the leading
                   master
    :type master: bool | None
    :param slave: The slave ID if the user has opted to SSH into a slave
    :type slave: str | None
    :param option: SSH option
    :type option: [str]
    :param config_file: SSH config file
    :type config_file: str | None
    :param user: SSH user
    :type user: str | None
    :rtype: int
    :returns: process return code
    """

    ssh_options = util.get_ssh_options(config_file, option)

    if master:
        host = mesos.MesosDNSClient().hosts('leader.mesos.')[0]['ip']
    else:
        summary = mesos.DCOSClient().get_state_summary()
        slave_obj = next((slave_ for slave_ in summary['slaves']
                          if slave_['id'] == slave),
                         None)
        if slave_obj:
            host = mesos.parse_pid(slave_obj['pid'])[1]
        else:
            raise DCOSException('No slave found with ID [{}]'.format(slave))

    cmd = "ssh -t {0}{1}@{2}".format(
        ssh_options,
        user,
        host)

    emitter.publish(DefaultError("Running `{}`".format(cmd)))

    return subprocess.call(cmd, shell=True)
Ejemplo n.º 19
0
def _load_slaves_state(slaves):
    """Fetch each slave's state.json in parallel, and return the reachable
    slaves.

    :param slaves: slaves to fetch
    :type slaves: [MesosSlave]
    :returns: MesosSlave objects that were successfully reached
    :rtype: [MesosSlave]
    """

    reachable_slaves = []

    for job, slave in util.stream(lambda slave: slave.state(), slaves):
        try:
            job.result()
            reachable_slaves.append(slave)
        except DCOSException as e:
            emitter.publish(
                DefaultError('Error accessing slave: {0}'.format(e)))

    return reachable_slaves
Ejemplo n.º 20
0
def unset(name):
    """
    :param name: name of config value to unset
    :type name: str
    :returns: process status
    :rtype: None
    """

    toml_config = util.get_config(True)
    toml_config_pre = copy.deepcopy(toml_config)
    section = name.split(".", 1)[0]
    if section not in toml_config_pre._dictionary:
        toml_config_pre._dictionary[section] = {}
    value = toml_config.pop(name, None)
    if value is None:
        raise DCOSException("Property {!r} doesn't exist".format(name))
    elif isinstance(value, collections.Mapping):
        raise DCOSException(_generate_choice_msg(name, value))
    else:
        emitter.publish(DefaultError("Removed [{}]".format(name)))
        save(toml_config)
        return
Ejemplo n.º 21
0
def _output(curr_header, output_header, header, lines):
    """Prints a sequence of lines.  If `header` is different than
    `curr_header`, first print the header.

    :param curr_header: most recently printed header
    :type curr_header: str
    :param output_header: whether or not to output the header
    :type output_header: bool
    :param header: header for `lines`
    :type header: str
    :param lines: lines to print
    :type lines: [str]
    :returns: `header`
    :rtype: str
    """

    if lines:
        if output_header and header != curr_header:
            emitter.publish('===> {} <==='.format(header))
        if lines == ['']:
            emitter.publish(DefaultError('No logs for this task'))
        for line in lines:
            emitter.publish(line)
    return header
Ejemplo n.º 22
0
def _log(follow, completed, lines, task, file_):
    """ Tail a file in the task's sandbox.

    :param follow: same as unix tail's -f
    :type follow: bool
    :param completed: whether to include completed tasks
    :type completed: bool
    :param lines: number of lines to print
    :type lines: int
    :param task: task pattern to match
    :type task: str
    :param file_: file path to read
    :type file_: str
    :returns: process return code
    :rtype: int
    """

    fltr = task

    if file_ is None:
        file_ = 'stdout'

    if lines is None:
        lines = 10
    lines = util.parse_int(lines)

    # get tasks
    client = mesos.DCOSClient()
    master = mesos.Master(client.get_master_state())
    tasks = master.tasks(completed=completed, fltr=fltr)

    if not tasks:
        if not fltr:
            raise DCOSException("No tasks found. Exiting.")
        elif not completed:
            completed_tasks = master.tasks(completed=True, fltr=fltr)
            if completed_tasks:
                msg = 'No running tasks match ID [{}]; however, there '.format(
                    fltr)
                if len(completed_tasks) > 1:
                    msg += 'are {} matching completed tasks. '.format(
                        len(completed_tasks))
                else:
                    msg += 'is 1 matching completed task. '
                msg += 'Run with --completed to see these logs.'
                raise DCOSException(msg)
        raise DCOSException('No matching tasks. Exiting.')

    if file_ in ('stdout', 'stderr') and log.dcos_log_enabled():
        try:
            _dcos_log(follow, tasks, lines, file_, completed)
            return 0
        except (DCOSAuthenticationException, DCOSAuthorizationException):
            raise
        except DCOSException as e:
            emitter.publish(DefaultError(e))
            emitter.publish(DefaultError('Falling back to files API...'))

    mesos_files = _mesos_files(tasks, file_, client)
    if not mesos_files:
        if fltr is None:
            msg = "No tasks found. Exiting."
        else:
            msg = "No matching tasks. Exiting."
        raise DCOSException(msg)

    log.log_files(mesos_files, follow, lines)

    return 0
Ejemplo n.º 23
0
def _ssh(leader, slave, option, config_file, user, master_proxy, proxy_ip,
         command):
    """SSH into a DC/OS node using the IP addresses found in master's
       state.json

    :param leader: True if the user has opted to SSH into the leading
                   master
    :type leader: bool | None
    :param slave: The slave ID if the user has opted to SSH into a slave
    :type slave: str | None
    :param option: SSH option
    :type option: [str]
    :param config_file: SSH config file
    :type config_file: str | None
    :param user: SSH user
    :type user: str | None
    :param master_proxy: If True, SSH-hop from a master
    :type master_proxy: bool | None
    :param proxy_ip: If set, SSH-hop from this IP address
    :type proxy_ip: str | None
    :param command: Command to run on the node
    :type command: str | None
    :rtype: int
    :returns: process return code
    """

    ssh_options = util.get_ssh_options(config_file, option)
    dcos_client = mesos.DCOSClient()

    if leader:
        host = mesos.MesosDNSClient().hosts('leader.mesos.')[0]['ip']
    else:
        summary = dcos_client.get_state_summary()
        slave_obj = next((slave_ for slave_ in summary['slaves']
                          if slave_['id'] == slave),
                         None)
        if slave_obj:
            host = mesos.parse_pid(slave_obj['pid'])[1]
        else:
            raise DCOSException('No slave found with ID [{}]'.format(slave))

    if command is None:
        command = ''

    master_public_ip = dcos_client.metadata().get('PUBLIC_IPV4')

    if master_proxy:
        if not master_public_ip:
            raise DCOSException(("Cannot use --master-proxy.  Failed to find "
                                 "'PUBLIC_IPV4' at {}").format(
                                     dcos_client.get_dcos_url('metadata')))
        proxy_ip = master_public_ip

    if proxy_ip:
        if not os.environ.get('SSH_AUTH_SOCK'):
            raise DCOSException(
                "There is no SSH_AUTH_SOCK env variable, which likely means "
                "you aren't running `ssh-agent`.  `dcos node ssh "
                "--master-proxy/--proxy-ip` depends on `ssh-agent` to safely "
                "use your private key to hop between nodes in your cluster.  "
                "Please run `ssh-agent`, then add your private key with "
                "`ssh-add`.")
        cmd = "ssh -A -t {0}{1}@{2} ssh -A -t {0}{1}@{3} {4}".format(
            ssh_options,
            user,
            proxy_ip,
            host,
            command)
    else:
        cmd = "ssh -t {0}{1}@{2} {3}".format(
            ssh_options,
            user,
            host,
            command)

    emitter.publish(DefaultError("Running `{}`".format(cmd)))
    if (not master_proxy and not proxy_ip) and master_public_ip:
        emitter.publish(
            DefaultError("If you are running this command from a separate "
                         "network than DC/OS, consider using "
                         "`--master-proxy` or `--proxy-ip`"))

    return subprocess.Subproc().call(cmd, shell=True)
Ejemplo n.º 24
0
def uninstall_app(app_name, remove_all, app_id, init_client, dcos_client):
    """Uninstalls an app.

    :param app_name: The app to uninstall
    :type app_name: str
    :param remove_all: Whether to remove all instances of the named app
    :type remove_all: boolean
    :param app_id: App ID of the app instance to uninstall
    :type app_id: str
    :param init_client: The program to use to run the app
    :type init_client: object
    :param dcos_client: the DCOS client
    :type dcos_client: dcos.mesos.DCOSClient
    :returns: number of apps uninstalled
    :rtype: int
    """

    apps = init_client.get_apps()

    def is_match(app):
        encoding = 'utf-8'  # We normalize encoding for byte-wise comparison
        name_label = app.get('labels', {}).get(PACKAGE_NAME_KEY, u'')
        name_label_enc = name_label.encode(encoding)
        app_name_enc = app_name.encode(encoding)
        name_matches = name_label_enc == app_name_enc

        if app_id is not None:
            pkg_app_id = app.get('id', '')
            normalized_app_id = init_client.normalize_app_id(app_id)
            return name_matches and pkg_app_id == normalized_app_id
        else:
            return name_matches

    matching_apps = [a for a in apps if is_match(a)]

    if not remove_all and len(matching_apps) > 1:
        app_ids = [a.get('id') for a in matching_apps]
        raise DCOSException(
            ("Multiple apps named [{}] are installed: [{}].\n" +
             "Please use --app-id to specify the ID of the app to uninstall," +
             " or use --all to uninstall all apps.").format(
                 app_name,
                 ', '.join(app_ids)))

    for app in matching_apps:
        package_json = _decode_and_add_context(
            app['id'],
            app.get('labels', {}))
        # First, remove the app from Marathon
        init_client.remove_app(app['id'], force=True)

        # Second, shutdown the framework with Mesos
        framework_name = app.get('labels', {}).get(PACKAGE_FRAMEWORK_NAME_KEY)
        if framework_name is not None:
            logger.info(
                'Trying to shutdown framework {}'.format(framework_name))
            frameworks = mesos.Master(dcos_client.get_master_state()) \
                              .frameworks(inactive=True)

            # Look up all the framework names
            framework_ids = [
                framework['id']
                for framework in frameworks
                if framework['name'] == framework_name
            ]

            logger.info(
                'Found the following frameworks: {}'.format(framework_ids))

            # Emit post uninstall notes
            emitter.publish(
                DefaultError(
                    'Uninstalled package [{}] version [{}]'.format(
                        package_json['name'],
                        package_json['version'])))

            if 'postUninstallNotes' in package_json:
                emitter.publish(
                    DefaultError(package_json['postUninstallNotes']))

            if len(framework_ids) == 1:
                dcos_client.shutdown_framework(framework_ids[0])
            elif len(framework_ids) > 1:
                raise DCOSException(
                    "Unable to shutdown the framework for [{}] because there "
                    "are multiple frameworks with the same name: [{}]. "
                    "Manually shut them down using 'dcos service "
                    "shutdown'.".format(
                        framework_name,
                        ', '.join(framework_ids)))

    return len(matching_apps)
Ejemplo n.º 25
0
def signal_handler(signal, frame):
    emitter.publish(DefaultError("User interrupted command with Ctrl-C"))
    sys.exit(0)
Ejemplo n.º 26
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
Ejemplo n.º 27
0
def signal_handler(signal, frame):
    emitter.publish(DefaultError(" User interrupted command. Exiting..."))
    sys.exit(130)
Ejemplo n.º 28
0
def _install(package_name, package_version, options_path, cli, global_, app,
             yes):
    """Install the specified package.

    :param package_name: the package to install
    :type package_name: str
    :param package_version: package version to install
    :type package_version: str
    :param options_path: path to file containing option values
    :type options_path: str
    :param cli: indicates if the cli should be installed
    :type cli: bool
    :param global_: indicates that the cli should be installed globally
    :type global_: bool
    :param app: indicate if the application should be installed
    :type app: bool
    :param yes: automatically assume yes to all prompts
    :type yes: bool
    :returns: process status
    :rtype: int
    """

    if global_:
        msg = 'Usage of --global is deprecated and will be removed in 1.12.'
        emitter.publish(DefaultError(msg))

    if cli is False and app is False:
        # Install both if neither flag is specified
        cli = app = True

    # Fail early if options file isn't valid
    user_options = util.read_file_json(options_path)

    package_manager = get_package_manager()
    pkg = package_manager.get_package_version(package_name, package_version)

    pkg_json = pkg.package_json()

    selected = pkg_json.get('selected')
    if selected:
        link = ('https://mesosphere.com/'
                'catalog-terms-conditions/#certified-services')
    else:
        link = ('https://mesosphere.com/'
                'catalog-terms-conditions/#community-services')
    emitter.publish(('By Deploying, you agree to '
                     'the Terms and Conditions ' + link))

    pre_install_notes = pkg_json.get('preInstallNotes')
    if app and pre_install_notes:
        emitter.publish(pre_install_notes)

    if not confirm('Continue installing?', yes):
        emitter.publish('Exiting installation.')
        return 1

    if app and pkg.marathon_template():

        # Even though package installation will check for template rendering
        # errors, we want to fail early, before trying to install.
        pkg.options(user_options)

        # Install in Marathon
        msg = 'Installing Marathon app for package [{}] version [{}]'.format(
            pkg.name(), pkg.version())

        emitter.publish(msg)

        package_manager.install_app(pkg, user_options)

    if cli and pkg.cli_definition():
        # Install subcommand
        msg = 'Installing CLI subcommand for package [{}] version [{}]'.format(
            pkg.name(), pkg.version())
        emitter.publish(msg)

        subcommand.install(pkg, global_)

        subcommand_paths = subcommand.get_package_commands(package_name)
        new_commands = [
            os.path.basename(p).replace('-', ' ', 1) for p in subcommand_paths
        ]

        if new_commands:
            commands = ', '.join(new_commands)
            plural = "s" if len(new_commands) > 1 else ""
            emitter.publish("New command{} available: dcos {}".format(
                plural, commands))

    post_install_notes = pkg_json.get('postInstallNotes')
    if app and post_install_notes:
        emitter.publish(post_install_notes)

    return 0
Ejemplo n.º 29
0
def _ssh(leader,
         slave,
         option,
         config_file,
         user,
         master_proxy,
         command,
         flag=[],
         print_command=True,
         short_circuit=False,
         output=False,
         output_dst=None,
         tty=True,
         raw=False):
    """SSH into a DCOS node using the IP addresses found in master's
       state.json

    :param leader: True if the user has opted to SSH into the leading
                   master
    :type leader: bool | None
    :param slave: The slave ID if the user has opted to SSH into a slave
    :type slave: str | None
    :param option: SSH option
    :type option: [str]
    :param config_file: SSH config file
    :type config_file: str | None
    :param user: SSH user
    :type user: str | None
    :param master_proxy: If True, SSH-hop from a master
    :type master_proxy: bool | None
    :param command: Command to run on the node
    :type command: str | None
    :param flag: SSH flags
    :type flag: [str]
    :param print_command: If True, print the raw SSH command
    :type print_command: bool
    :param short_circuit: Only use the first SSH connection made
    :type short_circuit: bool
    :param output: If True, return the output of the ssh command
    :type output: boolean
    :param output_dst: Where to send the output of SSH
    :type output_dst: object | None
    :param tty: If True, have SSH allocate a TTY
    :type tty: boolean
    :param raw: If True, return a subprocess.Popen object
    :type raw: boolean
    :rtype: int
    :returns: process return code | str
    """

    ssh_options = util.get_ssh_options(config_file, option)
    dcos_client = mesos.DCOSClient()
    flagstr = " ".join(flag)

    if tty:
        flagstr += ' -t'
    else:
        flagstr += ' -T'

    if leader:
        host = mesos.MesosDNSClient().hosts('leader.mesos.')[0]['ip']
    else:
        summary = dcos_client.get_state_summary()
        slave_obj = next(
            (slave_ for slave_ in summary['slaves'] if slave_['id'] == slave),
            None)
        if slave_obj:
            host = mesos.parse_pid(slave_obj['pid'])[1]
        else:
            raise DCOSException('No slave found with ID [{}]'.format(slave))

    if command is None:
        command = ''

    master_public_ip = dcos_client.metadata().get('PUBLIC_IPV4')
    if master_proxy:
        if not os.environ.get('SSH_AUTH_SOCK'):
            raise DCOSException(
                "There is no SSH_AUTH_SOCK env variable, which likely means "
                "you aren't running `ssh-agent`.  `dcos node ssh "
                "--master-proxy` depends on `ssh-agent` to safely use your "
                "private key to hop between nodes in your cluster.  Please "
                "run `ssh-agent`, then add your private key with `ssh-add`.")
        if not master_public_ip:
            raise DCOSException(("Cannot use --master-proxy.  Failed to find "
                                 "'PUBLIC_IPV4' at {}").format(
                                     dcos_client.get_dcos_url('metadata')))

        cmd = "ssh -A {0} {1}{2}@{3} ssh {0} {1}{2}@{4} {5}"
        if short_circuit:
            cmd = "ssh -A {0} {1}{2}@{3} {5}"
        cmd = cmd.format(flagstr, ssh_options, user, master_public_ip, host,
                         command)
    else:
        cmd = "ssh {0} {1}{2}@{3} {4}".format(flagstr, ssh_options, user, host,
                                              command)

    if print_command:
        emitter.publish(DefaultError("Running `{}`".format(cmd)))
    if (not master_proxy) and master_public_ip:
        emitter.publish(
            DefaultError("If you are running this command from a separate "
                         "network than DC/OS, consider using "
                         "`--master-proxy`"))

    cmd = shlex.split(cmd)
    if output:
        return subprocess.check_output(cmd)
    if raw:
        if output_dst is not None:
            return subprocess.Popen(cmd, stderr=output_dst, stdout=output_dst)
        return subprocess.Popen(cmd)
    if output_dst is not None:
        return subprocess.call(cmd, stderr=output_dst, stdout=output_dst)
    return subprocess.call(cmd)