Example #1
0
 def update_all(self, interactive=False):
     """Update all versions, prompting the user for each possible update"""
     io.activate()
     for name, table in self.toml.items():
         latest = self._latest_version(name)
         current = self._cached_version(name)
         now = datetime.datetime.now()
         if latest == current:
             io.stdout("{x} {name} is up to date ({current})".format(
                 x=green("✓"),
                 name=name,
                 current=current,
             ))
             continue
         if interactive:
             question = wrap_question(
                 name,
                 "{} → {}".format(red(current), green(latest)),
                 "Update {}".format(bold(name)),
                 prefix="{} versions".format(blue("?")))
             if not io.ask(question, True):
                 continue
         else:
             io.stdout("{x} updated {name}: {current} → {new}".format(
                 x=green("✓"),
                 name=bold(name),
                 current=red(current),
                 new=green(latest),
             ))
         table['version'] = latest
         table['version_date'] = now
         self.toml[name] = table
         self._save()
Example #2
0
    def _get_result(self, autoskip_selector="", interactive=False, interactive_default=True):
        if self.covered_by_autoskip_selector(autoskip_selector):
            io.debug(_(
                "autoskip matches {item} on {node}"
            ).format(item=self.id, node=self.node.name))
            return (self.STATUS_SKIPPED, [_("cmdline")])

        if interactive is False and self.attributes['interactive'] is True:
            return (self.STATUS_SKIPPED, [_("interactive only")])

        if self.triggered and not self.has_been_triggered:
            io.debug(_("skipping {} because it wasn't triggered").format(self.id))
            return (self.STATUS_SKIPPED, [_("no trigger")])

        if self.unless:
            with io.job(_("  {node}  {bundle}  {item}  checking 'unless' condition...").format(
                bundle=self.bundle.name,
                item=self.id,
                node=self.node.name,
            )):
                unless_result = self.bundle.node.run(
                    self.unless,
                    may_fail=True,
                )
            if unless_result.return_code == 0:
                io.debug(_("{node}:{bundle}:action:{name}: failed 'unless', not running").format(
                    bundle=self.bundle.name,
                    name=self.name,
                    node=self.bundle.node.name,
                ))
                return (self.STATUS_SKIPPED, ["unless"])

        if (
            interactive and
            self.attributes['interactive'] is not False and
            not io.ask(
                wrap_question(
                    self.id,
                    self.attributes['command'],
                    _("Run action {}?").format(
                        bold(self.name),
                    ),
                    prefix="{x} {node} ".format(
                        node=bold(self.node.name),
                        x=blue("?"),
                    ),
                ),
                interactive_default,
                epilogue="{x} {node}".format(
                    node=bold(self.node.name),
                    x=blue("?"),
                ),
            )
        ):
            return (self.STATUS_SKIPPED, [_("interactive")])
        try:
            self.run()
            return (self.STATUS_ACTION_SUCCEEDED, None)
        except ActionFailure:
            return (self.STATUS_FAILED, None)
Example #3
0
 def _test(self):
     with io.job(_("{node}  {bundle}  {item}").format(
         bundle=bold(self.bundle.name),
         item=self.id,
         node=bold(self.node.name),
     )):
         if self._faults_missing_for_attributes:
             self._raise_for_faults()
         return self.test()
Example #4
0
 def _test(self):
     with io.job(_("{node}  {bundle}  {item}").format(
         bundle=bold(self.bundle.name),
         item=self.id,
         node=bold(self.node.name),
     )):
         if self._faults_missing_for_attributes:
             self._raise_for_faults()
         return self.test()
Example #5
0
 def get_status(self, cached=True):
     """
     Returns an ItemStatus instance describing the current status of
     the item on the actual node.
     """
     with io.job(_("{node}  {bundle}  {item}").format(
         bundle=bold(self.bundle.name),
         item=self.id,
         node=bold(self.node.name),
     )):
         if not cached:
             del self._cache['cached_sdict']
         return ItemStatus(self.cached_cdict, self.cached_sdict, self.display_dicts)
Example #6
0
 def get_status(self, cached=True):
     """
     Returns an ItemStatus instance describing the current status of
     the item on the actual node.
     """
     with io.job(_("{node}  {bundle}  {item}").format(
         bundle=bold(self.bundle.name),
         item=self.id,
         node=bold(self.node.name),
     )):
         if not cached:
             del self._cache['cached_sdict']
         return ItemStatus(self.cached_cdict, self.cached_sdict, self.display_dicts)
Example #7
0
    def ask(self, status):
        if not status.info['exists']:
            return _("'{}' not found in /etc/passwd").format(self.name)
        elif self.attributes['delete']:
            return _("'{}' found in /etc/passwd. Will be deleted.").format(self.name)

        output = ""
        for key in status.info['needs_fixing']:
            if key in ('groups', 'password', 'password_hash'):
                continue
            output += "{} {} → {}\n".format(
                bold(_ATTRIBUTE_NAMES[key]),
                status.info[key],
                self.attributes[key],
            )

        if self.attributes['password_hash'] is not None:
            if self.attributes['use_shadow']:
                filename = "/etc/shadow"
                found_hash = status.info['shadow_hash']
            else:
                filename = "/etc/passwd"
                found_hash = status.info['passwd_hash']

            if found_hash is None:
                output += bold(_ATTRIBUTE_NAMES['password_hash']) + " " + \
                          _("not found in {}").format(filename) + "\n"
            elif found_hash != self.attributes['password_hash']:
                output += bold(_ATTRIBUTE_NAMES['password_hash']) + " " + \
                          found_hash + "\n"
                output += " " * (len(_ATTRIBUTE_NAMES['password_hash']) - 1) + "→ " + \
                          self.attributes['password_hash'] + "\n"

        if self.attributes['groups'] is not None:
            groups_should = set(self.attributes['groups'])
            groups_is = set(status.info['groups'])
            missing_groups = sorted(groups_should.difference(groups_is))
            extra_groups = sorted(groups_is.difference(groups_should))

            if missing_groups:
                output += bold(_("missing groups")) + " " + \
                          ", ".join(missing_groups) + "\n"

            if extra_groups:
                output += bold(_("extra groups")) + " " + \
                          ", ".join(extra_groups) + "\n"

        return output
Example #8
0
 def ask(self, status):
     if not status.info["exists"] and not self.attributes["delete"]:
         return _("Doesn't exist. Do you want to create it?")
     if status.info["exists"] and self.attributes["delete"]:
         return red(_("Will be deleted."))
     if status.info["owner"] != self.attributes["owner"]:
         return "{}  {} → {}".format(bold(_("owner")), status.info["owner"], self.attributes["owner"])
Example #9
0
    def apply(self, interactive=False, interactive_default=True):
        self.node.repo.hooks.item_apply_start(
            self.node.repo,
            self.node,
            self,
        )
        status_code = None
        status_before = None
        status_after = None
        start_time = datetime.now()

        if self.triggered and not self.has_been_triggered:
            LOG.debug(_("skipping {} because it wasn't triggered").format(self.id))
            status_code = self.STATUS_SKIPPED

        if status_code is None and self.cached_unless_result:
            LOG.debug(_("'unless' for {} succeeded, not fixing").format(self.id))
            status_code = self.STATUS_SKIPPED

        if status_code is None:
            status_before = self.cached_status
            if status_before.correct:
                status_code = self.STATUS_OK

        if status_code is None:
            if not interactive:
                self.fix(status_before)
                status_after = self.get_status()
            else:
                question = wrap_question(
                    self.id,
                    self.ask(status_before),
                    _("Fix {}?").format(bold(self.id)),
                )
                if ask_interactively(question,
                                     interactive_default):
                    self.fix(status_before)
                    status_after = self.get_status()
                else:
                    status_code = self.STATUS_SKIPPED

        if status_code is None:
            if status_after.correct:
                status_code = self.STATUS_FIXED
            else:
                status_code = self.STATUS_FAILED

        self.node.repo.hooks.item_apply_end(
            self.node.repo,
            self.node,
            self,
            duration=datetime.now() - start_time,
            status_code=status_code,
            status_before=status_before,
            status_after=status_after,
        )

        return status_code
Example #10
0
 def ask(self, status):
     before = _("running") if status.info['running'] \
         else _("not running")
     after = green(_("running")) if self.attributes['running'] \
         else red(_("not running"))
     return "{} {} → {}\n".format(
         bold(_("status")),
         before,
         after,
     )
Example #11
0
 def ask(self, status):
     if not status.info['exists']:
         return _("'{}' not found in /etc/group").format(self.name)
     elif self.attributes['delete']:
         return _("'{}' found in /etc/group. Will be deleted.").format(self.name)
     else:
         return "{} {} → {}\n".format(
             bold(_("GID")),
             status.info['gid'],
             self.attributes['gid'],
         )
Example #12
0
 def ask(self, status):
     before = status.info['version'] if status.info['version'] \
         else _("not installed")
     target = green(self.attributes['version']) if self.attributes['version'] else \
              green(_("installed"))
     after = target if self.attributes['installed'] \
         else red(_("not installed"))
     return "{} {} → {}\n".format(
         bold(_("status")),
         before,
         after,
     )
Example #13
0
def _fetch_secret(site, secret_id):
    try:
        return cache[site][secret_id]
    except KeyError:
        pass

    session = sessions.setdefault(getpid(), Session())

    try:
        full_url = "{}/api/secrets/{}/".format(
            config.get(site, "url"),
            secret_id,
        )
        credentials = (
            config.get(site, "username"),
            config.get(site, "password"),
        )
    except (NoSectionError, NoOptionError):
        raise FaultUnavailable(
            "Tried to get TeamVault secret with ID '{secret_id}' "
            "from site '{site}', but credentials missing in {path}".format(
                path=CONFIG_PATH,
                secret_id=secret_id,
                site=site,
            ),
        )

    with io.job(_("{tv}  fetching {secret}").format(tv=bold("TeamVault"), secret=secret_id)):
        response = session.get(full_url, auth=credentials)
    if response.status_code != 200:
        raise FaultUnavailable(
            "TeamVault returned {status} for {url}".format(
                status=response.status_code,
                url=full_url,
            )
        )
    secret = response.json()

    response = session.get(secret['current_revision'] + "data", auth=credentials)
    if response.status_code != 200:
        raise FaultUnavailable(
            "TeamVault returned {status} for {url}".format(
                status=response.status_code,
                url=full_url,
            )
        )

    secret['data'] = response.json()

    cache.setdefault(site, {})[secret_id] = secret

    return secret
Example #14
0
    def ask(self, status):
        if 'type' in status.info['needs_fixing']:
            if not status.info['path_info'].exists:
                return _("Doesn't exist.")
            else:
                return "{} {} → {}\n".format(
                    bold(_("type")),
                    status.info['path_info'].desc,
                    _("file"),
                )

        question = ""

        if 'owner' in status.info['needs_fixing']:
            question += "{} {} → {}\n".format(
                bold(_("owner")),
                status.info['path_info'].owner,
                self.attributes['owner'],
            )

        if 'group' in status.info['needs_fixing']:
            question += "{} {} → {}\n".format(
                bold(_("group")),
                status.info['path_info'].group,
                self.attributes['group'],
            )

        if 'target' in status.info['needs_fixing']:
            question += "{} {}\n".format(
                bold(_("target")),
                status.info['path_info'].symlink_target,
            )
            question += "{}{} {}\n".format(
                " " * (len(_("target")) - 1),
                bold("→"),
                self.attributes['target'],
            )

        return question.rstrip("\n")
Example #15
0
    def run(self):
        if self.attributes['data_stdin'] is not None:
            data_stdin = self.attributes['data_stdin']
            # Allow users to use either a string/unicode object or raw
            # bytes -- or Faults.
            if isinstance(data_stdin, Fault):
                data_stdin = data_stdin.value
            if type(data_stdin) is not bytes:
                data_stdin = data_stdin.encode('UTF-8')
        else:
            data_stdin = None

        with io.job(
                _("{node}  {bundle}  {item}").format(
                    bundle=bold(self.bundle.name),
                    item=self.id,
                    node=bold(self.node.name),
                )):
            result = self.bundle.node.run(
                self.attributes['command'],
                data_stdin=data_stdin,
                may_fail=True,
            )

        if self.attributes['expected_return_code'] is not None and \
                not result.return_code == self.attributes['expected_return_code']:
            raise ActionFailure(
                _("wrong return code: {}").format(result.return_code))

        if self.attributes['expected_stderr'] is not None and \
                result.stderr_text != self.attributes['expected_stderr']:
            raise ActionFailure(_("wrong stderr"))

        if self.attributes['expected_stdout'] is not None and \
                result.stdout_text != self.attributes['expected_stdout']:
            raise ActionFailure(_("wrong stdout"))

        return result
Example #16
0
def _fetch_secret(site, secret_id):
    try:
        return cache[site][secret_id]
    except KeyError:
        pass

    session = sessions.setdefault(getpid(), Session())

    try:
        full_url = "{}/api/secrets/{}/".format(
            config.get(site, "url"),
            secret_id,
        )
        credentials = (
            config.get(site, "username"),
            config.get(site, "password"),
        )
    except (NoSectionError, NoOptionError):
        raise FaultUnavailable(
            "Tried to get TeamVault secret with ID '{secret_id}' "
            "from site '{site}', but credentials missing in {path}".format(
                path=CONFIG_PATH,
                secret_id=secret_id,
                site=site,
            ), )

    with io.job(
            _("{tv}  fetching {secret}").format(tv=bold("TeamVault"),
                                                secret=secret_id)):
        response = session.get(full_url, auth=credentials)
    if response.status_code != 200:
        raise FaultUnavailable("TeamVault returned {status} for {url}".format(
            status=response.status_code,
            url=full_url,
        ))
    secret = response.json()

    response = session.get(secret['current_revision'] + "data",
                           auth=credentials)
    if response.status_code != 200:
        raise FaultUnavailable("TeamVault returned {status} for {url}".format(
            status=response.status_code,
            url=full_url,
        ))

    secret['data'] = response.json()

    cache.setdefault(site, {})[secret_id] = secret

    return secret
Example #17
0
    def ask(self, status):
        if 'type' in status.info['needs_fixing']:
            if not status.info['path_info'].exists:
                return _("Doesn't exist. Do you want to create it?")
            else:
                return _(
                    "Not a directory. "
                    "The `file` utility says it's a '{}'.\n"
                    "Do you want it removed and replaced?"
                ).format(
                    status.info['path_info'].desc,
                )

        question = ""

        if 'mode' in status.info['needs_fixing']:
            question += "{} {} → {}\n".format(
                bold(_("mode")),
                status.info['path_info'].mode,
                self.attributes['mode'],
            )

        if 'owner' in status.info['needs_fixing']:
            question += "{} {} → {}\n".format(
                bold(_("owner")),
                status.info['path_info'].owner,
                self.attributes['owner'],
            )

        if 'group' in status.info['needs_fixing']:
            question += "{} {} → {}\n".format(
                bold(_("group")),
                status.info['path_info'].group,
                self.attributes['group'],
            )

        return question.rstrip("\n")
Example #18
0
 def ask(self, status):
     if not status.info['exists'] and not self.attributes['delete']:
         return _("Doesn't exist. Do you want to create it?")
     if status.info['exists'] and self.attributes['delete']:
         return red(_("Will be deleted."))
     output = []
     for attr, attr_pretty in ATTRS.items():
         if self.attributes[attr] is None:
             continue
         if status.info[attr] != self.attributes[attr]:
             if attr in ('password_hash',):
                 output.append("{}  {}\n{}→  {}".format(
                     bold(attr_pretty),
                     status.info[attr],
                     " " * (len(attr_pretty) - 1),
                     self.attributes[attr],
                 ))
             else:
                 output.append("{}  {} → {}".format(
                     bold(attr_pretty),
                     status.info[attr],
                     self.attributes[attr],
                 ))
     return "\n".join(output)
Example #19
0
    def run(self):
        if self.attributes['data_stdin'] is not None:
            data_stdin = self.attributes['data_stdin']
            # Allow users to use either a string/unicode object or raw
            # bytes -- or Faults.
            if isinstance(data_stdin, Fault):
                data_stdin = data_stdin.value
            if type(data_stdin) is not bytes:
                data_stdin = data_stdin.encode('UTF-8')
        else:
            data_stdin = None

        with io.job(_("{node}  {bundle}  {item}").format(
            bundle=bold(self.bundle.name),
            item=self.id,
            node=bold(self.node.name),
        )):
            result = self.bundle.node.run(
                self.attributes['command'],
                data_stdin=data_stdin,
                may_fail=True,
            )

        if self.attributes['expected_return_code'] is not None and \
                not result.return_code == self.attributes['expected_return_code']:
            raise ActionFailure(_("wrong return code: {}").format(result.return_code))

        if self.attributes['expected_stderr'] is not None and \
                result.stderr_text != self.attributes['expected_stderr']:
            raise ActionFailure(_("wrong stderr"))

        if self.attributes['expected_stdout'] is not None and \
                result.stdout_text != self.attributes['expected_stdout']:
            raise ActionFailure(_("wrong stdout"))

        return result
Example #20
0
def install_dir(source, target):
    relpath = os.path.relpath(source, target)
    for _, dirs, files in os.walk(source, topdown=True):
        for directory in dirs:
            os.mkdir(target / directory)
        for file in files:
            try:
                os.symlink(
                    os.path.join(relpath, file),
                    os.path.join(target, file),
                )
            except FileExistsError:
                pass
            io.stdout("{} installed {}".format(
                green("✓"),
                bold(os.path.join(os.path.basename(target), file)),
            ))
Example #21
0
def apply_start(repo, target, nodes, interactive=False, **kwargs):
    '''
    Interactively update all versions if running in interactive mode.
    '''
    _ = target
    _ = nodes
    _ = kwargs
    if interactive:
        question = wrap_question(
            bold("Version management"),
            "Do you want to check configured software versions for updates",
            "Check for updates ?",
            prefix="{} versions".format(blue("?")),
        )
        if not io.ask(question, True):
            return
        repo.libs.versions.VersionManager().update_all(interactive=True)
Example #22
0
    def _get_result(self, interactive=False, interactive_default=True):
        if interactive is False and self.attributes['interactive'] is True:
            return self.STATUS_SKIPPED

        if self.triggered and not self.has_been_triggered:
            LOG.debug(_("skipping {} because it wasn't triggered").format(self.id))
            return self.STATUS_SKIPPED

        if self.unless:
            unless_result = self.bundle.node.run(
                self.unless,
                may_fail=True,
            )
            if unless_result.return_code == 0:
                LOG.debug(_("{node}:{bundle}:action:{name}: failed 'unless', not running").format(
                    bundle=self.bundle.name,
                    name=self.name,
                    node=self.bundle.node.name,
                ))
                return self.STATUS_SKIPPED

        if (
            interactive and
            self.attributes['interactive'] is not False
            and not ask_interactively(
                wrap_question(
                    self.id,
                    self.attributes['command'],
                    _("Run action {}?").format(
                        bold(self.name),
                    ),
                ),
                interactive_default,
            )
        ):
            return self.STATUS_SKIPPED
        try:
            self.run(interactive=interactive)
            return self.STATUS_ACTION_SUCCEEDED
        except ActionFailure:
            return self.STATUS_FAILED
Example #23
0
    def apply(
        self,
        autoskip_selector="",
        autoonly_selector="",
        my_soft_locks=(),
        other_peoples_soft_locks=(),
        interactive=False,
        interactive_default=True,
    ):
        self.node.repo.hooks.item_apply_start(
            self.node.repo,
            self.node,
            self,
        )
        status_code = None
        status_before = None
        status_after = None
        start_time = datetime.now()

        if not self.covered_by_autoonly_selector(autoonly_selector):
            io.debug(
                _("autoonly does not match {item} on {node}").format(
                    item=self.id, node=self.node.name))
            status_code = self.STATUS_SKIPPED
            skip_reason = self.SKIP_REASON_CMDLINE

        if self.covered_by_autoskip_selector(autoskip_selector):
            io.debug(
                _("autoskip matches {item} on {node}").format(
                    item=self.id, node=self.node.name))
            status_code = self.STATUS_SKIPPED
            skip_reason = self.SKIP_REASON_CMDLINE

        if self._skip_with_soft_locks(my_soft_locks, other_peoples_soft_locks):
            status_code = self.STATUS_SKIPPED
            skip_reason = self.SKIP_REASON_SOFTLOCK

        for item in self._precedes_items:
            if item._triggers_preceding_items(interactive=interactive):
                io.debug(
                    _("preceding item {item} on {node} has been triggered by {other_item}"
                      ).format(item=self.id,
                               node=self.node.name,
                               other_item=item.id))
                self.has_been_triggered = True
                break
            else:
                io.debug(
                    _("preceding item {item} on {node} has NOT been triggered by {other_item}"
                      ).format(item=self.id,
                               node=self.node.name,
                               other_item=item.id))

        if self.triggered and not self.has_been_triggered and status_code is None:
            io.debug(
                _("skipping {item} on {node} because it wasn't triggered").
                format(item=self.id, node=self.node.name))
            status_code = self.STATUS_SKIPPED
            skip_reason = self.SKIP_REASON_NO_TRIGGER

        if status_code is None and self.cached_unless_result and status_code is None:
            io.debug(
                _("'unless' for {item} on {node} succeeded, not fixing").
                format(item=self.id, node=self.node.name))
            status_code = self.STATUS_SKIPPED
            skip_reason = self.SKIP_REASON_UNLESS

        if self._faults_missing_for_attributes and status_code is None:
            if self.error_on_missing_fault:
                self._raise_for_faults()
            else:
                io.debug(
                    _("skipping {item} on {node} because it is missing faults "
                      "for these attributes: {attrs} "
                      "(most of the time this means you're missing "
                      "a required key in your .secrets.cfg)").format(
                          attrs=", ".join(
                              sorted(self._faults_missing_for_attributes)),
                          item=self.id,
                          node=self.node.name,
                      ))
                status_code = self.STATUS_SKIPPED
                skip_reason = self.SKIP_REASON_FAULT_UNAVAILABLE

        if status_code is None:
            try:
                status_before = self.cached_status
            except FaultUnavailable:
                if self.error_on_missing_fault:
                    self._raise_for_faults()
                else:
                    io.debug(
                        _("skipping {item} on {node} because it is missing Faults "
                          "(most of the time this means you're missing "
                          "a required key in your .secrets.cfg)").format(
                              item=self.id,
                              node=self.node.name,
                          ))
                    status_code = self.STATUS_SKIPPED
                    skip_reason = self.SKIP_REASON_FAULT_UNAVAILABLE
            else:
                if status_before.correct:
                    status_code = self.STATUS_OK

        if status_code is None:
            if not interactive:
                with io.job(
                        _("{node}  {bundle}  {item}").format(
                            bundle=bold(self.bundle.name),
                            item=self.id,
                            node=bold(self.node.name),
                        )):
                    self.fix(status_before)
            else:
                if status_before.must_be_created:
                    question_text = _("Doesn't exist. Will be created.")
                elif status_before.must_be_deleted:
                    question_text = _("Found on node. Will be removed.")
                else:
                    question_text = self.ask(
                        status_before.display_cdict,
                        status_before.display_sdict,
                        status_before.display_keys_to_fix,
                    )
                if self.comment:
                    question_text += format_comment(self.comment)
                question = wrap_question(
                    self.id,
                    question_text,
                    _("Fix {}?").format(bold(self.id)),
                    prefix="{x} {node} ".format(
                        node=bold(self.node.name),
                        x=blue("?"),
                    ),
                )
                answer = io.ask(
                    question,
                    interactive_default,
                    epilogue="{x} {node}".format(
                        node=bold(self.node.name),
                        x=blue("?"),
                    ),
                )
                if answer:
                    with io.job(
                            _("{node}  {bundle}  {item}").format(
                                bundle=bold(self.bundle.name),
                                item=self.id,
                                node=bold(self.node.name),
                            )):
                        self.fix(status_before)
                else:
                    status_code = self.STATUS_SKIPPED
                    skip_reason = self.SKIP_REASON_INTERACTIVE

        if status_code is None:
            status_after = self.get_status(cached=False)
            status_code = self.STATUS_FIXED if status_after.correct else self.STATUS_FAILED

        if status_code == self.STATUS_OK:
            details = None
        elif status_code == self.STATUS_SKIPPED:
            details = skip_reason
        elif status_before.must_be_created:
            details = True
        elif status_before.must_be_deleted:
            details = False
        elif status_code == self.STATUS_FAILED:
            details = status_after.display_keys_to_fix
        else:
            details = status_before.display_keys_to_fix

        self.node.repo.hooks.item_apply_end(
            self.node.repo,
            self.node,
            self,
            duration=datetime.now() - start_time,
            status_code=status_code,
            status_before=status_before,
            status_after=status_after,
        )
        return (status_code, details)
Example #24
0
    def ask(self, status):
        if 'type' in status.info['needs_fixing']:
            if not status.info['path_info'].exists:
                return _("Doesn't exist.")
            elif self.attributes['delete']:
                if status.info['path_info'].is_directory:
                    return _("Directory and its contents will be deleted.")
                else:
                    return _("File will be deleted.")
            else:
                return "{} {} → {}\n".format(
                    bold(_("type")),
                    status.info['path_info'].desc,
                    _("file"),
                )

        question = ""

        if 'content' in status.info['needs_fixing']:
            question += bold(_("content "))
            if (
                status.info['path_info'].is_text_file and
                not self.attributes['content_type'] == 'binary'
            ):
                if status.info['path_info'].size > DIFF_MAX_FILE_SIZE:
                    question += _("(remote file larger than {} bytes, skipping diff)\n").format(
                        DIFF_MAX_FILE_SIZE,
                    )
                elif len(self.content) > DIFF_MAX_FILE_SIZE:
                    question += _("(new content larger than {} bytes, skipping diff)\n").format(
                        DIFF_MAX_FILE_SIZE,
                    )
                else:
                    content_is = get_remote_file_contents(self.node, self.name)
                    content_should = self.content
                    question += "\n" + diff(
                        content_is,
                        content_should,
                        self.name,
                        encoding_hint=self.attributes['encoding'],
                    ) + "\n"
            else:
                question += "'{}' → {}\n".format(
                    status.info['path_info'].desc,
                    _("<bundlewrap content>"),
                )

        if 'mode' in status.info['needs_fixing']:
            question += "{} {} → {}\n".format(
                bold(_("mode")),
                status.info['path_info'].mode,
                self.attributes['mode'],
            )

        if 'owner' in status.info['needs_fixing']:
            question += "{} {} → {}\n".format(
                bold(_("owner")),
                status.info['path_info'].owner,
                self.attributes['owner'],
            )

        if 'group' in status.info['needs_fixing']:
            question += "{} {} → {}\n".format(
                bold(_("group")),
                status.info['path_info'].group,
                self.attributes['group'],
            )

        return question.rstrip("\n")
Example #25
0
    def apply(
        self,
        autoskip_selector="",
        my_soft_locks=(),
        other_peoples_soft_locks=(),
        interactive=False,
        interactive_default=True,
    ):
        self.node.repo.hooks.item_apply_start(self.node.repo, self.node, self)
        keys_to_fix = None
        status_code = None
        status_before = None
        status_after = None
        start_time = datetime.now()

        if self.covered_by_autoskip_selector(autoskip_selector):
            io.debug(_("autoskip matches {item} on {node}").format(item=self.id, node=self.node.name))
            status_code = self.STATUS_SKIPPED
            keys_to_fix = [_("cmdline")]

        if self._skip_with_soft_locks(my_soft_locks, other_peoples_soft_locks):
            status_code = self.STATUS_SKIPPED
            keys_to_fix = [_("soft locked")]

        if self.triggered and not self.has_been_triggered and status_code is None:
            io.debug(
                _("skipping {item} on {node} because it wasn't triggered").format(item=self.id, node=self.node.name)
            )
            status_code = self.STATUS_SKIPPED
            keys_to_fix = [_("not triggered")]

        if status_code is None and self.cached_unless_result and status_code is None:
            io.debug(_("'unless' for {item} on {node} succeeded, not fixing").format(item=self.id, node=self.node.name))
            status_code = self.STATUS_SKIPPED
            keys_to_fix = ["unless"]

        if self._faults_missing_for_attributes and status_code is None:
            if self.error_on_missing_fault:
                self._raise_for_faults()
            else:
                io.debug(
                    _(
                        "skipping {item} on {node} because it is missing faults "
                        "for these attributes: {attrs} "
                        "(most of the time this means you're missing "
                        "a required key in your .secrets.cfg)"
                    ).format(
                        attrs=", ".join(sorted(self._faults_missing_for_attributes)), item=self.id, node=self.node.name
                    )
                )
                status_code = self.STATUS_SKIPPED
                keys_to_fix = [_("Fault unavailable")]

        if status_code is None:
            try:
                status_before = self.cached_status
            except FaultUnavailable:
                if self.error_on_missing_fault:
                    self._raise_for_faults()
                else:
                    io.debug(
                        _(
                            "skipping {item} on {node} because it is missing Faults "
                            "(most of the time this means you're missing "
                            "a required key in your .secrets.cfg)"
                        ).format(item=self.id, node=self.node.name)
                    )
                    status_code = self.STATUS_SKIPPED
                    keys_to_fix = [_("Fault unavailable")]
            else:
                if status_before.correct:
                    status_code = self.STATUS_OK

        if status_code is None:
            keys_to_fix = self.display_keys(
                copy(self.cached_cdict), copy(status_before.sdict), status_before.keys_to_fix[:]
            )
            if not interactive:
                with io.job(
                    _("  {node}  {bundle}  {item}  fixing...").format(
                        bundle=self.bundle.name, item=self.id, node=self.node.name
                    )
                ):
                    self.fix(status_before)
            else:
                if status_before.must_be_created:
                    question_text = _("Doesn't exist. Will be created.")
                elif status_before.must_be_deleted:
                    question_text = _("Found on node. Will be removed.")
                else:
                    cdict, sdict = self.display_dicts(copy(self.cached_cdict), copy(status_before.sdict), keys_to_fix)
                    question_text = self.ask(cdict, sdict, keys_to_fix)
                question = wrap_question(
                    self.id,
                    question_text,
                    _("Fix {}?").format(bold(self.id)),
                    prefix="{x} {node} ".format(node=bold(self.node.name), x=blue("?")),
                )
                answer = io.ask(
                    question, interactive_default, epilogue="{x} {node}".format(node=bold(self.node.name), x=blue("?"))
                )
                if answer:
                    with io.job(
                        _("  {node}  {bundle}  {item}  fixing...").format(
                            bundle=self.bundle.name, item=self.id, node=self.node.name
                        )
                    ):
                        self.fix(status_before)
                else:
                    status_code = self.STATUS_SKIPPED
                    keys_to_fix = [_("interactive")]

        if status_code is None:
            status_after = self.get_status(cached=False)
            status_code = self.STATUS_FIXED if status_after.correct else self.STATUS_FAILED

        if status_code == self.STATUS_SKIPPED:
            # can't use else for this because status_before is None
            changes = keys_to_fix
        elif status_before.must_be_created:
            changes = True
        elif status_before.must_be_deleted:
            changes = False
        elif status_code == self.STATUS_FAILED:
            changes = self.display_keys(
                self.cached_cdict.copy(), status_after.sdict.copy(), status_after.keys_to_fix[:]
            )
        else:
            changes = keys_to_fix

        self.node.repo.hooks.item_apply_end(
            self.node.repo,
            self.node,
            self,
            duration=datetime.now() - start_time,
            status_code=status_code,
            status_before=status_before,
            status_after=status_after,
        )
        return (status_code, changes)
Example #26
0
def format_comment(comment):
    result = "\n\n"
    for line in wrapper.wrap(cleandoc(comment)):
        for inlineline in line.split("\n"):
            result += "{} {}\n".format(bold("#"), italic(inlineline))
    return result
Example #27
0
def _fetch_secret(site, secret_id):
    try:
        return cache[site][secret_id]
    except KeyError:
        pass

    session = sessions.setdefault(getpid(), Session())

    try:
        full_url = "{}/api/secrets/{}/".format(
            config.get(site, "url"),
            secret_id,
        )

        if site not in cached_credentials:
            if ('password' not in config[site] or not config[site]['password']
                    and 'pass_command' in config[site]):
                try:
                    password = check_output(
                        config[site]['pass_command'],
                        shell=True).decode('UTF-8').splitlines()[0].strip()
                except (FileNotFoundError, CalledProcessError,
                        IndexError) as e:
                    # To avoid trying to get the password over and over.
                    cached_credentials[site] = None

                    raise FaultUnavailable from e
                else:
                    cached_credentials[site] = (
                        config.get(site, 'username'),
                        password,
                    )
            else:
                cached_credentials[site] = (
                    config.get(site, "username"),
                    config.get(site, "password"),
                )
    except (NoSectionError, NoOptionError):
        raise FaultUnavailable(
            "Tried to get TeamVault secret with ID '{secret_id}' "
            "from site '{site}', but credentials missing in {path}".format(
                path=CONFIG_PATH,
                secret_id=secret_id,
                site=site,
            ), )

    if cached_credentials[site] is None:
        raise FaultUnavailable(
            "Getting credentials for {site} failed in earlier try".format(
                site=site, ), )

    try:
        with io.job(
                _("{tv}  fetching {secret}").format(tv=bold("TeamVault"),
                                                    secret=secret_id)):
            response = session.get(full_url, auth=cached_credentials[site])
    except RequestException as e:
        raise FaultUnavailable(
            "Exception while getting secret {secret} from TeamVault: {exc}".
            format(
                secret=secret_id,
                exc=repr(e),
            ))

    if response.status_code != 200:
        raise FaultUnavailable("TeamVault returned {status} for {url}".format(
            status=response.status_code,
            url=full_url,
        ))
    secret = response.json()

    try:
        response = session.get(secret['current_revision'] + "data",
                               auth=cached_credentials[site])
    except RequestException as e:
        raise FaultUnavailable(
            "Exception while getting secret {secret} from TeamVault: {exc}".
            format(
                secret=secret_id,
                exc=repr(e),
            ))

    if response.status_code != 200:
        raise FaultUnavailable("TeamVault returned {status} for {url}".format(
            status=response.status_code,
            url=secret['current_revision'] + "data",
        ))

    secret['data'] = response.json()

    cache.setdefault(site, {})[secret_id] = secret

    return secret
Example #28
0
    def apply(
        self,
        autoskip_selector="",
        my_soft_locks=(),
        other_peoples_soft_locks=(),
        interactive=False,
        interactive_default=True,
    ):
        self.node.repo.hooks.item_apply_start(
            self.node.repo,
            self.node,
            self,
        )
        status_code = None
        status_before = None
        status_after = None
        start_time = datetime.now()

        if self.covered_by_autoskip_selector(autoskip_selector):
            io.debug(_(
                "autoskip matches {item} on {node}"
            ).format(item=self.id, node=self.node.name))
            status_code = self.STATUS_SKIPPED
            skip_reason = self.SKIP_REASON_CMDLINE

        if self._skip_with_soft_locks(my_soft_locks, other_peoples_soft_locks):
            status_code = self.STATUS_SKIPPED
            skip_reason = self.SKIP_REASON_SOFTLOCK

        for item in self._precedes_items:
            if item._triggers_preceding_items(interactive=interactive):
                io.debug(_(
                    "preceding item {item} on {node} has been triggered by {other_item}"
                ).format(item=self.id, node=self.node.name, other_item=item.id))
                self.has_been_triggered = True
                break
            else:
                io.debug(_(
                    "preceding item {item} on {node} has NOT been triggered by {other_item}"
                ).format(item=self.id, node=self.node.name, other_item=item.id))

        if self.triggered and not self.has_been_triggered and status_code is None:
            io.debug(_(
                "skipping {item} on {node} because it wasn't triggered"
            ).format(item=self.id, node=self.node.name))
            status_code = self.STATUS_SKIPPED
            skip_reason = self.SKIP_REASON_NO_TRIGGER

        if status_code is None and self.cached_unless_result and status_code is None:
            io.debug(_(
                "'unless' for {item} on {node} succeeded, not fixing"
            ).format(item=self.id, node=self.node.name))
            status_code = self.STATUS_SKIPPED
            skip_reason = self.SKIP_REASON_UNLESS

        if self._faults_missing_for_attributes and status_code is None:
            if self.error_on_missing_fault:
                self._raise_for_faults()
            else:
                io.debug(_(
                    "skipping {item} on {node} because it is missing faults "
                    "for these attributes: {attrs} "
                    "(most of the time this means you're missing "
                    "a required key in your .secrets.cfg)"
                ).format(
                    attrs=", ".join(sorted(self._faults_missing_for_attributes)),
                    item=self.id,
                    node=self.node.name,
                ))
                status_code = self.STATUS_SKIPPED
                skip_reason = self.SKIP_REASON_FAULT_UNAVAILABLE

        if status_code is None:
            try:
                status_before = self.cached_status
            except FaultUnavailable:
                if self.error_on_missing_fault:
                    self._raise_for_faults()
                else:
                    io.debug(_(
                        "skipping {item} on {node} because it is missing Faults "
                        "(most of the time this means you're missing "
                        "a required key in your .secrets.cfg)"
                    ).format(
                        item=self.id,
                        node=self.node.name,
                    ))
                    status_code = self.STATUS_SKIPPED
                    skip_reason = self.SKIP_REASON_FAULT_UNAVAILABLE
            else:
                if status_before.correct:
                    status_code = self.STATUS_OK

        if status_code is None:
            if not interactive:
                with io.job(_("{node}  {bundle}  {item}").format(
                    bundle=bold(self.bundle.name),
                    item=self.id,
                    node=bold(self.node.name),
                )):
                    self.fix(status_before)
            else:
                if status_before.must_be_created:
                    question_text = _("Doesn't exist. Will be created.")
                elif status_before.must_be_deleted:
                    question_text = _("Found on node. Will be removed.")
                else:
                    question_text = self.ask(
                        status_before.display_cdict,
                        status_before.display_sdict,
                        status_before.display_keys_to_fix,
                    )
                if self.comment:
                    question_text += format_comment(self.comment)
                question = wrap_question(
                    self.id,
                    question_text,
                    _("Fix {}?").format(bold(self.id)),
                    prefix="{x} {node} ".format(
                        node=bold(self.node.name),
                        x=blue("?"),
                    ),
                )
                answer = io.ask(
                    question,
                    interactive_default,
                    epilogue="{x} {node}".format(
                        node=bold(self.node.name),
                        x=blue("?"),
                    ),
                )
                if answer:
                    with io.job(_("{node}  {bundle}  {item}").format(
                        bundle=bold(self.bundle.name),
                        item=self.id,
                        node=bold(self.node.name),
                    )):
                        self.fix(status_before)
                else:
                    status_code = self.STATUS_SKIPPED
                    skip_reason = self.SKIP_REASON_INTERACTIVE

        if status_code is None:
            status_after = self.get_status(cached=False)
            status_code = self.STATUS_FIXED if status_after.correct else self.STATUS_FAILED

        if status_code == self.STATUS_OK:
            details = None
        elif status_code == self.STATUS_SKIPPED:
            details = skip_reason
        elif status_before.must_be_created:
            details = True
        elif status_before.must_be_deleted:
            details = False
        elif status_code == self.STATUS_FAILED:
            details = status_after.display_keys_to_fix
        else:
            details = status_before.display_keys_to_fix

        self.node.repo.hooks.item_apply_end(
            self.node.repo,
            self.node,
            self,
            duration=datetime.now() - start_time,
            status_code=status_code,
            status_before=status_before,
            status_after=status_after,
        )
        return (status_code, details)
Example #29
0
    def _get_result(
        self,
        autoskip_selector="",
        my_soft_locks=(),
        other_peoples_soft_locks=(),
        interactive=False,
        interactive_default=True,
    ):
        if self._faults_missing_for_attributes:
            if self.error_on_missing_fault:
                self._raise_for_faults()
            else:
                io.debug(_(
                    "skipping {item} on {node} because it is missing faults "
                    "for these attributes: {attrs} "
                    "(most of the time this means you're missing "
                    "a required key in your .secrets.cfg)"
                ).format(
                    attrs=", ".join(sorted(self._faults_missing_for_attributes)),
                    item=self.id,
                    node=self.node.name,
                ))
                return (self.STATUS_SKIPPED, self.SKIP_REASON_FAULT_UNAVAILABLE)

        if self.covered_by_autoskip_selector(autoskip_selector):
            io.debug(_(
                "autoskip matches {item} on {node}"
            ).format(item=self.id, node=self.node.name))
            return (self.STATUS_SKIPPED, self.SKIP_REASON_CMDLINE)

        if self._skip_with_soft_locks(my_soft_locks, other_peoples_soft_locks):
            return (self.STATUS_SKIPPED, self.SKIP_REASON_SOFTLOCK)

        if interactive is False and self.attributes['interactive'] is True:
            return (self.STATUS_SKIPPED, self.SKIP_REASON_INTERACTIVE_ONLY)

        for item in self._precedes_items:
            if item._triggers_preceding_items(interactive=interactive):
                io.debug(_(
                    "preceding item {item} on {node} has been triggered by {other_item}"
                ).format(item=self.id, node=self.node.name, other_item=item.id))
                self.has_been_triggered = True
                break
            else:
                io.debug(_(
                    "preceding item {item} on {node} has NOT been triggered by {other_item}"
                ).format(item=self.id, node=self.node.name, other_item=item.id))

        if self.triggered and not self.has_been_triggered:
            io.debug(_("skipping {} because it wasn't triggered").format(self.id))
            return (self.STATUS_SKIPPED, self.SKIP_REASON_NO_TRIGGER)

        if self.unless:
            with io.job(_("{node}  {bundle}  {item}  checking 'unless' condition").format(
                bundle=bold(self.bundle.name),
                item=self.id,
                node=bold(self.node.name),
            )):
                unless_result = self.bundle.node.run(
                    self.unless,
                    may_fail=True,
                )
            if unless_result.return_code == 0:
                io.debug(_("{node}:{bundle}:action:{name}: failed 'unless', not running").format(
                    bundle=self.bundle.name,
                    name=self.name,
                    node=self.bundle.node.name,
                ))
                return (self.STATUS_SKIPPED, self.SKIP_REASON_UNLESS)

        question_body = ""
        if self.attributes['data_stdin'] is not None:
            question_body += "<" + _("data") + "> | "
        question_body += self.attributes['command']
        if self.comment:
            question_body += format_comment(self.comment)

        if (
            interactive and
            self.attributes['interactive'] is not False and
            not io.ask(
                wrap_question(
                    self.id,
                    question_body,
                    _("Run action {}?").format(
                        bold(self.name),
                    ),
                    prefix="{x} {node} ".format(
                        node=bold(self.node.name),
                        x=blue("?"),
                    ),
                ),
                interactive_default,
                epilogue="{x} {node}".format(
                    node=bold(self.node.name),
                    x=blue("?"),
                ),
            )
        ):
            return (self.STATUS_SKIPPED, self.SKIP_REASON_INTERACTIVE)
        try:
            self.run()
            return (self.STATUS_ACTION_SUCCEEDED, None)
        except ActionFailure as exc:
            return (self.STATUS_FAILED, [str(exc)])
Example #30
0
    def _get_result(
        self,
        autoskip_selector="",
        my_soft_locks=(),
        other_peoples_soft_locks=(),
        interactive=False,
        interactive_default=True,
    ):

        if self.covered_by_autoskip_selector(autoskip_selector):
            io.debug(
                _("autoskip matches {item} on {node}").format(
                    item=self.id, node=self.node.name))
            return (self.STATUS_SKIPPED, [_("cmdline")])

        if self._skip_with_soft_locks(my_soft_locks, other_peoples_soft_locks):
            return (self.STATUS_SKIPPED, [_("soft locked")])

        if interactive is False and self.attributes['interactive'] is True:
            return (self.STATUS_SKIPPED, [_("interactive only")])

        if self.triggered and not self.has_been_triggered:
            io.debug(
                _("skipping {} because it wasn't triggered").format(self.id))
            return (self.STATUS_SKIPPED, [_("no trigger")])

        if self.unless:
            with io.job(
                    _("  {node}  {bundle}  {item}  checking 'unless' condition..."
                      ).format(
                          bundle=self.bundle.name,
                          item=self.id,
                          node=self.node.name,
                      )):
                unless_result = self.bundle.node.run(
                    self.unless,
                    may_fail=True,
                )
            if unless_result.return_code == 0:
                io.debug(
                    _("{node}:{bundle}:action:{name}: failed 'unless', not running"
                      ).format(
                          bundle=self.bundle.name,
                          name=self.name,
                          node=self.bundle.node.name,
                      ))
                return (self.STATUS_SKIPPED, ["unless"])

        question_body = ""
        if self.attributes['data_stdin'] is not None:
            question_body += "<" + _("data") + "> | "
        question_body += self.attributes['command']
        if self.comment:
            question_body += format_comment(self.comment)

        if (interactive and self.attributes['interactive'] is not False
                and not io.ask(
                    wrap_question(
                        self.id,
                        question_body,
                        _("Run action {}?").format(bold(self.name), ),
                        prefix="{x} {node} ".format(
                            node=bold(self.node.name),
                            x=blue("?"),
                        ),
                    ),
                    interactive_default,
                    epilogue="{x} {node}".format(
                        node=bold(self.node.name),
                        x=blue("?"),
                    ),
                )):
            return (self.STATUS_SKIPPED, [_("interactive")])
        try:
            self.run()
            return (self.STATUS_ACTION_SUCCEEDED, None)
        except ActionFailure as exc:
            return (self.STATUS_FAILED, [str(exc)])
Example #31
0
    def apply(
        self,
        autoskip_selector="",
        my_soft_locks=(),
        other_peoples_soft_locks=(),
        interactive=False,
        interactive_default=True,
    ):
        self.node.repo.hooks.item_apply_start(
            self.node.repo,
            self.node,
            self,
        )
        keys_to_fix = None
        status_code = None
        status_before = None
        status_after = None
        start_time = datetime.now()

        if self.covered_by_autoskip_selector(autoskip_selector):
            io.debug(
                _("autoskip matches {item} on {node}").format(
                    item=self.id, node=self.node.name))
            status_code = self.STATUS_SKIPPED
            keys_to_fix = [_("cmdline")]

        if self._skip_with_soft_locks(my_soft_locks, other_peoples_soft_locks):
            status_code = self.STATUS_SKIPPED
            keys_to_fix = [_("soft locked")]

        if self.triggered and not self.has_been_triggered and status_code is None:
            io.debug(
                _("skipping {item} on {node} because it wasn't triggered").
                format(item=self.id, node=self.node.name))
            status_code = self.STATUS_SKIPPED
            keys_to_fix = [_("not triggered")]

        if status_code is None and self.cached_unless_result and status_code is None:
            io.debug(
                _("'unless' for {item} on {node} succeeded, not fixing").
                format(item=self.id, node=self.node.name))
            status_code = self.STATUS_SKIPPED
            keys_to_fix = ["unless"]

        if self._faults_missing_for_attributes and status_code is None:
            if self.error_on_missing_fault:
                self._raise_for_faults()
            else:
                io.debug(
                    _("skipping {item} on {node} because it is missing faults "
                      "for these attributes: {attrs} "
                      "(most of the time this means you're missing "
                      "a required key in your .secrets.cfg)").format(
                          attrs=", ".join(
                              sorted(self._faults_missing_for_attributes)),
                          item=self.id,
                          node=self.node.name,
                      ))
                status_code = self.STATUS_SKIPPED
                keys_to_fix = [_("Fault unavailable")]

        if status_code is None:
            try:
                status_before = self.cached_status
            except FaultUnavailable:
                if self.error_on_missing_fault:
                    self._raise_for_faults()
                else:
                    io.debug(
                        _("skipping {item} on {node} because it is missing Faults "
                          "(most of the time this means you're missing "
                          "a required key in your .secrets.cfg)").format(
                              item=self.id,
                              node=self.node.name,
                          ))
                    status_code = self.STATUS_SKIPPED
                    keys_to_fix = [_("Fault unavailable")]
            else:
                if status_before.correct:
                    status_code = self.STATUS_OK

        if status_code is None:
            keys_to_fix = self.display_keys(
                copy(self.cached_cdict),
                copy(status_before.sdict),
                status_before.keys_to_fix[:],
            )
            if not interactive:
                with io.job(
                        _("  {node}  {bundle}  {item}  fixing...").format(
                            bundle=self.bundle.name,
                            item=self.id,
                            node=self.node.name,
                        )):
                    self.fix(status_before)
            else:
                if status_before.must_be_created:
                    question_text = _("Doesn't exist. Will be created.")
                elif status_before.must_be_deleted:
                    question_text = _("Found on node. Will be removed.")
                else:
                    cdict, sdict = self.display_dicts(
                        copy(self.cached_cdict),
                        copy(status_before.sdict),
                        keys_to_fix,
                    )
                    question_text = self.ask(cdict, sdict, keys_to_fix)
                if self.comment:
                    question_text += format_comment(self.comment)
                question = wrap_question(
                    self.id,
                    question_text,
                    _("Fix {}?").format(bold(self.id)),
                    prefix="{x} {node} ".format(
                        node=bold(self.node.name),
                        x=blue("?"),
                    ),
                )
                answer = io.ask(
                    question,
                    interactive_default,
                    epilogue="{x} {node}".format(
                        node=bold(self.node.name),
                        x=blue("?"),
                    ),
                )
                if answer:
                    with io.job(
                            _("  {node}  {bundle}  {item}  fixing...").format(
                                bundle=self.bundle.name,
                                item=self.id,
                                node=self.node.name,
                            )):
                        self.fix(status_before)
                else:
                    status_code = self.STATUS_SKIPPED
                    keys_to_fix = [_("interactive")]

        if status_code is None:
            status_after = self.get_status(cached=False)
            status_code = self.STATUS_FIXED if status_after.correct else self.STATUS_FAILED

        if status_code == self.STATUS_SKIPPED:
            # can't use else for this because status_before is None
            changes = keys_to_fix
        elif status_before.must_be_created:
            changes = True
        elif status_before.must_be_deleted:
            changes = False
        elif status_code == self.STATUS_FAILED:
            changes = self.display_keys(
                self.cached_cdict.copy(),
                status_after.sdict.copy(),
                status_after.keys_to_fix[:],
            )
        else:
            changes = keys_to_fix

        self.node.repo.hooks.item_apply_end(
            self.node.repo,
            self.node,
            self,
            duration=datetime.now() - start_time,
            status_code=status_code,
            status_before=status_before,
            status_after=status_after,
        )
        return (status_code, changes)
Example #32
0
def test_ansi_clean():
    assert red("test") != "test"
    assert len(red("test")) != len("test")
    assert ansi_clean(red("test")) == "test"
    assert ansi_clean(bold(red("test"))) == "test"
Example #33
0
    def _get_result(
        self,
        autoskip_selector="",
        my_soft_locks=(),
        other_peoples_soft_locks=(),
        interactive=False,
        interactive_default=True,
    ):
        if self._faults_missing_for_attributes:
            if self.error_on_missing_fault:
                self._raise_for_faults()
            else:
                io.debug(
                    _("skipping {item} on {node} because it is missing faults "
                      "for these attributes: {attrs} "
                      "(most of the time this means you're missing "
                      "a required key in your .secrets.cfg)").format(
                          attrs=", ".join(
                              sorted(self._faults_missing_for_attributes)),
                          item=self.id,
                          node=self.node.name,
                      ))
                return (self.STATUS_SKIPPED,
                        self.SKIP_REASON_FAULT_UNAVAILABLE)

        if self.covered_by_autoskip_selector(autoskip_selector):
            io.debug(
                _("autoskip matches {item} on {node}").format(
                    item=self.id, node=self.node.name))
            return (self.STATUS_SKIPPED, self.SKIP_REASON_CMDLINE)

        if self._skip_with_soft_locks(my_soft_locks, other_peoples_soft_locks):
            return (self.STATUS_SKIPPED, self.SKIP_REASON_SOFTLOCK)

        if interactive is False and self.attributes['interactive'] is True:
            return (self.STATUS_SKIPPED, self.SKIP_REASON_INTERACTIVE_ONLY)

        for item in self._precedes_items:
            if item._triggers_preceding_items(interactive=interactive):
                io.debug(
                    _("preceding item {item} on {node} has been triggered by {other_item}"
                      ).format(item=self.id,
                               node=self.node.name,
                               other_item=item.id))
                self.has_been_triggered = True
                break
            else:
                io.debug(
                    _("preceding item {item} on {node} has NOT been triggered by {other_item}"
                      ).format(item=self.id,
                               node=self.node.name,
                               other_item=item.id))

        if self.triggered and not self.has_been_triggered:
            io.debug(
                _("skipping {} because it wasn't triggered").format(self.id))
            return (self.STATUS_SKIPPED, self.SKIP_REASON_NO_TRIGGER)

        if self.unless:
            with io.job(
                    _("{node}  {bundle}  {item}  checking 'unless' condition").
                    format(
                        bundle=bold(self.bundle.name),
                        item=self.id,
                        node=bold(self.node.name),
                    )):
                unless_result = self.bundle.node.run(
                    self.unless,
                    may_fail=True,
                )
            if unless_result.return_code == 0:
                io.debug(
                    _("{node}:{bundle}:action:{name}: failed 'unless', not running"
                      ).format(
                          bundle=self.bundle.name,
                          name=self.name,
                          node=self.bundle.node.name,
                      ))
                return (self.STATUS_SKIPPED, self.SKIP_REASON_UNLESS)

        question_body = ""
        if self.attributes['data_stdin'] is not None:
            question_body += "<" + _("data") + "> | "
        question_body += self.attributes['command']
        if self.comment:
            question_body += format_comment(self.comment)

        if (interactive and self.attributes['interactive'] is not False
                and not io.ask(
                    wrap_question(
                        self.id,
                        question_body,
                        _("Run action {}?").format(bold(self.name), ),
                        prefix="{x} {node} ".format(
                            node=bold(self.node.name),
                            x=blue("?"),
                        ),
                    ),
                    interactive_default,
                    epilogue="{x} {node}".format(
                        node=bold(self.node.name),
                        x=blue("?"),
                    ),
                )):
            return (self.STATUS_SKIPPED, self.SKIP_REASON_INTERACTIVE)
        try:
            self.run()
            return (self.STATUS_ACTION_SUCCEEDED, None)
        except ActionFailure as exc:
            return (self.STATUS_FAILED, [str(exc)])