Skip to content
This repository has been archived by the owner on Feb 25, 2024. It is now read-only.

Commit

Permalink
feat: added more options for bentoctl build to match options availabl…
Browse files Browse the repository at this point in the history
…e via buildx (#190)

* feat: added more options for bentoctl build to match options available via buildx

* fix typing issues

* remove flake8 checks

* fix linting checks

* bentoml_cli.utils

* remove some internal dependencies

* default to linux/amd64
  • Loading branch information
jjmachan authored Oct 6, 2022
1 parent 2200907 commit 148d231
Show file tree
Hide file tree
Showing 6 changed files with 224 additions and 40 deletions.
180 changes: 176 additions & 4 deletions bentoctl/cli/__init__.py
Original file line number Diff line number Diff line change
@@ -1,4 +1,8 @@
from __future__ import annotations

import logging
import os
import typing as t

import click

Expand All @@ -25,6 +29,16 @@
terraform_destroy,
)

logger = logging.getLogger(__name__)
try:
from bentoml_cli.utils import validate_docker_tag
except ImportError:
logger.warning(
"'bentoml._internal.utils.docker.validate_tag not imported. Validation dissabled"
)
validate_docker_tag = None


CONTEXT_SETTINGS = dict(help_option_names=["-h", "--help"])


Expand Down Expand Up @@ -123,18 +137,119 @@ def generate(deployment_config_file, values_only, save_path):
@click.option(
"--bento-tag", "-b", help="Bento tag to use for deployment.", required=True
)
@click.option(
"-t",
"--docker-image-tag",
help="Name and optionally a tag (format: 'name:tag'), defaults to bento tag.",
required=False,
callback=validate_docker_tag,
multiple=True,
)
@click.option(
"--deployment-config-file",
"-f",
help="path to deployment_config file",
default="deployment_config.yaml",
)
@click.option("--dry-run", is_flag=True, help="Dry run", default=False)
@click.option(
"--allow",
multiple=True,
default=None,
help="Allow extra privileged entitlement (e.g., 'network.host', 'security.insecure').",
)
@click.option("--build-arg", multiple=True, help="Set build-time variables.")
@click.option(
"--build-context",
multiple=True,
help="Additional build contexts (e.g., name=path).",
)
@click.option(
"--builder",
type=click.STRING,
default=None,
help="Override the configured builder instance.",
)
@click.option(
"--cache-from",
multiple=True,
default=None,
help="External cache sources (e.g., 'user/app:cache', 'type=local,src=path/to/dir').",
)
@click.option(
"--cache-to",
multiple=True,
default=None,
help="Cache export destinations (e.g., 'user/app:cache', 'type=local,dest=path/to/dir').",
)
@click.option(
"--load",
is_flag=True,
default=True,
help="Shorthand for '--output=type=docker'. Note that '--push' and '--load' are mutually exclusive.",
)
@click.option(
"--no-cache",
is_flag=True,
default=False,
help="Do not use cache when building the image.",
)
@click.option(
"--output",
multiple=True,
default=None,
help="Output destination (format: 'type=local,dest=path').",
)
@click.option(
"--platform",
default=["linux/amd64"],
multiple=True,
help="Set target platform for build.",
)
@click.option(
"--progress",
default="auto",
type=click.Choice(["auto", "tty", "plain"]),
help="Set type of progress output ('auto', 'plain', 'tty'). Use plain to show container output.",
)
@click.option(
"--pull",
is_flag=True,
default=False,
help="Always attempt to pull all referenced images.",
)
@click.option(
"--push",
is_flag=True,
default=False,
help="Shorthand for '--output=type=registry'. Note that '--push' and '--load' are mutually exclusive.",
)
@click.option(
"--target",
type=click.STRING,
default=None,
help="Set the target build stage to build.",
)
@handle_bentoctl_exceptions
def build(
bento_tag,
deployment_config_file,
dry_run,
bento_tag: str,
docker_image_tag: list[str],
deployment_config_file: str,
dry_run: bool,
allow: t.Iterable[str],
build_arg: list[str],
build_context: list[str],
builder: str,
cache_from: list[str],
cache_to: list[str],
load: bool,
no_cache: bool,
output: list[str],
platform: list[str],
progress: t.Literal["auto", "tty", "plain"],
pull: bool,
push: bool,
target: str,
):
"""
Build the Docker image for the given deployment config file and bento.
Expand All @@ -144,10 +259,67 @@ def build(
deployment_config.set_bento(bento_tag)
local_docker_tag = deployment_config.generate_local_image_tag()

# parse buildx args
tags = [local_docker_tag, *docker_image_tag]

allow_ = []
if allow:
allow_ = list(allow)

build_args = {}
if build_arg:
for build_arg_str in build_arg:
key, value = build_arg_str.split("=")
build_args[key] = value

build_context_ = {}
if build_context:
for build_context_str in build_context:
key, value = build_context_str.split("=")
build_context_[key] = value

output_ = None
if output:
output_ = {}
for arg in output:
if "," in arg:
for val in arg.split(","):
k, v = val.split("=")
output_[k] = v
key, value = arg.split("=")
output_[key] = value

load = True
if platform and len(platform) > 1:
if not push:
click.echo(
"Multiple '--platform' arguments were found. Make sure to also use '--push' to push images to a repository or generated images will not be saved. For more information, see https://docs.docker.com/engine/reference/commandline/buildx_build/#load."
)
if push:
click.echo(
"'--push' flag detected. bentoctl will not attempt to create repository and push image into it."
)
load = False
dry_run = True

generate_deployable_container(
tag=local_docker_tag,
tags=tags,
deployment_config=deployment_config,
cleanup=False if is_debug_mode() else True,
allow=allow_,
build_args=build_args,
build_context=build_context_,
builder=builder,
cache_from=cache_from,
cache_to=cache_to,
load=load,
no_cache=no_cache,
output=output_,
platform=platform,
progress=progress,
pull=pull,
push=push,
target=target,
)

if not dry_run:
Expand Down
2 changes: 2 additions & 0 deletions bentoctl/cli/utils.py
Original file line number Diff line number Diff line change
@@ -1,3 +1,5 @@
from __future__ import annotations

import functools
import os
import sys
Expand Down
2 changes: 1 addition & 1 deletion bentoctl/deployment_config.py
Original file line number Diff line number Diff line change
Expand Up @@ -8,7 +8,7 @@
import cerberus
import fs
import yaml
from bentoml._internal.bento import Bento
from bentoml import Bento
from bentoml.exceptions import NotFound

from bentoctl.exceptions import (
Expand Down
51 changes: 35 additions & 16 deletions bentoctl/docker_utils.py
Original file line number Diff line number Diff line change
@@ -1,3 +1,6 @@
from __future__ import annotations

import typing as t
from collections import OrderedDict

import docker
Expand Down Expand Up @@ -51,7 +54,23 @@ def __rich_console__(self, *_):


def generate_deployable_container(
tag: str, deployment_config: DeploymentConfig, cleanup: bool
tags: list[str],
deployment_config: DeploymentConfig,
cleanup: bool,
allow: list[str],
build_args: dict[str, str],
build_context: dict[str, str],
builder: str,
cache_from: list[str],
cache_to: list[str],
load: bool,
no_cache: bool,
output: dict[str, str] | None,
platform: list[str],
progress: t.Literal["auto", "tty", "plain"],
pull: bool,
push: bool,
target: str,
):
with TempDirectory(cleanup=cleanup) as dist_dir:
if cleanup is False:
Expand All @@ -64,33 +83,33 @@ def generate_deployable_container(
"subprocess_env": env,
"cwd": deployment_config.create_deployable(destination_dir=str(dist_dir)),
"file": DOCKERFILE_PATH,
"tags": tag,
"tags": tags,
"add_host": None,
"allow": None,
"build_args": None,
"build_context": None,
"builder": None,
"cache_from": None,
"cache_to": None,
"allow": allow,
"build_args": build_args,
"build_context": build_context,
"builder": builder,
"cache_from": cache_from,
"cache_to": cache_to,
"cgroup_parent": None,
"iidfile": None,
"labels": None,
"load": True, # loading built container to local registry.
"load": load, # loading built container to local registry.
"metadata_file": None,
"network": None,
"no_cache": False,
"no_cache": no_cache,
"no_cache_filter": None,
"output": None,
"platform": "linux/amd64",
"progress": "auto",
"pull": False,
"push": False,
"output": output,
"platform": platform,
"progress": progress,
"pull": pull,
"push": push,
"quiet": False,
"secrets": None,
"shm_size": None,
"rm": False,
"ssh": None,
"target": None,
"target": target,
"ulimit": None,
}

Expand Down
12 changes: 10 additions & 2 deletions bentoctl/utils/usage_stats.py
Original file line number Diff line number Diff line change
@@ -1,19 +1,27 @@
import os
import typing as t
from functools import lru_cache

import attr

# These are internal apis. We will need to make sure update these when BentoML changes.
# These are internal apis. We will need to make sure update these when BentoML changes
from bentoml._internal.utils.analytics.schemas import EventMeta
from bentoml._internal.utils.analytics.usage_stats import ( # noqa pylint: disable=unused-import
BENTOML_DO_NOT_TRACK,
do_not_track,
track,
)

from bentoctl import __version__
from bentoctl.deployment_config import DeploymentConfig


@lru_cache(maxsize=1)
def do_not_track() -> bool: # pragma: no cover
# Returns True if and only if the environment variable is defined and has value True.
# The function is cached for better performance.
return os.environ.get(BENTOML_DO_NOT_TRACK, str(False)).lower() == "true"


@attr.define
class BentoctlCliEvent(EventMeta):
cmd_group: str
Expand Down
17 changes: 0 additions & 17 deletions ci/linter.sh
Original file line number Diff line number Diff line change
Expand Up @@ -22,23 +22,6 @@ fi

# The first line of the tests are
# always empty if there are no linting errors

echo "Running flake8 on bentoctl module.."
output=$( poetry run flake8 --config=.flake8 bentoctl )
first_line=$(echo "${output}" | head -1)
echo "$output"
if [ -n "$first_line" ]; then
has_errors=1
fi

echo "Running flake8 on test module.."
output=$( poetry run flake8 --config=.flake8 tests )
first_line=$(echo "${output}" | head -1)
echo "$output"
if [ -n "$first_line" ]; then
has_errors=1
fi

echo "Running pylint on bentoctl module.."
output=$( poetry run pylint --rcfile="./pylintrc" bentoctl )
first_line=$(echo "${output}" | head -1)
Expand Down

0 comments on commit 148d231

Please sign in to comment.