def setup_terraform(version, environment, aws_region, terraform_bin_path, s3_bucket_name): print(f"deploy test to region {aws_region}") tf = Terraform(working_dir=full_path, terraform_bin_path=terraform_bin_path) var_tf = { "module_version": version, "tiingo_api_key": os.environ.get("TIINGO_API_KEY"), "aws_region": aws_region, "environment": environment, "s3_bucket_name": s3_bucket_name, } tf.init() ret_code, out, err = tf.apply(skip_plan=True, var=var_tf) if ret_code != 0: print(err) ret_code, out, err = tf.destroy(var=var_tf) raise Exception("Error applying terraform. Error \n {}".format(err)) yield ret_code, out, err = tf.destroy(var=var_tf) if ret_code != 0: print(err) raise Exception("Error detroying terraform. Error \n {}".format(err))
def deploy(self): os.makedirs('.overcloud', exist_ok=True) with open('.overcloud/plan.tf.json', 'w') as planfile: json.dump(self.tf.to_dict(), planfile, indent=2) cli = TfCli(working_dir='.overcloud') cli.init(capture_output=False) cli.apply(capture_output=False)
def test_destroy(self): tf = Terraform(working_dir=current_path, variables={"test_var": "test"}) tf.init("var_to_output") ret, out, err = tf.destroy("var_to_output") assert ret == 0 assert "Destroy complete! Resources: 0 destroyed." in out
class TerraformProvider(): def __init__(self, configuration, terraform_workspace): log.info("Preparing terraform deployment") log.debug("Using workspace: {}".format(terraform_workspace)) self._backend_provider = get_backend_provider(configuration, terraform_workspace) self._controller = Terraform( working_dir=terraform_workspace, variables=configuration["terraform"]["parameters"]) self._controller.init(capture_output=False, force_copy=IsFlagged) @stage("Terraform deploy") def deploy(self): log.info("Deploying terraform infrastructure") self._backend_provider.init_remote_backend() self._controller.apply(capture_output=False, skip_plan=True) output = self._controller.output() artifact.create("terraform_output", content=json.dumps(output)) def destroy(self): log.info("Destroying terraform infrastructure") self._controller.destroy(capture_output=False) self._backend_provider.remove_remote_backend()
def execute_terraform(working_dir): """Execute terraform code to setup cloud resources, including servers, networks and so on. Arguments: working_dir {string} -- The path of terraform working direcory. Returns: string -- return_code, stdout, stderr. """ tf = Terraform(working_dir=working_dir) hiden_dir = path.realpath(path.join(working_dir, ".terraform")) # Init terraform env. if not path.isdir(hiden_dir): tf.init() # Run terraform plan. plan_return_code, plan_stdout, plan_stderr = tf.plan() # If there is no error, the stderr returned is unicode instead of a None type. So have to # Check the length of the stderr. if len(plan_stderr) == 0: print(plan_stdout) input_str = raw_input("Do you want to perform these actions? Only 'yes' will be accepted to approve.\n Enter a value:") if input_str == "yes": print("Start setting up cloud resources ...") return tf.apply(skip_plan=True) else: print("Apply cancelled.") sys.exit() else: print(plan_stderr) sys.exit()
class Terraform: def __init__(self, init): from python_terraform import Terraform as PythonTerraform self.terraform = PythonTerraform(working_dir='terraform') Path(self.working_dir).mkdir(exist_ok=True) if init: return_code, _, err = self.terraform.init( dir_or_plan=self.working_dir) if return_code != 0: raise CwsCommandError(err) @property def working_dir(self): return self.terraform.working_dir def init(self): return_code, _, err = self.terraform.init() if return_code != 0: raise CwsCommandError(err) def apply(self, workspace, targets): self.select_workspace(workspace) return_code, _, err = self.terraform.apply(target=targets, skip_plan=True, input=False, raise_on_error=False, parallelism=1) if return_code != 0: raise CwsCommandError(err) def destroy(self, workspace, targets): self.select_workspace(workspace) return_code, _, err = self.terraform.destroy(target=targets) if return_code != 0: raise CwsCommandError(err) def output(self): self.select_workspace("default") values = self.terraform.output(capture_output=True) return {key: value['value'] for key, value in values.items()} if values else "{}" def workspace_list(self): self.select_workspace("default") return_code, out, err = self.terraform.cmd('workspace', 'list') if return_code != 0: raise CwsCommandError(err) values = out[1:].translate(str.maketrans('', '', ' \t\r')).split('\n') return filter(None, values) def select_workspace(self, workspace): return_code, out, err = self.terraform.workspace('select', workspace) if workspace != 'default' and return_code != 0: _, out, err = self.terraform.workspace('new', workspace, raise_on_error=True) if not (Path(self.working_dir) / '.terraform').exists(): self.terraform.init(input=False, raise_on_error=True)
def wrapper(workspace_name, create=True, delete=True, *args, **kwargs): tf = Terraform(working_dir=current_path) tf.init() if create: tf.create_workspace(workspace_name, *args, **kwargs) yield tf if delete: tf.set_workspace("default") tf.delete_workspace(workspace_name)
def test_plan(self, plan, variables, expected_ret): tf = Terraform(working_dir=current_path, variables=variables) tf.init(plan) with pytest.raises(TerraformCommandError) as e: tf.plan(plan) assert ( e.value.err == """\nError: Missing required argument\n\nThe argument "region" is required, but was not set.\n\n""" )
def run_terraform(directory, terraform_vars, target_module): terraform = Terraform(directory) terraform.init(from_module=target_module) with open(directory + "terraform.tfvars.json", "w") as fh_: fh_.write(json.dumps(terraform_vars)) ret_code, stdout, stderr = (terraform.apply(auto_approve=True, capture_output=False, raise_on_error=True))
def test_apply(self, folder, variables, var_files, expected_output, options): tf = Terraform(working_dir=current_path, variables=variables, var_file=var_files) tf.init(folder) ret, out, err = tf.apply(folder, **options) assert ret == 0 assert expected_output in out.replace("\n", "").replace(" ", "") assert err == ""
def test_override_default(self, folder, variables): tf = Terraform(working_dir=current_path, variables=variables) tf.init(folder) ret, out, err = tf.apply( folder, var={"test_var": "test2"}, no_color=IsNotFlagged, ) out = out.replace("\n", "") assert "\x1b[0m\x1b[1m\x1b[32mApply" in out out = tf.output("test_output") assert "test2" in out
def test_apply_with_var_file(self, caplog: LogCaptureFixture): with caplog.at_level(logging.INFO): tf = Terraform(working_dir=current_path) folder = "var_to_output" tf.init(folder) tf.apply( folder, var_file=os.path.join(current_path, "tfvar_files", "test.tfvars"), ) for log in caplog.messages: if log.startswith("Command: terraform apply"): assert log.count("-var-file=") == 1
def test_output(self, caplog: LogCaptureFixture, output_all: bool): expected_value = "test" required_output = "test_output" with caplog.at_level(logging.INFO): tf = Terraform(working_dir=current_path, variables={"test_var": expected_value}) tf.init("var_to_output") tf.apply("var_to_output") params = tuple() if output_all else (required_output, ) result = tf.output(*params) if output_all: assert result[required_output]["value"] == expected_value else: assert result == expected_value assert expected_value in caplog.messages[-1]
def vm_docker_deploy_old(config): # This script should deploy the instance and return the output/logs after the test has finished file_dir = os.path.dirname(os.path.realpath(__file__)) provider = config["params"]["Provider"][0] ### Check that a selection was made if config["selection"]["instance"] is None: config["logs"] = None return config ### Setup terraform objects instance_wkdir = file_dir + "/instance_deploy/" + provider instance_tf = Terraform(working_dir=instance_wkdir) docker_tf = Terraform(file_dir + "/docker_deploy") tfstate_path = config["base_dir"] + '/tf_states/' + str(config["job_id"]) tfvars = config["base_dir"] + "/tfvars.tfvars" ## ALSO DIRECT TO A VARS.TF IN THE BASE_DIR instance_tf.init( backend_config={'path': tfstate_path + '/terraform.tfstate'}) instance_tf.apply(var_file=tfvars, lock=False, var={'instance_type': config["selection"]["instance"]}, skip_plan=True) docker_tf.init(backend_config={ 'path': tfstate_path + '/docker_tfstate/terraform.tfstate' }) docker_tf.apply(var_file=tfvars, lock=False, var={'tfstate_path': tfstate_path}, skip_plan=True) docker_tf.init(backend_config={ 'path': tfstate_path + '/docker_tfstate/terraform.tfstate' }) logs = docker_tf.output() config["logs"] = logs docker_tf.init(backend_config={ 'path': tfstate_path + '/docker_tfstate/terraform.tfstate' }) docker_tf.destroy(auto_approve=True) instance_tf.init( backend_config={'path': tfstate_path + '/terraform.tfstate'}) instance_tf.destroy(auto_approve=True) return config
def terraform_init(self, init_spec): name = init_spec['name'] wd = init_spec['wd'] tf = Terraform(working_dir=wd) return_code, stdout, stderr = tf.init() error = self.check_output(name, return_code, stdout, stderr) if error: return name, None return name, tf
def terraform_init(backend_prefix, terraform_state_bucket, tf: Terraform): return_code, stdout, stderr = tf.init(capture_output=True, backend_config={ 'bucket': terraform_state_bucket, 'prefix': backend_prefix }) logger.debug('Terraform init return code is {}'.format(return_code)) logger.debug('Terraform init stdout is {}'.format(stdout)) logger.debug('Terraform init stderr is {}'.format(stderr))
def build_controller(): bucket_backend = 'bucket=' + str(s3_bucket) table_backend = 'dynamodb_table=' + str(s3_bucket) backend_configs = [bucket_backend, table_backend] tf_base = Terraform(workdir_ctrl) tf_base.init(backend_config=backend_configs) #tf_base.plan(capture_output=False, var_file=tfvars) return_code = tf_base.apply(skip_plan=True, capture_output=False, var_file=tfvars) controller_outputs = tf_base.output(capture_output=True) if return_code[0] == 1: print("Something went wrong!") sys.exit(1) else: print("All good!") pass return controller_outputs
def terraform_init(self, init_spec): name = init_spec['name'] wd = init_spec['wd'] tf = Terraform(working_dir=wd) return_code, stdout, stderr = tf.init() error = self.check_output(name, 'init', return_code, stdout, stderr) if error: raise TerraformCommandError( return_code, 'init', out=stdout, err=stderr) return name, tf
def vm_provision(config): print( f"Provisioning {config['selection']['instance']} instance from {config['params']['Provider']}" ) file_dir = os.path.dirname(os.path.realpath(__file__)) provider = config["params"]["Provider"][0] ### Check that a selection was made if config["selection"]["instance"] is None: config["logs"] = None return config ### Setup terraform objects instance_wkdir = file_dir + "/instance_deploy/" + provider instance_tf = Terraform(working_dir=instance_wkdir) tfstate_path = config["base_dir"] + '/tf_states/' + str(config["job_id"]) tfvars = config["base_dir"] + "/tfvars.tfvars" instance_tf.init( backend_config={'path': tfstate_path + '/terraform.tfstate'}) signal.signal(signal.SIGINT, partial(keyboard_interrupt_handler, instance_tf, config)) apply = instance_tf.apply( var_file=tfvars, lock=False, var={'instance_type': config["selection"]["instance"]}, skip_plan=True) print(apply) find = re.search(r"docker_host_ip = [\d.]+", apply[1]) instance_ip = apply[1][(find.regs[0][0] + 17):find.regs[0][1]] # instance_tf.init(backend_config={'path':tfstate_path + '/terraform.tfstate'}) # instance_ip = instance_tf.output()["docker_host_ip"]["value"] print( f"{config['selection']['instance']} instance created at {instance_ip}") return config, instance_tf, instance_ip
def create_terraform_stack(cluster_name, tf_vars, dir_path, state_path): hostname = re.sub(r"[^a-zA-Z0-9]+", '-', cluster_name).lower() tf_vars['hostname'] = hostname state_file = "{}/{}.tfstate".format(state_path, tf_vars['cluster_uuid']) tf_vars_file = create_tf_vars_file(state_path, tf_vars) tf = Terraform(dir_path) return_code, stdout, stderr = tf.get(capture_output=False) return_code, stdout, stderr = tf.init(capture_output=False) return_code, stdout, stderr = tf.apply(var_file=tf_vars_file, skip_plan=True, auto_approve=IsFlagged, capture_output=False, state=state_file) return return_code, stdout, stderr
def delete_terraform_stack(cluster_uuid, project_id, dir_path, state_path, proejct_deleted): state_file = "{}/{}.tfstate".format(state_path, cluster_uuid) tf_vars_file = "{}/vars.tf".format(state_path) tf = Terraform(dir_path) return_code, stdout, stderr = tf.get(capture_output=False) return_code, stdout, stderr = tf.init(capture_output=False) return_code, stdout, stderr = tf.destroy(var_file=tf_vars_file, auto_approve=IsFlagged, capture_output=False, state=state_file) shutil.rmtree(state_path) if proejct_deleted: shutil.rmtree("{}/{}".format(dir_path, project_id)) return return_code, stdout, stderr
def test_cmd( self, method: Callable[..., str], expected_output: str, expected_ret_code: int, expected_exception: bool, expected_logs: str, caplog: LogCaptureFixture, folder: str, ): with caplog.at_level(logging.INFO): tf = Terraform(working_dir=current_path) tf.init(folder) try: ret, out, _ = method(tf) assert not expected_exception except TerraformCommandError as e: assert expected_exception ret = e.returncode out = e.out assert expected_output in out assert expected_ret_code == ret assert expected_logs in caplog.text
def plan(): tf = Terraform(working_dir='./terraform') parser = SafeConfigParser() config = os.path.expanduser('~/.aws/config') parser.read(config) if not parser.has_section('profile pollexy'): print "You need to run 'pollexy credentials configure'" return region = parser.get('profile pollexy', 'region') print 'Initializing environment . . . ' + region code, stdout, stderr = tf.init() print stderr print stdout print 'Planning environment . . . ' code, stdout, stderr = tf.plan(var={'aws_region': region}) if (stderr): print stderr else: print stdout
__content = get_terraform_init_file_contents(logger=logger) print(__content, file=fOut) except Exception as ex: was_exception = True extype, ex, tb = sys.exc_info() logger.exception('EXCEPTION -> {}'.format(__terraform_main_tf), ex) finally: if (was_exception): logger.info('terraform init file EXCEPTION!') else: logger.info('terraform init saved -> "{}"'.format(__terraform_main_tf)) logger.info('END!!! terraform init.') else: logger.info('BEGIN: terraform dry-run (performing diagnostics but does not make any changes to any resources).') tf = Terraform(working_dir=terraform_root) resp = tf.init(backend=False) logger.info('BEGIN: Reading "{}".'.format(__docker_compose_location)) docker_compose_data = load_docker_compose(__docker_compose_location, logger=logger) if (is_json): __json = json.dumps(docker_compose_data, indent=3) save_docker_compose_data(__docker_compose_location, __json) logger.info('END!!! Reading "{}".'.format(__docker_compose_location)) was_exception = False try: with open(__terraform_main_tf, 'w') as fOut: __content = get_terraform_file_contents(docker_compose_data, do_init=False, aws_ecs_cluster_name=__aws_ecs_cluster_name, aws_ecs_repo_name=__aws_ecs_repo_name, docker_compose_location=__docker_compose_location, aws_creds=aws_creds, aws_config=aws_config, aws_creds_src=__aws_creds_src__, aws_config_src=__aws_config_src__, aws_default_region=__aws_default_region__, aws_cli_ecr_describe_repos=__aws_cli_ecr_describe_repos__, aws_ecs_compute_engine=__aws_ecs_compute_engine) #_content = pretty_json(__content) # this does not work, at this time. print(__content, file=fOut) except Exception as ex:
def remotestate(): tf_state = Terraform(remotestate_dic) tf_state.init() #tf_state.plan(capture_output=False, var_file=tfvars) tf_state.apply(skip_plan=True, capture_output=False, var_file=tfvars) state_output = tf_state.output(capture_output=True)
#!python from python_terraform import Terraform, IsFlagged tf_base = Terraform(working_dir='iac/layer-base') tf_users = Terraform(working_dir='iac/layer-users') tf_base.init(capture_output=False) tf_users.init(capture_output=False)
def init(stackname, context): working_dir = join(TERRAFORM_DIR, stackname) # ll: ./.cfn/terraform/project--prod/ terraform = Terraform(working_dir=working_dir) with _open(stackname, 'backend', mode='w') as fp: fp.write(json.dumps({ 'terraform': { 'backend': { 's3': { 'bucket': BUILDER_BUCKET, 'key': 'terraform/%s.tfstate' % stackname, 'region': BUILDER_REGION, }, }, }, })) with _open(stackname, 'providers', mode='w') as fp: # TODO: possibly remove unused providers # Terraform already prunes them when running, but would # simplify the .cfn/terraform/$stackname/ files # TODO: use TerraformTemplate? providers = { 'provider': { 'fastly': { # exact version constraint 'version': "= %s" % PROVIDER_FASTLY_VERSION, 'api_key': "${data.%s.%s.data[\"api_key\"]}" % (DATA_TYPE_VAULT_GENERIC_SECRET, DATA_NAME_VAULT_FASTLY_API_KEY), }, 'aws': { 'version': "= %s" % '2.3.0', 'region': context['aws']['region'], }, 'google': { 'version': "= %s" % '1.20.0', 'region': 'us-east4', 'credentials': "${data.%s.%s.data[\"credentials\"]}" % (DATA_TYPE_VAULT_GENERIC_SECRET, DATA_NAME_VAULT_GCP_API_KEY), }, 'vault': { 'address': context['vault']['address'], # exact version constraint 'version': "= %s" % PROVIDER_VAULT_VERSION, }, }, 'data': { DATA_TYPE_VAULT_GENERIC_SECRET: { # TODO: this should not be used unless Fastly is involved DATA_NAME_VAULT_FASTLY_API_KEY: { 'path': VAULT_PATH_FASTLY, }, # TODO: this should not be used unless GCP is involved DATA_NAME_VAULT_GCP_API_KEY: { 'path': VAULT_PATH_GCP, }, }, }, } if context.get('eks'): providers['provider']['kubernetes'] = { 'version': "= %s" % '1.5.2', 'host': '${data.aws_eks_cluster.main.endpoint}', 'cluster_ca_certificate': '${base64decode(data.aws_eks_cluster.main.certificate_authority.0.data)}', 'token': '${data.aws_eks_cluster_auth.main.token}', 'load_config_file': False, } providers['data']['aws_eks_cluster'] = { 'main': { 'name': '${aws_eks_cluster.main.name}', }, } providers['data']['aws_eks_cluster_auth'] = { 'main': { 'name': '${aws_eks_cluster.main.name}', }, } if context['eks']['helm']: providers['provider']['helm'] = { 'version': '= 0.9.0', 'service_account': '${kubernetes_cluster_role_binding.tiller.subject.0.name}', 'kubernetes': { 'host': '${data.aws_eks_cluster.main.endpoint}', 'cluster_ca_certificate': '${base64decode(data.aws_eks_cluster.main.certificate_authority.0.data)}', 'token': '${data.aws_eks_cluster_auth.main.token}', 'load_config_file': False, }, } fp.write(json.dumps(providers)) terraform.init(input=False, capture_output=False, raise_on_error=True) return terraform
parser = argparse.ArgumentParser() action = parser.add_mutually_exclusive_group() action.add_argument("--apply", action="store_true") action.add_argument("--destroy", action="store_true") parser.add_argument("--workdir") parser.add_argument("--tfvars") parser.add_argument("--lambdas") return parser.parse_args() if __name__ == "__main__": args = parse_args() tf = Terraform(working_dir=args.workdir) tf.init() if args.apply: for lf in args.lambdas.split(","): name = lf.split("/")[-1].split(".")[0] zip_lambda_function(lf, f"{args.workdir}/{name}.zip") tf.apply( no_color=IsFlagged, refresh=False, var_file=args.tfvars, skip_plan=True, capture_output=False, ) elif args.destroy:
def init_terraform(dir='.', backend_config=''): tf = Terraform(working_dir=dir) tf.init(capture_output=False, backend_config=backend_config) return tf
class Terraform: DEFAULT_DOCKER_HOST = 'unix:///var/run/docker.sock' DEFAULT_DOCKER_ENTRYPOINT_PATH = '/docker-entrypoint.sh' DEFAULT_NGINX_DOCKER_ENTRYPOINT_PATH = '/nginx.docker-entrypoint.sh' DEFAULT_NGINX_DOCKER_IMAGE = 'nginx:stable-alpine' DEFAULT_NGINX_DOCKER_CONTAINER_HTML_PATH = '/usr/share/nginx/html' DEFAULT_UPLOAD_PATH = f"$HOME/.{PROJECT_NAME}/" DEFAULT_SSH_USER = '******' DEFAULT_SSH_PORT = 22 TERRAFORM_RESOURCE_FILE = 'file' # trick for terrascript class null_resource(Resource): ... class vultr(Provider): ... class vultr_server(Resource): ... class vultr_ssh_key(Resource): ... def __init__(self): self.work_dir = TERRAFORM_WORK_DIR self.app = TF(working_dir=self.work_dir) @contextlib.contextmanager def terraform_workspace(self): workspace = f"terraform_workspace_{int(time.time())}" self.app.create_workspace(workspace) tmp_dir = TemporaryDirectory() yield tmp_dir.name self.app.set_workspace('default') self.app.cmd('workspace delete', workspace, force=IsFlagged) @contextlib.contextmanager def patch_terraform_docker_ssh_conn(self): # patch ssh config yield # clear ssh config def write_terraform_config(self, config: Terrascript, dir_path: str): tmp_config_file = NamedTemporaryFile(mode='wt', suffix='.tf.json', dir=dir_path, delete=False) tmp_config_file.write(str(config)) tmp_config_file.seek(0) self.app.init( dir_path, plugin_dir=f"{self.work_dir}/plugins", ) return tmp_config_file def run_terraform_plan(self, config: Terrascript): with self.terraform_workspace() as tw_dir: self.write_terraform_config(config, tw_dir) plan = self.app.plan(tw_dir, no_color=IsFlagged) return plan def run_terraform_apply(self, config: Terrascript): with self.terraform_workspace() as tw_dir: self.write_terraform_config(config, tw_dir) print(config) self.app.apply(tw_dir, skip_plan=True, no_color=IsFlagged) output_result = self.app.output(json=IsFlagged, no_color=IsFlagged) print(output_result) output_var = { output_var_key: output_result[output_var_key]['value'] for output_var_key in output_result } return output_var def run_terraform_destroy(self, config: Terrascript): with self.terraform_workspace() as tw_dir: self.write_terraform_config(config, tw_dir) destroy_result = self.app.destroy(tw_dir) return destroy_result @classmethod def gen_digital_ocean_config(cls, config_data: dict, token: str, public_key: str = None): do_config = Terrascript() do_provider = provider.digitalocean(token=token) do_droplet_resource = resource.digitalocean_droplet( f"server", image=config_data['os_code'], name=config_data['hostname'], region=config_data['region_code'], size=config_data['plan_code'], ssh_keys=config_data['ssh_keys'] if config_data.get('ssh_keys') else []) if public_key: digitalocean_ssh_key = resource.digitalocean_ssh_key( "digitalocean_ssh_key", name="default", public_key=public_key, ) do_droplet_resource['ssh_keys'] += [ "${digitalocean_ssh_key.digitalocean_ssh_key.id}" ] do_config += digitalocean_ssh_key do_output_ip = Output( 'ip', value="${digitalocean_droplet.server.ipv4_address}") do_output_id = Output('server_id', value="${digitalocean_droplet.server.id}") do_config += do_provider do_config += do_droplet_resource do_config += do_output_ip do_config += do_output_id return do_config @classmethod def gen_vultr_config(cls, config_data: dict, token: str, public_key: str = None): vultr_config = Terrascript() vultr_provider = cls.vultr(api_key=token, rate_limit=700, retry_limit=3) vultr_server = cls.vultr_server(f"server", plan_id=config_data['plan_code'], region_id=config_data['region_code'], os_id=config_data['os_code'], hostname=config_data['hostname'], ssh_key_ids=config_data['ssh_keys'] if config_data.get('ssh_keys') else []) vultr_output_ip = Output('ip', value="${vultr_server.server.main_ip}") vultr_output_id = Output('server_id', value="${vultr_server.server.id}") if public_key: vultr_ssh_key = cls.vultr_ssh_key('vultr_ssh_key', name='default_key', ssh_key=public_key) vultr_server["ssh_key_ids"] += [ "${vultr_ssh_key.vultr_ssh_key.id}" ] vultr_config += vultr_ssh_key vultr_config += vultr_provider vultr_config += vultr_server vultr_config += vultr_output_ip vultr_config += vultr_output_id return vultr_config @classmethod def add_ssh_key_config(cls, public_key: str): return provisioner("remote-exec", provisioner=provisioner( "remote-exec", inline=[ 'mkdir -p ~/.ssh', f"{public_key} >> ~/.ssh/authorized_keys" ], )) @classmethod def gen_ssh_conn_config(cls, *, ssh_user: str = DEFAULT_SSH_USER, ssh_private_key: str, ssh_host: str, ssh_port: int = DEFAULT_SSH_PORT) -> dict: # see more in https://www.terraform.io/docs/provisioners/connection.html return { 'type': 'ssh', 'user': ssh_user, 'private_key': ssh_private_key, 'host': ssh_host, 'port': ssh_port, 'timeout': '30s' } @classmethod def gen_site_docker_deploy_config(cls, *, docker_host: str = DEFAULT_DOCKER_HOST, site_name: str = None, template_tar_bytes: bytes = None, script: str = None, ssh_user: str = DEFAULT_SSH_USER, ssh_private_key: str, ssh_host: str, ssh_port: int = DEFAULT_SSH_PORT): config = Terrascript() docker_provider = provider.docker(host=docker_host, connection=cls.gen_ssh_conn_config( ssh_user=ssh_user, ssh_private_key=ssh_private_key, ssh_host=ssh_host, ssh_port=ssh_port)) docker_image_resource = resource.docker_image( 'nginx_image', name=cls.DEFAULT_NGINX_DOCKER_IMAGE, ) docker_container_resource = resource.docker_container( 'nginx_container', name=f"{site_name}-container-${{random_pet.docker_pet_name.id}}", image="${docker_image.nginx_image.latest}", restart="always", start=True, ports={'internal': 80}, upload=[]) docker_name_resource = resource.random_pet( 'docker_pet_name', length=1, ) if template_tar_bytes: template_tar_file = f"{site_name}-tar-${{random_pet.docker_pet_name.id}}.tar.gz", template_tar_file_content = base64.b64encode( template_tar_bytes).decode('utf8') template_tar_path = f"{cls.DEFAULT_NGINX_DOCKER_CONTAINER_HTML_PATH}/${template_tar_file}" # self.upload_file( # content='conf/myapp.conf', # destination=f"{self.DEFAULT_UPLOAD_PATH}/${template_tar_file}", # ssh_user=ssh_user, # ssh_private_key=ssh_private_key, # ssh_host=ssh_host, # ssh_port=ssh_port # ) docker_container_resource['upload'].append({ 'content_base64': template_tar_file_content, 'file': template_tar_path }) if script: entrypoint_sh_content = TemplateRender().render( cls.DEFAULT_NGINX_DOCKER_ENTRYPOINT_PATH, init_script_path=cls.DEFAULT_DOCKER_ENTRYPOINT_PATH, html_path=cls.DEFAULT_NGINX_DOCKER_CONTAINER_HTML_PATH) docker_container_resource['upload'].append({ 'content': entrypoint_sh_content, 'file': cls.DEFAULT_DOCKER_ENTRYPOINT_PATH }) config += docker_provider config += docker_image_resource config += docker_container_resource config += docker_name_resource return config def remote_exec(self, *, ssh_user: str = DEFAULT_SSH_USER, ssh_private_key: str, ssh_host: str, ssh_port: int = DEFAULT_SSH_PORT): exec_config = Terrascript() ssh_conn = self.gen_ssh_conn_config(ssh_user=ssh_user, ssh_private_key=ssh_private_key, ssh_host=ssh_host, ssh_port=ssh_port) exec_resource = self.null_resource('remote-exec', provisioner=provisioner( "remote-exec", inline=['ls -la'], connection=ssh_conn)) exec_config += exec_resource return exec_config def upload_file(self, content: str, *, destination: str = DEFAULT_UPLOAD_PATH, ssh_user: str = DEFAULT_SSH_USER, ssh_private_key: str, ssh_host: str, ssh_port: int = DEFAULT_SSH_PORT): upload_config = Terrascript() ssh_conn = self.gen_ssh_conn_config(ssh_user=ssh_user, ssh_private_key=ssh_private_key, ssh_host=ssh_host, ssh_port=ssh_port) file_resource = self.null_resource('upload_file_resource', provisioner=provisioner( self.TERRAFORM_RESOURCE_FILE, content=content, destination=destination, connection=ssh_conn)) upload_config += file_resource return upload_config
def init(pathtoPlatform): terra = Terraform(pathtoPlatform) return terra.init()