go.etcd.io/etcd@v3.3.27+incompatible/tests/e2e/ctl_v3_test.go (about)

     1  // Copyright 2016 The etcd Authors
     2  //
     3  // Licensed under the Apache License, Version 2.0 (the "License");
     4  // you may not use this file except in compliance with the License.
     5  // You may obtain a copy of the License at
     6  //
     7  //     http://www.apache.org/licenses/LICENSE-2.0
     8  //
     9  // Unless required by applicable law or agreed to in writing, software
    10  // distributed under the License is distributed on an "AS IS" BASIS,
    11  // WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
    12  // See the License for the specific language governing permissions and
    13  // limitations under the License.
    14  
    15  package e2e
    16  
    17  import (
    18  	"fmt"
    19  	"os"
    20  	"strings"
    21  	"testing"
    22  	"time"
    23  
    24  	"github.com/coreos/etcd/pkg/flags"
    25  	"github.com/coreos/etcd/pkg/testutil"
    26  	"github.com/coreos/etcd/version"
    27  )
    28  
    29  func TestCtlV3Version(t *testing.T) { testCtl(t, versionTest) }
    30  
    31  func versionTest(cx ctlCtx) {
    32  	if err := ctlV3Version(cx); err != nil {
    33  		cx.t.Fatalf("versionTest ctlV3Version error (%v)", err)
    34  	}
    35  }
    36  
    37  func ctlV3Version(cx ctlCtx) error {
    38  	cmdArgs := append(cx.PrefixArgs(), "version")
    39  	return spawnWithExpect(cmdArgs, version.Version)
    40  }
    41  
    42  // TestCtlV3DialWithHTTPScheme ensures that client handles endpoints with HTTPS scheme.
    43  func TestCtlV3DialWithHTTPScheme(t *testing.T) {
    44  	testCtl(t, dialWithSchemeTest, withCfg(configClientTLS))
    45  }
    46  
    47  func dialWithSchemeTest(cx ctlCtx) {
    48  	cmdArgs := append(cx.prefixArgs(cx.epc.EndpointsV3()), "put", "foo", "bar")
    49  	if err := spawnWithExpect(cmdArgs, "OK"); err != nil {
    50  		cx.t.Fatal(err)
    51  	}
    52  }
    53  
    54  type ctlCtx struct {
    55  	t                 *testing.T
    56  	apiPrefix         string
    57  	cfg               etcdProcessClusterConfig
    58  	quotaBackendBytes int64
    59  	corruptFunc       func(string) error
    60  	noStrictReconfig  bool
    61  
    62  	epc *etcdProcessCluster
    63  
    64  	envMap map[string]struct{}
    65  
    66  	dialTimeout time.Duration
    67  
    68  	quorum      bool // if true, set up 3-node cluster and linearizable read
    69  	interactive bool
    70  
    71  	user string
    72  	pass string
    73  
    74  	initialCorruptCheck bool
    75  
    76  	// for compaction
    77  	compactPhysical bool
    78  }
    79  
    80  type ctlOption func(*ctlCtx)
    81  
    82  func (cx *ctlCtx) applyOpts(opts []ctlOption) {
    83  	for _, opt := range opts {
    84  		opt(cx)
    85  	}
    86  	cx.initialCorruptCheck = true
    87  }
    88  
    89  func withCfg(cfg etcdProcessClusterConfig) ctlOption {
    90  	return func(cx *ctlCtx) { cx.cfg = cfg }
    91  }
    92  
    93  func withDialTimeout(timeout time.Duration) ctlOption {
    94  	return func(cx *ctlCtx) { cx.dialTimeout = timeout }
    95  }
    96  
    97  func withQuorum() ctlOption {
    98  	return func(cx *ctlCtx) { cx.quorum = true }
    99  }
   100  
   101  func withInteractive() ctlOption {
   102  	return func(cx *ctlCtx) { cx.interactive = true }
   103  }
   104  
   105  func withQuota(b int64) ctlOption {
   106  	return func(cx *ctlCtx) { cx.quotaBackendBytes = b }
   107  }
   108  
   109  func withCompactPhysical() ctlOption {
   110  	return func(cx *ctlCtx) { cx.compactPhysical = true }
   111  }
   112  
   113  func withInitialCorruptCheck() ctlOption {
   114  	return func(cx *ctlCtx) { cx.initialCorruptCheck = true }
   115  }
   116  
   117  func withCorruptFunc(f func(string) error) ctlOption {
   118  	return func(cx *ctlCtx) { cx.corruptFunc = f }
   119  }
   120  
   121  func withNoStrictReconfig() ctlOption {
   122  	return func(cx *ctlCtx) { cx.noStrictReconfig = true }
   123  }
   124  
   125  func withApiPrefix(p string) ctlOption {
   126  	return func(cx *ctlCtx) { cx.apiPrefix = p }
   127  }
   128  
   129  func withFlagByEnv() ctlOption {
   130  	return func(cx *ctlCtx) { cx.envMap = make(map[string]struct{}) }
   131  }
   132  
   133  func testCtl(t *testing.T, testFunc func(ctlCtx), opts ...ctlOption) {
   134  	defer testutil.AfterTest(t)
   135  
   136  	ret := ctlCtx{
   137  		t:           t,
   138  		cfg:         configAutoTLS,
   139  		dialTimeout: 7 * time.Second,
   140  	}
   141  	ret.applyOpts(opts)
   142  
   143  	mustEtcdctl(t)
   144  	if !ret.quorum {
   145  		ret.cfg = *configStandalone(ret.cfg)
   146  	}
   147  	if ret.quotaBackendBytes > 0 {
   148  		ret.cfg.quotaBackendBytes = ret.quotaBackendBytes
   149  	}
   150  	ret.cfg.noStrictReconfig = ret.noStrictReconfig
   151  	if ret.initialCorruptCheck {
   152  		ret.cfg.initialCorruptCheck = ret.initialCorruptCheck
   153  	}
   154  
   155  	epc, err := newEtcdProcessCluster(&ret.cfg)
   156  	if err != nil {
   157  		t.Fatalf("could not start etcd process cluster (%v)", err)
   158  	}
   159  	ret.epc = epc
   160  
   161  	defer func() {
   162  		if ret.envMap != nil {
   163  			for k := range ret.envMap {
   164  				os.Unsetenv(k)
   165  			}
   166  		}
   167  		if errC := ret.epc.Close(); errC != nil {
   168  			t.Fatalf("error closing etcd processes (%v)", errC)
   169  		}
   170  	}()
   171  
   172  	donec := make(chan struct{})
   173  	go func() {
   174  		defer close(donec)
   175  		testFunc(ret)
   176  	}()
   177  
   178  	timeout := 2*ret.dialTimeout + time.Second
   179  	if ret.dialTimeout == 0 {
   180  		timeout = 30 * time.Second
   181  	}
   182  	select {
   183  	case <-time.After(timeout):
   184  		testutil.FatalStack(t, fmt.Sprintf("test timed out after %v", timeout))
   185  	case <-donec:
   186  	}
   187  }
   188  
   189  func (cx *ctlCtx) prefixArgs(eps []string) []string {
   190  	fmap := make(map[string]string)
   191  	fmap["endpoints"] = strings.Join(eps, ",")
   192  	fmap["dial-timeout"] = cx.dialTimeout.String()
   193  	if cx.epc.cfg.clientTLS == clientTLS {
   194  		if cx.epc.cfg.isClientAutoTLS {
   195  			fmap["insecure-transport"] = "false"
   196  			fmap["insecure-skip-tls-verify"] = "true"
   197  		} else if cx.epc.cfg.isClientCRL {
   198  			fmap["cacert"] = caPath
   199  			fmap["cert"] = revokedCertPath
   200  			fmap["key"] = revokedPrivateKeyPath
   201  		} else {
   202  			fmap["cacert"] = caPath
   203  			fmap["cert"] = certPath
   204  			fmap["key"] = privateKeyPath
   205  		}
   206  	}
   207  	if cx.user != "" {
   208  		fmap["user"] = cx.user + ":" + cx.pass
   209  	}
   210  
   211  	useEnv := cx.envMap != nil
   212  
   213  	cmdArgs := []string{ctlBinPath + "3"}
   214  	for k, v := range fmap {
   215  		if useEnv {
   216  			ek := flags.FlagToEnv("ETCDCTL", k)
   217  			os.Setenv(ek, v)
   218  			cx.envMap[ek] = struct{}{}
   219  		} else {
   220  			cmdArgs = append(cmdArgs, fmt.Sprintf("--%s=%s", k, v))
   221  		}
   222  	}
   223  	return cmdArgs
   224  }
   225  
   226  // PrefixArgs prefixes etcdctl command.
   227  // Make sure to unset environment variables after tests.
   228  func (cx *ctlCtx) PrefixArgs() []string {
   229  	return cx.prefixArgs(cx.epc.EndpointsV3())
   230  }
   231  
   232  func isGRPCTimedout(err error) bool {
   233  	return strings.Contains(err.Error(), "grpc: timed out trying to connect")
   234  }
   235  
   236  func (cx *ctlCtx) memberToRemove() (ep string, memberID string, clusterID string) {
   237  	n1 := cx.cfg.clusterSize
   238  	if n1 < 2 {
   239  		cx.t.Fatalf("%d-node is too small to test 'member remove'", n1)
   240  	}
   241  
   242  	resp, err := getMemberList(*cx)
   243  	if err != nil {
   244  		cx.t.Fatal(err)
   245  	}
   246  	if n1 != len(resp.Members) {
   247  		cx.t.Fatalf("expected %d, got %d", n1, len(resp.Members))
   248  	}
   249  
   250  	ep = resp.Members[0].ClientURLs[0]
   251  	clusterID = fmt.Sprintf("%x", resp.Header.ClusterId)
   252  	memberID = fmt.Sprintf("%x", resp.Members[1].ID)
   253  
   254  	return ep, memberID, clusterID
   255  }