github.com/smintz/nomad@v0.8.3/e2e/migrations/migrations_test.go (about)

     1  package e2e
     2  
     3  import (
     4  	"bytes"
     5  	"flag"
     6  	"fmt"
     7  	"io/ioutil"
     8  	"os"
     9  	"os/exec"
    10  	"strings"
    11  	"testing"
    12  
    13  	"github.com/hashicorp/nomad/testutil"
    14  	"github.com/stretchr/testify/assert"
    15  )
    16  
    17  var integration = flag.Bool("integration", false, "run integration tests")
    18  
    19  const sleepJobOne = `job "sleep" {
    20  	type = "batch"
    21  	datacenters = ["dc1"]
    22  	constraint {
    23  		attribute = "${meta.secondary}"
    24  		value     = 1
    25  	}
    26  	group "group1" {
    27  		restart {
    28  			mode = "fail"
    29  		}
    30  		count = 1
    31  		ephemeral_disk {
    32  			migrate = true
    33  			sticky = true
    34  		}
    35  		task "sleep" {
    36  			template {
    37  				data = "hello world"
    38  				destination = "/local/hello-world"
    39  			}
    40  			driver = "exec"
    41  			config {
    42  				command = "/bin/sleep"
    43  				args = [ "infinity" ]
    44  			}
    45  		}
    46  	}
    47  }`
    48  
    49  const sleepJobTwo = `job "sleep" {
    50  	type = "batch"
    51  	datacenters = ["dc1"]
    52  	constraint {
    53  		attribute = "${meta.secondary}"
    54  		value     = 0
    55  	}
    56  	group "group1" {
    57  		restart {
    58  			mode     = "fail"
    59  		}
    60  		count = 1
    61  		ephemeral_disk {
    62  			migrate = true
    63  			sticky = true
    64  		}
    65  		task "sleep" {
    66  			driver = "exec"
    67  
    68  			config {
    69  				command = "test"
    70  				args = [ "-f", "/local/hello-world" ]
    71  			}
    72  		}
    73  	}
    74  }`
    75  
    76  // isSuccess waits until a given keyword is not present in the output of a
    77  // command. For example, isSuccess will poll for a given timeperiod as long as
    78  // the output of the command of "nomad node-status" includes the keyword
    79  // "initializing." The absence of this keyword means this command has returned
    80  // successfully.
    81  func isSuccess(execCmd *exec.Cmd, retries int, keyword string) (string, error) {
    82  	var successOut string
    83  	var err error
    84  
    85  	testutil.WaitForResultRetries(2000, func() (bool, error) {
    86  		var out bytes.Buffer
    87  		cmd := *execCmd
    88  		cmd.Stdout = &out
    89  		err := cmd.Run()
    90  
    91  		if err != nil {
    92  			return false, err
    93  		}
    94  
    95  		success := (out.String() != "" && !strings.Contains(out.String(), keyword))
    96  		if !success {
    97  			out.Reset()
    98  			return false, err
    99  		}
   100  
   101  		successOut = out.String()
   102  		return true, nil
   103  	}, func(cmd_err error) {
   104  		err = cmd_err
   105  	})
   106  
   107  	return successOut, err
   108  }
   109  
   110  // allNodesAreReady attempts to query the status of a cluster a specific number
   111  // of times
   112  func allNodesAreReady(retries int, flags string) (string, error) {
   113  	var cmd *exec.Cmd
   114  	if flags != "" {
   115  		cmd = exec.Command("nomad", "node-status", flags)
   116  	} else {
   117  		cmd = exec.Command("nomad", "node-status")
   118  	}
   119  
   120  	return isSuccess(cmd, retries, "initializing")
   121  }
   122  
   123  // jobIsReady attempts sto query the status of a specific job a fixed number of
   124  // times
   125  func jobIsReady(retries int, flags, jobName string) (string, error) {
   126  	var cmd *exec.Cmd
   127  	if flags != "" {
   128  		cmd = exec.Command("nomad", "job", "status", flags, jobName)
   129  	} else {
   130  		cmd = exec.Command("nomad", "job", "status", jobName)
   131  	}
   132  	return isSuccess(cmd, retries, "pending")
   133  }
   134  
   135  // startCluster will create a running cluster, given a list of agent config
   136  // files. In order to have a complete cluster, at least one server and one
   137  // client config file should be included.
   138  func startCluster(clusterConfig []string) (func(), error) {
   139  	cmds := make([]*exec.Cmd, 0)
   140  
   141  	for _, agentConfig := range clusterConfig {
   142  		cmd := exec.Command("nomad", "agent", "-config", agentConfig)
   143  		err := cmd.Start()
   144  
   145  		if err != nil {
   146  			return func() {}, err
   147  		}
   148  
   149  		cmds = append(cmds, cmd)
   150  	}
   151  
   152  	f := func() {
   153  		for _, cmd := range cmds {
   154  			cmd.Process.Kill()
   155  		}
   156  	}
   157  
   158  	return f, nil
   159  }
   160  
   161  func bootstrapACL() (string, error) {
   162  	var bootstrapOut bytes.Buffer
   163  
   164  	bootstrapCmd := exec.Command("nomad", "acl", "bootstrap")
   165  	bootstrapCmd.Stdout = &bootstrapOut
   166  
   167  	if err := bootstrapCmd.Run(); err != nil {
   168  		return "", err
   169  	}
   170  
   171  	parts := strings.Split(bootstrapOut.String(), "\n")
   172  	if len(parts) < 2 {
   173  		return "", fmt.Errorf("unexpected bootstrap output")
   174  	}
   175  
   176  	secretIDLine := strings.Split(parts[1], " ")
   177  	if secretIDLine[0] != "Secret" {
   178  		return "", fmt.Errorf("unable to find secret id in bootstrap output")
   179  	}
   180  	return secretIDLine[len(secretIDLine)-1], nil
   181  }
   182  
   183  func startACLServer(serverConfig string) (func(), string, error) {
   184  	cmd := exec.Command("nomad", "agent", "-config", serverConfig)
   185  	if err := cmd.Start(); err != nil {
   186  		return func() {}, "", err
   187  	}
   188  
   189  	f := func() {
   190  		cmd.Process.Kill()
   191  	}
   192  
   193  	var secretID string
   194  	var err error
   195  	testutil.WaitForResultRetries(2000, func() (bool, error) {
   196  
   197  		secretIDOutput, err := bootstrapACL()
   198  		if err != nil {
   199  			return false, err
   200  		}
   201  
   202  		secretID = secretIDOutput
   203  		return true, nil
   204  	}, func(cmd_err error) {
   205  		err = cmd_err
   206  	})
   207  
   208  	if err != nil {
   209  		return func() {}, "", err
   210  	}
   211  
   212  	return f, secretID, nil
   213  }
   214  
   215  func TestJobMigrations(t *testing.T) {
   216  	flag.Parse()
   217  	if !*integration {
   218  		t.Skip("skipping test in non-integration mode.")
   219  	}
   220  
   221  	t.Parallel()
   222  	assert := assert.New(t)
   223  
   224  	clusterConfig := []string{"server.hcl", "client1.hcl", "client2.hcl"}
   225  	stopCluster, err := startCluster(clusterConfig)
   226  	assert.Nil(err)
   227  	defer stopCluster()
   228  
   229  	_, err = allNodesAreReady(10, "")
   230  	assert.Nil(err)
   231  
   232  	fh, err := ioutil.TempFile("", "nomad-sleep-1")
   233  	assert.Nil(err)
   234  
   235  	defer os.Remove(fh.Name())
   236  	_, err = fh.WriteString(sleepJobOne)
   237  
   238  	assert.Nil(err)
   239  
   240  	jobCmd := exec.Command("nomad", "run", fh.Name())
   241  	err = jobCmd.Run()
   242  	assert.Nil(err)
   243  
   244  	firstJobOutput, err := jobIsReady(20, "", "sleep")
   245  	assert.Nil(err)
   246  	assert.NotContains(firstJobOutput, "failed")
   247  	assert.NotContains(firstJobOutput, "pending")
   248  
   249  	fh2, err := ioutil.TempFile("", "nomad-sleep-2")
   250  	assert.Nil(err)
   251  
   252  	defer os.Remove(fh2.Name())
   253  	_, err = fh2.WriteString(sleepJobTwo)
   254  	assert.Nil(err)
   255  
   256  	secondJobCmd := exec.Command("nomad", "run", fh2.Name())
   257  	err = secondJobCmd.Run()
   258  	assert.Nil(err)
   259  
   260  	jobOutput, err := jobIsReady(20, "", "sleep")
   261  	assert.Nil(err)
   262  	assert.NotContains(jobOutput, "failed")
   263  	assert.Contains(jobOutput, "complete")
   264  }
   265  
   266  func TestMigrations_WithACLs(t *testing.T) {
   267  	flag.Parse()
   268  	if !*integration {
   269  		t.Skip("skipping test in non-integration mode.")
   270  	}
   271  
   272  	t.Parallel()
   273  	assert := assert.New(t)
   274  
   275  	stopServer, secretID, err := startACLServer("server_acl.hcl")
   276  	assert.Nil(err)
   277  	defer stopServer()
   278  
   279  	clusterConfig := []string{"client1.hcl", "client2.hcl"}
   280  	stopCluster, err := startCluster(clusterConfig)
   281  	assert.Nil(err)
   282  	defer stopCluster()
   283  
   284  	_, err = allNodesAreReady(10, "-token="+secretID)
   285  	assert.Nil(err)
   286  
   287  	fh, err := ioutil.TempFile("", "nomad-sleep-1")
   288  	assert.Nil(err)
   289  
   290  	defer os.Remove(fh.Name())
   291  	_, err = fh.WriteString(sleepJobOne)
   292  
   293  	assert.Nil(err)
   294  
   295  	jobCmd := exec.Command("nomad", "run", "-token="+secretID, fh.Name())
   296  	err = jobCmd.Run()
   297  	assert.Nil(err)
   298  
   299  	_, err = jobIsReady(20, "-token="+secretID, "sleep")
   300  	assert.Nil(err)
   301  
   302  	fh2, err := ioutil.TempFile("", "nomad-sleep-2")
   303  	assert.Nil(err)
   304  
   305  	defer os.Remove(fh2.Name())
   306  	_, err = fh2.WriteString(sleepJobTwo)
   307  
   308  	assert.Nil(err)
   309  
   310  	secondJobCmd := exec.Command("nomad", "run", "-token="+secretID, fh2.Name())
   311  	err = secondJobCmd.Run()
   312  	assert.Nil(err)
   313  
   314  	jobOutput, err := jobIsReady(20, "-token="+secretID, "sleep")
   315  	assert.Nil(err)
   316  
   317  	assert.NotContains(jobOutput, "failed")
   318  	assert.NotContains(jobOutput, "pending")
   319  	assert.Contains(jobOutput, "complete")
   320  }