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 }