Exemple #1
0
    def _render_one_template(cls, env, template, result_filename, data, overwrite):
        # Get a template instance
        tpl = None
        try:
            logger.debug('Using template file: {0}'.format(template))
            tpl = env.get_template(template)
        except jinja2.TemplateNotFound as e:
            raise exceptions.CommandException('Template {t} not found in path {p}.'.\
                    format(t=template, p=env.loader.searchpath))
        except jinja2.TemplateError as e:
            raise exceptions.CommandException('Template file failure: {0}'.format(e.message))

        # Check if destination file exists, overwrite if needed
        if os.path.exists(result_filename):
            if overwrite:
                logger.info('Overwriting the destination file {0}'.format(result_filename))
                os.remove(result_filename)
            else:
                raise exceptions.CommandException('The destination file already exists: {0}'.\
                    format(result_filename))

        # Generate an output file finally...
        with open(result_filename, 'w') as out:
            result = tpl.render(**data)
            out.write(result)

        return (True, 'success')
Exemple #2
0
    def _get_docker_run_args(cls, inp):
        if not isinstance(inp, dict):
            raise exceptions.CommandException('docker_r expects mapping as input.')
        if not 'image' in inp:
            raise exceptions.CommandException('docker_r requires "image" argument.')

        return {'image': inp['image'], 'args': inp.get('args', '')}
Exemple #3
0
 def check_args(cls, c):
     if c.comm_type == 'dda_w':
         if not isinstance(c.input_res, list) or len(c.input_res) != 2:
             msg = 'dda_w expects Yaml list with path to .devassistant and mapping to write.'
             raise exceptions.CommandException(msg)
     else:
         if not isinstance(c.input_res, six.string_types):
             msg = '{0} expects a string as an argument.'.format(c.comm_type)
             raise exceptions.CommandException(msg)
Exemple #4
0
 def run(cls, c):
     if c.input_res and not isinstance(c.input_res, dict):
         raise exceptions.CommandException('{0} needs a mapping as input!'.format(c.comm_type))
     if c.comm_type == 'ask_password':
         res = DialogHelper.ask_for_password(**c.input_res)
     elif c.comm_type == 'ask_confirm':
         res = DialogHelper.ask_for_confirm_with_message(**c.input_res)
     else:
         raise exceptions.CommandException('Unknown command type {ct}.'.format(ct=c.comm_type))
     return bool(res), res
Exemple #5
0
    def run(cls, c):
        if c.comm_type in map(lambda x: 'log_{0}'.format(x), settings.LOG_LEVELS_MAP):
            logger.log(settings.LOG_SHORT_TO_NUM_LEVEL[c.comm_type[-1]], c.input_res)
            if c.comm_type[-1] in 'ce':
                e = exceptions.CommandException(c.input_res)
                e.already_logged = True
                raise e
        else:
            raise exceptions.CommandException('Unknown command type {ct}.'.format(ct=c.comm_type))

        return [True, c.input_res]
Exemple #6
0
    def run(cls, c):
        """Only users in "docker" group can use docker; there are three possible situations:
        1) user is not added to docker group => we need to add him there and then go to 2)
        2) user has been added to docker group, but would need to log out for it to
           take effect => use "newgrp" (_cmd_for_newgrp) for all docker commands
        3) user has been added to docker group in a previous login session => all ok
        """
        if not cls._docker_group_active() and not cls._docker_group_added():
            # situation 1
            cls._docker_group_add()
        # else situation 3

        if not cls._docker_service_running():
            cls._docker_service_enable_and_run()

        if c.comm_type == 'docker_build':
            # TODO: allow providing another argument - a repository name/tag for the built image
            ret = cls._docker_build(c.input_res)
        elif c.comm_type == 'docker_run':
            ret = cls._docker_run(c.input_res)
        elif c.comm_type == 'docker_attach':
            ret = cls._docker_attach(c.input_res)
        elif c.comm_type == 'docker_find_img':
            ret = cls._docker_find_image(c.input_res)
        elif c.comm_type == 'docker_container_ip':
            ret = cls._docker_get_container_attr('{{.NetworkSettings.IPAddress}}', c.input_res)
        elif c.comm_type == 'docker_container_name':
            ret = cls._docker_get_container_attr('{{.Name}}', c.input_res)
        else:
            raise exceptions.CommandException('Unknown command type {ct}.'.format(ct=c.comm_type))

        return ret
Exemple #7
0
    def run(cls, c):
        assistant = c.kwargs['__assistant__']
        kwargs = copy.deepcopy(c.kwargs)
        try:
            yaml_name, section_name = c.input_res.rsplit('.', 1)
        except ValueError:
            raise exceptions.CommandException('"use" command expects "use: what.which_section".')

        # Modify kwargs based on command
        if cls.is_snippet_call(c.input_res):
            snip = cls.get_snippet(yaml_name)
            section = cls.get_snippet_section(section_name, snip)

            kwargs['__files__'].append(snip.get_files_section())
            kwargs['__files_dir__'].append(snip.get_files_dir())
            kwargs['__sourcefiles__'].append(snip.path)
        else:
            assistant = cls.get_assistant(yaml_name, section_name, assistant)
            section = cls.get_assistant_section(section_name, assistant)

            kwargs['__assistant__'] = assistant

        # Get section with modified kwargs
        if section_name.startswith('dependencies'):
            result = lang.dependencies_section(section, kwargs, runner=assistant)
        else:
            result = lang.run_section(section, kwargs, runner=assistant)

        return result
Exemple #8
0
    def run(cls, c):
        """Arguments given to 'github' command may be:
        - Just a string (action), which implies that all the other arguments are deducted from
          global context and local system.
        - List containing a string (action) as a first item and rest of the args in a dict.
          (args not specified in the dict are taken from global context.

        Possible arguments:
        - login - taken from 'github' or system username - represents Github login
        - reponame - taken from 'name' (first applies os.path.basename) - repo to operate on
        """
        comm, kwargs = cls.format_args(c)
        if not cls._gh_module:
            logger.warning('PyGithub not installed, cannot execute github command.')
            return [False, '']

        # we pass arguments as kwargs, so that the auth decorator can easily query them
        # NOTE: these are not the variables from global context, but rather what
        # cls.format_args returned
        if comm == 'create_repo':
            ret = cls._github_create_repo(**kwargs)
        elif comm == 'push':
            ret = cls._github_push()
        elif comm == 'create_and_push':
            ret = cls._github_create_and_push(**kwargs)
        elif comm == 'add_remote_origin':
            ret = cls._github_add_remote_origin(**kwargs)
        elif comm == 'create_fork':
            ret = cls._github_fork(**kwargs)
        else:
            raise exceptions.CommandException('Unknown command type {ct}.'.format(ct=c.comm_type))

        return ret
Exemple #9
0
    def run(self):
        for cr in type(self).load_command_runners().command_runners:
            if cr.matches(self):
                return cr.run(self)

        raise exceptions.CommandException('No runner for command "{ct}: {c}".'.\
            format(ct=self.comm_type, c=self.input_res))
Exemple #10
0
 def _get_github_user(cls, login):
     if not cls._user:
         try:
             # try logging with token
             token = cls._github_token(login)
             gh = cls._gh_module.Github(login_or_token=token)
             cls._user = gh.get_user()
             # try if the authentication was successful
             cls._user.login
         except cls._gh_module.GithubException:
             # if the token was set, it was wrong, so make sure it's reset
             cls._token = None
             # login with username/password
             password = DialogHelper.ask_for_password(
                     prompt='Github Password for {username}:'.format(username=login))
             gh = cls._gh_module.Github(login_or_token=login, password=password)
             cls._user = gh.get_user()
             try:
                 cls._user.login
                 cls._github_create_auth() # create auth for future use
             except cls._gh_module.GithubException as e:
                 msg = 'Wrong username or password\nGitHub exception: {0}'.format(e)
                 # reset cls._user to None, so that we don't use it if calling this multiple times
                 cls._user = None
                 raise exceptions.CommandException(msg)
     return cls._user
Exemple #11
0
 def _github_create_ssh_key(cls):
     """Creates a local ssh key, if it doesn't exist already, and uploads it to Github."""
     try:
         login = cls._user.login
         pkey_path = '{home}/.ssh/{keyname}'.format(
             home=os.path.expanduser('~'),
             keyname=settings.GITHUB_SSH_KEYNAME.format(login=login))
         # generate ssh key only if it doesn't exist
         if not os.path.exists(pkey_path):
             ClHelper.run_command('ssh-keygen -t rsa -f {pkey_path}\
                                  -N \"\" -C \"DevAssistant\"'.format(
                 pkey_path=pkey_path))
         try:
             ClHelper.run_command(
                 'ssh-add {pkey_path}'.format(pkey_path=pkey_path))
         except exceptions.ClException:
             # ssh agent might not be running
             env = cls._start_ssh_agent()
             ClHelper.run_command(
                 'ssh-add {pkey_path}'.format(pkey_path=pkey_path), env=env)
         public_key = ClHelper.run_command(
             'cat {pkey_path}.pub'.format(pkey_path=pkey_path))
         cls._user.create_key("DevAssistant", public_key)
     except exceptions.ClException as e:
         msg = 'Couldn\'t create a new ssh key: {0}'.format(e)
         raise exceptions.CommandException(msg)
Exemple #12
0
    def docker_service_enable_and_run(cls):
        # TODO: add some conditionals for various platforms
        logger.info('Enabling and running docker service ...')
        try:
            cmd_str = 'bash -c "systemctl enable docker && systemctl start docker"'
            ClHelper.run_command(cmd_str, as_user='******')
        except exceptions.ClException:
            raise exceptions.CommandException(
                'Failed to enable and run docker service.')

        # we need to wait until /var/run/docker.sock is created
        # let's wait for 30 seconds
        logger.info(
            'Waiting for /var/run/docker.sock to be created (max 15 seconds) ...'
        )
        success = False
        for i in range(0, 30):
            time.sleep(i * 0.5)
            try:
                ClHelper.run_command('ls /var/run/docker.sock')
                success = True
                break
            except exceptions.ClException:
                pass

        if not success:
            logger.warning(
                '/var/run/docker.sock doesn\'t exist, docker will likely not work!'
            )
Exemple #13
0
    def run(cls, c):
        if not isinstance(c.input_res, list):
            msg = 'Dependencies for installation must be list, got {v}.'.format(v=c.input_res)
            raise exceptions.CommandException(msg)

        di = DependencyInstaller()
        di.install(c.input_res)
        return [True, c.input_res]
Exemple #14
0
 def __dot_devassistant_read_exact(cls, directory):
     """Helper for other methods that read .devassistant file."""
     dda_path = os.path.join(os.path.abspath(os.path.expanduser(directory)), '.devassistant')
     try:
         with open(dda_path, 'r') as stream:
             return yaml.load(stream)
     except IOError as e:
         msg = 'Couldn\'t find/open/read .devassistant file: {0}'.format(e)
         raise exceptions.CommandException(msg)
Exemple #15
0
 def get_assistant(cls, assistant_name, section_name, origin_assistant):
     if assistant_name == 'self':
         if not hasattr(origin_assistant, '_' + section_name):
             raise exceptions.CommandException('Assistant "{a}" has no section "{s}"'.\
                                               format(a=origin_assistant.name,
                                                      s=section_name))
         return origin_assistant
     elif assistant_name == 'super':
         a = origin_assistant.superassistant
         while a:
             if hasattr(a, 'assert_fully_loaded'):
                 a.assert_fully_loaded()
             if hasattr(a, '_' + section_name):
                 return a
             a = a.superassistant
         raise exceptions.CommandException('No superassistant of {a} has section {s}'.\
                                           format(a=origin_assistant.name,
                                                  s=section_name))
Exemple #16
0
 def add_user_to_docker_group(cls, username):
     try:
         logger.info('Adding {0} to group docker ...'.format(username))
         ClHelper.run_command(
             'bash -c "usermod -a -G docker {0}"'.format(username),
             as_user='******')
     except exceptions.ClException as e:
         msg = 'Failed to add user to "docker" group: {0}'.format(e.output)
         raise exceptions.CommandException(msg)
Exemple #17
0
    def _try_obtain_common_params(cls, comm):
        """ Retrieve parameters common for all jinja_render* actions from Command instance.
        These are mandatory:
        - 'template'    template descriptor from `files' section. it consist of
                         the only `source' key -- a name of template to use
        - 'data'        dict of parameters to use when rendering
        - 'destination' path for output files
        These are optional:
        - 'overwrite'   overwrite file(s) if it (they) exist(s)
        """
        args = comm.input_res
        ct = comm.comm_type

        wrong_tpl_msg = '{0} requires a "template" argument which must point to a file'.format(ct)
        wrong_tpl_msg += ' in "files" section. Got: {0}'.format(args.get('template', None))

        if 'template' not in args or not isinstance(args['template'], dict):
            raise exceptions.CommandException(wrong_tpl_msg)
        template = args['template']

        if 'source' not in template or not isinstance(template['source'], six.string_types):
            raise exceptions.CommandException(wrong_tpl_msg)
        template = template['source']

        if 'destination' not in args or not isinstance(args['destination'], six.string_types):
            msg = '{0} requires a string "destination" argument. Got: {1}'.\
                format(ct, args.get('destination'))
            raise exceptions.CommandException(msg)
        destination = args['destination']

        if not os.path.isdir(destination):
            msg = '{0}: Specified "destination" directory "{1}" doesn\'t exist!'.\
                format(ct, destination)
            raise exceptions.CommandException(msg)

        data = {}
        if 'data' in args and isinstance(args['data'], dict):
            data = args['data']
        logger.debug('Template context data: {0}'.format(data))

        overwrite = args.get('overwrite', False)
        overwrite = True if str(overwrite).lower() in ['true', 'yes'] else False

        return (template, destination, data, overwrite)
Exemple #18
0
    def get_snippet_section(cls, section_name, snip):
        if section_name.startswith('run'):
            section = snip.get_run_section(section_name) if snip else None
        else:
            section = snip.get_dependencies_section(section_name) if snip else None

        if not section:
            raise exceptions.CommandException('Couldn\'t find section "{t}" in snippet "{n}".'.\
                                              format(t=section_name, n=snip.dotted_name))
        return section
Exemple #19
0
    def run(cls, c):
        args = cls._get_args(c.input_res, c.kwargs)
        contdir, topdir = os.path.split(args['from'])
        normalized_topdir = lang.Command('normalize', topdir).run()[1]
        try:  # ok, this is a bit ugly, but we need to check multiple calls for the exception
            if contdir:  # we need to create containing directory
                if not args['accept_path']:
                    msg = 'Path is not accepted as project name by this assistant (got "{0}")'
                    raise exceptions.CommandException(msg.format(args['from']))

                if not os.path.exists(contdir):
                    os.makedirs(contdir)
                elif not os.path.isdir(contdir):
                    msg = 'Can\'t create subdirectory in "{0}", it\'s not a directory'.\
                        format(contdir)
                    raise exceptions.CommandException(msg)
            actual_topdir = normalized_topdir if args['create_topdir'] == 'normalized' else topdir
            topdir_fullpath = os.path.join(contdir, actual_topdir)
            if args['create_topdir']:
                if os.path.exists(topdir_fullpath):
                    if args['on_existing'] == 'fail':
                        msg = 'Directory "{0}" already exists, can\'t proceed'.format(topdir_fullpath)
                        raise exceptions.CommandException(msg)
                    elif not os.path.isdir(topdir_fullpath):
                        msg = 'Location "{0}" exists, but is not a directory, can\'t proceed'.\
                            format(topdir_fullpath)
                        raise exceptions.CommandException(msg)
                else:
                    os.makedirs(topdir_fullpath)

        except OSError as e:
            msg = 'Failed to create directory {0}: {1}'.format(args['from'], e.message)
            raise CommandException(msg)

        # if contdir == '', then return current dir ('.')
        c.kwargs[args['contdir_var']] = contdir or '.'
        c.kwargs[args['topdir_var']] = topdir
        c.kwargs[args['topdir_normalized_var']] = normalized_topdir

        return (True, topdir_fullpath if args['create_topdir'] else contdir)
Exemple #20
0
    def run(self):
        for crs_prefix, crs in type(
                self).load_command_runners().command_runners.items():
            if self.prefix == crs_prefix:
                # traverse in reversed order, so that dynamically loaded user command runners
                #  can outrun (=> override) the builtin ones
                for cr in reversed(crs):
                    if cr.matches(self):
                        return cr(self).run()

        prefix_with_colon = self.prefix + '.' if self.prefix else self.prefix
        raise exceptions.CommandException(
            'No runner for command "{p}{ct}: {c}".'.format(p=prefix_with_colon,
                                                           ct=self.comm_type,
                                                           c=self.input_res))
Exemple #21
0
    def _get_args(cls, inp, ctxt):
        args = {}
        if not isinstance(inp, dict):
            raise exceptions.CommandException('"setup_project_dir" expects mapping as input')

        args['from'] = inp.get('from', None)
        if args['from'] is None:
            raise exceptions.CommandException('"setup_project_dir" requires "from" argument')

        args['contdir_var'] = inp.get('contdir_var', 'contdir')
        args['topdir_var'] = inp.get('topdir_var', 'topdir')
        args['topdir_normalized_var'] = inp.get('topdir_normalized_var', 'topdir_normalized')
        args['accept_path'] = bool(inp.get('accept_path', True))
        args['create_topdir'] = inp.get('create_topdir', True)
        if not args['create_topdir'] in [True, False, 'normalized']:
            msg = '"setup_project_dir" expects "create_topdir" to be one of: ' +\
                'True, False, normalized'
            raise exceptions.CommandException(msg)
        args['on_existing'] = inp.get('on_existing', 'fail')
        if not args['on_existing'] in ['fail', 'pass']:
            msg = '"setup_project_dir" expects "on_existing" to be one of: "fail", "pass"'
            raise exceptions.CommandException(msg)

        return args
Exemple #22
0
 def _github_create_ssh_key(cls):
     try:
         login = cls._user.login
         pkey_path = '{home}/.ssh/{keyname}'.format(home=os.path.expanduser('~'),
                                                    keyname=settings.GITHUB_SSH_KEYNAME.format(login=login))
         # TODO: handle situation where {pkey_path} exists, but it's not registered on GH
         # generate ssh key
         ClHelper.run_command('ssh-keygen -t rsa -f {pkey_path}\
                              -N \"\" -C \"DevAssistant\"'.\
                              format(pkey_path=pkey_path))
         ClHelper.run_command('ssh-add {pkey_path}'.format(pkey_path=pkey_path))
         public_key = ClHelper.run_command('cat {pkey_path}.pub'.format(pkey_path=pkey_path))
         cls._user.create_key("devassistant", public_key)
     except exceptions.ClException as e:
         msg = 'Couldn\'t create a new ssh key: {e}'.format(e)
         raise exceptions.CommandException(msg)
Exemple #23
0
    def run(cls, c):
        cls.check_args(c)
        if c.comm_type == 'dda_c':
            cls._dot_devassistant_create(c.input_res, c.kwargs)
        elif c.comm_type == 'dda_r':
            cls._dot_devassistant_read(c.input_res, c.kwargs)
        elif c.comm_type == 'dda_dependencies':
            cls._dot_devassistant_dependencies(c.input_res, c.kwargs)
        elif c.comm_type == 'dda_run':
            cls._dot_devassistant_run(c.input_res, c.kwargs)
        elif c.comm_type == 'dda_w':
            cls._dot_devassistant_write(c.input_res)
        else:
            raise exceptions.CommandException('Unknown command type {ct}.'.format(ct=c.comm_type))

        return [True, '']
Exemple #24
0
    def _guess_reponame(cls, explicit, ctxt):
        """Extract reponame, either from explicitly given string or from 'name' global variable,
        which is possibly a path.

        Args:
            ctxt: global context

        Returns:
            guessed reponame
        """
        name = explicit
        if not name:
            name = os.path.basename(ctxt.get('name', ''))
        if not name:
            raise exceptions.CommandException('Cannot guess Github reponame - no argument given'
                                              'and there is no "name" variable.')
        return name
Exemple #25
0
    def run(cls, c):
        """Normalizes c.input_res (string):

        - removes digit from start
        - replaces dashes and whitespaces with underscores
        """
        to_norm = c.input_res
        if not isinstance(to_norm, six.string_types):
            raise exceptions.CommandException('"normalize" expects string input, got {0}'.\
                format(to_norm))

        normalized = to_norm.lstrip('0123456789')
        badchars = '-+\\|()[]{}<>,./:\'" \t;`!@#$%^&*'
        if sys.version_info[0] < 3:
            tt = string.maketrans(badchars, '_' * len(badchars))
        else:
            tt = str.maketrans(badchars, '_' * len(badchars))
        normalized = normalized.translate(tt)
        return (True, normalized)
Exemple #26
0
    def _guess_repo_url(cls, explicit, ctxt):
        """Get repo to fork in form of '<login>/<reponame>' from explicitly given string or
        global variable 'url'.

        Args:
            ctxt: global context

        Returns:
            guessed fork reponame
        """
        url = explicit or ctxt.get('url')
        if not url:
            raise exceptions.CommandException('Cannot guess name of Github repo to fork - no'
                                              'argument given and there is no "url" variable.')

        url = url[:-4] if url.endswith('.git') else url
        # if using git@github:username/reponame.git, strip the stuff before ":"
        url = url.split(':')[-1]
        return '/'.join(url.split('/')[-2:])
Exemple #27
0
    def run(cls, c):
        # Transform list of dicts (where keys are unique) into a single dict
        args = c.input_res
        logger.debug('Jinja2Runner args={0}'.format(repr(args)))

        # Create a jinja environment
        logger.debug('Using templates dir: {0}'.format(c.files_dir))
        env = jinja2.Environment(loader=jinja2.FileSystemLoader(c.files_dir))
        env.trim_blocks = True
        env.lstrip_blocks = True
        template, destination, data, overwrite = cls._try_obtain_common_params(c)

        if c.comm_type == 'jinja_render':
            given_output = args.get('output', '')
            if not isinstance(given_output, six.string_types):
                raise exceptions.CommandException('Jinja2Runner: output must be string, got {0}'.\
                    format(given_output))
            result_fn = cls._make_output_file_name(destination, template, given_output)
            cls._render_one_template(env, template, result_fn, data, overwrite)
        elif c.comm_type == 'jinja_render_dir':
            cls._render_dir(env, template, destination, data, overwrite)

        return (True, 'success')
Exemple #28
0
 def get_snippet(cls, yaml_name):
     try:
         return yaml_snippet_loader.YamlSnippetLoader.get_snippet_by_name(yaml_name)
     except exceptions.SnippetNotFoundException as e:
         raise exceptions.CommandException(e)
Exemple #29
0
 def get_assistant_section(cls, section_name, assistant):
     if not hasattr(assistant, '_' + section_name):
         raise exceptions.CommandException('Assistant {a} has no section {s}'.\
                                             format(a=assistant.name,
                                                    s=section_name))
     return getattr(assistant, '_' + section_name)
Exemple #30
0
 def get_user_from_comm_type(cls, comm_type):
     split_type = comm_type.split()
     if len(split_type) != 2:
         raise exceptions.CommandException('"as" expects format "as <username>".')
     user = split_type[1]
     return user