Skip to content

Commit

Permalink
Add container build image command
Browse files Browse the repository at this point in the history
fixes: pulp#424
  • Loading branch information
gerrod3 committed Mar 10, 2023
1 parent aa3cc7d commit 7cb86cc
Show file tree
Hide file tree
Showing 5 changed files with 120 additions and 3 deletions.
1 change: 1 addition & 0 deletions CHANGES/424.feature
Original file line number Diff line number Diff line change
@@ -0,0 +1 @@
Added container build image command.
5 changes: 5 additions & 0 deletions pulp-glue/pulp_glue/common/context.py
Original file line number Diff line number Diff line change
Expand Up @@ -168,6 +168,11 @@ def _patch_api_spec(self) -> None:
python_remote_serializer["properties"][prop]["items"] = {"type": "string"}
patched_python_remote_serializer["properties"][prop]["type"] = "array"
patched_python_remote_serializer["properties"][prop]["items"] = {"type": "string"}
if self.has_plugin(PluginRequirement("container", min="1.1.0")):
# TODO Add upper bound when fixed
oci_build_schema = api_spec["components"]["schemas"]["OCIBuildImage"]
oci_artifacts = oci_build_schema["properties"]["artifacts"]
oci_artifacts["type"] = "string"

@property
def domain_enabled(self) -> bool:
Expand Down
17 changes: 16 additions & 1 deletion pulp-glue/pulp_glue/container/context.py
Original file line number Diff line number Diff line change
@@ -1,4 +1,5 @@
from typing import Any, List, Optional
import json
from typing import Any, Dict, List, Optional

from pulp_glue.common.context import (
EntityDefinition,
Expand Down Expand Up @@ -128,6 +129,7 @@ class PulpContainerRepositoryContext(PulpContainerBaseRepositoryContext):
"pulpexport": [PluginRequirement("container", min="2.8.0")],
"tag": [PluginRequirement("container", min="2.3.0")],
"roles": [PluginRequirement("container", min="2.11.0")],
"build": [PluginRequirement("container", min="1.1.0")],
}

def modify(
Expand Down Expand Up @@ -167,6 +169,19 @@ def copy_manifest(
}
return self.call("copy_manifests", parameters={self.HREF: self.pulp_href}, body=body)

def build_image(
self,
container_artifact: str,
tag: Optional[str],
artifacts: Optional[Dict[str, str]],
) -> Any:
body = {"containerfile_artifact": container_artifact, "tag": tag, "artifacts": artifacts}
# TODO: Add plugin version check when schema is fixed
if artifacts:
body["artifacts"] = json.dumps(artifacts)
body = self.preprocess_body(body)
return self.call("build_image", parameters={self.HREF: self.pulp_href}, body=body)


class PulpContainerPushRepositoryContext(PulpContainerBaseRepositoryContext):
HREF = "container_container_push_repository_href"
Expand Down
95 changes: 93 additions & 2 deletions pulpcore/cli/container/repository.py
Original file line number Diff line number Diff line change
@@ -1,8 +1,14 @@
import re
from typing import Any, Dict, List, Optional
from pathlib import Path
from typing import Any, Dict, List, Optional, Union

import click
from pulp_glue.common.context import EntityFieldDefinition, PulpRemoteContext, PulpRepositoryContext
from pulp_glue.common.context import (
EntityFieldDefinition,
PulpContext,
PulpRemoteContext,
PulpRepositoryContext,
)
from pulp_glue.common.i18n import get_translation
from pulp_glue.container.context import (
PulpContainerBaseRepositoryContext,
Expand All @@ -13,6 +19,7 @@
PulpContainerRepositoryContext,
PulpContainerTagContext,
)
from pulp_glue.core.context import PulpArtifactContext

from pulpcore.cli.common.generic import (
create_command,
Expand All @@ -21,7 +28,9 @@
label_command,
label_select_option,
list_command,
load_json_callback,
name_option,
pass_pulp_context,
pass_repository_context,
pulp_group,
pulp_labels_option,
Expand Down Expand Up @@ -53,6 +62,22 @@ def _tag_callback(ctx: click.Context, param: click.Parameter, value: str) -> str
return value


def _directory_or_json_callback(
ctx: click.Context, param: click.Parameter, value: Optional[str]
) -> Union[Dict[str, str], Path, None]:
if not value:
return None
uvalue: Union[Dict[str, str], Path]
try:
uvalue = load_json_callback(ctx, param, value)
except click.ClickException:
uvalue = Path(value)
if not uvalue.exists() or not uvalue.is_dir():
raise click.ClickException(_("{} is not a valid directory").format(value))

return uvalue


source_option = resource_option(
"--source",
default_plugin="container",
Expand Down Expand Up @@ -291,6 +316,72 @@ def copy_manifest(
)


def upload_file(pulp_ctx: PulpContext, file_location: str) -> str:
try:
with click.open_file(file_location, "rb") as fp:
artifact_ctx = PulpArtifactContext(pulp_ctx)
artifact_href = artifact_ctx.upload(fp)
except OSError:
raise click.ClickException(
_("Failed to load content from {file}").format(file=file_location)
)
click.echo(_("Uploaded file: {}").format(artifact_href))
return artifact_href # type: ignore


@repository.command(allowed_with_contexts=container_context)
@name_option
@href_option
@click.option(
"--containerfile",
help=_(
"An artifact href of an uploaded Containerfile. Can also be a local Containerfile to be"
" uploaded using @."
),
required=True,
)
@click.option("--tag", help=_("A tag name for the new image being built."))
@click.option(
"--artifacts",
help=_(
"Directory of files to be uploaded and used during the build. Or a JSON string where each"
" key is an artifact href and the value is it's relative path (name) inside the "
"/pulp_working_directory of the build container executing the Containerfile."
),
callback=_directory_or_json_callback,
)
@pass_repository_context
@pass_pulp_context
def build_image(
pulp_ctx: PulpContext,
repository_ctx: PulpContainerRepositoryContext,
containerfile: str,
tag: Optional[str],
artifacts: Union[Dict[str, str], Path, None],
) -> None:
if not repository_ctx.capable("build"):
raise click.ClickException(_("Repository does not support image building."))

container_artifact_href: str
# Upload necessary files as artifacts if specified
if containerfile[0] == "@":
container_artifact_href = upload_file(pulp_ctx, containerfile[1:])
else:
artifact_ctx = PulpArtifactContext(pulp_ctx, pulp_href=containerfile)
container_artifact_href = artifact_ctx.pulp_href

if isinstance(artifacts, Path):
artifacts_path = artifacts
# Upload files in directory
artifacts = {}
for child in artifacts_path.rglob("*"):
if child.is_file():
artifact_href = upload_file(pulp_ctx, str(child))
artifacts[artifact_href] = str(child.relative_to(artifacts_path))

repository_ctx.build_image(container_artifact_href, tag, artifacts)


@repository.command(allowed_with_contexts=push_container_context)
@name_option
@href_option
Expand Down
5 changes: 5 additions & 0 deletions tests/assets/test_containerfile
Original file line number Diff line number Diff line change
@@ -0,0 +1,5 @@
FROM busybox:latest
# Copy a file using COPY statement. Use the relative path specified in the 'artifacts' parameter.
COPY foo/bar/example.txt /tmp/inside-image.txt
# Print the content of the file when the container starts
CMD ["cat", "/tmp/inside-image.txt"]

0 comments on commit 7cb86cc

Please sign in to comment.