gopkg.in/hashicorp/nomad.v0@v0.11.8/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, cleanupS := TestServer(t, nil)
    25  	defer cleanupS()
    26  	codec := rpcClient(t, s)
    27  	testutil.WaitForLeader(t, s.RPC)
    28  
    29  	c, cleanupC := client.TestClient(t, func(c *config.Config) {
    30  		c.Servers = []string{s.config.RPCAddr.String()}
    31  	})
    32  	defer cleanupC()
    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, cleanupS := TestACLServer(t, nil)
    66  	defer cleanupS()
    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, cleanupS := TestServer(t, nil)
   126  	defer cleanupS()
   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, cleanupS := TestServer(t, nil)
   150  	defer cleanupS()
   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, cleanupS1 := TestServer(t, func(c *Config) {
   177  		c.BootstrapExpect = 2
   178  	})
   179  	defer cleanupS1()
   180  	s2, cleanupS2 := TestServer(t, func(c *Config) {
   181  		c.BootstrapExpect = 2
   182  	})
   183  	defer cleanupS2()
   184  	TestJoin(t, s1, s2)
   185  	testutil.WaitForLeader(t, s1.RPC)
   186  	testutil.WaitForLeader(t, s2.RPC)
   187  	codec := rpcClient(t, s2)
   188  
   189  	c, cleanup := client.TestClient(t, func(c *config.Config) {
   190  		c.Servers = []string{s2.config.RPCAddr.String()}
   191  	})
   192  	defer cleanup()
   193  
   194  	// Wait for client initialization
   195  	select {
   196  	case <-c.Ready():
   197  	case <-time.After(10 * time.Second):
   198  		require.Fail("client timedout on initialize")
   199  	}
   200  
   201  	testutil.WaitForResult(func() (bool, error) {
   202  		nodes := s2.connectedNodes()
   203  		return len(nodes) == 1, nil
   204  	}, func(err error) {
   205  		t.Fatalf("should have a clients")
   206  	})
   207  
   208  	// Force remove the connection locally in case it exists
   209  	s1.nodeConnsLock.Lock()
   210  	delete(s1.nodeConns, c.NodeID())
   211  	s1.nodeConnsLock.Unlock()
   212  
   213  	// Make the request without having a node-id
   214  	req := &structs.NodeSpecificRequest{
   215  		NodeID:       uuid.Generate(),
   216  		QueryOptions: structs.QueryOptions{Region: "global"},
   217  	}
   218  
   219  	// Fetch the response
   220  	req.NodeID = c.NodeID()
   221  	var resp cstructs.ClientStatsResponse
   222  	err := msgpackrpc.CallWithCodec(codec, "ClientStats.Stats", req, &resp)
   223  	require.Nil(err)
   224  	require.NotNil(resp.HostStats)
   225  }