-
Notifications
You must be signed in to change notification settings - Fork 14
/
load_config.py
executable file
·286 lines (236 loc) · 8.29 KB
/
load_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
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
248
249
250
251
252
253
254
255
256
257
258
259
260
261
262
263
264
265
266
267
268
269
270
271
272
273
274
275
276
277
278
279
280
281
282
283
284
285
286
"""Loads a Socotra Configuration into a Socotra Tenant
After running this script you will will have a Socotra Tenant loaded
with the Configuration you provide. This script will give you the
URL of that Tenant. You need an administrative user+password for the
Socotra Sandbox Environment / Socotra Config Studio.
Usage
-----
$ python load_config.py --help
Python 3
--------
This script now requires Python 3.6+
Required Packages
-----------------
This script requires the following packages to be installed within the
Python environment you are running this script in:
- requests
Reference
---------
Socotra Configuration: https://docs.socotra.com/production/configuration/walkthrough.html
configuration/deployTest endpoint: https://docs.socotra.com/production/api/configuration.html
Problems and Improvements
-------------------------
This script is maintained by Will Barley, [email protected]
Reach out!
[email protected] for *quick support*
[email protected] to talk directly to the maintainer
Or open an new issue in the repository:
https://github.com/socotra/public/issues/new?title=issue%20with%20load_config.py
Development
-----------
Conventional Commits: https://www.conventionalcommits.org/
Black for deterministic code formatting: https://black.readthedocs.io/
"""
import argparse
import json
import os
import sys
import tempfile
from pathlib import Path
from shutil import make_archive
from typing import Union
import requests
from requests.packages.urllib3.exceptions import InsecureRequestWarning
requests.packages.urllib3.disable_warnings(InsecureRequestWarning)
SANDBOX_URL = "https://api.sandbox.socotra.com"
class BetterArgParser(argparse.ArgumentParser):
"""Extension to ArgumentParser that prints the usage statement alongside any error"""
def error(self, message):
"""Prints the script's usage statement and then the typical error message"""
self.print_help()
super().error(message)
def get_arguments() -> object:
"""Gets arguments from the command line
Returns
-------
argparse.Namespace
The arguments collected from the command line
"""
parser = BetterArgParser(
description="Load Socotra Configuration into a Socotra Tenant."
)
parser.add_argument(
"--tenant_suffix",
"-t",
help="This will be used to create the hostname of your tenant as follows: "
"<username>-<tenant_suffix>.co.sandbox.socotra.com. "
"Note: 'configeditor' is not allowed.",
required=True,
)
parser.add_argument(
"--folder",
"-f",
help="The path of the root folder of your Socotra Config that you want to load.",
required=True,
)
parser.add_argument(
"--username",
"-u",
help="Socotra Config Studio username. "
"Uses SOCOTRA_USERNAME env variable if not provided.",
required=False,
)
parser.add_argument(
"--password",
"-p",
help="Socotra Config Studio password. "
"Uses SOCOTRA_PASSWORD env variable if not provided.",
required=False,
)
parser.add_argument(
"--debug", "-d", help="prints debugging info", action="store_true"
)
arg = parser.parse_args()
if arg.username is None:
try:
arg.username = os.environ["SOCOTRA_USERNAME"]
except KeyError:
parser.error(
"No --username provided "
"and no SOCOTRA_USERNAME environment variable found"
)
if arg.password is None:
try:
arg.password = os.environ["SOCOTRA_PASSWORD"]
except KeyError:
parser.error(
"No --password provided "
"and no SOCOTRA_PASSWORD environment variable found"
)
arg.folder = str(Path(arg.folder).resolve())
return arg
def post_zip_to_server(
file: Union[str, Path],
tenant_suffix: str,
username: str,
password: str,
debug: bool = True,
) -> None:
"""Loads a zipped Socotra Configuration into a Tenant.
Parameters
----------
file
The path to the zip file containing the Socotra Configuration
tenant_suffix
The suffix to use to create the hostname of your target Tenant
username
password
debug
Examples
--------
Bad User/Password
>>> post_zip_to_server('/tmp/file.zip', 'tenant12', 'user', 'pass')
Traceback (most recent call last):
...
ValueError: HTTP 401: Unauthorized: Could not authenticate with the provided username and password.
"""
# Authenticate
token = get_auth_token(username, password, debug=debug)
# Construct the Request
auth_header = {"Authorization": token}
post_data = {"tenantNameSuffix": tenant_suffix}
url = SANDBOX_URL + "/configuration/deployTest"
with open(str(file), mode="rb") as binary_data:
file_data = {"zipFile": binary_data}
response = requests.post(
url, post_data, files=file_data, verify=False, headers=auth_header
)
try:
json_response = json.loads(response.text)
except ValueError:
print("An error happened!\n")
print("Headers: %s" % response.headers)
print("Status Code: %s" % response.status_code)
print("Data: %s" % response.text)
return
if json_response.get("success"):
print("============== load was successful ==============")
print(f"hostname: {json_response['hostname']}")
print(f"url: https://{json_response['hostname']}")
else:
print("============== load failed ==============")
log = json_response.get("logfile")
if log:
print(f"Loading Log:\n {log}")
else:
print(f"Loading Response:\n {json_response}")
def get_auth_token(username, password, debug: bool = False):
"""Get an Authorization Token for the user and password.
Parameters
----------
username
password
debug
Print debug messages
Returns
-------
str
The Authorization token
"""
post_data = {"username": username, "password": password}
auth_url = SANDBOX_URL + "/account/authenticate"
r = requests.post(auth_url, json=post_data, verify=False)
json_response = json.loads(r.text)
log_debug(f"Response from load: {json_response}", debug)
token = json_response.get("authorizationToken")
if token:
return token
status = json_response.get("httpStatus")
if status == "401":
raise ValueError(
f"HTTP {status}: Unauthorized: Could not authenticate with "
f"the provided username and password."
)
raise ValueError(
f"HTTP {status}: There was some sort of problem while trying to "
f"authenticate the user and password."
)
def log_debug(message: str, debug: bool = False):
"""Print out debug messages if the `debug` parameter is True"""
if debug:
print(message)
def main():
"""Main! Executed when this script is run from the command line.
Examples
--------
Simulate: $ python load_config.py -t myTenant -f . -u myUser -p myPass
>>> import sys
>>> sys.argv = ['load_config.py', '-t', 'myTenant', '-f', '.', '-u', 'myUser', '-p', 'myPass']
>>> main()
Traceback (most recent call last):
...
ValueError: HTTP 401: Unauthorized: Could not authenticate with the provided username and password.
"""
args = get_arguments()
log_debug(f"Arguments: {args}", args.debug)
# Create a temporary directory (that will work on any operating system)
# where we will create our zip file, and clean it up when done
with tempfile.TemporaryDirectory() as dir_name:
d = Path(dir_name) # turn into a Path object
log_debug(f"Created temporary directory: {d}", args.debug)
print("Zipping")
log_debug(f"Zipping up: {args.folder}", args.debug)
zipped = make_archive(
base_name=d / "archive", format="zip", root_dir=args.folder
)
log_debug(f"Zipped to: {zipped}", args.debug)
print("Uploading")
post_zip_to_server(
zipped, args.tenant_suffix, args.username, args.password, debug=args.debug
)
log_debug(
f"Finishing with temporary directory, which will be removed: {d}",
args.debug,
)
if __name__ == "__main__":
main()