import argh
import os
import toml
from glob import glob
from .log import Logger
from .run_spec import do_run_spec
from .run_crate import CrateNode, get_crate
from .clients import client_errors


class Executor:
    def __init__(self,
                 *,
                 track_dir,
                 log,
                 sample_mode,
                 result_hosts=None,
                 crate_root=None,
                 fail_fast=None):
        self.track_dir = track_dir
        self.sample_mode = sample_mode
        self.result_hosts = result_hosts
        self.crate_root = crate_root
        self.log = log
        self.fail_fast = fail_fast

    def _expand_paths(self, paths):
        paths = (os.path.join(self.track_dir, path) for path in paths)
        paths = (os.path.abspath(path) for path in paths)
        return (p for path in paths for p in glob(path))

    def _run_specs(self, specs, benchmark_host, action=None):
        specs = self._expand_paths(specs)
        for spec in specs:
            self.log.info('### Running spec file: ', os.path.basename(spec))
            try:
                do_run_spec(
                    spec=spec,
                    benchmark_hosts=benchmark_host,
                    log=self.log,
                    result_hosts=self.result_hosts,
                    sample_mode=self.sample_mode,
                    action=action)
            except Exception:
                if self.fail_fast:
                    raise
                else:
                    self.log.info('WARNING: Spec file failed due to the following exception:')
                    import traceback
                    traceback.print_exc()

    def _execute_specs(self, specs, benchmark_host):
        if isinstance(specs, list):
            self._run_specs(specs, benchmark_host)
        else:
            fixtures = specs.get('fixtures', [])
            queries = specs.get('queries', [])
            full = specs.get('full', [])
            self._run_specs(fixtures, benchmark_host, action=['setup'])
            self._run_specs(queries, benchmark_host, action=['queries'])
            self._run_specs(fixtures, benchmark_host, action=['teardown'])
            self._run_specs(full, benchmark_host)

    def execute(self, track):
        configurations = list(self._expand_paths(track['configurations']))
        versions = track['versions']
        for version in versions:
            self.log.info('# Version: ', version)
            for c, configuration in enumerate(configurations):
                self.log.info('## Starting Crate {0}, configuration: {1}'.format(
                    os.path.basename(version),
                    os.path.basename(configuration)
                ))
                configuration = toml.load(os.path.join(self.track_dir, configuration))
                crate_dir = get_crate(version, self.crate_root)
                with CrateNode(crate_dir=crate_dir,
                               env=configuration.get('env'),
                               settings=configuration.get('settings')) as node:
                    node.start()
                    self._execute_specs(track['specs'], node.http_url)


@argh.arg('-r', '--result_hosts', type=str)
@argh.arg('-of', '--output-fmt', choices=['json', 'text'], default='text')
@argh.arg('--failfast', action='store_true')
@argh.arg('--logfile-info', help='Redirect info messages to a file')
@argh.arg('--logfile-result', help='Redirect benchmark results to a file')
@argh.arg('--sample-mode', choices=('all', 'reservoir'),
          help='Method used for sampling', default='reservoir')
@argh.wrap_errors([KeyboardInterrupt] + client_errors)
def run_track(track,
              result_hosts=None,
              crate_root=None,
              output_fmt=None,
              logfile_info=None,
              logfile_result=None,
              failfast=False,
              sample_mode='reservoir'):
    """Execute a track file"""
    with Logger(output_fmt=output_fmt,
                logfile_info=logfile_info,
                logfile_result=logfile_result) as log:
        executor = Executor(
            track_dir=os.path.dirname(track),
            log=log,
            result_hosts=result_hosts,
            crate_root=crate_root,
            fail_fast=failfast,
            sample_mode=sample_mode
        )
        executor.execute(toml.load(track))


def main():
    argh.dispatch_command(run_track)


if __name__ == "__main__":
    main()

@@ -27,16 +27,27 @@
 rt toml%0A
+import sys%0A
 from glo
@@ -980,16 +980,36 @@
 (specs)%0A
+        errors = %5B%5D%0A
         
@@ -1434,16 +1434,52 @@
 eption:%0A
+                errors.append(True)%0A
         
@@ -1726,16 +1726,43 @@
 nt_exc()
+%0A        return any(errors)
 %0A%0A    de
@@ -1804,24 +1804,44 @@
 mark_host):%0A
+        errors = %5B%5D%0A
         if i
@@ -1868,32 +1868,46 @@
 t):%0A            
+errors.append(
 self._run_specs(
@@ -1928,16 +1928,17 @@
 rk_host)
+)
 %0A       
@@ -2077,24 +2077,55 @@
 'full', %5B%5D)%0A
+            errors.append(%0A    
             
@@ -2183,17 +2183,49 @@
 setup'%5D)
+)
 %0A
+            errors.append(%0A    
         
@@ -2288,17 +2288,49 @@
 eries'%5D)
+)
 %0A
+            errors.append(%0A    
         
@@ -2395,17 +2395,49 @@
 rdown'%5D)
+)
 %0A
+            errors.append(%0A    
         
@@ -2477,16 +2477,44 @@
 rk_host)
+)%0A        return any(errors)
 %0A%0A    de
@@ -2649,16 +2649,38 @@
 sions'%5D%0A
+        error = False%0A
         
@@ -3409,24 +3409,33 @@
             
+ _error =
  self._execu
@@ -3474,16 +3474,81 @@
 tp_url)%0A
+                    error = error or _error%0A        return error%0A
 %0A%0A@argh.
@@ -4694,32 +4694,40 @@
        )%0A       
+ error =
  executor.execut
@@ -4746,16 +4746,58 @@
 track))%0A
+        if error:%0A            sys.exit(1)%0A
 %0A%0Adef ma

Make run-track exit with a failure code on errors