-
Notifications
You must be signed in to change notification settings - Fork 725
/
Copy pathupdate_selenoid_browsers.py
282 lines (249 loc) · 10.3 KB
/
update_selenoid_browsers.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
##########################################################################
#
# pgAdmin 4 - PostgreSQL Tools
#
# Copyright (C) 2013 - 2025, The pgAdmin Development Team
# This software is released under the PostgreSQL Licence
#
# #########################################################################
# Updates browser images(selenoid-docker) depending on arguments passed while
# running this script.
# e.g. --chrome /usr/bin/google-chrome --firefox /usr/bin/firefox
# Access details about switches using help
# e.g. --help
import argparse
import os
import subprocess
import sys
import traceback
import requests
import json
def read_command_line():
"""Read the command line arguments.
Returns:
ArgumentParser: The parsed arguments object
"""
parser = argparse.ArgumentParser(
description='Get latest browser images(chrome & firefox) for selenoid.'
'e.g. - --chrome /usr/bin/google-chrome --firefox '
'/usr/bin/firefox')
parser.add_argument("--chrome", metavar="CHROME",
help="the Chrome executable path")
parser.add_argument("--firefox", metavar="FIREFOX",
help="the firefox executable path")
args_val = parser.parse_args()
return args_val
def get_browser_version(browser_name, executable_path):
"""
Function returns browser version for specified browser using executable
path passed in arguments.
:param browser_name:
:param executable_path: e.g. /usr/bin/firefox
:return: browser version
"""
# On Linux/Mac we run the browser executable with the --version flag,
# then parse the output.
browser_version_val = None
try:
result = subprocess.Popen([executable_path, '--version'],
stdout=subprocess.PIPE)
except FileNotFoundError:
print('The specified browser executable could not be found.')
sys.exit(1)
version_str = result.stdout.read().decode("utf-8")
if browser_name.lower() == "chrome":
# Check for 'Chrom' not 'Chrome' in case the user is using Chromium.
if "Chrom" not in version_str:
print('The specified Chrome executable output an unexpected '
'version string: {}.'.format(version_str))
sys.exit(1)
# On some linux distro `chrome--version` gives output like
# 'Google Chrome 80.0.3987.132 unknown\n'
# so we need to check and remove the unknown string from the version
if version_str.endswith("unknown\n"):
version_str = version_str.strip("unknown\n").strip()
chrome_version = '.'.join(version_str.split()[-1].split('.')[:-2])
# Make sure browser version has only 1 decimal point
if chrome_version.count('.') != 1:
print('The specified Chrome executable output an unexpected '
'version string: {}.'.format(version_str))
sys.exit(1)
browser_version_val = chrome_version
elif browser_name.lower() == "firefox":
if "Firefox" not in version_str:
print('The specified Firefox executable output an unexpected '
'version string: {}.'.format(version_str))
sys.exit(1)
# Some time firefox --version gives output like
# 'Running without a11y support!
# Mozilla Firefox 68.7.0esr'
# Other output - [root@localhost local]# /usr/bin/firefox --version
# Mozilla Firefox 75.0
if 'esr' in version_str:
firefox_version = '.'.join(
version_str.split('esr')[0].split()[-1].split('.')[:-1])
else:
firefox_version = '.'.join(
version_str.split()[-1].split('.')[:-1])
if firefox_version.count('.') == 0:
firefox_version = firefox_version + '.0'
# Make sure browser version has only 1 decimal point
if firefox_version.count('.') != 1:
print('The specified Firefox executable output an unexpected '
'version string: {}.'.format(version_str))
sys.exit(1)
browser_version_val = firefox_version
else:
print("{0} is not recognised ".format(browser_name))
sys.exit(1)
return browser_version_val
def check_and_download_vnc_browser_image(browser_name, browser_version):
"""
Function checks presence for vnc images for passed browser
at docker.io/selenoid/ registry
:param browser_name:
:param browser_version:
:return:true if browser image is available & downloaded else false
"""
res = requests.get(
'https://registry.hub.docker.com/v2/repositories/selenoid/vnc_' +
browser_name + '/tags/')
res = res.json()
version_tag = []
if len(res['results']) > 0:
for result in res['results']:
if 'name' in result:
version_tag.append(result['name'])
vnc_image_available = False
image_name = 'vnc_' + browser_name + ':' + browser_version
for idx, tag in enumerate(version_tag):
if browser_version == tag:
command = 'docker pull selenoid/vnc_' + browser_name + ':' \
+ browser_version
print(' VNC image is available & downloading now... {0}'.format(
command))
try:
subprocess.call([command], shell=True, stdout=subprocess.PIPE)
vnc_image_available = True
except Exception:
traceback.print_exc(file=sys.stderr)
print(
'{0} Image found but could not be downloaded.'.
format(command))
sys.exit(1)
break
elif idx == len(version_tag):
print("{0} Image is not available.".format(image_name))
vnc_image_available = False
return vnc_image_available
def reload_selenoid_config():
"""
Function runs command to refresh selenoid configuration
:return: true if command execution for selenoid reload is successful
else false
"""
command = 'docker kill -s HUP selenoid'
reload_successful = False
try:
subprocess.call([command], shell=True, stdout=subprocess.PIPE)
print(" Selenoid Configuration is reloaded.")
reload_successful = True
except Exception:
traceback.print_exc(file=sys.stderr)
print('Error while reloading selenoid configuration.')
sys.exit(1)
return reload_successful
def edit_browsers_json(browser_name, browser_version):
"""
Function edits browsers.json which is used by selenoid to
load browser configuration.
Default path for this file is
"user_home_dir + '/.aerokube/selenoid/browsers.json'"
Currently this is hardcoded, might need to modify
if we want to pass customize browsers.json
:param browser_name:
:param browser_version:
:return:
"""
file_edited = True
# Read existing browsers.json
json_file = open(file_path, 'r')
existing_data = json.load(json_file)
updated_data = None
# Update data for new browser images
if browser_name.lower() == 'chrome':
version_data = existing_data['chrome']['versions']
if browser_version in version_data.keys():
print(" {0}:{1} is already updated in browsers.json.".format(
browser_name, browser_version))
file_edited = True
else:
data_to_insert = dict(
{browser_version: {
'image': 'selenoid/vnc_chrome:' + browser_version,
'port': '4444', 'path': '/'}})
(existing_data['chrome']['versions']).update(data_to_insert)
updated_data = existing_data
print(updated_data)
elif browser_name.lower() == 'firefox':
version_data = existing_data['firefox']['versions']
if browser_version in version_data.keys():
print(" {0}:{1} is already updated in browsers.json.".format(
browser_name, browser_version))
file_edited = True
else:
data_to_insert = dict(
{browser_version: {
'image': 'selenoid/vnc_firefox:' + browser_version,
'port': '4444', 'path': '/wd/hub'}})
(existing_data['firefox']['versions']).update(data_to_insert)
updated_data = existing_data
print(updated_data)
else:
print("Browser version not matched")
file_edited = False
# Write updated data in browsers.json
if updated_data is not None:
json_file = open(file_path, 'w')
json.dump(updated_data, json_file)
print(" 'browsers.json' is updated for {0} {1}".format(
browser_name, browser_version))
file_edited = True
return file_edited
# Main Program starts here
# Read command line arguments & get list of browser_name, executable path.
args = vars(read_command_line())
# Get path path for browsers.json
user_home_dir = os.getenv("HOME")
file_path = user_home_dir + '/.aerokube/selenoid/browsers.json'
print("***** Updating '{0}' for new browser versions.*****".format(file_path))
# Iterate over arguments passed
for browser, executable_path in args.items():
if executable_path is not None:
# Get browser name
browser_name = browser
# Get browser version
browser_version = get_browser_version(browser, executable_path)
print(
" Browser version for {0} is {1} in current executable path ".
format(browser_name, browser_version))
# Download vnc browser image.
download_new_image = check_and_download_vnc_browser_image(
browser_name, browser_version)
# If browser vnc image is available, then edit browsers.json
if download_new_image:
if edit_browsers_json(browser_name, browser_version):
print(
" File 'browsers.json' is updated for {0} - {1} \n".format(
browser_name, browser_version))
else:
print(
" File 'browsers.json' can NOT be updated for {0} - {1} \n"
.format(browser_name, browser_version))
else:
print(" Browser image is not available for {0}, {1}".format(
browser_name, browser_version))
# Reload selenoid configuration
if reload_selenoid_config():
print(
"***** Updated '{0}' for new browser versions.*****".format(file_path))