Add rudimentary --resume API tracking.

Currently, this is just inlining the basic check of "are we completely
incompatible".  Later options added to cbuildbot that are used in
--resume will result in bumping the minor API version up (which
can be used by consuming code to limit if a option should be passed in).

BUG=chromium-os:31210
TEST=cbuildbot --reexec-api-version
TEST=cbuildbot -p chromiumos/chromite x86-generic-paladin
TEST=ran from a chromite that has this patch,
	 cbuildbot x86-generic-paladin --nobootstrap

Change-Id: I92c169543abc6ee1c1c18da84b6b2b5dfb329409
Reviewed-on: https://gerrit.chromium.org/gerrit/23322
Commit-Ready: Brian Harring <ferringb@chromium.org>
Reviewed-by: Brian Harring <ferringb@chromium.org>
Tested-by: Brian Harring <ferringb@chromium.org>
diff --git a/scripts/cbuildbot.py b/scripts/cbuildbot.py
index b7803f2..9bf3a8a 100644
--- a/scripts/cbuildbot.py
+++ b/scripts/cbuildbot.py
@@ -51,6 +51,17 @@
                       constants.PALADIN_TYPE]
 _BUILDBOT_REQUIRED_BINARIES = ('pbzip2',)
 
+# Used by --resume and --bootstrap to decipher which options they
+# can pass to the target cbuildbot (since it may not have that
+# option).
+# Format is Major:Minor.  Minor is used for tracking new options added
+# that aren't critical to the older version if it's not ran.
+# Major is used for tracking heavy API breakage- for example, no longer
+# supporting the --resume option.
+_REEXEC_API_MAJOR = 0
+_REEXEC_API_MINOR = 1
+_REEXEC_API_VERSION = '%i.%i' % (_REEXEC_API_MAJOR, _REEXEC_API_MINOR)
+
 
 def _PrintValidConfigs(display_all=False):
   """Print a list of valid buildbot configs.
@@ -253,6 +264,23 @@
     if not self.options.resume:
       results_lib.WriteCheckpoint(self.options.buildroot)
 
+    # Get the re-exec API version of the target chromite; if it's incompatible
+    # with us, bail now.
+    api = cros_lib.RunCommandCaptureOutput(
+        [constants.PATH_TO_CBUILDBOT] + ['--reexec-api-version'],
+        cwd=self.options.buildroot, error_code_ok=True)
+    # If the command failed, then we're targeting a cbuildbot that lacks the
+    # option; assume 0:0 (ie, initial state).
+    major, minor = 0, 0
+    if api.returncode == 0:
+      major, minor = map(int, api.output.strip().split('.', 1))
+
+    if major != _REEXEC_API_MAJOR:
+      cros_lib.Die(
+          'The targeted version of chromite in buildroot %s requires '
+          'api version %i, but we are api version %i.  We cannot proceed.'
+          % (self.options.buildroot, major, _REEXEC_API_MAJOR))
+
     # Re-write paths to use absolute paths.
     # Suppress any timeout options given from the commandline in the
     # invoked cbuildbot; our timeout will enforce it instead.
@@ -841,6 +869,11 @@
                         "default, if this option isn't given but cbuildbot "
                         'is invoked from a repo checkout, cbuildbot will '
                         'use the repo root.')
+  # Used for handling forwards/backwards compatibility for --resume and
+  # --bootstrap.
+  group.add_option('--reexec-api-version', dest='output_api_version',
+                   action='store_true', default=False,
+                   help=optparse.SUPPRESS_HELP)
   # Indicates this is running on a remote trybot machine.
   group.add_option('--remote-trybot', dest='remote_trybot', action='store_true',
                    default=False, help=optparse.SUPPRESS_HELP)
@@ -1010,6 +1043,11 @@
 def _ParseCommandLine(parser, argv):
   """Completely parse the commandline arguments"""
   (options, args) = parser.parse_args(argv)
+
+  if options.output_api_version:
+    print _REEXEC_API_VERSION
+    sys.exit(0)
+
   if options.list:
     _PrintValidConfigs(options.print_all)
     sys.exit(0)