7
0
mirror of https://github.com/ChristianLight/tutor.git synced 2024-06-19 05:42:21 +00:00
tutor/tutor/commands/images.py

218 lines
6.1 KiB
Python
Raw Normal View History

from typing import Iterator, List, Tuple
import click
from .. import config as tutor_config
from .. import env as tutor_env
from .. import exceptions
from .. import images
from .. import plugins
from ..types import Config
from .context import Context
feat: run all services as unprivileged containers With this change, containers are no longer run as "root" but as unprivileged users. This is necessary in some environments, notably some Kubernetes clusters. To make this possible, we need to manually fix bind-mounted volumes in docker-compose. This is pretty much equivalent to the behaviour in Kubernetes, where permissions are fixed at runtime if the volume owner is incorrect. Thus, we have a consistent behaviour between docker-compose and Kubernetes. We achieve this by bind-mounting some repos inside "*-permissions" services. These services run as root user on docker-compose and will fix the required permissions, as per build/permissions/setowner.sh These services simply do not run on Kubernetes, where we don't rely on bind-mounted volumes. There, we make use of Kubernete's built-in volume ownership feature. With this change, we get rid of the "openedx-dev" Docker image, in the sense that it no longer has its own Dockerfile. Instead, the dev image is now simply a different target in the multi-layer openedx Docker image. This makes it much faster to build the openedx-dev image. Because we declare the APP_USER_ID in the dev/docker-compose.yml file, we need to pass the user ID from the host there. The only way to achieve that is with a tutor config variable. The downside of this approach is that the dev/docker-compose.yml file is no longer portable from one machine to the next. We consider that this is not such a big issue, as it affects the development environment only. We take this opportunity to replace the base image of the "forum" image. There is now no need to re-install ruby inside the image. The total image size is only decreased by 10%, but re-building the image is faster. In order to run the smtp service as non-root, we switch from namshi/smtp to devture/exim-relay. This change should be backward-compatible. Note that the nginx container remains privileged. We could switch to nginxinc/nginx-unprivileged, but it's probably not worth the effort, as we are considering to get rid of the nginx container altogether. Close #323.
2021-09-23 10:04:19 +00:00
BASE_IMAGE_NAMES = ["openedx", "forum", "permissions"]
v11.0.0 (2020-12-09) - 💥[Improvement] Upgrade Open edX to Koa - 💥 Setting changes: - The ``ACTIVATE_HTTPS`` setting was renamed to ``ENABLE_HTTPS``. - Other ``ACTIVATE_*`` variables were all renamed to ``RUN_*``. - The ``WEB_PROXY`` setting was removed and ``RUN_CADDY`` was added. - The ``NGINX_HTTPS_PORT`` setting is deprecated. - Architectural changes: - Use Caddy as a web proxy for automated SSL/TLS certificate generation: - Nginx no longer listens to port 443 for https traffic - The Caddy configuration file comes with a new ``caddyfile`` patch for much simpler SSL/TLS management. - Configuration files for web proxies are no longer provided. - Kubernetes deployment no longer requires setting up a custom Ingress resource or custom manager. - Gunicorn and Whitenoise are replaced by uwsgi: this increases boostrap performance and makes it no longer necessary to mount media folders in the Nginx container. - Replace memcached and rabbitmq by redis. - Additional features: - Make it possible to disable all plugins at once with ``plugins disable all``. - Add ``tutor k8s wait`` command to wait for a pod to become ready - Faster, more reliable static assets with local memory caching - Deprecation: proxy files for Apache and Nginx are no longer provided out of the box. - Removed plugin `{{ patch (...) }}` statements: - "https-create", "k8s-ingress-rules", "k8s-ingress-tls-hosts": these are no longer necessary. Instead, declare your app in the "caddyfile" patch. - "local-docker-compose-nginx-volumes": this patch was primarily used to serve media assets. The recommended is now to serve assets with uwsgi.
2020-09-17 10:53:14 +00:00
VENDOR_IMAGES = [
"caddy",
"elasticsearch",
"mongodb",
"mysql",
"redis",
"smtp",
]
2019-07-03 14:09:33 +00:00
2019-04-23 07:57:55 +00:00
@click.group(name="images", short_help="Manage docker images")
def images_command() -> None:
pass
2019-04-23 07:57:55 +00:00
@click.command(
short_help="Build docker images",
help="Build the docker images necessary for an Open edX platform.",
2019-02-13 19:18:47 +00:00
)
2020-10-01 22:25:03 +00:00
@click.argument("image_names", metavar="image", nargs=-1)
@click.option(
"--no-cache", is_flag=True, help="Do not use cache when building the image"
)
@click.option(
"-a",
"--build-arg",
"build_args",
multiple=True,
help="Set build-time docker ARGS in the form 'myarg=value'. This option may be specified multiple times.",
)
@click.option(
"--add-host",
"add_hosts",
multiple=True,
help="Set a custom host-to-IP mapping (host:ip).",
)
@click.option(
"--target",
help="Set the target build stage to build.",
)
@click.option(
"-d",
"--docker-arg",
"docker_args",
multiple=True,
help="Set extra options for docker build command.",
)
@click.pass_obj
def build(
context: Context,
image_names: List[str],
no_cache: bool,
build_args: List[str],
add_hosts: List[str],
target: str,
docker_args: List[str],
) -> None:
config = tutor_config.load(context.root)
command_args = []
if no_cache:
command_args.append("--no-cache")
for build_arg in build_args:
command_args += ["--build-arg", build_arg]
for add_host in add_hosts:
command_args += ["--add-host", add_host]
if target:
command_args += ["--target", target]
if docker_args:
command_args += docker_args
2020-10-01 22:25:03 +00:00
for image in image_names:
build_image(context.root, config, image, *command_args)
@click.command(short_help="Pull images from the Docker registry")
2020-10-01 22:25:03 +00:00
@click.argument("image_names", metavar="image", nargs=-1)
@click.pass_obj
def pull(context: Context, image_names: List[str]) -> None:
config = tutor_config.load(context.root)
2020-10-01 22:25:03 +00:00
for image in image_names:
pull_image(config, image)
@click.command(short_help="Push images to the Docker registry")
2020-10-01 22:25:03 +00:00
@click.argument("image_names", metavar="image", nargs=-1)
@click.pass_obj
def push(context: Context, image_names: List[str]) -> None:
config = tutor_config.load(context.root)
2020-10-01 22:25:03 +00:00
for image in image_names:
push_image(config, image)
@click.command(short_help="Print tag associated to a Docker image")
@click.argument("image_names", metavar="image", nargs=-1)
@click.pass_obj
def printtag(context: Context, image_names: List[str]) -> None:
2020-10-01 22:25:03 +00:00
config = tutor_config.load(context.root)
for image in image_names:
to_print = []
2020-10-01 22:25:03 +00:00
for _img, tag in iter_images(config, image, BASE_IMAGE_NAMES):
to_print.append(tag)
for _plugin, _img, tag in iter_plugin_images(config, image, "build-image"):
to_print.append(tag)
if not to_print:
raise ImageNotFoundError(image)
for tag in to_print:
2020-10-01 22:25:03 +00:00
print(tag)
2019-10-24 19:34:14 +00:00
def build_image(root: str, config: Config, image: str, *args: str) -> None:
to_build = []
# Build base images
for img, tag in iter_images(config, image, BASE_IMAGE_NAMES):
to_build.append((tutor_env.pathjoin(root, "build", img), tag, args))
# Build plugin images
for plugin, img, tag in iter_plugin_images(config, image, "build-image"):
to_build.append(
(tutor_env.pathjoin(root, "plugins", plugin, "build", img), tag, args)
)
if not to_build:
raise ImageNotFoundError(image)
for path, tag, build_args in to_build:
images.build(path, tag, *args)
def pull_image(config: Config, image: str) -> None:
to_pull = []
for _img, tag in iter_images(config, image, all_image_names(config)):
to_pull.append(tag)
for _plugin, _img, tag in iter_plugin_images(config, image, "remote-image"):
to_pull.append(tag)
if not to_pull:
raise ImageNotFoundError(image)
for tag in to_pull:
images.pull(tag)
def push_image(config: Config, image: str) -> None:
to_push = []
for _img, tag in iter_images(config, image, BASE_IMAGE_NAMES):
to_push.append(tag)
for _plugin, _img, tag in iter_plugin_images(config, image, "remote-image"):
to_push.append(tag)
if not to_push:
raise ImageNotFoundError(image)
for tag in to_push:
images.push(tag)
def iter_images(
config: Config, image: str, image_list: List[str]
) -> Iterator[Tuple[str, str]]:
for img in image_list:
if image in [img, "all"]:
tag = images.get_tag(config, img)
yield img, tag
def iter_plugin_images(
config: Config, image: str, hook_name: str
) -> Iterator[Tuple[str, str, str]]:
for plugin, hook in plugins.iter_hooks(config, hook_name):
if not isinstance(hook, dict):
raise exceptions.TutorError(
"Invalid hook '{}': expected dict, got {}".format(
hook_name, hook.__class__
)
)
for img, tag in hook.items():
if image in [img, "all"]:
tag = tutor_env.render_str(config, tag)
yield plugin, img, tag
2019-04-23 07:57:55 +00:00
def all_image_names(config: Config) -> List[str]:
return BASE_IMAGE_NAMES + vendor_image_names(config)
def vendor_image_names(config: Config) -> List[str]:
v11.0.0 (2020-12-09) - 💥[Improvement] Upgrade Open edX to Koa - 💥 Setting changes: - The ``ACTIVATE_HTTPS`` setting was renamed to ``ENABLE_HTTPS``. - Other ``ACTIVATE_*`` variables were all renamed to ``RUN_*``. - The ``WEB_PROXY`` setting was removed and ``RUN_CADDY`` was added. - The ``NGINX_HTTPS_PORT`` setting is deprecated. - Architectural changes: - Use Caddy as a web proxy for automated SSL/TLS certificate generation: - Nginx no longer listens to port 443 for https traffic - The Caddy configuration file comes with a new ``caddyfile`` patch for much simpler SSL/TLS management. - Configuration files for web proxies are no longer provided. - Kubernetes deployment no longer requires setting up a custom Ingress resource or custom manager. - Gunicorn and Whitenoise are replaced by uwsgi: this increases boostrap performance and makes it no longer necessary to mount media folders in the Nginx container. - Replace memcached and rabbitmq by redis. - Additional features: - Make it possible to disable all plugins at once with ``plugins disable all``. - Add ``tutor k8s wait`` command to wait for a pod to become ready - Faster, more reliable static assets with local memory caching - Deprecation: proxy files for Apache and Nginx are no longer provided out of the box. - Removed plugin `{{ patch (...) }}` statements: - "https-create", "k8s-ingress-rules", "k8s-ingress-tls-hosts": these are no longer necessary. Instead, declare your app in the "caddyfile" patch. - "local-docker-compose-nginx-volumes": this patch was primarily used to serve media assets. The recommended is now to serve assets with uwsgi.
2020-09-17 10:53:14 +00:00
vendor_images = VENDOR_IMAGES[:]
for image in VENDOR_IMAGES:
if not config.get("RUN_" + image.upper(), True):
2019-07-03 14:09:33 +00:00
vendor_images.remove(image)
return vendor_images
2019-04-23 07:57:55 +00:00
class ImageNotFoundError(exceptions.TutorError):
def __init__(self, image_name: str):
super().__init__("Image '{}' could not be found".format(image_name))
2019-04-23 07:57:55 +00:00
images_command.add_command(build)
images_command.add_command(pull)
images_command.add_command(push)
2020-10-01 22:25:03 +00:00
images_command.add_command(printtag)