launchpad-reviewers team mailing list archive
-
launchpad-reviewers team
-
Mailing list archive
-
Message #28698
[Merge] ~jugmac00/lpcraft:pass-in-credentials into lpcraft:main
Jürgen Gmach has proposed merging ~jugmac00/lpcraft:pass-in-credentials into lpcraft:main with ~jugmac00/lpcraft:add-additional-apt-repositories as a prerequisite.
Commit message:
Add new CLI option to provide credentials
Requested reviews:
Launchpad code reviewers (launchpad-reviewers)
For more details, see:
https://code.launchpad.net/~jugmac00/lpcraft/+git/lpcraft/+merge/425865
--
Your team Launchpad code reviewers is requested to review the proposed merge of ~jugmac00/lpcraft:pass-in-credentials into lpcraft:main.
diff --git a/NEWS.rst b/NEWS.rst
index 8f0d35c..e41f7b5 100644
--- a/NEWS.rst
+++ b/NEWS.rst
@@ -11,6 +11,8 @@ Version history
- Add new configuration option to provide additional apt repositories.
+- Add new CLI option to provide credentials.
+
0.0.17 (2022-06-17)
===================
diff --git a/docs/cli-interface.rst b/docs/cli-interface.rst
index 45a3914..3d17527 100644
--- a/docs/cli-interface.rst
+++ b/docs/cli-interface.rst
@@ -29,6 +29,9 @@ lpcraft run optional arguments
- ``--plugin-setting``, e.g.
``lpcraft run --plugin-setting="foo=bar"``
+- ``--credentials``, e.g.
+ ``lpcraft run --credentials="user:pass"``
+
lpcraft run-one
---------------
@@ -52,3 +55,6 @@ lpcraft run-one optional arguments
- ``--plugin-setting``, e.g.
``lpcraft run-one --plugin-setting="foo=bar" test 0``.
+
+- ``--credentials``, e.g.
+ ``lpcraft run-one --credentials="user:pass" test 0``.
diff --git a/docs/configuration.rst b/docs/configuration.rst
index c4c5af4..a51c835 100644
--- a/docs/configuration.rst
+++ b/docs/configuration.rst
@@ -52,7 +52,8 @@ Job definitions
``additional-apt-repositories`` (optional)
Repositories which will be added to the already existing ones in
- `/etc/apt/sources.list`.
+ `/etc/apt/sources.list`. May contain a placeholder (`<credentials>`) for
+ authentication credentials.
``snaps`` (optional)
Snaps to install as dependencies of this job.
diff --git a/lpcraft/commands/run.py b/lpcraft/commands/run.py
index 60925af..96c5a9e 100644
--- a/lpcraft/commands/run.py
+++ b/lpcraft/commands/run.py
@@ -229,6 +229,7 @@ def _install_apt_packages(
apt_replacement_repositories: Optional[List[str]],
additional_apt_repositories: Optional[List[str]],
environment: Optional[Dict[str, Optional[str]]],
+ credentials: Optional[str],
) -> None:
if apt_replacement_repositories or additional_apt_repositories:
sources_list_path = "/etc/apt/sources.list"
@@ -244,6 +245,11 @@ def _install_apt_packages(
if apt_replacement_repositories:
sources = "\n".join(apt_replacement_repositories) + "\n"
if additional_apt_repositories:
+ if credentials:
+ additional_apt_repositories = [
+ repository.replace("<credentials>", credentials)
+ for repository in additional_apt_repositories
+ ]
sources += "\n" + "\n".join(additional_apt_repositories) + "\n"
with emit.open_stream("Replacing /etc/apt/sources.list") as stream:
instance.push_file_io(
@@ -334,6 +340,7 @@ def _run_job(
additional_apt_repositories: Optional[List[str]] = None,
env_from_cli: Optional[List[str]] = None,
plugin_settings: Optional[List[str]] = None,
+ credentials: Optional[str] = None,
) -> None:
"""Run a single job."""
# XXX jugmac00 2022-04-27: we should create a configuration object to be
@@ -424,6 +431,7 @@ def _run_job(
apt_replacement_repositories=apt_replacement_repositories,
additional_apt_repositories=additional_apt_repositories,
environment=environment,
+ credentials=credentials,
)
for cmd in (pre_run_command, run_command, post_run_command):
if cmd:
@@ -504,6 +512,10 @@ class RunCommand(BaseCommand):
action="append",
help="Set an environment variable.",
)
+ parser.add_argument(
+ "--credentials",
+ help="Pass in credentials.",
+ )
def run(self, args: Namespace) -> int:
"""Run the command."""
@@ -527,6 +539,7 @@ class RunCommand(BaseCommand):
launched_instances.append(
_get_job_instance_name(provider, job)
)
+ # breakpoint()
_run_job(
job_name,
job,
@@ -538,6 +551,7 @@ class RunCommand(BaseCommand):
additional_apt_repositories=job.additional_apt_repositories, # noqa: E501
env_from_cli=args.set_env,
plugin_settings=args.plugin_setting,
+ credentials=args.credentials,
)
except CommandError as e:
if len(stage) == 1:
@@ -619,6 +633,10 @@ class RunOneCommand(BaseCommand):
action="append",
help="Set an environment variable.",
)
+ parser.add_argument(
+ "--credentials",
+ help="Pass in credentials.",
+ )
def run(self, args: Namespace) -> int:
"""Run the command."""
@@ -646,6 +664,7 @@ class RunOneCommand(BaseCommand):
additional_apt_repositories=job.additional_apt_repositories,
env_from_cli=args.set_env,
plugin_settings=args.plugin_setting,
+ credentials=args.credentials,
)
finally:
if args.clean:
diff --git a/lpcraft/commands/tests/test_run.py b/lpcraft/commands/tests/test_run.py
index 86c6df5..3446d8d 100644
--- a/lpcraft/commands/tests/test_run.py
+++ b/lpcraft/commands/tests/test_run.py
@@ -2078,6 +2078,98 @@ class TestRun(RunBaseTestCase):
file_contents,
)
+ @patch("lpcraft.commands.run.get_provider")
+ @patch("lpcraft.commands.run.get_host_architecture", return_value="amd64")
+ def test_run_with_additional_apt_repositories_with_credentials(
+ self, mock_get_host_architecture, mock_get_provider
+ ):
+ existing_repositories = [
+ "deb http://archive.ubuntu.com/ubuntu/ focal main restricted",
+ "deb-src http://archive.ubuntu.com/ubuntu/ focal main restricted",
+ ]
+
+ def fake_pull_file(source: Path, destination: Path) -> None:
+ destination.write_text("\n".join(existing_repositories))
+
+ launcher = Mock(spec=launch)
+ provider = makeLXDProvider(lxd_launcher=launcher)
+ mock_get_provider.return_value = provider
+ execute_run = launcher.return_value.execute_run
+ execute_run.return_value = subprocess.CompletedProcess([], 0)
+ launcher.return_value.pull_file.side_effect = fake_pull_file
+ additional_repositories = [
+ "deb https://<credentials>@canonical.example.org/artifactory/jammy-golang-backport focal main", # noqa: E501
+ "deb https://<credentials>@canonical.example.org/artifactory/jammy-golang-backport focal universe", # noqa: E501
+ ]
+ config = dedent(
+ f"""
+ pipeline:
+ - test
+ jobs:
+ test:
+ series: focal
+ architectures: amd64
+ run: ls -la
+ packages: [git]
+ additional-apt-repositories: {additional_repositories}
+ """
+ )
+ Path(".launchpad.yaml").write_text(config)
+
+ result = self.run_command(
+ "run",
+ "--credentials",
+ "user:pass",
+ )
+
+ self.assertEqual(0, result.exit_code)
+ self.assertEqual(
+ [
+ call(
+ ["apt", "update"],
+ cwd=Path("/root/lpcraft/project"),
+ env={},
+ stdout=ANY,
+ stderr=ANY,
+ ),
+ call(
+ ["apt", "install", "-y", "git"],
+ cwd=Path("/root/lpcraft/project"),
+ env={},
+ stdout=ANY,
+ stderr=ANY,
+ ),
+ call(
+ ["bash", "--noprofile", "--norc", "-ec", "ls -la"],
+ cwd=Path("/root/lpcraft/project"),
+ env={},
+ stdout=ANY,
+ stderr=ANY,
+ ),
+ ],
+ execute_run.call_args_list,
+ )
+ mock_info = launcher.return_value.push_file_io.call_args_list
+
+ self.assertEqual(
+ Path("/etc/apt/sources.list"), mock_info[0][1]["destination"]
+ )
+
+ file_contents = mock_info[0][1]["content"].read().decode()
+
+ updated_additional_repositories = [
+ "deb https://user:pass@xxxxxxxxxxxxxxxxxxxxx/artifactory/jammy-golang-backport focal main", # noqa: E501
+ "deb https://user:pass@xxxxxxxxxxxxxxxxxxxxx/artifactory/jammy-golang-backport focal universe", # noqa: E501
+ ]
+
+ self.assertEqual(
+ "\n".join(existing_repositories)
+ + "\n"
+ + "\n".join(updated_additional_repositories)
+ + "\n",
+ file_contents,
+ )
+
class TestRunOne(RunBaseTestCase):
def test_config_file_not_under_project_directory(self):
@@ -2848,3 +2940,130 @@ class TestRunOne(RunBaseTestCase):
exit_code=1, errors=[CommandError("File not found", retcode=1)]
),
)
+
+ @patch("lpcraft.commands.run.get_provider")
+ @patch("lpcraft.commands.run.get_host_architecture", return_value="amd64")
+ def test_provide_credentials_via_cli(
+ self, mock_get_host_architecture, mock_get_provider
+ ):
+ launcher = Mock(spec=launch)
+ provider = makeLXDProvider(lxd_launcher=launcher)
+ mock_get_provider.return_value = provider
+ execute_run = launcher.return_value.execute_run
+ execute_run.return_value = subprocess.CompletedProcess([], 0)
+ config = dedent(
+ """
+ pipeline:
+ - test
+ jobs:
+ test:
+ series: focal
+ architectures: amd64
+ run: tox
+ """
+ )
+ Path(".launchpad.yaml").write_text(config)
+ result = self.run_command(
+ "run-one",
+ "--credentials",
+ "user:pass",
+ "test",
+ "0",
+ )
+ self.assertEqual(0, result.exit_code)
+
+ self.assertIn("'--credentials', 'user:pass'", result.trace[0])
+
+ @patch("lpcraft.commands.run.get_provider")
+ @patch("lpcraft.commands.run.get_host_architecture", return_value="amd64")
+ def test_run_with_additional_apt_repositories_with_credentials(
+ self, mock_get_host_architecture, mock_get_provider
+ ):
+ existing_repositories = [
+ "deb http://archive.ubuntu.com/ubuntu/ focal main restricted",
+ "deb-src http://archive.ubuntu.com/ubuntu/ focal main restricted",
+ ]
+
+ def fake_pull_file(source: Path, destination: Path) -> None:
+ destination.write_text("\n".join(existing_repositories))
+
+ launcher = Mock(spec=launch)
+ provider = makeLXDProvider(lxd_launcher=launcher)
+ mock_get_provider.return_value = provider
+ execute_run = launcher.return_value.execute_run
+ execute_run.return_value = subprocess.CompletedProcess([], 0)
+ launcher.return_value.pull_file.side_effect = fake_pull_file
+ additional_repositories = [
+ "deb https://<credentials>@canonical.example.org/artifactory/jammy-golang-backport focal main", # noqa: E501
+ "deb https://<credentials>@canonical.example.org/artifactory/jammy-golang-backport focal universe", # noqa: E501
+ ]
+ config = dedent(
+ f"""
+ pipeline:
+ - test
+ jobs:
+ test:
+ series: focal
+ architectures: amd64
+ run: ls -la
+ packages: [git]
+ additional-apt-repositories: {additional_repositories}
+ """
+ )
+ Path(".launchpad.yaml").write_text(config)
+
+ result = self.run_command(
+ "run-one",
+ "--credentials",
+ "user:pass",
+ "test",
+ "0",
+ )
+
+ self.assertEqual(0, result.exit_code)
+ self.assertEqual(
+ [
+ call(
+ ["apt", "update"],
+ cwd=Path("/root/lpcraft/project"),
+ env={},
+ stdout=ANY,
+ stderr=ANY,
+ ),
+ call(
+ ["apt", "install", "-y", "git"],
+ cwd=Path("/root/lpcraft/project"),
+ env={},
+ stdout=ANY,
+ stderr=ANY,
+ ),
+ call(
+ ["bash", "--noprofile", "--norc", "-ec", "ls -la"],
+ cwd=Path("/root/lpcraft/project"),
+ env={},
+ stdout=ANY,
+ stderr=ANY,
+ ),
+ ],
+ execute_run.call_args_list,
+ )
+ mock_info = launcher.return_value.push_file_io.call_args_list
+
+ self.assertEqual(
+ Path("/etc/apt/sources.list"), mock_info[0][1]["destination"]
+ )
+
+ file_contents = mock_info[0][1]["content"].read().decode()
+
+ updated_additional_repositories = [
+ "deb https://user:pass@xxxxxxxxxxxxxxxxxxxxx/artifactory/jammy-golang-backport focal main", # noqa: E501
+ "deb https://user:pass@xxxxxxxxxxxxxxxxxxxxx/artifactory/jammy-golang-backport focal universe", # noqa: E501
+ ]
+
+ self.assertEqual(
+ "\n".join(existing_repositories)
+ + "\n"
+ + "\n".join(updated_additional_repositories)
+ + "\n",
+ file_contents,
+ )
diff --git a/tox.ini b/tox.ini
index 71f524e..1d9be16 100644
--- a/tox.ini
+++ b/tox.ini
@@ -6,6 +6,8 @@ envlist =
py39
py310
coverage
+ docs
+
skip_missing_interpreters =
true