Пример #1
0
def test_make_executable_read_bits(tmp_path):
    pth = tmp_path / "test"
    pth.touch(mode=0o640)
    # sanity check
    assert pth.stat().st_mode & 0o777 == 0o640
    with pth.open() as fd:
        make_executable(fd)
        # only read bits got made executable
        assert pth.stat().st_mode & 0o777 == 0o750
Пример #2
0
    def handle_dispatcher(self, linked_entrypoint):
        """Handle modern and classic dispatch mechanisms."""
        # dispatch mechanism, create one if wasn't provided by the project
        dispatch_path = self.buildpath / DISPATCH_FILENAME
        if not dispatch_path.exists():
            logger.debug("Creating the dispatch mechanism")
            dispatch_content = DISPATCH_CONTENT.format(
                entrypoint_relative_path=linked_entrypoint.relative_to(
                    self.buildpath))
            with dispatch_path.open("wt", encoding="utf8") as fh:
                fh.write(dispatch_content)
                make_executable(fh)

        # bunch of symlinks, to support old juju: verify that any of the already included hooks
        # in the directory is not linking directly to the entrypoint, and also check all the
        # mandatory ones are present
        dest_hookpath = self.buildpath / HOOKS_DIR
        if not dest_hookpath.exists():
            dest_hookpath.mkdir()

        # get those built hooks that we need to replace because they are pointing to the
        # entrypoint directly and we need to fix the environment in the middle
        current_hooks_to_replace = []
        for node in dest_hookpath.iterdir():
            if node.resolve() == linked_entrypoint:
                current_hooks_to_replace.append(node)
                node.unlink()
                logger.debug(
                    "Replacing existing hook %r as it's a symlink to the entrypoint",
                    node.name,
                )

        # include the mandatory ones and those we need to replace
        hooknames = MANDATORY_HOOK_NAMES | {
            x.name
            for x in current_hooks_to_replace
        }
        for hookname in hooknames:
            logger.debug("Creating the %r hook script pointing to dispatch",
                         hookname)
            dest_hook = dest_hookpath / hookname
            if not dest_hook.exists():
                relative_link = relativise(dest_hook, dispatch_path)
                dest_hook.symlink_to(relative_link)
Пример #3
0
    def run(self, args):
        """Execute command's actual functionality."""
        if any(self.config.project.dirpath.iterdir()) and not args.force:
            raise CommandError(
                "{} is not empty (consider using --force to work on nonempty directories)"
                .format(self.config.project.dirpath))
        logger.debug("Using project directory '%s'",
                     self.config.project.dirpath)

        if args.author is None:
            gecos = pwd.getpwuid(os.getuid()).pw_gecos.split(",", 1)[0]
            if not gecos:
                raise CommandError(
                    "Author not given, and nothing in GECOS field")
            logger.debug("Setting author to %r from GECOS field", gecos)
            args.author = gecos

        if not args.name:
            args.name = self.config.project.dirpath.name
            logger.debug("Set project name to '%s'", args.name)

        if not re.match(r"[a-z][a-z0-9-]*[a-z0-9]$", args.name):
            raise CommandError("{} is not a valid charm name".format(
                args.name))

        context = {
            "name": args.name,
            "author": args.author,
            "year": date.today().year,
            "class_name":
            "".join(re.split(r"\W+", args.name.title())) + "Charm",
        }

        env = get_templates_environment("init")

        _todo_rx = re.compile("TODO: (.*)")
        todos = []
        executables = ["run_tests", "src/charm.py"]
        for template_name in env.list_templates():
            if not template_name.endswith(".j2"):
                continue
            template = env.get_template(template_name)
            template_name = template_name[:-3]
            logger.debug("Rendering %s", template_name)
            path = self.config.project.dirpath / template_name
            if path.exists():
                continue
            path.parent.mkdir(parents=True, exist_ok=True)
            with path.open("wt", encoding="utf8") as fh:
                out = template.render(context)
                fh.write(out)
                for todo in _todo_rx.findall(out):
                    todos.append((template_name, todo))
                if template_name in executables:
                    make_executable(fh)
                    logger.debug("  made executable")
        logger.info(
            "Charm operator package file and directory tree initialized.")
        if todos:
            logger.info("TODO:")
            logger.info("")
            w = max(len(i[0]) for i in todos)
            for fn, todo in todos:
                logger.info("%*s: %s", w + 2, fn, todo)
Пример #4
0
    def run(self, args):
        """Execute command's actual functionality."""
        init_dirpath = self.config.project.dirpath
        if not init_dirpath.exists():
            init_dirpath.mkdir(parents=True)
        elif any(init_dirpath.iterdir()) and not args.force:
            tpl = "{!r} is not empty (consider using --force to work on nonempty directories)"
            raise CommandError(tpl.format(str(init_dirpath)))
        emit.trace(f"Using project directory {str(init_dirpath)!r}")

        if args.author is None and pwd is not None:
            args.author = _get_users_full_name_gecos()

        if not args.author:
            raise CommandError(
                "Unable to automatically determine author's name, specify it with --author"
            )

        if not args.name:
            args.name = init_dirpath.name
            emit.trace(f"Set project name to '{args.name}'")

        if not re.match(r"[a-z][a-z0-9-]*[a-z0-9]$", args.name):
            raise CommandError("{} is not a valid charm name".format(
                args.name))

        context = {
            "name": args.name,
            "author": args.author,
            "year": date.today().year,
            "class_name":
            "".join(re.split(r"\W+", args.name.title())) + "Charm",
        }

        env = get_templates_environment("init")

        _todo_rx = re.compile("TODO: (.*)")
        todos = []
        executables = ["run_tests", "src/charm.py"]
        for template_name in env.list_templates():
            if not template_name.endswith(".j2"):
                continue
            template = env.get_template(template_name)
            template_name = template_name[:-3]
            emit.trace(f"Rendering {template_name}")
            path = init_dirpath / template_name
            if path.exists():
                continue
            path.parent.mkdir(parents=True, exist_ok=True)
            with path.open("wt", encoding="utf8") as fh:
                out = template.render(context)
                fh.write(out)
                for todo in _todo_rx.findall(out):
                    todos.append((template_name, todo))
                if template_name in executables and os.name == "posix":
                    make_executable(fh)
                    emit.trace("  made executable")
        emit.message(
            "Charm operator package file and directory tree initialized.")
        if todos:
            emit.message("TODO:")
            emit.message("")
            width = max(len(i[0]) for i in todos) + 2
            for fn, todo in todos:
                emit.message(f"{fn:>{width}s}: {todo}")