cli.py 5.84 KB
Newer Older
borgespires's avatar
borgespires committed
1
2
import click
import os.path
3
from os import symlink
borgespires's avatar
borgespires committed
4
import subprocess
5
from glob import glob
borgespires's avatar
borgespires committed
6
7
8

SCRIPT_DIR = os.path.dirname(__file__)

9
ANSIBLE = os.path.join(SCRIPT_DIR, 'ansible')
borgespires's avatar
borgespires committed
10
11
12
REQUIREMENTS = 'roles/roles-on-droplet/roles-from-ansible-galaxy'
CONFIGURATION = 'group_vars'
CONFIGURATION_FILE = 'all'
13
14
15
INVENTORY = 'inventory'
INVENTORY_DROPLETS_LINK = 'ansible-droplets'
INVENTORY_DROPLETS_FILE = '.ansible-droplet-inventory'
borgespires's avatar
borgespires committed
16
17
18
CREATE_PLAYBOOK = 'create-droplet-playbook.yml'
DESTROY_PLAYBOOK = 'delete-droplet-playbook.yml'

19
DEFAULT_SSH_KEY = '~/.ssh/id_rsa.pub'
borgespires's avatar
borgespires committed
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37

def _install_requirements_if_needed():
    if not _are_requirements_installed():
        _install_requirements()

def _are_requirements_installed():
    return os.path.exists(os.path.join(ANSIBLE, REQUIREMENTS))

def _install_requirements():
    cmd = [
        'ansible-galaxy', 'install',
        '-r', './requirements.yml',
        '-p', '%s' % os.path.join("./", REQUIREMENTS)
    ]
    _run(cmd)

def _set_configuration_if_needed():
    if not _is_configured():
Florian Kempenich's avatar
Florian Kempenich committed
38
        ssh_key_path, ssh_key_name, do_token_path, user_username, user_default_pass = _ask_for_configuration()
39
        _create_configuration_file(ssh_key_path, ssh_key_name, do_token_path, user_username, user_default_pass)
borgespires's avatar
borgespires committed
40

41
42
43
    if not _has_droplet_inventory_symlink():
        _create_droplet_inventory_symlink()

borgespires's avatar
borgespires committed
44
45
46
47
def _is_configured():
    return os.path.isfile(os.path.join(ANSIBLE, CONFIGURATION, CONFIGURATION_FILE))

def _ask_for_configuration():
48
49
50
51
52
53
54
55
56
    # TODO Remove defaults (or put as constants)
    ssh_key_path      = click.prompt('[SSH PUBLIC KEY] - Path?', type=str, default=DEFAULT_SSH_KEY)
    ssh_key_name      = click.prompt('[SSH PUBLIC KEY] - Name on DigitalOcean?', type=str, default="Main SSH Key")
    do_token_path     = click.prompt('[Digital Ocean Token] - Path?', type=str, default="~/config-in-the-cloud/secrets/digitalocean/token")
    user_username     = click.prompt('[User on Droplet] - Username?', type=str, default="bonjour")
    user_default_pass = click.prompt('[User on Droplet] - Default Password?', type=str, default="pass")
    # user_username     = click.prompt('[User on Droplet] - Username?', type=str)
    # user_default_pass = click.prompt('[User on Droplet] - Default Password?', type=str)
    return ssh_key_path, ssh_key_name, do_token_path, user_username, user_default_pass
borgespires's avatar
borgespires committed
57

58
def _create_configuration_file(ssh_key_path, ssh_key_name, do_token_path, user_username, user_default_pass):
borgespires's avatar
borgespires committed
59
    if not os.path.exists(os.path.join(ANSIBLE, CONFIGURATION)):
Florian Kempenich's avatar
Fix bug  
Florian Kempenich committed
60
        os.makedirs(os.path.join(ANSIBLE, CONFIGURATION))
borgespires's avatar
borgespires committed
61

62
63
64
65
66
67
68
69
    def lookup_file_format(path):
        # Regarding the curly braces:
        # Expected result '{{'
        # But python interprets '{' in strings, so need to escape '{' with '{{'
        # since expected result is '{{', we need to escape twice, hence '{{{{'
        ansible_compatible_path = os.path.expanduser(path)
        return "{{{{ lookup('file', '{0}') }}}}".format(ansible_compatible_path)

70
    with open(os.path.join(ANSIBLE, CONFIGURATION, CONFIGURATION_FILE), "w+") as file:
71
72
73
74
75
        file.write('ssh_pub_key_name_on_digitalocean: "{0}"\n'.format(ssh_key_name))
        file.write('ssh_pub_key_to_load_on_droplet: "{0}"\n'.format(lookup_file_format(ssh_key_path)))
        file.write('do_token: "{0}"\n'.format(lookup_file_format(do_token_path)))
        file.write('user_to_create_username: "{0}"\n'.format(user_username))
        file.write('user_to_create_default_password: "{0}"\n'.format(user_default_pass))
borgespires's avatar
borgespires committed
76

77
def _has_droplet_inventory_symlink():
Florian Kempenich's avatar
Florian Kempenich committed
78
79
80
81
82
    inventory_link = os.path.join(ANSIBLE, INVENTORY, INVENTORY_DROPLETS_LINK)
    inventory_file = os.path.join(os.path.expanduser("~"), INVENTORY_DROPLETS_FILE)

    return (os.path.islink(inventory_link)
            and os.path.isfile(inventory_file))
83
84
85
86
87
88
89

def _create_droplet_inventory_symlink():
    inventory_link = os.path.join(ANSIBLE, INVENTORY, INVENTORY_DROPLETS_LINK)
    inventory_file = os.path.join(os.path.expanduser("~"), INVENTORY_DROPLETS_FILE)

    if not os.path.isfile(inventory_file):
        raise AssertionError(
Florian Kempenich's avatar
Florian Kempenich committed
90
            "\n\n" +
91
            "/!\\/!\\/!\\/!\\/!\\/!\\/!\\/!\\/!\\/!\\/!\\/!\\/!\\/!\\/!\\/!\\/!\\/!\\/!\\/!\\/!\\\n" +
92
            "Please ensure you didn't delete the generated inventory in the HOME dir\n" +
Florian Kempenich's avatar
Florian Kempenich committed
93
            "In case you did, just create a new empty file at '{0}'\n".format(inventory_file) +
94
            "/!\\/!\\/!\\/!\\/!\\/!\\/!\\/!\\/!\\/!\\/!\\/!\\/!\\/!\\/!\\/!\\/!\\/!\\/!\\/!\\/!\\\n" +
Florian Kempenich's avatar
Florian Kempenich committed
95
            "\n"
96
97
98
99
        )

    symlink(inventory_file, inventory_link)

borgespires's avatar
borgespires committed
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
def _delete_current_configuration():
    try:
        os.remove(os.path.join(ANSIBLE, CONFIGURATION, CONFIGURATION_FILE))
    except OSError:
        pass

def _create_droplet(name, droplet_spec):
    cmd = [
        'ansible-playbook',
        '%s' % os.path.join("./", CREATE_PLAYBOOK),
        '--extra-vars',
        'droplet_name=%s droplet_spec_name=%s' % (name, droplet_spec)
    ]
    _run(cmd)

def _destroy_droplet(name):
    cmd = [
        'ansible-playbook',
        '%s' % os.path.join("./", DESTROY_PLAYBOOK),
        '--extra-vars',
        'droplet_name=%s' % (name)
    ]
    _run(cmd)

def _run(cmd):
    subprocess.Popen(cmd, cwd=ANSIBLE).communicate()


#########################
##### Cli functions #####
#########################
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
def start():
    cli = click.Group()

    @cli.command('create')
    @click.argument('name', type=str)
    @click.argument('droplet-spec', type=click.Choice(['micro', 'mini', 'power']), default='micro')
    def create(name, droplet_spec):
        _set_configuration_if_needed()
        _install_requirements_if_needed()
        _create_droplet(name, droplet_spec)

    @cli.command('destroy')
    @click.argument('name', type=str)
    def destroy(name):
        _set_configuration_if_needed()
        _destroy_droplet(name)

    @cli.command('config')
    def config():
        _delete_current_configuration()
        _set_configuration_if_needed()

    cli()