예제 #1
0
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))
예제 #2
0
 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)
예제 #3
0
 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
예제 #4
0
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()
예제 #5
0
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()
예제 #6
0
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)
예제 #7
0
 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)
예제 #8
0
 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"""
     )
예제 #9
0
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))
예제 #10
0
 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 == ""
예제 #11
0
 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
예제 #12
0
    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
예제 #13
0
 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]
예제 #14
0
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
예제 #16
0
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))
예제 #17
0
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
예제 #18
0
 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
예제 #19
0
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
예제 #20
0
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
예제 #21
0
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
예제 #22
0
    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
예제 #23
0
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:
예제 #25
0
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)
예제 #26
0
#!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)
예제 #27
0
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
예제 #28
0
    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:
예제 #29
0
def init_terraform(dir='.', backend_config=''):
    tf = Terraform(working_dir=dir)
    tf.init(capture_output=False, backend_config=backend_config)
    return tf
예제 #30
0
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
예제 #31
0
def init(pathtoPlatform):

    terra = Terraform(pathtoPlatform)
    return terra.init()