drone-deploy/main.py

130 lines
4.4 KiB
Python
Raw Permalink Normal View History

2019-03-05 18:30:10 +00:00
import io
import os
import random
import string
import subprocess
2020-01-13 08:22:37 +00:00
import warnings
2019-03-05 18:30:10 +00:00
from os.path import abspath, join, dirname
2020-01-13 08:22:37 +00:00
from typing import List
2019-03-05 18:30:10 +00:00
import paramiko
from paramiko import SSHClient
2020-01-13 08:22:37 +00:00
VERSION = '1.0.3'
2019-03-05 18:30:10 +00:00
def get_random_string(length: int = 128) -> str:
return ''.join(random.choices(string.ascii_lowercase + string.digits, k=length))
2020-01-13 08:22:37 +00:00
def execute(c: SSHClient, cmd: str, path: str = None, env: dict = None) -> str:
2019-03-05 18:30:10 +00:00
if path is not None:
cmd = 'cd {}; {}'.format(path, cmd)
2019-05-25 14:42:19 +00:00
stdin, stdout, stderr = c.exec_command(cmd, environment=env)
2019-03-05 19:24:51 +00:00
return stdout.read().decode('utf-8').strip()
2019-03-05 18:30:10 +00:00
def main():
2020-01-13 08:22:37 +00:00
print(f'> Version: {VERSION}')
print('> Deployment started 🚀')
2019-03-05 18:30:10 +00:00
host = os.environ.get('PLUGIN_HOST')
port = os.environ.get('PLUGIN_PORT', 22)
user = os.environ.get('PLUGIN_USER')
password = os.environ.get('PLUGIN_PASSWORD')
key = os.environ.get('PLUGIN_KEY')
2019-05-25 14:42:19 +00:00
# Takes a string, splits it at the comma and removes empty elements
2019-03-05 18:30:10 +00:00
def clean_array(s: str) -> List[str]:
return list(filter(None, s.split(',')))
commands = clean_array(os.environ.get('PLUGIN_COMMANDS', ''))
sources = clean_array(os.environ.get('PLUGIN_SOURCES', ''))
deletes = clean_array(os.environ.get('PLUGIN_DELETE', ''))
target = os.environ.get('PLUGIN_TARGET')
2019-05-25 14:42:19 +00:00
# Check for host, port and user
2019-03-05 18:30:10 +00:00
for env in [host, port, user]:
if env is None:
2019-05-25 14:42:19 +00:00
raise Exception('Missing host, port or user env variable')
2019-03-05 18:30:10 +00:00
2019-05-25 14:42:19 +00:00
# Check if there is a possible authentication method
2020-01-13 08:22:37 +00:00
if len(list(filter(lambda x: x is not None, [password, key]))) == 0:
2019-03-05 18:30:10 +00:00
raise Exception('No authentication method provided')
2019-05-25 14:42:19 +00:00
# Check if target is set
2020-01-13 08:22:37 +00:00
if len(sources) != 0 and target is None:
2019-03-05 18:30:10 +00:00
raise Exception('Target not set')
2019-05-25 14:42:19 +00:00
# Remote Envs
2020-01-13 08:22:37 +00:00
envs_raw = os.environ.get('PLUGIN_ENVS')
2019-05-27 18:25:09 +00:00
envs = None
2020-01-13 08:22:37 +00:00
if envs_raw is not None:
2019-05-25 14:42:19 +00:00
prefix = 'PLUGIN_'
2020-01-13 08:22:37 +00:00
# Take only the envs that start with PLUGIN_ and remove the prefix
envs = {k[len(prefix):]: v for k, v in os.environ.items() if k.startswith(prefix)}
2019-05-25 14:42:19 +00:00
2020-01-13 08:22:37 +00:00
if 'all' != envs_raw or ',' in envs_raw:
2019-05-25 14:42:19 +00:00
# Make them uppercase
2020-01-13 08:22:37 +00:00
selected = [x.upper() for x in clean_array(envs_raw)]
2019-05-25 14:42:19 +00:00
# Select only the envs that where specified inside of envs
2020-01-13 08:22:37 +00:00
envs = {k: v for k, v in envs.items() if k in selected}
2019-05-25 14:42:19 +00:00
2019-03-05 18:30:10 +00:00
ssh: SSHClient = paramiko.SSHClient()
try:
k = paramiko.RSAKey.from_private_key(io.StringIO(key))
ssh.set_missing_host_key_policy(paramiko.AutoAddPolicy())
2020-01-13 08:22:37 +00:00
print(host, user, port)
ssh.connect(hostname=host, username=user, pkey=k, port=port, password=password, timeout=3)
2019-03-05 18:30:10 +00:00
# If a target is set, make sure the directory is created and writable
if target is not None:
try:
execute(ssh, 'mkdir -p {}'.format(target))
tmp_file = get_random_string()
execute(ssh, 'touch {}; rm {}'.format(tmp_file, tmp_file), target)
except Exception:
raise Exception('Could not create directory')
sftp = ssh.open_sftp()
try:
# DELETE
for delete in deletes:
sftp.remove(join(target, delete))
# COPY
2020-01-13 08:22:37 +00:00
if len(sources) != 0:
archive = get_random_string(64) + '.tar.gz' # Keep the max file name length under 128 chars
2019-03-05 18:30:10 +00:00
archive_local = abspath(archive)
archive_remote = join(target, archive)
# Compress
cmd = ['tar', '-czf', archive, '-C', dirname(archive_local), *sources]
run = subprocess.run(cmd, capture_output=True)
2020-01-13 08:22:37 +00:00
if run.returncode != 0:
2019-03-05 18:30:10 +00:00
raise Exception('Error while compressing locally. {}'.format(run.stderr.decode('utf-8').strip()))
# Upload
sftp.put(archive_local, archive_remote)
# Extract
execute(ssh, 'tar -xzf {}'.format(archive), target)
# Delete Archives
sftp.remove(archive_remote)
subprocess.run(['rm', archive_local], capture_output=True)
finally:
sftp.close()
for command in commands:
2019-05-25 14:55:08 +00:00
output = execute(ssh, command, target, envs)
2019-03-05 19:24:51 +00:00
print(command)
2019-03-05 18:30:10 +00:00
print(output)
finally:
ssh.close()
with warnings.catch_warnings():
warnings.simplefilter('ignore')
main()