forked from mozilla/DeepSpeech
-
Notifications
You must be signed in to change notification settings - Fork 0
/
Copy pathwebsite.py
190 lines (163 loc) · 6.63 KB
/
website.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
from __future__ import print_function
from __future__ import absolute_import
import os
import paramiko
import pysftp
import sys
from bs4 import BeautifulSoup
def parse_for_deps(filename):
"""
This takes an HTML file as input and output a list of existing depenencies.
Empty output means something was wrong.
Dependency is one of: js script loaded, css stylesheet loaded
"""
with open(filename, 'r') as code:
soup = BeautifulSoup(code.read(), 'html.parser')
external_links = filter(lambda x: x is not None, [ link.get('href') for link in soup.find_all('link') ])
external_scripts = filter(lambda x: x is not None, [ script.get('src') for script in soup.find_all('script') ])
# Verify with stat()
try:
all_resources = map(lambda x: os.stat(x), external_links + external_scripts)
except OSError as ex:
if ex.errno == 2:
print("Missing dependency", ex)
return []
raise ex
# Try to read the file
try:
readable_resources = map(lambda x: os.open(x, os.O_RDONLY), external_links + external_scripts)
map(lambda x: os.close(x), readable_resources)
except OSError as ex:
print("Unreadable dependency", ex)
return []
# It should be all good.
return external_links + external_scripts
def get_ssh(auth_infos):
"""
Connect to SSH server
"""
cinfo = {'host': auth_infos['ds_website_server_fqdn'],
'username': auth_infos['ds_website_username'],
'private_key': auth_infos['ds_website_privkey'],
'port': auth_infos['ds_website_server_port']}
return pysftp.Connection(**cinfo)
def verify_ssh_dir(auth_infos):
"""
This should ensure that SSH connection works and that the directory where
we want to push data is either empty or contains at least a .htaccess and
index.htm file
"""
print("Checking connection: %s@%s:%s (port %d)" % (auth_infos['ds_website_username'], auth_infos['ds_website_server_fqdn'], auth_infos['ds_website_server_root'], auth_infos['ds_website_server_port']))
try:
with get_ssh(auth_infos) as sftp:
with sftp.cd(auth_infos['ds_website_server_root']):
files = sftp.listdir()
if len(files) == 0 or (('.htaccess' in files) and ('index.htm' in files)):
return True
else:
print("Invalid content for %s:" % (auth_infos['ds_website_server_root']), files)
return False
except paramiko.ssh_exception.AuthenticationException as ex:
print("Authentication error, please verify credentials")
return False
except IOError as ex:
print("Unable to read a file (private key or invalid path on server ?)", ex)
return False
# Should not be reached
return False
def push_files_sftp(files, auth_infos):
"""
This will push all of the ``files`` listed to the server root folder.
"""
# Directories that we might be required to create
dirs = filter(lambda x: len(x) > 0, list(set([ os.path.dirname(x) for x in files ])))
created = []
pushed = []
try:
with get_ssh(auth_infos) as sftp:
with sftp.cd(auth_infos['ds_website_server_root']):
# Create dirs if needed, they all should depend from the root
for dir in dirs:
if not sftp.isdir(dir):
print("Creating directory", dir)
sftp.makedirs(dir)
created.append(dir)
# Push all files, chdir() for each
for fil in files:
with sftp.cd(os.path.dirname(fil)):
print("Pushing", fil)
sftp.put(fil)
pushed.append(fil)
return pushed
except paramiko.ssh_exception.AuthenticationException as ex:
print("Authentication error, please verify credentials")
return False
except IOError as ex:
print("Unable to read a file (private key or invalid path on server ?)", ex)
return False
# Should not be reached
return False
def maybe_publish(file='index.htm'):
"""
Publishing to the web requires the following env variables to be set
'ds_website_username: defines the SSH username to connect to
'ds_website_privkey: defines the SSH privkey filename to use for connection
'ds_website_server_fqdn: hostname of the SSH server
'ds_website_server_port: port of the SSH server, defaults to 22
'ds_website_server_root: directory on the server where to push data,
should be either empty, or containing at least
a .htaccess and index.htm file
"""
ssh_auth_infos = {
'ds_website_username': '',
'ds_website_privkey': '',
'ds_website_server_fqdn': '',
'ds_website_server_port': 22,
'ds_website_server_root': ''
}
for key in ssh_auth_infos.keys():
vartype = type(ssh_auth_infos[key])
value = os.environ.get(key)
if value is not None:
if vartype == str:
ssh_auth_infos[key] = str(os.environ.get(key))
elif vartype == int:
try:
ssh_auth_infos[key] = int(os.environ.get(key))
except TypeError as ex:
print("WARNING:", "Keeping default SSH port value because of:", ex)
missing_env = [
x for x in ssh_auth_infos.keys()
if (len(str(ssh_auth_infos[x])) == 0)
]
if len(missing_env) > 0:
print("Not publishing, missing some required environment variables:", missing_env)
print("But maybe this is what you wanted, after all ...")
return False
all_deps = parse_for_deps(file)
if len(all_deps) == 0:
print("Problem during deps computation, aborting")
return False
if not verify_ssh_dir(ssh_auth_infos):
print("Problem during SSH directory verification, aborting")
return False
all_files = [ file ] + all_deps
uploaded = push_files_sftp(all_files, ssh_auth_infos)
if len(uploaded) == 0:
print("Unable to upload anything")
return False
elif len(uploaded) != len(all_files):
print("Partial upload has been completed:")
print("all_files=", all_files)
print("uploaded=", uploaded)
else:
print("Complete upload has been completed.")
return True
# Support CLI invocation
if __name__ == "__main__":
if maybe_publish():
print("All good!")
sys.exit(0)
else:
print("Error happened ...")
sys.exit(1)