github.com/smithx10/nomad@v0.9.1-rc1/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 }