Don Garrett | 8db752c | 2014-10-17 16:56:55 -0700 | [diff] [blame] | 1 | #!/usr/bin/python |
| 2 | # Copyright (c) 2014 The Chromium OS Authors. All rights reserved. |
| 3 | # Use of this source code is governed by a BSD-style license that can be |
| 4 | # found in the LICENSE file. |
| 5 | |
| 6 | """Runs on autotest servers from a cron job to self update them. |
| 7 | |
| 8 | This script is designed to run on all autotest servers to allow them to |
| 9 | automatically self-update based on the manifests used to create their (existing) |
| 10 | repos. |
| 11 | """ |
| 12 | |
| 13 | from __future__ import print_function |
| 14 | |
| 15 | import ConfigParser |
Don Garrett | 03432d6 | 2014-11-19 18:18:35 -0800 | [diff] [blame] | 16 | import argparse |
Don Garrett | 8db752c | 2014-10-17 16:56:55 -0700 | [diff] [blame] | 17 | import os |
Don Garrett | 699b4b3 | 2014-12-11 13:10:15 -0800 | [diff] [blame] | 18 | import re |
Don Garrett | 8db752c | 2014-10-17 16:56:55 -0700 | [diff] [blame] | 19 | import subprocess |
| 20 | import sys |
| 21 | import time |
| 22 | |
| 23 | import common |
| 24 | |
| 25 | from autotest_lib.client.common_lib import global_config |
| 26 | |
Don Garrett | d032172 | 2014-11-18 16:03:33 -0800 | [diff] [blame] | 27 | # How long after restarting a service do we watch it to see if it's stable. |
| 28 | SERVICE_STABILITY_TIMER = 60 |
| 29 | |
Don Garrett | 8db752c | 2014-10-17 16:56:55 -0700 | [diff] [blame] | 30 | |
| 31 | class DirtyTreeException(Exception): |
Don Garrett | d032172 | 2014-11-18 16:03:33 -0800 | [diff] [blame] | 32 | """Raised when the tree has been modified in an unexpected way.""" |
Don Garrett | 8db752c | 2014-10-17 16:56:55 -0700 | [diff] [blame] | 33 | |
| 34 | |
| 35 | class UnknownCommandException(Exception): |
Don Garrett | d032172 | 2014-11-18 16:03:33 -0800 | [diff] [blame] | 36 | """Raised when we try to run a command name with no associated command.""" |
Don Garrett | 8db752c | 2014-10-17 16:56:55 -0700 | [diff] [blame] | 37 | |
| 38 | |
| 39 | class UnstableServices(Exception): |
Don Garrett | d032172 | 2014-11-18 16:03:33 -0800 | [diff] [blame] | 40 | """Raised if a service appears unstable after restart.""" |
Don Garrett | 8db752c | 2014-10-17 16:56:55 -0700 | [diff] [blame] | 41 | |
| 42 | |
| 43 | def verify_repo_clean(): |
| 44 | """This function verifies that the current repo is valid, and clean. |
| 45 | |
| 46 | @raises DirtyTreeException if the repo is not clean. |
| 47 | @raises subprocess.CalledProcessError on a repo command failure. |
| 48 | """ |
Don Garrett | 8db752c | 2014-10-17 16:56:55 -0700 | [diff] [blame] | 49 | out = subprocess.check_output(['repo', 'status'], stderr=subprocess.STDOUT) |
Don Garrett | 699b4b3 | 2014-12-11 13:10:15 -0800 | [diff] [blame] | 50 | out = out.strip() |
| 51 | |
| 52 | # We're clean, with no branches. |
| 53 | CLEAN_STATUS_OUTPUT = 'nothing to commit (working directory clean)' |
| 54 | if out == CLEAN_STATUS_OUTPUT: |
| 55 | return |
| 56 | |
| 57 | # We're clean, but the branch 'prod' exists in the project autotest. |
| 58 | # We use wildcards to skip over the text format characters repo uses. |
| 59 | if re.match(r'^.*project autotest/.*branch prod.*$\Z', out): |
| 60 | return |
| 61 | |
| 62 | raise DirtyTreeException(out) |
Don Garrett | 8db752c | 2014-10-17 16:56:55 -0700 | [diff] [blame] | 63 | |
Don Garrett | 8db752c | 2014-10-17 16:56:55 -0700 | [diff] [blame] | 64 | |
| 65 | def repo_versions(): |
| 66 | """This function collects the versions of all git repos in the general repo. |
| 67 | |
Don Garrett | fa2c1c4 | 2014-12-11 12:11:49 -0800 | [diff] [blame^] | 68 | @returns A dictionary mapping project names to git hashes for HEAD. |
Don Garrett | 8db752c | 2014-10-17 16:56:55 -0700 | [diff] [blame] | 69 | @raises subprocess.CalledProcessError on a repo command failure. |
| 70 | """ |
Don Garrett | fa2c1c4 | 2014-12-11 12:11:49 -0800 | [diff] [blame^] | 71 | cmd = ['repo', 'forall', '-p', '-c', 'pwd && git log -1 --format=%h'] |
| 72 | output = subprocess.check_output(cmd) |
| 73 | |
| 74 | # The expected output format is: |
| 75 | |
| 76 | # project chrome_build/ |
| 77 | # /dir/holding/chrome_build |
| 78 | # 73dee9d |
| 79 | # |
| 80 | # project chrome_release/ |
| 81 | # /dir/holding/chrome_release |
| 82 | # 9f3a5d8 |
| 83 | |
| 84 | lines = output.splitlines() |
| 85 | |
| 86 | PROJECT_PREFIX = 'project ' |
| 87 | |
| 88 | project_heads = {} |
| 89 | for n in range(0, len(lines), 4): |
| 90 | project_line = lines[n] |
| 91 | project_dir = lines[n+1] |
| 92 | project_hash = lines[n+2] |
| 93 | # lines[n+3] is a blank line, but doesn't exist for the final block. |
| 94 | |
| 95 | # Convert 'project chrome_build/' -> 'chrome_build' |
| 96 | assert project_line.startswith(PROJECT_PREFIX) |
| 97 | name = project_line[len(PROJECT_PREFIX):].rstrip('/') |
| 98 | |
| 99 | project_heads[name] = (project_dir, project_hash) |
| 100 | |
| 101 | return project_heads |
Don Garrett | 8db752c | 2014-10-17 16:56:55 -0700 | [diff] [blame] | 102 | |
| 103 | |
| 104 | def repo_sync(): |
| 105 | """Perform a repo sync. |
| 106 | |
| 107 | @raises subprocess.CalledProcessError on a repo command failure. |
| 108 | """ |
Don Garrett | d032172 | 2014-11-18 16:03:33 -0800 | [diff] [blame] | 109 | subprocess.check_output(['repo', 'sync']) |
Don Garrett | 8db752c | 2014-10-17 16:56:55 -0700 | [diff] [blame] | 110 | |
| 111 | |
Don Garrett | d032172 | 2014-11-18 16:03:33 -0800 | [diff] [blame] | 112 | def discover_update_commands(): |
| 113 | """Lookup the commands to run on this server. |
Don Garrett | 8db752c | 2014-10-17 16:56:55 -0700 | [diff] [blame] | 114 | |
Don Garrett | d032172 | 2014-11-18 16:03:33 -0800 | [diff] [blame] | 115 | These commonly come from shadow_config.ini, since they vary by server type. |
Don Garrett | 8db752c | 2014-10-17 16:56:55 -0700 | [diff] [blame] | 116 | |
Don Garrett | d032172 | 2014-11-18 16:03:33 -0800 | [diff] [blame] | 117 | @returns List of command names in string format. |
Don Garrett | 8db752c | 2014-10-17 16:56:55 -0700 | [diff] [blame] | 118 | """ |
Don Garrett | 8db752c | 2014-10-17 16:56:55 -0700 | [diff] [blame] | 119 | try: |
Don Garrett | d032172 | 2014-11-18 16:03:33 -0800 | [diff] [blame] | 120 | return global_config.global_config.get_config_value( |
Don Garrett | 8db752c | 2014-10-17 16:56:55 -0700 | [diff] [blame] | 121 | 'UPDATE', 'commands', type=list) |
| 122 | |
| 123 | except (ConfigParser.NoSectionError, global_config.ConfigError): |
Don Garrett | d032172 | 2014-11-18 16:03:33 -0800 | [diff] [blame] | 124 | return [] |
Don Garrett | 8db752c | 2014-10-17 16:56:55 -0700 | [diff] [blame] | 125 | |
Don Garrett | d032172 | 2014-11-18 16:03:33 -0800 | [diff] [blame] | 126 | |
| 127 | def discover_restart_services(): |
| 128 | """Find the services that need restarting on the current server. |
| 129 | |
| 130 | These commonly come from shadow_config.ini, since they vary by server type. |
| 131 | |
| 132 | @returns List of service names in string format. |
| 133 | """ |
Don Garrett | 8db752c | 2014-10-17 16:56:55 -0700 | [diff] [blame] | 134 | try: |
| 135 | # From shadow_config.ini, lookup which services to restart. |
Don Garrett | d032172 | 2014-11-18 16:03:33 -0800 | [diff] [blame] | 136 | return global_config.global_config.get_config_value( |
Don Garrett | 8db752c | 2014-10-17 16:56:55 -0700 | [diff] [blame] | 137 | 'UPDATE', 'services', type=list) |
| 138 | |
| 139 | except (ConfigParser.NoSectionError, global_config.ConfigError): |
Don Garrett | d032172 | 2014-11-18 16:03:33 -0800 | [diff] [blame] | 140 | return [] |
Don Garrett | 8db752c | 2014-10-17 16:56:55 -0700 | [diff] [blame] | 141 | |
Don Garrett | 8db752c | 2014-10-17 16:56:55 -0700 | [diff] [blame] | 142 | |
Don Garrett | 03432d6 | 2014-11-19 18:18:35 -0800 | [diff] [blame] | 143 | def update_command(cmd_tag, dryrun=False): |
Don Garrett | d032172 | 2014-11-18 16:03:33 -0800 | [diff] [blame] | 144 | """Restart a command. |
Don Garrett | 8db752c | 2014-10-17 16:56:55 -0700 | [diff] [blame] | 145 | |
Don Garrett | d032172 | 2014-11-18 16:03:33 -0800 | [diff] [blame] | 146 | The command name is looked up in global_config.ini to find the full command |
| 147 | to run, then it's executed. |
Don Garrett | 8db752c | 2014-10-17 16:56:55 -0700 | [diff] [blame] | 148 | |
Don Garrett | d032172 | 2014-11-18 16:03:33 -0800 | [diff] [blame] | 149 | @param cmd_tag: Which command to restart. |
Don Garrett | 03432d6 | 2014-11-19 18:18:35 -0800 | [diff] [blame] | 150 | @param dryrun: If true print the command that would have been run. |
Don Garrett | 8db752c | 2014-10-17 16:56:55 -0700 | [diff] [blame] | 151 | |
Don Garrett | d032172 | 2014-11-18 16:03:33 -0800 | [diff] [blame] | 152 | @raises UnknownCommandException If cmd_tag can't be looked up. |
| 153 | @raises subprocess.CalledProcessError on a command failure. |
| 154 | """ |
| 155 | # Lookup the list of commands to consider. They are intended to be |
| 156 | # in global_config.ini so that they can be shared everywhere. |
| 157 | cmds = dict(global_config.global_config.config.items( |
| 158 | 'UPDATE_COMMANDS')) |
Don Garrett | 8db752c | 2014-10-17 16:56:55 -0700 | [diff] [blame] | 159 | |
Don Garrett | d032172 | 2014-11-18 16:03:33 -0800 | [diff] [blame] | 160 | if cmd_tag not in cmds: |
| 161 | raise UnknownCommandException(cmd_tag, cmds) |
Don Garrett | 8db752c | 2014-10-17 16:56:55 -0700 | [diff] [blame] | 162 | |
Don Garrett | d032172 | 2014-11-18 16:03:33 -0800 | [diff] [blame] | 163 | expanded_command = cmds[cmd_tag].replace('AUTOTEST_REPO', |
| 164 | common.autotest_dir) |
Don Garrett | 8db752c | 2014-10-17 16:56:55 -0700 | [diff] [blame] | 165 | |
Don Garrett | 699b4b3 | 2014-12-11 13:10:15 -0800 | [diff] [blame] | 166 | print('Running: %s: %s' % (cmd_tag, expanded_command)) |
Don Garrett | 03432d6 | 2014-11-19 18:18:35 -0800 | [diff] [blame] | 167 | if dryrun: |
Don Garrett | 699b4b3 | 2014-12-11 13:10:15 -0800 | [diff] [blame] | 168 | print('Skip: %s' % expanded_command) |
Don Garrett | 03432d6 | 2014-11-19 18:18:35 -0800 | [diff] [blame] | 169 | else: |
Don Garrett | 03432d6 | 2014-11-19 18:18:35 -0800 | [diff] [blame] | 170 | subprocess.check_call(expanded_command, shell=True) |
Don Garrett | 8db752c | 2014-10-17 16:56:55 -0700 | [diff] [blame] | 171 | |
Don Garrett | 8db752c | 2014-10-17 16:56:55 -0700 | [diff] [blame] | 172 | |
Don Garrett | 03432d6 | 2014-11-19 18:18:35 -0800 | [diff] [blame] | 173 | def restart_service(service_name, dryrun=False): |
Don Garrett | d032172 | 2014-11-18 16:03:33 -0800 | [diff] [blame] | 174 | """Restart a service. |
| 175 | |
| 176 | Restarts the standard service with "service <name> restart". |
| 177 | |
| 178 | @param service_name: The name of the service to restart. |
Don Garrett | 03432d6 | 2014-11-19 18:18:35 -0800 | [diff] [blame] | 179 | @param dryrun: Don't really run anything, just print out the command. |
Don Garrett | d032172 | 2014-11-18 16:03:33 -0800 | [diff] [blame] | 180 | |
| 181 | @raises subprocess.CalledProcessError on a command failure. |
| 182 | """ |
Don Garrett | 03432d6 | 2014-11-19 18:18:35 -0800 | [diff] [blame] | 183 | cmd = ['sudo', 'service', service_name, 'restart'] |
Don Garrett | 699b4b3 | 2014-12-11 13:10:15 -0800 | [diff] [blame] | 184 | print('Restarting: %s' % service_name) |
Don Garrett | 03432d6 | 2014-11-19 18:18:35 -0800 | [diff] [blame] | 185 | if dryrun: |
Don Garrett | 699b4b3 | 2014-12-11 13:10:15 -0800 | [diff] [blame] | 186 | print('Skip: %s' % ' '.join(cmd)) |
Don Garrett | 03432d6 | 2014-11-19 18:18:35 -0800 | [diff] [blame] | 187 | else: |
Don Garrett | 03432d6 | 2014-11-19 18:18:35 -0800 | [diff] [blame] | 188 | subprocess.check_call(cmd) |
Don Garrett | d032172 | 2014-11-18 16:03:33 -0800 | [diff] [blame] | 189 | |
| 190 | |
| 191 | def service_status(service_name): |
| 192 | """Return the results "status <name>" for a given service. |
| 193 | |
| 194 | This string is expected to contain the pid, and so to change is the service |
| 195 | is shutdown or restarted for any reason. |
| 196 | |
| 197 | @param service_name: The name of the service to check on. |
Don Garrett | 03432d6 | 2014-11-19 18:18:35 -0800 | [diff] [blame] | 198 | |
Don Garrett | d032172 | 2014-11-18 16:03:33 -0800 | [diff] [blame] | 199 | @returns The output of the external command. |
| 200 | Ex: autofs start/running, process 1931 |
| 201 | |
| 202 | @raises subprocess.CalledProcessError on a command failure. |
| 203 | """ |
| 204 | return subprocess.check_output(['sudo', 'status', service_name]) |
| 205 | |
| 206 | |
Don Garrett | 03432d6 | 2014-11-19 18:18:35 -0800 | [diff] [blame] | 207 | def restart_services(service_names, dryrun=False): |
Don Garrett | d032172 | 2014-11-18 16:03:33 -0800 | [diff] [blame] | 208 | """Restart services as needed for the current server type. |
| 209 | |
| 210 | Restart the listed set of services, and watch to see if they are stable for |
| 211 | at least SERVICE_STABILITY_TIMER. It restarts all services quickly, |
| 212 | waits for that delay, then verifies the status of all of them. |
| 213 | |
| 214 | @param service_names: The list of service to restart and monitor. |
Don Garrett | 03432d6 | 2014-11-19 18:18:35 -0800 | [diff] [blame] | 215 | @param dryrun: Don't really restart the service, just print out the command. |
Don Garrett | d032172 | 2014-11-18 16:03:33 -0800 | [diff] [blame] | 216 | |
| 217 | @raises subprocess.CalledProcessError on a command failure. |
Don Garrett | 03432d6 | 2014-11-19 18:18:35 -0800 | [diff] [blame] | 218 | @raises UnstableServices if any services are unstable after restart. |
Don Garrett | d032172 | 2014-11-18 16:03:33 -0800 | [diff] [blame] | 219 | """ |
| 220 | service_statuses = {} |
| 221 | |
Don Garrett | 03432d6 | 2014-11-19 18:18:35 -0800 | [diff] [blame] | 222 | if dryrun: |
| 223 | for name in service_names: |
| 224 | restart_service(name, dryrun=True) |
| 225 | return |
| 226 | |
Don Garrett | d032172 | 2014-11-18 16:03:33 -0800 | [diff] [blame] | 227 | # Restart each, and record the status (including pid). |
| 228 | for name in service_names: |
| 229 | restart_service(name) |
| 230 | service_statuses[name] = service_status(name) |
| 231 | |
| 232 | # Wait for a while to let the services settle. |
| 233 | time.sleep(SERVICE_STABILITY_TIMER) |
| 234 | |
| 235 | # Look for any services that changed status. |
| 236 | unstable_services = [n for n in service_names |
| 237 | if service_status(n) != service_statuses[n]] |
| 238 | |
| 239 | # Report any services having issues. |
| 240 | if unstable_services: |
| 241 | raise UnstableServices(unstable_services) |
Don Garrett | 8db752c | 2014-10-17 16:56:55 -0700 | [diff] [blame] | 242 | |
| 243 | |
Don Garrett | 03432d6 | 2014-11-19 18:18:35 -0800 | [diff] [blame] | 244 | def run_deploy_actions(dryrun=False): |
Don Garrett | fa2c1c4 | 2014-12-11 12:11:49 -0800 | [diff] [blame^] | 245 | """Run arbitrary update commands specified in global.ini. |
Don Garrett | 8db752c | 2014-10-17 16:56:55 -0700 | [diff] [blame] | 246 | |
Don Garrett | 03432d6 | 2014-11-19 18:18:35 -0800 | [diff] [blame] | 247 | @param dryrun: Don't really restart the service, just print out the command. |
Don Garrett | 8db752c | 2014-10-17 16:56:55 -0700 | [diff] [blame] | 248 | |
Don Garrett | 03432d6 | 2014-11-19 18:18:35 -0800 | [diff] [blame] | 249 | @raises subprocess.CalledProcessError on a command failure. |
| 250 | @raises UnstableServices if any services are unstable after restart. |
| 251 | """ |
Don Garrett | d032172 | 2014-11-18 16:03:33 -0800 | [diff] [blame] | 252 | cmds = discover_update_commands() |
| 253 | if cmds: |
| 254 | print('Running update commands:', ', '.join(cmds)) |
| 255 | for cmd in cmds: |
Don Garrett | 03432d6 | 2014-11-19 18:18:35 -0800 | [diff] [blame] | 256 | update_command(cmd, dryrun=dryrun) |
Don Garrett | d032172 | 2014-11-18 16:03:33 -0800 | [diff] [blame] | 257 | |
| 258 | services = discover_restart_services() |
| 259 | if services: |
Don Garrett | 03432d6 | 2014-11-19 18:18:35 -0800 | [diff] [blame] | 260 | print('Restarting Services:', ', '.join(services)) |
| 261 | restart_services(services, dryrun=dryrun) |
| 262 | |
| 263 | |
Don Garrett | fa2c1c4 | 2014-12-11 12:11:49 -0800 | [diff] [blame^] | 264 | def report_changes(versions_before, versions_after): |
| 265 | """Produce a report describing what changed in all repos. |
| 266 | |
| 267 | @param versions_before: Results of repo_versions() from before the update. |
| 268 | @param versions_after: Results of repo_versions() from after the update. |
| 269 | |
| 270 | @returns string containing a human friendly changes report. |
| 271 | """ |
| 272 | result = [] |
| 273 | |
| 274 | for project in sorted(set(versions_before.keys() + versions_after.keys())): |
| 275 | result.append('%s:' % project) |
| 276 | |
| 277 | _, before_hash = versions_before.get(project, (None, None)) |
| 278 | after_dir, after_hash = versions_after.get(project, (None, None)) |
| 279 | |
| 280 | if project not in versions_before: |
| 281 | result.append('Added.') |
| 282 | |
| 283 | elif project not in versions_after: |
| 284 | result.append('Removed.') |
| 285 | |
| 286 | elif before_hash == after_hash: |
| 287 | result.append('No Change.') |
| 288 | |
| 289 | else: |
| 290 | hashes = '%s..%s' % (before_hash, after_hash) |
| 291 | cmd = ['git', 'log', hashes, '--oneline'] |
| 292 | out = subprocess.check_output(cmd, cwd=after_dir, |
| 293 | stderr=subprocess.STDOUT) |
| 294 | result.append(out.strip()) |
| 295 | |
| 296 | result.append('') |
| 297 | |
| 298 | return '\n'.join(result) |
| 299 | |
| 300 | |
Don Garrett | 03432d6 | 2014-11-19 18:18:35 -0800 | [diff] [blame] | 301 | def parse_arguments(args): |
| 302 | """Parse command line arguments. |
| 303 | |
| 304 | @param args: The command line arguments to parse. (ususally sys.argsv[1:]) |
| 305 | |
Don Garrett | 4003636 | 2014-12-08 15:52:44 -0800 | [diff] [blame] | 306 | @returns An argparse.Namespace populated with argument values. |
Don Garrett | 03432d6 | 2014-11-19 18:18:35 -0800 | [diff] [blame] | 307 | """ |
| 308 | parser = argparse.ArgumentParser( |
| 309 | description='Command to update an autotest server.') |
| 310 | parser.add_argument('--skip-verify', action='store_false', |
| 311 | dest='verify', default=True, |
| 312 | help='Disable verification of a clean repository.') |
| 313 | parser.add_argument('--skip-update', action='store_false', |
| 314 | dest='update', default=True, |
| 315 | help='Skip the repository source code update.') |
| 316 | parser.add_argument('--skip-actions', action='store_false', |
| 317 | dest='actions', default=True, |
| 318 | help='Skip the post update actions.') |
| 319 | parser.add_argument('--skip-report', action='store_false', |
| 320 | dest='report', default=True, |
| 321 | help='Skip the git version report.') |
Don Garrett | e371891 | 2014-12-05 13:11:44 -0800 | [diff] [blame] | 322 | parser.add_argument('--actions-only', action='store_true', |
| 323 | help='Run the post update actions (restart services).') |
Don Garrett | 03432d6 | 2014-11-19 18:18:35 -0800 | [diff] [blame] | 324 | parser.add_argument('--dryrun', action='store_true', |
| 325 | help='Don\'t actually run any commands, just log.') |
| 326 | |
| 327 | results = parser.parse_args(args) |
| 328 | |
Don Garrett | e371891 | 2014-12-05 13:11:44 -0800 | [diff] [blame] | 329 | if results.actions_only: |
| 330 | results.verify = False |
| 331 | results.update = False |
| 332 | results.report = False |
| 333 | |
Don Garrett | 03432d6 | 2014-11-19 18:18:35 -0800 | [diff] [blame] | 334 | # TODO(dgarrett): Make these behaviors support dryrun. |
| 335 | if results.dryrun: |
| 336 | results.verify = False |
| 337 | results.update = False |
| 338 | |
| 339 | return results |
| 340 | |
| 341 | |
| 342 | def main(args): |
| 343 | """Main method.""" |
| 344 | os.chdir(common.autotest_dir) |
| 345 | global_config.global_config.parse_config_file() |
| 346 | |
| 347 | behaviors = parse_arguments(args) |
| 348 | |
| 349 | if behaviors.verify: |
Don Garrett | d032172 | 2014-11-18 16:03:33 -0800 | [diff] [blame] | 350 | try: |
Don Garrett | 03432d6 | 2014-11-19 18:18:35 -0800 | [diff] [blame] | 351 | print('Checking tree status:') |
| 352 | verify_repo_clean() |
| 353 | print('Clean.') |
| 354 | except DirtyTreeException as e: |
| 355 | print('Local tree is dirty, can\'t perform update safely.') |
| 356 | print() |
| 357 | print('repo status:') |
Don Garrett | d032172 | 2014-11-18 16:03:33 -0800 | [diff] [blame] | 358 | print(e.args[0]) |
| 359 | return 1 |
Don Garrett | 8db752c | 2014-10-17 16:56:55 -0700 | [diff] [blame] | 360 | |
Don Garrett | fa2c1c4 | 2014-12-11 12:11:49 -0800 | [diff] [blame^] | 361 | versions_before = versions_after = {} |
| 362 | |
Don Garrett | 03432d6 | 2014-11-19 18:18:35 -0800 | [diff] [blame] | 363 | if behaviors.update: |
| 364 | print('Checking repository versions.') |
| 365 | versions_before = repo_versions() |
| 366 | |
| 367 | print('Updating Repo.') |
| 368 | repo_sync() |
| 369 | |
| 370 | print('Checking repository versions after update.') |
Don Garrett | fa2c1c4 | 2014-12-11 12:11:49 -0800 | [diff] [blame^] | 371 | versions_after = repo_versions() |
| 372 | if versions_before == versions_after: |
Don Garrett | 03432d6 | 2014-11-19 18:18:35 -0800 | [diff] [blame] | 373 | print('No change found.') |
| 374 | return |
| 375 | |
| 376 | if behaviors.actions: |
| 377 | try: |
| 378 | run_deploy_actions(dryrun=behaviors.dryrun) |
| 379 | except UnstableServices as e: |
| 380 | print('The following services were not stable after ' |
| 381 | 'the update:') |
| 382 | print(e.args[0]) |
| 383 | return 1 |
| 384 | |
Don Garrett | fa2c1c4 | 2014-12-11 12:11:49 -0800 | [diff] [blame^] | 385 | if behaviors.report and versions_before and versions_after: |
| 386 | print('Changes:') |
| 387 | print(report_changes(versions_before, versions_after)) |
Don Garrett | 8db752c | 2014-10-17 16:56:55 -0700 | [diff] [blame] | 388 | |
| 389 | |
| 390 | if __name__ == '__main__': |
Don Garrett | 03432d6 | 2014-11-19 18:18:35 -0800 | [diff] [blame] | 391 | sys.exit(main(sys.argv[1:])) |