open-nomad/nomad/status_endpoint_test.go
Michael Schurter 7494a0c4fd core: remove all traces of unused protocol version
Nomad inherited protocol version numbering configuration from Consul and
Serf, but unlike those projects Nomad has never used it. Nomad's
`protocol_version` has always been `1`.

While the code is effectively unused and therefore poses no runtime
risks to leave, I felt like removing it was best because:

1. Nomad's RPC subsystem has been able to evolve extensively without
   needing to increment the version number.
2. Nomad's HTTP API has evolved extensively without increment
   `API{Major,Minor}Version`. If we want to version the HTTP API in the
   future, I doubt this is the mechanism we would choose.
3. The presence of the `server.protocol_version` configuration
   parameter is confusing since `server.raft_protocol` *is* an important
   parameter for operators to consider. Even more confusing is that
   there is a distinct Serf protocol version which is included in `nomad
   server members` output under the heading `Protocol`. `raft_protocol`
   is the *only* protocol version relevant to Nomad developers and
   operators. The other protocol versions are either deadcode or have
   never changed (Serf).
4. If we were to need to version the RPC, HTTP API, or Serf protocols, I
   don't think these configuration parameters and variables are the best
   choice. If we come to that point we should choose a versioning scheme
   based on the use case and modern best practices -- not this 6+ year
   old dead code.
2022-02-18 16:12:36 -08:00

183 lines
4.4 KiB
Go

package nomad
import (
"testing"
msgpackrpc "github.com/hashicorp/net-rpc-msgpackrpc"
"github.com/hashicorp/nomad/acl"
"github.com/hashicorp/nomad/helper/uuid"
"github.com/hashicorp/nomad/nomad/mock"
"github.com/hashicorp/nomad/nomad/structs"
"github.com/hashicorp/nomad/testutil"
"github.com/stretchr/testify/assert"
"github.com/stretchr/testify/require"
)
func TestStatusPing(t *testing.T) {
t.Parallel()
s1, cleanupS1 := TestServer(t, nil)
defer cleanupS1()
codec := rpcClient(t, s1)
arg := struct{}{}
var out struct{}
if err := msgpackrpc.CallWithCodec(codec, "Status.Ping", arg, &out); err != nil {
t.Fatalf("err: %v", err)
}
}
func TestStatusLeader(t *testing.T) {
t.Parallel()
s1, cleanupS1 := TestServer(t, nil)
defer cleanupS1()
codec := rpcClient(t, s1)
testutil.WaitForLeader(t, s1.RPC)
arg := &structs.GenericRequest{
QueryOptions: structs.QueryOptions{
Region: "global",
AllowStale: true,
},
}
var leader string
if err := msgpackrpc.CallWithCodec(codec, "Status.Leader", arg, &leader); err != nil {
t.Fatalf("err: %v", err)
}
if leader == "" {
t.Fatalf("unexpected leader: %v", leader)
}
}
func TestStatusPeers(t *testing.T) {
t.Parallel()
s1, cleanupS1 := TestServer(t, nil)
defer cleanupS1()
codec := rpcClient(t, s1)
arg := &structs.GenericRequest{
QueryOptions: structs.QueryOptions{
Region: "global",
AllowStale: true,
},
}
var peers []string
if err := msgpackrpc.CallWithCodec(codec, "Status.Peers", arg, &peers); err != nil {
t.Fatalf("err: %v", err)
}
if len(peers) != 1 {
t.Fatalf("no peers: %v", peers)
}
}
func TestStatusMembers(t *testing.T) {
t.Parallel()
s1, cleanupS1 := TestServer(t, nil)
defer cleanupS1()
codec := rpcClient(t, s1)
assert := assert.New(t)
arg := &structs.GenericRequest{
QueryOptions: structs.QueryOptions{
Region: "global",
AllowStale: true,
},
}
var out structs.ServerMembersResponse
assert.Nil(msgpackrpc.CallWithCodec(codec, "Status.Members", arg, &out))
assert.Len(out.Members, 1)
}
func TestStatusMembers_ACL(t *testing.T) {
t.Parallel()
s1, root, cleanupS1 := TestACLServer(t, nil)
defer cleanupS1()
codec := rpcClient(t, s1)
assert := assert.New(t)
state := s1.fsm.State()
// Create the namespace policy and tokens
validToken := mock.CreatePolicyAndToken(t, state, 1001, "test-valid", mock.NodePolicy(acl.PolicyRead))
invalidToken := mock.CreatePolicyAndToken(t, state, 1003, "test-invalid", mock.AgentPolicy(acl.PolicyRead))
arg := &structs.GenericRequest{
QueryOptions: structs.QueryOptions{
Region: "global",
AllowStale: true,
},
}
// Try without a token and expect failure
{
var out structs.ServerMembersResponse
err := msgpackrpc.CallWithCodec(codec, "Status.Members", arg, &out)
assert.NotNil(err)
assert.Equal(err.Error(), structs.ErrPermissionDenied.Error())
}
// Try with an invalid token and expect failure
{
arg.AuthToken = invalidToken.SecretID
var out structs.ServerMembersResponse
err := msgpackrpc.CallWithCodec(codec, "Status.Members", arg, &out)
assert.NotNil(err)
assert.Equal(err.Error(), structs.ErrPermissionDenied.Error())
}
// Try with a valid token
{
arg.AuthToken = validToken.SecretID
var out structs.ServerMembersResponse
assert.Nil(msgpackrpc.CallWithCodec(codec, "Status.Members", arg, &out))
assert.Len(out.Members, 1)
}
// Try with a management token
{
arg.AuthToken = root.SecretID
var out structs.ServerMembersResponse
assert.Nil(msgpackrpc.CallWithCodec(codec, "Status.Members", arg, &out))
assert.Len(out.Members, 1)
}
}
func TestStatus_HasClientConn(t *testing.T) {
t.Parallel()
s1, cleanupS1 := TestServer(t, nil)
defer cleanupS1()
codec := rpcClient(t, s1)
require := require.New(t)
arg := &structs.NodeSpecificRequest{
QueryOptions: structs.QueryOptions{
Region: "global",
AllowStale: true,
},
}
// Try without setting a node id
var out structs.NodeConnQueryResponse
require.NotNil(msgpackrpc.CallWithCodec(codec, "Status.HasNodeConn", arg, &out))
// Set a bad node id
arg.NodeID = uuid.Generate()
var out2 structs.NodeConnQueryResponse
require.Nil(msgpackrpc.CallWithCodec(codec, "Status.HasNodeConn", arg, &out2))
require.False(out2.Connected)
// Create a connection on that node
s1.addNodeConn(&RPCContext{
NodeID: arg.NodeID,
})
var out3 structs.NodeConnQueryResponse
require.Nil(msgpackrpc.CallWithCodec(codec, "Status.HasNodeConn", arg, &out3))
require.True(out3.Connected)
require.NotZero(out3.Established)
}