16 hclog "github.com/hashicorp/go-hclog"
19 func TestClient(t *testing.T) {
20 process := helperProcess("mock")
21 c := NewClient(&ClientConfig{Cmd: process, HandshakeConfig: testHandshake})
24 // Test that it parses the proper address
25 addr, err := c.Start()
27 t.Fatalf("err should be nil, got %s", err)
30 if addr.Network() != "tcp" {
31 t.Fatalf("bad: %#v", addr)
34 if addr.String() != ":1234" {
35 t.Fatalf("bad: %#v", addr)
38 // Test that it exits properly if killed
41 if process.ProcessState == nil {
42 t.Fatal("should have process state")
45 // Test that it knows it is exited
47 t.Fatal("should say client has exited")
51 // This tests a bug where Kill would start
52 func TestClient_killStart(t *testing.T) {
53 // Create a temporary dir to store the result file
54 td, err := ioutil.TempDir("", "plugin")
56 t.Fatalf("err: %s", err)
58 defer os.RemoveAll(td)
61 path := filepath.Join(td, "booted")
62 process := helperProcess("bad-version", path)
63 c := NewClient(&ClientConfig{Cmd: process, HandshakeConfig: testHandshake})
66 // Verify our path doesn't exist
67 if _, err := os.Stat(path); err == nil || !os.IsNotExist(err) {
68 t.Fatalf("bad: %s", err)
71 // Test that it parses the proper address
72 if _, err := c.Start(); err == nil {
73 t.Fatal("expected error")
77 if _, err := os.Stat(path); err != nil {
78 t.Fatalf("bad: %s", err)
80 if err := os.Remove(path); err != nil {
81 t.Fatalf("bad: %s", err)
84 // Test that Kill does nothing really
87 // Test that it knows it is exited
89 t.Fatal("should say client has exited")
92 if process.ProcessState == nil {
93 t.Fatal("should have no process state")
96 // Verify our path doesn't exist
97 if _, err := os.Stat(path); err == nil || !os.IsNotExist(err) {
98 t.Fatalf("bad: %s", err)
102 func TestClient_testCleanup(t *testing.T) {
103 // Create a temporary dir to store the result file
104 td, err := ioutil.TempDir("", "plugin")
106 t.Fatalf("err: %s", err)
108 defer os.RemoveAll(td)
110 // Create a path that the helper process will write on cleanup
111 path := filepath.Join(td, "output")
114 process := helperProcess("cleanup", path)
115 c := NewClient(&ClientConfig{
117 HandshakeConfig: testHandshake,
118 Plugins: testPluginMap,
121 // Grab the client so the process starts
122 if _, err := c.Client(); err != nil {
124 t.Fatalf("err: %s", err)
127 // Kill it gracefully
131 if _, err := os.Stat(path); err != nil {
132 t.Fatalf("err: %s", err)
136 func TestClient_testInterface(t *testing.T) {
137 process := helperProcess("test-interface")
138 c := NewClient(&ClientConfig{
140 HandshakeConfig: testHandshake,
141 Plugins: testPluginMap,
145 // Grab the RPC client
146 client, err := c.Client()
148 t.Fatalf("err should be nil, got %s", err)
152 raw, err := client.Dispense("test")
154 t.Fatalf("err should be nil, got %s", err)
157 impl, ok := raw.(testInterface)
159 t.Fatalf("bad: %#v", raw)
162 result := impl.Double(21)
164 t.Fatalf("bad: %#v", result)
170 // Test that it knows it is exited
172 t.Fatal("should say client has exited")
176 func TestClient_grpc_servercrash(t *testing.T) {
177 process := helperProcess("test-grpc")
178 c := NewClient(&ClientConfig{
180 HandshakeConfig: testHandshake,
181 Plugins: testPluginMap,
182 AllowedProtocols: []Protocol{ProtocolGRPC},
186 if _, err := c.Start(); err != nil {
187 t.Fatalf("err: %s", err)
190 if v := c.Protocol(); v != ProtocolGRPC {
191 t.Fatalf("bad: %s", v)
194 // Grab the RPC client
195 client, err := c.Client()
197 t.Fatalf("err should be nil, got %s", err)
201 raw, err := client.Dispense("test")
203 t.Fatalf("err should be nil, got %s", err)
206 _, ok := raw.(testInterface)
208 t.Fatalf("bad: %#v", raw)
214 case <-c.doneCtx.Done():
215 case <-time.After(time.Second * 2):
216 t.Fatal("Context was not closed")
220 func TestClient_grpc(t *testing.T) {
221 process := helperProcess("test-grpc")
222 c := NewClient(&ClientConfig{
224 HandshakeConfig: testHandshake,
225 Plugins: testPluginMap,
226 AllowedProtocols: []Protocol{ProtocolGRPC},
230 if _, err := c.Start(); err != nil {
231 t.Fatalf("err: %s", err)
234 if v := c.Protocol(); v != ProtocolGRPC {
235 t.Fatalf("bad: %s", v)
238 // Grab the RPC client
239 client, err := c.Client()
241 t.Fatalf("err should be nil, got %s", err)
245 raw, err := client.Dispense("test")
247 t.Fatalf("err should be nil, got %s", err)
250 impl, ok := raw.(testInterface)
252 t.Fatalf("bad: %#v", raw)
255 result := impl.Double(21)
257 t.Fatalf("bad: %#v", result)
263 // Test that it knows it is exited
265 t.Fatal("should say client has exited")
269 func TestClient_grpcNotAllowed(t *testing.T) {
270 process := helperProcess("test-grpc")
271 c := NewClient(&ClientConfig{
273 HandshakeConfig: testHandshake,
274 Plugins: testPluginMap,
278 if _, err := c.Start(); err == nil {
279 t.Fatal("should error")
283 func TestClient_cmdAndReattach(t *testing.T) {
284 config := &ClientConfig{
285 Cmd: helperProcess("start-timeout"),
286 Reattach: &ReattachConfig{},
289 c := NewClient(config)
294 t.Fatal("err should not be nil")
298 func TestClient_reattach(t *testing.T) {
299 process := helperProcess("test-interface")
300 c := NewClient(&ClientConfig{
302 HandshakeConfig: testHandshake,
303 Plugins: testPluginMap,
307 // Grab the RPC client
310 t.Fatalf("err should be nil, got %s", err)
313 // Get the reattach configuration
314 reattach := c.ReattachConfig()
316 // Create a new client
317 c = NewClient(&ClientConfig{
319 HandshakeConfig: testHandshake,
320 Plugins: testPluginMap,
323 // Grab the RPC client
324 client, err := c.Client()
326 t.Fatalf("err should be nil, got %s", err)
330 raw, err := client.Dispense("test")
332 t.Fatalf("err should be nil, got %s", err)
335 impl, ok := raw.(testInterface)
337 t.Fatalf("bad: %#v", raw)
340 result := impl.Double(21)
342 t.Fatalf("bad: %#v", result)
348 // Test that it knows it is exited
350 t.Fatal("should say client has exited")
354 func TestClient_reattachNoProtocol(t *testing.T) {
355 process := helperProcess("test-interface")
356 c := NewClient(&ClientConfig{
358 HandshakeConfig: testHandshake,
359 Plugins: testPluginMap,
363 // Grab the RPC client
366 t.Fatalf("err should be nil, got %s", err)
369 // Get the reattach configuration
370 reattach := c.ReattachConfig()
371 reattach.Protocol = ""
373 // Create a new client
374 c = NewClient(&ClientConfig{
376 HandshakeConfig: testHandshake,
377 Plugins: testPluginMap,
380 // Grab the RPC client
381 client, err := c.Client()
383 t.Fatalf("err should be nil, got %s", err)
387 raw, err := client.Dispense("test")
389 t.Fatalf("err should be nil, got %s", err)
392 impl, ok := raw.(testInterface)
394 t.Fatalf("bad: %#v", raw)
397 result := impl.Double(21)
399 t.Fatalf("bad: %#v", result)
405 // Test that it knows it is exited
407 t.Fatal("should say client has exited")
411 func TestClient_reattachGRPC(t *testing.T) {
412 process := helperProcess("test-grpc")
413 c := NewClient(&ClientConfig{
415 HandshakeConfig: testHandshake,
416 Plugins: testPluginMap,
417 AllowedProtocols: []Protocol{ProtocolGRPC},
421 // Grab the RPC client
424 t.Fatalf("err should be nil, got %s", err)
427 // Get the reattach configuration
428 reattach := c.ReattachConfig()
430 // Create a new client
431 c = NewClient(&ClientConfig{
433 HandshakeConfig: testHandshake,
434 Plugins: testPluginMap,
435 AllowedProtocols: []Protocol{ProtocolGRPC},
438 // Grab the RPC client
439 client, err := c.Client()
441 t.Fatalf("err should be nil, got %s", err)
445 raw, err := client.Dispense("test")
447 t.Fatalf("err should be nil, got %s", err)
450 impl, ok := raw.(testInterface)
452 t.Fatalf("bad: %#v", raw)
455 result := impl.Double(21)
457 t.Fatalf("bad: %#v", result)
463 // Test that it knows it is exited
465 t.Fatal("should say client has exited")
469 func TestClient_reattachNotFound(t *testing.T) {
472 for i := pid; i < 32000; i++ {
473 if _, err := os.FindProcess(i); err != nil {
479 // Addr that won't work
480 l, err := net.Listen("tcp", "127.0.0.1:0")
482 t.Fatalf("err: %s", err)
488 c := NewClient(&ClientConfig{
489 Reattach: &ReattachConfig{
493 HandshakeConfig: testHandshake,
494 Plugins: testPluginMap,
497 // Start shouldn't error
498 if _, err := c.Start(); err == nil {
499 t.Fatal("should error")
500 } else if err != ErrProcessNotFound {
501 t.Fatalf("err: %s", err)
505 func TestClientStart_badVersion(t *testing.T) {
506 config := &ClientConfig{
507 Cmd: helperProcess("bad-version"),
508 StartTimeout: 50 * time.Millisecond,
509 HandshakeConfig: testHandshake,
512 c := NewClient(config)
517 t.Fatal("err should not be nil")
521 func TestClient_Start_Timeout(t *testing.T) {
522 config := &ClientConfig{
523 Cmd: helperProcess("start-timeout"),
524 StartTimeout: 50 * time.Millisecond,
525 HandshakeConfig: testHandshake,
528 c := NewClient(config)
533 t.Fatal("err should not be nil")
537 func TestClient_Stderr(t *testing.T) {
538 stderr := new(bytes.Buffer)
539 process := helperProcess("stderr")
540 c := NewClient(&ClientConfig{
543 HandshakeConfig: testHandshake,
547 if _, err := c.Start(); err != nil {
548 t.Fatalf("err: %s", err)
552 time.Sleep(10 * time.Millisecond)
555 if !strings.Contains(stderr.String(), "HELLO\n") {
556 t.Fatalf("bad log data: '%s'", stderr.String())
559 if !strings.Contains(stderr.String(), "WORLD\n") {
560 t.Fatalf("bad log data: '%s'", stderr.String())
564 func TestClient_StderrJSON(t *testing.T) {
565 stderr := new(bytes.Buffer)
566 process := helperProcess("stderr-json")
567 c := NewClient(&ClientConfig{
570 HandshakeConfig: testHandshake,
574 if _, err := c.Start(); err != nil {
575 t.Fatalf("err: %s", err)
579 time.Sleep(10 * time.Millisecond)
582 if !strings.Contains(stderr.String(), "[\"HELLO\"]\n") {
583 t.Fatalf("bad log data: '%s'", stderr.String())
586 if !strings.Contains(stderr.String(), "12345\n") {
587 t.Fatalf("bad log data: '%s'", stderr.String())
591 func TestClient_Stdin(t *testing.T) {
592 // Overwrite stdin for this test with a temporary file
593 tf, err := ioutil.TempFile("", "terraform")
595 t.Fatalf("err: %s", err)
597 defer os.Remove(tf.Name())
600 if _, err = tf.WriteString("hello"); err != nil {
601 t.Fatalf("error: %s", err)
604 if err = tf.Sync(); err != nil {
605 t.Fatalf("error: %s", err)
608 if _, err = tf.Seek(0, 0); err != nil {
609 t.Fatalf("error: %s", err)
613 defer func() { os.Stdin = oldStdin }()
616 process := helperProcess("stdin")
617 c := NewClient(&ClientConfig{Cmd: process, HandshakeConfig: testHandshake})
622 t.Fatalf("error: %s", err)
630 time.Sleep(50 * time.Millisecond)
633 if !process.ProcessState.Success() {
634 t.Fatal("process didn't exit cleanly")
638 func TestClient_SecureConfig(t *testing.T) {
640 secureConfig := &SecureConfig{
641 Checksum: []byte{'1'},
644 process := helperProcess("test-interface")
645 c := NewClient(&ClientConfig{
647 HandshakeConfig: testHandshake,
648 Plugins: testPluginMap,
649 SecureConfig: secureConfig,
652 // Grab the RPC client, should error
655 if err != ErrChecksumsDoNotMatch {
656 t.Fatalf("err should be %s, got %s", ErrChecksumsDoNotMatch, err)
659 // Get the checksum of the executable
660 file, err := os.Open(os.Args[0])
668 _, err = io.Copy(hash, file)
675 secureConfig = &SecureConfig{
680 c = NewClient(&ClientConfig{
682 HandshakeConfig: testHandshake,
683 Plugins: testPluginMap,
684 SecureConfig: secureConfig,
688 // Grab the RPC client
691 t.Fatalf("err should be nil, got %s", err)
695 func TestClient_TLS(t *testing.T) {
697 process := helperProcess("test-interface-tls")
698 cBad := NewClient(&ClientConfig{
700 HandshakeConfig: testHandshake,
701 Plugins: testPluginMap,
705 // Grab the RPC client
706 clientBad, err := cBad.Client()
708 t.Fatalf("err should be nil, got %s", err)
712 raw, err := clientBad.Dispense("test")
714 t.Fatal("expected error, got nil")
719 // Add TLS config to client
720 tlsConfig, err := helperTLSProvider()
722 t.Fatalf("err should be nil, got %s", err)
725 process = helperProcess("test-interface-tls")
726 c := NewClient(&ClientConfig{
728 HandshakeConfig: testHandshake,
729 Plugins: testPluginMap,
730 TLSConfig: tlsConfig,
734 // Grab the RPC client
735 client, err := c.Client()
737 t.Fatalf("err should be nil, got %s", err)
741 raw, err = client.Dispense("test")
743 t.Fatalf("err should be nil, got %s", err)
746 impl, ok := raw.(testInterface)
748 t.Fatalf("bad: %#v", raw)
751 result := impl.Double(21)
753 t.Fatalf("bad: %#v", result)
759 // Test that it knows it is exited
761 t.Fatal("should say client has exited")
765 func TestClient_TLS_grpc(t *testing.T) {
766 // Add TLS config to client
767 tlsConfig, err := helperTLSProvider()
769 t.Fatalf("err should be nil, got %s", err)
772 process := helperProcess("test-grpc-tls")
773 c := NewClient(&ClientConfig{
775 HandshakeConfig: testHandshake,
776 Plugins: testPluginMap,
777 TLSConfig: tlsConfig,
778 AllowedProtocols: []Protocol{ProtocolGRPC},
782 // Grab the RPC client
783 client, err := c.Client()
785 t.Fatalf("err should be nil, got %s", err)
789 raw, err := client.Dispense("test")
791 t.Fatalf("err should be nil, got %s", err)
794 impl, ok := raw.(testInterface)
796 t.Fatalf("bad: %#v", raw)
799 result := impl.Double(21)
801 t.Fatalf("bad: %#v", result)
807 // Test that it knows it is exited
809 t.Fatal("should say client has exited")
813 func TestClient_secureConfigAndReattach(t *testing.T) {
814 config := &ClientConfig{
815 SecureConfig: &SecureConfig{},
816 Reattach: &ReattachConfig{},
819 c := NewClient(config)
823 if err != ErrSecureConfigAndReattach {
824 t.Fatalf("err should not be %s, got %s", ErrSecureConfigAndReattach, err)
828 func TestClient_ping(t *testing.T) {
829 process := helperProcess("test-interface")
830 c := NewClient(&ClientConfig{
832 HandshakeConfig: testHandshake,
833 Plugins: testPluginMap,
838 client, err := c.Client()
840 t.Fatalf("err: %s", err)
844 if err := client.Ping(); err != nil {
845 t.Fatalf("err: %s", err)
850 if err := client.Ping(); err == nil {
851 t.Fatal("should error")
855 func TestClient_logger(t *testing.T) {
856 t.Run("net/rpc", func(t *testing.T) { testClient_logger(t, "netrpc") })
857 t.Run("grpc", func(t *testing.T) { testClient_logger(t, "grpc") })
860 func testClient_logger(t *testing.T, proto string) {
861 var buffer bytes.Buffer
862 mutex := new(sync.Mutex)
863 stderr := io.MultiWriter(os.Stderr, &buffer)
864 // Custom hclog.Logger
865 clientLogger := hclog.New(&hclog.LoggerOptions{
872 process := helperProcess("test-interface-logger-" + proto)
873 c := NewClient(&ClientConfig{
875 HandshakeConfig: testHandshake,
876 Plugins: testPluginMap,
877 Logger: clientLogger,
878 AllowedProtocols: []Protocol{ProtocolNetRPC, ProtocolGRPC},
882 // Grab the RPC client
883 client, err := c.Client()
885 t.Fatalf("err should be nil, got %s", err)
889 raw, err := client.Dispense("test")
891 t.Fatalf("err should be nil, got %s", err)
894 impl, ok := raw.(testInterface)
896 t.Fatalf("bad: %#v", raw)
900 // Discard everything else, and capture the output we care about
904 impl.PrintKV("foo", "bar")
905 time.Sleep(100 * time.Millisecond)
907 line, err := buffer.ReadString('\n')
912 if !strings.Contains(line, "foo=bar") {
913 t.Fatalf("bad: %q", line)
918 // Try an integer type
922 impl.PrintKV("foo", 12)
923 time.Sleep(100 * time.Millisecond)
925 line, err := buffer.ReadString('\n')
930 if !strings.Contains(line, "foo=12") {
931 t.Fatalf("bad: %q", line)
938 // Test that it knows it is exited
940 t.Fatal("should say client has exited")