open-consul/agent/consul/operator_backend_test.go

194 lines
6.1 KiB
Go
Raw Normal View History

Leadership transfer cmd (#14132) * add leadership transfer command * add RPC call test (flaky) * add missing import * add changelog * add command registration * Apply suggestions from code review Co-authored-by: Matt Keeler <mkeeler@users.noreply.github.com> * add the possibility of providing an id to raft leadership transfer. Add few tests. * delete old file from cherry pick * rename changelog filename to PR # * rename changelog and fix import * fix failing test * check for OperatorWrite Co-authored-by: Matt Keeler <mkeeler@users.noreply.github.com> * rename from leader-transfer to transfer-leader * remove version check and add test for operator read * move struct to operator.go * first pass * add code for leader transfer in the grpc backend and tests * wire the http endpoint to the new grpc endpoint * remove the RPC endpoint * remove non needed struct * fix naming * add mog glue to API * fix comment * remove dead code * fix linter error * change package name for proto file * remove error wrapping * fix failing test * add command registration * add grpc service mock tests * fix receiver to be pointer * use defined values Co-authored-by: Matt Keeler <mkeeler@users.noreply.github.com> * reuse MockAclAuthorizer * add documentation * remove usage of external.TokenFromContext * fix failing tests * fix proto generation * Apply suggestions from code review Co-authored-by: Jared Kirschner <85913323+jkirschner-hashicorp@users.noreply.github.com> * Apply suggestions from code review Co-authored-by: Jared Kirschner <85913323+jkirschner-hashicorp@users.noreply.github.com> * Apply suggestions from code review Co-authored-by: Jared Kirschner <85913323+jkirschner-hashicorp@users.noreply.github.com> * Apply suggestions from code review * add more context in doc for the reason * Apply suggestions from docs code review Co-authored-by: Jeff Boruszak <104028618+boruszak@users.noreply.github.com> * regenerate proto * fix linter errors Co-authored-by: github-team-consul-core <github-team-consul-core@hashicorp.com> Co-authored-by: Matt Keeler <mkeeler@users.noreply.github.com> Co-authored-by: Jared Kirschner <85913323+jkirschner-hashicorp@users.noreply.github.com> Co-authored-by: Jeff Boruszak <104028618+boruszak@users.noreply.github.com>
2022-11-14 20:35:12 +00:00
package consul
import (
"context"
"github.com/hashicorp/consul/acl"
external "github.com/hashicorp/consul/agent/grpc-external"
"github.com/hashicorp/consul/agent/structs"
"github.com/hashicorp/consul/proto/pboperator"
"github.com/hashicorp/consul/sdk/testutil/retry"
"google.golang.org/grpc/credentials/insecure"
"testing"
"time"
"github.com/stretchr/testify/require"
gogrpc "google.golang.org/grpc"
"github.com/hashicorp/consul/sdk/testutil"
"github.com/hashicorp/consul/testrpc"
)
func TestOperatorBackend_TransferLeader(t *testing.T) {
t.Parallel()
conf := testClusterConfig{
Datacenter: "dc1",
Servers: 3,
ServerConf: func(config *Config) {
config.RaftConfig.HeartbeatTimeout = 2 * time.Second
config.RaftConfig.ElectionTimeout = 2 * time.Second
config.RaftConfig.LeaderLeaseTimeout = 1 * time.Second
},
}
nodes := newTestCluster(t, &conf)
s1 := nodes.Servers[0]
// Make sure a leader is elected
testrpc.WaitForLeader(t, s1.RPC, "dc1")
// Make a write call to server2 and make sure it gets forwarded to server1
ctx, cancel := context.WithTimeout(context.Background(), 10*time.Second)
t.Cleanup(cancel)
// Dial server2 directly
conn, err := gogrpc.DialContext(ctx, s1.config.RPCAddr.String(),
gogrpc.WithContextDialer(newServerDialer(s1.config.RPCAddr.String())),
gogrpc.WithTransportCredentials(insecure.NewCredentials()),
gogrpc.WithBlock())
require.NoError(t, err)
t.Cleanup(func() { conn.Close() })
operatorClient := pboperator.NewOperatorServiceClient(conn)
testutil.RunStep(t, "transfer leader", func(t *testing.T) {
beforeLeader, _ := s1.raft.LeaderWithID()
require.NotEmpty(t, beforeLeader)
// Do the grpc Write call to server2
req := pboperator.TransferLeaderRequest{
ID: "",
}
reply, err := operatorClient.TransferLeader(ctx, &req)
require.NoError(t, err)
require.True(t, reply.Success)
time.Sleep(1 * time.Second)
testrpc.WaitForLeader(t, s1.RPC, "dc1")
retry.Run(t, func(r *retry.R) {
afterLeader, _ := s1.raft.LeaderWithID()
require.NotEmpty(r, afterLeader)
})
afterLeader, _ := s1.raft.LeaderWithID()
require.NotEmpty(t, afterLeader)
if afterLeader == beforeLeader {
t.Fatalf("leader should have changed %s == %s", afterLeader, beforeLeader)
}
})
}
func TestOperatorBackend_TransferLeaderWithACL(t *testing.T) {
t.Parallel()
conf := testClusterConfig{
Datacenter: "dc1",
Servers: 3,
ServerConf: func(config *Config) {
config.RaftConfig.HeartbeatTimeout = 2 * time.Second
config.RaftConfig.ElectionTimeout = 2 * time.Second
config.RaftConfig.LeaderLeaseTimeout = 1 * time.Second
config.ACLsEnabled = true
config.ACLInitialManagementToken = "root"
config.ACLResolverSettings.ACLDefaultPolicy = "deny"
},
}
nodes := newTestCluster(t, &conf)
s1 := nodes.Servers[0]
// Make sure a leader is elected
testrpc.WaitForLeader(t, s1.RPC, "dc1")
// Make a write call to server2 and make sure it gets forwarded to server1
ctx, cancel := context.WithTimeout(context.Background(), 10*time.Second)
t.Cleanup(cancel)
// Dial server2 directly
conn, err := gogrpc.DialContext(ctx, s1.config.RPCAddr.String(),
gogrpc.WithContextDialer(newServerDialer(s1.config.RPCAddr.String())),
gogrpc.WithTransportCredentials(insecure.NewCredentials()),
gogrpc.WithBlock())
require.NoError(t, err)
t.Cleanup(func() { conn.Close() })
operatorClient := pboperator.NewOperatorServiceClient(conn)
testutil.RunStep(t, "transfer leader no token", func(t *testing.T) {
beforeLeader, _ := s1.raft.LeaderWithID()
require.NotEmpty(t, beforeLeader)
// Do the grpc Write call to server2
req := pboperator.TransferLeaderRequest{
ID: "",
}
reply, err := operatorClient.TransferLeader(ctx, &req)
require.True(t, acl.IsErrPermissionDenied(err))
require.Nil(t, reply)
time.Sleep(1 * time.Second)
testrpc.WaitForLeader(t, s1.RPC, "dc1")
retry.Run(t, func(r *retry.R) {
afterLeader, _ := s1.raft.LeaderWithID()
require.NotEmpty(r, afterLeader)
})
afterLeader, _ := s1.raft.LeaderWithID()
require.NotEmpty(t, afterLeader)
if afterLeader != beforeLeader {
t.Fatalf("leader should have changed %s == %s", afterLeader, beforeLeader)
}
})
testutil.RunStep(t, "transfer leader operator read token", func(t *testing.T) {
beforeLeader, _ := s1.raft.LeaderWithID()
require.NotEmpty(t, beforeLeader)
// Do the grpc Write call to server2
req := pboperator.TransferLeaderRequest{
ID: "",
}
codec := rpcClient(t, s1)
rules := `operator = "read"`
tokenRead := createToken(t, codec, rules)
ctxToken, err := external.ContextWithQueryOptions(ctx, structs.QueryOptions{Token: tokenRead})
require.NoError(t, err)
reply, err := operatorClient.TransferLeader(ctxToken, &req)
require.True(t, acl.IsErrPermissionDenied(err))
require.Nil(t, reply)
time.Sleep(1 * time.Second)
testrpc.WaitForLeader(t, s1.RPC, "dc1")
retry.Run(t, func(r *retry.R) {
afterLeader, _ := s1.raft.LeaderWithID()
require.NotEmpty(r, afterLeader)
})
afterLeader, _ := s1.raft.LeaderWithID()
require.NotEmpty(t, afterLeader)
if afterLeader != beforeLeader {
t.Fatalf("leader should have changed %s == %s", afterLeader, beforeLeader)
}
})
testutil.RunStep(t, "transfer leader operator write token", func(t *testing.T) {
beforeLeader, _ := s1.raft.LeaderWithID()
require.NotEmpty(t, beforeLeader)
// Do the grpc Write call to server2
req := pboperator.TransferLeaderRequest{
ID: "",
}
codec := rpcClient(t, s1)
rules := `operator = "write"`
tokenWrite := createTokenWithPolicyNameFull(t, codec, "the-policy-write", rules, "root")
ctxToken, err := external.ContextWithQueryOptions(ctx, structs.QueryOptions{Token: tokenWrite.SecretID})
require.NoError(t, err)
reply, err := operatorClient.TransferLeader(ctxToken, &req)
require.NoError(t, err)
require.True(t, reply.Success)
time.Sleep(1 * time.Second)
testrpc.WaitForLeader(t, s1.RPC, "dc1")
retry.Run(t, func(r *retry.R) {
afterLeader, _ := s1.raft.LeaderWithID()
require.NotEmpty(r, afterLeader)
})
afterLeader, _ := s1.raft.LeaderWithID()
require.NotEmpty(t, afterLeader)
if afterLeader == beforeLeader {
t.Fatalf("leader should have changed %s == %s", afterLeader, beforeLeader)
}
})
}