"""
Start a single container
"""
import os
from typing import List, Optional, Union
import typer
from controller import REGISTRY, log, print_and_exit
from controller.app import Application, Configuration
from controller.deploy.builds import verify_available_images
from controller.deploy.compose_v2 import PortMapping, PortRangeMapping
from controller.deploy.docker import Docker
from controller.templating import password
[docs]
def get_publish_ports(
service: str, change_first_port: Optional[int]
) -> Optional[List[Union[PortMapping, PortRangeMapping]]]:
service_config = Application.data.compose_config.get(service, None)
if not service_config:
print_and_exit("Services misconfiguration, can't find {}", service)
ports: List[Union[PortMapping, PortRangeMapping]] = []
if service_config.ports:
for p in service_config.ports:
port = change_first_port or p.published
target = p.target
if port is None or target is None:
log.warning(
"Found null port on {}; port: {}, target: {}", service, port, target
)
continue
# Remove it, because this option is to be applied only to the first port
change_first_port = None
ports.append(
(
port,
target,
)
)
return ports
# This command replaces volatile, interfaces and registry
[docs]
@Application.app.command(help="Start a single container")
def run(
service: str = typer.Argument(
...,
help="Service name",
shell_complete=Application.autocomplete_allservice,
),
pull: bool = typer.Option(
False,
"--pull",
help="Pull the image before starting the container",
show_default=False,
),
debug: bool = typer.Option(
False,
"--debug",
help="Start the container in debug mode",
show_default=False,
),
command: str = typer.Option(
None,
"--command",
help="UNIX command to be executed in the container",
show_default=False,
),
user: str = typer.Option(
None,
"--user",
"-u",
help="User existing in selected service",
show_default=False,
),
first_port: Optional[int] = typer.Option(
None,
"--port",
"-p",
help="port to be associated to the current service interface",
),
detach: Optional[bool] = typer.Option(
None,
"--detach",
help="Start the container in detach mode (default for non-interfaces)",
show_default=False,
),
) -> None:
Application.print_command(
Application.serialize_parameter("--pull", pull, IF=pull),
Application.serialize_parameter("--debug", debug, IF=debug),
Application.serialize_parameter("--command", command, IF=command),
Application.serialize_parameter("--user", user, IF=user),
Application.serialize_parameter("--port", first_port, IF=first_port),
Application.serialize_parameter("", service),
)
Configuration.FORCE_COMPOSE_ENGINE = True
Application.get_controller().controller_init()
Application.get_controller().check_placeholders_and_passwords(
Application.data.compose_config, [service]
)
if service == REGISTRY and not Configuration.swarm_mode:
print_and_exit("Can't start the registry in compose mode")
docker = Docker()
if Configuration.swarm_mode:
if service != REGISTRY:
docker.registry.ping()
else:
if docker.registry.ping(do_exit=False):
registry = docker.registry.get_host()
print_and_exit("The registry is already running at {}", registry)
if docker.client.container.exists("registry"):
log.debug("The registry container is already existing, removing")
docker.client.container.remove("registry", force=True)
if not debug:
if user:
print_and_exit("Can't specify a user if debug mode is OFF")
if command:
print_and_exit("Can't specify a command if debug mode is OFF")
if user:
log.warning(
"Please remember that users in volatile containers are not mapped on"
" current uid and gid. You should not write or modify files on volumes"
" to prevent permissions errors"
)
if pull:
log.info("Pulling image for {}...", service)
docker.client.compose.pull([service])
else:
verify_available_images(
[service],
Application.data.compose_config,
Application.data.base_services,
is_run_command=True,
)
# This is equivalent to the old volatile command
if debug:
if not command:
command = "bash"
log.info("Starting {}...", service)
docker.compose.create_volatile_container(
service,
command=command,
user=user,
# if None the wrapper will automatically switch the default ones
# How to prevent ports on volatile containers?
# publish=None,
)
log.info("Service {} removed", service)
return None
# This is equivalent to the old registry command
if service == REGISTRY:
# @ symbol in secrets is not working
# https://github.com/bitnami/charts/issues/1954
# Other symbols like # and " also lead to configuration errors
os.environ["REGISTRY_HTTP_SECRET"] = password(
param_not_used="",
length=96,
# , symbols="%*,-.=?[]^_~"
)
publish_ports = get_publish_ports(service, first_port)
if detach is None:
if service == "swaggerui" or service == "adminer":
detach = False
else:
detach = True
log.info("Running {}...", service)
if service == "swaggerui":
if Configuration.production:
prot = "https"
else:
prot = "http"
port = publish_ports[0][0] if publish_ports else first_port
log.info(
"You can access SwaggerUI web page here: {}\n",
f"{prot}://{Configuration.hostname}:{port}",
)
if service == "adminer":
if Configuration.production:
prot = "https"
else:
prot = "http"
port = publish_ports[0][0] if publish_ports else first_port
log.info(
"You can access Adminer interface on: {}\n",
f"{prot}://{Configuration.hostname}:{port}",
)
docker.compose.create_volatile_container(
service, detach=detach, publish=publish_ports
)