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 }