fabric/modules/docker.py

472 lines
15 KiB
Python

from fabric.api import env, task
from fabric.contrib.files import upload_template
import os
import sys
import pathlib
dir_parent = pathlib.Path(os.path.abspath(__file__)).parents[2]
sys.path.append(str(dir_parent))
try:
from customfabric.modules.utils import loggify, booleanize
from customfabric.modules.utils import generate_template_files_path
from customfabric.modules.utils import generate_template_build_path
from customfabric.modules.utils import executize, print_console
except ImportError:
raise
# from .utils import loggify, generate_template_files_path, booleanize
# from .utils import generate_template_build_path, print_console
# from .utils import executize
@task
def docker_ip():
"""
Retrieves the IP address for the Docker database.
This method determines the appropriate IP address for the Docker-based
database. If the host is set to 'local' in the configuration under
docker.database, it assumes the environment is OSX and uses docker-machine
to retrieve the IP. Otherwise, it returns the predefined host IP address
from the configuration.
Note:
- This method currently returns 'localhost' for 'local' configurations
instead of using docker-machine.
- For configurations with a specific IP (not 'local'), it directly
returns the configured IP address.
:returns: The IP address of the Docker database.
:rtype: str
"""
configuration = env.config
# in the configuration file under docker,database, if host is set to local
# use docker-machine, because it means we are probably on OSX
#
# otherwise, if host is set to anything else (like the actual ip) then just
# return the host ip address that we set in configuration
if configuration.docker.database.host == 'local':
# docker_cmd = 'docker-machine ip %s' % \
# configuration.docker.machine
# docker_cmd = "docker inspect -f '{{"
# docker_cmd += "range.NetworkSettings.Networks}}"
# docker_cmd += "{{.IPAddress}}{{end}}' pedantic_tu"
# _execute = executize("run")
# return _execute(docker_cmd)
return "localhost"
else:
return configuration.docker.database.host
def docker_run(cmd):
"""
Executes a Docker command either locally or on a remote host.
This function facilitates the execution of Docker commands. It determines
whether to run the command directly or with sudo based on the host
configuration. If the Docker database host is set to 'local', it assumes a
local (OSX) environment where the user can directly run Docker commands.
Otherwise, for remote hosts, it uses sudo for command execution.
:param cmd: The Docker command to be executed.
:type cmd: str
Note:
- The function uses 'executize' to dynamically select between 'run' and
'sudo' methods from fabric.operations based on the host setting.
- If 'env.debug' is enabled, the command to be executed is logged for
debugging purposes.
:returns: None. The function executes a command but does not return any
value.
"""
configuration = env.config
if env.debug:
logger = loggify("docker", 'create')
# use executize to return either the methods sudo or run from
# fabric.operations
#
# we are checking to see if the host is local or not
# if it is local, that means OSX, which means the user can directly run any
# docker commands, but if it is remote, it probably requires sudo to run
# docker commands
_execute = executize("run")
if configuration.docker.database.host != 'local':
_execute = executize("sudo")
if env.debug:
logger.debug("_execute(cmd): %s" % cmd)
else:
_execute(cmd)
@task
def generate():
"""
Generates and uploads a docker.yml configuration file.
This method creates a docker.yml file based on settings specified in the
YAML file for the current branch. It dynamically sets up the Docker
container configuration for the branch in use. This includes service names,
container names, environment variables (such as user, password, and
database name), Docker image, and port settings. If additional volume
settings are specified, these are also included in the context.
e.g. if we are using development.yml then it will check for the docker
settings in there to find out what conf values we want to use when creating
whatever docker containers we are usign for this branch
Note:
- Primarily used for development branch currently, but can be adapted
for others.
- In debug mode, additional logging and context printing are enabled.
:returns: None. The method generates a file and optionally logs the
process.
"""
configuration = env.config
if env.debug:
logger = loggify('docker', 'generate')
build_path = generate_template_build_path('docker', 'database')
files_path = generate_template_files_path('docker')
context = dict()
context['docker_service_name'] = \
configuration.docker.database.service_name
context['docker_container_name'] = \
configuration.docker.database.container_name
context['docker_database_env_user'] = \
configuration.docker.database.env.user
context['docker_database_env_pass'] = \
configuration.docker.database.env.password
context['docker_database_env_db'] = \
configuration.docker.database.env.dbname
context['docker_database_image'] = configuration.docker.database.image
context['docker_database_port_external'] = \
configuration.server.database.port
context['docker_database_port_internal'] = \
configuration.docker.database.port
context['projectbranch'] = configuration.project.branch
context['database_user'] = configuration.server.database.admin.user
context['database_pass'] = configuration.server.database.admin.password
context['database_name'] = configuration.server.database.name
if hasattr(configuration.docker.database, 'volumes') and \
hasattr(configuration.docker.database.volumes, 'data'):
context['docker_volume_data_external'] = \
configuration.docker.database.volumes.data.external
context['docker_volume_data_internal'] = \
configuration.docker.database.volumes.data.internal
if env.debug:
for key in context.keys():
logger.debug("context[{key}] : {value}".format(
key=key,
value=context[key]))
upload_msg = "upload_template(" \
"\n\tfilename={filename}," \
"\n\tdestination={destination}," \
"\n\tcontext={context}," \
"\n\tuse_jinja=True," \
"\n\tuse_sudo=False," \
"\n\tbackup=False," \
"\n\ttemplate_dir={template_dir})".format(
filename=configuration.templates.docker.database.src,
destination=build_path,
context=context,
template_dir=files_path)
logger.debug("upload_msg : %s" % upload_msg)
import pprint
pp = pprint.PrettyPrinter(indent=4)
print("context:\n")
pp.pprint(context)
else:
config_src = configuration.templates.docker.database.src
upload_template(
filename=config_src,
destination=build_path,
context=context,
use_jinja=True,
use_sudo=False,
backup=False,
template_dir=files_path)
@task
def create(container='database'):
"""
Creates a Docker-based database container.
This function is responsible for setting up a Docker container,
specifically a database container by default. It generates a container
template based on the specified type and uses Docker Compose to bring up
the container.
:param container: Specifies the type of container to create, defaults to
'database'. The container type should have corresponding
settings in the configuration file.
:type container: str
Note:
- The configuration file must contain corresponding settings for the
specified container type under the 'docker' section.
- In debug mode, additional logging information about the build path
and Docker Compose command is provided.
:returns: None. The function primarily executes Docker Compose commands.
"""
configuration = env.config
if env.debug:
logger = loggify("docker", 'create')
build_path = generate_template_build_path('docker', container)
info_msg = """
Generating container template for {container}, note that
the container paramter of "{container}" must have a
corresponding value in the {branch} configuration file
under "docker"
""".format(container=container, branch="dev")
print_console(info_msg, numsep=60)
docker_service_name = configuration.docker.database.service_name
dockercompose_cmd = \
"docker compose -f {build_path} up -d {docker_service_name}".format(
build_path=build_path,
docker_service_name=docker_service_name
)
if env.debug:
logger.debug("build_path : %s" % build_path)
logger.debug("dockercompose_cmd : %s" % dockercompose_cmd)
logger.debug("docker_service_name : %s" % docker_service_name)
else:
docker_run(dockercompose_cmd)
@task
def status():
"""
Displays the status of all Docker containers.
This function runs the 'docker ps -a' command which lists all Docker
containers and their current status, including those that are stopped.
:returns: None. The output is displayed to the console.
"""
docker_run("docker ps -a")
@task
def start(create=False):
"""
Starts a specific Docker container.
This function starts the Docker container as specified in the
configuration. If the container does not exist and 'create' is set to True,
the container will be created and started.
:param create: Flag indicating whether to create the container if it does
not exist, defaults to False.
:type create: bool
Note:
- The container is identified by the 'container_name' attribute in the
'docker.database' section of the configuration.
- The container should be created first using the 'docker.create'
method, unless 'create' is True.
:returns: None. The container is started, but no value is returned.
"""
if env.debug:
logger = loggify("docker", 'create')
configuration = env.config
create = booleanize(create)
docker_start = 'docker start %s' % \
configuration.docker.database.container_name
if env.debug:
logger.debug("docker_run(%s)" % docker_start)
docker_run(docker_start)
else:
docker_run(docker_start)
@task
def stop(remove=False):
"""
Stops and optionally removes a specific Docker container.
This function stops the Docker container as specified in the configuration.
If the 'remove' flag is set to True, the container will also be removed
after being stopped.
:param remove: Flag indicating whether to remove the container after
stopping, defaults to False.
:type remove: bool
Note:
- The container is identified by the 'container_name' attribute in the
'docker.database' section of the configuration.
- The container should be created first using the 'docker.create'
method.
:returns: None. The container is stopped, and optionally removed, but no
value is returned.
"""
configuration = env.config
remove = booleanize(remove)
docker_stop = 'docker stop %s' % \
configuration.docker.database.container_name
docker_rm = 'docker rm %s' % configuration.docker.database.container_name
if env.debug:
print("docker_run(%s)" % docker_stop)
print("docker_run(%s)" % docker_rm)
else:
docker_run(docker_stop)
if remove:
docker_run(docker_rm)
@task
def edit(param='help'):
"""
Opens Docker configuration and build files in MacVim for editing.
This function streamlines the process of editing Docker-related files by
opening them in MacVim, a macOS version of the Vim text editor. It can be
directed to open specific Docker configuration or build files based on the
provided parameter. If 'help' or an unrecognized key is supplied as the
parameter, the function lists all the available files for editing along
with their paths.
:param param: The key for the specific Docker file to edit. Providing
'help' lists all available keys and their associated file
paths. Defaults to 'help'.
:type param: str
Note:
- The editable file paths are determined from the project's
configuration.
- This function is tailored for use in environments where MacVim is
available.
- File editing is facilitated by the 'maintenance_edit' function, which
integrates with MacVim.
:returns: None. The function either invokes MacVim for a specific file or
outputs information.
"""
from .maintenance import edit as maintenance_edit
configuration = env.config
print('keys: %s' % configuration.templates.docker.keys())
print('path: %s' % configuration.templates.docker.path.keys())
print('path.remote: %s' % configuration.templates.docker.path.remote)
print('path.local: %s' % configuration.templates.docker.path.local)
print('database: %s' % configuration.templates.docker.database.keys())
print('database.src: %s' % configuration.templates.docker.database.src)
print('database.dst: %s' % configuration.templates.docker.database.dst)
import os
database_build_path = os.path.join(
configuration.templates.docker.path.remote,
'build',
configuration.templates.docker.database.dst
)
database_template_path = os.path.join(
configuration.templates.docker.path.remote,
'files',
configuration.templates.docker.database.src
)
print('database build path: %s' % database_build_path)
print('database template path: %s' % database_template_path)
project_branch = configuration.project.branch
print('project branch: %s' % project_branch)
locations = {
'database': {
'path': database_build_path,
'desc': 'docker database file in scripts/conf/docker/build',
},
'template': {
'path': database_template_path,
'desc': 'docker template file for database is'
'scripts/conf/docker/files',
}
}
if param in locations.keys():
remote_path = locations[param]['path']
maintenance_edit(remote_path=remote_path)
else:
# if param == 'help':
print("""
"fab docker.edit" automates editing template configuration files
for docker database
to use this you must pass one of the editable locations in as a
parameter
currently editable locations are:
""")
for k_loc in locations.keys():
print("\t{0: <20} - {1}".format(k_loc, locations[k_loc]['desc']))
return