Passed
Push — master ( 0fd795...73442f )
by Emmanuel
14:19
created

cli.mysql()   A

Complexity

Conditions 1

Size

Total Lines 18
Code Lines 8

Duplication

Lines 0
Ratio 0 %

Code Coverage

Tests 7
CRAP Score 1

Importance

Changes 0
Metric Value
cc 1
eloc 8
nop 3
dl 0
loc 18
ccs 7
cts 7
cp 1
crap 1
rs 10
c 0
b 0
f 0
1
"""
2
CLI Entry Point. From click, build stakkr and give all options to manage
3
services to be launched, stopped, etc.
4
"""
5
6 1
import sys
7 1
import click
8 1
from click_plugins import with_plugins
9 1
from pkg_resources import iter_entry_points
10 1
from stakkr import package_utils
11 1
from stakkr.docker_actions import get_running_containers_name
12
13
14 1
@with_plugins(iter_entry_points('stakkr.plugins'))
15 1
@click.group(help="""Main CLI Tool that easily create / maintain
16
a stack of services, for example for web development.
17
18
Read the configuration file and setup the required services by
19
linking and managing everything for you.""")
20 1
@click.version_option('3.7.1.1')
21 1
@click.option('--config', '-c', help='Change the configuration file')
22 1
@click.option('--debug/--no-debug', '-d', default=False)
23 1
@click.option('--verbose', '-v', is_flag=True)
24 1
@click.pass_context
25
def stakkr(ctx, config, debug, verbose):
26
    """click group, set context and main object"""
27
28 1
    from stakkr.actions import StakkrActions
29
30
    # Add the virtual env in the path
31 1
    venv_base = package_utils.get_venv_basedir()
32 1
    sys.path.append(venv_base)
33
34 1
    ctx.obj['CONFIG'] = config
35 1
    ctx.obj['DEBUG'] = debug
36 1
    ctx.obj['VERBOSE'] = verbose
37 1
    ctx.obj['STAKKR'] = StakkrActions(venv_base, ctx.obj)
38 1
    ctx.obj['CTS'] = get_running_containers_name(ctx.obj['STAKKR'].project_name)
39
40
41 1
@stakkr.command(help="""Enter a container to perform direct actions such as
42
install packages, run commands, etc.""")
43 1
@click.argument('container', required=True)
44 1
@click.option('--user', '-u', help="User's name. Valid choices : www-data or root")
45 1
@click.option('--tty/--no-tty', '-t/ ', is_flag=True, default=True, help="Use a TTY")
46 1
@click.pass_context
47 1
def console(ctx, container: str, user: str, tty: bool):
48
    """See command Help"""
49
50 1
    if len(ctx.obj['CTS']) is not 0:
51 1
        ct_choice = click.Choice(ctx.obj['CTS'])
52 1
        ct_choice.convert(container, None, ctx)
53
54 1
    ctx.obj['STAKKR'].console(container, _get_cmd_user(user, container), tty)
55
56
57 1
@stakkr.command(help="""Execute a command into a container.
58
59
Examples:\n
60
- ``stakkr -v exec mysql mysqldump -p'$MYSQL_ROOT_PASSWORD' mydb > /tmp/backup.sql``\n
61
- ``stakkr exec php php -v`` : Execute the php binary in the php container with option -v\n
62
- ``stakkr exec apache service apache2 restart``\n
63
""", name='exec', context_settings=dict(ignore_unknown_options=True, allow_interspersed_args=False))
64 1
@click.pass_context
65 1
@click.option('--user', '-u', help="User's name. Be careful, each container have its own users.")
66 1
@click.option('--tty/--no-tty', '-t/ ', is_flag=True, default=True, help="Use a TTY")
67 1
@click.argument('container', required=True)
68 1
@click.argument('command', required=True, nargs=-1, type=click.UNPROCESSED)
69 1
def exec_cmd(ctx, user: str, container: str, command: tuple, tty: bool):
70
    """See command Help"""
71
72 1
    if len(ctx.obj['CTS']) is not 0:
73 1
        click.Choice(ctx.obj['CTS']).convert(container, None, ctx)
74
75 1
    ctx.obj['STAKKR'].exec_cmd(container, _get_cmd_user(user, container), command, tty)
76
77
78 1
@stakkr.command(help="""`stakkr mysql` is a wrapper for the mysql binary
79
located in the mysql service.
80
81
You can run any mysql command as root, such as :\n
82
- ``stakkr mysql -e "CREATE DATABASE mydb"`` to create a DB from outside\n
83
- ``stakkr mysql`` to enter the mysql console\n
84
- ``cat myfile.sql | stakkr mysql --no-tty mydb`` to import a file from outside to mysql\n
85
86
For scripts, you must use the relative path.
87
""", context_settings=dict(ignore_unknown_options=True, allow_interspersed_args=False))
88 1
@click.pass_context
89 1
@click.option('--tty/--no-tty', '-t/ ', is_flag=True, default=True, help="Use a TTY")
90 1
@click.argument('command', nargs=-1, type=click.UNPROCESSED)
91 1
def mysql(ctx, tty: bool, command: tuple):
92
    """See command Help"""
93
94 1
    command = ('mysql', '-p$MYSQL_ROOT_PASSWORD') + command
95 1
    ctx.invoke(exec_cmd, user='root', container='mysql', command=command, tty=tty)
96
97
98 1
@stakkr.command(help='Required to be launched if you install a new plugin', name="refresh-plugins")
99 1
@click.pass_context
100
def refresh_plugins(ctx):
101
    """See command Help"""
102
103 1
    from stakkr.plugins import add_plugins
104
105 1
    print(click.style('Adding plugins from plugins/', fg='green'))
106 1
    plugins = add_plugins()
107 1
    if len(plugins) is 0:
108 1
        print(click.style('No plugin to add', fg='yellow'))
109 1
        exit(0)
110
111 1
    print()
112 1
    print(click.style('Plugins refreshed', fg='green'))
113
114
115 1
@stakkr.command(help="Restart all (or a single as CONTAINER) container(s)")
116 1
@click.argument('container', required=False)
117 1
@click.option('--pull', '-p', help="Force a pull of the latest images versions", is_flag=True)
118 1
@click.option('--recreate', '-r', help="Recreate all containers", is_flag=True)
119 1
@click.option('--proxy/--no-proxy', '-P', help="Restart the proxy", default=True)
120 1
@click.pass_context
121 1
def restart(ctx, container: str, pull: bool, recreate: bool, proxy: bool):
122
    """See command Help"""
123
124 1
    print(click.style('[RESTARTING]', fg='green') + ' your stakkr services')
125 1
    try:
126 1
        ctx.invoke(stop, container=container, proxy=proxy)
127 1
    except Exception:
128 1
        print()
129
130 1
    ctx.invoke(start, container=container, pull=pull, recreate=recreate, proxy=proxy)
131
132
133 1
@stakkr.command(help="List available services available for compose.ini (with info if the service is enabled)")
134 1
@click.pass_context
135
def services(ctx):
136
    """See command Help"""
137
138
    from stakkr.stakkr_compose import get_available_services
139
140
    print('Available services usable in compose.ini ', end='')
141
    print('({} = currently in use) : '.format(click.style('✔', fg='green')))
142
143
    enabled_services = ctx.obj['STAKKR']._get_config()['main']['services']
144
    for available_service in sorted(list(get_available_services().keys())):
145
        sign = click.style('✘', fg='red')
146
        if available_service in enabled_services:
147
            sign = click.style('✔', fg='green')
148
149
        print('  - {} ({})'.format(available_service, sign))
150
151
152 1
@stakkr.command(help="Start all (or a single as CONTAINER) container(s) defined in compose.ini")
153 1
@click.argument('container', required=False)
154 1
@click.option('--pull', '-p', help="Force a pull of the latest images versions", is_flag=True)
155 1
@click.option('--recreate', '-r', help="Recreate all containers", is_flag=True)
156 1
@click.option('--proxy/--no-proxy', '-P', help="Start the proxy", default=True)
157 1
@click.pass_context
158 1
def start(ctx, container: str, pull: bool, recreate: bool, proxy: bool):
159
    """See command Help"""
160 1
    print(click.style('[STARTING]', fg='green') + ' your stakkr services')
161
162 1
    ctx.obj['STAKKR'].start(container, pull, recreate, proxy)
163 1
    _show_status(ctx)
164
165
166 1
@stakkr.command(help="Display a list of running containers")
167 1
@click.pass_context
168
def status(ctx):
169
    """See command Help"""
170
171 1
    ctx.obj['STAKKR'].status()
172
173
174 1
@stakkr.command(help="Stop all (or a single as CONTAINER) container(s)")
175 1
@click.argument('container', required=False)
176 1
@click.option('--proxy/--no-proxy', '-P', help="Stop the proxy", default=True)
177 1
@click.pass_context
178 1
def stop(ctx, container: str, proxy: bool):
179
    """See command Help"""
180
181 1
    print(click.style('[STOPPING]', fg='yellow') + ' your stakkr services')
182 1
    ctx.obj['STAKKR'].stop(container, proxy)
183
184
185 1
def _get_cmd_user(user: str, container: str):
186 1
    users = {'apache': 'www-data', 'nginx': 'www-data', 'php': 'www-data'}
187
188 1
    cmd_user = 'root' if user is None else user
189 1
    if container in users and user is None:
190 1
        cmd_user = users[container]
191
192 1
    return cmd_user
193
194
195 1
def _show_status(ctx):
196 1
    services_ports = ctx.obj['STAKKR'].get_services_urls()
197 1
    if services_ports == '':
198 1
        print('\nServices Status:')
199 1
        ctx.invoke(status)
200 1
        return
201
202 1
    print('\nServices URLs :')
203 1
    print(services_ports)
204
205
206 1
def debug_mode():
207
    """Guess if we are in debug mode, useful to display runtime errors"""
208
209 1
    if '--debug' in sys.argv or '-d' in sys.argv:
210 1
        return True
211
212 1
    return False
213
214
215 1
def main():
216
    """Main function when the CLI Script is called directly"""
217
218 1
    try:
219 1
        stakkr(obj={})
220 1
    except Exception as error:
221 1
        msg = click.style(r""" ______ _____  _____   ____  _____
222
|  ____|  __ \|  __ \ / __ \|  __ \
223
| |__  | |__) | |__) | |  | | |__) |
224
|  __| |  _  /|  _  /| |  | |  _  /
225
| |____| | \ \| | \ \| |__| | | \ \
226
|______|_|  \_\_|  \_\\____/|_|  \_\
227
228
""", fg='yellow')
229 1
        msg += click.style('{}'.format(error), fg='red')
230 1
        print(msg + '\n', file=sys.stderr)
231
232 1
        if debug_mode() is True:
233 1
            raise error
234
235 1
        sys.exit(1)
236
237
238 1
if __name__ == '__main__':
239
    main()
240