Refactor Etcd
- Store stdout,stderr in private buffers - Configure the etcURL on construction instead of at start time - Handle the creation of the temporary directory (for the data directory) internally
This commit is contained in:
parent
a04f00234e
commit
2fd15f82f9
|
@ -1,9 +1,11 @@
|
||||||
package test
|
package test
|
||||||
|
|
||||||
import (
|
import (
|
||||||
|
"io/ioutil"
|
||||||
|
"os"
|
||||||
"os/exec"
|
"os/exec"
|
||||||
|
|
||||||
"github.com/onsi/ginkgo"
|
"github.com/onsi/gomega"
|
||||||
"github.com/onsi/gomega/gbytes"
|
"github.com/onsi/gomega/gbytes"
|
||||||
"github.com/onsi/gomega/gexec"
|
"github.com/onsi/gomega/gexec"
|
||||||
)
|
)
|
||||||
|
@ -11,25 +13,33 @@ import (
|
||||||
// Etcd knows how to run an etcd server. Set it up with the path to a precompiled binary.
|
// Etcd knows how to run an etcd server. Set it up with the path to a precompiled binary.
|
||||||
type Etcd struct {
|
type Etcd struct {
|
||||||
// The path to the etcd binary
|
// The path to the etcd binary
|
||||||
Path string
|
Path string
|
||||||
session *gexec.Session
|
EtcdURL string
|
||||||
|
session *gexec.Session
|
||||||
|
stdOut *gbytes.Buffer
|
||||||
|
stdErr *gbytes.Buffer
|
||||||
|
tempDirManager TempDirManager
|
||||||
}
|
}
|
||||||
|
|
||||||
// Start starts the etcd, and returns a gexec.Session. To stop it again, call Terminate and Wait on that session.
|
// Start starts the etcd, and returns a gexec.Session. To stop it again, call Terminate and Wait on that session.
|
||||||
func (e *Etcd) Start(etcdURL string, datadir string) error {
|
func (e *Etcd) Start() error {
|
||||||
|
e.tempDirManager = &tempDirManager{}
|
||||||
|
|
||||||
|
dataDir := e.tempDirManager.Create()
|
||||||
|
|
||||||
args := []string{
|
args := []string{
|
||||||
"--advertise-client-urls",
|
|
||||||
etcdURL,
|
|
||||||
"--data-dir",
|
|
||||||
datadir,
|
|
||||||
"--listen-client-urls",
|
|
||||||
etcdURL,
|
|
||||||
"--debug",
|
"--debug",
|
||||||
|
"--advertise-client-urls",
|
||||||
|
e.EtcdURL,
|
||||||
|
"--listen-client-urls",
|
||||||
|
e.EtcdURL,
|
||||||
|
"--data-dir",
|
||||||
|
dataDir,
|
||||||
}
|
}
|
||||||
|
|
||||||
command := exec.Command(e.Path, args...)
|
command := exec.Command(e.Path, args...)
|
||||||
var err error
|
var err error
|
||||||
e.session, err = gexec.Start(command, ginkgo.GinkgoWriter, ginkgo.GinkgoWriter)
|
e.session, err = gexec.Start(command, e.stdOut, e.stdErr)
|
||||||
return err
|
return err
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -37,6 +47,8 @@ func (e *Etcd) Start(etcdURL string, datadir string) error {
|
||||||
func (e *Etcd) Stop() {
|
func (e *Etcd) Stop() {
|
||||||
if e.session != nil {
|
if e.session != nil {
|
||||||
e.session.Terminate().Wait()
|
e.session.Terminate().Wait()
|
||||||
|
err := e.tempDirManager.Destroy()
|
||||||
|
gomega.Expect(err).NotTo(gomega.HaveOccurred())
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -49,3 +61,32 @@ func (e *Etcd) ExitCode() int {
|
||||||
func (e *Etcd) Buffer() *gbytes.Buffer {
|
func (e *Etcd) Buffer() *gbytes.Buffer {
|
||||||
return e.session.Buffer()
|
return e.session.Buffer()
|
||||||
}
|
}
|
||||||
|
|
||||||
|
//------
|
||||||
|
|
||||||
|
// TempDirManager knows how to create and destroy temporary directories.
|
||||||
|
type TempDirManager interface {
|
||||||
|
Create() string
|
||||||
|
Destroy() error
|
||||||
|
}
|
||||||
|
|
||||||
|
//go:generate counterfeiter . TempDirManager
|
||||||
|
|
||||||
|
type tempDirManager struct {
|
||||||
|
dir string
|
||||||
|
}
|
||||||
|
|
||||||
|
func (t *tempDirManager) Create() string {
|
||||||
|
var err error
|
||||||
|
t.dir, err = ioutil.TempDir("", "kube-test-framework")
|
||||||
|
gomega.ExpectWithOffset(2, err).NotTo(gomega.HaveOccurred(),
|
||||||
|
"expected to be able to create a temporary directory in the kube test framework")
|
||||||
|
return t.dir
|
||||||
|
}
|
||||||
|
|
||||||
|
func (t *tempDirManager) Destroy() error {
|
||||||
|
if t.dir != "" {
|
||||||
|
return os.RemoveAll(t.dir)
|
||||||
|
}
|
||||||
|
return nil
|
||||||
|
}
|
||||||
|
|
|
@ -15,10 +15,13 @@ var _ = Describe("Etcd", func() {
|
||||||
It("can start and stop that binary", func() {
|
It("can start and stop that binary", func() {
|
||||||
pathToFakeEtcd, err := gexec.Build("k8s.io/kubectl/pkg/framework/test/assets/fakeetcd")
|
pathToFakeEtcd, err := gexec.Build("k8s.io/kubectl/pkg/framework/test/assets/fakeetcd")
|
||||||
Expect(err).NotTo(HaveOccurred())
|
Expect(err).NotTo(HaveOccurred())
|
||||||
etcd := &Etcd{Path: pathToFakeEtcd}
|
etcd := &Etcd{
|
||||||
|
Path: pathToFakeEtcd,
|
||||||
|
EtcdURL: "our etcd url",
|
||||||
|
}
|
||||||
|
|
||||||
By("Starting the Etcd Server")
|
By("Starting the Etcd Server")
|
||||||
err = etcd.Start("our etcd url", "our data directory")
|
err = etcd.Start()
|
||||||
Expect(err).NotTo(HaveOccurred())
|
Expect(err).NotTo(HaveOccurred())
|
||||||
|
|
||||||
Eventually(etcd).Should(gbytes.Say("Everything is dandy"))
|
Eventually(etcd).Should(gbytes.Say("Everything is dandy"))
|
||||||
|
@ -34,7 +37,7 @@ var _ = Describe("Etcd", func() {
|
||||||
Context("when no path is given", func() {
|
Context("when no path is given", func() {
|
||||||
It("fails with a helpful error", func() {
|
It("fails with a helpful error", func() {
|
||||||
etcd := &Etcd{}
|
etcd := &Etcd{}
|
||||||
err := etcd.Start("our etcd url", "")
|
err := etcd.Start()
|
||||||
Expect(err).To(MatchError(ContainSubstring("no such file or directory")))
|
Expect(err).To(MatchError(ContainSubstring("no such file or directory")))
|
||||||
})
|
})
|
||||||
})
|
})
|
||||||
|
@ -44,7 +47,7 @@ var _ = Describe("Etcd", func() {
|
||||||
etcd := &Etcd{
|
etcd := &Etcd{
|
||||||
Path: "./etcd.go",
|
Path: "./etcd.go",
|
||||||
}
|
}
|
||||||
err := etcd.Start("our etcd url", "")
|
err := etcd.Start()
|
||||||
Expect(err).To(MatchError(ContainSubstring("./etcd.go: permission denied")))
|
Expect(err).To(MatchError(ContainSubstring("./etcd.go: permission denied")))
|
||||||
})
|
})
|
||||||
})
|
})
|
||||||
|
|
|
@ -2,24 +2,19 @@ package test
|
||||||
|
|
||||||
import (
|
import (
|
||||||
"fmt"
|
"fmt"
|
||||||
"io/ioutil"
|
|
||||||
"os"
|
|
||||||
|
|
||||||
"github.com/onsi/gomega"
|
|
||||||
)
|
)
|
||||||
|
|
||||||
// Fixtures is a struct that knows how to start all your test fixtures.
|
// Fixtures is a struct that knows how to start all your test fixtures.
|
||||||
//
|
//
|
||||||
// Right now, that means Etcd and your APIServer. This is likely to increase in future.
|
// Right now, that means Etcd and your APIServer. This is likely to increase in future.
|
||||||
type Fixtures struct {
|
type Fixtures struct {
|
||||||
Etcd EtcdStartStopper
|
Etcd EtcdStartStopper
|
||||||
APIServer APIServerStartStopper
|
APIServer APIServerStartStopper
|
||||||
TempDirManager TempDirManager
|
|
||||||
}
|
}
|
||||||
|
|
||||||
// EtcdStartStopper knows how to start an Etcd. One good implementation is Etcd.
|
// EtcdStartStopper knows how to start an Etcd. One good implementation is Etcd.
|
||||||
type EtcdStartStopper interface {
|
type EtcdStartStopper interface {
|
||||||
Start(etcdURL, datadir string) error
|
Start() error
|
||||||
Stop()
|
Stop()
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -33,46 +28,21 @@ type APIServerStartStopper interface {
|
||||||
|
|
||||||
//go:generate counterfeiter . APIServerStartStopper
|
//go:generate counterfeiter . APIServerStartStopper
|
||||||
|
|
||||||
// TempDirManager knows how to create and destroy temporary directories.
|
|
||||||
type TempDirManager interface {
|
|
||||||
Create() string
|
|
||||||
Destroy() error
|
|
||||||
}
|
|
||||||
|
|
||||||
//go:generate counterfeiter . TempDirManager
|
|
||||||
|
|
||||||
// NewFixtures will give you a Fixtures struct that's properly wired together.
|
// NewFixtures will give you a Fixtures struct that's properly wired together.
|
||||||
func NewFixtures(pathToEtcd, pathToAPIServer string) *Fixtures {
|
func NewFixtures(pathToEtcd, pathToAPIServer string) *Fixtures {
|
||||||
|
etcdURL := "http://127.0.0.1:2379"
|
||||||
return &Fixtures{
|
return &Fixtures{
|
||||||
Etcd: &Etcd{Path: pathToEtcd},
|
Etcd: &Etcd{
|
||||||
APIServer: &APIServer{Path: pathToAPIServer},
|
Path: pathToEtcd,
|
||||||
TempDirManager: &tempDirManager{},
|
EtcdURL: etcdURL,
|
||||||
|
},
|
||||||
|
APIServer: &APIServer{Path:pathToAPIServer},
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
type tempDirManager struct {
|
|
||||||
dir string
|
|
||||||
}
|
|
||||||
|
|
||||||
func (t *tempDirManager) Create() string {
|
|
||||||
var err error
|
|
||||||
t.dir, err = ioutil.TempDir("", "kube-test-framework")
|
|
||||||
gomega.ExpectWithOffset(2, err).NotTo(gomega.HaveOccurred(),
|
|
||||||
"expected to be able to create a temporary directory in the kube test framework")
|
|
||||||
return t.dir
|
|
||||||
}
|
|
||||||
|
|
||||||
func (t *tempDirManager) Destroy() error {
|
|
||||||
if t.dir != "" {
|
|
||||||
return os.RemoveAll(t.dir)
|
|
||||||
}
|
|
||||||
return nil
|
|
||||||
}
|
|
||||||
|
|
||||||
// Start will start all your fixtures. To stop them, call Stop().
|
// Start will start all your fixtures. To stop them, call Stop().
|
||||||
func (f *Fixtures) Start() error {
|
func (f *Fixtures) Start() error {
|
||||||
tmpDir := f.TempDirManager.Create()
|
if err := f.Etcd.Start(); err != nil {
|
||||||
if err := f.Etcd.Start("http://127.0.0.1:2379", tmpDir); err != nil {
|
|
||||||
return fmt.Errorf("Error starting etcd: %s", err)
|
return fmt.Errorf("Error starting etcd: %s", err)
|
||||||
}
|
}
|
||||||
if err := f.APIServer.Start("http://127.0.0.1:2379"); err != nil {
|
if err := f.APIServer.Start("http://127.0.0.1:2379"); err != nil {
|
||||||
|
@ -85,5 +55,5 @@ func (f *Fixtures) Start() error {
|
||||||
func (f *Fixtures) Stop() error {
|
func (f *Fixtures) Stop() error {
|
||||||
f.APIServer.Stop()
|
f.APIServer.Stop()
|
||||||
f.Etcd.Stop()
|
f.Etcd.Stop()
|
||||||
return f.TempDirManager.Destroy()
|
return nil
|
||||||
}
|
}
|
||||||
|
|
|
@ -21,36 +21,24 @@ var _ = Describe("Fixtures", func() {
|
||||||
var (
|
var (
|
||||||
fakeEtcdStartStopper *testfakes.FakeEtcdStartStopper
|
fakeEtcdStartStopper *testfakes.FakeEtcdStartStopper
|
||||||
fakeAPIServerStartStopper *testfakes.FakeAPIServerStartStopper
|
fakeAPIServerStartStopper *testfakes.FakeAPIServerStartStopper
|
||||||
fakeTempDirManager *testfakes.FakeTempDirManager
|
|
||||||
fixtures Fixtures
|
fixtures Fixtures
|
||||||
)
|
)
|
||||||
BeforeEach(func() {
|
BeforeEach(func() {
|
||||||
fakeEtcdStartStopper = &testfakes.FakeEtcdStartStopper{}
|
fakeEtcdStartStopper = &testfakes.FakeEtcdStartStopper{}
|
||||||
fakeAPIServerStartStopper = &testfakes.FakeAPIServerStartStopper{}
|
fakeAPIServerStartStopper = &testfakes.FakeAPIServerStartStopper{}
|
||||||
fakeTempDirManager = &testfakes.FakeTempDirManager{}
|
|
||||||
fixtures = Fixtures{
|
fixtures = Fixtures{
|
||||||
Etcd: fakeEtcdStartStopper,
|
Etcd: fakeEtcdStartStopper,
|
||||||
APIServer: fakeAPIServerStartStopper,
|
APIServer: fakeAPIServerStartStopper,
|
||||||
TempDirManager: fakeTempDirManager,
|
|
||||||
}
|
}
|
||||||
})
|
})
|
||||||
|
|
||||||
It("can start them", func() {
|
It("can start them", func() {
|
||||||
fakeTempDirManager.CreateReturns("some temp dir")
|
|
||||||
|
|
||||||
err := fixtures.Start()
|
err := fixtures.Start()
|
||||||
Expect(err).NotTo(HaveOccurred())
|
Expect(err).NotTo(HaveOccurred())
|
||||||
|
|
||||||
By("creating a temporary directory")
|
|
||||||
Expect(fakeTempDirManager.CreateCallCount()).To(Equal(1),
|
|
||||||
"the TempDirManager should be called exactly once")
|
|
||||||
|
|
||||||
By("starting Etcd")
|
By("starting Etcd")
|
||||||
Expect(fakeEtcdStartStopper.StartCallCount()).To(Equal(1),
|
Expect(fakeEtcdStartStopper.StartCallCount()).To(Equal(1),
|
||||||
"the EtcdStartStopper should be called exactly once")
|
"the EtcdStartStopper should be called exactly once")
|
||||||
url, datadir := fakeEtcdStartStopper.StartArgsForCall(0)
|
|
||||||
Expect(url).To(Equal("http://127.0.0.1:2379"))
|
|
||||||
Expect(datadir).To(Equal("some temp dir"))
|
|
||||||
|
|
||||||
By("starting APIServer")
|
By("starting APIServer")
|
||||||
Expect(fakeAPIServerStartStopper.StartCallCount()).To(Equal(1),
|
Expect(fakeAPIServerStartStopper.StartCallCount()).To(Equal(1),
|
||||||
|
@ -79,18 +67,7 @@ var _ = Describe("Fixtures", func() {
|
||||||
fixtures.Stop()
|
fixtures.Stop()
|
||||||
Expect(fakeEtcdStartStopper.StopCallCount()).To(Equal(1))
|
Expect(fakeEtcdStartStopper.StopCallCount()).To(Equal(1))
|
||||||
Expect(fakeAPIServerStartStopper.StopCallCount()).To(Equal(1))
|
Expect(fakeAPIServerStartStopper.StopCallCount()).To(Equal(1))
|
||||||
Expect(fakeTempDirManager.DestroyCallCount()).To(Equal(1))
|
|
||||||
})
|
})
|
||||||
|
|
||||||
Context("when cleanup fails", func() {
|
|
||||||
It("still stops the services, and it bubbles up the error", func() {
|
|
||||||
fakeTempDirManager.DestroyReturns(fmt.Errorf("deletion failed"))
|
|
||||||
err := fixtures.Stop()
|
|
||||||
Expect(err).To(MatchError(ContainSubstring("deletion failed")))
|
|
||||||
|
|
||||||
Expect(fakeEtcdStartStopper.StopCallCount()).To(Equal(1))
|
|
||||||
Expect(fakeAPIServerStartStopper.StopCallCount()).To(Equal(1))
|
|
||||||
})
|
|
||||||
})
|
|
||||||
})
|
})
|
||||||
})
|
})
|
||||||
|
|
|
@ -8,13 +8,10 @@ import (
|
||||||
)
|
)
|
||||||
|
|
||||||
type FakeEtcdStartStopper struct {
|
type FakeEtcdStartStopper struct {
|
||||||
StartStub func(etcdURL, datadir string) error
|
StartStub func() error
|
||||||
startMutex sync.RWMutex
|
startMutex sync.RWMutex
|
||||||
startArgsForCall []struct {
|
startArgsForCall []struct{}
|
||||||
etcdURL string
|
startReturns struct {
|
||||||
datadir string
|
|
||||||
}
|
|
||||||
startReturns struct {
|
|
||||||
result1 error
|
result1 error
|
||||||
}
|
}
|
||||||
startReturnsOnCall map[int]struct {
|
startReturnsOnCall map[int]struct {
|
||||||
|
@ -27,17 +24,14 @@ type FakeEtcdStartStopper struct {
|
||||||
invocationsMutex sync.RWMutex
|
invocationsMutex sync.RWMutex
|
||||||
}
|
}
|
||||||
|
|
||||||
func (fake *FakeEtcdStartStopper) Start(etcdURL string, datadir string) error {
|
func (fake *FakeEtcdStartStopper) Start() error {
|
||||||
fake.startMutex.Lock()
|
fake.startMutex.Lock()
|
||||||
ret, specificReturn := fake.startReturnsOnCall[len(fake.startArgsForCall)]
|
ret, specificReturn := fake.startReturnsOnCall[len(fake.startArgsForCall)]
|
||||||
fake.startArgsForCall = append(fake.startArgsForCall, struct {
|
fake.startArgsForCall = append(fake.startArgsForCall, struct{}{})
|
||||||
etcdURL string
|
fake.recordInvocation("Start", []interface{}{})
|
||||||
datadir string
|
|
||||||
}{etcdURL, datadir})
|
|
||||||
fake.recordInvocation("Start", []interface{}{etcdURL, datadir})
|
|
||||||
fake.startMutex.Unlock()
|
fake.startMutex.Unlock()
|
||||||
if fake.StartStub != nil {
|
if fake.StartStub != nil {
|
||||||
return fake.StartStub(etcdURL, datadir)
|
return fake.StartStub()
|
||||||
}
|
}
|
||||||
if specificReturn {
|
if specificReturn {
|
||||||
return ret.result1
|
return ret.result1
|
||||||
|
@ -51,12 +45,6 @@ func (fake *FakeEtcdStartStopper) StartCallCount() int {
|
||||||
return len(fake.startArgsForCall)
|
return len(fake.startArgsForCall)
|
||||||
}
|
}
|
||||||
|
|
||||||
func (fake *FakeEtcdStartStopper) StartArgsForCall(i int) (string, string) {
|
|
||||||
fake.startMutex.RLock()
|
|
||||||
defer fake.startMutex.RUnlock()
|
|
||||||
return fake.startArgsForCall[i].etcdURL, fake.startArgsForCall[i].datadir
|
|
||||||
}
|
|
||||||
|
|
||||||
func (fake *FakeEtcdStartStopper) StartReturns(result1 error) {
|
func (fake *FakeEtcdStartStopper) StartReturns(result1 error) {
|
||||||
fake.StartStub = nil
|
fake.StartStub = nil
|
||||||
fake.startReturns = struct {
|
fake.startReturns = struct {
|
||||||
|
|
Loading…
Reference in New Issue