github.com/bigcommerce/nomad@v0.9.3-bc/nomad/client_stats_endpoint_test.go (about)

     1  package nomad
     2  
     3  import (
     4  	"testing"
     5  	"time"
     6  
     7  	msgpackrpc "github.com/hashicorp/net-rpc-msgpackrpc"
     8  	"github.com/hashicorp/nomad/acl"
     9  	"github.com/hashicorp/nomad/client"
    10  	"github.com/hashicorp/nomad/client/config"
    11  	cstructs "github.com/hashicorp/nomad/client/structs"
    12  	"github.com/hashicorp/nomad/helper/uuid"
    13  	"github.com/hashicorp/nomad/nomad/mock"
    14  	"github.com/hashicorp/nomad/nomad/structs"
    15  	"github.com/hashicorp/nomad/testutil"
    16  	"github.com/stretchr/testify/require"
    17  )
    18  
    19  func TestClientStats_Stats_Local(t *testing.T) {
    20  	t.Parallel()
    21  	require := require.New(t)
    22  
    23  	// Start a server and client
    24  	s := TestServer(t, nil)
    25  	defer s.Shutdown()
    26  	codec := rpcClient(t, s)
    27  	testutil.WaitForLeader(t, s.RPC)
    28  
    29  	c, cleanup := client.TestClient(t, func(c *config.Config) {
    30  		c.Servers = []string{s.config.RPCAddr.String()}
    31  	})
    32  	defer cleanup()
    33  
    34  	testutil.WaitForResult(func() (bool, error) {
    35  		nodes := s.connectedNodes()
    36  		return len(nodes) == 1, nil
    37  	}, func(err error) {
    38  		t.Fatalf("should have a clients")
    39  	})
    40  
    41  	// Make the request without having a node-id
    42  	req := &structs.NodeSpecificRequest{
    43  		QueryOptions: structs.QueryOptions{Region: "global"},
    44  	}
    45  
    46  	// Fetch the response
    47  	var resp cstructs.ClientStatsResponse
    48  	err := msgpackrpc.CallWithCodec(codec, "ClientStats.Stats", req, &resp)
    49  	require.NotNil(err)
    50  	require.Contains(err.Error(), "missing")
    51  
    52  	// Fetch the response setting the node id
    53  	req.NodeID = c.NodeID()
    54  	var resp2 cstructs.ClientStatsResponse
    55  	err = msgpackrpc.CallWithCodec(codec, "ClientStats.Stats", req, &resp2)
    56  	require.Nil(err)
    57  	require.NotNil(resp2.HostStats)
    58  }
    59  
    60  func TestClientStats_Stats_Local_ACL(t *testing.T) {
    61  	t.Parallel()
    62  	require := require.New(t)
    63  
    64  	// Start a server
    65  	s, root := TestACLServer(t, nil)
    66  	defer s.Shutdown()
    67  	codec := rpcClient(t, s)
    68  	testutil.WaitForLeader(t, s.RPC)
    69  
    70  	// Create a bad token
    71  	policyBad := mock.NamespacePolicy("other", "", []string{acl.NamespaceCapabilityReadFS})
    72  	tokenBad := mock.CreatePolicyAndToken(t, s.State(), 1005, "invalid", policyBad)
    73  
    74  	policyGood := mock.NodePolicy(acl.PolicyRead)
    75  	tokenGood := mock.CreatePolicyAndToken(t, s.State(), 1009, "valid2", policyGood)
    76  
    77  	cases := []struct {
    78  		Name          string
    79  		Token         string
    80  		ExpectedError string
    81  	}{
    82  		{
    83  			Name:          "bad token",
    84  			Token:         tokenBad.SecretID,
    85  			ExpectedError: structs.ErrPermissionDenied.Error(),
    86  		},
    87  		{
    88  			Name:          "good token",
    89  			Token:         tokenGood.SecretID,
    90  			ExpectedError: "Unknown node",
    91  		},
    92  		{
    93  			Name:          "root token",
    94  			Token:         root.SecretID,
    95  			ExpectedError: "Unknown node",
    96  		},
    97  	}
    98  
    99  	for _, c := range cases {
   100  		t.Run(c.Name, func(t *testing.T) {
   101  
   102  			// Make the request without having a node-id
   103  			req := &structs.NodeSpecificRequest{
   104  				NodeID: uuid.Generate(),
   105  				QueryOptions: structs.QueryOptions{
   106  					AuthToken: c.Token,
   107  					Region:    "global",
   108  				},
   109  			}
   110  
   111  			// Fetch the response
   112  			var resp cstructs.ClientStatsResponse
   113  			err := msgpackrpc.CallWithCodec(codec, "ClientStats.Stats", req, &resp)
   114  			require.NotNil(err)
   115  			require.Contains(err.Error(), c.ExpectedError)
   116  		})
   117  	}
   118  }
   119  
   120  func TestClientStats_Stats_NoNode(t *testing.T) {
   121  	t.Parallel()
   122  	require := require.New(t)
   123  
   124  	// Start a server and client
   125  	s := TestServer(t, nil)
   126  	defer s.Shutdown()
   127  	codec := rpcClient(t, s)
   128  	testutil.WaitForLeader(t, s.RPC)
   129  
   130  	// Make the request with a nonexistent node-id
   131  	req := &structs.NodeSpecificRequest{
   132  		NodeID:       uuid.Generate(),
   133  		QueryOptions: structs.QueryOptions{Region: "global"},
   134  	}
   135  
   136  	// Fetch the response
   137  	var resp cstructs.ClientStatsResponse
   138  	err := msgpackrpc.CallWithCodec(codec, "ClientStats.Stats", req, &resp)
   139  	require.Nil(resp.HostStats)
   140  	require.NotNil(err)
   141  	require.Contains(err.Error(), "Unknown node")
   142  }
   143  
   144  func TestClientStats_Stats_OldNode(t *testing.T) {
   145  	t.Parallel()
   146  	require := require.New(t)
   147  
   148  	// Start a server
   149  	s := TestServer(t, nil)
   150  	defer s.Shutdown()
   151  	state := s.State()
   152  	codec := rpcClient(t, s)
   153  	testutil.WaitForLeader(t, s.RPC)
   154  
   155  	// Test for an old version error
   156  	node := mock.Node()
   157  	node.Attributes["nomad.version"] = "0.7.1"
   158  	require.Nil(state.UpsertNode(1005, node))
   159  
   160  	req := &structs.NodeSpecificRequest{
   161  		NodeID:       node.ID,
   162  		QueryOptions: structs.QueryOptions{Region: "global"},
   163  	}
   164  
   165  	// Fetch the response
   166  	var resp cstructs.ClientStatsResponse
   167  	err := msgpackrpc.CallWithCodec(codec, "ClientStats.Stats", req, &resp)
   168  	require.True(structs.IsErrNodeLacksRpc(err), err.Error())
   169  }
   170  
   171  func TestClientStats_Stats_Remote(t *testing.T) {
   172  	t.Parallel()
   173  	require := require.New(t)
   174  
   175  	// Start a server and client
   176  	s1 := TestServer(t, nil)
   177  	defer s1.Shutdown()
   178  	s2 := TestServer(t, func(c *Config) {
   179  		c.DevDisableBootstrap = true
   180  	})
   181  	defer s2.Shutdown()
   182  	TestJoin(t, s1, s2)
   183  	testutil.WaitForLeader(t, s1.RPC)
   184  	testutil.WaitForLeader(t, s2.RPC)
   185  	codec := rpcClient(t, s2)
   186  
   187  	c, cleanup := client.TestClient(t, func(c *config.Config) {
   188  		c.Servers = []string{s2.config.RPCAddr.String()}
   189  	})
   190  	defer cleanup()
   191  
   192  	// Wait for client initialization
   193  	select {
   194  	case <-c.Ready():
   195  	case <-time.After(10 * time.Second):
   196  		require.Fail("client timedout on initialize")
   197  	}
   198  
   199  	testutil.WaitForResult(func() (bool, error) {
   200  		nodes := s2.connectedNodes()
   201  		return len(nodes) == 1, nil
   202  	}, func(err error) {
   203  		t.Fatalf("should have a clients")
   204  	})
   205  
   206  	// Force remove the connection locally in case it exists
   207  	s1.nodeConnsLock.Lock()
   208  	delete(s1.nodeConns, c.NodeID())
   209  	s1.nodeConnsLock.Unlock()
   210  
   211  	// Make the request without having a node-id
   212  	req := &structs.NodeSpecificRequest{
   213  		NodeID:       uuid.Generate(),
   214  		QueryOptions: structs.QueryOptions{Region: "global"},
   215  	}
   216  
   217  	// Fetch the response
   218  	req.NodeID = c.NodeID()
   219  	var resp cstructs.ClientStatsResponse
   220  	err := msgpackrpc.CallWithCodec(codec, "ClientStats.Stats", req, &resp)
   221  	require.Nil(err)
   222  	require.NotNil(resp.HostStats)
   223  }