Пример #1
0
    def run(self, parsed_args):
        """Run the command."""
        # this command is deprecated now (note that the whole infrastructure behind
        # is ok to use, but through PackCommand)
        notify_deprecation("dn06")

        validator = Validator(self.config)
        args = validator.process(parsed_args)
        emit.trace(f"Working arguments: {args}")
        builder = Builder(args, self.config)
        builder.run(destructive_mode=args["destructive_mode"])
Пример #2
0
def test_notice_skipped_in_managed_mode(monkeypatch, emitter):
    """Present proper messages to the user."""
    monkeypatch.setitem(_DEPRECATION_MESSAGES, "dn666",
                        "Test message for the user.")
    monkeypatch.setattr(deprecations, "_DEPRECATION_URL_FMT",
                        "http://docs.com/#{deprecation_id}")

    with patch(
            "charmcraft.deprecations.is_charmcraft_running_in_managed_mode",
            return_value=True,
    ):
        notify_deprecation("dn666")

    emitter.assert_interactions(None)
Пример #3
0
def test_notice_ok(monkeypatch, caplog):
    """Present proper messages to the user."""
    caplog.set_level(logging.WARNING, logger="charmcraft")

    monkeypatch.setitem(_DEPRECATION_MESSAGES, "dn666",
                        "Test message for the user.")
    monkeypatch.setattr(deprecations, "_DEPRECATION_URL_FMT",
                        "http://docs.com/#{deprecation_id}")

    notify_deprecation("dn666")
    expected = [
        "DEPRECATED: Test message for the user.",
        "See http://docs.com/#dn666 for more information.",
    ]
    assert expected == [rec.message for rec in caplog.records]
Пример #4
0
def test_notice_ok(monkeypatch, emitter):
    """Present proper messages to the user."""
    monkeypatch.setitem(_DEPRECATION_MESSAGES, "dn666",
                        "Test message for the user.")
    monkeypatch.setattr(deprecations, "_DEPRECATION_URL_FMT",
                        "http://docs.com/#{deprecation_id}")

    notify_deprecation("dn666")
    emitter.assert_interactions([
        call("message",
             "DEPRECATED: Test message for the user.",
             intermediate=True),
        call("message",
             "See http://docs.com/#dn666 for more information.",
             intermediate=True),
    ])
Пример #5
0
def test_notice_skipped_in_managed_mode(monkeypatch, caplog):
    """Present proper messages to the user."""
    caplog.set_level(logging.WARNING, logger="charmcraft")

    monkeypatch.setitem(_DEPRECATION_MESSAGES, "dn666",
                        "Test message for the user.")
    monkeypatch.setattr(deprecations, "_DEPRECATION_URL_FMT",
                        "http://docs.com/#{deprecation_id}")

    with patch(
            "charmcraft.deprecations.is_charmcraft_running_in_managed_mode",
            return_value=True,
    ):
        notify_deprecation("dn666")

    assert [rec.message for rec in caplog.records] == []
Пример #6
0
def test_log_deprecation_only_once(monkeypatch, caplog):
    """Show the message only once even if it was called several times."""
    caplog.set_level(logging.WARNING, logger="charmcraft")

    monkeypatch.setitem(_DEPRECATION_MESSAGES, "dn666",
                        "Test message for the user.")
    monkeypatch.setattr(deprecations, "_DEPRECATION_URL_FMT",
                        "http://docs.com/#{deprecation_id}")

    # call twice, log once
    notify_deprecation("dn666")
    notify_deprecation("dn666")
    expected = [
        "DEPRECATED: Test message for the user.",
        "See http://docs.com/#dn666 for more information.",
    ]
    assert expected == [rec.message for rec in caplog.records]
Пример #7
0
def test_log_deprecation_only_once(monkeypatch, emitter):
    """Show the message only once even if it was called several times."""
    monkeypatch.setitem(_DEPRECATION_MESSAGES, "dn666",
                        "Test message for the user.")
    monkeypatch.setattr(deprecations, "_DEPRECATION_URL_FMT",
                        "http://docs.com/#{deprecation_id}")

    # call twice, log once
    notify_deprecation("dn666")
    notify_deprecation("dn666")
    emitter.assert_interactions([
        call("message",
             "DEPRECATED: Test message for the user.",
             intermediate=True),
        call("message",
             "See http://docs.com/#dn666 for more information.",
             intermediate=True),
    ])
Пример #8
0
    def unmarshal(cls, obj: Dict[str, Any], project: Project):
        """Unmarshal object with necessary translations and error handling.

        (1) Perform any necessary translations.

        (2) Standardize error reporting.

        :returns: valid CharmcraftConfig.

        :raises CommandError: On failure to unmarshal object.
        """
        try:
            # Ensure optional type is specified if loading the yaml.
            # This can be removed once charmcraft.yaml is mandatory.
            if "type" not in obj:
                obj["type"] = None

            # Ensure short-form bases are expanded into long-form
            # base configurations.  Doing it here rather than a Union
            # type will simplify user facing errors.
            bases = obj.get("bases")
            if bases is None:
                if obj["type"] in (None, "charm"):
                    notify_deprecation("dn03")
                # Set default bases to Ubuntu 20.04 to match strict snap's
                # effective behavior.
                bases = [
                    {
                        "name": "ubuntu",
                        "channel": "20.04",
                        "architectures": [get_host_architecture()],
                    }
                ]

            # Expand short-form bases if only the bases is a valid list. If it
            # is not a valid list, parse_obj() will properly handle the error.
            if isinstance(bases, list):
                cls.expand_short_form_bases(bases)

            return cls.parse_obj({"project": project, **obj})
        except pydantic.error_wrappers.ValidationError as error:
            raise CommandError(format_pydantic_errors(error.errors()))
Пример #9
0
    def unmarshal(cls, obj: Dict[str, Any], project: Project):
        """Unmarshal object with necessary translations and error handling.

        (1) Perform any necessary translations.

        (2) Standardize error reporting.

        :returns: valid CharmcraftConfig.

        :raises CraftError: On failure to unmarshal object.
        """
        try:
            # Ensure short-form bases are expanded into long-form
            # base configurations.  Doing it here rather than a Union
            # type will simplify user facing errors.
            bases = obj.get("bases")
            if bases is None:
                # "type" is accessed with get because this code happens before
                # pydantic actually validating that type is present
                if obj.get("type") == "charm":
                    notify_deprecation("dn03")
                # Set default bases to Ubuntu 20.04 to match strict snap's
                # effective behavior.
                bases = [{
                    "name": "ubuntu",
                    "channel": "20.04",
                    "architectures": [get_host_architecture()],
                }]

            # Expand short-form bases if only the bases is a valid list. If it
            # is not a valid list, parse_obj() will properly handle the error.
            if isinstance(bases, list):
                cls.expand_short_form_bases(bases)

            return cls.parse_obj({"project": project, **obj})
        except pydantic.error_wrappers.ValidationError as error:
            raise CraftError(format_pydantic_errors(error.errors()))
Пример #10
0
def load(dirpath: Optional[str]) -> Config:
    """Load the config from charmcraft.yaml in the indicated directory."""
    if dirpath is None:
        if is_charmcraft_running_in_managed_mode():
            dirpath = get_managed_environment_project_path()
        else:
            dirpath = pathlib.Path.cwd()
    else:
        dirpath = pathlib.Path(dirpath).expanduser().resolve()

    now = datetime.datetime.utcnow()

    content = load_yaml(dirpath / "charmcraft.yaml")
    if content is None:
        # configuration is mandatory only for some commands; when not provided, it will
        # be initialized all with defaults (but marked as not provided for later verification)
        return Config(
            project=Project(
                dirpath=dirpath,
                config_provided=False,
                started_at=now,
            ),
        )

    if any("_" in x for x in content.get("charmhub", {}).keys()):
        # underscores in config attribs deprecated on 2021-05-31
        notify_deprecation("dn01")

    return Config.unmarshal(
        content,
        project=Project(
            dirpath=dirpath,
            config_provided=True,
            started_at=now,
        ),
    )
Пример #11
0
    def run(self,
            bases_indices: Optional[List[int]] = None,
            destructive_mode: bool = False) -> List[str]:
        """Run build process.

        In managed-mode or destructive-mode, build for each bases configuration
        which has a matching build-on to the host we are executing on.  Warn for
        each base configuration that is incompatible.  Error if unable to
        produce any builds for any bases configuration.

        :returns: List of charm files created.
        """
        charms: List[str] = []

        managed_mode = env.is_charmcraft_running_in_managed_mode()
        if not managed_mode and not destructive_mode:
            self.provider.ensure_provider_is_available()

        if self.entrypoint:
            notify_deprecation("dn04")

        if self.requirement_paths:
            notify_deprecation("dn05")

        if not (self.charmdir / "charmcraft.yaml").exists():
            notify_deprecation("dn02")

        build_plan = self.plan(
            bases_indices=bases_indices,
            destructive_mode=destructive_mode,
            managed_mode=managed_mode,
        )
        if not build_plan:
            raise CommandError(
                "No suitable 'build-on' environment found in any 'bases' configuration."
            )

        charms = []
        for bases_config, build_on, bases_index, build_on_index in build_plan:
            emit.trace(
                f"Building for 'bases[{ bases_index:d}][{build_on_index:d}]'.")
            if managed_mode or destructive_mode:
                if self.shell:
                    # Execute shell in lieu of build.
                    launch_shell()
                    continue

                try:
                    charm_name = self.build_charm(bases_config)
                except (CommandError, RuntimeError) as error:
                    if self.debug:
                        emit.trace(
                            f"Launching shell as charm building ended in error: {error}"
                        )
                        launch_shell()
                    raise

                if self.shell_after:
                    launch_shell()
            else:
                charm_name = self.pack_charm_in_instance(
                    bases_index=bases_index,
                    build_on=build_on,
                    build_on_index=build_on_index,
                )
            charms.append(charm_name)

        return charms
Пример #12
0
    def run(self,
            bases_indices: Optional[List[int]] = None,
            destructive_mode: bool = False) -> List[str]:
        """Run build process.

        In managed-mode or destructive-mode, build for each bases configuration
        which has a matching build-on to the host we are executing on.  Warn for
        each base configuration that is incompatible.  Error if unable to
        produce any builds for any bases configuration.

        :returns: List of charm files created.
        """
        charms: List[str] = []

        managed_mode = is_charmcraft_running_in_managed_mode()
        if not managed_mode and not destructive_mode:
            ensure_provider_is_available()

        if self.entrypoint:
            notify_deprecation("dn04")

        if self.requirement_paths:
            notify_deprecation("dn05")

        if not (self.charmdir / "charmcraft.yaml").exists():
            notify_deprecation("dn02")

        for bases_index, bases_config in enumerate(self.config.bases):
            if bases_indices and bases_index not in bases_indices:
                logger.debug(
                    "Skipping 'bases[%d]' due to --base-index usage.",
                    bases_index,
                )
                continue

            for build_on_index, build_on in enumerate(bases_config.build_on):
                if managed_mode or destructive_mode:
                    matches, reason = check_if_base_matches_host(build_on)
                else:
                    matches, reason = is_base_providable(build_on)

                if matches:
                    logger.debug(
                        "Building for 'bases[%d]' as host matches 'build-on[%d]'.",
                        bases_index,
                        build_on_index,
                    )
                    if managed_mode or destructive_mode:
                        charm_name = self.build_charm(bases_config)
                    else:
                        charm_name = self.pack_charm_in_instance(
                            bases_index=bases_index,
                            build_on=build_on,
                            build_on_index=build_on_index,
                        )

                    charms.append(charm_name)
                    break
                else:
                    logger.info(
                        "Skipping 'bases[%d].build-on[%d]': %s.",
                        bases_index,
                        build_on_index,
                        reason,
                    )
            else:
                logger.warning(
                    "No suitable 'build-on' environment found in 'bases[%d]' configuration.",
                    bases_index,
                )

        if not charms:
            raise CommandError(
                "No suitable 'build-on' environment found in any 'bases' configuration."
            )

        return charms