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 }