Index: appengine/swarming/swarming_bot/bot_code/bot_main.py |
diff --git a/appengine/swarming/swarming_bot/bot_code/bot_main.py b/appengine/swarming/swarming_bot/bot_code/bot_main.py |
index ebf2a09660f6e5a7f653963ac50b383bcfe28db0..f1fbe0edc4fbfc370cf15afcab23ff51a91b97ab 100644 |
--- a/appengine/swarming/swarming_bot/bot_code/bot_main.py |
+++ b/appengine/swarming/swarming_bot/bot_code/bot_main.py |
@@ -27,7 +27,10 @@ import time |
import traceback |
import zipfile |
+import bot_auth |
import common |
+import file_refresher |
+import remote_client |
import singleton |
from api import bot |
from api import os_utilities |
@@ -177,6 +180,19 @@ def setup_bot(skip_reboot): |
botobj.restart('Starting new swarming bot: %s' % THIS_FILE) |
+def get_authentication_headers(botobj): |
+ """Calls bot_config.get_authentication_headers() if it is defined. |
+ |
+ Doesn't catch exceptions. |
+ """ |
+ if _in_load_test_mode(): |
+ return (None, None) |
+ logging.info('get_authentication_headers()') |
+ from config import bot_config |
+ func = getattr(bot_config, 'get_authentication_headers', None) |
+ return func(botobj) if func else (None, None) |
+ |
+ |
### end of bot_config handler part. |
@@ -235,8 +251,8 @@ def post_error_task(botobj, error, task_id): |
'message': error, |
'task_id': task_id, |
} |
- return net.url_read_json( |
- botobj.server + '/swarming/api/v1/bot/task_error/%s' % task_id, data=data) |
+ return botobj.remote.url_read_json( |
+ '/swarming/api/v1/bot/task_error/%s' % task_id, data=data) |
def on_shutdown_hook(b): |
@@ -263,19 +279,30 @@ def get_bot(): |
config = get_config() |
assert not config['server'].endswith('/'), config |
- # Create a temporary object to call the hooks. |
- botobj = bot.Bot( |
+ # Use temporary Bot object to call get_attributes. Attributes are needed to |
+ # construct the "real" bot.Bot. |
+ attributes = get_attributes( |
+ bot.Bot( |
+ remote_client.RemoteClient(config['server'], None), |
attributes, |
config['server'], |
config['server_version'], |
os.path.dirname(THIS_FILE), |
- on_shutdown_hook) |
- return bot.Bot( |
- get_attributes(botobj), |
+ on_shutdown_hook)) |
+ |
+ # Make remote client callback use the returned bot object. We assume here |
+ # RemoteClient doesn't call its callback in the constructor (since 'botobj' is |
+ # undefined during the construction). |
+ botobj = bot.Bot( |
+ remote_client.RemoteClient( |
+ config['server'], |
+ lambda: get_authentication_headers(botobj)), |
+ attributes, |
config['server'], |
config['server_version'], |
os.path.dirname(THIS_FILE), |
on_shutdown_hook) |
+ return botobj |
def clean_isolated_cache(botobj): |
@@ -342,16 +369,24 @@ def run_bot(arg_error): |
# clause is kept there "just in case". |
logging.exception('server_ping threw') |
+ # Next we make sure the bot can make authenticated calls by grabbing |
+ # the auth headers, retrying on errors a bunch of times. We don't give up |
+ # if it fails though (maybe the bot will "fix itself" later). |
+ botobj = get_bot() |
+ try: |
+ botobj.remote.initialize(quit_bit) |
+ except remote_client.InitializationError as exc: |
+ botobj.post_error('failed to grab auth headers: %s' % exc.last_error) |
+ logging.error('Can\'t grab auth headers, continuing anyway...') |
+ |
if quit_bit.is_set(): |
logging.info('Early quit 1') |
return 0 |
# If this fails, there's hardly anything that can be done, the bot can't |
# even get to the point to be able to self-update. |
- botobj = get_bot() |
- resp = net.url_read_json( |
- botobj.server + '/swarming/api/v1/bot/handshake', |
- data=botobj._attributes) |
+ resp = botobj.remote.url_read_json( |
+ '/swarming/api/v1/bot/handshake', data=botobj._attributes) |
if not resp: |
logging.error('Failed to contact for handshake') |
else: |
@@ -418,8 +453,8 @@ def poll_server(botobj, quit_bit): |
""" |
# Access to a protected member _XXX of a client class - pylint: disable=W0212 |
start = time.time() |
- resp = net.url_read_json( |
- botobj.server + '/swarming/api/v1/bot/poll', data=botobj._attributes) |
+ resp = botobj.remote.url_read_json( |
+ '/swarming/api/v1/bot/poll', data=botobj._attributes) |
if not resp: |
return False |
logging.debug('Server response:\n%s', resp) |
@@ -443,8 +478,8 @@ def poll_server(botobj, quit_bit): |
'output_chunk_start': 0, |
'task_id': resp['task_id'], |
} |
- net.url_read_json( |
- botobj.server + '/swarming/api/v1/bot/task_update/%s' % resp['task_id'], |
+ botobj.remote.url_read_json( |
+ '/swarming/api/v1/bot/task_update/%s' % resp['task_id'], |
data=params) |
return False |
@@ -503,6 +538,7 @@ def run_manifest(botobj, manifest, start): |
failure = False |
internal_failure = False |
msg = None |
+ auth_params_dumper = None |
work_dir = os.path.join(botobj.base_dir, 'work') |
try: |
try: |
@@ -530,6 +566,21 @@ def run_manifest(botobj, manifest, start): |
task_result_file = os.path.join(work_dir, 'task_runner_out.json') |
if os.path.exists(task_result_file): |
os.remove(task_result_file) |
+ |
+ # Start a thread that periodically puts authentication headers and other |
+ # authentication related information to a file on disk. task_runner and its |
+ # subprocesses read it from there before making authenticated HTTP calls. |
+ auth_params_file = os.path.join(work_dir, 'bot_auth_params.json') |
+ if botobj.remote.uses_auth: |
+ env['SWARMING_AUTH_PARAMS'] = str(auth_params_file) |
+ auth_params_dumper = file_refresher.FileRefresherThread( |
+ auth_params_file, lambda: bot_auth.prepare_auth_params_json(botobj)) |
+ auth_params_dumper.start() |
+ else: |
+ env.pop('SWARMING_AUTH_PARAMS', None) |
+ if os.path.exists(auth_params_file): |
+ os.remove(auth_params_file) |
+ |
command = [ |
sys.executable, THIS_FILE, 'task_runner', |
'--swarming-server', url, |
@@ -541,6 +592,7 @@ def run_manifest(botobj, manifest, start): |
'--min-free-space', str(get_min_free_space()), |
] |
logging.debug('Running command: %s', command) |
+ |
# Put the output file into the current working directory, which should be |
# the one containing swarming_bot.zip. |
log_path = os.path.join(botobj.base_dir, 'logs', 'task_runner_stdout.log') |
@@ -601,6 +653,8 @@ def run_manifest(botobj, manifest, start): |
e, traceback.format_exc()[-2048:]) |
internal_failure = True |
finally: |
+ if auth_params_dumper: |
+ auth_params_dumper.stop() |
if internal_failure: |
post_error_task(botobj, msg, task_id) |
call_hook( |
@@ -631,12 +685,12 @@ def update_bot(botobj, version): |
new_zip = os.path.join(os.path.dirname(THIS_FILE), new_zip) |
# Download as a new file. |
- url = botobj.server + '/swarming/api/v1/bot/bot_code/%s' % version |
- if not net.url_retrieve(new_zip, url): |
+ url_path = '/swarming/api/v1/bot/bot_code/%s' % version |
+ if not botobj.remote.url_retrieve(new_zip, url_path): |
# It can happen when a server is rapidly updated multiple times in a row. |
botobj.post_error( |
'Unable to download %s from %s; first tried version %s' % |
- (new_zip, url, version)) |
+ (new_zip, botobj.server + url_path, version)) |
# Poll again, this may work next time. To prevent busy-loop, sleep a little. |
time.sleep(2) |
return |
@@ -751,6 +805,6 @@ def main(args): |
try: |
return run_bot(error) |
finally: |
- call_hook(bot.Bot(None, None, None, os.path.dirname(THIS_FILE), None), |
+ call_hook(bot.Bot(None, None, None, None, os.path.dirname(THIS_FILE), None), |
'on_bot_shutdown') |
logging.info('main() returning') |