github.com/Ilhicas/nomad@v1.0.4-0.20210304152020-e86851182bc3/client/agent_endpoint_test.go (about) 1 package client 2 3 import ( 4 "encoding/json" 5 "fmt" 6 "io" 7 "net" 8 "strings" 9 "testing" 10 "time" 11 12 "github.com/hashicorp/go-msgpack/codec" 13 "github.com/hashicorp/nomad/acl" 14 "github.com/hashicorp/nomad/client/config" 15 sframer "github.com/hashicorp/nomad/client/lib/streamframer" 16 cstructs "github.com/hashicorp/nomad/client/structs" 17 "github.com/hashicorp/nomad/command/agent/pprof" 18 "github.com/hashicorp/nomad/nomad" 19 "github.com/hashicorp/nomad/nomad/mock" 20 "github.com/hashicorp/nomad/nomad/structs" 21 "github.com/hashicorp/nomad/testutil" 22 "github.com/stretchr/testify/assert" 23 "github.com/stretchr/testify/require" 24 ) 25 26 func TestMonitor_Monitor(t *testing.T) { 27 t.Parallel() 28 require := require.New(t) 29 30 // start server and client 31 s, cleanupS := nomad.TestServer(t, nil) 32 defer cleanupS() 33 testutil.WaitForLeader(t, s.RPC) 34 35 c, cleanupC := TestClient(t, func(c *config.Config) { 36 c.Servers = []string{s.GetConfig().RPCAddr.String()} 37 }) 38 defer cleanupC() 39 40 req := cstructs.MonitorRequest{ 41 LogLevel: "debug", 42 NodeID: c.NodeID(), 43 } 44 45 handler, err := c.StreamingRpcHandler("Agent.Monitor") 46 require.Nil(err) 47 48 // create pipe 49 p1, p2 := net.Pipe() 50 defer p1.Close() 51 defer p2.Close() 52 53 errCh := make(chan error) 54 streamMsg := make(chan *cstructs.StreamErrWrapper) 55 56 go handler(p2) 57 58 // Start decoder 59 go func() { 60 decoder := codec.NewDecoder(p1, structs.MsgpackHandle) 61 for { 62 var msg cstructs.StreamErrWrapper 63 if err := decoder.Decode(&msg); err != nil { 64 if err == io.EOF || strings.Contains(err.Error(), "closed") { 65 return 66 } 67 errCh <- fmt.Errorf("error decoding: %v", err) 68 } 69 70 streamMsg <- &msg 71 } 72 }() 73 74 // send request 75 encoder := codec.NewEncoder(p1, structs.MsgpackHandle) 76 require.Nil(encoder.Encode(req)) 77 78 timeout := time.After(5 * time.Second) 79 expected := "[DEBUG]" 80 received := "" 81 82 OUTER: 83 for { 84 select { 85 case <-timeout: 86 t.Fatal("timeout waiting for logs") 87 case err := <-errCh: 88 t.Fatal(err) 89 case msg := <-streamMsg: 90 if msg.Error != nil { 91 t.Fatalf("Got error: %v", msg.Error.Error()) 92 } 93 94 var frame sframer.StreamFrame 95 err := json.Unmarshal(msg.Payload, &frame) 96 assert.NoError(t, err) 97 98 received += string(frame.Data) 99 if strings.Contains(received, expected) { 100 require.Nil(p2.Close()) 101 break OUTER 102 } 103 } 104 } 105 } 106 107 func TestMonitor_Monitor_ACL(t *testing.T) { 108 t.Parallel() 109 require := require.New(t) 110 111 // start server 112 s, root, cleanupS := nomad.TestACLServer(t, nil) 113 defer cleanupS() 114 testutil.WaitForLeader(t, s.RPC) 115 116 c, cleanupC := TestClient(t, func(c *config.Config) { 117 c.ACLEnabled = true 118 c.Servers = []string{s.GetConfig().RPCAddr.String()} 119 }) 120 defer cleanupC() 121 122 policyBad := mock.NodePolicy(acl.PolicyDeny) 123 tokenBad := mock.CreatePolicyAndToken(t, s.State(), 1005, "invalid", policyBad) 124 125 policyGood := mock.AgentPolicy(acl.PolicyRead) 126 tokenGood := mock.CreatePolicyAndToken(t, s.State(), 1009, "valid", policyGood) 127 128 cases := []struct { 129 Name string 130 Token string 131 ExpectedErr string 132 }{ 133 { 134 Name: "bad token", 135 Token: tokenBad.SecretID, 136 ExpectedErr: structs.ErrPermissionDenied.Error(), 137 }, 138 { 139 Name: "good token", 140 Token: tokenGood.SecretID, 141 ExpectedErr: "Unknown log level", 142 }, 143 { 144 Name: "root token", 145 Token: root.SecretID, 146 ExpectedErr: "Unknown log level", 147 }, 148 } 149 150 for _, tc := range cases { 151 t.Run(tc.Name, func(t *testing.T) { 152 req := &cstructs.MonitorRequest{ 153 LogLevel: "unknown", 154 QueryOptions: structs.QueryOptions{ 155 Namespace: structs.DefaultNamespace, 156 Region: "global", 157 AuthToken: tc.Token, 158 }, 159 } 160 161 handler, err := c.StreamingRpcHandler("Agent.Monitor") 162 require.Nil(err) 163 164 // create pipe 165 p1, p2 := net.Pipe() 166 defer p1.Close() 167 defer p2.Close() 168 169 errCh := make(chan error) 170 streamMsg := make(chan *cstructs.StreamErrWrapper) 171 172 go handler(p2) 173 174 // Start decoder 175 go func() { 176 decoder := codec.NewDecoder(p1, structs.MsgpackHandle) 177 for { 178 var msg cstructs.StreamErrWrapper 179 if err := decoder.Decode(&msg); err != nil { 180 if err == io.EOF || strings.Contains(err.Error(), "closed") { 181 return 182 } 183 errCh <- fmt.Errorf("error decoding: %v", err) 184 } 185 186 streamMsg <- &msg 187 } 188 }() 189 190 // send request 191 encoder := codec.NewEncoder(p1, structs.MsgpackHandle) 192 require.Nil(encoder.Encode(req)) 193 194 timeout := time.After(5 * time.Second) 195 OUTER: 196 for { 197 select { 198 case <-timeout: 199 t.Fatal("timeout") 200 case err := <-errCh: 201 t.Fatal(err) 202 case msg := <-streamMsg: 203 if msg.Error == nil { 204 continue 205 } 206 207 if strings.Contains(msg.Error.Error(), tc.ExpectedErr) { 208 break OUTER 209 } else { 210 t.Fatalf("Bad error: %v", msg.Error) 211 } 212 } 213 } 214 }) 215 } 216 } 217 218 // Test that by default with no acl, endpoint is disabled 219 func TestAgentProfile_DefaultDisabled(t *testing.T) { 220 t.Parallel() 221 require := require.New(t) 222 223 // start server and client 224 s1, cleanup := nomad.TestServer(t, nil) 225 defer cleanup() 226 227 testutil.WaitForLeader(t, s1.RPC) 228 229 c, cleanupC := TestClient(t, func(c *config.Config) { 230 c.Servers = []string{s1.GetConfig().RPCAddr.String()} 231 }) 232 defer cleanupC() 233 234 req := structs.AgentPprofRequest{ 235 ReqType: pprof.CPUReq, 236 NodeID: c.NodeID(), 237 } 238 239 reply := structs.AgentPprofResponse{} 240 241 err := c.ClientRPC("Agent.Profile", &req, &reply) 242 require.EqualError(err, structs.ErrPermissionDenied.Error()) 243 } 244 245 func TestAgentProfile(t *testing.T) { 246 t.Parallel() 247 require := require.New(t) 248 249 // start server and client 250 s1, cleanup := nomad.TestServer(t, nil) 251 defer cleanup() 252 253 testutil.WaitForLeader(t, s1.RPC) 254 255 c, cleanupC := TestClient(t, func(c *config.Config) { 256 c.Servers = []string{s1.GetConfig().RPCAddr.String()} 257 c.EnableDebug = true 258 }) 259 defer cleanupC() 260 261 // Successful request 262 { 263 req := structs.AgentPprofRequest{ 264 ReqType: pprof.CPUReq, 265 NodeID: c.NodeID(), 266 } 267 268 reply := structs.AgentPprofResponse{} 269 270 err := c.ClientRPC("Agent.Profile", &req, &reply) 271 require.NoError(err) 272 273 require.NotNil(reply.Payload) 274 require.Equal(c.NodeID(), reply.AgentID) 275 } 276 277 // Unknown profile request 278 { 279 req := structs.AgentPprofRequest{ 280 ReqType: pprof.LookupReq, 281 Profile: "unknown", 282 NodeID: c.NodeID(), 283 } 284 285 reply := structs.AgentPprofResponse{} 286 287 err := c.ClientRPC("Agent.Profile", &req, &reply) 288 require.EqualError(err, "RPC Error:: 404,Pprof profile not found profile: unknown") 289 } 290 } 291 292 func TestAgentProfile_ACL(t *testing.T) { 293 t.Parallel() 294 require := require.New(t) 295 296 // start server 297 s, root, cleanupS := nomad.TestACLServer(t, nil) 298 defer cleanupS() 299 testutil.WaitForLeader(t, s.RPC) 300 301 c, cleanupC := TestClient(t, func(c *config.Config) { 302 c.ACLEnabled = true 303 c.Servers = []string{s.GetConfig().RPCAddr.String()} 304 }) 305 defer cleanupC() 306 307 policyBad := mock.AgentPolicy(acl.PolicyRead) 308 tokenBad := mock.CreatePolicyAndToken(t, s.State(), 1005, "invalid", policyBad) 309 310 policyGood := mock.AgentPolicy(acl.PolicyWrite) 311 tokenGood := mock.CreatePolicyAndToken(t, s.State(), 1009, "valid", policyGood) 312 313 cases := []struct { 314 Name string 315 Token string 316 authErr bool 317 }{ 318 { 319 Name: "bad token", 320 Token: tokenBad.SecretID, 321 authErr: true, 322 }, 323 { 324 Name: "good token", 325 Token: tokenGood.SecretID, 326 }, 327 { 328 Name: "root token", 329 Token: root.SecretID, 330 }, 331 } 332 333 for _, tc := range cases { 334 t.Run(tc.Name, func(t *testing.T) { 335 req := &structs.AgentPprofRequest{ 336 ReqType: pprof.CmdReq, 337 QueryOptions: structs.QueryOptions{ 338 Namespace: structs.DefaultNamespace, 339 Region: "global", 340 AuthToken: tc.Token, 341 }, 342 } 343 344 reply := &structs.AgentPprofResponse{} 345 346 err := c.ClientRPC("Agent.Profile", req, reply) 347 if tc.authErr { 348 require.EqualError(err, structs.ErrPermissionDenied.Error()) 349 } else { 350 require.NoError(err) 351 require.NotNil(reply.Payload) 352 } 353 }) 354 } 355 } 356 357 func TestAgentHost(t *testing.T) { 358 t.Parallel() 359 360 // start server and client 361 s1, cleanup := nomad.TestServer(t, nil) 362 defer cleanup() 363 364 testutil.WaitForLeader(t, s1.RPC) 365 366 c, cleanupC := TestClient(t, func(c *config.Config) { 367 c.Servers = []string{s1.GetConfig().RPCAddr.String()} 368 c.EnableDebug = true 369 }) 370 defer cleanupC() 371 372 req := structs.HostDataRequest{} 373 var resp structs.HostDataResponse 374 375 err := c.ClientRPC("Agent.Host", &req, &resp) 376 require.NoError(t, err) 377 378 require.NotNil(t, resp.HostData) 379 require.Equal(t, c.NodeID(), resp.AgentID) 380 } 381 382 func TestAgentHost_ACL(t *testing.T) { 383 t.Parallel() 384 385 s, root, cleanupS := nomad.TestACLServer(t, nil) 386 defer cleanupS() 387 testutil.WaitForLeader(t, s.RPC) 388 389 c, cleanupC := TestClient(t, func(c *config.Config) { 390 c.ACLEnabled = true 391 c.Servers = []string{s.GetConfig().RPCAddr.String()} 392 }) 393 defer cleanupC() 394 395 policyGood := mock.AgentPolicy(acl.PolicyRead) 396 tokenGood := mock.CreatePolicyAndToken(t, s.State(), 1005, "valid", policyGood) 397 398 policyBad := mock.NodePolicy(acl.PolicyWrite) 399 tokenBad := mock.CreatePolicyAndToken(t, s.State(), 1009, "invalid", policyBad) 400 401 cases := []struct { 402 Name string 403 Token string 404 authErr bool 405 }{ 406 { 407 Name: "bad token", 408 Token: tokenBad.SecretID, 409 authErr: true, 410 }, 411 { 412 Name: "good token", 413 Token: tokenGood.SecretID, 414 }, 415 { 416 Name: "root token", 417 Token: root.SecretID, 418 }, 419 } 420 421 for _, tc := range cases { 422 t.Run(tc.Name, func(t *testing.T) { 423 req := structs.HostDataRequest{ 424 QueryOptions: structs.QueryOptions{ 425 AuthToken: tc.Token, 426 }, 427 } 428 var resp structs.HostDataResponse 429 430 err := c.ClientRPC("Agent.Host", &req, &resp) 431 if tc.authErr { 432 require.EqualError(t, err, structs.ErrPermissionDenied.Error()) 433 } else { 434 require.NoError(t, err) 435 require.NotEmpty(t, resp.HostData) 436 } 437 }) 438 } 439 }