github.com/lfch/etcd-io/tests/v3@v3.0.0-20221004140520-eac99acd3e9d/functional/tester/cluster_read_config.go (about)

     1  // Copyright 2018 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 tester
    16  
    17  import (
    18  	"errors"
    19  	"fmt"
    20  	"net/url"
    21  	"os"
    22  	"path/filepath"
    23  	"strings"
    24  
    25  	"github.com/lfch/etcd-io/tests/v3/functional/rpcpb"
    26  
    27  	"go.uber.org/zap"
    28  	yaml "gopkg.in/yaml.v2"
    29  )
    30  
    31  func read(lg *zap.Logger, fpath string) (*Cluster, error) {
    32  	bts, err := os.ReadFile(fpath)
    33  	if err != nil {
    34  		return nil, err
    35  	}
    36  	lg.Info("opened configuration file", zap.String("path", fpath))
    37  
    38  	clus := &Cluster{lg: lg}
    39  	if err = yaml.Unmarshal(bts, clus); err != nil {
    40  		return nil, err
    41  	}
    42  
    43  	if len(clus.Members) < 3 {
    44  		return nil, fmt.Errorf("len(clus.Members) expects at least 3, got %d", len(clus.Members))
    45  	}
    46  
    47  	failpointsEnabled := false
    48  	for _, c := range clus.Tester.Cases {
    49  		if c == rpcpb.Case_FAILPOINTS.String() {
    50  			failpointsEnabled = true
    51  			break
    52  		}
    53  	}
    54  
    55  	if len(clus.Tester.Cases) == 0 {
    56  		return nil, errors.New("cases not found")
    57  	}
    58  	if clus.Tester.DelayLatencyMs <= clus.Tester.DelayLatencyMsRv*5 {
    59  		return nil, fmt.Errorf("delay latency %d ms must be greater than 5x of delay latency random variable %d ms", clus.Tester.DelayLatencyMs, clus.Tester.DelayLatencyMsRv)
    60  	}
    61  	if clus.Tester.UpdatedDelayLatencyMs == 0 {
    62  		clus.Tester.UpdatedDelayLatencyMs = clus.Tester.DelayLatencyMs
    63  	}
    64  
    65  	for _, v := range clus.Tester.Cases {
    66  		if _, ok := rpcpb.Case_value[v]; !ok {
    67  			return nil, fmt.Errorf("%q is not defined in 'rpcpb.Case_value'", v)
    68  		}
    69  	}
    70  
    71  	for _, s := range clus.Tester.Stressers {
    72  		if _, ok := rpcpb.StresserType_value[s.Type]; !ok {
    73  			return nil, fmt.Errorf("unknown 'StresserType' %+v", s)
    74  		}
    75  	}
    76  
    77  	for _, v := range clus.Tester.Checkers {
    78  		if _, ok := rpcpb.Checker_value[v]; !ok {
    79  			return nil, fmt.Errorf("Checker is unknown; got %q", v)
    80  		}
    81  	}
    82  
    83  	if clus.Tester.StressKeySuffixRangeTxn > 100 {
    84  		return nil, fmt.Errorf("StressKeySuffixRangeTxn maximum value is 100, got %v", clus.Tester.StressKeySuffixRangeTxn)
    85  	}
    86  	if clus.Tester.StressKeyTxnOps > 64 {
    87  		return nil, fmt.Errorf("StressKeyTxnOps maximum value is 64, got %v", clus.Tester.StressKeyTxnOps)
    88  	}
    89  
    90  	for i, mem := range clus.Members {
    91  		if mem.EtcdExec == "embed" && failpointsEnabled {
    92  			return nil, errors.New("EtcdExec 'embed' cannot be run with failpoints enabled")
    93  		}
    94  		if mem.BaseDir == "" {
    95  			return nil, fmt.Errorf("BaseDir cannot be empty (got %q)", mem.BaseDir)
    96  		}
    97  		if mem.Etcd.Name == "" {
    98  			return nil, fmt.Errorf("'--name' cannot be empty (got %+v)", mem)
    99  		}
   100  		if mem.Etcd.DataDir == "" {
   101  			return nil, fmt.Errorf("'--data-dir' cannot be empty (got %+v)", mem)
   102  		}
   103  		if mem.Etcd.SnapshotCount == 0 {
   104  			return nil, fmt.Errorf("'--snapshot-count' cannot be 0 (got %+v)", mem.Etcd.SnapshotCount)
   105  		}
   106  		if mem.Etcd.DataDir == "" {
   107  			return nil, fmt.Errorf("'--data-dir' cannot be empty (got %q)", mem.Etcd.DataDir)
   108  		}
   109  		if mem.Etcd.WALDir == "" {
   110  			clus.Members[i].Etcd.WALDir = filepath.Join(mem.Etcd.DataDir, "member", "wal")
   111  		}
   112  
   113  		switch mem.Etcd.InitialClusterState {
   114  		case "new":
   115  		case "existing":
   116  		default:
   117  			return nil, fmt.Errorf("'--initial-cluster-state' got %q", mem.Etcd.InitialClusterState)
   118  		}
   119  
   120  		if mem.Etcd.HeartbeatIntervalMs == 0 {
   121  			return nil, fmt.Errorf("'--heartbeat-interval' cannot be 0 (got %+v)", mem.Etcd)
   122  		}
   123  		if mem.Etcd.ElectionTimeoutMs == 0 {
   124  			return nil, fmt.Errorf("'--election-timeout' cannot be 0 (got %+v)", mem.Etcd)
   125  		}
   126  		if int64(clus.Tester.DelayLatencyMs) <= mem.Etcd.ElectionTimeoutMs {
   127  			return nil, fmt.Errorf("delay latency %d ms must be greater than election timeout %d ms", clus.Tester.DelayLatencyMs, mem.Etcd.ElectionTimeoutMs)
   128  		}
   129  
   130  		port := ""
   131  		listenClientPorts := make([]string, len(clus.Members))
   132  		for i, u := range mem.Etcd.ListenClientURLs {
   133  			if !isValidURL(u) {
   134  				return nil, fmt.Errorf("'--listen-client-urls' has valid URL %q", u)
   135  			}
   136  			listenClientPorts[i], err = getPort(u)
   137  			if err != nil {
   138  				return nil, fmt.Errorf("'--listen-client-urls' has no port %q", u)
   139  			}
   140  		}
   141  		for i, u := range mem.Etcd.AdvertiseClientURLs {
   142  			if !isValidURL(u) {
   143  				return nil, fmt.Errorf("'--advertise-client-urls' has valid URL %q", u)
   144  			}
   145  			port, err = getPort(u)
   146  			if err != nil {
   147  				return nil, fmt.Errorf("'--advertise-client-urls' has no port %q", u)
   148  			}
   149  			if mem.EtcdClientProxy && listenClientPorts[i] == port {
   150  				return nil, fmt.Errorf("clus.Members[%d] requires client port proxy, but advertise port %q conflicts with listener port %q", i, port, listenClientPorts[i])
   151  			}
   152  		}
   153  
   154  		listenPeerPorts := make([]string, len(clus.Members))
   155  		for i, u := range mem.Etcd.ListenPeerURLs {
   156  			if !isValidURL(u) {
   157  				return nil, fmt.Errorf("'--listen-peer-urls' has valid URL %q", u)
   158  			}
   159  			listenPeerPorts[i], err = getPort(u)
   160  			if err != nil {
   161  				return nil, fmt.Errorf("'--listen-peer-urls' has no port %q", u)
   162  			}
   163  		}
   164  		for j, u := range mem.Etcd.AdvertisePeerURLs {
   165  			if !isValidURL(u) {
   166  				return nil, fmt.Errorf("'--initial-advertise-peer-urls' has valid URL %q", u)
   167  			}
   168  			port, err = getPort(u)
   169  			if err != nil {
   170  				return nil, fmt.Errorf("'--initial-advertise-peer-urls' has no port %q", u)
   171  			}
   172  			if mem.EtcdPeerProxy && listenPeerPorts[j] == port {
   173  				return nil, fmt.Errorf("clus.Members[%d] requires peer port proxy, but advertise port %q conflicts with listener port %q", i, port, listenPeerPorts[j])
   174  			}
   175  		}
   176  
   177  		if !strings.HasPrefix(mem.Etcd.DataDir, mem.BaseDir) {
   178  			return nil, fmt.Errorf("Etcd.DataDir must be prefixed with BaseDir (got %q)", mem.Etcd.DataDir)
   179  		}
   180  
   181  		// TODO: support separate WALDir that can be handled via failure-archive
   182  		if !strings.HasPrefix(mem.Etcd.WALDir, mem.BaseDir) {
   183  			return nil, fmt.Errorf("Etcd.WALDir must be prefixed with BaseDir (got %q)", mem.Etcd.WALDir)
   184  		}
   185  
   186  		// TODO: only support generated certs with TLS generator
   187  		// deprecate auto TLS
   188  		if mem.Etcd.PeerAutoTLS && mem.Etcd.PeerCertFile != "" {
   189  			return nil, fmt.Errorf("Etcd.PeerAutoTLS 'true', but Etcd.PeerCertFile is %q", mem.Etcd.PeerCertFile)
   190  		}
   191  		if mem.Etcd.PeerAutoTLS && mem.Etcd.PeerKeyFile != "" {
   192  			return nil, fmt.Errorf("Etcd.PeerAutoTLS 'true', but Etcd.PeerKeyFile is %q", mem.Etcd.PeerKeyFile)
   193  		}
   194  		if mem.Etcd.PeerAutoTLS && mem.Etcd.PeerTrustedCAFile != "" {
   195  			return nil, fmt.Errorf("Etcd.PeerAutoTLS 'true', but Etcd.PeerTrustedCAFile is %q", mem.Etcd.PeerTrustedCAFile)
   196  		}
   197  		if mem.Etcd.ClientAutoTLS && mem.Etcd.ClientCertFile != "" {
   198  			return nil, fmt.Errorf("Etcd.ClientAutoTLS 'true', but Etcd.ClientCertFile is %q", mem.Etcd.ClientCertFile)
   199  		}
   200  		if mem.Etcd.ClientAutoTLS && mem.Etcd.ClientKeyFile != "" {
   201  			return nil, fmt.Errorf("Etcd.ClientAutoTLS 'true', but Etcd.ClientKeyFile is %q", mem.Etcd.ClientKeyFile)
   202  		}
   203  		if mem.Etcd.ClientAutoTLS && mem.Etcd.ClientTrustedCAFile != "" {
   204  			return nil, fmt.Errorf("Etcd.ClientAutoTLS 'true', but Etcd.ClientTrustedCAFile is %q", mem.Etcd.ClientTrustedCAFile)
   205  		}
   206  
   207  		if mem.Etcd.PeerClientCertAuth && mem.Etcd.PeerCertFile == "" {
   208  			return nil, fmt.Errorf("Etcd.PeerClientCertAuth 'true', but Etcd.PeerCertFile is %q", mem.Etcd.PeerCertFile)
   209  		}
   210  		if mem.Etcd.PeerClientCertAuth && mem.Etcd.PeerKeyFile == "" {
   211  			return nil, fmt.Errorf("Etcd.PeerClientCertAuth 'true', but Etcd.PeerKeyFile is %q", mem.Etcd.PeerCertFile)
   212  		}
   213  		// only support self-signed certs
   214  		if mem.Etcd.PeerClientCertAuth && mem.Etcd.PeerTrustedCAFile == "" {
   215  			return nil, fmt.Errorf("Etcd.PeerClientCertAuth 'true', but Etcd.PeerTrustedCAFile is %q", mem.Etcd.PeerCertFile)
   216  		}
   217  		if !mem.Etcd.PeerClientCertAuth && mem.Etcd.PeerCertFile != "" {
   218  			return nil, fmt.Errorf("Etcd.PeerClientCertAuth 'false', but Etcd.PeerCertFile is %q", mem.Etcd.PeerCertFile)
   219  		}
   220  		if !mem.Etcd.PeerClientCertAuth && mem.Etcd.PeerKeyFile != "" {
   221  			return nil, fmt.Errorf("Etcd.PeerClientCertAuth 'false', but Etcd.PeerKeyFile is %q", mem.Etcd.PeerCertFile)
   222  		}
   223  		if !mem.Etcd.PeerClientCertAuth && mem.Etcd.PeerTrustedCAFile != "" {
   224  			return nil, fmt.Errorf("Etcd.PeerClientCertAuth 'false', but Etcd.PeerTrustedCAFile is %q", mem.Etcd.PeerTrustedCAFile)
   225  		}
   226  		if mem.Etcd.PeerClientCertAuth && mem.Etcd.PeerAutoTLS {
   227  			return nil, fmt.Errorf("Etcd.PeerClientCertAuth and Etcd.PeerAutoTLS cannot be both 'true'")
   228  		}
   229  		if (mem.Etcd.PeerCertFile == "") != (mem.Etcd.PeerKeyFile == "") {
   230  			return nil, fmt.Errorf("both Etcd.PeerCertFile %q and Etcd.PeerKeyFile %q must be either empty or non-empty", mem.Etcd.PeerCertFile, mem.Etcd.PeerKeyFile)
   231  		}
   232  		if mem.Etcd.ClientCertAuth && mem.Etcd.ClientAutoTLS {
   233  			return nil, fmt.Errorf("Etcd.ClientCertAuth and Etcd.ClientAutoTLS cannot be both 'true'")
   234  		}
   235  		if mem.Etcd.ClientCertAuth && mem.Etcd.ClientCertFile == "" {
   236  			return nil, fmt.Errorf("Etcd.ClientCertAuth 'true', but Etcd.ClientCertFile is %q", mem.Etcd.PeerCertFile)
   237  		}
   238  		if mem.Etcd.ClientCertAuth && mem.Etcd.ClientKeyFile == "" {
   239  			return nil, fmt.Errorf("Etcd.ClientCertAuth 'true', but Etcd.ClientKeyFile is %q", mem.Etcd.PeerCertFile)
   240  		}
   241  		if mem.Etcd.ClientCertAuth && mem.Etcd.ClientTrustedCAFile == "" {
   242  			return nil, fmt.Errorf("Etcd.ClientCertAuth 'true', but Etcd.ClientTrustedCAFile is %q", mem.Etcd.ClientTrustedCAFile)
   243  		}
   244  		if !mem.Etcd.ClientCertAuth && mem.Etcd.ClientCertFile != "" {
   245  			return nil, fmt.Errorf("Etcd.ClientCertAuth 'false', but Etcd.ClientCertFile is %q", mem.Etcd.PeerCertFile)
   246  		}
   247  		if !mem.Etcd.ClientCertAuth && mem.Etcd.ClientKeyFile != "" {
   248  			return nil, fmt.Errorf("Etcd.ClientCertAuth 'false', but Etcd.ClientKeyFile is %q", mem.Etcd.PeerCertFile)
   249  		}
   250  		if !mem.Etcd.ClientCertAuth && mem.Etcd.ClientTrustedCAFile != "" {
   251  			return nil, fmt.Errorf("Etcd.ClientCertAuth 'false', but Etcd.ClientTrustedCAFile is %q", mem.Etcd.PeerCertFile)
   252  		}
   253  		if (mem.Etcd.ClientCertFile == "") != (mem.Etcd.ClientKeyFile == "") {
   254  			return nil, fmt.Errorf("both Etcd.ClientCertFile %q and Etcd.ClientKeyFile %q must be either empty or non-empty", mem.Etcd.ClientCertFile, mem.Etcd.ClientKeyFile)
   255  		}
   256  
   257  		peerTLS := mem.Etcd.PeerAutoTLS ||
   258  			(mem.Etcd.PeerClientCertAuth && mem.Etcd.PeerCertFile != "" && mem.Etcd.PeerKeyFile != "" && mem.Etcd.PeerTrustedCAFile != "")
   259  		if peerTLS {
   260  			for _, cu := range mem.Etcd.ListenPeerURLs {
   261  				var u *url.URL
   262  				u, err = url.Parse(cu)
   263  				if err != nil {
   264  					return nil, err
   265  				}
   266  				if u.Scheme != "https" { // TODO: support unix
   267  					return nil, fmt.Errorf("peer TLS is enabled with wrong scheme %q", cu)
   268  				}
   269  			}
   270  			for _, cu := range mem.Etcd.AdvertisePeerURLs {
   271  				var u *url.URL
   272  				u, err = url.Parse(cu)
   273  				if err != nil {
   274  					return nil, err
   275  				}
   276  				if u.Scheme != "https" { // TODO: support unix
   277  					return nil, fmt.Errorf("peer TLS is enabled with wrong scheme %q", cu)
   278  				}
   279  			}
   280  			clus.Members[i].PeerCertPath = mem.Etcd.PeerCertFile
   281  			if mem.Etcd.PeerCertFile != "" {
   282  				var data []byte
   283  				data, err = os.ReadFile(mem.Etcd.PeerCertFile)
   284  				if err != nil {
   285  					return nil, fmt.Errorf("failed to read %q (%v)", mem.Etcd.PeerCertFile, err)
   286  				}
   287  				clus.Members[i].PeerCertData = string(data)
   288  			}
   289  			clus.Members[i].PeerKeyPath = mem.Etcd.PeerKeyFile
   290  			if mem.Etcd.PeerKeyFile != "" {
   291  				var data []byte
   292  				data, err = os.ReadFile(mem.Etcd.PeerKeyFile)
   293  				if err != nil {
   294  					return nil, fmt.Errorf("failed to read %q (%v)", mem.Etcd.PeerKeyFile, err)
   295  				}
   296  				clus.Members[i].PeerCertData = string(data)
   297  			}
   298  			clus.Members[i].PeerTrustedCAPath = mem.Etcd.PeerTrustedCAFile
   299  			if mem.Etcd.PeerTrustedCAFile != "" {
   300  				var data []byte
   301  				data, err = os.ReadFile(mem.Etcd.PeerTrustedCAFile)
   302  				if err != nil {
   303  					return nil, fmt.Errorf("failed to read %q (%v)", mem.Etcd.PeerTrustedCAFile, err)
   304  				}
   305  				clus.Members[i].PeerCertData = string(data)
   306  			}
   307  		}
   308  
   309  		clientTLS := mem.Etcd.ClientAutoTLS ||
   310  			(mem.Etcd.ClientCertAuth && mem.Etcd.ClientCertFile != "" && mem.Etcd.ClientKeyFile != "" && mem.Etcd.ClientTrustedCAFile != "")
   311  		if clientTLS {
   312  			for _, cu := range mem.Etcd.ListenClientURLs {
   313  				var u *url.URL
   314  				u, err = url.Parse(cu)
   315  				if err != nil {
   316  					return nil, err
   317  				}
   318  				if u.Scheme != "https" { // TODO: support unix
   319  					return nil, fmt.Errorf("client TLS is enabled with wrong scheme %q", cu)
   320  				}
   321  			}
   322  			for _, cu := range mem.Etcd.AdvertiseClientURLs {
   323  				var u *url.URL
   324  				u, err = url.Parse(cu)
   325  				if err != nil {
   326  					return nil, err
   327  				}
   328  				if u.Scheme != "https" { // TODO: support unix
   329  					return nil, fmt.Errorf("client TLS is enabled with wrong scheme %q", cu)
   330  				}
   331  			}
   332  			clus.Members[i].ClientCertPath = mem.Etcd.ClientCertFile
   333  			if mem.Etcd.ClientCertFile != "" {
   334  				var data []byte
   335  				data, err = os.ReadFile(mem.Etcd.ClientCertFile)
   336  				if err != nil {
   337  					return nil, fmt.Errorf("failed to read %q (%v)", mem.Etcd.ClientCertFile, err)
   338  				}
   339  				clus.Members[i].ClientCertData = string(data)
   340  			}
   341  			clus.Members[i].ClientKeyPath = mem.Etcd.ClientKeyFile
   342  			if mem.Etcd.ClientKeyFile != "" {
   343  				var data []byte
   344  				data, err = os.ReadFile(mem.Etcd.ClientKeyFile)
   345  				if err != nil {
   346  					return nil, fmt.Errorf("failed to read %q (%v)", mem.Etcd.ClientKeyFile, err)
   347  				}
   348  				clus.Members[i].ClientCertData = string(data)
   349  			}
   350  			clus.Members[i].ClientTrustedCAPath = mem.Etcd.ClientTrustedCAFile
   351  			if mem.Etcd.ClientTrustedCAFile != "" {
   352  				var data []byte
   353  				data, err = os.ReadFile(mem.Etcd.ClientTrustedCAFile)
   354  				if err != nil {
   355  					return nil, fmt.Errorf("failed to read %q (%v)", mem.Etcd.ClientTrustedCAFile, err)
   356  				}
   357  				clus.Members[i].ClientCertData = string(data)
   358  			}
   359  
   360  			if len(mem.Etcd.LogOutputs) == 0 {
   361  				return nil, fmt.Errorf("mem.Etcd.LogOutputs cannot be empty")
   362  			}
   363  			for _, v := range mem.Etcd.LogOutputs {
   364  				switch v {
   365  				case "stderr", "stdout", "/dev/null", "default":
   366  				default:
   367  					if !strings.HasPrefix(v, mem.BaseDir) {
   368  						return nil, fmt.Errorf("LogOutput %q must be prefixed with BaseDir %q", v, mem.BaseDir)
   369  					}
   370  				}
   371  			}
   372  		}
   373  	}
   374  
   375  	return clus, err
   376  }