github.com/quite/nomad@v0.8.6/command/agent/operator_endpoint_test.go (about)

     1  package agent
     2  
     3  import (
     4  	"bytes"
     5  	"fmt"
     6  	"net/http"
     7  	"net/http/httptest"
     8  	"strings"
     9  	"testing"
    10  	"time"
    11  
    12  	"github.com/hashicorp/consul/testutil/retry"
    13  	"github.com/hashicorp/nomad/api"
    14  	"github.com/hashicorp/nomad/nomad/structs"
    15  	"github.com/stretchr/testify/assert"
    16  )
    17  
    18  func TestHTTP_OperatorRaftConfiguration(t *testing.T) {
    19  	t.Parallel()
    20  	httpTest(t, nil, func(s *TestAgent) {
    21  		body := bytes.NewBuffer(nil)
    22  		req, err := http.NewRequest("GET", "/v1/operator/raft/configuration", body)
    23  		if err != nil {
    24  			t.Fatalf("err: %v", err)
    25  		}
    26  
    27  		resp := httptest.NewRecorder()
    28  		obj, err := s.Server.OperatorRaftConfiguration(resp, req)
    29  		if err != nil {
    30  			t.Fatalf("err: %v", err)
    31  		}
    32  		if resp.Code != 200 {
    33  			t.Fatalf("bad code: %d", resp.Code)
    34  		}
    35  		out, ok := obj.(structs.RaftConfigurationResponse)
    36  		if !ok {
    37  			t.Fatalf("unexpected: %T", obj)
    38  		}
    39  		if len(out.Servers) != 1 ||
    40  			!out.Servers[0].Leader ||
    41  			!out.Servers[0].Voter {
    42  			t.Fatalf("bad: %v", out)
    43  		}
    44  	})
    45  }
    46  
    47  func TestHTTP_OperatorRaftPeer(t *testing.T) {
    48  	assert := assert.New(t)
    49  	t.Parallel()
    50  	httpTest(t, nil, func(s *TestAgent) {
    51  		body := bytes.NewBuffer(nil)
    52  		req, err := http.NewRequest("DELETE", "/v1/operator/raft/peer?address=nope", body)
    53  		assert.Nil(err)
    54  
    55  		// If we get this error, it proves we sent the address all the
    56  		// way through.
    57  		resp := httptest.NewRecorder()
    58  		_, err = s.Server.OperatorRaftPeer(resp, req)
    59  		if err == nil || !strings.Contains(err.Error(),
    60  			"address \"nope\" was not found in the Raft configuration") {
    61  			t.Fatalf("err: %v", err)
    62  		}
    63  	})
    64  
    65  	httpTest(t, nil, func(s *TestAgent) {
    66  		body := bytes.NewBuffer(nil)
    67  		req, err := http.NewRequest("DELETE", "/v1/operator/raft/peer?id=nope", body)
    68  		assert.Nil(err)
    69  
    70  		// If we get this error, it proves we sent the address all the
    71  		// way through.
    72  		resp := httptest.NewRecorder()
    73  		_, err = s.Server.OperatorRaftPeer(resp, req)
    74  		if err == nil || !strings.Contains(err.Error(),
    75  			"id \"nope\" was not found in the Raft configuration") {
    76  			t.Fatalf("err: %v", err)
    77  		}
    78  	})
    79  }
    80  
    81  func TestOperator_AutopilotGetConfiguration(t *testing.T) {
    82  	t.Parallel()
    83  	httpTest(t, nil, func(s *TestAgent) {
    84  		body := bytes.NewBuffer(nil)
    85  		req, _ := http.NewRequest("GET", "/v1/operator/autopilot/configuration", body)
    86  		resp := httptest.NewRecorder()
    87  		obj, err := s.Server.OperatorAutopilotConfiguration(resp, req)
    88  		if err != nil {
    89  			t.Fatalf("err: %v", err)
    90  		}
    91  		if resp.Code != 200 {
    92  			t.Fatalf("bad code: %d", resp.Code)
    93  		}
    94  		out, ok := obj.(api.AutopilotConfiguration)
    95  		if !ok {
    96  			t.Fatalf("unexpected: %T", obj)
    97  		}
    98  		if !out.CleanupDeadServers {
    99  			t.Fatalf("bad: %#v", out)
   100  		}
   101  	})
   102  }
   103  
   104  func TestOperator_AutopilotSetConfiguration(t *testing.T) {
   105  	t.Parallel()
   106  	httpTest(t, nil, func(s *TestAgent) {
   107  		body := bytes.NewBuffer([]byte(`{"CleanupDeadServers": false}`))
   108  		req, _ := http.NewRequest("PUT", "/v1/operator/autopilot/configuration", body)
   109  		resp := httptest.NewRecorder()
   110  		if _, err := s.Server.OperatorAutopilotConfiguration(resp, req); err != nil {
   111  			t.Fatalf("err: %v", err)
   112  		}
   113  		if resp.Code != 200 {
   114  			t.Fatalf("bad code: %d, %q", resp.Code, resp.Body.String())
   115  		}
   116  
   117  		args := structs.GenericRequest{
   118  			QueryOptions: structs.QueryOptions{
   119  				Region: s.Config.Region,
   120  			},
   121  		}
   122  
   123  		var reply structs.AutopilotConfig
   124  		if err := s.RPC("Operator.AutopilotGetConfiguration", &args, &reply); err != nil {
   125  			t.Fatalf("err: %v", err)
   126  		}
   127  		if reply.CleanupDeadServers {
   128  			t.Fatalf("bad: %#v", reply)
   129  		}
   130  	})
   131  }
   132  
   133  func TestOperator_AutopilotCASConfiguration(t *testing.T) {
   134  	t.Parallel()
   135  	httpTest(t, nil, func(s *TestAgent) {
   136  		body := bytes.NewBuffer([]byte(`{"CleanupDeadServers": false}`))
   137  		req, _ := http.NewRequest("PUT", "/v1/operator/autopilot/configuration", body)
   138  		resp := httptest.NewRecorder()
   139  		if _, err := s.Server.OperatorAutopilotConfiguration(resp, req); err != nil {
   140  			t.Fatalf("err: %v", err)
   141  		}
   142  		if resp.Code != 200 {
   143  			t.Fatalf("bad code: %d", resp.Code)
   144  		}
   145  
   146  		args := structs.GenericRequest{
   147  			QueryOptions: structs.QueryOptions{
   148  				Region: s.Config.Region,
   149  			},
   150  		}
   151  
   152  		var reply structs.AutopilotConfig
   153  		if err := s.RPC("Operator.AutopilotGetConfiguration", &args, &reply); err != nil {
   154  			t.Fatalf("err: %v", err)
   155  		}
   156  
   157  		if reply.CleanupDeadServers {
   158  			t.Fatalf("bad: %#v", reply)
   159  		}
   160  
   161  		// Create a CAS request, bad index
   162  		{
   163  			buf := bytes.NewBuffer([]byte(`{"CleanupDeadServers": true}`))
   164  			req, _ := http.NewRequest("PUT", fmt.Sprintf("/v1/operator/autopilot/configuration?cas=%d", reply.ModifyIndex-1), buf)
   165  			resp := httptest.NewRecorder()
   166  			obj, err := s.Server.OperatorAutopilotConfiguration(resp, req)
   167  			if err != nil {
   168  				t.Fatalf("err: %v", err)
   169  			}
   170  
   171  			if res := obj.(bool); res {
   172  				t.Fatalf("should NOT work")
   173  			}
   174  		}
   175  
   176  		// Create a CAS request, good index
   177  		{
   178  			buf := bytes.NewBuffer([]byte(`{"CleanupDeadServers": true}`))
   179  			req, _ := http.NewRequest("PUT", fmt.Sprintf("/v1/operator/autopilot/configuration?cas=%d", reply.ModifyIndex), buf)
   180  			resp := httptest.NewRecorder()
   181  			obj, err := s.Server.OperatorAutopilotConfiguration(resp, req)
   182  			if err != nil {
   183  				t.Fatalf("err: %v", err)
   184  			}
   185  
   186  			if res := obj.(bool); !res {
   187  				t.Fatalf("should work")
   188  			}
   189  		}
   190  
   191  		// Verify the update
   192  		if err := s.RPC("Operator.AutopilotGetConfiguration", &args, &reply); err != nil {
   193  			t.Fatalf("err: %v", err)
   194  		}
   195  		if !reply.CleanupDeadServers {
   196  			t.Fatalf("bad: %#v", reply)
   197  		}
   198  	})
   199  }
   200  
   201  func TestOperator_ServerHealth(t *testing.T) {
   202  	httpTest(t, func(c *Config) {
   203  		c.Server.RaftProtocol = 3
   204  	}, func(s *TestAgent) {
   205  		body := bytes.NewBuffer(nil)
   206  		req, _ := http.NewRequest("GET", "/v1/operator/autopilot/health", body)
   207  		retry.Run(t, func(r *retry.R) {
   208  			resp := httptest.NewRecorder()
   209  			obj, err := s.Server.OperatorServerHealth(resp, req)
   210  			if err != nil {
   211  				r.Fatalf("err: %v", err)
   212  			}
   213  			if resp.Code != 200 {
   214  				r.Fatalf("bad code: %d", resp.Code)
   215  			}
   216  			out, ok := obj.(*api.OperatorHealthReply)
   217  			if !ok {
   218  				r.Fatalf("unexpected: %T", obj)
   219  			}
   220  			if len(out.Servers) != 1 ||
   221  				!out.Servers[0].Healthy ||
   222  				out.Servers[0].Name != s.server.LocalMember().Name ||
   223  				out.Servers[0].SerfStatus != "alive" ||
   224  				out.FailureTolerance != 0 {
   225  				r.Fatalf("bad: %v, %q", out, s.server.LocalMember().Name)
   226  			}
   227  		})
   228  	})
   229  }
   230  
   231  func TestOperator_ServerHealth_Unhealthy(t *testing.T) {
   232  	t.Parallel()
   233  	httpTest(t, func(c *Config) {
   234  		c.Server.RaftProtocol = 3
   235  		c.Autopilot.LastContactThreshold = -1 * time.Second
   236  	}, func(s *TestAgent) {
   237  		body := bytes.NewBuffer(nil)
   238  		req, _ := http.NewRequest("GET", "/v1/operator/autopilot/health", body)
   239  		retry.Run(t, func(r *retry.R) {
   240  			resp := httptest.NewRecorder()
   241  			obj, err := s.Server.OperatorServerHealth(resp, req)
   242  			if err != nil {
   243  				r.Fatalf("err: %v", err)
   244  			}
   245  			if resp.Code != 429 {
   246  				r.Fatalf("bad code: %d, %v", resp.Code, obj.(*api.OperatorHealthReply))
   247  			}
   248  			out, ok := obj.(*api.OperatorHealthReply)
   249  			if !ok {
   250  				r.Fatalf("unexpected: %T", obj)
   251  			}
   252  			if len(out.Servers) != 1 ||
   253  				out.Healthy ||
   254  				out.Servers[0].Name != s.server.LocalMember().Name {
   255  				r.Fatalf("bad: %#v", out.Servers)
   256  			}
   257  		})
   258  	})
   259  }