blob: bb8b3cf82fdacefb162021f88d50460b2aee8fcf [file] [log] [blame]
Don Garrett8db752c2014-10-17 16:56:55 -07001#!/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
8This script is designed to run on all autotest servers to allow them to
9automatically self-update based on the manifests used to create their (existing)
10repos.
11"""
12
13from __future__ import print_function
14
15import ConfigParser
Don Garrett03432d62014-11-19 18:18:35 -080016import argparse
Don Garrett8db752c2014-10-17 16:56:55 -070017import os
Don Garrett699b4b32014-12-11 13:10:15 -080018import re
Dan Shicf278042016-04-06 21:16:34 -070019import socket
Don Garrett8db752c2014-10-17 16:56:55 -070020import subprocess
21import sys
22import time
23
24import common
25
26from autotest_lib.client.common_lib import global_config
Dan Shiac6fdbf2016-04-09 17:36:28 -070027from autotest_lib.server import utils as server_utils
Dan Shicf278042016-04-06 21:16:34 -070028from autotest_lib.server.cros.dynamic_suite import frontend_wrappers
Don Garrett8db752c2014-10-17 16:56:55 -070029
Dan Shiac6fdbf2016-04-09 17:36:28 -070030
Don Garrettd0321722014-11-18 16:03:33 -080031# How long after restarting a service do we watch it to see if it's stable.
Don Garrett6073ba92015-07-23 15:01:39 -070032SERVICE_STABILITY_TIMER = 120
Don Garrettd0321722014-11-18 16:03:33 -080033
Dan Shicf278042016-04-06 21:16:34 -070034# A list of commands that only applies to primary server. For example,
35# test_importer should only be run in primary master scheduler. If two servers
36# are both running test_importer, there is a chance to fail as both try to
37# update the same table.
38PRIMARY_ONLY_COMMANDS = ['test_importer']
Shuqian Zhaoa3438a52016-09-20 15:11:02 -070039# A dict to map update_commands defined in config file to repos or files that
40# decide whether need to update these commands. E.g. if no changes under
41# frontend repo, no need to update afe.
Aviv Keshet85622032016-10-03 03:17:26 -070042COMMANDS_TO_REPOS_DICT = {'afe': 'frontend/',
43 'tko': 'tko/'}
Dan Shicf278042016-04-06 21:16:34 -070044
Dan Shiac6fdbf2016-04-09 17:36:28 -070045AFE = frontend_wrappers.RetryingAFE(
46 server=server_utils.get_global_afe_hostname(), timeout_min=5,
47 delay_sec=10)
Don Garrett8db752c2014-10-17 16:56:55 -070048
49class DirtyTreeException(Exception):
Don Garrettd0321722014-11-18 16:03:33 -080050 """Raised when the tree has been modified in an unexpected way."""
Don Garrett8db752c2014-10-17 16:56:55 -070051
52
53class UnknownCommandException(Exception):
Don Garrettd0321722014-11-18 16:03:33 -080054 """Raised when we try to run a command name with no associated command."""
Don Garrett8db752c2014-10-17 16:56:55 -070055
56
57class UnstableServices(Exception):
Don Garrettd0321722014-11-18 16:03:33 -080058 """Raised if a service appears unstable after restart."""
Don Garrett8db752c2014-10-17 16:56:55 -070059
60
Don Garrett35711212014-12-18 14:33:41 -080061def strip_terminal_codes(text):
62 """This function removes all terminal formatting codes from a string.
63
64 @param text: String of text to cleanup.
65 @returns String with format codes removed.
66 """
67 ESC = '\x1b'
68 return re.sub(ESC+r'\[[^m]*m', '', text)
69
70
Don Garrett8db752c2014-10-17 16:56:55 -070071def verify_repo_clean():
Shuqian Zhao8754a1a2016-08-24 12:54:11 -070072 """This function cleans the current repo then verifies that it is valid.
Don Garrett8db752c2014-10-17 16:56:55 -070073
Shuqian Zhao8754a1a2016-08-24 12:54:11 -070074 @raises DirtyTreeException if the repo is still not clean.
Don Garrett8db752c2014-10-17 16:56:55 -070075 @raises subprocess.CalledProcessError on a repo command failure.
76 """
Shuqian Zhao8754a1a2016-08-24 12:54:11 -070077 subprocess.check_output(['git', 'reset', '--hard'])
Don Garrett8db752c2014-10-17 16:56:55 -070078 out = subprocess.check_output(['repo', 'status'], stderr=subprocess.STDOUT)
Don Garrett35711212014-12-18 14:33:41 -080079 out = strip_terminal_codes(out).strip()
Don Garrett699b4b32014-12-11 13:10:15 -080080
Shuqian Zhao8754a1a2016-08-24 12:54:11 -070081 if not 'working directory clean' in out:
Dan Shicf278042016-04-06 21:16:34 -070082 raise DirtyTreeException(out)
Don Garrett8db752c2014-10-17 16:56:55 -070083
Don Garrett8db752c2014-10-17 16:56:55 -070084
85def repo_versions():
86 """This function collects the versions of all git repos in the general repo.
87
Don Garrettfa2c1c42014-12-11 12:11:49 -080088 @returns A dictionary mapping project names to git hashes for HEAD.
Don Garrett8db752c2014-10-17 16:56:55 -070089 @raises subprocess.CalledProcessError on a repo command failure.
90 """
Don Garrettfa2c1c42014-12-11 12:11:49 -080091 cmd = ['repo', 'forall', '-p', '-c', 'pwd && git log -1 --format=%h']
Don Garrett35711212014-12-18 14:33:41 -080092 output = strip_terminal_codes(subprocess.check_output(cmd))
Don Garrettfa2c1c42014-12-11 12:11:49 -080093
94 # The expected output format is:
95
96 # project chrome_build/
97 # /dir/holding/chrome_build
98 # 73dee9d
99 #
100 # project chrome_release/
101 # /dir/holding/chrome_release
102 # 9f3a5d8
103
104 lines = output.splitlines()
105
106 PROJECT_PREFIX = 'project '
107
108 project_heads = {}
109 for n in range(0, len(lines), 4):
110 project_line = lines[n]
111 project_dir = lines[n+1]
112 project_hash = lines[n+2]
113 # lines[n+3] is a blank line, but doesn't exist for the final block.
114
115 # Convert 'project chrome_build/' -> 'chrome_build'
116 assert project_line.startswith(PROJECT_PREFIX)
117 name = project_line[len(PROJECT_PREFIX):].rstrip('/')
118
119 project_heads[name] = (project_dir, project_hash)
120
121 return project_heads
Don Garrett8db752c2014-10-17 16:56:55 -0700122
123
Shuqian Zhaoa3438a52016-09-20 15:11:02 -0700124def repo_versions_to_decide_whether_run_cmd_update():
125 """Collect versions of repos/files defined in COMMANDS_TO_REPOS_DICT.
126
127 For the update_commands defined in config files, no need to run the command
128 every time. Only run it when the repos/files related to the commands have
129 been changed.
130
131 @returns A set of tuples: {(cmd, repo_version), ()...}
132 """
133 results = set()
134 for cmd, repo in COMMANDS_TO_REPOS_DICT.iteritems():
135 version = subprocess.check_output(
136 ['git', 'log', '-1', '--pretty=tformat:%h',
137 '%s/%s' % (common.autotest_dir, repo)])
138 results.add((cmd, version.strip()))
139 return results
140
141
Shuqian Zhao8754a1a2016-08-24 12:54:11 -0700142def repo_sync(update_push_servers=False):
Don Garrett8db752c2014-10-17 16:56:55 -0700143 """Perform a repo sync.
144
Shuqian Zhao8754a1a2016-08-24 12:54:11 -0700145 @param update_push_servers: If True, then update test_push servers to ToT.
146 Otherwise, update server to prod branch.
Don Garrett8db752c2014-10-17 16:56:55 -0700147 @raises subprocess.CalledProcessError on a repo command failure.
148 """
Don Garrettd0321722014-11-18 16:03:33 -0800149 subprocess.check_output(['repo', 'sync'])
Shuqian Zhao8754a1a2016-08-24 12:54:11 -0700150 if update_push_servers:
151 print('Updating push servers, checkout cros/master')
152 subprocess.check_output(['git', 'checkout', 'cros/master'])
153 else:
154 print('Updating server to prod branch')
155 subprocess.check_output(['git', 'checkout', 'cros/prod'])
Don Garrett8db752c2014-10-17 16:56:55 -0700156
157
Don Garrettd0321722014-11-18 16:03:33 -0800158def discover_update_commands():
159 """Lookup the commands to run on this server.
Don Garrett8db752c2014-10-17 16:56:55 -0700160
Don Garrettd0321722014-11-18 16:03:33 -0800161 These commonly come from shadow_config.ini, since they vary by server type.
Don Garrett8db752c2014-10-17 16:56:55 -0700162
Don Garrettd0321722014-11-18 16:03:33 -0800163 @returns List of command names in string format.
Don Garrett8db752c2014-10-17 16:56:55 -0700164 """
Don Garrett8db752c2014-10-17 16:56:55 -0700165 try:
Don Garrettd0321722014-11-18 16:03:33 -0800166 return global_config.global_config.get_config_value(
Don Garrett8db752c2014-10-17 16:56:55 -0700167 'UPDATE', 'commands', type=list)
168
169 except (ConfigParser.NoSectionError, global_config.ConfigError):
Don Garrettd0321722014-11-18 16:03:33 -0800170 return []
Don Garrett8db752c2014-10-17 16:56:55 -0700171
Don Garrettd0321722014-11-18 16:03:33 -0800172
173def discover_restart_services():
174 """Find the services that need restarting on the current server.
175
176 These commonly come from shadow_config.ini, since they vary by server type.
177
178 @returns List of service names in string format.
179 """
Don Garrett8db752c2014-10-17 16:56:55 -0700180 try:
181 # From shadow_config.ini, lookup which services to restart.
Don Garrettd0321722014-11-18 16:03:33 -0800182 return global_config.global_config.get_config_value(
Don Garrett8db752c2014-10-17 16:56:55 -0700183 'UPDATE', 'services', type=list)
184
185 except (ConfigParser.NoSectionError, global_config.ConfigError):
Don Garrettd0321722014-11-18 16:03:33 -0800186 return []
Don Garrett8db752c2014-10-17 16:56:55 -0700187
Don Garrett8db752c2014-10-17 16:56:55 -0700188
Don Garrett03432d62014-11-19 18:18:35 -0800189def update_command(cmd_tag, dryrun=False):
Don Garrettd0321722014-11-18 16:03:33 -0800190 """Restart a command.
Don Garrett8db752c2014-10-17 16:56:55 -0700191
Don Garrettd0321722014-11-18 16:03:33 -0800192 The command name is looked up in global_config.ini to find the full command
193 to run, then it's executed.
Don Garrett8db752c2014-10-17 16:56:55 -0700194
Don Garrettd0321722014-11-18 16:03:33 -0800195 @param cmd_tag: Which command to restart.
Don Garrett03432d62014-11-19 18:18:35 -0800196 @param dryrun: If true print the command that would have been run.
Don Garrett8db752c2014-10-17 16:56:55 -0700197
Don Garrettd0321722014-11-18 16:03:33 -0800198 @raises UnknownCommandException If cmd_tag can't be looked up.
199 @raises subprocess.CalledProcessError on a command failure.
200 """
201 # Lookup the list of commands to consider. They are intended to be
202 # in global_config.ini so that they can be shared everywhere.
203 cmds = dict(global_config.global_config.config.items(
204 'UPDATE_COMMANDS'))
Don Garrett8db752c2014-10-17 16:56:55 -0700205
Don Garrettd0321722014-11-18 16:03:33 -0800206 if cmd_tag not in cmds:
207 raise UnknownCommandException(cmd_tag, cmds)
Don Garrett8db752c2014-10-17 16:56:55 -0700208
Don Garrettd0321722014-11-18 16:03:33 -0800209 expanded_command = cmds[cmd_tag].replace('AUTOTEST_REPO',
210 common.autotest_dir)
Don Garrett8db752c2014-10-17 16:56:55 -0700211
Don Garrett699b4b32014-12-11 13:10:15 -0800212 print('Running: %s: %s' % (cmd_tag, expanded_command))
Don Garrett03432d62014-11-19 18:18:35 -0800213 if dryrun:
Don Garrett699b4b32014-12-11 13:10:15 -0800214 print('Skip: %s' % expanded_command)
Don Garrett03432d62014-11-19 18:18:35 -0800215 else:
Don Garrett4769c902015-01-05 15:58:56 -0800216 try:
217 subprocess.check_output(expanded_command, shell=True,
218 stderr=subprocess.STDOUT)
219 except subprocess.CalledProcessError as e:
220 print('FAILED:')
221 print(e.output)
222 raise
Don Garrett8db752c2014-10-17 16:56:55 -0700223
Don Garrett8db752c2014-10-17 16:56:55 -0700224
Don Garrett03432d62014-11-19 18:18:35 -0800225def restart_service(service_name, dryrun=False):
Don Garrettd0321722014-11-18 16:03:33 -0800226 """Restart a service.
227
228 Restarts the standard service with "service <name> restart".
229
230 @param service_name: The name of the service to restart.
Don Garrett03432d62014-11-19 18:18:35 -0800231 @param dryrun: Don't really run anything, just print out the command.
Don Garrettd0321722014-11-18 16:03:33 -0800232
233 @raises subprocess.CalledProcessError on a command failure.
234 """
Don Garrett03432d62014-11-19 18:18:35 -0800235 cmd = ['sudo', 'service', service_name, 'restart']
Don Garrett699b4b32014-12-11 13:10:15 -0800236 print('Restarting: %s' % service_name)
Don Garrett03432d62014-11-19 18:18:35 -0800237 if dryrun:
Don Garrett699b4b32014-12-11 13:10:15 -0800238 print('Skip: %s' % ' '.join(cmd))
Don Garrett03432d62014-11-19 18:18:35 -0800239 else:
Don Garrett03432d62014-11-19 18:18:35 -0800240 subprocess.check_call(cmd)
Don Garrettd0321722014-11-18 16:03:33 -0800241
242
243def service_status(service_name):
244 """Return the results "status <name>" for a given service.
245
246 This string is expected to contain the pid, and so to change is the service
247 is shutdown or restarted for any reason.
248
249 @param service_name: The name of the service to check on.
Don Garrett03432d62014-11-19 18:18:35 -0800250
Don Garrettd0321722014-11-18 16:03:33 -0800251 @returns The output of the external command.
252 Ex: autofs start/running, process 1931
253
254 @raises subprocess.CalledProcessError on a command failure.
255 """
256 return subprocess.check_output(['sudo', 'status', service_name])
257
258
Dan Shi57d4c732015-01-22 18:38:50 -0800259def restart_services(service_names, dryrun=False, skip_service_status=False):
Don Garrettd0321722014-11-18 16:03:33 -0800260 """Restart services as needed for the current server type.
261
262 Restart the listed set of services, and watch to see if they are stable for
263 at least SERVICE_STABILITY_TIMER. It restarts all services quickly,
264 waits for that delay, then verifies the status of all of them.
265
266 @param service_names: The list of service to restart and monitor.
Don Garrett03432d62014-11-19 18:18:35 -0800267 @param dryrun: Don't really restart the service, just print out the command.
Dan Shi57d4c732015-01-22 18:38:50 -0800268 @param skip_service_status: Set to True to skip service status check.
269 Default is False.
Don Garrettd0321722014-11-18 16:03:33 -0800270
271 @raises subprocess.CalledProcessError on a command failure.
Don Garrett03432d62014-11-19 18:18:35 -0800272 @raises UnstableServices if any services are unstable after restart.
Don Garrettd0321722014-11-18 16:03:33 -0800273 """
274 service_statuses = {}
275
Don Garrett03432d62014-11-19 18:18:35 -0800276 if dryrun:
277 for name in service_names:
278 restart_service(name, dryrun=True)
279 return
280
Don Garrettd0321722014-11-18 16:03:33 -0800281 # Restart each, and record the status (including pid).
282 for name in service_names:
283 restart_service(name)
284 service_statuses[name] = service_status(name)
285
Dan Shi57d4c732015-01-22 18:38:50 -0800286 # Skip service status check if --skip-service-status is specified. Used for
287 # servers in backup status.
288 if skip_service_status:
289 print('--skip-service-status is specified, skip checking services.')
290 return
291
Don Garrettd0321722014-11-18 16:03:33 -0800292 # Wait for a while to let the services settle.
293 time.sleep(SERVICE_STABILITY_TIMER)
294
295 # Look for any services that changed status.
296 unstable_services = [n for n in service_names
297 if service_status(n) != service_statuses[n]]
298
299 # Report any services having issues.
300 if unstable_services:
301 raise UnstableServices(unstable_services)
Don Garrett8db752c2014-10-17 16:56:55 -0700302
303
Shuqian Zhaoa3438a52016-09-20 15:11:02 -0700304def run_deploy_actions(cmds_skip=set(), dryrun=False,
305 skip_service_status=False):
Don Garrettfa2c1c42014-12-11 12:11:49 -0800306 """Run arbitrary update commands specified in global.ini.
Don Garrett8db752c2014-10-17 16:56:55 -0700307
Shuqian Zhaoa3438a52016-09-20 15:11:02 -0700308 @param cmds_skip: cmds no need to run since the corresponding repo/file
309 does not change.
Don Garrett03432d62014-11-19 18:18:35 -0800310 @param dryrun: Don't really restart the service, just print out the command.
Dan Shi57d4c732015-01-22 18:38:50 -0800311 @param skip_service_status: Set to True to skip service status check.
312 Default is False.
Don Garrett8db752c2014-10-17 16:56:55 -0700313
Don Garrett03432d62014-11-19 18:18:35 -0800314 @raises subprocess.CalledProcessError on a command failure.
315 @raises UnstableServices if any services are unstable after restart.
316 """
Shuqian Zhaoa3438a52016-09-20 15:11:02 -0700317 defined_cmds = set(discover_update_commands())
318 cmds = defined_cmds - cmds_skip
Don Garrettd0321722014-11-18 16:03:33 -0800319 if cmds:
320 print('Running update commands:', ', '.join(cmds))
321 for cmd in cmds:
Dan Shicf278042016-04-06 21:16:34 -0700322 if (cmd in PRIMARY_ONLY_COMMANDS and
323 not AFE.run('get_servers', hostname=socket.getfqdn(),
324 status='primary')):
325 print('Command %s is only applicable to primary servers.' % cmd)
326 continue
Don Garrett03432d62014-11-19 18:18:35 -0800327 update_command(cmd, dryrun=dryrun)
Don Garrettd0321722014-11-18 16:03:33 -0800328
329 services = discover_restart_services()
330 if services:
Don Garrett03432d62014-11-19 18:18:35 -0800331 print('Restarting Services:', ', '.join(services))
Dan Shi57d4c732015-01-22 18:38:50 -0800332 restart_services(services, dryrun=dryrun,
333 skip_service_status=skip_service_status)
Don Garrett03432d62014-11-19 18:18:35 -0800334
335
Don Garrettfa2c1c42014-12-11 12:11:49 -0800336def report_changes(versions_before, versions_after):
337 """Produce a report describing what changed in all repos.
338
339 @param versions_before: Results of repo_versions() from before the update.
340 @param versions_after: Results of repo_versions() from after the update.
341
342 @returns string containing a human friendly changes report.
343 """
344 result = []
345
Don Garrett35711212014-12-18 14:33:41 -0800346 if versions_after:
347 for project in sorted(set(versions_before.keys() + versions_after.keys())):
348 result.append('%s:' % project)
Don Garrettfa2c1c42014-12-11 12:11:49 -0800349
Don Garrett35711212014-12-18 14:33:41 -0800350 _, before_hash = versions_before.get(project, (None, None))
351 after_dir, after_hash = versions_after.get(project, (None, None))
Don Garrettfa2c1c42014-12-11 12:11:49 -0800352
Don Garrett35711212014-12-18 14:33:41 -0800353 if project not in versions_before:
354 result.append('Added.')
Don Garrettfa2c1c42014-12-11 12:11:49 -0800355
Don Garrett35711212014-12-18 14:33:41 -0800356 elif project not in versions_after:
357 result.append('Removed.')
Don Garrettfa2c1c42014-12-11 12:11:49 -0800358
Don Garrett35711212014-12-18 14:33:41 -0800359 elif before_hash == after_hash:
360 result.append('No Change.')
Don Garrettfa2c1c42014-12-11 12:11:49 -0800361
Don Garrett35711212014-12-18 14:33:41 -0800362 else:
363 hashes = '%s..%s' % (before_hash, after_hash)
364 cmd = ['git', 'log', hashes, '--oneline']
365 out = subprocess.check_output(cmd, cwd=after_dir,
366 stderr=subprocess.STDOUT)
367 result.append(out.strip())
Don Garrettfa2c1c42014-12-11 12:11:49 -0800368
Don Garrett35711212014-12-18 14:33:41 -0800369 result.append('')
370 else:
371 for project in sorted(versions_before.keys()):
372 _, before_hash = versions_before[project]
373 result.append('%s: %s' % (project, before_hash))
Don Garrettfa2c1c42014-12-11 12:11:49 -0800374 result.append('')
375
376 return '\n'.join(result)
377
378
Don Garrett03432d62014-11-19 18:18:35 -0800379def parse_arguments(args):
380 """Parse command line arguments.
381
382 @param args: The command line arguments to parse. (ususally sys.argsv[1:])
383
Don Garrett40036362014-12-08 15:52:44 -0800384 @returns An argparse.Namespace populated with argument values.
Don Garrett03432d62014-11-19 18:18:35 -0800385 """
386 parser = argparse.ArgumentParser(
387 description='Command to update an autotest server.')
388 parser.add_argument('--skip-verify', action='store_false',
389 dest='verify', default=True,
390 help='Disable verification of a clean repository.')
391 parser.add_argument('--skip-update', action='store_false',
392 dest='update', default=True,
393 help='Skip the repository source code update.')
394 parser.add_argument('--skip-actions', action='store_false',
395 dest='actions', default=True,
396 help='Skip the post update actions.')
397 parser.add_argument('--skip-report', action='store_false',
398 dest='report', default=True,
399 help='Skip the git version report.')
Don Garrette3718912014-12-05 13:11:44 -0800400 parser.add_argument('--actions-only', action='store_true',
401 help='Run the post update actions (restart services).')
Don Garrett03432d62014-11-19 18:18:35 -0800402 parser.add_argument('--dryrun', action='store_true',
403 help='Don\'t actually run any commands, just log.')
Dan Shi57d4c732015-01-22 18:38:50 -0800404 parser.add_argument('--skip-service-status', action='store_true',
405 help='Skip checking the service status.')
Shuqian Zhao8754a1a2016-08-24 12:54:11 -0700406 parser.add_argument('--update_push_servers', action='store_true',
407 help='Indicate to update test_push server. If not '
408 'specify, then update server to production.')
Shuqian Zhaoa3438a52016-09-20 15:11:02 -0700409 parser.add_argument('--force_update', action='store_true',
410 help='Force to run the update commands for afe, tko '
411 'and build_externals')
Don Garrett03432d62014-11-19 18:18:35 -0800412
413 results = parser.parse_args(args)
414
Don Garrette3718912014-12-05 13:11:44 -0800415 if results.actions_only:
416 results.verify = False
417 results.update = False
418 results.report = False
419
Don Garrett03432d62014-11-19 18:18:35 -0800420 # TODO(dgarrett): Make these behaviors support dryrun.
421 if results.dryrun:
422 results.verify = False
423 results.update = False
424
425 return results
426
427
428def main(args):
429 """Main method."""
430 os.chdir(common.autotest_dir)
431 global_config.global_config.parse_config_file()
432
433 behaviors = parse_arguments(args)
434
435 if behaviors.verify:
Don Garrettd0321722014-11-18 16:03:33 -0800436 try:
Don Garrett03432d62014-11-19 18:18:35 -0800437 print('Checking tree status:')
438 verify_repo_clean()
439 print('Clean.')
440 except DirtyTreeException as e:
441 print('Local tree is dirty, can\'t perform update safely.')
442 print()
443 print('repo status:')
Don Garrettd0321722014-11-18 16:03:33 -0800444 print(e.args[0])
445 return 1
Don Garrett8db752c2014-10-17 16:56:55 -0700446
Don Garrett35711212014-12-18 14:33:41 -0800447 versions_before = repo_versions()
448 versions_after = {}
Shuqian Zhaoa3438a52016-09-20 15:11:02 -0700449 cmd_versions_before = repo_versions_to_decide_whether_run_cmd_update()
450 cmd_versions_after = {}
Don Garrettfa2c1c42014-12-11 12:11:49 -0800451
Don Garrett03432d62014-11-19 18:18:35 -0800452 if behaviors.update:
Don Garrett03432d62014-11-19 18:18:35 -0800453 print('Updating Repo.')
Shuqian Zhao8754a1a2016-08-24 12:54:11 -0700454 repo_sync(behaviors.update_push_servers)
Don Garrettfa2c1c42014-12-11 12:11:49 -0800455 versions_after = repo_versions()
Shuqian Zhaoa3438a52016-09-20 15:11:02 -0700456 cmd_versions_after = repo_versions_to_decide_whether_run_cmd_update()
Don Garrett03432d62014-11-19 18:18:35 -0800457
458 if behaviors.actions:
Shuqian Zhaoa3438a52016-09-20 15:11:02 -0700459 # If the corresponding repo/file not change, no need to run the cmd.
460 cmds_skip = (set() if behaviors.force_update else
461 {t[0] for t in cmd_versions_before & cmd_versions_after})
Don Garrett03432d62014-11-19 18:18:35 -0800462 try:
Dan Shi57d4c732015-01-22 18:38:50 -0800463 run_deploy_actions(
Shuqian Zhaoa3438a52016-09-20 15:11:02 -0700464 cmds_skip, behaviors.dryrun, behaviors.skip_service_status)
Don Garrett03432d62014-11-19 18:18:35 -0800465 except UnstableServices as e:
466 print('The following services were not stable after '
467 'the update:')
468 print(e.args[0])
469 return 1
470
Don Garrett35711212014-12-18 14:33:41 -0800471 if behaviors.report:
Don Garrettfa2c1c42014-12-11 12:11:49 -0800472 print('Changes:')
473 print(report_changes(versions_before, versions_after))
Don Garrett8db752c2014-10-17 16:56:55 -0700474
475
476if __name__ == '__main__':
Don Garrett03432d62014-11-19 18:18:35 -0800477 sys.exit(main(sys.argv[1:]))