def pack_charm_in_instance(self, *, bases_index: int, build_on: Base, build_on_index: int) -> str: """Pack instance in Charm.""" charm_name = format_charm_file_name(self.metadata.name, self.config.bases[bases_index]) # If building in project directory, use the project path as the working # directory. The output charms will be placed in the correct directory # without needing retrieval. If outputing to a directory other than the # charm project directory, we need to output the charm outside the # project directory and can retrieve it when complete. cwd = pathlib.Path.cwd() if cwd == self.charmdir: instance_output_dir = get_managed_environment_project_path() pull_charm = False else: instance_output_dir = get_managed_environment_home_path() pull_charm = True cmd = ["charmcraft", "pack", "--bases-index", str(bases_index)] if message_handler.mode == message_handler.VERBOSE: cmd.append("--verbose") elif message_handler.mode == message_handler.QUIET: cmd.append("--quiet") logger.info(f"Packing charm {charm_name!r}...") with launched_environment( charm_name=self.metadata.name, project_path=self.charmdir, base=build_on, bases_index=bases_index, build_on_index=build_on_index, ) as instance: try: instance.execute_run( cmd, check=True, cwd=instance_output_dir.as_posix(), ) except subprocess.CalledProcessError as error: capture_logs_from_instance(instance) raise CommandError( f"Failed to build charm for bases index '{bases_index}'." ) from error if pull_charm: try: instance.pull_file( source=instance_output_dir / charm_name, destination=cwd / charm_name, ) except FileNotFoundError as error: raise CommandError( "Unexpected error retrieving charm from instance." ) from error return charm_name
def test_capture_logs_from_instance_not_found( emitter, mock_instance, mock_namedtemporaryfile, tmp_path ): fake_log = pathlib.Path(mock_namedtemporaryfile.return_value.name) mock_instance.pull_file.side_effect = FileNotFoundError() providers.capture_logs_from_instance(mock_instance) assert mock_instance.mock_calls == [ mock.call.pull_file(source=pathlib.Path("/tmp/charmcraft.log"), destination=fake_log), ] emitter.assert_trace("No logs found in instance.")
def test_capture_logs_from_instance_not_found(mock_executor, mock_logger, mock_mkstemp, tmp_path): fake_log = tmp_path / "x.log" mock_mkstemp.return_value = (None, str(fake_log)) mock_executor.pull_file.side_effect = FileNotFoundError() providers.capture_logs_from_instance(mock_executor) assert mock_executor.mock_calls == [ mock.call.pull_file(source=pathlib.Path("/tmp/charmcraft.log"), destination=fake_log), ] assert mock_logger.mock_calls == [ mock.call.debug("No logs found in instance.") ]
def test_capture_logs_from_instance(mock_executor, mock_logger, mock_mkstemp, tmp_path): fake_log = tmp_path / "x.log" mock_mkstemp.return_value = (None, str(fake_log)) fake_log_data = "some\nlog data\nhere" fake_log.write_text(fake_log_data) providers.capture_logs_from_instance(mock_executor) assert mock_executor.mock_calls == [ mock.call.pull_file(source=pathlib.Path("/tmp/charmcraft.log"), destination=fake_log), ] assert mock_logger.mock_calls == [ mock.call.debug("Logs captured from managed instance:\n%s", fake_log_data) ]
def test_capture_logs_from_instance(emitter, mock_instance, mock_namedtemporaryfile, tmp_path): fake_log = pathlib.Path(mock_namedtemporaryfile.return_value.name) fake_log_data = "some\nlog data\nhere" fake_log.write_text(fake_log_data) providers.capture_logs_from_instance(mock_instance) assert mock_instance.mock_calls == [ mock.call.pull_file(source=pathlib.Path("/tmp/charmcraft.log"), destination=fake_log), ] emitter.assert_interactions( [ mock.call("trace", "Logs captured from managed instance:"), mock.call("trace", ":: some"), mock.call("trace", ":: log data"), mock.call("trace", ":: here"), ] ) assert mock_namedtemporaryfile.mock_calls == [ mock.call(delete=False, prefix="charmcraft-"), mock.call().close(), ]
def pack_charm_in_instance(self, *, bases_index: int, build_on: Base, build_on_index: int) -> str: """Pack instance in Charm.""" charm_name = format_charm_file_name(self.metadata.name, self.config.bases[bases_index]) # If building in project directory, use the project path as the working # directory. The output charms will be placed in the correct directory # without needing retrieval. If outputing to a directory other than the # charm project directory, we need to output the charm outside the # project directory and can retrieve it when complete. cwd = pathlib.Path.cwd() if cwd == self.charmdir: instance_output_dir = env.get_managed_environment_project_path() pull_charm = False else: instance_output_dir = env.get_managed_environment_home_path() pull_charm = True cmd = ["charmcraft", "pack", "--bases-index", str(bases_index)] if emit.get_mode() == EmitterMode.VERBOSE: cmd.append("--verbose") elif emit.get_mode() == EmitterMode.QUIET: cmd.append("--quiet") elif emit.get_mode() == EmitterMode.TRACE: cmd.append("--trace") if self.debug: cmd.append("--debug") if self.shell: cmd.append("--shell") if self.shell_after: cmd.append("--shell-after") emit.progress(f"Launching environment to pack for base {build_on}") with self.provider.launched_environment( charm_name=self.metadata.name, project_path=self.charmdir, base=build_on, bases_index=bases_index, build_on_index=build_on_index, ) as instance: emit.progress("Packing the charm") try: with emit.open_stream(f"Running {cmd}") as stream: instance.execute_run( cmd, check=True, cwd=instance_output_dir, stdout=stream, stderr=stream, ) except subprocess.CalledProcessError as error: capture_logs_from_instance(instance) raise CommandError( f"Failed to build charm for bases index '{bases_index}'." ) from error if pull_charm: try: instance.pull_file( source=instance_output_dir / charm_name, destination=cwd / charm_name, ) except FileNotFoundError as error: raise CommandError( "Unexpected error retrieving charm from instance." ) from error emit.progress("Charm packed ok") return charm_name