blis-cloud-cli 0.1.1__tar.gz
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- blis_cloud_cli-0.1.1/LICENSE +21 -0
- blis_cloud_cli-0.1.1/PKG-INFO +31 -0
- blis_cloud_cli-0.1.1/README.md +6 -0
- blis_cloud_cli-0.1.1/blis_cli/__init__.py +0 -0
- blis_cloud_cli-0.1.1/blis_cli/blis.py +67 -0
- blis_cloud_cli-0.1.1/blis_cli/commands/__init__.py +0 -0
- blis_cloud_cli-0.1.1/blis_cli/commands/docker.py +139 -0
- blis_cloud_cli-0.1.1/blis_cli/commands/domain.py +144 -0
- blis_cloud_cli-0.1.1/blis_cli/commands/install.py +92 -0
- blis_cloud_cli-0.1.1/blis_cli/commands/logs.py +73 -0
- blis_cloud_cli-0.1.1/blis_cli/commands/start.py +13 -0
- blis_cloud_cli-0.1.1/blis_cli/commands/status.py +78 -0
- blis_cloud_cli-0.1.1/blis_cli/commands/stop.py +18 -0
- blis_cloud_cli-0.1.1/blis_cli/commands/swap.py +50 -0
- blis_cloud_cli-0.1.1/blis_cli/commands/update.py +37 -0
- blis_cloud_cli-0.1.1/blis_cli/extra/docker-compose.yml +57 -0
- blis_cloud_cli-0.1.1/blis_cli/extra/dump-blis_12-202202091919.sql +1539 -0
- blis_cloud_cli-0.1.1/blis_cli/extra/dump-blis_127-202202091919.sql +1604 -0
- blis_cloud_cli-0.1.1/blis_cli/extra/dump-blis_revamp-20220704.sql +1644 -0
- blis_cloud_cli-0.1.1/blis_cli/extra/templates/Caddyfile.j2 +22 -0
- blis_cloud_cli-0.1.1/blis_cli/util/__init__.py +1 -0
- blis_cloud_cli-0.1.1/blis_cli/util/bash.py +25 -0
- blis_cloud_cli-0.1.1/blis_cli/util/caddy.py +63 -0
- blis_cloud_cli-0.1.1/blis_cli/util/config.py +159 -0
- blis_cloud_cli-0.1.1/blis_cli/util/docker_util.py +118 -0
- blis_cloud_cli-0.1.1/blis_cli/util/environment.py +35 -0
- blis_cloud_cli-0.1.1/blis_cli/util/packages.py +26 -0
- blis_cloud_cli-0.1.1/pyproject.toml +28 -0
|
@@ -0,0 +1,21 @@
|
|
|
1
|
+
MIT License
|
|
2
|
+
|
|
3
|
+
Copyright (c) 2023 Mitchell Rysavy
|
|
4
|
+
|
|
5
|
+
Permission is hereby granted, free of charge, to any person obtaining a copy
|
|
6
|
+
of this software and associated documentation files (the "Software"), to deal
|
|
7
|
+
in the Software without restriction, including without limitation the rights
|
|
8
|
+
to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
|
|
9
|
+
copies of the Software, and to permit persons to whom the Software is
|
|
10
|
+
furnished to do so, subject to the following conditions:
|
|
11
|
+
|
|
12
|
+
The above copyright notice and this permission notice shall be included in all
|
|
13
|
+
copies or substantial portions of the Software.
|
|
14
|
+
|
|
15
|
+
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
|
|
16
|
+
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
|
|
17
|
+
FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
|
|
18
|
+
AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
|
|
19
|
+
LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
|
|
20
|
+
OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
|
|
21
|
+
SOFTWARE.
|
|
@@ -0,0 +1,31 @@
|
|
|
1
|
+
Metadata-Version: 2.1
|
|
2
|
+
Name: blis-cloud-cli
|
|
3
|
+
Version: 0.1.1
|
|
4
|
+
Summary: Command-line tool for the BLIS web-based lab information system.
|
|
5
|
+
License: MIT
|
|
6
|
+
Author: Mitchell Rysavy
|
|
7
|
+
Author-email: mitchell.rysavy@gmail.com
|
|
8
|
+
Requires-Python: >=3.8,<4.0
|
|
9
|
+
Classifier: License :: OSI Approved :: MIT License
|
|
10
|
+
Classifier: Programming Language :: Python :: 3
|
|
11
|
+
Classifier: Programming Language :: Python :: 3.8
|
|
12
|
+
Classifier: Programming Language :: Python :: 3.9
|
|
13
|
+
Classifier: Programming Language :: Python :: 3.10
|
|
14
|
+
Classifier: Programming Language :: Python :: 3.11
|
|
15
|
+
Classifier: Programming Language :: Python :: 3.12
|
|
16
|
+
Requires-Dist: Jinja2 (>=3.1.3,<3.2.0)
|
|
17
|
+
Requires-Dist: PyYAML (>=6.0.1,<6.1.0)
|
|
18
|
+
Requires-Dist: click (>=8.1.7,<8.2.0)
|
|
19
|
+
Requires-Dist: colorama (>=0.4.4,<0.5.0)
|
|
20
|
+
Requires-Dist: docker (>=6.1.3,<6.2.0)
|
|
21
|
+
Requires-Dist: psutil (>=5.9.0,<5.10.0)
|
|
22
|
+
Requires-Dist: requests (>=2.31.0,<2.32.0)
|
|
23
|
+
Description-Content-Type: text/markdown
|
|
24
|
+
|
|
25
|
+
# blis-cloud-cli
|
|
26
|
+
|
|
27
|
+
A tool for managing cloud installations of [BLIS](http://blis.cc.gatech.edu/index.php).
|
|
28
|
+
|
|
29
|
+
Documentation for the tool can be found on [the BLIS documentation website.](https://docs.c4gblis.org/)
|
|
30
|
+
You can view the original proposal for this tool [here](docs/proposal.md).
|
|
31
|
+
|
|
@@ -0,0 +1,6 @@
|
|
|
1
|
+
# blis-cloud-cli
|
|
2
|
+
|
|
3
|
+
A tool for managing cloud installations of [BLIS](http://blis.cc.gatech.edu/index.php).
|
|
4
|
+
|
|
5
|
+
Documentation for the tool can be found on [the BLIS documentation website.](https://docs.c4gblis.org/)
|
|
6
|
+
You can view the original proposal for this tool [here](docs/proposal.md).
|
|
File without changes
|
|
@@ -0,0 +1,67 @@
|
|
|
1
|
+
#!/usr/bin/env python3
|
|
2
|
+
|
|
3
|
+
import click
|
|
4
|
+
import os
|
|
5
|
+
import sys
|
|
6
|
+
import psutil
|
|
7
|
+
|
|
8
|
+
from blis_cli.util import config as blis_config
|
|
9
|
+
from blis_cli.util import docker_util as blis_docker_util
|
|
10
|
+
from blis_cli.util import environment as blis_env
|
|
11
|
+
|
|
12
|
+
from blis_cli.util import bash
|
|
13
|
+
from blis_cli.commands import docker as cmd_docker_grp
|
|
14
|
+
from blis_cli.commands import install as cmd_install
|
|
15
|
+
from blis_cli.commands import logs as cmd_logs_grp
|
|
16
|
+
from blis_cli.commands import start as cmd_start
|
|
17
|
+
from blis_cli.commands import status as cmd_status
|
|
18
|
+
from blis_cli.commands import stop as cmd_stop
|
|
19
|
+
from blis_cli.commands import update as cmd_update
|
|
20
|
+
from blis_cli.commands import domain as cmd_domain_grp
|
|
21
|
+
from blis_cli.commands import swap as cmd_swap_grp
|
|
22
|
+
|
|
23
|
+
|
|
24
|
+
@click.command()
|
|
25
|
+
def status():
|
|
26
|
+
exit(cmd_status.run())
|
|
27
|
+
|
|
28
|
+
|
|
29
|
+
@click.command()
|
|
30
|
+
def install():
|
|
31
|
+
exit(cmd_install.run())
|
|
32
|
+
|
|
33
|
+
|
|
34
|
+
@click.command()
|
|
35
|
+
def start():
|
|
36
|
+
exit(cmd_start.run())
|
|
37
|
+
|
|
38
|
+
|
|
39
|
+
@click.command()
|
|
40
|
+
def stop():
|
|
41
|
+
exit(cmd_stop.run())
|
|
42
|
+
|
|
43
|
+
|
|
44
|
+
@click.command()
|
|
45
|
+
def update():
|
|
46
|
+
exit(cmd_update.run())
|
|
47
|
+
|
|
48
|
+
|
|
49
|
+
@click.group()
|
|
50
|
+
def entry_point():
|
|
51
|
+
pass
|
|
52
|
+
|
|
53
|
+
|
|
54
|
+
def main():
|
|
55
|
+
entry_point.add_command(install)
|
|
56
|
+
entry_point.add_command(start)
|
|
57
|
+
entry_point.add_command(status)
|
|
58
|
+
entry_point.add_command(stop)
|
|
59
|
+
entry_point.add_command(update)
|
|
60
|
+
|
|
61
|
+
entry_point.add_command(cmd_logs_grp.entrypoint, "logs")
|
|
62
|
+
entry_point.add_command(cmd_docker_grp.entrypoint, "docker")
|
|
63
|
+
entry_point.add_command(cmd_domain_grp.entrypoint, "domain")
|
|
64
|
+
entry_point.add_command(cmd_swap_grp.entrypoint, "swap")
|
|
65
|
+
|
|
66
|
+
entry_point()
|
|
67
|
+
|
|
File without changes
|
|
@@ -0,0 +1,139 @@
|
|
|
1
|
+
import click
|
|
2
|
+
import docker as lib_docker
|
|
3
|
+
import getpass
|
|
4
|
+
import os
|
|
5
|
+
import requests
|
|
6
|
+
import shutil
|
|
7
|
+
|
|
8
|
+
from blis_cli.util import bash
|
|
9
|
+
from blis_cli.util import config
|
|
10
|
+
from blis_cli.util import docker_util
|
|
11
|
+
from blis_cli.util import environment as env
|
|
12
|
+
from blis_cli.util import packages
|
|
13
|
+
|
|
14
|
+
|
|
15
|
+
@click.group()
|
|
16
|
+
def entrypoint():
|
|
17
|
+
pass
|
|
18
|
+
|
|
19
|
+
|
|
20
|
+
@click.command
|
|
21
|
+
def install():
|
|
22
|
+
# If Docker is installed, and we are in the docker group, we will not need root privileges.
|
|
23
|
+
if not (env.in_docker_grp() and docker_util.installed()):
|
|
24
|
+
if not (os.geteuid() == 0 or env.can_sudo()):
|
|
25
|
+
click.secho(
|
|
26
|
+
"Docker must be installed. You must run this script as root or have passwordless sudo privileges.",
|
|
27
|
+
fg="red",
|
|
28
|
+
)
|
|
29
|
+
exit(1)
|
|
30
|
+
|
|
31
|
+
install_docker()
|
|
32
|
+
|
|
33
|
+
# Returns a success code because we probably succeeded and installed Docker, but we need to log out and log back in.
|
|
34
|
+
exit(0)
|
|
35
|
+
|
|
36
|
+
# We might have Docker installed, but not be in the docker group.
|
|
37
|
+
if docker_util.installed() and not env.in_docker_grp():
|
|
38
|
+
click.secho(
|
|
39
|
+
"You have Docker installed, but you are not in the docker group.",
|
|
40
|
+
fg="yellow",
|
|
41
|
+
)
|
|
42
|
+
# Try to fix the problem...
|
|
43
|
+
if env.can_sudo():
|
|
44
|
+
bash.sudo(f"usermod -aG docker {getpass.getuser()}")
|
|
45
|
+
click.echo("Please log out and log back in, and run this command again.")
|
|
46
|
+
exit(0)
|
|
47
|
+
else:
|
|
48
|
+
click.secho(
|
|
49
|
+
"You must run this script as root or have passwordless sudo privileges.",
|
|
50
|
+
fg="red",
|
|
51
|
+
)
|
|
52
|
+
exit(1)
|
|
53
|
+
|
|
54
|
+
click.secho("Docker is installed!", fg="green")
|
|
55
|
+
|
|
56
|
+
|
|
57
|
+
def install_docker():
|
|
58
|
+
click.echo("Setting up Docker... ")
|
|
59
|
+
packages.remove(["docker", "docker-engine", "docker.io", "containerd", "runc"])
|
|
60
|
+
packages.install(["ca-certificates", "curl", "gnupg", "lsb-release"])
|
|
61
|
+
bash.sudo("mkdir -p /etc/apt/keyrings")
|
|
62
|
+
bash.run(
|
|
63
|
+
"curl -fsSL https://download.docker.com/linux/ubuntu/gpg | sudo gpg --dearmor -o - | sudo tee /etc/apt/keyrings/docker.gpg >/dev/null"
|
|
64
|
+
)
|
|
65
|
+
bash.run(
|
|
66
|
+
'echo "deb [arch=$(dpkg --print-architecture) signed-by=/etc/apt/keyrings/docker.gpg] https://download.docker.com/linux/ubuntu $(lsb_release -cs) stable" | sudo tee /etc/apt/sources.list.d/docker.list > /dev/null'
|
|
67
|
+
)
|
|
68
|
+
packages.apt_update()
|
|
69
|
+
packages.install(
|
|
70
|
+
[
|
|
71
|
+
"docker-ce",
|
|
72
|
+
"docker-ce-cli",
|
|
73
|
+
"containerd.io",
|
|
74
|
+
"docker-compose-plugin",
|
|
75
|
+
"docker-buildx-plugin",
|
|
76
|
+
]
|
|
77
|
+
)
|
|
78
|
+
bash.sudo(f"usermod -aG docker {getpass.getuser()}")
|
|
79
|
+
bash.sudo("systemctl enable docker.service")
|
|
80
|
+
bash.sudo("systemctl start docker.service")
|
|
81
|
+
click.secho("Success!", fg="green")
|
|
82
|
+
click.echo("Please log out and log back in, and run this command again.")
|
|
83
|
+
|
|
84
|
+
|
|
85
|
+
@click.command
|
|
86
|
+
def purge():
|
|
87
|
+
if not env.can_sudo():
|
|
88
|
+
click.secho("You must have root privileges to run this.", fg="red")
|
|
89
|
+
exit(1)
|
|
90
|
+
|
|
91
|
+
packages.remove(["docker", "docker-engine", "docker.io", "containerd", "runc"])
|
|
92
|
+
packages.remove(
|
|
93
|
+
[
|
|
94
|
+
"docker-ce",
|
|
95
|
+
"docker-ce-cli",
|
|
96
|
+
"containerd.io",
|
|
97
|
+
"docker-compose-plugin",
|
|
98
|
+
"docker-buildx-plugin",
|
|
99
|
+
]
|
|
100
|
+
)
|
|
101
|
+
packages.apt_update()
|
|
102
|
+
|
|
103
|
+
|
|
104
|
+
@click.command
|
|
105
|
+
def status():
|
|
106
|
+
docker_ok = True
|
|
107
|
+
click.echo("Docker is accessible? ", nl=False)
|
|
108
|
+
try:
|
|
109
|
+
client = lib_docker.from_env()
|
|
110
|
+
client.containers.list()
|
|
111
|
+
click.secho("Yes", fg="green")
|
|
112
|
+
except Exception as e:
|
|
113
|
+
docker_ok = False
|
|
114
|
+
click.secho("No", fg="red")
|
|
115
|
+
|
|
116
|
+
cmd = "blis docker install"
|
|
117
|
+
if not env.can_sudo():
|
|
118
|
+
cmd = "sudo " + cmd
|
|
119
|
+
|
|
120
|
+
if not docker_ok:
|
|
121
|
+
click.echo("Docker is not accessible. Please run:")
|
|
122
|
+
click.echo(" " + cmd)
|
|
123
|
+
return 0
|
|
124
|
+
|
|
125
|
+
click.echo("Docker Compose is installed? ", nl=False)
|
|
126
|
+
if docker_util.compose_v2_installed():
|
|
127
|
+
click.secho("v2", fg="green")
|
|
128
|
+
elif docker_util.compose_v1_installed():
|
|
129
|
+
click.secho("v1", fg="green")
|
|
130
|
+
else:
|
|
131
|
+
click.secho("No", fg="red")
|
|
132
|
+
click.echo("Docker Compose is not installed. Please run:")
|
|
133
|
+
click.echo(" " + cmd)
|
|
134
|
+
return 0
|
|
135
|
+
|
|
136
|
+
|
|
137
|
+
entrypoint.add_command(install)
|
|
138
|
+
entrypoint.add_command(purge)
|
|
139
|
+
entrypoint.add_command(status)
|
|
@@ -0,0 +1,144 @@
|
|
|
1
|
+
import click
|
|
2
|
+
import psutil
|
|
3
|
+
import socket
|
|
4
|
+
|
|
5
|
+
from blis_cli.util import caddy
|
|
6
|
+
from blis_cli.util import docker_util
|
|
7
|
+
|
|
8
|
+
|
|
9
|
+
@click.group()
|
|
10
|
+
def entrypoint():
|
|
11
|
+
pass
|
|
12
|
+
|
|
13
|
+
|
|
14
|
+
@click.command()
|
|
15
|
+
def status():
|
|
16
|
+
click.secho("Caddy is configured: ", nl=False)
|
|
17
|
+
if caddy.installed():
|
|
18
|
+
click.secho("Yes!", fg="green")
|
|
19
|
+
else:
|
|
20
|
+
click.secho("No", fg="red")
|
|
21
|
+
click.echo("\nThere are no domains configured. Please run:")
|
|
22
|
+
click.echo(" blis domain add [mydomain.example.com]")
|
|
23
|
+
exit(0)
|
|
24
|
+
|
|
25
|
+
domains = caddy.get_domains()
|
|
26
|
+
if len(domains) < 1:
|
|
27
|
+
click.echo("\nThere are no domains configured. Please run:")
|
|
28
|
+
click.echo(" blis domain add [mydomain.example.com]")
|
|
29
|
+
exit(0)
|
|
30
|
+
|
|
31
|
+
# These are only "possible" IPs because the computer
|
|
32
|
+
# might be behind a firewall/NAT and these will only contain
|
|
33
|
+
# the local IP addresses.
|
|
34
|
+
possible_ipv4_addrs = get_interfaces()
|
|
35
|
+
|
|
36
|
+
for domain in domains:
|
|
37
|
+
click.echo()
|
|
38
|
+
click.secho(domain, fg="green")
|
|
39
|
+
click.echo("IP Addresses: ", nl=False)
|
|
40
|
+
ip_addrs = get_ipv4_by_hostname(domain)
|
|
41
|
+
click.secho(", ".join(ip_addrs), fg="green")
|
|
42
|
+
|
|
43
|
+
for ip in ip_addrs:
|
|
44
|
+
if ip in possible_ipv4_addrs:
|
|
45
|
+
# This is the 'happiest' path since the computer/VM has an IP that is facing the Internet.
|
|
46
|
+
click.secho("Domain points to computer!")
|
|
47
|
+
break
|
|
48
|
+
|
|
49
|
+
exit(0)
|
|
50
|
+
|
|
51
|
+
|
|
52
|
+
@click.command()
|
|
53
|
+
@click.argument("name")
|
|
54
|
+
def add(name: str):
|
|
55
|
+
current_domains = caddy.get_domains()
|
|
56
|
+
if name in current_domains:
|
|
57
|
+
click.echo(f"{name} is already configured as a domain.")
|
|
58
|
+
return 0
|
|
59
|
+
|
|
60
|
+
click.confirm(f"Do you want to add {name} as a domain?", abort=True)
|
|
61
|
+
|
|
62
|
+
restart_blis = False
|
|
63
|
+
if docker_util.blis_is_running():
|
|
64
|
+
if not docker_util.stop_blis_with_confirm():
|
|
65
|
+
return 1
|
|
66
|
+
restart_blis = True
|
|
67
|
+
|
|
68
|
+
if not caddy.installed():
|
|
69
|
+
click.echo("Installing Caddy... ", nl=False)
|
|
70
|
+
try:
|
|
71
|
+
caddy.install()
|
|
72
|
+
click.secho("Success!", fg="green")
|
|
73
|
+
except Exception as e:
|
|
74
|
+
click.secho(e, fg="red")
|
|
75
|
+
return 1
|
|
76
|
+
|
|
77
|
+
click.echo(f"Adding {name} to Caddyfile... ", nl=False)
|
|
78
|
+
|
|
79
|
+
current_domains.append(name)
|
|
80
|
+
caddy.set_domains(current_domains)
|
|
81
|
+
|
|
82
|
+
click.secho("Success!", fg="green")
|
|
83
|
+
|
|
84
|
+
if restart_blis:
|
|
85
|
+
docker_util.start_blis("app")
|
|
86
|
+
docker_util.restart_caddy()
|
|
87
|
+
|
|
88
|
+
|
|
89
|
+
@click.command()
|
|
90
|
+
def clear():
|
|
91
|
+
click.secho("This will remove all of the domains from the configuration.", fg="red")
|
|
92
|
+
click.confirm(f"Do you want to continue?", abort=True)
|
|
93
|
+
|
|
94
|
+
restart_blis = False
|
|
95
|
+
if docker_util.blis_is_running():
|
|
96
|
+
if not docker_util.stop_blis_with_confirm():
|
|
97
|
+
return 1
|
|
98
|
+
restart_blis = True
|
|
99
|
+
|
|
100
|
+
click.echo(f"Removing all domains from Caddyfile... ", nl=False)
|
|
101
|
+
|
|
102
|
+
caddy.set_domains([])
|
|
103
|
+
|
|
104
|
+
click.secho("Success!", fg="green")
|
|
105
|
+
|
|
106
|
+
if restart_blis:
|
|
107
|
+
docker_util.start_blis("app")
|
|
108
|
+
docker_util.restart_caddy()
|
|
109
|
+
|
|
110
|
+
|
|
111
|
+
# Source:
|
|
112
|
+
# https://stackoverflow.com/questions/2805231/how-can-i-do-dns-lookups-in-python-including-referring-to-etc-hosts/66000439#66000439
|
|
113
|
+
# Licensed: CC BY-SA 4.0
|
|
114
|
+
def get_ipv4_by_hostname(hostname):
|
|
115
|
+
# see `man getent` `/ hosts `
|
|
116
|
+
# see `man getaddrinfo`
|
|
117
|
+
try:
|
|
118
|
+
return list(
|
|
119
|
+
i[4][0] # raw socket structure # internet protocol info # address
|
|
120
|
+
for i in socket.getaddrinfo(hostname, 0) # port, required
|
|
121
|
+
if i[0] is socket.AddressFamily.AF_INET # ipv4
|
|
122
|
+
# ignore duplicate addresses with other socket types
|
|
123
|
+
and i[1] is socket.SocketKind.SOCK_RAW
|
|
124
|
+
)
|
|
125
|
+
except Exception as e:
|
|
126
|
+
click.secho(str(e), fg="red", nl=False)
|
|
127
|
+
return []
|
|
128
|
+
|
|
129
|
+
|
|
130
|
+
def get_interfaces():
|
|
131
|
+
addrs = psutil.net_if_addrs()
|
|
132
|
+
ip_addrs = []
|
|
133
|
+
for key in addrs:
|
|
134
|
+
addr = addrs[key]
|
|
135
|
+
for a in addr:
|
|
136
|
+
if a.family == socket.AddressFamily.AF_INET:
|
|
137
|
+
ip_addrs.append(a.address)
|
|
138
|
+
|
|
139
|
+
return ip_addrs
|
|
140
|
+
|
|
141
|
+
|
|
142
|
+
entrypoint.add_command(add)
|
|
143
|
+
entrypoint.add_command(clear)
|
|
144
|
+
entrypoint.add_command(status)
|
|
@@ -0,0 +1,92 @@
|
|
|
1
|
+
import click
|
|
2
|
+
import glob
|
|
3
|
+
import os
|
|
4
|
+
import docker as lib_docker
|
|
5
|
+
import shutil
|
|
6
|
+
|
|
7
|
+
from blis_cli.util import bash
|
|
8
|
+
from blis_cli.util import caddy
|
|
9
|
+
from blis_cli.util import config
|
|
10
|
+
from blis_cli.util import docker_util as docker
|
|
11
|
+
from blis_cli.util import environment as env
|
|
12
|
+
from blis_cli.util import packages
|
|
13
|
+
|
|
14
|
+
|
|
15
|
+
def run():
|
|
16
|
+
try:
|
|
17
|
+
version = lib_docker.from_env().version()
|
|
18
|
+
click.echo(f"Docker version: {click.style(version['Version'], fg='green')}")
|
|
19
|
+
except Exception as e:
|
|
20
|
+
click.secho("There was a problem accessing Docker.", fg="red")
|
|
21
|
+
click.echo("Please run `blis docker status` and follow the instructions.")
|
|
22
|
+
return 1
|
|
23
|
+
|
|
24
|
+
if os.path.exists(config.compose_file()):
|
|
25
|
+
if not click.confirm(
|
|
26
|
+
"BLIS has already been installed in ~/.blis. Do you want to overwrite the configuration?"
|
|
27
|
+
):
|
|
28
|
+
return 0
|
|
29
|
+
|
|
30
|
+
config.make_basedir()
|
|
31
|
+
copy_docker_files()
|
|
32
|
+
|
|
33
|
+
if config.validate_compose():
|
|
34
|
+
click.echo("docker-compose.yml is valid.")
|
|
35
|
+
else:
|
|
36
|
+
click.secho("docker-compose.yml is not valid.", fg="red")
|
|
37
|
+
return 1
|
|
38
|
+
|
|
39
|
+
run_blis_and_setup_db()
|
|
40
|
+
|
|
41
|
+
click.secho("BLIS has been successfully installed!", fg="green")
|
|
42
|
+
|
|
43
|
+
|
|
44
|
+
def copy_docker_files():
|
|
45
|
+
click.echo("Copying docker-compose.yml to ~/.blis/...")
|
|
46
|
+
shutil.copy(
|
|
47
|
+
f"{os.path.dirname(__file__)}/../extra/docker-compose.yml",
|
|
48
|
+
config.compose_file(),
|
|
49
|
+
)
|
|
50
|
+
click.echo("Creating ~/.blis/Caddyfile...")
|
|
51
|
+
caddy.set_domains([])
|
|
52
|
+
|
|
53
|
+
|
|
54
|
+
def run_blis_and_setup_db():
|
|
55
|
+
click.echo("Starting BLIS database... ", nl=False)
|
|
56
|
+
out, err = bash.run(
|
|
57
|
+
f"{docker.compose()} -f {config.compose_file()} up -d --wait db"
|
|
58
|
+
)
|
|
59
|
+
if err:
|
|
60
|
+
click.secho("Failed", fg="red")
|
|
61
|
+
click.echo(err, err=True)
|
|
62
|
+
return False
|
|
63
|
+
|
|
64
|
+
click.secho("Success!", fg="green")
|
|
65
|
+
|
|
66
|
+
db_password = config.compose_key("services.db.environment.MYSQL_ROOT_PASSWORD")
|
|
67
|
+
seed_failed = False
|
|
68
|
+
|
|
69
|
+
for file in glob.glob(f"{os.path.dirname(__file__)}/../extra/*.sql"):
|
|
70
|
+
click.echo(f"Seeding {os.path.basename(file)}... ", nl=False)
|
|
71
|
+
out, err = bash.run(
|
|
72
|
+
f"{docker.compose()} -f {config.compose_file()} exec -T db mysql -hdb -uroot -p{db_password} < {file}"
|
|
73
|
+
)
|
|
74
|
+
if err:
|
|
75
|
+
click.secho("Failed", fg="red")
|
|
76
|
+
click.echo(err, err=True, nl=False)
|
|
77
|
+
seed_failed = True
|
|
78
|
+
else:
|
|
79
|
+
click.secho("Success!", fg="green")
|
|
80
|
+
|
|
81
|
+
if seed_failed:
|
|
82
|
+
click.secho("Seeding database failed.", fg="yellow")
|
|
83
|
+
click.echo("BLIS might still start. Please check the errors for details.")
|
|
84
|
+
|
|
85
|
+
click.echo("Starting BLIS... ", nl=False)
|
|
86
|
+
out, err = bash.run(f"{docker.compose()} -f {config.compose_file()} up -d app")
|
|
87
|
+
if err:
|
|
88
|
+
click.secho("Failed", fg="red")
|
|
89
|
+
click.echo(err, err=True)
|
|
90
|
+
return False
|
|
91
|
+
|
|
92
|
+
click.secho("Success!", fg="green")
|
|
@@ -0,0 +1,73 @@
|
|
|
1
|
+
import click
|
|
2
|
+
import docker
|
|
3
|
+
import tarfile
|
|
4
|
+
import tempfile
|
|
5
|
+
|
|
6
|
+
from blis_cli.util import config
|
|
7
|
+
from blis_cli.util import environment
|
|
8
|
+
from blis_cli.util import docker_util
|
|
9
|
+
|
|
10
|
+
|
|
11
|
+
@click.group
|
|
12
|
+
def entrypoint():
|
|
13
|
+
pass
|
|
14
|
+
|
|
15
|
+
|
|
16
|
+
def get_container_log_file(filename):
|
|
17
|
+
try:
|
|
18
|
+
c = docker_util.blis_container()
|
|
19
|
+
|
|
20
|
+
# This is technically not required. We could probably extract logs from
|
|
21
|
+
# a stopped BLIS since the /log folder is mounted as its own volume.
|
|
22
|
+
# However... I don't know how to reliably do that right now.
|
|
23
|
+
# And it would definitely need root.
|
|
24
|
+
if c == None:
|
|
25
|
+
click.secho("BLIS is not running.", fg="red")
|
|
26
|
+
click.echo("Please start BLIS to see logs.")
|
|
27
|
+
return 0
|
|
28
|
+
|
|
29
|
+
with tempfile.TemporaryFile() as f:
|
|
30
|
+
bits, stat = c.get_archive(filename)
|
|
31
|
+
|
|
32
|
+
for chunk in bits:
|
|
33
|
+
f.write(chunk)
|
|
34
|
+
|
|
35
|
+
f.seek(0)
|
|
36
|
+
tf = tarfile.open(fileobj=f)
|
|
37
|
+
m = tf.extractfile(tf.getnames()[0])
|
|
38
|
+
for chunk in m:
|
|
39
|
+
click.echo(chunk, nl=False)
|
|
40
|
+
|
|
41
|
+
except docker.errors.NotFound as e:
|
|
42
|
+
click.echo("The log file was not found.", err=True)
|
|
43
|
+
return 1
|
|
44
|
+
except Exception as e:
|
|
45
|
+
click.echo("There was a problem getting the logs for BLIS!", err=True)
|
|
46
|
+
click.echo(e)
|
|
47
|
+
return 1
|
|
48
|
+
|
|
49
|
+
|
|
50
|
+
@click.command()
|
|
51
|
+
def application():
|
|
52
|
+
exit(get_container_log_file("/var/www/blis/log/application.log"))
|
|
53
|
+
|
|
54
|
+
|
|
55
|
+
@click.command()
|
|
56
|
+
def database():
|
|
57
|
+
exit(get_container_log_file("/var/www/blis/log/database.log"))
|
|
58
|
+
|
|
59
|
+
|
|
60
|
+
@click.command(name="apache2/access")
|
|
61
|
+
def apache2_access():
|
|
62
|
+
exit(get_container_log_file("/var/log/apache2/access.log"))
|
|
63
|
+
|
|
64
|
+
|
|
65
|
+
@click.command(name="apache2/error")
|
|
66
|
+
def apache2_error():
|
|
67
|
+
exit(get_container_log_file("/var/log/apache2/error.log"))
|
|
68
|
+
|
|
69
|
+
|
|
70
|
+
entrypoint.add_command(application)
|
|
71
|
+
entrypoint.add_command(database)
|
|
72
|
+
entrypoint.add_command(apache2_access)
|
|
73
|
+
entrypoint.add_command(apache2_error)
|
|
@@ -0,0 +1,13 @@
|
|
|
1
|
+
import click
|
|
2
|
+
import os
|
|
3
|
+
|
|
4
|
+
from blis_cli.util import config
|
|
5
|
+
from blis_cli.util import docker_util
|
|
6
|
+
|
|
7
|
+
|
|
8
|
+
def run():
|
|
9
|
+
if not os.path.exists(config.compose_file()):
|
|
10
|
+
click.secho("Please run `blis install` and run this command again.", fg="red")
|
|
11
|
+
return 1
|
|
12
|
+
|
|
13
|
+
return docker_util.start_blis()
|
|
@@ -0,0 +1,78 @@
|
|
|
1
|
+
import click
|
|
2
|
+
import psutil
|
|
3
|
+
|
|
4
|
+
from blis_cli.util import caddy
|
|
5
|
+
from blis_cli.util import config
|
|
6
|
+
from blis_cli.util import environment as blis_env
|
|
7
|
+
from blis_cli.util import docker_util as blis_docker_util
|
|
8
|
+
|
|
9
|
+
|
|
10
|
+
def run():
|
|
11
|
+
try:
|
|
12
|
+
blis_installed = False
|
|
13
|
+
click.echo("BLIS is installed: ", nl=False)
|
|
14
|
+
if config.validate_compose():
|
|
15
|
+
blis_installed = True
|
|
16
|
+
click.secho("Yes!", fg="green")
|
|
17
|
+
else:
|
|
18
|
+
click.secho("No", fg="red")
|
|
19
|
+
|
|
20
|
+
click.echo("BLIS is running: ", nl=False)
|
|
21
|
+
if blis_docker_util.blis_is_running():
|
|
22
|
+
click.secho("Yes!", fg="green")
|
|
23
|
+
else:
|
|
24
|
+
click.secho("No", fg="red")
|
|
25
|
+
|
|
26
|
+
click.echo("Caddy is enabled: ", nl=False)
|
|
27
|
+
if caddy.installed():
|
|
28
|
+
click.secho("Yes!", fg="green")
|
|
29
|
+
else:
|
|
30
|
+
click.secho("No", fg="red")
|
|
31
|
+
if blis_installed:
|
|
32
|
+
click.secho("Custom domain support is disabled. Please run `blis domain add` to setup.", fg="red")
|
|
33
|
+
|
|
34
|
+
total_ram = psutil.virtual_memory().total / (1024.0**3)
|
|
35
|
+
swap_space = psutil.swap_memory().total / (1024.0**3)
|
|
36
|
+
click.echo("Total RAM: ", nl=False)
|
|
37
|
+
click.secho(f"{total_ram:.2f} GiB", fg="green", nl=False)
|
|
38
|
+
if swap_space > 0:
|
|
39
|
+
click.secho(f" (swap: {swap_space:.2f} GiB)", fg="green")
|
|
40
|
+
else:
|
|
41
|
+
click.echo()
|
|
42
|
+
|
|
43
|
+
if total_ram < 0.9:
|
|
44
|
+
click.secho(
|
|
45
|
+
"1GB of RAM is recommended to run BLIS. Things might not work as expected!",
|
|
46
|
+
fg="red",
|
|
47
|
+
)
|
|
48
|
+
|
|
49
|
+
click.echo("Supported Ubuntu distribution: ", nl=False)
|
|
50
|
+
if blis_env.supported_distro():
|
|
51
|
+
click.secho("Yes!", fg="green")
|
|
52
|
+
else:
|
|
53
|
+
click.secho("No", fg="red")
|
|
54
|
+
click.echo("BLIS is supported on these Ubuntu distributions: ", nl=False)
|
|
55
|
+
click.secho(", ".join(blis_env.SUPPORTED_DISTROS), fg="green")
|
|
56
|
+
click.echo("You have: ", nl=False)
|
|
57
|
+
click.secho(blis_env.distro(), fg="green")
|
|
58
|
+
|
|
59
|
+
click.echo("Passwordless sudo: ", nl=False)
|
|
60
|
+
if blis_env.can_sudo():
|
|
61
|
+
click.secho("Yes!", fg="green")
|
|
62
|
+
else:
|
|
63
|
+
click.secho("No", fg="red")
|
|
64
|
+
|
|
65
|
+
if (
|
|
66
|
+
blis_docker_util.blis_container() == None
|
|
67
|
+
and not blis_docker_util.installed()
|
|
68
|
+
):
|
|
69
|
+
click.secho(
|
|
70
|
+
"Please run `blis docker status` to check the status of Docker on your machine.",
|
|
71
|
+
fg="yellow",
|
|
72
|
+
)
|
|
73
|
+
|
|
74
|
+
return 0
|
|
75
|
+
except Exception as e:
|
|
76
|
+
click.echo("There was a problem getting the status of BLIS!")
|
|
77
|
+
click.echo(e)
|
|
78
|
+
return 1
|
|
@@ -0,0 +1,18 @@
|
|
|
1
|
+
import click
|
|
2
|
+
|
|
3
|
+
from blis_cli.util import bash
|
|
4
|
+
from blis_cli.util import config
|
|
5
|
+
from blis_cli.util import docker_util as docker
|
|
6
|
+
|
|
7
|
+
|
|
8
|
+
def run():
|
|
9
|
+
click.echo("Stopping BLIS... ", nl=False)
|
|
10
|
+
out, err = bash.run(f"{docker.compose()} -f {config.compose_file()} down")
|
|
11
|
+
if err:
|
|
12
|
+
click.secho("Failed", fg="red")
|
|
13
|
+
click.echo(err, err=True)
|
|
14
|
+
return False
|
|
15
|
+
|
|
16
|
+
click.secho("Success!", fg="green")
|
|
17
|
+
|
|
18
|
+
return 0
|