// Copyright 2017 The etcd Authors // // Licensed under the Apache License, Version 2.0 (the "License"); // you may not use this file except in compliance with the License. // You may obtain a copy of the License at // // http://www.apache.org/licenses/LICENSE-2.0 // // Unless required by applicable law or agreed to in writing, software // distributed under the License is distributed on an "AS IS" BASIS, // WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. // See the License for the specific language governing permissions and // limitations under the License. package e2e import ( "fmt" "net/url" "os" "go.etcd.io/etcd/pkg/expect" "go.etcd.io/etcd/pkg/fileutil" ) var ( etcdServerReadyLines = []string{"enabled capabilities for version", "published"} binPath string ctlBinPath string ) // etcdProcess is a process that serves etcd requests. type etcdProcess interface { EndpointsV2() []string EndpointsV3() []string EndpointsMetrics() []string Start() error Restart() error Stop() error Close() error WithStopSignal(sig os.Signal) os.Signal Config() *etcdServerProcessConfig } type etcdServerProcess struct { cfg *etcdServerProcessConfig proc *expect.ExpectProcess donec chan struct{} // closed when Interact() terminates } type etcdServerProcessConfig struct { execPath string args []string tlsArgs []string dataDirPath string keepDataDir bool name string purl url.URL acurl string murl string initialToken string initialCluster string } func newEtcdServerProcess(cfg *etcdServerProcessConfig) (*etcdServerProcess, error) { if !fileutil.Exist(cfg.execPath) { return nil, fmt.Errorf("could not find etcd binary") } if !cfg.keepDataDir { if err := os.RemoveAll(cfg.dataDirPath); err != nil { return nil, err } } return &etcdServerProcess{cfg: cfg, donec: make(chan struct{})}, nil } func (ep *etcdServerProcess) EndpointsV2() []string { return []string{ep.cfg.acurl} } func (ep *etcdServerProcess) EndpointsV3() []string { return ep.EndpointsV2() } func (ep *etcdServerProcess) EndpointsMetrics() []string { return []string{ep.cfg.murl} } func (ep *etcdServerProcess) Start() error { if ep.proc != nil { panic("already started") } proc, err := spawnCmd(append([]string{ep.cfg.execPath}, ep.cfg.args...)) if err != nil { return err } ep.proc = proc return ep.waitReady() } func (ep *etcdServerProcess) Restart() error { if err := ep.Stop(); err != nil { return err } ep.donec = make(chan struct{}) return ep.Start() } func (ep *etcdServerProcess) Stop() (err error) { if ep == nil || ep.proc == nil { return nil } err = ep.proc.Stop() if err != nil { return err } ep.proc = nil <-ep.donec ep.donec = make(chan struct{}) if ep.cfg.purl.Scheme == "unix" || ep.cfg.purl.Scheme == "unixs" { err = os.Remove(ep.cfg.purl.Host + ep.cfg.purl.Path) if err != nil { return err } } return nil } func (ep *etcdServerProcess) Close() error { if err := ep.Stop(); err != nil { return err } return os.RemoveAll(ep.cfg.dataDirPath) } func (ep *etcdServerProcess) WithStopSignal(sig os.Signal) os.Signal { ret := ep.proc.StopSignal ep.proc.StopSignal = sig return ret } func (ep *etcdServerProcess) waitReady() error { defer close(ep.donec) return waitReadyExpectProc(ep.proc, etcdServerReadyLines) } func (ep *etcdServerProcess) Config() *etcdServerProcessConfig { return ep.cfg }