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 15package e2e 16 17import ( 18 "fmt" 19 "net/url" 20 "os" 21 22 "go.etcd.io/etcd/pkg/expect" 23 "go.etcd.io/etcd/pkg/fileutil" 24) 25 26var ( 27 etcdServerReadyLines = []string{"enabled capabilities for version", "published"} 28 binPath string 29 ctlBinPath string 30) 31 32// etcdProcess is a process that serves etcd requests. 33type etcdProcess interface { 34 EndpointsV2() []string 35 EndpointsV3() []string 36 EndpointsMetrics() []string 37 38 Start() error 39 Restart() error 40 Stop() error 41 Close() error 42 WithStopSignal(sig os.Signal) os.Signal 43 Config() *etcdServerProcessConfig 44} 45 46type etcdServerProcess struct { 47 cfg *etcdServerProcessConfig 48 proc *expect.ExpectProcess 49 donec chan struct{} // closed when Interact() terminates 50} 51 52type etcdServerProcessConfig struct { 53 execPath string 54 args []string 55 tlsArgs []string 56 57 dataDirPath string 58 keepDataDir bool 59 60 name string 61 62 purl url.URL 63 64 acurl string 65 murl string 66 67 initialToken string 68 initialCluster string 69} 70 71func newEtcdServerProcess(cfg *etcdServerProcessConfig) (*etcdServerProcess, error) { 72 if !fileutil.Exist(cfg.execPath) { 73 return nil, fmt.Errorf("could not find etcd binary") 74 } 75 if !cfg.keepDataDir { 76 if err := os.RemoveAll(cfg.dataDirPath); err != nil { 77 return nil, err 78 } 79 } 80 return &etcdServerProcess{cfg: cfg, donec: make(chan struct{})}, nil 81} 82 83func (ep *etcdServerProcess) EndpointsV2() []string { return []string{ep.cfg.acurl} } 84func (ep *etcdServerProcess) EndpointsV3() []string { return ep.EndpointsV2() } 85func (ep *etcdServerProcess) EndpointsMetrics() []string { return []string{ep.cfg.murl} } 86 87func (ep *etcdServerProcess) Start() error { 88 if ep.proc != nil { 89 panic("already started") 90 } 91 proc, err := spawnCmd(append([]string{ep.cfg.execPath}, ep.cfg.args...)) 92 if err != nil { 93 return err 94 } 95 ep.proc = proc 96 return ep.waitReady() 97} 98 99func (ep *etcdServerProcess) Restart() error { 100 if err := ep.Stop(); err != nil { 101 return err 102 } 103 ep.donec = make(chan struct{}) 104 return ep.Start() 105} 106 107func (ep *etcdServerProcess) Stop() (err error) { 108 if ep == nil || ep.proc == nil { 109 return nil 110 } 111 err = ep.proc.Stop() 112 if err != nil { 113 return err 114 } 115 ep.proc = nil 116 <-ep.donec 117 ep.donec = make(chan struct{}) 118 if ep.cfg.purl.Scheme == "unix" || ep.cfg.purl.Scheme == "unixs" { 119 err = os.Remove(ep.cfg.purl.Host + ep.cfg.purl.Path) 120 if err != nil { 121 return err 122 } 123 } 124 return nil 125} 126 127func (ep *etcdServerProcess) Close() error { 128 if err := ep.Stop(); err != nil { 129 return err 130 } 131 return os.RemoveAll(ep.cfg.dataDirPath) 132} 133 134func (ep *etcdServerProcess) WithStopSignal(sig os.Signal) os.Signal { 135 ret := ep.proc.StopSignal 136 ep.proc.StopSignal = sig 137 return ret 138} 139 140func (ep *etcdServerProcess) waitReady() error { 141 defer close(ep.donec) 142 return waitReadyExpectProc(ep.proc, etcdServerReadyLines) 143} 144 145func (ep *etcdServerProcess) Config() *etcdServerProcessConfig { return ep.cfg } 146