github.phpd.cn/hashicorp/consul@v1.4.5/agent/consul/operator_autopilot_endpoint_test.go (about)

     1  package consul
     2  
     3  import (
     4  	"os"
     5  	"strings"
     6  	"testing"
     7  	"time"
     8  
     9  	"github.com/hashicorp/consul/acl"
    10  	"github.com/hashicorp/consul/agent/consul/autopilot"
    11  	"github.com/hashicorp/consul/agent/structs"
    12  	"github.com/hashicorp/consul/testrpc"
    13  	"github.com/hashicorp/consul/testutil/retry"
    14  	"github.com/hashicorp/net-rpc-msgpackrpc"
    15  	"github.com/hashicorp/raft"
    16  )
    17  
    18  func TestOperator_Autopilot_GetConfiguration(t *testing.T) {
    19  	t.Parallel()
    20  	dir1, s1 := testServerWithConfig(t, func(c *Config) {
    21  		c.AutopilotConfig.CleanupDeadServers = false
    22  	})
    23  	defer os.RemoveAll(dir1)
    24  	defer s1.Shutdown()
    25  	codec := rpcClient(t, s1)
    26  	defer codec.Close()
    27  
    28  	testrpc.WaitForLeader(t, s1.RPC, "dc1")
    29  
    30  	arg := structs.DCSpecificRequest{
    31  		Datacenter: "dc1",
    32  	}
    33  	var reply autopilot.Config
    34  	err := msgpackrpc.CallWithCodec(codec, "Operator.AutopilotGetConfiguration", &arg, &reply)
    35  	if err != nil {
    36  		t.Fatalf("err: %v", err)
    37  	}
    38  	if reply.CleanupDeadServers {
    39  		t.Fatalf("bad: %#v", reply)
    40  	}
    41  }
    42  
    43  func TestOperator_Autopilot_GetConfiguration_ACLDeny(t *testing.T) {
    44  	t.Parallel()
    45  	dir1, s1 := testServerWithConfig(t, func(c *Config) {
    46  		c.ACLDatacenter = "dc1"
    47  		c.ACLsEnabled = true
    48  		c.ACLMasterToken = "root"
    49  		c.ACLDefaultPolicy = "deny"
    50  		c.AutopilotConfig.CleanupDeadServers = false
    51  	})
    52  	defer os.RemoveAll(dir1)
    53  	defer s1.Shutdown()
    54  	codec := rpcClient(t, s1)
    55  	defer codec.Close()
    56  
    57  	testrpc.WaitForLeader(t, s1.RPC, "dc1")
    58  
    59  	// Try to get config without permissions
    60  	arg := structs.DCSpecificRequest{
    61  		Datacenter: "dc1",
    62  	}
    63  	var reply autopilot.Config
    64  	err := msgpackrpc.CallWithCodec(codec, "Operator.AutopilotGetConfiguration", &arg, &reply)
    65  	if !acl.IsErrPermissionDenied(err) {
    66  		t.Fatalf("err: %v", err)
    67  	}
    68  
    69  	// Create an ACL with operator read permissions.
    70  	var token string
    71  	{
    72  		var rules = `
    73                      operator = "read"
    74                  `
    75  
    76  		req := structs.ACLRequest{
    77  			Datacenter: "dc1",
    78  			Op:         structs.ACLSet,
    79  			ACL: structs.ACL{
    80  				Name:  "User token",
    81  				Type:  structs.ACLTokenTypeClient,
    82  				Rules: rules,
    83  			},
    84  			WriteRequest: structs.WriteRequest{Token: "root"},
    85  		}
    86  		if err := msgpackrpc.CallWithCodec(codec, "ACL.Apply", &req, &token); err != nil {
    87  			t.Fatalf("err: %v", err)
    88  		}
    89  	}
    90  
    91  	// Now we can read and verify the config
    92  	arg.Token = token
    93  	err = msgpackrpc.CallWithCodec(codec, "Operator.AutopilotGetConfiguration", &arg, &reply)
    94  	if err != nil {
    95  		t.Fatalf("err: %v", err)
    96  	}
    97  	if reply.CleanupDeadServers {
    98  		t.Fatalf("bad: %#v", reply)
    99  	}
   100  }
   101  
   102  func TestOperator_Autopilot_SetConfiguration(t *testing.T) {
   103  	t.Parallel()
   104  	dir1, s1 := testServerWithConfig(t, func(c *Config) {
   105  		c.AutopilotConfig.CleanupDeadServers = false
   106  	})
   107  	defer os.RemoveAll(dir1)
   108  	defer s1.Shutdown()
   109  	codec := rpcClient(t, s1)
   110  	defer codec.Close()
   111  
   112  	testrpc.WaitForLeader(t, s1.RPC, "dc1")
   113  
   114  	// Change the autopilot config from the default
   115  	arg := structs.AutopilotSetConfigRequest{
   116  		Datacenter: "dc1",
   117  		Config: autopilot.Config{
   118  			CleanupDeadServers: true,
   119  		},
   120  	}
   121  	var reply *bool
   122  	err := msgpackrpc.CallWithCodec(codec, "Operator.AutopilotSetConfiguration", &arg, &reply)
   123  	if err != nil {
   124  		t.Fatalf("err: %v", err)
   125  	}
   126  
   127  	// Make sure it's changed
   128  	state := s1.fsm.State()
   129  	_, config, err := state.AutopilotConfig()
   130  	if err != nil {
   131  		t.Fatal(err)
   132  	}
   133  	if !config.CleanupDeadServers {
   134  		t.Fatalf("bad: %#v", config)
   135  	}
   136  }
   137  
   138  func TestOperator_Autopilot_SetConfiguration_ACLDeny(t *testing.T) {
   139  	t.Parallel()
   140  	dir1, s1 := testServerWithConfig(t, func(c *Config) {
   141  		c.ACLDatacenter = "dc1"
   142  		c.ACLsEnabled = true
   143  		c.ACLMasterToken = "root"
   144  		c.ACLDefaultPolicy = "deny"
   145  		c.AutopilotConfig.CleanupDeadServers = false
   146  	})
   147  	defer os.RemoveAll(dir1)
   148  	defer s1.Shutdown()
   149  	codec := rpcClient(t, s1)
   150  	defer codec.Close()
   151  
   152  	testrpc.WaitForLeader(t, s1.RPC, "dc1")
   153  
   154  	// Try to set config without permissions
   155  	arg := structs.AutopilotSetConfigRequest{
   156  		Datacenter: "dc1",
   157  		Config: autopilot.Config{
   158  			CleanupDeadServers: true,
   159  		},
   160  	}
   161  	var reply *bool
   162  	err := msgpackrpc.CallWithCodec(codec, "Operator.AutopilotSetConfiguration", &arg, &reply)
   163  	if !acl.IsErrPermissionDenied(err) {
   164  		t.Fatalf("err: %v", err)
   165  	}
   166  
   167  	// Create an ACL with operator write permissions.
   168  	var token string
   169  	{
   170  		var rules = `
   171                      operator = "write"
   172                  `
   173  
   174  		req := structs.ACLRequest{
   175  			Datacenter: "dc1",
   176  			Op:         structs.ACLSet,
   177  			ACL: structs.ACL{
   178  				Name:  "User token",
   179  				Type:  structs.ACLTokenTypeClient,
   180  				Rules: rules,
   181  			},
   182  			WriteRequest: structs.WriteRequest{Token: "root"},
   183  		}
   184  		if err := msgpackrpc.CallWithCodec(codec, "ACL.Apply", &req, &token); err != nil {
   185  			t.Fatalf("err: %v", err)
   186  		}
   187  	}
   188  
   189  	// Now we can update the config
   190  	arg.Token = token
   191  	err = msgpackrpc.CallWithCodec(codec, "Operator.AutopilotSetConfiguration", &arg, &reply)
   192  	if err != nil {
   193  		t.Fatalf("err: %v", err)
   194  	}
   195  
   196  	// Make sure it's changed
   197  	state := s1.fsm.State()
   198  	_, config, err := state.AutopilotConfig()
   199  	if err != nil {
   200  		t.Fatal(err)
   201  	}
   202  	if !config.CleanupDeadServers {
   203  		t.Fatalf("bad: %#v", config)
   204  	}
   205  }
   206  
   207  func TestOperator_ServerHealth(t *testing.T) {
   208  	t.Parallel()
   209  	conf := func(c *Config) {
   210  		c.Datacenter = "dc1"
   211  		c.Bootstrap = false
   212  		c.BootstrapExpect = 3
   213  		c.RaftConfig.ProtocolVersion = 3
   214  		c.ServerHealthInterval = 100 * time.Millisecond
   215  		c.AutopilotInterval = 100 * time.Millisecond
   216  	}
   217  	dir1, s1 := testServerWithConfig(t, conf)
   218  	defer os.RemoveAll(dir1)
   219  	defer s1.Shutdown()
   220  	codec := rpcClient(t, s1)
   221  	defer codec.Close()
   222  
   223  	dir2, s2 := testServerWithConfig(t, conf)
   224  	defer os.RemoveAll(dir2)
   225  	defer s2.Shutdown()
   226  	joinLAN(t, s2, s1)
   227  
   228  	dir3, s3 := testServerWithConfig(t, conf)
   229  	defer os.RemoveAll(dir3)
   230  	defer s3.Shutdown()
   231  	joinLAN(t, s3, s1)
   232  
   233  	testrpc.WaitForLeader(t, s1.RPC, "dc1")
   234  	retry.Run(t, func(r *retry.R) {
   235  		arg := structs.DCSpecificRequest{
   236  			Datacenter: "dc1",
   237  		}
   238  		var reply autopilot.OperatorHealthReply
   239  		err := msgpackrpc.CallWithCodec(codec, "Operator.ServerHealth", &arg, &reply)
   240  		if err != nil {
   241  			r.Fatalf("err: %v", err)
   242  		}
   243  		if !reply.Healthy {
   244  			r.Fatalf("bad: %v", reply)
   245  		}
   246  		if reply.FailureTolerance != 1 {
   247  			r.Fatalf("bad: %v", reply)
   248  		}
   249  		if len(reply.Servers) != 3 {
   250  			r.Fatalf("bad: %v", reply)
   251  		}
   252  		// Leader should have LastContact == 0, others should be positive
   253  		for _, s := range reply.Servers {
   254  			isLeader := s1.raft.Leader() == raft.ServerAddress(s.Address)
   255  			if isLeader && s.LastContact != 0 {
   256  				r.Fatalf("bad: %v", reply)
   257  			}
   258  			if !isLeader && s.LastContact <= 0 {
   259  				r.Fatalf("bad: %v", reply)
   260  			}
   261  		}
   262  	})
   263  }
   264  
   265  func TestOperator_ServerHealth_UnsupportedRaftVersion(t *testing.T) {
   266  	t.Parallel()
   267  	dir1, s1 := testServerWithConfig(t, func(c *Config) {
   268  		c.Datacenter = "dc1"
   269  		c.Bootstrap = true
   270  		c.RaftConfig.ProtocolVersion = 2
   271  	})
   272  	defer os.RemoveAll(dir1)
   273  	defer s1.Shutdown()
   274  	codec := rpcClient(t, s1)
   275  	defer codec.Close()
   276  
   277  	arg := structs.DCSpecificRequest{
   278  		Datacenter: "dc1",
   279  	}
   280  	var reply autopilot.OperatorHealthReply
   281  	err := msgpackrpc.CallWithCodec(codec, "Operator.ServerHealth", &arg, &reply)
   282  	if err == nil || !strings.Contains(err.Error(), "raft_protocol set to 3 or higher") {
   283  		t.Fatalf("bad: %v", err)
   284  	}
   285  }