github.com/kjdelisle/consul@v1.4.5/command/exec/exec_test.go (about) 1 package exec 2 3 import ( 4 "strings" 5 "testing" 6 "time" 7 8 "github.com/hashicorp/consul/testrpc" 9 10 "github.com/hashicorp/consul/agent" 11 consulapi "github.com/hashicorp/consul/api" 12 "github.com/hashicorp/consul/testutil/retry" 13 "github.com/mitchellh/cli" 14 ) 15 16 func TestExecCommand_noTabs(t *testing.T) { 17 t.Parallel() 18 if strings.ContainsRune(New(nil, nil).Help(), '\t') { 19 t.Fatal("help has tabs") 20 } 21 } 22 23 func TestExecCommand(t *testing.T) { 24 t.Parallel() 25 a := agent.NewTestAgent(t, t.Name(), ` 26 disable_remote_exec = false 27 `) 28 defer a.Shutdown() 29 30 testrpc.WaitForTestAgent(t, a.RPC, "dc1") 31 32 ui := cli.NewMockUi() 33 c := New(ui, nil) 34 args := []string{"-http-addr=" + a.HTTPAddr(), "-wait=1s", "uptime"} 35 36 code := c.Run(args) 37 if code != 0 { 38 t.Fatalf("bad: %d. Error:%#v (std)Output:%#v", code, ui.ErrorWriter.String(), ui.OutputWriter.String()) 39 } 40 41 if !strings.Contains(ui.OutputWriter.String(), "load") { 42 t.Fatalf("bad: %#v", ui.OutputWriter.String()) 43 } 44 } 45 46 func TestExecCommand_NoShell(t *testing.T) { 47 t.Parallel() 48 a := agent.NewTestAgent(t, t.Name(), ` 49 disable_remote_exec = false 50 `) 51 defer a.Shutdown() 52 53 testrpc.WaitForTestAgent(t, a.RPC, "dc1") 54 55 ui := cli.NewMockUi() 56 c := New(ui, nil) 57 args := []string{"-http-addr=" + a.HTTPAddr(), "-shell=false", "-wait=1s", "uptime"} 58 59 code := c.Run(args) 60 if code != 0 { 61 t.Fatalf("bad: %d. Error:%#v (std)Output:%#v", code, ui.ErrorWriter.String(), ui.OutputWriter.String()) 62 } 63 64 if !strings.Contains(ui.OutputWriter.String(), "load") { 65 t.Fatalf("bad: %#v", ui.OutputWriter.String()) 66 } 67 } 68 69 func TestExecCommand_CrossDC(t *testing.T) { 70 t.Parallel() 71 a1 := agent.NewTestAgent(t, t.Name(), ` 72 disable_remote_exec = false 73 `) 74 defer a1.Shutdown() 75 76 testrpc.WaitForTestAgent(t, a1.RPC, "dc1") 77 78 a2 := agent.NewTestAgent(t, t.Name(), ` 79 datacenter = "dc2" 80 disable_remote_exec = false 81 `) 82 defer a2.Shutdown() 83 84 testrpc.WaitForTestAgent(t, a2.RPC, "dc2") 85 86 // Join over the WAN 87 _, err := a2.JoinWAN([]string{a1.Config.SerfBindAddrWAN.String()}) 88 if err != nil { 89 t.Fatalf("err: %v", err) 90 } 91 92 if got, want := len(a1.WANMembers()), 2; got != want { 93 t.Fatalf("got %d WAN members on a1 want %d", got, want) 94 } 95 if got, want := len(a2.WANMembers()), 2; got != want { 96 t.Fatalf("got %d WAN members on a2 want %d", got, want) 97 } 98 99 ui := cli.NewMockUi() 100 c := New(ui, nil) 101 args := []string{"-http-addr=" + a1.HTTPAddr(), "-wait=500ms", "-datacenter=dc2", "uptime"} 102 103 code := c.Run(args) 104 if code != 0 { 105 t.Fatalf("bad: %d. %#v", code, ui.ErrorWriter.String()) 106 } 107 108 if !strings.Contains(ui.OutputWriter.String(), "load") { 109 t.Fatalf("bad: %#v", ui.OutputWriter.String()) 110 } 111 } 112 113 func TestExecCommand_Validate(t *testing.T) { 114 t.Parallel() 115 conf := &rExecConf{} 116 err := conf.validate() 117 if err != nil { 118 t.Fatalf("err: %v", err) 119 } 120 121 conf.node = "(" 122 err = conf.validate() 123 if err == nil { 124 t.Fatalf("err: %v", err) 125 } 126 127 conf.node = "" 128 conf.service = "(" 129 err = conf.validate() 130 if err == nil { 131 t.Fatalf("err: %v", err) 132 } 133 134 conf.service = "()" 135 conf.tag = "(" 136 err = conf.validate() 137 if err == nil { 138 t.Fatalf("err: %v", err) 139 } 140 141 conf.service = "" 142 conf.tag = "foo" 143 err = conf.validate() 144 if err == nil { 145 t.Fatalf("err: %v", err) 146 } 147 } 148 149 func TestExecCommand_Sessions(t *testing.T) { 150 t.Parallel() 151 a := agent.NewTestAgent(t, t.Name(), ` 152 disable_remote_exec = false 153 `) 154 defer a.Shutdown() 155 156 testrpc.WaitForTestAgent(t, a.RPC, "dc1") 157 158 ui := cli.NewMockUi() 159 c := New(ui, nil) 160 c.apiclient = a.Client() 161 id, err := c.createSession() 162 if err != nil { 163 t.Fatalf("err: %v", err) 164 } 165 166 se, _, err := a.Client().Session().Info(id, nil) 167 if err != nil { 168 t.Fatalf("err: %v", err) 169 } 170 if se == nil || se.Name != "Remote Exec" { 171 t.Fatalf("bad: %v", se) 172 } 173 174 c.sessionID = id 175 err = c.destroySession() 176 if err != nil { 177 t.Fatalf("err: %v", err) 178 } 179 180 se, _, err = a.Client().Session().Info(id, nil) 181 if err != nil { 182 t.Fatalf("err: %v", err) 183 } 184 if se != nil { 185 t.Fatalf("bad: %v", se) 186 } 187 } 188 189 func TestExecCommand_Sessions_Foreign(t *testing.T) { 190 t.Parallel() 191 a := agent.NewTestAgent(t, t.Name(), ` 192 disable_remote_exec = false 193 `) 194 defer a.Shutdown() 195 196 ui := cli.NewMockUi() 197 c := New(ui, nil) 198 c.apiclient = a.Client() 199 200 c.conf.foreignDC = true 201 c.conf.localDC = "dc1" 202 c.conf.localNode = "foo" 203 204 var id string 205 retry.Run(t, func(r *retry.R) { 206 var err error 207 id, err = c.createSession() 208 if err != nil { 209 r.Fatal(err) 210 } 211 if id == "" { 212 r.Fatal("no id") 213 } 214 }) 215 216 se, _, err := a.Client().Session().Info(id, nil) 217 if err != nil { 218 t.Fatalf("err: %v", err) 219 } 220 if se == nil || se.Name != "Remote Exec via foo@dc1" { 221 t.Fatalf("bad: %v", se) 222 } 223 224 c.sessionID = id 225 err = c.destroySession() 226 if err != nil { 227 t.Fatalf("err: %v", err) 228 } 229 230 se, _, err = a.Client().Session().Info(id, nil) 231 if err != nil { 232 t.Fatalf("err: %v", err) 233 } 234 if se != nil { 235 t.Fatalf("bad: %v", se) 236 } 237 } 238 239 func TestExecCommand_UploadDestroy(t *testing.T) { 240 t.Parallel() 241 a := agent.NewTestAgent(t, t.Name(), ` 242 disable_remote_exec = false 243 `) 244 defer a.Shutdown() 245 246 testrpc.WaitForTestAgent(t, a.RPC, "dc1") 247 248 ui := cli.NewMockUi() 249 250 c := New(ui, nil) 251 c.apiclient = a.Client() 252 id, err := c.createSession() 253 if err != nil { 254 t.Fatalf("err: %v", err) 255 } 256 c.sessionID = id 257 258 c.conf.prefix = "_rexec" 259 c.conf.cmd = "uptime" 260 c.conf.wait = time.Second 261 262 buf, err := c.makeRExecSpec() 263 if err != nil { 264 t.Fatalf("err: %v", err) 265 } 266 267 err = c.uploadPayload(buf) 268 if err != nil { 269 t.Fatalf("err: %v", err) 270 } 271 272 pair, _, err := a.Client().KV().Get("_rexec/"+id+"/job", nil) 273 if err != nil { 274 t.Fatalf("err: %v", err) 275 } 276 277 if pair == nil || len(pair.Value) == 0 { 278 t.Fatalf("missing job spec") 279 } 280 281 err = c.destroyData() 282 if err != nil { 283 t.Fatalf("err: %v", err) 284 } 285 286 pair, _, err = a.Client().KV().Get("_rexec/"+id+"/job", nil) 287 if err != nil { 288 t.Fatalf("err: %v", err) 289 } 290 291 if pair != nil { 292 t.Fatalf("should be destroyed") 293 } 294 } 295 296 func TestExecCommand_StreamResults(t *testing.T) { 297 t.Parallel() 298 a := agent.NewTestAgent(t, t.Name(), ` 299 disable_remote_exec = false 300 `) 301 defer a.Shutdown() 302 303 testrpc.WaitForTestAgent(t, a.RPC, "dc1") 304 305 ui := cli.NewMockUi() 306 c := New(ui, nil) 307 c.apiclient = a.Client() 308 c.conf.prefix = "_rexec" 309 310 id, err := c.createSession() 311 if err != nil { 312 t.Fatalf("err: %v", err) 313 } 314 c.sessionID = id 315 316 ackCh := make(chan rExecAck, 128) 317 heartCh := make(chan rExecHeart, 128) 318 outputCh := make(chan rExecOutput, 128) 319 exitCh := make(chan rExecExit, 128) 320 doneCh := make(chan struct{}) 321 errCh := make(chan struct{}, 1) 322 defer close(doneCh) 323 go c.streamResults(doneCh, ackCh, heartCh, outputCh, exitCh, errCh) 324 325 prefix := "_rexec/" + id + "/" 326 ok, _, err := a.Client().KV().Acquire(&consulapi.KVPair{ 327 Key: prefix + "foo/ack", 328 Session: id, 329 }, nil) 330 if err != nil { 331 t.Fatalf("err: %v", err) 332 } 333 if !ok { 334 t.Fatalf("should be ok bro") 335 } 336 337 retry.Run(t, func(r *retry.R) { 338 select { 339 case a := <-ackCh: 340 if a.Node != "foo" { 341 r.Fatalf("bad: %#v", a) 342 } 343 case <-time.After(50 * time.Millisecond): 344 r.Fatalf("timeout") 345 } 346 }) 347 348 ok, _, err = a.Client().KV().Acquire(&consulapi.KVPair{ 349 Key: prefix + "foo/exit", 350 Value: []byte("127"), 351 Session: id, 352 }, nil) 353 if err != nil { 354 t.Fatalf("err: %v", err) 355 } 356 if !ok { 357 t.Fatalf("should be ok bro") 358 } 359 360 retry.Run(t, func(r *retry.R) { 361 select { 362 case e := <-exitCh: 363 if e.Node != "foo" || e.Code != 127 { 364 r.Fatalf("bad: %#v", e) 365 } 366 case <-time.After(50 * time.Millisecond): 367 r.Fatalf("timeout") 368 } 369 }) 370 371 // Random key, should ignore 372 ok, _, err = a.Client().KV().Acquire(&consulapi.KVPair{ 373 Key: prefix + "foo/random", 374 Session: id, 375 }, nil) 376 if err != nil { 377 t.Fatalf("err: %v", err) 378 } 379 if !ok { 380 t.Fatalf("should be ok bro") 381 } 382 383 // Output heartbeat 384 ok, _, err = a.Client().KV().Acquire(&consulapi.KVPair{ 385 Key: prefix + "foo/out/00000", 386 Session: id, 387 }, nil) 388 if err != nil { 389 t.Fatalf("err: %v", err) 390 } 391 if !ok { 392 t.Fatalf("should be ok bro") 393 } 394 395 retry.Run(t, func(r *retry.R) { 396 select { 397 case h := <-heartCh: 398 if h.Node != "foo" { 399 r.Fatalf("bad: %#v", h) 400 } 401 case <-time.After(50 * time.Millisecond): 402 r.Fatalf("timeout") 403 } 404 }) 405 406 // Output value 407 ok, _, err = a.Client().KV().Acquire(&consulapi.KVPair{ 408 Key: prefix + "foo/out/00001", 409 Value: []byte("test"), 410 Session: id, 411 }, nil) 412 if err != nil { 413 t.Fatalf("err: %v", err) 414 } 415 if !ok { 416 t.Fatalf("should be ok bro") 417 } 418 419 retry.Run(t, func(r *retry.R) { 420 select { 421 case o := <-outputCh: 422 if o.Node != "foo" || string(o.Output) != "test" { 423 r.Fatalf("bad: %#v", o) 424 } 425 case <-time.After(50 * time.Millisecond): 426 r.Fatalf("timeout") 427 } 428 }) 429 }