예제 #1
0
파일: file.py 프로젝트: ZeitOnline/batou
    def verify(self):
        if self._delayed:
            self._render()
        try:
            with open(self.path, 'rb') as target:
                current = target.read()
                if current == self.content:
                    return
        except FileNotFoundError:
            current = b''
        except Exception:
            output.annotate('Unknown content - can\'t predict diff.')
            raise batou.UpdateNeeded()

        encoding = self.encoding or 'ascii'
        current_text = current.decode(encoding, errors='replace')
        wanted_text = self.content.decode(encoding)
        for line in difflib.unified_diff(current_text.splitlines(),
                                         wanted_text.splitlines()):
            line = line.replace('\n', '')
            if not line.strip():
                continue
            output.annotate('\t{} {}'.format(os.path.basename(self.path),
                                             line),
                            red=line.startswith('-'),
                            green=line.startswith('+'))
        raise batou.UpdateNeeded()
예제 #2
0
파일: rabbitmq.py 프로젝트: frlan/batou_ext
 def verify(self):
     with open(self.path, 'r') as target:
         current = target.read()
         if current != self.cookie:
             raise batou.UpdateNeeded()
     current = os.stat(self.path).st_mode
     if stat.S_IMODE(current) != 0o400:
         raise batou.UpdateNeeded()
예제 #3
0
 def verify(self):
     if not os.path.exists(self.known_hosts):
         raise batou.UpdateNeeded()
     with open(self.known_hosts, 'r') as f:
         content = f.read()
     if self.port == 22:
         match = self.hostname
     else:
         match = '[{}]:{}'.format(self.hostname, self.port)
     if match not in content:
         raise batou.UpdateNeeded()
예제 #4
0
    def assert_component_is_current(self, requirements=[], **kw):
        """Assert that this component has been updated more recently
        than the components specified in the ``requirements``,
        raise :py:class:`UpdateNeeded` otherwise.

        :param list requirements: The list of components you want to
            check against.

        :return: ``None``, if this component is as new or newer as all
            ``requirements``.

        :param dict kw: Arguments that are passed through to
            each ``last_update`` call. The semantics depend on the components'
            implementations.

        :raises UpdateNeeded: if this component is older than any of the
            ``requirements``.

        The age of a component is determined by calling ``last_updated``
        on this and each requirement component.

        """

        if isinstance(requirements, Component):
            requirements = [requirements]
        reference = self.last_updated(**kw)
        if reference is None:
            output.annotate(
                "assert_component_is_current({}, ...): No reference".format(
                    self._breadcrumb),
                debug=True,
            )
            raise batou.UpdateNeeded()
        for requirement in requirements:
            self |= requirement
            required = requirement.last_updated(**kw)
            if required is None:
                continue
            if reference < required:
                output.annotate(
                    "assert_component_is_current({}, {}): {} < {}".format(
                        self._breadcrumb,
                        requirement._breadcrumb,
                        reference,
                        required,
                    ),
                    debug=True,
                )
                raise batou.UpdateNeeded()
예제 #5
0
 def verify(self):
     cmd_out, cmt_err = self.pgcmd(
         self.expand("psql -d {{component.db}} -qtAX "
                     '-c "SELECT extname FROM pg_extension '
                     "WHERE extname = '{{component.extension_name}}';\""))
     if cmd_out.strip() != self.extension_name:
         raise batou.UpdateNeeded()
예제 #6
0
    def deploy(self, predict_only=False):
        # Remember: this is a tight loop - we need to keep this code fast.

        # Reset changed flag here to support triggering deploy() multiple
        # times. This is mostly helpful for testing, but who knows.
        self.changed = False
        for sub_component in self.sub_components:
            sub_component.deploy(predict_only)
            if sub_component.changed:
                self.changed = True

        if not os.path.exists(self.workdir):
            os.makedirs(self.workdir)
        with self.chdir(self.workdir), self:
            try:
                with batou.utils.Timer('{} verify()'.format(
                        self._breadcrumbs)):
                    try:
                        self.verify()
                    except Exception:
                        if predict_only:
                            # XXX?!?!?
                            raise batou.UpdateNeeded()
                        raise
            except batou.UpdateNeeded:
                self.__trigger_event__('before-update',
                                       predict_only=predict_only)
                output.annotate(self._breadcrumbs)
                if not predict_only:
                    self.update()
                self.changed = True
예제 #7
0
 def verify(self):
     try:
         self.pgcmd(
             self.expand('psql -c "SELECT true;" -d "{{component.db}}"'),
             silent=True)
     except batou.utils.CmdExecutionError:
         raise batou.UpdateNeeded()
예제 #8
0
파일: rabbitmq.py 프로젝트: frlan/batou_ext
 def verify(self):
     stdout, stderr = self.cmd('rabbitmqctl -q list_users')
     users = stdout.splitlines()
     for line in users:
         if not line:
             continue
         user, tags = line.split('\t', 1)
         if user == self.username:
             raise batou.UpdateNeeded()
예제 #9
0
 def verify(self):
     if self.attribute:
         stdout, stderr = self.cmd("nix-env -qaA {{component.attribute}}")
         self._installs_package = stdout.strip()
     else:
         self._installs_package = self.package
     stdout, stderr = self.cmd("nix-env --query")
     if self._installs_package not in stdout.splitlines():
         raise batou.UpdateNeeded()
예제 #10
0
파일: postgres.py 프로젝트: frlan/batou_ext
 def verify(self):
     os.environ['PGPASSWORD'] = self.password
     try:
         self.cmd(self.expand(
             'psql -d postgres -c "SELECT true;" -U {{component.name}} '
             '-w -h localhost'))
     except batou.utils.CmdExecutionError:
         raise batou.UpdateNeeded()
     finally:
         del os.environ['PGPASSWORD']
예제 #11
0
파일: file.py 프로젝트: ZeitOnline/batou
 def verify(self):
     try:
         self._select_stat_implementation()
     except AttributeError:
         # Happens on systems without lstat/lchmod implementation (like
         # Linux) Not sure whether ignoring it is really the right thing.
         return
     current = self._stat(self.path).st_mode
     if stat.S_IMODE(current) != self.mode:
         raise batou.UpdateNeeded()
예제 #12
0
파일: file.py 프로젝트: ZeitOnline/batou
    def verify(self):
        stdout, stderr = self.cmd('rsync {} {}{}/ {}'.format(
            self.verify_opts, self.exclude_arg, self.source, self.path))

        # In case of we see non-convergent rsync runs
        output.annotate('rsync result:', debug=True)
        output.annotate(stdout, debug=True)

        if len(stdout.strip().splitlines()) - 4 > 0:
            raise batou.UpdateNeeded()
예제 #13
0
    def assert_cmd(self, *args, **kw):
        """Assert that given command returns successfully, raise
        :py:class:`UpdateNeeded` otherwise.

        For details about the command arguments and what a successful execution
        means, see :py:func:`batou.component.Component.cmd`.

        """
        try:
            self.cmd(*args, **kw)
        except batou.utils.CmdExecutionError:
            raise batou.UpdateNeeded()
예제 #14
0
파일: python.py 프로젝트: wosc/batou
 def verify_pkg(self, pkg):
     try:
         self.cmd('bin/python -c "'
                  "import pkg_resources; "
                  "assert pkg_resources.require('{}')[0].parsed_version == "
                  "pkg_resources.parse_version('{}')\"".format(
                      pkg.package, pkg.version))
     except CmdExecutionError:
         raise batou.UpdateNeeded()
     # Is the package usable? Is the package a module?  This might be
     # overspecific - I'm looking for a way to deal with:
     # https://github.com/pypa/pip/issues/3 if a namespace package was not
     # installed cleanly. This only works (currently), when the package name
     # corresponds with what it contains. I.E. it works for zc.buildout but
     # not for distribute, which installs a setuptools package.
     if pkg.check_package_is_module:
         try:
             self.cmd('bin/python -c "import pkg_resources; '
                      'import {0};{0}.__file__"'.format(pkg.package))
         except CmdExecutionError:
             raise batou.UpdateNeeded()
예제 #15
0
파일: file.py 프로젝트: frlan/batou
 def verify(self):
     if self._delayed:
         self._render()
     with open(self.path, 'rb') as target:
         current = target.read()
         if current == self.content:
             return
         if self.encoding:
             current_text = current.decode(self.encoding, errors='replace')
             wanted_text = self.content.decode(self.encoding)
             for line in difflib.unified_diff(current_text.splitlines(),
                                              wanted_text.splitlines()):
                 output.annotate(line, debug=True)
         raise batou.UpdateNeeded()
예제 #16
0
파일: rabbitmq.py 프로젝트: frlan/batou_ext
 def verify(self):
     stdout, stderr = self.cmd(
         'rabbitmqctl -q list_user_permissions {{component.username}}')
     lines = stdout.splitlines()
     to_validate = self.permissions.copy()
     self.to_update = []
     self.to_delete = []
     for line in lines:
         vhost, conf, write, read = line.split('\t', 3)
         perms = to_validate.pop(vhost, None)
         if perms is None:
             self.to_delete.append(vhost)
         elif perms != (conf, write, read):
             self.to_update.append(vhost)
     self.to_update.extend(to_validate.keys())
     if self.to_update or self.to_delete:
         raise batou.UpdateNeeded()
예제 #17
0
파일: rabbitmq.py 프로젝트: frlan/batou_ext
 def verify(self):
     stdout, stderr = self.cmd('rabbitmqctl -q list_users')
     users = stdout.splitlines()
     self.create = True
     self.set_tags = True
     for line in users:
         if not line:
             continue
         user, tags = line.split('\t', 1)
         tags = sorted(tags[1:-1].split(', '))
         if user == self.username:
             self.create = False
             if tags == self.tags:
                 self.set_tags = False
             break
     if self.create or self.set_tags:
         raise batou.UpdateNeeded()
예제 #18
0
    def assert_no_changes(self):
        """Assert that, during this run of batou, neither
        this component nor any of its sub-components have required an update.

        :return: ``None``, if neither this component nor any of its
            sub-components have required an update during this run of batou.

        :raises UpdateNeeded: if this component or any of its sub-components
            have required an update during this run of batou.

        .. note::

            Using this change indicator can be unreliable if you fail to
            perform your update correctly. It is likely that when later
            resuming an aborted deployment this change won't be triggered
            again.

        """
        if self.changed:
            raise batou.UpdateNeeded()
        self.assert_no_subcomponent_changes()
예제 #19
0
 def verify(self):
     if not os.path.exists(self.target):
         raise batou.UpdateNeeded()
     if self.checksum != batou.utils.hash(self.target,
                                          self.checksum_function):
         raise batou.UpdateNeeded()
예제 #20
0
파일: file.py 프로젝트: ZeitOnline/batou
 def verify(self):
     if glob.glob(self.pattern):
         raise batou.UpdateNeeded()
예제 #21
0
파일: nix.py 프로젝트: frlan/batou_ext
 def verify(self):
     stdout, stderr = self.cmd("nix-env --query")
     if self.package in stdout.splitlines():
         raise batou.UpdateNeeded()
예제 #22
0
    def verify(self, predicting=False):
        try:
            if self._delayed:
                self._render()
        except FileNotFoundError:
            if predicting:
                # During prediction runs we accept that delayed rending may
                # not yet work and that we will change. We might want to
                # turn this into an explicit flag so we don't implicitly
                # run into a broken deployment.
                assert False
            # If we are not predicting then this is definitely a problem.
            # Stop here.
            raise
        try:
            with open(self.path, "rb") as target:
                current = target.read()
                if current == self.content:
                    return
        except FileNotFoundError:
            current = b""
        except Exception:
            output.annotate("Unknown content - can't predict diff.")
            raise batou.UpdateNeeded()

        if self.encoding:
            current_text = current.decode(self.encoding, errors="replace")
            wanted_text = self.content.decode(self.encoding, errors="replace")

        if not self.encoding:
            output.annotate("Not showing diff for binary data.", yellow=True)
        elif self.sensitive_data:
            output.annotate("Not showing diff as it contains sensitive data.",
                            red=True)
        else:
            current_lines = current_text.splitlines()
            wanted_lines = wanted_text.splitlines()
            words = set(
                itertools.chain(*(x.split() for x in current_lines),
                                *(x.split() for x in wanted_lines)))
            contains_secrets = bool(
                self.environment.secret_data.intersection(words))

            diff = difflib.unified_diff(current_lines, wanted_lines)
            if not os.path.exists(self.diff_dir):
                os.makedirs(self.diff_dir)
            diff, diff_too_long, diff_log = limited_buffer(
                diff,
                self._max_diff,
                self._max_diff_lead,
                logdir=self.diff_dir)

            if diff_too_long:
                output.line(
                    f"More than {self._max_diff} lines of diff. Showing first "
                    f"and last {self._max_diff_lead} lines.",
                    yellow=True)
                output.line(f"see {diff_log} for the full diff.".format(),
                            yellow=True)
            if contains_secrets:
                output.line("Not showing diff as it contains sensitive data,",
                            yellow=True)
                output.line(f"see {diff_log} for the diff.".format(),
                            yellow=True)
            else:
                for line in diff:
                    line = line.replace("\n", "")
                    if not line.strip():
                        continue
                    output.annotate(f"  {os.path.basename(self.path)} {line}",
                                    red=line.startswith("-"),
                                    green=line.startswith("+"))
        raise batou.UpdateNeeded()
예제 #23
0
파일: file.py 프로젝트: ZeitOnline/batou
 def verify(self):
     if isinstance(self.owner, str):
         self.owner = pwd.getpwnam(self.owner).pw_uid
     current = os.stat(self.path).st_uid
     if current != self.owner:
         raise batou.UpdateNeeded()
예제 #24
0
파일: file.py 프로젝트: ZeitOnline/batou
 def verify(self):
     if not os.path.isdir(self.path):
         raise batou.UpdateNeeded()
예제 #25
0
파일: file.py 프로젝트: ZeitOnline/batou
 def verify(self):
     current = os.stat(self.path).st_gid
     if current != self.group:
         raise batou.UpdateNeeded()
예제 #26
0
파일: rabbitmq.py 프로젝트: frlan/batou_ext
 def verify(self):
     stdout, stderr = self.cmd('rabbitmqctl -q list_vhosts')
     vhosts = stdout.splitlines()
     if self.name not in vhosts:
         raise batou.UpdateNeeded()
예제 #27
0
파일: file.py 프로젝트: ZeitOnline/batou
 def verify(self):
     if not os.path.islink(self.target):
         raise batou.UpdateNeeded()
     if os.readlink(self.target) != self.source:
         raise batou.UpdateNeeded()
예제 #28
0
 def verify(self):
     try:
         self.cmd("nix-env --query {{component.package}}")
         raise batou.UpdateNeeded()
     except batou.utils.CmdExecutionError:
         pass
예제 #29
0
 def verify(self):
     log.append('{}:verify'.format(self.id))
     raise batou.UpdateNeeded()