2017-10-20 18:10:30 +00:00
|
|
|
package e2e
|
|
|
|
|
|
|
|
import (
|
|
|
|
"bytes"
|
2017-10-25 19:56:42 +00:00
|
|
|
"flag"
|
2017-10-24 17:22:48 +00:00
|
|
|
"fmt"
|
2017-10-20 18:10:30 +00:00
|
|
|
"io/ioutil"
|
|
|
|
"os"
|
|
|
|
"os/exec"
|
2017-10-24 13:56:46 +00:00
|
|
|
"strings"
|
2017-10-20 18:10:30 +00:00
|
|
|
"testing"
|
|
|
|
|
2017-10-25 00:56:19 +00:00
|
|
|
"github.com/hashicorp/nomad/testutil"
|
2017-10-25 01:38:15 +00:00
|
|
|
"github.com/stretchr/testify/assert"
|
2017-10-20 18:10:30 +00:00
|
|
|
)
|
|
|
|
|
2017-10-25 19:56:42 +00:00
|
|
|
var integration = flag.Bool("integration", false, "run integration tests")
|
|
|
|
|
2017-10-25 01:38:15 +00:00
|
|
|
const sleepJobOne = `job "sleep" {
|
|
|
|
type = "batch"
|
|
|
|
datacenters = ["dc1"]
|
|
|
|
constraint {
|
|
|
|
attribute = "${meta.secondary}"
|
|
|
|
value = 1
|
|
|
|
}
|
|
|
|
group "group1" {
|
|
|
|
restart {
|
|
|
|
mode = "fail"
|
|
|
|
}
|
|
|
|
count = 1
|
|
|
|
ephemeral_disk {
|
|
|
|
migrate = true
|
|
|
|
sticky = true
|
|
|
|
}
|
|
|
|
task "sleep" {
|
|
|
|
template {
|
|
|
|
data = "hello world"
|
|
|
|
destination = "/local/hello-world"
|
|
|
|
}
|
|
|
|
driver = "exec"
|
|
|
|
config {
|
|
|
|
command = "/bin/sleep"
|
|
|
|
args = [ "infinity" ]
|
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
|
|
|
}`
|
|
|
|
|
|
|
|
const sleepJobTwo = `job "sleep" {
|
|
|
|
type = "batch"
|
|
|
|
datacenters = ["dc1"]
|
|
|
|
constraint {
|
|
|
|
attribute = "${meta.secondary}"
|
|
|
|
value = 0
|
|
|
|
}
|
|
|
|
group "group1" {
|
|
|
|
restart {
|
|
|
|
mode = "fail"
|
|
|
|
}
|
|
|
|
count = 1
|
|
|
|
ephemeral_disk {
|
|
|
|
migrate = true
|
|
|
|
sticky = true
|
|
|
|
}
|
|
|
|
task "sleep" {
|
|
|
|
driver = "exec"
|
|
|
|
|
|
|
|
config {
|
|
|
|
command = "test"
|
|
|
|
args = [ "-f", "/local/hello-world" ]
|
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
|
|
|
}`
|
|
|
|
|
2017-10-25 21:50:47 +00:00
|
|
|
// isSuccess waits until a given keyword is not present in the output of a
|
|
|
|
// command. For example, isSuccess will poll for a given timeperiod as long as
|
|
|
|
// the output of the command of "nomad node-status" includes the keyword
|
|
|
|
// "initializing." The absence of this keyword means this command has returned
|
|
|
|
// successfully.
|
2017-10-25 00:56:19 +00:00
|
|
|
func isSuccess(execCmd *exec.Cmd, retries int, keyword string) (string, error) {
|
|
|
|
var successOut string
|
|
|
|
var err error
|
2017-10-20 22:15:24 +00:00
|
|
|
|
2017-10-25 00:56:19 +00:00
|
|
|
testutil.WaitForResultRetries(2000, func() (bool, error) {
|
|
|
|
var out bytes.Buffer
|
2017-10-20 22:15:24 +00:00
|
|
|
cmd := *execCmd
|
|
|
|
cmd.Stdout = &out
|
|
|
|
err := cmd.Run()
|
2017-10-25 00:56:19 +00:00
|
|
|
|
2017-10-20 22:15:24 +00:00
|
|
|
if err != nil {
|
2017-10-25 00:56:19 +00:00
|
|
|
return false, err
|
2017-10-20 22:15:24 +00:00
|
|
|
}
|
|
|
|
|
2017-10-25 00:56:19 +00:00
|
|
|
success := (out.String() != "" && !strings.Contains(out.String(), keyword))
|
|
|
|
if !success {
|
|
|
|
out.Reset()
|
|
|
|
return false, err
|
2017-10-20 22:15:24 +00:00
|
|
|
}
|
|
|
|
|
2017-10-25 00:56:19 +00:00
|
|
|
successOut = out.String()
|
|
|
|
return true, nil
|
|
|
|
}, func(cmd_err error) {
|
|
|
|
err = cmd_err
|
|
|
|
})
|
2017-10-20 22:15:24 +00:00
|
|
|
|
2017-10-25 00:56:19 +00:00
|
|
|
return successOut, err
|
2017-10-20 22:15:24 +00:00
|
|
|
}
|
|
|
|
|
2017-10-25 01:38:15 +00:00
|
|
|
// allNodesAreReady attempts to query the status of a cluster a specific number
|
|
|
|
// of times
|
2017-10-24 17:22:48 +00:00
|
|
|
func allNodesAreReady(retries int, flags string) (string, error) {
|
|
|
|
var cmd *exec.Cmd
|
|
|
|
if flags != "" {
|
|
|
|
cmd = exec.Command("nomad", "node-status", flags)
|
|
|
|
} else {
|
|
|
|
cmd = exec.Command("nomad", "node-status")
|
|
|
|
}
|
|
|
|
|
2017-10-20 22:15:24 +00:00
|
|
|
return isSuccess(cmd, retries, "initializing")
|
|
|
|
}
|
|
|
|
|
2017-10-25 01:38:15 +00:00
|
|
|
// jobIsReady attempts sto query the status of a specific job a fixed number of
|
|
|
|
// times
|
2017-10-24 17:22:48 +00:00
|
|
|
func jobIsReady(retries int, flags, jobName string) (string, error) {
|
|
|
|
var cmd *exec.Cmd
|
|
|
|
if flags != "" {
|
|
|
|
cmd = exec.Command("nomad", "job", "status", flags, jobName)
|
|
|
|
} else {
|
|
|
|
cmd = exec.Command("nomad", "job", "status", jobName)
|
|
|
|
}
|
2017-10-20 22:15:24 +00:00
|
|
|
return isSuccess(cmd, retries, "pending")
|
|
|
|
}
|
|
|
|
|
2017-10-25 01:38:15 +00:00
|
|
|
// startCluster will create a running cluster, given a list of agent config
|
|
|
|
// files. In order to have a complete cluster, at least one server and one
|
|
|
|
// client config file should be included.
|
2017-10-20 19:09:58 +00:00
|
|
|
func startCluster(clusterConfig []string) (func(), error) {
|
|
|
|
cmds := make([]*exec.Cmd, 0)
|
2017-10-20 18:10:30 +00:00
|
|
|
|
2017-10-20 19:09:58 +00:00
|
|
|
for _, agentConfig := range clusterConfig {
|
|
|
|
cmd := exec.Command("nomad", "agent", "-config", agentConfig)
|
|
|
|
err := cmd.Start()
|
2017-10-20 18:10:30 +00:00
|
|
|
|
2017-10-20 19:09:58 +00:00
|
|
|
if err != nil {
|
|
|
|
return func() {}, err
|
|
|
|
}
|
2017-10-20 18:10:30 +00:00
|
|
|
|
2017-10-20 19:09:58 +00:00
|
|
|
cmds = append(cmds, cmd)
|
2017-10-20 18:10:30 +00:00
|
|
|
}
|
|
|
|
|
|
|
|
f := func() {
|
2017-10-20 19:09:58 +00:00
|
|
|
for _, cmd := range cmds {
|
|
|
|
cmd.Process.Kill()
|
|
|
|
}
|
2017-10-20 18:10:30 +00:00
|
|
|
}
|
|
|
|
|
|
|
|
return f, nil
|
|
|
|
}
|
|
|
|
|
2017-10-25 15:54:59 +00:00
|
|
|
func bootstrapACL() (string, error) {
|
|
|
|
var bootstrapOut bytes.Buffer
|
|
|
|
|
|
|
|
bootstrapCmd := exec.Command("nomad", "acl", "bootstrap")
|
|
|
|
bootstrapCmd.Stdout = &bootstrapOut
|
|
|
|
|
|
|
|
if err := bootstrapCmd.Run(); err != nil {
|
|
|
|
return "", err
|
|
|
|
}
|
|
|
|
|
|
|
|
parts := strings.Split(bootstrapOut.String(), "\n")
|
|
|
|
if len(parts) < 2 {
|
|
|
|
return "", fmt.Errorf("unexpected bootstrap output")
|
|
|
|
}
|
|
|
|
|
|
|
|
secretIDLine := strings.Split(parts[1], " ")
|
|
|
|
if secretIDLine[0] != "Secret" {
|
|
|
|
return "", fmt.Errorf("unable to find secret id in bootstrap output")
|
|
|
|
}
|
|
|
|
return secretIDLine[len(secretIDLine)-1], nil
|
|
|
|
}
|
|
|
|
|
2017-10-24 17:22:48 +00:00
|
|
|
func startACLServer(serverConfig string) (func(), string, error) {
|
|
|
|
cmd := exec.Command("nomad", "agent", "-config", serverConfig)
|
|
|
|
if err := cmd.Start(); err != nil {
|
|
|
|
return func() {}, "", err
|
|
|
|
}
|
|
|
|
|
|
|
|
f := func() {
|
|
|
|
cmd.Process.Kill()
|
|
|
|
}
|
|
|
|
|
2017-10-25 15:54:59 +00:00
|
|
|
var secretID string
|
|
|
|
var err error
|
|
|
|
testutil.WaitForResultRetries(2000, func() (bool, error) {
|
2017-10-24 17:22:48 +00:00
|
|
|
|
2017-10-25 15:54:59 +00:00
|
|
|
secretIDOutput, err := bootstrapACL()
|
|
|
|
if err != nil {
|
|
|
|
return false, err
|
|
|
|
}
|
2017-10-24 17:22:48 +00:00
|
|
|
|
2017-10-25 15:54:59 +00:00
|
|
|
secretID = secretIDOutput
|
|
|
|
return true, nil
|
|
|
|
}, func(cmd_err error) {
|
|
|
|
err = cmd_err
|
|
|
|
})
|
2017-10-24 17:22:48 +00:00
|
|
|
|
2017-10-25 15:54:59 +00:00
|
|
|
if err != nil {
|
|
|
|
return func() {}, "", err
|
2017-10-24 17:22:48 +00:00
|
|
|
}
|
|
|
|
|
|
|
|
return f, secretID, nil
|
|
|
|
}
|
|
|
|
|
2017-10-20 18:10:30 +00:00
|
|
|
func TestJobMigrations(t *testing.T) {
|
2017-10-25 19:56:42 +00:00
|
|
|
flag.Parse()
|
|
|
|
if !*integration {
|
|
|
|
t.Skip("skipping test in non-integration mode.")
|
|
|
|
}
|
|
|
|
|
2017-10-20 18:10:30 +00:00
|
|
|
t.Parallel()
|
|
|
|
assert := assert.New(t)
|
|
|
|
|
2017-10-20 19:09:58 +00:00
|
|
|
clusterConfig := []string{"server.hcl", "client1.hcl", "client2.hcl"}
|
|
|
|
stopCluster, err := startCluster(clusterConfig)
|
2017-10-20 18:10:30 +00:00
|
|
|
assert.Nil(err)
|
|
|
|
defer stopCluster()
|
|
|
|
|
2017-10-24 17:22:48 +00:00
|
|
|
_, err = allNodesAreReady(10, "")
|
2017-10-25 00:56:19 +00:00
|
|
|
assert.Nil(err)
|
2017-10-20 22:15:24 +00:00
|
|
|
|
2017-10-20 18:10:30 +00:00
|
|
|
fh, err := ioutil.TempFile("", "nomad-sleep-1")
|
|
|
|
assert.Nil(err)
|
|
|
|
|
|
|
|
defer os.Remove(fh.Name())
|
2017-10-25 01:38:15 +00:00
|
|
|
_, err = fh.WriteString(sleepJobOne)
|
2017-10-20 18:10:30 +00:00
|
|
|
|
2017-10-20 22:15:24 +00:00
|
|
|
assert.Nil(err)
|
|
|
|
|
2017-10-20 18:10:30 +00:00
|
|
|
jobCmd := exec.Command("nomad", "run", fh.Name())
|
|
|
|
err = jobCmd.Run()
|
|
|
|
assert.Nil(err)
|
|
|
|
|
2017-10-24 17:22:48 +00:00
|
|
|
firstJobOutput, err := jobIsReady(20, "", "sleep")
|
2017-10-25 00:56:19 +00:00
|
|
|
assert.Nil(err)
|
2017-10-26 17:14:58 +00:00
|
|
|
assert.NotContains(firstJobOutput, "failed")
|
|
|
|
assert.NotContains(firstJobOutput, "pending")
|
2017-10-20 18:10:30 +00:00
|
|
|
|
|
|
|
fh2, err := ioutil.TempFile("", "nomad-sleep-2")
|
|
|
|
assert.Nil(err)
|
|
|
|
|
|
|
|
defer os.Remove(fh2.Name())
|
2017-10-25 01:38:15 +00:00
|
|
|
_, err = fh2.WriteString(sleepJobTwo)
|
2017-10-20 18:10:30 +00:00
|
|
|
assert.Nil(err)
|
|
|
|
|
2017-10-20 22:15:24 +00:00
|
|
|
secondJobCmd := exec.Command("nomad", "run", fh2.Name())
|
|
|
|
err = secondJobCmd.Run()
|
2017-10-20 18:10:30 +00:00
|
|
|
assert.Nil(err)
|
|
|
|
|
2017-10-24 17:22:48 +00:00
|
|
|
jobOutput, err := jobIsReady(20, "", "sleep")
|
|
|
|
assert.Nil(err)
|
|
|
|
assert.NotContains(jobOutput, "failed")
|
|
|
|
assert.Contains(jobOutput, "complete")
|
|
|
|
}
|
|
|
|
|
|
|
|
func TestMigrations_WithACLs(t *testing.T) {
|
2017-10-25 20:12:30 +00:00
|
|
|
flag.Parse()
|
|
|
|
if !*integration {
|
|
|
|
t.Skip("skipping test in non-integration mode.")
|
|
|
|
}
|
|
|
|
|
2017-10-24 17:22:48 +00:00
|
|
|
t.Parallel()
|
|
|
|
assert := assert.New(t)
|
|
|
|
|
|
|
|
stopServer, secretID, err := startACLServer("server_acl.hcl")
|
|
|
|
assert.Nil(err)
|
|
|
|
defer stopServer()
|
|
|
|
|
2017-10-27 17:19:38 +00:00
|
|
|
clusterConfig := []string{"client1.hcl", "client2.hcl"}
|
2017-10-24 17:22:48 +00:00
|
|
|
stopCluster, err := startCluster(clusterConfig)
|
|
|
|
assert.Nil(err)
|
|
|
|
defer stopCluster()
|
|
|
|
|
|
|
|
_, err = allNodesAreReady(10, "-token="+secretID)
|
|
|
|
assert.Nil(err)
|
|
|
|
|
|
|
|
fh, err := ioutil.TempFile("", "nomad-sleep-1")
|
|
|
|
assert.Nil(err)
|
|
|
|
|
|
|
|
defer os.Remove(fh.Name())
|
|
|
|
_, err = fh.WriteString(sleepJobOne)
|
|
|
|
|
|
|
|
assert.Nil(err)
|
|
|
|
|
|
|
|
jobCmd := exec.Command("nomad", "run", "-token="+secretID, fh.Name())
|
|
|
|
err = jobCmd.Run()
|
|
|
|
assert.Nil(err)
|
|
|
|
|
|
|
|
_, err = jobIsReady(20, "-token="+secretID, "sleep")
|
|
|
|
assert.Nil(err)
|
|
|
|
|
|
|
|
fh2, err := ioutil.TempFile("", "nomad-sleep-2")
|
|
|
|
assert.Nil(err)
|
|
|
|
|
|
|
|
defer os.Remove(fh2.Name())
|
|
|
|
_, err = fh2.WriteString(sleepJobTwo)
|
|
|
|
|
|
|
|
assert.Nil(err)
|
|
|
|
|
|
|
|
secondJobCmd := exec.Command("nomad", "run", "-token="+secretID, fh2.Name())
|
|
|
|
err = secondJobCmd.Run()
|
|
|
|
assert.Nil(err)
|
|
|
|
|
|
|
|
jobOutput, err := jobIsReady(20, "-token="+secretID, "sleep")
|
2017-10-25 00:56:19 +00:00
|
|
|
assert.Nil(err)
|
2017-10-24 17:22:48 +00:00
|
|
|
|
2017-10-20 18:10:30 +00:00
|
|
|
assert.NotContains(jobOutput, "failed")
|
2017-10-25 21:50:47 +00:00
|
|
|
assert.NotContains(jobOutput, "pending")
|
2017-10-20 18:10:30 +00:00
|
|
|
assert.Contains(jobOutput, "complete")
|
|
|
|
}
|