github.com/smithx10/nomad@v0.9.1-rc1/e2e/cli/command/run.go (about)

     1  package command
     2  
     3  import (
     4  	"fmt"
     5  	"os"
     6  	"os/exec"
     7  	"strings"
     8  
     9  	capi "github.com/hashicorp/consul/api"
    10  	hclog "github.com/hashicorp/go-hclog"
    11  	vapi "github.com/hashicorp/vault/api"
    12  	"github.com/mitchellh/cli"
    13  )
    14  
    15  func RunCommandFactory(meta Meta) cli.CommandFactory {
    16  	return func() (cli.Command, error) {
    17  		return &Run{Meta: meta}, nil
    18  	}
    19  }
    20  
    21  type Run struct {
    22  	Meta
    23  }
    24  
    25  func (c *Run) Help() string {
    26  	helpText := `
    27  Usage: nomad-e2e run (<provider>/<name>)...
    28  
    29    Two modes exist when using the run command.
    30  
    31    When no arguments are given to the run command, it will launch
    32    the e2e test suite against the Nomad cluster specified by the
    33    NOMAD_ADDR environment variable. If this is not set, it defaults
    34    to 'http://localhost:4646'
    35  
    36    Multiple arguments may be given to specify one or more environments to
    37    provision and run the e2e tests against. These are given in the form of
    38    <provider>/<name>. Globs are support, for example 'aws/*' would run tests
    39    against all of the environments under the aws provider. When using this mode,
    40    all of the provision flags are supported.
    41  
    42  General Options:
    43  
    44  ` + generalOptionsUsage() + `
    45  
    46  Run Options:
    47  
    48    -run regex
    49      Sets a regular expression for what tests to run. Uses '/' as a separator
    50  	to allow hierarchy between Suite/Case/Test.
    51  
    52  	Example '-run MyTestSuite' would only run tests under the MyTestSuite suite.
    53  
    54    -slow
    55      If set, will only run test suites marked as slow.
    56  `
    57  	return strings.TrimSpace(helpText)
    58  }
    59  
    60  func (c *Run) Synopsis() string {
    61  	return "Runs the e2e test suite"
    62  }
    63  
    64  func (c *Run) Run(args []string) int {
    65  	var envPath string
    66  	var nomadBinary string
    67  	var tfPath string
    68  	var slow bool
    69  	var run string
    70  	cmdFlags := c.FlagSet("run")
    71  	cmdFlags.Usage = func() { c.Ui.Output(c.Help()) }
    72  	cmdFlags.StringVar(&envPath, "env-path", DefaultEnvironmentsPath, "Path to e2e environment terraform configs")
    73  	cmdFlags.StringVar(&nomadBinary, "nomad-binary", "", "")
    74  	cmdFlags.StringVar(&tfPath, "tf-path", "", "")
    75  	cmdFlags.StringVar(&run, "run", "", "Regex to target specific test suites/cases")
    76  	cmdFlags.BoolVar(&slow, "slow", false, "Toggle slow running suites")
    77  
    78  	if err := cmdFlags.Parse(args); err != nil {
    79  		c.logger.Error("failed to parse flags", "error", err)
    80  		return 1
    81  	}
    82  	if c.verbose {
    83  		c.logger.SetLevel(hclog.Debug)
    84  	}
    85  
    86  	args = cmdFlags.Args()
    87  
    88  	if len(args) == 0 {
    89  		c.logger.Info("no environments specified, running test suite locally")
    90  		report, err := c.runTest(&runOpts{
    91  			run:     run,
    92  			slow:    slow,
    93  			verbose: c.verbose,
    94  		})
    95  		if err != nil {
    96  			c.logger.Error("failed to run test suite", "error", err)
    97  			return 1
    98  		}
    99  		if report.TotalFailedTests > 0 {
   100  			c.Ui.Error("***FAILED***")
   101  			c.Ui.Error(report.Summary())
   102  			return 1
   103  		}
   104  		c.Ui.Output("PASSED!")
   105  		if c.verbose {
   106  			c.Ui.Output(report.Summary())
   107  		}
   108  		return 0
   109  	}
   110  
   111  	environments := []*environment{}
   112  	for _, e := range args {
   113  		if len(strings.Split(e, "/")) != 2 {
   114  			c.logger.Error("argument should be formated as <provider>/<environment>", "args", e)
   115  			return 1
   116  		}
   117  		envs, err := envsFromGlob(envPath, e, tfPath, c.logger)
   118  		if err != nil {
   119  			c.logger.Error("failed to build environment", "environment", e, "error", err)
   120  			return 1
   121  		}
   122  		environments = append(environments, envs...)
   123  
   124  	}
   125  
   126  	// Use go-getter to fetch the nomad binary
   127  	nomadPath, err := fetchBinary(nomadBinary)
   128  	defer os.RemoveAll(nomadPath)
   129  	if err != nil {
   130  		c.logger.Error("failed to fetch nomad binary", "error", err)
   131  		return 1
   132  	}
   133  
   134  	envCount := len(environments)
   135  	c.logger.Debug("starting tests", "totalEnvironments", envCount)
   136  	failedEnvs := map[string]*TestReport{}
   137  	for i, env := range environments {
   138  		logger := c.logger.With("name", env.name, "provider", env.provider)
   139  		logger.Debug("provisioning environment")
   140  		results, err := env.provision(nomadPath)
   141  		if err != nil {
   142  			logger.Error("failed to provision environment", "error", err)
   143  			return 1
   144  		}
   145  
   146  		opts := &runOpts{
   147  			provider:   env.provider,
   148  			env:        env.name,
   149  			slow:       slow,
   150  			run:        run,
   151  			verbose:    c.verbose,
   152  			nomadAddr:  results.nomadAddr,
   153  			consulAddr: results.consulAddr,
   154  			vaultAddr:  results.vaultAddr,
   155  		}
   156  
   157  		var report *TestReport
   158  		if report, err = c.runTest(opts); err != nil {
   159  			logger.Error("failed to run tests against environment", "error", err)
   160  			return 1
   161  		}
   162  		if report.TotalFailedTests > 0 {
   163  			c.Ui.Error(fmt.Sprintf("[%d/%d] %s: ***FAILED***", i+1, envCount, env.canonicalName()))
   164  			c.Ui.Error(fmt.Sprintf("[%d/%d] %s: %s", i+1, envCount, env.canonicalName(), report.Summary()))
   165  			failedEnvs[env.canonicalName()] = report
   166  		}
   167  
   168  		c.Ui.Output(fmt.Sprintf("[%d/%d] %s: PASSED!", i+1, envCount, env.canonicalName()))
   169  		if c.verbose {
   170  			c.Ui.Output(fmt.Sprintf("[%d/%d] %s: %s", i+1, envCount, env.canonicalName(), report.Summary()))
   171  		}
   172  	}
   173  
   174  	if len(failedEnvs) > 0 {
   175  		c.Ui.Error(fmt.Sprintf("The following environments ***FAILED***"))
   176  		for name, report := range failedEnvs {
   177  			c.Ui.Error(fmt.Sprintf("  [%s]: %d out of %d suite failures",
   178  				name, report.TotalFailedSuites, report.TotalSuites))
   179  		}
   180  		return 1
   181  	}
   182  	c.Ui.Output("All Environments PASSED!")
   183  	return 0
   184  }
   185  
   186  func (c *Run) runTest(opts *runOpts) (*TestReport, error) {
   187  	goBin, err := exec.LookPath("go")
   188  	if err != nil {
   189  		return nil, err
   190  	}
   191  
   192  	cmd := exec.Command(goBin, opts.goArgs()...)
   193  	cmd.Env = opts.goEnv()
   194  	out, err := cmd.StdoutPipe()
   195  	defer out.Close()
   196  	if err != nil {
   197  		return nil, err
   198  	}
   199  
   200  	err = cmd.Start()
   201  	if err != nil {
   202  		return nil, err
   203  	}
   204  
   205  	dec := NewDecoder(out)
   206  	report, err := dec.Decode(c.logger.Named("run.gotest"))
   207  	if err != nil {
   208  		return nil, err
   209  	}
   210  
   211  	return report, nil
   212  
   213  }
   214  
   215  // runOpts contains fields used to build the arguments and environment variabled
   216  // nessicary to run go test and initialize the e2e framework
   217  type runOpts struct {
   218  	nomadAddr  string
   219  	consulAddr string
   220  	vaultAddr  string
   221  	provider   string
   222  	env        string
   223  	run        string
   224  	local      bool
   225  	slow       bool
   226  	verbose    bool
   227  }
   228  
   229  // goArgs returns the list of arguments passed to the go command to start the
   230  // e2e test framework
   231  func (opts *runOpts) goArgs() []string {
   232  	a := []string{
   233  		"test",
   234  		"-json",
   235  	}
   236  
   237  	if opts.run != "" {
   238  		a = append(a, "-run=TestE2E/"+opts.run)
   239  	}
   240  
   241  	a = append(a, []string{
   242  		"github.com/hashicorp/nomad/e2e",
   243  		"-env=" + opts.env,
   244  		"-env.provider=" + opts.provider,
   245  	}...)
   246  
   247  	if opts.slow {
   248  		a = append(a, "-slow")
   249  	}
   250  
   251  	if opts.local {
   252  		a = append(a, "-local")
   253  	}
   254  	return a
   255  }
   256  
   257  // goEnv returns the list of environment variabled passed to the go command to start
   258  // the e2e test framework
   259  func (opts *runOpts) goEnv() []string {
   260  	env := append(os.Environ(), "NOMAD_E2E=1")
   261  	if opts.nomadAddr != "" {
   262  		env = append(env, "NOMAD_ADDR="+opts.nomadAddr)
   263  	}
   264  	if opts.consulAddr != "" {
   265  		env = append(env, fmt.Sprintf("%s=%s", capi.HTTPAddrEnvName, opts.consulAddr))
   266  	}
   267  	if opts.vaultAddr != "" {
   268  		env = append(env, fmt.Sprintf("%s=%s", vapi.EnvVaultAddress, opts.consulAddr))
   269  	}
   270  
   271  	return env
   272  }