github.com/hashicorp/go-plugin@v1.6.0/server_test.go (about) 1 // Copyright (c) HashiCorp, Inc. 2 // SPDX-License-Identifier: MPL-2.0 3 4 package plugin 5 6 import ( 7 "bytes" 8 "context" 9 "log" 10 "net" 11 "os" 12 "path" 13 "runtime" 14 "strings" 15 "testing" 16 "time" 17 18 hclog "github.com/hashicorp/go-hclog" 19 ) 20 21 func TestServer_testMode(t *testing.T) { 22 ctx, cancel := context.WithCancel(context.Background()) 23 defer cancel() 24 25 ch := make(chan *ReattachConfig, 1) 26 closeCh := make(chan struct{}) 27 go Serve(&ServeConfig{ 28 HandshakeConfig: testHandshake, 29 Plugins: testGRPCPluginMap, 30 GRPCServer: DefaultGRPCServer, 31 Test: &ServeTestConfig{ 32 Context: ctx, 33 ReattachConfigCh: ch, 34 CloseCh: closeCh, 35 }, 36 }) 37 38 // We should get a config 39 var config *ReattachConfig 40 select { 41 case config = <-ch: 42 case <-time.After(2000 * time.Millisecond): 43 t.Fatal("should've received reattach") 44 } 45 if config == nil { 46 t.Fatal("config should not be nil") 47 } 48 49 // Check that the reattach config includes the negotiated protocol version 50 if config.ProtocolVersion != int(testHandshake.ProtocolVersion) { 51 t.Fatalf("wrong protocol version in reattach config. got %d, expected %d", config.ProtocolVersion, testHandshake.ProtocolVersion) 52 } 53 54 // Connect! 55 c := NewClient(&ClientConfig{ 56 Cmd: nil, 57 HandshakeConfig: testHandshake, 58 Plugins: testGRPCPluginMap, 59 Reattach: config, 60 AllowedProtocols: []Protocol{ProtocolGRPC}, 61 }) 62 client, err := c.Client() 63 if err != nil { 64 t.Fatalf("err: %s", err) 65 } 66 67 // Pinging should work 68 if err := client.Ping(); err != nil { 69 t.Fatalf("should not err: %s", err) 70 } 71 72 // Kill which should do nothing 73 c.Kill() 74 if err := client.Ping(); err != nil { 75 t.Fatalf("should not err: %s", err) 76 } 77 78 // Canceling should cause an exit 79 cancel() 80 <-closeCh 81 if err := client.Ping(); err == nil { 82 t.Fatal("should error") 83 } 84 85 // Try logging, this should show out in tests. We have to manually verify. 86 t.Logf("HELLO") 87 } 88 89 func TestServer_testMode_AutoMTLS(t *testing.T) { 90 ctx, cancel := context.WithCancel(context.Background()) 91 defer cancel() 92 93 closeCh := make(chan struct{}) 94 go Serve(&ServeConfig{ 95 HandshakeConfig: testVersionedHandshake, 96 VersionedPlugins: map[int]PluginSet{ 97 2: testGRPCPluginMap, 98 }, 99 GRPCServer: DefaultGRPCServer, 100 Logger: hclog.NewNullLogger(), 101 Test: &ServeTestConfig{ 102 Context: ctx, 103 ReattachConfigCh: nil, 104 CloseCh: closeCh, 105 }, 106 }) 107 108 // Connect! 109 process := helperProcess("test-mtls") 110 c := NewClient(&ClientConfig{ 111 Cmd: process, 112 HandshakeConfig: testVersionedHandshake, 113 VersionedPlugins: map[int]PluginSet{ 114 2: testGRPCPluginMap, 115 }, 116 AllowedProtocols: []Protocol{ProtocolGRPC}, 117 AutoMTLS: true, 118 }) 119 client, err := c.Client() 120 if err != nil { 121 t.Fatalf("err: %s", err) 122 } 123 124 // Pinging should work 125 if err := client.Ping(); err != nil { 126 t.Fatalf("should not err: %s", err) 127 } 128 129 // Grab the impl 130 raw, err := client.Dispense("test") 131 if err != nil { 132 t.Fatalf("err should be nil, got %s", err) 133 } 134 135 tester, ok := raw.(testInterface) 136 if !ok { 137 t.Fatalf("bad: %#v", raw) 138 } 139 140 n := tester.Double(3) 141 if n != 6 { 142 t.Fatal("invalid response", n) 143 } 144 145 // ensure we can make use of bidirectional communication with AutoMTLS 146 // enabled 147 err = tester.Bidirectional() 148 if err != nil { 149 t.Fatal("invalid response", err) 150 } 151 152 c.Kill() 153 // Canceling should cause an exit 154 cancel() 155 <-closeCh 156 } 157 158 func TestServer_RPC(t *testing.T) { 159 closeCh := make(chan struct{}) 160 ctx, cancel := context.WithCancel(context.Background()) 161 defer cancel() 162 163 // make a server, but we don't need to attach to it 164 ch := make(chan *ReattachConfig, 1) 165 go Serve(&ServeConfig{ 166 HandshakeConfig: testHandshake, 167 Plugins: testPluginMap, 168 Test: &ServeTestConfig{ 169 Context: ctx, 170 CloseCh: closeCh, 171 ReattachConfigCh: ch, 172 }, 173 }) 174 175 // Wait for the server 176 select { 177 case cfg := <-ch: 178 if cfg == nil { 179 t.Fatal("attach config should not be nil") 180 } 181 case <-time.After(2000 * time.Millisecond): 182 t.Fatal("should've received reattach") 183 } 184 185 cancel() 186 <-closeCh 187 } 188 189 func TestRmListener_impl(t *testing.T) { 190 var _ net.Listener = new(rmListener) 191 } 192 193 func TestRmListener(t *testing.T) { 194 l, err := net.Listen("tcp", "127.0.0.1:0") 195 if err != nil { 196 t.Fatalf("err: %s", err) 197 } 198 199 tf, err := os.CreateTemp("", "plugin") 200 if err != nil { 201 t.Fatalf("err: %s", err) 202 } 203 path := tf.Name() 204 205 // Close the file 206 if err := tf.Close(); err != nil { 207 t.Fatalf("err: %s", err) 208 } 209 210 // Create the listener and test close 211 rmL := newDeleteFileListener(l, path) 212 if err := rmL.Close(); err != nil { 213 t.Fatalf("err: %s", err) 214 } 215 216 // File should be goe 217 if _, err := os.Stat(path); err == nil || !os.IsNotExist(err) { 218 t.Fatalf("err: %s", err) 219 } 220 } 221 222 func TestProtocolSelection_no_server(t *testing.T) { 223 conf := &ServeConfig{ 224 HandshakeConfig: testVersionedHandshake, 225 VersionedPlugins: map[int]PluginSet{ 226 2: testGRPCPluginMap, 227 }, 228 GRPCServer: DefaultGRPCServer, 229 TLSProvider: helperTLSProvider, 230 } 231 232 _, protocol, _ := protocolVersion(conf) 233 if protocol != ProtocolGRPC { 234 t.Fatalf("bad protocol %s", protocol) 235 } 236 237 conf = &ServeConfig{ 238 HandshakeConfig: testVersionedHandshake, 239 VersionedPlugins: map[int]PluginSet{ 240 2: testGRPCPluginMap, 241 }, 242 TLSProvider: helperTLSProvider, 243 } 244 245 _, protocol, _ = protocolVersion(conf) 246 if protocol != ProtocolNetRPC { 247 t.Fatalf("bad protocol %s", protocol) 248 } 249 } 250 251 func TestServer_testStdLogger(t *testing.T) { 252 closeCh := make(chan struct{}) 253 ctx, cancel := context.WithCancel(context.Background()) 254 defer cancel() 255 256 var logOut bytes.Buffer 257 258 hclogger := hclog.New(&hclog.LoggerOptions{ 259 Name: "test", 260 Level: hclog.Trace, 261 Output: &logOut, 262 JSONFormat: true, 263 }) 264 265 // Wrap the hclog.Logger to use it from the default std library logger 266 // (and restore the original logger) 267 defer func() { 268 log.SetOutput(os.Stderr) 269 log.SetFlags(log.LstdFlags) 270 log.SetPrefix(log.Prefix()) 271 }() 272 log.SetOutput(hclogger.StandardWriter(&hclog.StandardLoggerOptions{InferLevels: true})) 273 log.SetFlags(0) 274 log.SetPrefix("") 275 276 // make a server, but we don't need to attach to it 277 ch := make(chan *ReattachConfig, 1) 278 go Serve(&ServeConfig{ 279 HandshakeConfig: testHandshake, 280 Plugins: testGRPCPluginMap, 281 GRPCServer: DefaultGRPCServer, 282 Logger: hclog.NewNullLogger(), 283 Test: &ServeTestConfig{ 284 Context: ctx, 285 CloseCh: closeCh, 286 ReattachConfigCh: ch, 287 }, 288 }) 289 290 // Wait for the server 291 select { 292 case cfg := <-ch: 293 if cfg == nil { 294 t.Fatal("attach config should not be nil") 295 } 296 case <-time.After(2000 * time.Millisecond): 297 t.Fatal("should've received reattach") 298 } 299 300 log.Println("[DEBUG] test log") 301 // shut down the server so there's no race on the buffer 302 cancel() 303 <-closeCh 304 305 if !strings.Contains(logOut.String(), "test log") { 306 t.Fatalf("expected: %q\ngot: %q", "test log", logOut.String()) 307 } 308 } 309 310 func TestUnixSocketDir(t *testing.T) { 311 if runtime.GOOS == "windows" { 312 t.Skip("go-plugin doesn't support unix sockets on Windows") 313 } 314 315 tmpDir := t.TempDir() 316 t.Setenv(EnvUnixSocketDir, tmpDir) 317 318 closeCh := make(chan struct{}) 319 ctx, cancel := context.WithCancel(context.Background()) 320 defer cancel() 321 322 // make a server, but we don't need to attach to it 323 ch := make(chan *ReattachConfig, 1) 324 go Serve(&ServeConfig{ 325 HandshakeConfig: testHandshake, 326 Plugins: testGRPCPluginMap, 327 GRPCServer: DefaultGRPCServer, 328 Logger: hclog.NewNullLogger(), 329 Test: &ServeTestConfig{ 330 Context: ctx, 331 CloseCh: closeCh, 332 ReattachConfigCh: ch, 333 }, 334 }) 335 336 // Wait for the server 337 var cfg *ReattachConfig 338 select { 339 case cfg = <-ch: 340 if cfg == nil { 341 t.Fatal("attach config should not be nil") 342 } 343 case <-time.After(2000 * time.Millisecond): 344 t.Fatal("should've received reattach") 345 } 346 347 actualDir := path.Clean(path.Dir(cfg.Addr.String())) 348 expectedDir := path.Clean(tmpDir) 349 if actualDir != expectedDir { 350 t.Fatalf("Expected socket in dir: %s, but was in %s", expectedDir, actualDir) 351 } 352 353 cancel() 354 <-closeCh 355 }