Example #1
0
async def create_linux_user(
    kitchen: Kitchen,
    name: str,
    password_hash: Optional[str],
    create_home: bool = True,
    create_group: bool = True,
    home: Optional[str] = None,
) -> None:
    args = ["useradd"]

    if password_hash:
        # N.B. if you don't use a password hash, the account will be locked
        # but passwordless SSH still works
        args += ["-p", password_hash]

    if create_home:
        args.append("-m")

    if create_group:
        args.append("-U")
    else:
        args.append("-N")

    if home:
        args += ["-d", home]

    # finally, append the user name
    args.append(name)

    result = await kitchen.ut1areq(SimpleExec(args, "/"), SimpleExec.Result)

    if result.exit_code != 0:
        raise RuntimeError("Failed to create user. Error was: " +
                           result.stderr.strip().decode())
Example #2
0
async def cook_systemd_start(kitchen: Kitchen, unit_name: str):
    result = await kitchen.ut1areq(
        SimpleExec(
            ["systemctl", "start", unit_name],
            "/",
        ),
        SimpleExec.Result,
    )

    if result.exit_code != 0:
        raise RuntimeError(
            f"Failed to start {unit_name}: {result.stderr.decode()}")
Example #3
0
async def cook_systemd_daemon_reload(kitchen):
    result = await kitchen.ut1areq(
        SimpleExec(
            ["systemctl", "daemon-reload"],
            "/",
        ),
        SimpleExec.Result,
    )

    if result.exit_code != 0:
        raise RuntimeError(
            f"Failed to reload systemd: {result.stderr.decode()}")
Example #4
0
    async def cook(self, kitchen: Kitchen) -> None:
        kitchen.get_dependency_tracker().ignore()

        changed = await kitchen.ut1(
            HasChangedInSousStore(self.purpose, self.watching))

        if changed:
            result = await kitchen.ut1areq(
                SimpleExec(self.command, self.working_dir), SimpleExec.Result)

            if result.exit_code != 0:
                raise RuntimeError(
                    f"exit code not 0 ({result.exit_code}), {result.stderr!r}")
Example #5
0
    async def _apt_command(
        self, kitchen: Kitchen, args: List[str]
    ) -> SimpleExec.Result:
        retries = 3

        while retries > 0:
            result = await kitchen.ut1areq(SimpleExec(args, "/"), SimpleExec.Result)

            if result.exit_code == 0 or b"/lock" not in result.stderr:
                return result

            logger.warning(
                "Failed apt command due to suspected locking issue. Will retry…"
            )

            retries -= 1

            # /lock seen in stderr, probably a locking issue...
            lock_check = await kitchen.ut1areq(
                SimpleExec(
                    ["fuser", "/var/lib/dpkg/lock", "/var/lib/apt/lists/lock"], "/"
                ),
                SimpleExec.Result,
            )

            if lock_check.exit_code != 0:
                # non-zero code means the file is not being accessed;
                # use up a retry (N.B. we retry because this could be racy...)
                logger.warning(
                    "Suspected locking issue is either racy or a red herring."
                )
                retries -= 1

            await asyncio.sleep(2.0)

        return result  # noqa
Example #6
0
    async def cook(self, k: "Kitchen"):
        res = await k.ut1areq(SimpleExec(["tar", "xf", self.tar], self.dir),
                              SimpleExec.Result)
        if res.exit_code != 0:
            raise RuntimeError(
                f"tar failed with ec {res.exit_code}; stderr = <<<"
                f"\n{res.stderr.decode()}\n>>>")

        for expect_relative in self.expect_files:
            expect = str(Path(self.dir, expect_relative))
            stat = await k.ut1a(Stat(expect), Stat.Result)
            if stat is None:
                raise RuntimeError(
                    f"tar succeeded but expectation failed; {expect!r} not found."
                )
Example #7
0
async def cook_systemd_enable(kitchen: Kitchen, enabled: bool, unit_name: str):
    # thoughts: we could find the unit path with:
    #   systemctl show -p FragmentPath apache2.service

    result = await kitchen.ut1areq(
        SimpleExec(
            ["systemctl", "enable" if enabled else "disable", unit_name],
            "/",
        ),
        SimpleExec.Result,
    )

    if result.exit_code != 0:
        raise RuntimeError(
            f"Failed to en/disable {unit_name}: {result.stderr.decode()}")
Example #8
0
async def exec_no_fails(
        kitchen: Kitchen, args: List[str],
        working_dir: Union[str, PurePath]) -> SimpleExec.Result:
    if not isinstance(working_dir, str):
        working_dir = str(working_dir)

    result = await kitchen.start_and_consume_attrs(
        SimpleExec(args, working_dir), SimpleExec.Result)

    if result.exit_code != 0:
        recipe: Optional[Recipe] = current_recipe.get(None)  # type: ignore
        if recipe:
            raise ExecutionFailure(
                args,
                working_dir,
                recipe.recipe_context.sous,
                recipe.recipe_context.user,
                result,
            )
        else:
            raise ExecutionFailure(args, working_dir, "???", "???", result)

    return result
Example #9
0
    async def cook(self, k: Kitchen) -> None:
        # no non-arg dependencies
        k.get_dependency_tracker()

        stat = await k.ut1a(Stat(self.dest_dir), Stat.Result)
        if stat is None:
            # doesn't exist; git init it
            await exec_no_fails(k, ["git", "init", self.dest_dir], "/")

        stat = await k.ut1a(Stat(self.dest_dir), Stat.Result)
        if stat is None:
            raise RuntimeError("Directory vanished after creation!")

        if not stat.dir:
            raise RuntimeError("Already exists but not a dir: " +
                               self.dest_dir)

        # add the remote, removing it first to ensure it's what we want
        # don't care if removing fails
        await k.ut1areq(
            SimpleExec(["git", "remote", "remove", "scone"], self.dest_dir),
            SimpleExec.Result,
        )
        await exec_no_fails(k,
                            ["git", "remote", "add", "scone", self.repo_src],
                            self.dest_dir)

        # fetch the latest from the remote
        await exec_no_fails(k, ["git", "fetch", "scone"], self.dest_dir)

        # figure out what ref we want to use
        # TODO(performance): fetch only this ref?
        ref = self.ref or f"scone/{self.branch}"

        # switch to that ref
        await exec_no_fails(k, ["git", "switch", "--detach", ref],
                            self.dest_dir)

        # if we use submodules
        if self.submodules:
            await exec_no_fails(
                k,
                ["git", "submodule", "update", "--init", "--recursive"],
                self.dest_dir,
            )

        for expected in self.expect:
            expected_path_str = str(Path(self.dest_dir, expected))
            # TODO(performance, low): parallelise these
            stat = await k.ut1a(Stat(expected_path_str), Stat.Result)
            if not stat:
                raise RuntimeError(
                    f"expected {expected_path_str} to exist but it did not")

            if stat.dir and not expected.endswith("/"):
                raise RuntimeError(
                    f"expected {expected_path_str} to exist as a file but it is a dir"
                )

            if not stat.dir and expected.endswith("/"):
                raise RuntimeError(
                    f"expected {expected_path_str} to exist as a dir but it is a file"
                )