diff --git a/src/python/pants/backend/python/util_rules/pex_cli.py b/src/python/pants/backend/python/util_rules/pex_cli.py index a1376f24153..b62c2d83183 100644 --- a/src/python/pants/backend/python/util_rules/pex_cli.py +++ b/src/python/pants/backend/python/util_rules/pex_cli.py @@ -2,8 +2,10 @@ # Licensed under the Apache License, Version 2.0 (see LICENSE). import dataclasses +import os from dataclasses import dataclass -from typing import Iterable, Mapping, Optional, Tuple +from pathlib import Path +from typing import Iterable, List, Mapping, Optional, Tuple from pants.backend.python.subsystems.python_native_code import PythonNativeCode from pants.backend.python.util_rules import pex_environment @@ -14,11 +16,12 @@ ExternalTool, ExternalToolRequest, ) -from pants.engine.fs import CreateDigest, Digest, Directory, MergeDigests +from pants.engine.fs import CreateDigest, Digest, Directory, FileContent, MergeDigests from pants.engine.internals.selectors import MultiGet from pants.engine.platform import Platform from pants.engine.process import Process from pants.engine.rules import Get, collect_rules, rule +from pants.option.global_options import GlobalOptions from pants.util.frozendict import FrozenDict from pants.util.logging import LogLevel from pants.util.meta import classproperty, frozen_after_init @@ -97,21 +100,43 @@ async def setup_pex_cli_process( pex_binary: PexBinary, pex_env: PexEnvironment, python_native_code: PythonNativeCode, + global_options: GlobalOptions, ) -> Process: tmpdir = ".tmp" - downloaded_pex_bin, tmp_dir_digest = await MultiGet( + gets: List[Get] = [ Get(DownloadedExternalTool, ExternalToolRequest, pex_binary.get_request(Platform.current)), Get(Digest, CreateDigest([Directory(f"{tmpdir}/.reserve")])), - ) + ] + cert_args = [] + + # The certs file will typically not be in the repo, so we can't digest it via a PathGlobs. + # Instead we manually create a FileContent for it. + if global_options.options.ca_certs_path: + ca_certs_content = Path(global_options.options.ca_certs_path).read_bytes() + chrooted_ca_certs_path = os.path.basename(global_options.options.ca_certs_path) + + gets.append( + Get( + Digest, + CreateDigest((FileContent(chrooted_ca_certs_path, ca_certs_content),)), + ) + ) + cert_args = ["--cert", chrooted_ca_certs_path] - digests_to_merge = [downloaded_pex_bin.digest, tmp_dir_digest] + downloaded_pex_bin, *digests_to_merge = await MultiGet(gets) + digests_to_merge.append(downloaded_pex_bin.digest) if request.additional_input_digest: digests_to_merge.append(request.additional_input_digest) input_digest = await Get(Digest, MergeDigests(digests_to_merge)) pex_root_path = ".cache/pex_root" argv = pex_env.create_argv( - downloaded_pex_bin.exe, *request.argv, "--pex-root", pex_root_path, python=request.python + downloaded_pex_bin.exe, + *request.argv, + *cert_args, + "--pex-root", + pex_root_path, + python=request.python, ) env = { # Ensure Pex and its subprocesses create temporary files in the the process execution diff --git a/src/python/pants/backend/python/util_rules/pex_cli_test.py b/src/python/pants/backend/python/util_rules/pex_cli_test.py new file mode 100644 index 00000000000..5955b980781 --- /dev/null +++ b/src/python/pants/backend/python/util_rules/pex_cli_test.py @@ -0,0 +1,45 @@ +# Copyright 2020 Pants project contributors (see CONTRIBUTORS.md). +# Licensed under the Apache License, Version 2.0 (see LICENSE). + +from pathlib import Path + +import pytest + +from pants.backend.python.util_rules import pex_cli +from pants.backend.python.util_rules.pex_cli import PexCliProcess +from pants.core.util_rules.pants_environment import PantsEnvironment +from pants.engine.fs import DigestContents +from pants.engine.process import Process +from pants.engine.rules import QueryRule +from pants.testutil.option_util import create_options_bootstrapper +from pants.testutil.rule_runner import RuleRunner +from pants.util.contextutil import temporary_dir + + +@pytest.fixture +def rule_runner() -> RuleRunner: + return RuleRunner( + rules=[ + *pex_cli.rules(), + QueryRule(Process, (PexCliProcess, PantsEnvironment)), + ] + ) + + +def test_custom_ca_certs(rule_runner: RuleRunner) -> None: + with temporary_dir() as tmpdir: + certs_file = Path(tmpdir) / "certsfile" + certs_file.write_text("Some fake cert") + proc = rule_runner.request( + Process, + [ + PexCliProcess(argv=["some", "--args"], description=""), + PantsEnvironment(), + create_options_bootstrapper(args=[f"--ca-certs-path={certs_file}"]), + ], + ) + assert proc.argv[2:6] == ("some", "--args", "--cert", "certsfile") + files = rule_runner.request(DigestContents, [proc.input_digest]) + chrooted_certs_file = [f for f in files if f.path == "certsfile"] + assert len(chrooted_certs_file) == 1 + assert b"Some fake cert" == chrooted_certs_file[0].content