github.com/lfch/etcd-io/tests/v3@v3.0.0-20221004140520-eac99acd3e9d/framework/e2e/cluster_proxy.go (about) 1 // Copyright 2017 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 //go:build cluster_proxy 16 // +build cluster_proxy 17 18 package e2e 19 20 import ( 21 "context" 22 "fmt" 23 "net" 24 "net/url" 25 "path" 26 "strconv" 27 "strings" 28 29 "go.uber.org/zap" 30 31 "github.com/lfch/etcd-io/pkg/v3/expect" 32 ) 33 34 type proxyEtcdProcess struct { 35 etcdProc EtcdProcess 36 // TODO(ahrtr): We need to remove `proxyV2` and v2discovery when the v2client is removed. 37 proxyV2 *proxyV2Proc 38 proxyV3 *proxyV3Proc 39 } 40 41 func NewEtcdProcess(cfg *EtcdServerProcessConfig) (EtcdProcess, error) { 42 return NewProxyEtcdProcess(cfg) 43 } 44 45 func NewProxyEtcdProcess(cfg *EtcdServerProcessConfig) (*proxyEtcdProcess, error) { 46 ep, err := NewEtcdServerProcess(cfg) 47 if err != nil { 48 return nil, err 49 } 50 pep := &proxyEtcdProcess{ 51 etcdProc: ep, 52 proxyV2: newProxyV2Proc(cfg), 53 proxyV3: newProxyV3Proc(cfg), 54 } 55 return pep, nil 56 } 57 58 func (p *proxyEtcdProcess) Config() *EtcdServerProcessConfig { return p.etcdProc.Config() } 59 60 func (p *proxyEtcdProcess) EndpointsV2() []string { return p.proxyV2.endpoints() } 61 func (p *proxyEtcdProcess) EndpointsV3() []string { return p.proxyV3.endpoints() } 62 func (p *proxyEtcdProcess) EndpointsMetrics() []string { 63 panic("not implemented; proxy doesn't provide health information") 64 } 65 66 func (p *proxyEtcdProcess) Start(ctx context.Context) error { 67 if err := p.etcdProc.Start(ctx); err != nil { 68 return err 69 } 70 return p.proxyV3.Start(ctx) 71 } 72 73 func (p *proxyEtcdProcess) Restart(ctx context.Context) error { 74 if err := p.etcdProc.Restart(ctx); err != nil { 75 return err 76 } 77 return p.proxyV3.Restart(ctx) 78 } 79 80 func (p *proxyEtcdProcess) Stop() error { 81 err := p.proxyV3.Stop() 82 if eerr := p.etcdProc.Stop(); eerr != nil && err == nil { 83 // fails on go-grpc issue #1384 84 if !strings.Contains(eerr.Error(), "exit status 2") { 85 err = eerr 86 } 87 } 88 return err 89 } 90 91 func (p *proxyEtcdProcess) Close() error { 92 err := p.proxyV3.Close() 93 if eerr := p.etcdProc.Close(); eerr != nil && err == nil { 94 // fails on go-grpc issue #1384 95 if !strings.Contains(eerr.Error(), "exit status 2") { 96 err = eerr 97 } 98 } 99 return err 100 } 101 102 func (p *proxyEtcdProcess) Logs() LogsExpect { 103 return p.etcdProc.Logs() 104 } 105 106 type proxyProc struct { 107 lg *zap.Logger 108 name string 109 execPath string 110 args []string 111 ep string 112 murl string 113 donec chan struct{} 114 115 proc *expect.ExpectProcess 116 } 117 118 func (pp *proxyProc) endpoints() []string { return []string{pp.ep} } 119 120 func (pp *proxyProc) start() error { 121 if pp.proc != nil { 122 panic("already started") 123 } 124 proc, err := SpawnCmdWithLogger(pp.lg, append([]string{pp.execPath}, pp.args...), nil, pp.name) 125 if err != nil { 126 return err 127 } 128 pp.proc = proc 129 return nil 130 } 131 132 func (pp *proxyProc) waitReady(ctx context.Context, readyStr string) error { 133 defer close(pp.donec) 134 return WaitReadyExpectProc(ctx, pp.proc, []string{readyStr}) 135 } 136 137 func (pp *proxyProc) Stop() error { 138 if pp.proc == nil { 139 return nil 140 } 141 if err := pp.proc.Stop(); err != nil && !strings.Contains(err.Error(), "exit status 1") { 142 // v2proxy exits with status 1 on auto tls; not sure why 143 return err 144 } 145 pp.proc = nil 146 <-pp.donec 147 pp.donec = make(chan struct{}) 148 return nil 149 } 150 151 func (pp *proxyProc) Close() error { return pp.Stop() } 152 153 type proxyV2Proc struct { 154 proxyProc 155 dataDir string 156 } 157 158 func proxyListenURL(cfg *EtcdServerProcessConfig, portOffset int) string { 159 u, err := url.Parse(cfg.Acurl) 160 if err != nil { 161 panic(err) 162 } 163 host, port, _ := net.SplitHostPort(u.Host) 164 p, _ := strconv.ParseInt(port, 10, 16) 165 u.Host = fmt.Sprintf("%s:%d", host, int(p)+portOffset) 166 return u.String() 167 } 168 169 func newProxyV2Proc(cfg *EtcdServerProcessConfig) *proxyV2Proc { 170 listenAddr := proxyListenURL(cfg, 2) 171 name := fmt.Sprintf("testname-proxy-%p", cfg) 172 dataDir := path.Join(cfg.DataDirPath, name+".etcd") 173 args := []string{ 174 "--name", name, 175 "--proxy", "on", 176 "--listen-client-urls", listenAddr, 177 "--initial-cluster", cfg.Name + "=" + cfg.Purl.String(), 178 "--data-dir", dataDir, 179 } 180 return &proxyV2Proc{ 181 proxyProc: proxyProc{ 182 name: cfg.Name, 183 lg: cfg.lg, 184 execPath: cfg.ExecPath, 185 args: append(args, cfg.TlsArgs...), 186 ep: listenAddr, 187 donec: make(chan struct{}), 188 }, 189 dataDir: dataDir, 190 } 191 } 192 193 type proxyV3Proc struct { 194 proxyProc 195 } 196 197 func newProxyV3Proc(cfg *EtcdServerProcessConfig) *proxyV3Proc { 198 listenAddr := proxyListenURL(cfg, 3) 199 args := []string{ 200 "grpc-proxy", 201 "start", 202 "--listen-addr", strings.Split(listenAddr, "/")[2], 203 "--endpoints", cfg.Acurl, 204 // pass-through member RPCs 205 "--advertise-client-url", "", 206 "--data-dir", cfg.DataDirPath, 207 } 208 murl := "" 209 if cfg.Murl != "" { 210 murl = proxyListenURL(cfg, 4) 211 args = append(args, "--metrics-addr", murl) 212 } 213 tlsArgs := []string{} 214 for i := 0; i < len(cfg.TlsArgs); i++ { 215 switch cfg.TlsArgs[i] { 216 case "--cert-file": 217 tlsArgs = append(tlsArgs, "--cert-file", cfg.TlsArgs[i+1]) 218 i++ 219 case "--key-file": 220 tlsArgs = append(tlsArgs, "--key-file", cfg.TlsArgs[i+1]) 221 i++ 222 case "--trusted-ca-file": 223 tlsArgs = append(tlsArgs, "--trusted-ca-file", cfg.TlsArgs[i+1]) 224 i++ 225 case "--auto-tls": 226 tlsArgs = append(tlsArgs, "--auto-tls", "--insecure-skip-tls-verify") 227 case "--peer-trusted-ca-file", "--peer-cert-file", "--peer-key-file": 228 i++ // skip arg 229 case "--client-cert-auth", "--peer-auto-tls": 230 default: 231 tlsArgs = append(tlsArgs, cfg.TlsArgs[i]) 232 } 233 } 234 if len(cfg.TlsArgs) > 0 { 235 // Configure certificates for connection proxy ---> server. 236 // This certificate must NOT have CN set. 237 tlsArgs = append(tlsArgs, 238 "--cert", path.Join(FixturesDir, "client-nocn.crt"), 239 "--key", path.Join(FixturesDir, "client-nocn.key.insecure"), 240 "--cacert", path.Join(FixturesDir, "ca.crt"), 241 "--client-crl-file", path.Join(FixturesDir, "revoke.crl")) 242 } 243 244 return &proxyV3Proc{ 245 proxyProc{ 246 name: cfg.Name, 247 lg: cfg.lg, 248 execPath: cfg.ExecPath, 249 args: append(args, tlsArgs...), 250 ep: listenAddr, 251 murl: murl, 252 donec: make(chan struct{}), 253 }, 254 } 255 } 256 257 func (v3p *proxyV3Proc) Restart(ctx context.Context) error { 258 if err := v3p.Stop(); err != nil { 259 return err 260 } 261 return v3p.Start(ctx) 262 } 263 264 func (v3p *proxyV3Proc) Start(ctx context.Context) error { 265 if err := v3p.start(); err != nil { 266 return err 267 } 268 return v3p.waitReady(ctx, "started gRPC proxy") 269 }