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 "github.com/coreos/etcd/pkg/expect" 23 "github.com/coreos/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() error { 108 if ep == nil || ep.proc == nil { 109 return nil 110 } 111 if err := ep.proc.Stop(); err != nil { 112 return err 113 } 114 ep.proc = nil 115 <-ep.donec 116 ep.donec = make(chan struct{}) 117 if ep.cfg.purl.Scheme == "unix" || ep.cfg.purl.Scheme == "unixs" { 118 os.Remove(ep.cfg.purl.Host + ep.cfg.purl.Path) 119 } 120 return nil 121} 122 123func (ep *etcdServerProcess) Close() error { 124 if err := ep.Stop(); err != nil { 125 return err 126 } 127 return os.RemoveAll(ep.cfg.dataDirPath) 128} 129 130func (ep *etcdServerProcess) WithStopSignal(sig os.Signal) os.Signal { 131 ret := ep.proc.StopSignal 132 ep.proc.StopSignal = sig 133 return ret 134} 135 136func (ep *etcdServerProcess) waitReady() error { 137 defer close(ep.donec) 138 return waitReadyExpectProc(ep.proc, etcdServerReadyLines) 139} 140 141func (ep *etcdServerProcess) Config() *etcdServerProcessConfig { return ep.cfg } 142