/
config.py
148 lines (126 loc) · 5.8 KB
/
config.py
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
import click
import dns.exception
import dns.resolver
import os
import requests
import subprocess
import yaml
from clickclick import Action, info
def get_path(section):
# "old" style config files (one file per app)
directory = click.get_app_dir(section)
path = os.path.join(directory, '{}.yaml'.format(section))
return path
def load_config(section):
'''Get configuration for given section/project
Tries to load YAML configuration file and also considers environment variables'''
path = get_path(section)
try:
with open(path, 'rb') as fd:
config = yaml.safe_load(fd)
except:
config = None
config = config or {}
env_prefix = '{}_'.format(section.upper().replace('-', '_'))
for key, val in os.environ.items():
if key.startswith(env_prefix):
config_key = key[len(env_prefix):].lower()
config[config_key] = val
return config
def store_config(config, section):
path = get_path(section)
dir_path = os.path.dirname(path)
if dir_path:
os.makedirs(dir_path, exist_ok=True)
try:
with open(path, 'w') as fd:
yaml.safe_dump(config, fd)
except PermissionError:
# we ignore permission errors here as users might make their config file readonly
# to prevent corrupt files when running multiple processes
pass
def is_valid_domain(domain):
try:
dns.resolver.query(domain, raise_on_no_answer=False)
return True
except:
return False
def configure(preselected_domain=None):
while True:
errors = None
autoconfigs = {}
urls = {}
existing_config = load_config('stups')
# first use the provided domain
domain = preselected_domain or existing_config.get('domain')
while True:
domain = click.prompt('Please enter your STUPS domain (e.g. "stups.example.org")', default=domain)
if is_valid_domain(domain):
break
else:
info('The entered domain is not valid. Please try again.')
for component in ('mai', 'zign', 'zalando-token-cli', 'zalando-aws-cli'):
with Action('Trying to autoconfigure {}..'.format(component)) as act:
try:
answer = dns.resolver.query('_{}._autoconfig.{}'.format(component, domain), 'TXT')
for rdata in answer.rrset.items:
for string in rdata.strings:
autoconfigs[component] = yaml.safe_load(string)
except dns.exception.DNSException as e:
act.error(str(e.__class__.__name__))
errors = True
except:
act.error('ERROR')
errors = True
for component in ('pierone', 'even', 'fullstop', 'kio'):
url = 'https://{}.{}'.format(component, domain)
with Action('Checking {}..'.format(url)) as act:
try:
requests.get(url, timeout=5, allow_redirects=False)
except:
act.error('ERROR')
errors = True
urls[component] = url
token_service_url = autoconfigs.get('zign', {}).get('token_service_url')
if token_service_url:
username = existing_config.get('user') or os.getenv('USER')
username = click.prompt('Please enter your OAuth username if it differs from $USER (e.g. "jdoe")',
default=username)
if not errors:
with Action('Writing global config..'):
store_config({'domain': domain}, 'stups')
with Action('Writing config for Pier One..'):
store_config({'url': urls['pierone']}, 'pierone')
with Action('Writing config for Più..'):
store_config({'even_url': urls['even']}, 'piu')
with Action('Writing config for Fullstop..'):
store_config({'url': urls['fullstop']}, 'fullstop')
with Action('Writing config for Kio..'):
store_config({'url': urls['kio']}, 'kio')
with Action('Writing config for Zign..'):
store_config({'url': token_service_url, 'user': username}, 'zign')
if autoconfigs.get('zalando-token-cli'):
with Action('Writing config for Zalando Token CLI..'):
store_config(autoconfigs['zalando-token-cli'], 'zalando-token-cli')
if autoconfigs.get('zalando-aws-cli'):
with Action('Writing config for Zalando AWS CLI..'):
store_config(autoconfigs['zalando-aws-cli'], 'zalando-aws-cli')
user_pattern = autoconfigs.get('mai', {}).get('saml_user_pattern')
if user_pattern:
info('Now running "mai create-all" to configure your AWS profile(s)..')
identity_provider_url = autoconfigs.get('mai', {}).get('saml_identity_provider_url')
info('Please use the following pattern for your SAML username: {}'.format(user_pattern))
returncode = subprocess.call(['mai', 'create-all', '--url', identity_provider_url])
if returncode == 0:
info('You can now use "mai login .." to get temporary AWS credentials for your AWS account(s).')
if errors:
info('Automatic configuration failed. Please check the entered STUPS domain.')
parts = domain.split('.')
if len(parts) <= 2:
domain = '.'.join(['stups'] + parts)
info('The entered domain looks too short. Did you mean {}?'.format(domain))
else:
domain = '.'.join(['stups'] + parts[1:])
info('The entered domain might be a team domain. Did you mean {}?'.format(domain))
else:
break