| #!/usr/bin/env python3 |
| # |
| # Copyright 2018 The Bazel Authors. All rights reserved. |
| # |
| # Licensed under the Apache License, Version 2.0 (the "License"); |
| # you may not use this file except in compliance with the License. |
| # You may obtain a copy of the License at |
| # |
| # http://www.apache.org/licenses/LICENSE-2.0 |
| # |
| # Unless required by applicable law or agreed to in writing, software |
| # distributed under the License is distributed on an "AS IS" BASIS, |
| # WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. |
| # See the License for the specific language governing permissions and |
| # limitations under the License. |
| |
| import gcloud |
| import json |
| import subprocess |
| import time |
| import threading |
| import re |
| |
| |
| def wait_for_instance(instance_name, zone, status): |
| while True: |
| result = gcloud.describe_instance(instance_name, zone=zone, format='json') |
| current_status = json.loads(result.stdout)['status'] |
| if current_status == status: |
| gcloud.debug('wait_for_instance: {}/{} arrived at status {}'.format(zone, instance_name, status)) |
| break |
| else: |
| gcloud.debug('wait_for_instance: Waiting for {}/{} to go from status {} to status {}'.format( |
| zone, instance_name, current_status, status)) |
| time.sleep(5) |
| |
| |
| def prettify_logs(instance_name, log, with_prefix=True): |
| for line in log.splitlines(): |
| # Skip empty lines. |
| if not line: |
| continue |
| |
| # Filter for log lines printed by our startup script, ignore the rest. |
| # Then drop the common prefix to make the output easier to read. |
| # For unknown platforms, we just take every line unmodified. |
| if 'ubuntu' in instance_name: |
| match = re.match(r'.*INFO startup-script: (.*)', line) |
| if not match: |
| continue |
| line = match.group(1) |
| elif 'windows' in instance_name: |
| match = re.match(r'.*windows-startup-script-ps1: (.*)', line) |
| if not match: |
| continue |
| line = match.group(1) |
| |
| if with_prefix: |
| yield "{}: {}".format(instance_name, line) |
| else: |
| yield line |
| |
| |
| def print_pretty_logs(instance_name, log): |
| lines = ('\n'.join(prettify_logs(instance_name, log))).strip() |
| if lines: |
| with gcloud.PRINT_LOCK: |
| print(lines) |
| |
| |
| def tail_serial_console(instance_name, zone, start=None, until=None): |
| next_start = start if start else '0' |
| while True: |
| try: |
| result = gcloud.get_serial_port_output(instance_name, zone=zone, start=next_start) |
| except subprocess.CalledProcessError as e: |
| if 'Could not fetch serial port output: TIMEOUT' in e.stderr: |
| gcloud.debug('tail_serial_console: Retrying after TIMEOUT') |
| continue |
| gcloud.debug('tail_serial_console: Done, because got exception: {}'.format(e)) |
| if e.stdout: |
| gcloud.debug('stdout: ' + e.stdout) |
| if e.stderr: |
| gcloud.debug('stderr: ' + e.stderr) |
| break |
| print_pretty_logs(instance_name, result.stdout) |
| next_start = re.search(r'--start=(\d*)', result.stderr).group(1) |
| if until and until in result.stdout: |
| gcloud.debug('tail_serial_console: Done, because found string "{}"'.format(until)) |
| break |
| time.sleep(5) |
| return next_start |