From 08d1bffdf645a118521c8120e6a84c898ec36785 Mon Sep 17 00:00:00 2001 From: Iain Sproat <68657+iainsproat@users.noreply.github.com> Date: Fri, 21 Jun 2024 17:26:04 +0100 Subject: [PATCH] chore(do/1-click): sunset DigitalOcean 1-click app (#2074) --- README.md | 5 +- utils/1click_image_scripts/download.sh | 17 -- utils/1click_image_scripts/setup.py | 203 +----------------- .../template-docker-compose.yml | 129 ----------- .../template-nginx-site.conf | 35 --- 5 files changed, 10 insertions(+), 379 deletions(-) delete mode 100644 utils/1click_image_scripts/download.sh delete mode 100644 utils/1click_image_scripts/template-docker-compose.yml delete mode 100644 utils/1click_image_scripts/template-nginx-site.conf diff --git a/README.md b/README.md index c388b4abe7..e929eabadd 100644 --- a/README.md +++ b/README.md @@ -39,8 +39,9 @@ What is Speckle? Check our [![YouTube Video Views](https://img.shields.io/youtub Give Speckle a try in no time by: -- [![speckle](https://img.shields.io/badge/https://-app.speckle.systems-0069ff?style=flat-square&logo=hackthebox&logoColor=white)](https://app.speckle.systems) ⇒ creating an account -- [![create a droplet](https://img.shields.io/badge/Create%20a%20Droplet-0069ff?style=flat-square&logo=digitalocean&logoColor=white)](https://marketplace.digitalocean.com/apps/speckle-server?refcode=947a2b5d7dc1) ⇒ deploying an instance in 1 click +- [![app.speckle.systems](https://img.shields.io/badge/https://-app.speckle.systems-0069ff?style=flat-square&logo=hackthebox&logoColor=white)](https://app.speckle.systems) ⇒ Create an account at app.speckle.systems +- [![Deploy on your own infrastructure with docker compose](https://img.shields.io/badge/https://-speckle.guide-0069ff?style=flat-square&logo=hackthebox&logoColor=white)](<[https://](https://speckle.guide/dev/server-manualsetup.html)>) ⇒ Deploy on your own infrastructure with Docker Compose +- [![Deploy on your own infrastructure with docker compose](https://img.shields.io/badge/https://-speckle.guide-0069ff?style=flat-square&logo=hackthebox&logoColor=white)](<[https://](https://speckle.guide/dev/server-setup-k8s.html)>) ⇒ Deploy on your own infrastructure with Kubernetes ## Resources diff --git a/utils/1click_image_scripts/download.sh b/utils/1click_image_scripts/download.sh deleted file mode 100644 index 68d446a259..0000000000 --- a/utils/1click_image_scripts/download.sh +++ /dev/null @@ -1,17 +0,0 @@ -#!/bin/bash -set -euo pipefail - -echo "* Getting latest version of SpeckleServer Setup files..." - -mkdir -p /opt/speckle-server -cd /opt/speckle-server || exit 1 - -wget https://raw.githubusercontent.com/specklesystems/speckle-server/main/utils/1click_image_scripts/setup.py -O setup.py -wget https://raw.githubusercontent.com/specklesystems/speckle-server/main/utils/1click_image_scripts/template-nginx-site.conf -O template-nginx-site.conf -wget https://raw.githubusercontent.com/specklesystems/speckle-server/main/utils/1click_image_scripts/template-docker-compose.yml -O template-docker-compose.yml - -docker image rm {speckle/speckle-preview-service:2,speckle/speckle-webhook-service:2,speckle/speckle-server:2,speckle/speckle-frontend:2} || true -chmod +x setup.py - -echo "* Getting the docker images for the latest SpeckleServer release..." -docker compose -f template-docker-compose.yml pull diff --git a/utils/1click_image_scripts/setup.py b/utils/1click_image_scripts/setup.py index 0d8f133b10..c93cb184ee 100755 --- a/utils/1click_image_scripts/setup.py +++ b/utils/1click_image_scripts/setup.py @@ -1,14 +1,5 @@ #!/usr/bin/env python3 -import os -import socket -import subprocess -import secrets -import ruamel.yaml # this module preserves yaml comments and whitespaces -from ruamel.yaml.scalarstring import DoubleQuotedScalarString - -FILE_PATH = os.path.dirname(os.path.abspath(__file__)) - LOGO_STR = """ _____ _ _ _____ / ___| | | | | / ___| @@ -21,202 +12,22 @@ """ -def get_local_ip(): - s = socket.socket(socket.AF_INET, socket.SOCK_DGRAM) - s.connect(("8.8.8.8", 80)) - ip = s.getsockname()[0] - s.close() - if not ip: - print("Error: Can't get local IP address") - exit(1) - return ip - - -def read_domain(ip): - print("\nYou can set up a domain name for this Speckle server.") - print( - "Important: To use a domain name, you must first configure it to point to this VM address (so we can issue the SSL certificate)" - ) - print(f"VM address: {ip}") - while True: - domain = input("Domain name (leave blank to use the IP address): ").strip() - if not domain: - return None - try: - domain_ip = socket.gethostbyname(domain.strip()) - except Exception as ex: - print(f"Error: Domain '{domain}' cannot be resolved: {str(ex)}") - continue - - if domain_ip != ip: - print(f"Error: Domain '{domain}' points to {domain_ip} instead of {ip}") - continue - - return domain - - -def read_email_settings(domain): +def main(): + print(LOGO_STR) print( - "\nYou should configure an email provider to allow the Speckle Server to send emails." + "\nAs of March 2024, Speckle server DigitalOcean 1-click setup script is no longer supported." ) print( - "Supported vendors: Any email provider that can provide SMTP connection details (mailjet, mailgun, etc)." + "\nPlease use the official Speckle Server installation guide to install Speckle Server on your own infrastructure." ) print( - "Important: If you don't configure email details, some features that require sending emails will not work, nevertheless the server should be functional." + "\nThis could be on a DigitalOcean Droplet that you have created yourself using an Ubuntu image." ) - while True: - enable_email = False - while True: - enable_email = input("Enable emails? [Y/n]: ").strip().lower() - if enable_email in ["n", "no"]: - enable_email = False - break - elif enable_email in ["", "y", "yes"]: - enable_email = True - break - else: - print("Unrecognized option") - continue - - if not enable_email: - return None - - print("Enter your SMTP connection details offered by your email provider") - smtp_host = input("SMTP server / host: ").strip() - smtp_port = input("SMTP port: ").strip() - try: - int(smtp_port) - except Exception: - print("Error: SMTP port must be a number. Retrying...") - continue - smtp_user = input("SMTP Username: ").strip() - smtp_pass = input("SMTP Password: ").strip() - - if domain: - default_from_email = "no-reply@" + domain - else: - default_from_email = "" - email_from = input(f"Email address to send email as [{default_from_email}]: ") - if not email_from.strip(): - email_from = default_from_email - - if ( - not smtp_host - or not smtp_port - or not smtp_user - or not smtp_pass - or not email_from - ): - print("Error: One or more fields were empty. Retrying...") - continue - - return { - "host": smtp_host, - "port": smtp_port, - "user": smtp_user, - "pass": smtp_pass, - "from": email_from, - } - - -def main(): - print(LOGO_STR) - ip = get_local_ip() - - ### - ### Read user input - ######### - domain = read_domain(ip) - if domain: - canonical_url = f"https://{domain}" - else: - canonical_url = f"http://{ip}" - - email = read_email_settings(domain) - - ### - ### Create docker-compose.yml from the template - ######### - print("\nConfiguring docker containers...") - - yaml = ruamel.yaml.YAML() - yaml.preserve_quotes = True - with open(os.path.join(FILE_PATH, "template-docker-compose.yml"), "r") as f: - yml_doc = yaml.load(f) - env = yml_doc["services"]["speckle-server"]["environment"] - env["CANONICAL_URL"] = DoubleQuotedScalarString(canonical_url) - env["FRONTEND_ORIGIN"] = DoubleQuotedScalarString(canonical_url) - env["SESSION_SECRET"] = DoubleQuotedScalarString(secrets.token_hex(32)) - if email: - env["EMAIL"] = DoubleQuotedScalarString("true") - env["EMAIL_HOST"] = DoubleQuotedScalarString(email["host"]) - env["EMAIL_PORT"] = DoubleQuotedScalarString(email["port"]) - env["EMAIL_USERNAME"] = DoubleQuotedScalarString(email["user"]) - env["EMAIL_PASSWORD"] = DoubleQuotedScalarString(email["pass"]) - env["EMAIL_FROM"] = DoubleQuotedScalarString(email["from"]) - else: - env["EMAIL"] = DoubleQuotedScalarString("false") - - fe2env = yml_doc["services"]["speckle-frontend-2"]["environment"] - fe2env["NUXT_PUBLIC_SERVER_NAME"] = DoubleQuotedScalarString(canonical_url) - fe2env["NUXT_PUBLIC_API_ORIGIN"] = DoubleQuotedScalarString(canonical_url) - fe2env["NUXT_PUBLIC_BASE_URL"] = DoubleQuotedScalarString(canonical_url) - - with open(os.path.join(FILE_PATH, "docker-compose.yml"), "w") as f: - f.write("# This file was generated by SpeckleServer setup.\n") - f.write("# If the setup is re-run, this file will be overwritten.\n\n") - yaml.dump(yml_doc, f) - - ### - ### Run the new docker compose file (will update containers if already running) - ######### - subprocess.run( - ["bash", "-c", f'cd "{FILE_PATH}"; docker compose up -d'], check=True - ) - - ### - ### Update nginx config and restart nginx - ######### - print("\nConfiguring local nginx...") - - nginx_conf_str = "# This file is managed by SpeckleServer setup script.\n" - nginx_conf_str += ( - "# Any modifications will be removed when the setup script is re-executed\n\n" - ) - with open(os.path.join(FILE_PATH, "template-nginx-site.conf"), "r") as f: - nginx_conf_str += f.read() - if domain: - nginx_conf_str = nginx_conf_str.replace("TODO_REPLACE_WITH_SERVER_NAME", domain) - else: - nginx_conf_str = nginx_conf_str.replace("TODO_REPLACE_WITH_SERVER_NAME", "_") - with open("/etc/nginx/sites-available/speckle-server", "w") as f: - f.write(nginx_conf_str) - subprocess.run(["nginx", "-s", "reload"], check=True) - - ### - ### Run letsencrypt on new config - ######### - if domain: - print("\n***") - print( - "*** Will now run LetsEncrypt utility to generate https certificate. Please answer any questions that are presented" - ) - print( - "*** We highly recommend setting a good email address so that you are notified if there is any action needed to renew certificates" - ) - print("***") - subprocess.run(["certbot", "--nginx", "-d", domain]) - - print("\nConfiguration complete!") - print("You can access your speckle server at: " + canonical_url) - print(LOGO_STR) - print("\nOne more thing and you are ready to roll:") print( - f" - Go to {canonical_url} in your browser and create an account. The first user to register will be granted administrator rights." + "\nOur documentation can be found at https://speckle.guide/dev/server-manualsetup.html" ) print( - " - Fill in information about your server under your profile page (in the lower left corner)." + "\nIf you require to view previous versions of the 1-click setup script, please review the history of the speckle-server repository on GitHub." ) print("\nHappy Speckling!") diff --git a/utils/1click_image_scripts/template-docker-compose.yml b/utils/1click_image_scripts/template-docker-compose.yml deleted file mode 100644 index b57d90ee8b..0000000000 --- a/utils/1click_image_scripts/template-docker-compose.yml +++ /dev/null @@ -1,129 +0,0 @@ -version: '2.3' -services: - #### - # Speckle Server dependencies - ####### - postgres: - image: 'postgres:14.5-alpine' - restart: always - environment: - POSTGRES_DB: speckle - POSTGRES_USER: speckle - POSTGRES_PASSWORD: speckle - volumes: - - ./postgres-data:/var/lib/postgresql/data/ - ports: - - '127.0.0.1:5432:5432' - - redis: - image: 'redis:7.0-alpine' - restart: always - volumes: - - ./redis-data:/data - ports: - - '127.0.0.1:6379:6379' - - minio: - image: 'minio/minio' - command: server /data --console-address ":9001" - restart: always - volumes: - - ./minio-data:/data - ports: - - '127.0.0.1:9000:9000' - - '127.0.0.1:9001:9001' - - #### - # Speckle Server - ####### - speckle-frontend-2: - image: speckle/speckle-frontend-2:2 - restart: always - ports: - - '127.0.0.1:8080:8080' - environment: - NUXT_PUBLIC_SERVER_NAME: 'TODO: change' # e.g. 'my-speckle-server' - NUXT_PUBLIC_API_ORIGIN: 'TODO: change' # e.g. 'http://127.0.0.1' - NUXT_PUBLIC_BASE_URL: 'TODO: change' # e.g. 'http://127.0.0.1' - NUXT_PUBLIC_BACKEND_API_ORIGIN: 'http://speckle-server:3000' - NUXT_PUBLIC_LOG_LEVEL: 'warn' - NUXT_REDIS_URL: 'redis://redis' - - speckle-server: - image: speckle/speckle-server:2 - restart: always - healthcheck: - test: - - CMD - - node - - -e - - "try { require('node:http').request({headers: {'Content-Type': 'application/json'}, port:3000, hostname:'127.0.0.1', path:'/liveness', method: 'GET', timeout: 2000 }, (res) => { body = ''; res.on('data', (chunk) => {body += chunk;}); res.on('end', () => {process.exit(res.statusCode != 200 || body.toLowerCase().includes('error'));}); }).end(); } catch { process.exit(1); }" - interval: 10s - timeout: 10s - retries: 3 - start_period: 90s - ports: - - '127.0.0.1:3000:3000' - environment: - CANONICAL_URL: 'TODO: change' - SESSION_SECRET: 'TODO: change' - - STRATEGY_LOCAL: 'true' - LOG_LEVEL: 'info' - - POSTGRES_URL: 'postgres' - POSTGRES_USER: 'speckle' - POSTGRES_PASSWORD: 'speckle' - POSTGRES_DB: 'speckle' - - REDIS_URL: 'redis://redis' - WAIT_HOSTS: 'postgres:5432, redis:6379, minio:9000' - - EMAIL: 'false' - EMAIL_HOST: 'TODO' - EMAIL_PORT: 'TODO' - EMAIL_USERNAME: 'TODO' - EMAIL_PASSWORD: 'TODO' - EMAIL_FROM: 'TODO' - - EMAIL_SECURE: 'false' - - S3_ENDPOINT: 'http://minio:9000' - S3_ACCESS_KEY: 'minioadmin' - S3_SECRET_KEY: 'minioadmin' - S3_BUCKET: 'speckle-server' - S3_CREATE_BUCKET: 'true' - S3_REGION: '' # optional, defaults to 'us-east-1' - - FILE_SIZE_LIMIT_MB: 100 - - USE_FRONTEND_2: 'true' - FRONTEND_ORIGIN: 'TODO: change' - - speckle-preview-service: - image: speckle/speckle-preview-service:2 - restart: always - mem_limit: '1000m' - memswap_limit: '1000m' - - environment: - LOG_LEVEL: 'info' - PG_CONNECTION_STRING: 'postgres://speckle:speckle@postgres/speckle' - WAIT_HOSTS: 'postgres:5432' - - speckle-webhook-service: - image: speckle/speckle-webhook-service:2 - restart: always - environment: - LOG_LEVEL: 'info' - PG_CONNECTION_STRING: 'postgres://speckle:speckle@postgres/speckle' - WAIT_HOSTS: 'postgres:5432' - - fileimport-service: - image: speckle/speckle-fileimport-service:2 - restart: always - environment: - LOG_LEVEL: 'info' - PG_CONNECTION_STRING: 'postgres://speckle:speckle@postgres/speckle' - WAIT_HOSTS: 'postgres:5432' - SPECKLE_SERVER_URL: 'http://speckle-server:3000' diff --git a/utils/1click_image_scripts/template-nginx-site.conf b/utils/1click_image_scripts/template-nginx-site.conf deleted file mode 100644 index b978f6cfa9..0000000000 --- a/utils/1click_image_scripts/template-nginx-site.conf +++ /dev/null @@ -1,35 +0,0 @@ -server { - listen 80 default_server; - listen [::]:80 default_server; - - server_name TODO_REPLACE_WITH_SERVER_NAME; - - client_max_body_size 100m; - - location / { - client_max_body_size 100m; - proxy_set_header Host $host; - proxy_set_header X-Real-IP $remote_addr; - proxy_pass http://localhost:8080; - - proxy_buffering off; - proxy_request_buffering off; - proxy_http_version 1.1; - proxy_set_header Upgrade $http_upgrade; - proxy_set_header Connection "upgrade"; - } - - location ~* ^/(graphql|explorer|(auth/.*)|(objects/.*)|(preview/.*)|(api/.*)|(static/.*)) { - client_max_body_size 100m; - proxy_set_header Host $host; - proxy_set_header X-Real-IP $remote_addr; - proxy_pass http://localhost:3000; - - proxy_buffering off; - proxy_request_buffering off; - proxy_http_version 1.1; - proxy_set_header Upgrade $http_upgrade; - proxy_set_header Connection "upgrade"; - } -} -