""" Command for automatic deployments using ansible playbooks """
import logging
import os
from typing import Optional, List
import jinja2
import typer
from fastiot.cli.constants import DEPLOYMENTS_CONFIG_DIR
from fastiot.cli.helper_fn import get_jinja_env
from fastiot.cli.model.project import ProjectContext
from fastiot.cli.typer_app import DEFAULT_CONTEXT_SETTINGS, app
def _deployment_completion(*_) -> List[str]:
return ProjectContext.default.deployment_names
[docs]@app.command(context_settings=DEFAULT_CONTEXT_SETTINGS)
def deploy(deployments: Optional[List[str]] = typer.Argument(default=None, shell_complete=_deployment_completion,
help="Select the deployments to deploy"),
ask_pass: bool = typer.Option(False, '--ask-pass', '-k', help="Use password instead of SSH publickey to connect to targets"),
dry: bool = typer.Option(False, help="Only create playbooks but do not run actual deployment.")):
"""
This command handles deployment of a config using an Ansible Playbook if you have configured any deployments based
on :class:`fastiot.cli.model.deployment.DeploymentTargetSetup` in your :file:`deployment.yaml`. """
context = ProjectContext.default
if not deployments:
deployments = []
for deployment_name in deployments:
if deployment_name not in context.deployment_names:
typer.echo(f"Deployment {deployment_name} not in project deployments.")
raise typer.Exit(code=1)
for deployment_name in deployments:
deployment = context.deployment_by_name(deployment_name)
if deployment.deployment_target is None:
typer.echo(f"Deployment {deployment_name} does not have a deployment_target configured.")
raise typer.Exit(code=2)
_write_ansible_playbook(deployment_name, deployment)
if not dry:
ask_pass = "-k" if ask_pass else ""
os.system(f"cd {context.build_dir}/{DEPLOYMENTS_CONFIG_DIR}/{deployment_name};"
f"ansible-playbook {ask_pass} -i hosts --diff ansible-playbook.yaml")
def _write_ansible_playbook(deployment_name, deployment):
""" Creates the actual playbook and inventory files"""
context = ProjectContext.default
output_dir = os.path.join(context.project_root_dir, context.build_dir, DEPLOYMENTS_CONFIG_DIR,
deployment_name)
os.makedirs(output_dir, exist_ok=True)
try:
jinja_env = get_jinja_env()
playbook_template = jinja_env.get_template('ansible-playbook.yaml.j2')
playbook_document = playbook_template.render(deployment_name=deployment_name,
deployment_target=deployment.deployment_target)
with open(os.path.join(output_dir, "ansible-playbook.yaml"), "w") as playbook_file:
playbook_file.write(playbook_document)
jinja_env = get_jinja_env()
inventory_template = jinja_env.get_template('ansible_hosts.j2')
inventory_document = inventory_template.render(deployment_target=deployment.deployment_target)
with open(os.path.join(output_dir, "hosts"), "w") as inventory_file:
inventory_file.write(inventory_document)
except jinja2.exceptions.UndefinedError:
logging.exception('Exception raised during transpiling of playbook %s', deployment_name)