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  }