github.com/clly/consul@v1.4.5/agent/consul/operator_raft_endpoint_test.go (about)

     1  package consul
     2  
     3  import (
     4  	"fmt"
     5  	"os"
     6  	"strings"
     7  	"testing"
     8  	"time"
     9  
    10  	"github.com/hashicorp/consul/acl"
    11  	"github.com/hashicorp/consul/agent/structs"
    12  	"github.com/hashicorp/consul/lib/freeport"
    13  	"github.com/hashicorp/consul/testrpc"
    14  	"github.com/hashicorp/net-rpc-msgpackrpc"
    15  	"github.com/hashicorp/raft"
    16  	"github.com/pascaldekloe/goe/verify"
    17  )
    18  
    19  func TestOperator_RaftGetConfiguration(t *testing.T) {
    20  	t.Parallel()
    21  	dir1, s1 := testServer(t)
    22  	defer os.RemoveAll(dir1)
    23  	defer s1.Shutdown()
    24  	codec := rpcClient(t, s1)
    25  	defer codec.Close()
    26  
    27  	testrpc.WaitForTestAgent(t, s1.RPC, "dc1")
    28  
    29  	arg := structs.DCSpecificRequest{
    30  		Datacenter: "dc1",
    31  	}
    32  	var reply structs.RaftConfigurationResponse
    33  	if err := msgpackrpc.CallWithCodec(codec, "Operator.RaftGetConfiguration", &arg, &reply); err != nil {
    34  		t.Fatalf("err: %v", err)
    35  	}
    36  
    37  	future := s1.raft.GetConfiguration()
    38  	if err := future.Error(); err != nil {
    39  		t.Fatalf("err: %v", err)
    40  	}
    41  	if len(future.Configuration().Servers) != 1 {
    42  		t.Fatalf("bad: %v", future.Configuration().Servers)
    43  	}
    44  	me := future.Configuration().Servers[0]
    45  	expected := structs.RaftConfigurationResponse{
    46  		Servers: []*structs.RaftServer{
    47  			&structs.RaftServer{
    48  				ID:              me.ID,
    49  				Node:            s1.config.NodeName,
    50  				Address:         me.Address,
    51  				Leader:          true,
    52  				Voter:           true,
    53  				ProtocolVersion: "3",
    54  			},
    55  		},
    56  		Index: future.Index(),
    57  	}
    58  	verify.Values(t, "", reply, expected)
    59  }
    60  
    61  func TestOperator_RaftGetConfiguration_ACLDeny(t *testing.T) {
    62  	t.Parallel()
    63  	dir1, s1 := testServerWithConfig(t, func(c *Config) {
    64  		c.ACLDatacenter = "dc1"
    65  		c.ACLsEnabled = true
    66  		c.ACLMasterToken = "root"
    67  		c.ACLDefaultPolicy = "deny"
    68  	})
    69  	defer os.RemoveAll(dir1)
    70  	defer s1.Shutdown()
    71  	codec := rpcClient(t, s1)
    72  	defer codec.Close()
    73  
    74  	testrpc.WaitForTestAgent(t, s1.RPC, "dc1")
    75  
    76  	// Make a request with no token to make sure it gets denied.
    77  	arg := structs.DCSpecificRequest{
    78  		Datacenter: "dc1",
    79  	}
    80  	var reply structs.RaftConfigurationResponse
    81  	err := msgpackrpc.CallWithCodec(codec, "Operator.RaftGetConfiguration", &arg, &reply)
    82  	if !acl.IsErrPermissionDenied(err) {
    83  		t.Fatalf("err: %v", err)
    84  	}
    85  
    86  	// Create an ACL with operator read permissions.
    87  	var token string
    88  	{
    89  		var rules = `
    90                      operator = "read"
    91                  `
    92  
    93  		req := structs.ACLRequest{
    94  			Datacenter: "dc1",
    95  			Op:         structs.ACLSet,
    96  			ACL: structs.ACL{
    97  				Name:  "User token",
    98  				Type:  structs.ACLTokenTypeClient,
    99  				Rules: rules,
   100  			},
   101  			WriteRequest: structs.WriteRequest{Token: "root"},
   102  		}
   103  		if err := msgpackrpc.CallWithCodec(codec, "ACL.Apply", &req, &token); err != nil {
   104  			t.Fatalf("err: %v", err)
   105  		}
   106  	}
   107  
   108  	// Now it should go through.
   109  	arg.Token = token
   110  	if err := msgpackrpc.CallWithCodec(codec, "Operator.RaftGetConfiguration", &arg, &reply); err != nil {
   111  		t.Fatalf("err: %v", err)
   112  	}
   113  
   114  	future := s1.raft.GetConfiguration()
   115  	if err := future.Error(); err != nil {
   116  		t.Fatalf("err: %v", err)
   117  	}
   118  	if len(future.Configuration().Servers) != 1 {
   119  		t.Fatalf("bad: %v", future.Configuration().Servers)
   120  	}
   121  	me := future.Configuration().Servers[0]
   122  	expected := structs.RaftConfigurationResponse{
   123  		Servers: []*structs.RaftServer{
   124  			&structs.RaftServer{
   125  				ID:              me.ID,
   126  				Node:            s1.config.NodeName,
   127  				Address:         me.Address,
   128  				Leader:          true,
   129  				Voter:           true,
   130  				ProtocolVersion: "3",
   131  			},
   132  		},
   133  		Index: future.Index(),
   134  	}
   135  	verify.Values(t, "", reply, expected)
   136  }
   137  
   138  func TestOperator_RaftRemovePeerByAddress(t *testing.T) {
   139  	t.Parallel()
   140  	dir1, s1 := testServer(t)
   141  	defer os.RemoveAll(dir1)
   142  	defer s1.Shutdown()
   143  	codec := rpcClient(t, s1)
   144  	defer codec.Close()
   145  
   146  	testrpc.WaitForLeader(t, s1.RPC, "dc1")
   147  
   148  	// Try to remove a peer that's not there.
   149  	arg := structs.RaftRemovePeerRequest{
   150  		Datacenter: "dc1",
   151  		Address:    raft.ServerAddress(fmt.Sprintf("127.0.0.1:%d", freeport.Get(1)[0])),
   152  	}
   153  	var reply struct{}
   154  	err := msgpackrpc.CallWithCodec(codec, "Operator.RaftRemovePeerByAddress", &arg, &reply)
   155  	if err == nil || !strings.Contains(err.Error(), "not found in the Raft configuration") {
   156  		t.Fatalf("err: %v", err)
   157  	}
   158  
   159  	// Add it manually to Raft.
   160  	{
   161  		id := raft.ServerID("fake-node-id")
   162  		future := s1.raft.AddVoter(id, arg.Address, 0, time.Second)
   163  		if err := future.Error(); err != nil {
   164  			t.Fatalf("err: %v", err)
   165  		}
   166  	}
   167  
   168  	// Make sure it's there.
   169  	{
   170  		future := s1.raft.GetConfiguration()
   171  		if err := future.Error(); err != nil {
   172  			t.Fatalf("err: %v", err)
   173  		}
   174  		configuration := future.Configuration()
   175  		if len(configuration.Servers) != 2 {
   176  			t.Fatalf("bad: %v", configuration)
   177  		}
   178  	}
   179  
   180  	// Remove it, now it should go through.
   181  	if err := msgpackrpc.CallWithCodec(codec, "Operator.RaftRemovePeerByAddress", &arg, &reply); err != nil {
   182  		t.Fatalf("err: %v", err)
   183  	}
   184  
   185  	// Make sure it's not there.
   186  	{
   187  		future := s1.raft.GetConfiguration()
   188  		if err := future.Error(); err != nil {
   189  			t.Fatalf("err: %v", err)
   190  		}
   191  		configuration := future.Configuration()
   192  		if len(configuration.Servers) != 1 {
   193  			t.Fatalf("bad: %v", configuration)
   194  		}
   195  	}
   196  }
   197  
   198  func TestOperator_RaftRemovePeerByAddress_ACLDeny(t *testing.T) {
   199  	t.Parallel()
   200  	dir1, s1 := testServerWithConfig(t, func(c *Config) {
   201  		c.ACLDatacenter = "dc1"
   202  		c.ACLsEnabled = true
   203  		c.ACLMasterToken = "root"
   204  		c.ACLDefaultPolicy = "deny"
   205  	})
   206  	defer os.RemoveAll(dir1)
   207  	defer s1.Shutdown()
   208  	codec := rpcClient(t, s1)
   209  	defer codec.Close()
   210  
   211  	testrpc.WaitForTestAgent(t, s1.RPC, "dc1")
   212  
   213  	// Make a request with no token to make sure it gets denied.
   214  	arg := structs.RaftRemovePeerRequest{
   215  		Datacenter: "dc1",
   216  		Address:    raft.ServerAddress(s1.config.RPCAddr.String()),
   217  	}
   218  	var reply struct{}
   219  	err := msgpackrpc.CallWithCodec(codec, "Operator.RaftRemovePeerByAddress", &arg, &reply)
   220  	if !acl.IsErrPermissionDenied(err) {
   221  		t.Fatalf("err: %v", err)
   222  	}
   223  
   224  	// Create an ACL with operator write permissions.
   225  	var token string
   226  	{
   227  		var rules = `
   228                      operator = "write"
   229                  `
   230  
   231  		req := structs.ACLRequest{
   232  			Datacenter: "dc1",
   233  			Op:         structs.ACLSet,
   234  			ACL: structs.ACL{
   235  				Name:  "User token",
   236  				Type:  structs.ACLTokenTypeClient,
   237  				Rules: rules,
   238  			},
   239  			WriteRequest: structs.WriteRequest{Token: "root"},
   240  		}
   241  		if err := msgpackrpc.CallWithCodec(codec, "ACL.Apply", &req, &token); err != nil {
   242  			t.Fatalf("err: %v", err)
   243  		}
   244  	}
   245  
   246  	// Now it should kick back for being an invalid config, which means it
   247  	// tried to do the operation.
   248  	arg.Token = token
   249  	err = msgpackrpc.CallWithCodec(codec, "Operator.RaftRemovePeerByAddress", &arg, &reply)
   250  	if err == nil || !strings.Contains(err.Error(), "at least one voter") {
   251  		t.Fatalf("err: %v", err)
   252  	}
   253  }
   254  
   255  func TestOperator_RaftRemovePeerByID(t *testing.T) {
   256  	t.Parallel()
   257  	dir1, s1 := testServerWithConfig(t, func(c *Config) {
   258  		c.RaftConfig.ProtocolVersion = 3
   259  	})
   260  	defer os.RemoveAll(dir1)
   261  	defer s1.Shutdown()
   262  	codec := rpcClient(t, s1)
   263  	defer codec.Close()
   264  
   265  	testrpc.WaitForLeader(t, s1.RPC, "dc1")
   266  
   267  	// Try to remove a peer that's not there.
   268  	arg := structs.RaftRemovePeerRequest{
   269  		Datacenter: "dc1",
   270  		ID:         raft.ServerID("e35bde83-4e9c-434f-a6ef-453f44ee21ea"),
   271  	}
   272  	var reply struct{}
   273  	err := msgpackrpc.CallWithCodec(codec, "Operator.RaftRemovePeerByID", &arg, &reply)
   274  	if err == nil || !strings.Contains(err.Error(), "not found in the Raft configuration") {
   275  		t.Fatalf("err: %v", err)
   276  	}
   277  
   278  	// Add it manually to Raft.
   279  	{
   280  		future := s1.raft.AddVoter(arg.ID, raft.ServerAddress(fmt.Sprintf("127.0.0.1:%d", freeport.Get(1)[0])), 0, 0)
   281  		if err := future.Error(); err != nil {
   282  			t.Fatalf("err: %v", err)
   283  		}
   284  	}
   285  
   286  	// Make sure it's there.
   287  	{
   288  		future := s1.raft.GetConfiguration()
   289  		if err := future.Error(); err != nil {
   290  			t.Fatalf("err: %v", err)
   291  		}
   292  		configuration := future.Configuration()
   293  		if len(configuration.Servers) != 2 {
   294  			t.Fatalf("bad: %v", configuration)
   295  		}
   296  	}
   297  
   298  	// Remove it, now it should go through.
   299  	if err := msgpackrpc.CallWithCodec(codec, "Operator.RaftRemovePeerByID", &arg, &reply); err != nil {
   300  		t.Fatalf("err: %v", err)
   301  	}
   302  
   303  	// Make sure it's not there.
   304  	{
   305  		future := s1.raft.GetConfiguration()
   306  		if err := future.Error(); err != nil {
   307  			t.Fatalf("err: %v", err)
   308  		}
   309  		configuration := future.Configuration()
   310  		if len(configuration.Servers) != 1 {
   311  			t.Fatalf("bad: %v", configuration)
   312  		}
   313  	}
   314  }
   315  
   316  func TestOperator_RaftRemovePeerByID_ACLDeny(t *testing.T) {
   317  	t.Parallel()
   318  	dir1, s1 := testServerWithConfig(t, func(c *Config) {
   319  		c.ACLDatacenter = "dc1"
   320  		c.ACLsEnabled = true
   321  		c.ACLMasterToken = "root"
   322  		c.ACLDefaultPolicy = "deny"
   323  		c.RaftConfig.ProtocolVersion = 3
   324  	})
   325  	defer os.RemoveAll(dir1)
   326  	defer s1.Shutdown()
   327  	codec := rpcClient(t, s1)
   328  	defer codec.Close()
   329  
   330  	testrpc.WaitForLeader(t, s1.RPC, "dc1")
   331  
   332  	// Make a request with no token to make sure it gets denied.
   333  	arg := structs.RaftRemovePeerRequest{
   334  		Datacenter: "dc1",
   335  		ID:         raft.ServerID(s1.config.NodeID),
   336  	}
   337  	var reply struct{}
   338  	err := msgpackrpc.CallWithCodec(codec, "Operator.RaftRemovePeerByID", &arg, &reply)
   339  	if !acl.IsErrPermissionDenied(err) {
   340  		t.Fatalf("err: %v", err)
   341  	}
   342  
   343  	// Create an ACL with operator write permissions.
   344  	var token string
   345  	{
   346  		var rules = `
   347                      operator = "write"
   348                  `
   349  
   350  		req := structs.ACLRequest{
   351  			Datacenter: "dc1",
   352  			Op:         structs.ACLSet,
   353  			ACL: structs.ACL{
   354  				Name:  "User token",
   355  				Type:  structs.ACLTokenTypeClient,
   356  				Rules: rules,
   357  			},
   358  			WriteRequest: structs.WriteRequest{Token: "root"},
   359  		}
   360  		if err := msgpackrpc.CallWithCodec(codec, "ACL.Apply", &req, &token); err != nil {
   361  			t.Fatalf("err: %v", err)
   362  		}
   363  	}
   364  
   365  	// Now it should kick back for being an invalid config, which means it
   366  	// tried to do the operation.
   367  	arg.Token = token
   368  	err = msgpackrpc.CallWithCodec(codec, "Operator.RaftRemovePeerByID", &arg, &reply)
   369  	if err == nil || !strings.Contains(err.Error(), "at least one voter") {
   370  		t.Fatalf("err: %v", err)
   371  	}
   372  }