vitess.io/vitess@v0.16.2/go/vt/topo/consultopo/server_flaky_test.go (about) 1 /* 2 Copyright 2019 The Vitess Authors. 3 4 Licensed under the Apache License, Version 2.0 (the "License"); 5 you may not use this file except in compliance with the License. 6 You may obtain a copy of the License at 7 8 http://www.apache.org/licenses/LICENSE-2.0 9 10 Unless required by applicable law or agreed to in writing, software 11 distributed under the License is distributed on an "AS IS" BASIS, 12 WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 13 See the License for the specific language governing permissions and 14 limitations under the License. 15 */ 16 17 package consultopo 18 19 import ( 20 "context" 21 "encoding/json" 22 "fmt" 23 "os" 24 "os/exec" 25 "path" 26 "testing" 27 "time" 28 29 "vitess.io/vitess/go/vt/log" 30 31 "github.com/hashicorp/consul/api" 32 33 "vitess.io/vitess/go/testfiles" 34 "vitess.io/vitess/go/vt/topo" 35 "vitess.io/vitess/go/vt/topo/test" 36 37 topodatapb "vitess.io/vitess/go/vt/proto/topodata" 38 ) 39 40 // startConsul starts a consul subprocess, and waits for it to be ready. 41 // Returns the exec.Cmd forked, the config file to remove after the test, 42 // and the server address to RPC-connect to. 43 func startConsul(t *testing.T, authToken string) (*exec.Cmd, string, string) { 44 // Create a temporary config file, as ports cannot all be set 45 // via command line. The file name has to end with '.json' so 46 // we're not using TempFile. 47 configDir := t.TempDir() 48 49 configFilename := path.Join(configDir, "consul.json") 50 configFile, err := os.OpenFile(configFilename, os.O_RDWR|os.O_CREATE, 0600) 51 if err != nil { 52 t.Fatalf("cannot create tempfile: %v", err) 53 } 54 55 // Create the JSON config, save it. 56 port := testfiles.GoVtTopoConsultopoPort 57 config := map[string]any{ 58 "ports": map[string]int{ 59 "dns": port, 60 "http": port + 1, 61 "serf_lan": port + 2, 62 "serf_wan": port + 3, 63 }, 64 } 65 66 // TODO(deepthi): this is the legacy ACL format. We run v1.4.0 by default in which this has been deprecated. 67 // We should start using the new format 68 // https://learn.hashicorp.com/tutorials/consul/access-control-replication-multiple-datacenters?in=consul/security-operations 69 if authToken != "" { 70 config["datacenter"] = "vitess" 71 config["acl_datacenter"] = "vitess" 72 config["acl_master_token"] = authToken 73 config["acl_default_policy"] = "deny" 74 config["acl_down_policy"] = "extend-cache" 75 } 76 77 data, err := json.Marshal(config) 78 if err != nil { 79 t.Fatalf("cannot json-encode config: %v", err) 80 } 81 if _, err := configFile.Write(data); err != nil { 82 t.Fatalf("cannot write config: %v", err) 83 } 84 if err := configFile.Close(); err != nil { 85 t.Fatalf("cannot close config: %v", err) 86 } 87 88 cmd := exec.Command("consul", 89 "agent", 90 "-dev", 91 "-config-file", configFilename) 92 err = cmd.Start() 93 if err != nil { 94 t.Fatalf("failed to start consul: %v", err) 95 } 96 97 // Create a client to connect to the created consul. 98 serverAddr := fmt.Sprintf("localhost:%v", port+1) 99 cfg := api.DefaultConfig() 100 cfg.Address = serverAddr 101 if authToken != "" { 102 cfg.Token = authToken 103 } 104 c, err := api.NewClient(cfg) 105 if err != nil { 106 t.Fatalf("api.NewClient(%v) failed: %v", serverAddr, err) 107 } 108 109 // Wait until we can list "/", or timeout. 110 start := time.Now() 111 kv := c.KV() 112 for { 113 _, _, err := kv.List("/", nil) 114 if err == nil { 115 break 116 } 117 if time.Since(start) > 10*time.Second { 118 t.Fatalf("Failed to start consul daemon in time. Consul is returning error: %v", err) 119 } 120 time.Sleep(10 * time.Millisecond) 121 } 122 123 return cmd, configFilename, serverAddr 124 } 125 126 func TestConsulTopo(t *testing.T) { 127 // One test is going to wait that full period, so make it shorter. 128 watchPollDuration = 100 * time.Millisecond 129 130 // Start a single consul in the background. 131 cmd, configFilename, serverAddr := startConsul(t, "") 132 defer func() { 133 // Alerts command did not run successful 134 if err := cmd.Process.Kill(); err != nil { 135 log.Errorf("cmd process kill has an error: %v", err) 136 } 137 // Alerts command did not run successful 138 if err := cmd.Wait(); err != nil { 139 log.Errorf("cmd wait has an error: %v", err) 140 } 141 142 os.Remove(configFilename) 143 }() 144 145 // Run the TopoServerTestSuite tests. 146 testIndex := 0 147 test.TopoServerTestSuite(t, func() *topo.Server { 148 // Each test will use its own sub-directories. 149 testRoot := fmt.Sprintf("test-%v", testIndex) 150 testIndex++ 151 152 // Create the server on the new root. 153 ts, err := topo.OpenServer("consul", serverAddr, path.Join(testRoot, topo.GlobalCell)) 154 if err != nil { 155 t.Fatalf("OpenServer() failed: %v", err) 156 } 157 158 // Create the CellInfo. 159 if err := ts.CreateCellInfo(context.Background(), test.LocalCellName, &topodatapb.CellInfo{ 160 ServerAddress: serverAddr, 161 Root: path.Join(testRoot, test.LocalCellName), 162 }); err != nil { 163 t.Fatalf("CreateCellInfo() failed: %v", err) 164 } 165 166 return ts 167 }, []string{}) 168 } 169 170 func TestConsulTopoWithChecks(t *testing.T) { 171 // One test is going to wait that full period, so make it shorter. 172 watchPollDuration = 100 * time.Millisecond 173 consulLockSessionChecks = "serfHealth" 174 consulLockSessionTTL = "15s" 175 176 // Start a single consul in the background. 177 cmd, configFilename, serverAddr := startConsul(t, "") 178 defer func() { 179 // Alerts command did not run successful 180 if err := cmd.Process.Kill(); err != nil { 181 log.Errorf("cmd process kill has an error: %v", err) 182 } 183 // Alerts command did not run successful 184 if err := cmd.Wait(); err != nil { 185 log.Errorf("cmd wait has an error: %v", err) 186 } 187 188 os.Remove(configFilename) 189 }() 190 191 // Run the TopoServerTestSuite tests. 192 testIndex := 0 193 test.TopoServerTestSuite(t, func() *topo.Server { 194 // Each test will use its own sub-directories. 195 testRoot := fmt.Sprintf("test-%v", testIndex) 196 testIndex++ 197 198 // Create the server on the new root. 199 ts, err := topo.OpenServer("consul", serverAddr, path.Join(testRoot, topo.GlobalCell)) 200 if err != nil { 201 t.Fatalf("OpenServer() failed: %v", err) 202 } 203 204 // Create the CellInfo. 205 if err := ts.CreateCellInfo(context.Background(), test.LocalCellName, &topodatapb.CellInfo{ 206 ServerAddress: serverAddr, 207 Root: path.Join(testRoot, test.LocalCellName), 208 }); err != nil { 209 t.Fatalf("CreateCellInfo() failed: %v", err) 210 } 211 212 return ts 213 }, []string{}) 214 } 215 216 func TestConsulTopoWithAuth(t *testing.T) { 217 // One test is going to wait that full period, so make it shorter. 218 watchPollDuration = 100 * time.Millisecond 219 220 // Start a single consul in the background. 221 cmd, configFilename, serverAddr := startConsul(t, "123456") 222 defer func() { 223 // Alerts command did not run successful 224 if err := cmd.Process.Kill(); err != nil { 225 log.Errorf("cmd process kill has an error: %v", err) 226 } 227 // Alerts command did not run successful 228 if err := cmd.Wait(); err != nil { 229 log.Errorf("cmd process wait has an error: %v", err) 230 } 231 os.Remove(configFilename) 232 }() 233 234 // Run the TopoServerTestSuite tests. 235 testIndex := 0 236 tmpFile, err := os.CreateTemp("", "consul_auth_client_static_file.json") 237 238 if err != nil { 239 t.Fatalf("couldn't create temp file: %v", err) 240 } 241 defer os.Remove(tmpFile.Name()) 242 243 consulAuthClientStaticFile = tmpFile.Name() 244 245 jsonConfig := "{\"global\":{\"acl_token\":\"123456\"}, \"test\":{\"acl_token\":\"123456\"}}" 246 if err := os.WriteFile(tmpFile.Name(), []byte(jsonConfig), 0600); err != nil { 247 t.Fatalf("couldn't write temp file: %v", err) 248 } 249 250 test.TopoServerTestSuite(t, func() *topo.Server { 251 // Each test will use its own sub-directories. 252 testRoot := fmt.Sprintf("test-%v", testIndex) 253 testIndex++ 254 255 // Create the server on the new root. 256 ts, err := topo.OpenServer("consul", serverAddr, path.Join(testRoot, topo.GlobalCell)) 257 if err != nil { 258 t.Fatalf("OpenServer() failed: %v", err) 259 } 260 261 // Create the CellInfo. 262 if err := ts.CreateCellInfo(context.Background(), test.LocalCellName, &topodatapb.CellInfo{ 263 ServerAddress: serverAddr, 264 Root: path.Join(testRoot, test.LocalCellName), 265 }); err != nil { 266 t.Fatalf("CreateCellInfo() failed: %v", err) 267 } 268 269 return ts 270 }, []string{}) 271 } 272 273 func TestConsulTopoWithAuthFailure(t *testing.T) { 274 // One test is going to wait that full period, so make it shorter. 275 watchPollDuration = 100 * time.Millisecond 276 277 // Start a single consul in the background. 278 cmd, configFilename, serverAddr := startConsul(t, "123456") 279 defer func() { 280 cmd.Process.Kill() 281 cmd.Wait() 282 os.Remove(configFilename) 283 }() 284 285 tmpFile, err := os.CreateTemp("", "consul_auth_client_static_file.json") 286 287 if err != nil { 288 t.Fatalf("couldn't create temp file: %v", err) 289 } 290 defer os.Remove(tmpFile.Name()) 291 292 consulAuthClientStaticFile = tmpFile.Name() 293 294 jsonConfig := "{\"global\":{\"acl_token\":\"badtoken\"}}" 295 if err := os.WriteFile(tmpFile.Name(), []byte(jsonConfig), 0600); err != nil { 296 t.Fatalf("couldn't write temp file: %v", err) 297 } 298 299 // Create the server on the new root. 300 ts, err := topo.OpenServer("consul", serverAddr, path.Join("globalRoot", topo.GlobalCell)) 301 if err != nil { 302 t.Fatalf("OpenServer() failed: %v", err) 303 } 304 305 // Attempt to Create the CellInfo. 306 err = ts.CreateCellInfo(context.Background(), test.LocalCellName, &topodatapb.CellInfo{ 307 ServerAddress: serverAddr, 308 Root: path.Join("globalRoot", test.LocalCellName), 309 }) 310 311 want := "Failed request: ACL not found" 312 if err == nil || err.Error() != want { 313 t.Errorf("Expected CreateCellInfo to fail: got %v, want %s", err, want) 314 } 315 }