From 80f04de3ec69fc980d6446991d78b81db3758a7d Mon Sep 17 00:00:00 2001 From: Ivan Gromov Date: Wed, 28 Oct 2020 00:54:45 +0500 Subject: [PATCH] Updated DigitalOcean UX, proxied requests to it --- app/server.py | 56 ++++++++++++--- app/static/provider-do.vue | 130 ++++++++++++++++++++++------------ app/static/provider-vultr.vue | 2 +- app/static/region-select.vue | 18 +++-- 4 files changed, 144 insertions(+), 62 deletions(-) diff --git a/app/server.py b/app/server.py index 5eb134d..be1a5d4 100644 --- a/app/server.py +++ b/app/server.py @@ -1,18 +1,28 @@ import asyncio +import concurrent.futures import json +import os +import sys +from os.path import join, dirname, expanduser import yaml -import boto3 -from os.path import join, dirname, expanduser from aiohttp import web, ClientSession -import concurrent.futures -import sys -import os -from google.auth.transport.requests import AuthorizedSession -from google.oauth2 import service_account from playbook import PlaybookCLI +try: + import boto3 + HAS_BOTO3 = True +except ImportError: + HAS_BOTO3 = False + +try: + from google.auth.transport.requests import AuthorizedSession + from google.oauth2 import service_account + HAS_GOOGLE_LIBRARIES = True +except ImportError: + HAS_GOOGLE_LIBRARIES = False + routes = web.RouteTableDef() PROJECT_ROOT = dirname(dirname(__file__)) pool = None @@ -63,7 +73,7 @@ async def playbook_post_handler(request): @routes.delete('/playbook') -async def playbook_delete_handler(request): +async def playbook_delete_handler(_): global task_future if not task_future: return web.json_response({'ok': False}) @@ -105,6 +115,30 @@ async def post_exit(_): sys.exit(1) +@routes.get('/do_config') +async def check_do_config(_): + return web.json_response({"ok": 'DO_API_TOKEN' in os.environ}) + + +@routes.get('/do_regions') +async def do_regions(request): + if 'token' in request.query: + token = request.query['token'] + elif 'DO_API_TOKEN' in os.environ: + token = os.environ['DO_API_TOKEN'] + else: + return web.json_response({'error': 'no token provided'}, status=400) + + headers = { + 'Content-Type': 'application/json', + 'Authorization': 'Bearer {0}'.format(token), + } + async with ClientSession(headers=headers) as session: + async with session.get('https://api.digitalocean.com/v2/regions') as r: + json_body = await r.json() + return web.json_response(json_body, status=r.status) + + @routes.post('/lightsail_regions') async def lightsail_regions(request): data = await request.json() @@ -132,7 +166,7 @@ async def ec2_regions(request): @routes.get('/gce_config') -async def check_gce_config(request): +async def check_gce_config(_): gce_file = join(PROJECT_ROOT, 'configs', 'gce.json') response = {} try: @@ -189,7 +223,7 @@ async def check_vultr_config(request): @routes.get('/vultr_regions') -async def vultr_regions(request): +async def vultr_regions(_): async with ClientSession() as session: async with session.get('https://api.vultr.com/v1/regions/list') as r: json_body = await r.json() @@ -197,7 +231,7 @@ async def vultr_regions(request): @routes.get('/scaleway_config') -async def check_scaleway_config(request): +async def check_scaleway_config(_): return web.json_response({"ok": 'SCW_TOKEN' in os.environ}) diff --git a/app/static/provider-do.vue b/app/static/provider-do.vue index 90c00dd..58de4ad 100644 --- a/app/static/provider-do.vue +++ b/app/static/provider-do.vue @@ -5,41 +5,37 @@ Enter your API token. The token must have read and write permissions (https://cloud.digitalocean.com/settings/api/tokens): - + + + +
+ + @blur="load_regions" + /> +
+ -
- - - - -
- + + + @@ -48,34 +44,76 @@ module.exports = { data: function() { return { do_token: null, - do_region: null, - do_regions: [], - do_region_loading: false + region: null, + // helper variables + ui_loading_check: false, + ui_loading_regions: false, + ui_region_error: null, + ui_token_from_env: false, + ui_region_options: [] } }, + computed: { + is_valid() { + return (this.do_config || this.ui_token_from_env) && this.region; + } + }, + created: function() { + this.check_config(); + }, methods: { - load_do_regions: function () { - this.do_region_loading = true; - fetch('https://api.digitalocean.com/v2/regions', { - headers: { - 'Content-Type': 'application/json', - 'Authorization': `Bearer ${this.do_token}` + check_config() { + this.ui_loading_check = true; + return fetch("/do_config") + .then(r => r.json()) + .then(response => { + if (response.ok) { + this.ui_token_from_env = true; + this.load_regions(); } }) - .then(r => r.json()) - .then(r => { - this.do_regions = r.regions; + .finally(() => { + this.ui_loading_check = false; + }); + }, + load_regions() { + if (this.ui_token_from_env || this.do_token) { + this.ui_loading_regions = true; + this.ui_region_error = null; + const url = this.ui_token_from_env ? "/do_regions" : "/do_regions?token=" + this.do_token; + fetch(url) + .then((r) => { + if (r.status === 200) { + return r.json(); + } + throw new Error(r.status); + }) + .then((data) => { + this.ui_region_options = data.regions.map(i => ({key: i.slug, value: i.name})); + }) + .catch((err) => { + this.ui_region_error = err; }) .finally(() => { - this.do_region_loading = false; + this.ui_loading_regions = false; }); + } }, submit() { - this.$emit('submit', { - do_token: this.do_token, - region: this.do_region - }) + if (this.ui_token_from_env) { + this.$emit("submit", { + region: this.region + }); + } else { + this.$emit("submit", { + do_token: this.do_token, + region: this.region + }); + } } + }, + components: { + "region-select": window.httpVueLoader("/static/region-select.vue"), } }; diff --git a/app/static/provider-vultr.vue b/app/static/provider-vultr.vue index 2d156db..8c1329b 100644 --- a/app/static/provider-vultr.vue +++ b/app/static/provider-vultr.vue @@ -103,6 +103,6 @@ module.exports = { }, components: { "region-select": window.httpVueLoader("/static/region-select.vue"), - }, + } }; \ No newline at end of file diff --git a/app/static/region-select.vue b/app/static/region-select.vue index ce2ea82..e6e74df 100644 --- a/app/static/region-select.vue +++ b/app/static/region-select.vue @@ -8,6 +8,7 @@ +
+ There was an error during fetching regions +