From c1d80561a697f767bf9c077c2415d052690e4664 Mon Sep 17 00:00:00 2001 From: Vishnu KS Date: Thu, 18 Mar 2021 00:33:43 +0530 Subject: [PATCH] droplets: Add support for creating prod droplets. --- tools/droplets/README.md | 9 ++++ tools/droplets/create.py | 102 +++++++++++++++++++++++++++++++-------- 2 files changed, 91 insertions(+), 20 deletions(-) diff --git a/tools/droplets/README.md b/tools/droplets/README.md index 80b9ac91bd..7303cf2d98 100644 --- a/tools/droplets/README.md +++ b/tools/droplets/README.md @@ -210,3 +210,12 @@ They can remove your SSH keys by running: ``` $ python3 ~/zulip/tools/droplets/add_mentor.py --remove ``` + + +# Creating a production droplet + +`create.py` can also create a production droplet quickly for testing purposes. + +``` +$ python3 create.py --production +``` diff --git a/tools/droplets/create.py b/tools/droplets/create.py index 759711dd7f..08938a52a9 100644 --- a/tools/droplets/create.py +++ b/tools/droplets/create.py @@ -1,4 +1,6 @@ -# Creates a Droplet on DigitalOcean for remote Zulip development. +# Creates a Zulip remote development environment droplet or +# a production droplet in DigitalOcean. +# # Particularly useful for sprints/hackathons, interns, and other # situation where one wants to quickly onboard new contributors. # @@ -25,6 +27,7 @@ import urllib.request from typing import Any, Dict, List import digitalocean +import requests # initiation argument parser parser = argparse.ArgumentParser(description="Create a Zulip devopment VM DigitalOcean droplet.") @@ -33,6 +36,7 @@ parser.add_argument( ) parser.add_argument("--tags", nargs="+", default=[]) parser.add_argument("-f", "--recreate", action="store_true") +parser.add_argument("-p", "--production", action="store_true") def get_config() -> configparser.ConfigParser: @@ -109,7 +113,7 @@ def get_ssh_keys_string_from_github_ssh_key_dicts(userkey_dicts: List[Dict[str, return "\n".join([userkey_dict["key"] for userkey_dict in userkey_dicts]) -def set_user_data(username: str, userkey_dicts: List[Dict[str, Any]]) -> str: +def generate_dev_droplet_user_data(username: str, userkey_dicts: List[Dict[str, Any]]) -> str: ssh_keys_string = get_ssh_keys_string_from_github_ssh_key_dicts(userkey_dicts) setup_root_ssh_keys = f"printf '{ssh_keys_string}' > /root/.ssh/authorized_keys" setup_zulipdev_ssh_keys = f"printf '{ssh_keys_string}' > /home/zulipdev/.ssh/authorized_keys" @@ -146,6 +150,22 @@ su -c 'git config --global pull.rebase true' zulipdev return cloudconf +def generate_prod_droplet_user_data(username: str, userkey_dicts: List[Dict[str, Any]]) -> str: + ssh_keys_string = get_ssh_keys_string_from_github_ssh_key_dicts(userkey_dicts) + setup_root_ssh_keys = f"printf '{ssh_keys_string}' > /root/.ssh/authorized_keys" + + cloudconf = f"""\ +#!/bin/bash + +{setup_root_ssh_keys} +passwd -d root +sed -i "s/PasswordAuthentication yes/PasswordAuthentication no/g" /etc/ssh/sshd_config +service ssh restart +""" + print("...returning cloud-config data.") + return cloudconf + + def create_droplet( my_token: str, template_id: str, name: str, tags: List[str], user_data: str ) -> str: @@ -205,7 +225,7 @@ def create_dns_record(my_token: str, record_name: str, ip_address: str) -> None: domain.create_new_domain_record(type="A", name=wildcard_name, data=ip_address) -def print_completion(droplet_domain_name: str) -> None: +def print_dev_droplet_instructions(droplet_domain_name: str) -> None: print( """ COMPLETE! Droplet for GitHub user {0} is available at {0}.zulipdev.org. @@ -242,41 +262,80 @@ Your remote Zulip dev server has been created! print("------") -if __name__ == "__main__": - # define id of image to create new droplets from - # You can get this with something like the following. You may need to try other pages. - # Broken in two to satisfy linter (line too long) - # curl -X GET -H "Content-Type: application/json" -u : "https://api.digitaloc - # ean.com/v2/images?page=5" | grep --color=always base.zulipdev.org - template_id = "63219191" +def print_production_droplet_instructions(droplet_domain_name: str) -> None: + print( + """ +----- +Production droplet created successfully! + +Connect to the server by running + +ssh root@{} + +----- +""".format( + droplet_domain_name + ) + ) + + +def get_zulip_oneclick_app_slug(api_token: str) -> str: + response = requests.get( + "https://api.digitalocean.com/v2/1-clicks", headers={"Authorization": f"Bearer {api_token}"} + ).json() + one_clicks = response["1_clicks"] + + for one_click in one_clicks: + if one_click["slug"].startswith("kandralabs"): + return one_click["slug"] + raise Exception("Unable to find Zulip One-click app slug") + + +if __name__ == "__main__": # get command line arguments args = parser.parse_args() username = args.username.lower() - print(f"Creating Zulip developer environment for GitHub user {username}...") - subdomain = username - droplet_domain_name = f"{subdomain}.zulipdev.org" + if args.production: + print(f"Creating production droplet for GitHub user {username}...") + else: + print(f"Creating Zulip developer environment for GitHub user {username}...") # get config details config = get_config() + api_token = config["digitalocean"]["api_token"] assert_github_user_exists(github_username=username) # grab user's public keys public_keys = get_keys(username=username) - # now make sure the user has forked zulip/zulip - fork_exists(username=username) + if args.production: + subdomain = f"{username}-prod" + droplet_domain_name = f"{subdomain}.zulipdev.org" + template_id = get_zulip_oneclick_app_slug(api_token) + user_data = generate_prod_droplet_user_data(username=username, userkey_dicts=public_keys) + + else: + # now make sure the user has forked zulip/zulip + fork_exists(username=username) + + subdomain = username + droplet_domain_name = f"{subdomain}.zulipdev.org" + user_data = generate_dev_droplet_user_data(username=username, userkey_dicts=public_keys) + + # define id of image to create new droplets from + # You can get this with something like the following. You may need to try other pages. + # Broken in two to satisfy linter (line too long) + # curl -X GET -H "Content-Type: application/json" -u : "https://api.digitaloc + # ean.com/v2/images?page=5" | grep --color=always base.zulipdev.org + template_id = "63219191" - api_token = config["digitalocean"]["api_token"] assert_droplet_does_not_exist( my_token=api_token, droplet_name=droplet_domain_name, recreate=args.recreate ) - # set user_data - user_data = set_user_data(username=username, userkey_dicts=public_keys) - # create droplet ip_address = create_droplet( my_token=api_token, @@ -288,6 +347,9 @@ if __name__ == "__main__": create_dns_record(my_token=api_token, record_name=subdomain, ip_address=ip_address) - print_completion(droplet_domain_name=droplet_domain_name) + if args.production: + print_production_droplet_instructions(droplet_domain_name=droplet_domain_name) + else: + print_dev_droplet_instructions(droplet_domain_name=droplet_domain_name) sys.exit(1)