def test_docker_binary_push_image(docker_path: str, docker: DockerBinary) -> None: assert docker.push_image(()) is None image_ref = "registry/repo/name:tag" push_request = docker.push_image((image_ref, )) assert push_request == Process( argv=(docker_path, "push", image_ref), cache_scope=ProcessCacheScope.PER_SESSION, description="", # The description field is marked `compare=False` ) assert push_request.description == f"Pushing docker image {image_ref}"
async def push_docker_images( request: PublishDockerImageRequest, docker: DockerBinary, options: DockerOptions ) -> PublishProcesses: tags = tuple( chain.from_iterable( cast(BuiltDockerImage, image).tags for pkg in request.packages for image in pkg.artifacts ) ) if request.field_set.skip_push.value: return PublishProcesses( [ PublishPackages( names=tags, description=f"(by `{request.field_set.skip_push.alias}` on {request.field_set.address})", ), ] ) env = await Get(Environment, EnvironmentRequest(options.env_vars)) processes = zip(tags, docker.push_image(tags, env)) return PublishProcesses( [ PublishPackages( names=(tag,), process=InteractiveProcess.from_process(process), ) for tag, process in processes ] )
async def push_docker_images( request: PublishDockerImageRequest, docker: DockerBinary, options: DockerOptions ) -> PublishProcesses: tags = tuple( chain.from_iterable( cast(BuiltDockerImage, image).tags for pkg in request.packages for image in pkg.artifacts ) ) if request.field_set.skip_push.value: return PublishProcesses( [ PublishPackages( names=tags, description=f"(by `{request.field_set.skip_push.alias}` on {request.field_set.address})", ), ] ) process = docker.push_image(tags) return PublishProcesses( [ PublishPackages( names=tags, process=InteractiveProcess.from_process(process) if process else None, ), ] )
async def docker_image_run_request( field_set: DockerFieldSet, docker: DockerBinary, options: DockerOptions ) -> RunRequest: env, image = await MultiGet( Get(Environment, EnvironmentRequest(options.env_vars)), Get(BuiltPackage, PackageFieldSet, field_set), ) tag = cast(BuiltDockerImage, image.artifacts[0]).tags[0] run = docker.run_image(tag, docker_run_args=options.run_args, env=env) return RunRequest(args=run.argv, digest=image.digest, extra_env=run.env)
def test_docker_binary_run_image(docker_path: str, docker: DockerBinary) -> None: image_ref = "registry/repo/name:tag" port_spec = "127.0.0.1:80:8080/tcp" run_request = docker.run_image(image_ref, docker_run_args=("-p", port_spec), image_args=("test-input", )) assert run_request == Process( argv=(docker_path, "run", "-p", port_spec, image_ref, "test-input"), cache_scope=ProcessCacheScope.PER_SESSION, description="", # The description field is marked `compare=False` ) assert run_request.description == f"Running docker image {image_ref}"
async def push_docker_images(request: PublishDockerImageRequest, docker: DockerBinary, options: DockerOptions) -> PublishProcesses: tags = tuple( chain.from_iterable( cast(BuiltDockerImage, image).tags for pkg in request.packages for image in pkg.artifacts)) if request.field_set.skip_push.value: return PublishProcesses([ PublishPackages( names=tags, description= f"(by `{request.field_set.skip_push.alias}` on {request.field_set.address})", ), ]) env = await Get(Environment, EnvironmentRequest(options.env_vars)) skip_push = defaultdict(set) jobs: list[PublishPackages] = [] refs: list[str] = [] processes: list[Get] = [] for tag in tags: for registry in options.registries().registries.values(): if tag.startswith(registry.address) and registry.skip_push: skip_push[registry.alias].add(tag) break else: refs.append(tag) processes.append( Get(InteractiveProcess, InteractiveProcessRequest(docker.push_image(tag, env)))) interactive_processes = await MultiGet(processes) for ref, process in zip(refs, interactive_processes): jobs.append(PublishPackages( names=(ref, ), process=process, )) if skip_push: for name, skip_tags in skip_push.items(): jobs.append( PublishPackages( names=tuple(skip_tags), description=f"(by `skip_push` on registry @{name})", ), ) return PublishProcesses(jobs)
def test_docker_binary_build_image(docker_path: str, docker: DockerBinary) -> None: dockerfile = "src/test/repo/Dockerfile" digest = Digest(sha256().hexdigest(), 123) tags = ( "test:0.1.0", "test:latest", ) build_request = docker.build_image(tags, digest, dockerfile) assert build_request == Process( argv=(docker_path, "build", "-t", tags[0], "-t", tags[1], "-f", dockerfile, "."), input_digest=digest, cache_scope=ProcessCacheScope.PER_SESSION, description="", # The description field is marked `compare=False` ) assert build_request.description == "Building docker image test:0.1.0 +1 additional tag."
def test_docker_binary_build_image(docker_path: str, docker: DockerBinary) -> None: dockerfile = "src/test/repo/Dockerfile" digest = Digest(sha256().hexdigest(), 123) tags = ( "test:0.1.0", "test:latest", ) env = {"DOCKER_HOST": "tcp://127.0.0.1:1234"} build_request = docker.build_image( tags=tags, digest=digest, dockerfile=dockerfile, build_args=DockerBuildArgs.from_strings("arg1=2"), context_root="build/context", env=env, extra_args=("--pull", "--squash"), ) assert build_request == Process( argv=( docker_path, "build", "--pull", "--squash", "--tag", tags[0], "--tag", tags[1], "--build-arg", "arg1=2", "--file", dockerfile, "build/context", ), env=env, input_digest=digest, cache_scope=ProcessCacheScope.PER_SESSION, description="", # The description field is marked `compare=False` ) assert build_request.description == "Building docker image test:0.1.0 +1 additional tag."
async def build_docker_image( field_set: DockerFieldSet, options: DockerOptions, docker: DockerBinary, ) -> BuiltPackage: context = await Get( DockerBuildContext, DockerBuildContextRequest( address=field_set.address, build_upstream_images=True, ), ) tags = field_set.image_refs( default_repository=options.default_repository, registries=options.registries(), version_context=context.version_context, ) result = await Get( ProcessResult, Process, docker.build_image( build_args=context.build_args, digest=context.digest, dockerfile=context.dockerfile, env=context.env, tags=tags, ), ) logger.debug(f"Docker build output for {tags[0]}:\n" f"{result.stdout.decode()}\n" f"{result.stderr.decode()}") return BuiltPackage( result.output_digest, (BuiltDockerImage.create(tags), ), )
async def build_docker_image( field_set: DockerFieldSet, options: DockerOptions, global_options: GlobalOptions, docker: DockerBinary, process_cleanup: ProcessCleanupOption, ) -> BuiltPackage: context, wrapped_target = await MultiGet( Get( DockerBuildContext, DockerBuildContextRequest( address=field_set.address, build_upstream_images=True, ), ), Get(WrappedTarget, Address, field_set.address), ) tags = field_set.image_refs( default_repository=options.default_repository, registries=options.registries(), interpolation_context=context.interpolation_context, ) context_root = field_set.get_context_root(options.default_context_root) process = docker.build_image( build_args=context.build_args, digest=context.digest, dockerfile=context.dockerfile, context_root=context_root, env=context.build_env.environment, tags=tags, extra_args=tuple( get_build_options( context=context, field_set=field_set, global_target_stage_option=options.build_target_stage, target=wrapped_target.target, )), ) result = await Get(FallibleProcessResult, Process, process) if result.exit_code != 0: maybe_msg = format_docker_build_context_help_message( address=field_set.address, context_root=context_root, context=context, colors=global_options.colors, ) if maybe_msg: logger.warning(maybe_msg) raise ProcessExecutionFailure( result.exit_code, result.stdout, result.stderr, process.description, process_cleanup=process_cleanup.val, ) image_id = parse_image_id_from_docker_build_output(result.stdout, result.stderr) docker_build_output_msg = "\n".join(( f"Docker build output for {tags[0]}:", "stdout:", result.stdout.decode(), "stderr:", result.stderr.decode(), )) if options.build_verbose: logger.info(docker_build_output_msg) else: logger.debug(docker_build_output_msg) return BuiltPackage( result.output_digest, (BuiltDockerImage.create(image_id, tags), ), )
def assert_build( rule_runner: RuleRunner, address: Address, *extra_log_lines: str, options: dict | None = None, process_assertions: Callable[[Process], None] | None = None, ) -> None: tgt = rule_runner.get_target(address) def build_context_mock(request: DockerBuildContextRequest) -> DockerBuildContext: return DockerBuildContext.create( digest=EMPTY_DIGEST, dockerfile_info=DockerfileInfo(source="docker/test/Dockerfile"), build_args=rule_runner.request(DockerBuildArgs, [DockerBuildArgsRequest(tgt)]), env=rule_runner.request(DockerBuildEnvironment, [DockerBuildEnvironmentRequest(tgt)]), ) def run_process_mock(process: Process) -> ProcessResult: if process_assertions: process_assertions(process) return ProcessResult( stdout=b"stdout", stdout_digest=EMPTY_FILE_DIGEST, stderr=b"stderr", stderr_digest=EMPTY_FILE_DIGEST, output_digest=EMPTY_DIGEST, platform=Platform.current, metadata=ProcessResultMetadata(0, "ran_locally", 0), ) if options: opts = options or {} opts.setdefault("registries", {}) opts.setdefault("default_repository", "{directory}/{name}") opts.setdefault("build_args", []) opts.setdefault("env_vars", []) docker_options = create_subsystem( DockerOptions, **opts, ) else: docker_options = rule_runner.request(DockerOptions, []) result = run_rule_with_mocks( build_docker_image, rule_args=[ DockerFieldSet.create(tgt), docker_options, DockerBinary("/dummy/docker"), ], mock_gets=[ MockGet( output_type=DockerBuildContext, input_type=DockerBuildContextRequest, mock=build_context_mock, ), MockGet( output_type=ProcessResult, input_type=Process, mock=run_process_mock, ), ], ) assert result.digest == EMPTY_DIGEST assert len(result.artifacts) == 1 assert result.artifacts[0].relpath is None for log_line in extra_log_lines: assert log_line in result.artifacts[0].extra_log_lines
def docker(docker_path: str) -> DockerBinary: return DockerBinary(docker_path)
def assert_build( rule_runner: RuleRunner, address: Address, *extra_log_lines: str, options: dict | None = None, process_assertions: Callable[[Process], None] | None = None, exit_code: int = 0, copy_sources: tuple[str, ...] = (), build_context_snapshot: Snapshot = EMPTY_SNAPSHOT, version_tags: tuple[str, ...] = (), ) -> None: tgt = rule_runner.get_target(address) def build_context_mock( request: DockerBuildContextRequest) -> DockerBuildContext: return DockerBuildContext.create( snapshot=build_context_snapshot, dockerfile_info=DockerfileInfo( request.address, digest=EMPTY_DIGEST, source=os.path.join(address.spec_path, "Dockerfile"), copy_source_paths=copy_sources, version_tags=version_tags, ), build_args=rule_runner.request(DockerBuildArgs, [DockerBuildArgsRequest(tgt)]), build_env=rule_runner.request( DockerBuildEnvironment, [DockerBuildEnvironmentRequest(tgt)]), ) def run_process_mock(process: Process) -> FallibleProcessResult: if process_assertions: process_assertions(process) return FallibleProcessResult( exit_code=exit_code, stdout=b"stdout", stdout_digest=EMPTY_FILE_DIGEST, stderr=b"stderr", stderr_digest=EMPTY_FILE_DIGEST, output_digest=EMPTY_DIGEST, platform=Platform.current, metadata=ProcessResultMetadata(0, "ran_locally", 0), ) if options: opts = options or {} opts.setdefault("registries", {}) opts.setdefault("default_repository", "{name}") opts.setdefault("default_context_root", "") opts.setdefault("build_args", []) opts.setdefault("build_target_stage", None) opts.setdefault("build_verbose", False) opts.setdefault("env_vars", []) docker_options = create_subsystem( DockerOptions, **opts, ) else: docker_options = rule_runner.request(DockerOptions, []) global_options = rule_runner.request(GlobalOptions, []) result = run_rule_with_mocks( build_docker_image, rule_args=[ DockerFieldSet.create(tgt), docker_options, global_options, DockerBinary("/dummy/docker"), ProcessCleanupOption(True), ], mock_gets=[ MockGet( output_type=DockerBuildContext, input_type=DockerBuildContextRequest, mock=build_context_mock, ), MockGet( output_type=WrappedTarget, input_type=WrappedTargetRequest, mock=lambda _: WrappedTarget(tgt), ), MockGet( output_type=FallibleProcessResult, input_type=Process, mock=run_process_mock, ), ], ) assert result.digest == EMPTY_DIGEST assert len(result.artifacts) == 1 assert result.artifacts[0].relpath is None for log_line in extra_log_lines: assert log_line in result.artifacts[0].extra_log_lines