Skip to content

Commit 7effd85

Browse files
committed
Updated DigitalOcean UX, proxied requests to it
1 parent 4b576e1 commit 7effd85

File tree

4 files changed

+144
-62
lines changed

4 files changed

+144
-62
lines changed

app/server.py

Lines changed: 45 additions & 11 deletions
Original file line numberDiff line numberDiff line change
@@ -1,18 +1,28 @@
11
import asyncio
2+
import concurrent.futures
23
import json
4+
import os
5+
import sys
6+
from os.path import join, dirname, expanduser
37

48
import yaml
5-
import boto3
6-
from os.path import join, dirname, expanduser
79
from aiohttp import web, ClientSession
8-
import concurrent.futures
9-
import sys
10-
import os
1110

12-
from google.auth.transport.requests import AuthorizedSession
13-
from google.oauth2 import service_account
1411
from playbook import PlaybookCLI
1512

13+
try:
14+
import boto3
15+
HAS_BOTO3 = True
16+
except ImportError:
17+
HAS_BOTO3 = False
18+
19+
try:
20+
from google.auth.transport.requests import AuthorizedSession
21+
from google.oauth2 import service_account
22+
HAS_GOOGLE_LIBRARIES = True
23+
except ImportError:
24+
HAS_GOOGLE_LIBRARIES = False
25+
1626
routes = web.RouteTableDef()
1727
PROJECT_ROOT = dirname(dirname(__file__))
1828
pool = None
@@ -63,7 +73,7 @@ async def playbook_post_handler(request):
6373

6474

6575
@routes.delete('/playbook')
66-
async def playbook_delete_handler(request):
76+
async def playbook_delete_handler(_):
6777
global task_future
6878
if not task_future:
6979
return web.json_response({'ok': False})
@@ -105,6 +115,30 @@ async def post_exit(_):
105115
sys.exit(1)
106116

107117

118+
@routes.get('/do_config')
119+
async def check_do_config(_):
120+
return web.json_response({"ok": 'DO_API_TOKEN' in os.environ})
121+
122+
123+
@routes.get('/do_regions')
124+
async def do_regions(request):
125+
if 'token' in request.query:
126+
token = request.query['token']
127+
elif 'DO_API_TOKEN' in os.environ:
128+
token = os.environ['DO_API_TOKEN']
129+
else:
130+
return web.json_response({'error': 'no token provided'}, status=400)
131+
132+
headers = {
133+
'Content-Type': 'application/json',
134+
'Authorization': 'Bearer {0}'.format(token),
135+
}
136+
async with ClientSession(headers=headers) as session:
137+
async with session.get('https://api.digitalocean.com/v2/regions') as r:
138+
json_body = await r.json()
139+
return web.json_response(json_body, status=r.status)
140+
141+
108142
@routes.post('/lightsail_regions')
109143
async def lightsail_regions(request):
110144
data = await request.json()
@@ -132,7 +166,7 @@ async def ec2_regions(request):
132166

133167

134168
@routes.get('/gce_config')
135-
async def check_gce_config(request):
169+
async def check_gce_config(_):
136170
gce_file = join(PROJECT_ROOT, 'configs', 'gce.json')
137171
response = {}
138172
try:
@@ -189,15 +223,15 @@ async def check_vultr_config(request):
189223

190224

191225
@routes.get('/vultr_regions')
192-
async def vultr_regions(request):
226+
async def vultr_regions(_):
193227
async with ClientSession() as session:
194228
async with session.get('https://api.vultr.com/v1/regions/list') as r:
195229
json_body = await r.json()
196230
return web.json_response(json_body)
197231

198232

199233
@routes.get('/scaleway_config')
200-
async def check_scaleway_config(request):
234+
async def check_scaleway_config(_):
201235
return web.json_response({"ok": 'SCW_TOKEN' in os.environ})
202236

203237

app/static/provider-do.vue

Lines changed: 84 additions & 46 deletions
Original file line numberDiff line numberDiff line change
@@ -5,41 +5,37 @@
55
Enter your API token. The token must have read and write permissions
66
(<a href="https://cloud.digitalocean.com/settings/api/tokens" target="_blank" rel="noopener noreferrer">https://cloud.digitalocean.com/settings/api/tokens</a>):
77
</label>
8-
<input
8+
<div v-if="ui_token_from_env">
9+
<input
10+
type="password"
11+
class="form-control"
12+
v-bind:disabled="ui_loading_check"
13+
v-bind:value="'1234567890abcdef'"
14+
/>
15+
<div v-if="ui_token_from_env" class="form-text alert alert-success" role="alert">
16+
The token was read from the environment variable
17+
</div>
18+
</div>
19+
<div v-else>
20+
<input
921
type="text"
1022
class="form-control"
1123
id="id_do_token"
1224
name="do_token"
25+
v-bind:disabled="ui_loading_check"
1326
v-model="do_token"
14-
@blur="load_do_regions"
15-
/>
16-
</div>
17-
<div class="form-group">
18-
<label
19-
v-if="do_regions.length > 0"
20-
for="id_region"
21-
>What region should the server be located in?</label>
22-
<label
23-
v-if="do_regions.length === 0"
24-
for="id_region"
25-
>Please enter API key above to select region</label>
26-
<label v-if="do_region_loading" for="id_region">Loading regions...</label>
27-
<select
28-
name="region"
29-
id="id_region"
30-
class="form-control"
31-
v-model="do_region"
32-
v-bind:disabled="do_region_loading"
33-
>
34-
<option value disabled>Select region</option>
35-
<option
36-
v-for="(region, i) in do_regions"
37-
v-bind:key="region.slug"
38-
v-bind:value="region.slug"
39-
>{{region.name}}</option>
40-
</select>
27+
@blur="load_regions"
28+
/>
29+
</div>
30+
4131
</div>
42-
<button v-on:click="submit" v-bind:disabled="!do_region" class="btn btn-primary" type="button">Next</button>
32+
<region-select v-model="region"
33+
v-bind:options="ui_region_options"
34+
v-bind:loading="ui_loading_check || ui_loading_regions"
35+
v-bind:error="ui_region_error">
36+
</region-select>
37+
<button v-on:click="submit"
38+
v-bind:disabled="!is_valid" class="btn btn-primary" type="button">Next</button>
4339
</div>
4440
</template>
4541

@@ -48,34 +44,76 @@ module.exports = {
4844
data: function() {
4945
return {
5046
do_token: null,
51-
do_region: null,
52-
do_regions: [],
53-
do_region_loading: false
47+
region: null,
48+
// helper variables
49+
ui_loading_check: false,
50+
ui_loading_regions: false,
51+
ui_region_error: null,
52+
ui_token_from_env: false,
53+
ui_region_options: []
54+
}
55+
},
56+
computed: {
57+
is_valid() {
58+
return (this.do_config || this.ui_token_from_env) && this.region;
5459
}
5560
},
61+
created: function() {
62+
this.check_config();
63+
},
5664
methods: {
57-
load_do_regions: function () {
58-
this.do_region_loading = true;
59-
fetch('https://api.digitalocean.com/v2/regions', {
60-
headers: {
61-
'Content-Type': 'application/json',
62-
'Authorization': `Bearer ${this.do_token}`
65+
check_config() {
66+
this.ui_loading_check = true;
67+
return fetch("/do_config")
68+
.then(r => r.json())
69+
.then(response => {
70+
if (response.ok) {
71+
this.ui_token_from_env = true;
72+
this.load_regions();
6373
}
6474
})
65-
.then(r => r.json())
66-
.then(r => {
67-
this.do_regions = r.regions;
75+
.finally(() => {
76+
this.ui_loading_check = false;
77+
});
78+
},
79+
load_regions() {
80+
if (this.ui_token_from_env || this.do_token) {
81+
this.ui_loading_regions = true;
82+
this.ui_region_error = null;
83+
const url = this.ui_token_from_env ? "/do_regions" : "/do_regions?token=" + this.do_token;
84+
fetch(url)
85+
.then((r) => {
86+
if (r.status === 200) {
87+
return r.json();
88+
}
89+
throw new Error(r.status);
90+
})
91+
.then((data) => {
92+
this.ui_region_options = data.regions.map(i => ({key: i.slug, value: i.name}));
93+
})
94+
.catch((err) => {
95+
this.ui_region_error = err;
6896
})
6997
.finally(() => {
70-
this.do_region_loading = false;
98+
this.ui_loading_regions = false;
7199
});
100+
}
72101
},
73102
submit() {
74-
this.$emit('submit', {
75-
do_token: this.do_token,
76-
region: this.do_region
77-
})
103+
if (this.ui_token_from_env) {
104+
this.$emit("submit", {
105+
region: this.region
106+
});
107+
} else {
108+
this.$emit("submit", {
109+
do_token: this.do_token,
110+
region: this.region
111+
});
112+
}
78113
}
114+
},
115+
components: {
116+
"region-select": window.httpVueLoader("/static/region-select.vue"),
79117
}
80118
};
81119
</script>

app/static/provider-vultr.vue

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -103,6 +103,6 @@ module.exports = {
103103
},
104104
components: {
105105
"region-select": window.httpVueLoader("/static/region-select.vue"),
106-
},
106+
}
107107
};
108108
</script>

app/static/region-select.vue

Lines changed: 14 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -8,6 +8,7 @@
88
<select
99
name="region"
1010
class="form-control"
11+
v-bind:class="{ 'is-invalid': has_error }"
1112
v-bind:value="value"
1213
v-bind:disabled="ui_disabled"
1314
v-on:input="$emit('input', $event.target.value)"
@@ -20,24 +21,33 @@
2021
>{{ region.value }}</option
2122
>
2223
</select>
24+
<div v-if="has_error" class="invalid-feedback">
25+
There was an error during fetching regions
26+
</div>
2327
</div>
2428
</template>
2529

2630
<script>
2731
module.exports = {
28-
props: ["value", "options", "loading"],
32+
props: ["value", "options", "loading", "error"],
2933
computed: {
34+
has_options: function() {
35+
return this.options && this.options.length;
36+
},
3037
ui_disabled: function() {
31-
return !this.options.length || this.loading;
38+
return !this.has_options || this.loading;
3239
},
3340
ui_show_slot: function() {
34-
return !this.loading && !this.options.length
41+
return !this.loading && !this.has_options
3542
},
3643
ui_show_loading: function() {
3744
return this.loading;
3845
},
3946
ui_show_select_prompt: function() {
40-
return this.options.length > 0;
47+
return this.has_options;
48+
},
49+
has_error: function() {
50+
return !!this.error;
4151
}
4252
}
4353
};

0 commit comments

Comments
 (0)