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  }