Simplify Address handling for Etcd
For Etcd, make the address it should listen on configurable via
```
ectd := &Etcd{
Address: &url.URL{Scheme: "http", Host: "localhost:12345"},
}
```
If not specified, it will internally use the `DefaultAddressManager` do
find a free port to listen on.
This commit is contained in:
parent
f370ccb496
commit
32821b2077
|
|
@ -6,6 +6,8 @@ import (
|
||||||
"os/exec"
|
"os/exec"
|
||||||
"time"
|
"time"
|
||||||
|
|
||||||
|
"net/url"
|
||||||
|
|
||||||
"github.com/onsi/gomega/gbytes"
|
"github.com/onsi/gomega/gbytes"
|
||||||
"github.com/onsi/gomega/gexec"
|
"github.com/onsi/gomega/gexec"
|
||||||
)
|
)
|
||||||
|
|
@ -15,7 +17,7 @@ import (
|
||||||
// The documentation and examples for the Etcd's properties can be found in
|
// The documentation and examples for the Etcd's properties can be found in
|
||||||
// in the documentation for the `APIServer`, as both implement a `ControlPaneProcess`.
|
// in the documentation for the `APIServer`, as both implement a `ControlPaneProcess`.
|
||||||
type Etcd struct {
|
type Etcd struct {
|
||||||
AddressManager AddressManager
|
Address *url.URL
|
||||||
Path string
|
Path string
|
||||||
ProcessStarter SimpleSessionStarter
|
ProcessStarter SimpleSessionStarter
|
||||||
DataDir *Directory
|
DataDir *Directory
|
||||||
|
|
@ -43,18 +45,10 @@ type SimpleSessionStarter func(command *exec.Cmd, out, err io.Writer) (SimpleSes
|
||||||
|
|
||||||
// URL returns the URL Etcd is listening on. Clients can use this to connect to Etcd.
|
// URL returns the URL Etcd is listening on. Clients can use this to connect to Etcd.
|
||||||
func (e *Etcd) URL() (string, error) {
|
func (e *Etcd) URL() (string, error) {
|
||||||
if e.AddressManager == nil {
|
if e.Address == nil {
|
||||||
return "", fmt.Errorf("Etcd's AddressManager is not initialized")
|
return "", fmt.Errorf("Etcd's Address not initialized or configured")
|
||||||
}
|
}
|
||||||
port, err := e.AddressManager.Port()
|
return e.Address.String(), nil
|
||||||
if err != nil {
|
|
||||||
return "", err
|
|
||||||
}
|
|
||||||
host, err := e.AddressManager.Host()
|
|
||||||
if err != nil {
|
|
||||||
return "", err
|
|
||||||
}
|
|
||||||
return fmt.Sprintf("http://%s:%d", host, port), nil
|
|
||||||
}
|
}
|
||||||
|
|
||||||
// Start starts the etcd, waits for it to come up, and returns an error, if occoured.
|
// Start starts the etcd, waits for it to come up, and returns an error, if occoured.
|
||||||
|
|
@ -64,22 +58,16 @@ func (e *Etcd) Start() error {
|
||||||
return err
|
return err
|
||||||
}
|
}
|
||||||
|
|
||||||
port, host, err := e.AddressManager.Initialize()
|
|
||||||
if err != nil {
|
|
||||||
return err
|
|
||||||
}
|
|
||||||
|
|
||||||
clientURL := fmt.Sprintf("http://%s:%d", host, port)
|
|
||||||
args := []string{
|
args := []string{
|
||||||
"--debug",
|
"--debug",
|
||||||
"--listen-peer-urls=http://localhost:0",
|
"--listen-peer-urls=http://localhost:0",
|
||||||
fmt.Sprintf("--advertise-client-urls=%s", clientURL),
|
fmt.Sprintf("--advertise-client-urls=%s", e.Address),
|
||||||
fmt.Sprintf("--listen-client-urls=%s", clientURL),
|
fmt.Sprintf("--listen-client-urls=%s", e.Address),
|
||||||
fmt.Sprintf("--data-dir=%s", e.DataDir.Path),
|
fmt.Sprintf("--data-dir=%s", e.DataDir.Path),
|
||||||
}
|
}
|
||||||
|
|
||||||
detectedStart := e.stdErr.Detect(fmt.Sprintf(
|
detectedStart := e.stdErr.Detect(fmt.Sprintf(
|
||||||
"serving insecure client requests on %s", host))
|
"serving insecure client requests on %s", e.Address.Hostname()))
|
||||||
timedOut := time.After(e.StartTimeout)
|
timedOut := time.After(e.StartTimeout)
|
||||||
|
|
||||||
command := exec.Command(e.Path, args...)
|
command := exec.Command(e.Path, args...)
|
||||||
|
|
@ -100,9 +88,17 @@ func (e *Etcd) ensureInitialized() error {
|
||||||
if e.Path == "" {
|
if e.Path == "" {
|
||||||
e.Path = DefaultBinPathFinder("etcd")
|
e.Path = DefaultBinPathFinder("etcd")
|
||||||
}
|
}
|
||||||
|
if e.Address == nil {
|
||||||
|
am := &DefaultAddressManager{}
|
||||||
|
port, host, err := am.Initialize()
|
||||||
|
if err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
|
||||||
if e.AddressManager == nil {
|
e.Address = &url.URL{
|
||||||
e.AddressManager = &DefaultAddressManager{}
|
Scheme: "http",
|
||||||
|
Host: fmt.Sprintf("%s:%d", host, port),
|
||||||
|
}
|
||||||
}
|
}
|
||||||
if e.ProcessStarter == nil {
|
if e.ProcessStarter == nil {
|
||||||
e.ProcessStarter = func(command *exec.Cmd, out, err io.Writer) (SimpleSession, error) {
|
e.ProcessStarter = func(command *exec.Cmd, out, err io.Writer) (SimpleSession, error) {
|
||||||
|
|
|
||||||
|
|
@ -7,6 +7,8 @@ import (
|
||||||
|
|
||||||
"time"
|
"time"
|
||||||
|
|
||||||
|
"net/url"
|
||||||
|
|
||||||
. "github.com/onsi/ginkgo"
|
. "github.com/onsi/ginkgo"
|
||||||
. "github.com/onsi/gomega"
|
. "github.com/onsi/gomega"
|
||||||
"github.com/onsi/gomega/gbytes"
|
"github.com/onsi/gomega/gbytes"
|
||||||
|
|
@ -18,7 +20,6 @@ import (
|
||||||
var _ = Describe("Etcd", func() {
|
var _ = Describe("Etcd", func() {
|
||||||
var (
|
var (
|
||||||
fakeSession *testfakes.FakeSimpleSession
|
fakeSession *testfakes.FakeSimpleSession
|
||||||
fakeAddressManager *testfakes.FakeAddressManager
|
|
||||||
etcd *Etcd
|
etcd *Etcd
|
||||||
etcdStopper chan struct{}
|
etcdStopper chan struct{}
|
||||||
dataDirCleanupCount int
|
dataDirCleanupCount int
|
||||||
|
|
@ -26,7 +27,6 @@ var _ = Describe("Etcd", func() {
|
||||||
|
|
||||||
BeforeEach(func() {
|
BeforeEach(func() {
|
||||||
fakeSession = &testfakes.FakeSimpleSession{}
|
fakeSession = &testfakes.FakeSimpleSession{}
|
||||||
fakeAddressManager = &testfakes.FakeAddressManager{}
|
|
||||||
|
|
||||||
etcdStopper = make(chan struct{}, 1)
|
etcdStopper = make(chan struct{}, 1)
|
||||||
fakeSession.TerminateReturns(&gexec.Session{
|
fakeSession.TerminateReturns(&gexec.Session{
|
||||||
|
|
@ -35,8 +35,7 @@ var _ = Describe("Etcd", func() {
|
||||||
close(etcdStopper)
|
close(etcdStopper)
|
||||||
|
|
||||||
etcd = &Etcd{
|
etcd = &Etcd{
|
||||||
AddressManager: fakeAddressManager,
|
Path: "/path/to/some/etcd",
|
||||||
Path: "/path/to/some/etcd",
|
|
||||||
DataDir: &Directory{
|
DataDir: &Directory{
|
||||||
Path: "/path/to/some/etcd",
|
Path: "/path/to/some/etcd",
|
||||||
Cleanup: func() error {
|
Cleanup: func() error {
|
||||||
|
|
@ -58,7 +57,7 @@ var _ = Describe("Etcd", func() {
|
||||||
fakeSession.ExitCodeReturnsOnCall(0, -1)
|
fakeSession.ExitCodeReturnsOnCall(0, -1)
|
||||||
fakeSession.ExitCodeReturnsOnCall(1, 143)
|
fakeSession.ExitCodeReturnsOnCall(1, 143)
|
||||||
|
|
||||||
fakeAddressManager.InitializeReturns(1234, "this.is.etcd.listening.for.clients", nil)
|
etcd.Address = &url.URL{Scheme: "http", Host: "this.is.etcd.listening.for.clients:1234"}
|
||||||
|
|
||||||
etcd.ProcessStarter = func(command *exec.Cmd, out, err io.Writer) (SimpleSession, error) {
|
etcd.ProcessStarter = func(command *exec.Cmd, out, err io.Writer) (SimpleSession, error) {
|
||||||
Expect(command.Args).To(ContainElement(fmt.Sprintf("--advertise-client-urls=http://%s:%d", "this.is.etcd.listening.for.clients", 1234)))
|
Expect(command.Args).To(ContainElement(fmt.Sprintf("--advertise-client-urls=http://%s:%d", "this.is.etcd.listening.for.clients", 1234)))
|
||||||
|
|
@ -72,9 +71,6 @@ var _ = Describe("Etcd", func() {
|
||||||
err := etcd.Start()
|
err := etcd.Start()
|
||||||
Expect(err).NotTo(HaveOccurred())
|
Expect(err).NotTo(HaveOccurred())
|
||||||
|
|
||||||
By("...in turn calling using the AddressManager")
|
|
||||||
Expect(fakeAddressManager.InitializeCallCount()).To(Equal(1))
|
|
||||||
|
|
||||||
Eventually(etcd).Should(gbytes.Say("Everything is dandy"))
|
Eventually(etcd).Should(gbytes.Say("Everything is dandy"))
|
||||||
Expect(fakeSession.ExitCodeCallCount()).To(Equal(0))
|
Expect(fakeSession.ExitCodeCallCount()).To(Equal(0))
|
||||||
Expect(etcd).NotTo(gexec.Exit())
|
Expect(etcd).NotTo(gexec.Exit())
|
||||||
|
|
@ -95,7 +91,7 @@ var _ = Describe("Etcd", func() {
|
||||||
etcd.DataDir.Cleanup = func() error {
|
etcd.DataDir.Cleanup = func() error {
|
||||||
return fmt.Errorf("destroy failed")
|
return fmt.Errorf("destroy failed")
|
||||||
}
|
}
|
||||||
fakeAddressManager.InitializeReturns(1234, "this.is.etcd", nil)
|
etcd.Address = &url.URL{Scheme: "http", Host: "this.is.etcd:1234"}
|
||||||
etcd.ProcessStarter = func(Command *exec.Cmd, out, err io.Writer) (SimpleSession, error) {
|
etcd.ProcessStarter = func(Command *exec.Cmd, out, err io.Writer) (SimpleSession, error) {
|
||||||
fmt.Fprint(err, "serving insecure client requests on this.is.etcd:1234")
|
fmt.Fprint(err, "serving insecure client requests on this.is.etcd:1234")
|
||||||
return fakeSession, nil
|
return fakeSession, nil
|
||||||
|
|
@ -110,7 +106,7 @@ var _ = Describe("Etcd", func() {
|
||||||
Context("when there is no function to cleanup the data directory", func() {
|
Context("when there is no function to cleanup the data directory", func() {
|
||||||
It("does not panic", func() {
|
It("does not panic", func() {
|
||||||
etcd.DataDir.Cleanup = nil
|
etcd.DataDir.Cleanup = nil
|
||||||
fakeAddressManager.InitializeReturns(1234, "this.is.etcd", nil)
|
etcd.Address = &url.URL{Scheme: "http", Host: "this.is.etcd:1234"}
|
||||||
etcd.ProcessStarter = func(Command *exec.Cmd, out, err io.Writer) (SimpleSession, error) {
|
etcd.ProcessStarter = func(Command *exec.Cmd, out, err io.Writer) (SimpleSession, error) {
|
||||||
fmt.Fprint(err, "serving insecure client requests on this.is.etcd:1234")
|
fmt.Fprint(err, "serving insecure client requests on this.is.etcd:1234")
|
||||||
return fakeSession, nil
|
return fakeSession, nil
|
||||||
|
|
@ -126,20 +122,6 @@ var _ = Describe("Etcd", func() {
|
||||||
})
|
})
|
||||||
})
|
})
|
||||||
|
|
||||||
Context("when the address manager fails to get a new address", func() {
|
|
||||||
It("propagates the error and does not start any process", func() {
|
|
||||||
fakeAddressManager.InitializeReturns(0, "", fmt.Errorf("some error finding a free port"))
|
|
||||||
|
|
||||||
etcd.ProcessStarter = func(Command *exec.Cmd, out, err io.Writer) (SimpleSession, error) {
|
|
||||||
Expect(true).To(BeFalse(),
|
|
||||||
"the etcd process starter shouldn't be called if getting a free port fails")
|
|
||||||
return nil, nil
|
|
||||||
}
|
|
||||||
|
|
||||||
Expect(etcd.Start()).To(MatchError(ContainSubstring("some error finding a free port")))
|
|
||||||
})
|
|
||||||
})
|
|
||||||
|
|
||||||
Context("when the starter returns an error", func() {
|
Context("when the starter returns an error", func() {
|
||||||
It("propagates the error", func() {
|
It("propagates the error", func() {
|
||||||
etcd.ProcessStarter = func(command *exec.Cmd, out, err io.Writer) (SimpleSession, error) {
|
etcd.ProcessStarter = func(command *exec.Cmd, out, err io.Writer) (SimpleSession, error) {
|
||||||
|
|
@ -181,7 +163,7 @@ var _ = Describe("Etcd", func() {
|
||||||
})
|
})
|
||||||
})
|
})
|
||||||
It("propagates the error", func() {
|
It("propagates the error", func() {
|
||||||
fakeAddressManager.InitializeReturns(1234, "this.is.etcd", nil)
|
etcd.Address = &url.URL{Scheme: "http", Host: "this.is.etcd:1234"}
|
||||||
|
|
||||||
etcd.ProcessStarter = func(command *exec.Cmd, out, err io.Writer) (SimpleSession, error) {
|
etcd.ProcessStarter = func(command *exec.Cmd, out, err io.Writer) (SimpleSession, error) {
|
||||||
fmt.Fprint(err, "serving insecure client requests on this.is.etcd:1234")
|
fmt.Fprint(err, "serving insecure client requests on this.is.etcd:1234")
|
||||||
|
|
@ -197,26 +179,11 @@ var _ = Describe("Etcd", func() {
|
||||||
|
|
||||||
Describe("querying the server for its URL", func() {
|
Describe("querying the server for its URL", func() {
|
||||||
It("can be queried for the URL it listens on", func() {
|
It("can be queried for the URL it listens on", func() {
|
||||||
fakeAddressManager.HostReturns("the.host.for.etcd", nil)
|
etcd.Address = &url.URL{Scheme: "http", Host: "the.host.for.etcd:6789"}
|
||||||
fakeAddressManager.PortReturns(6789, nil)
|
|
||||||
apiServerURL, err := etcd.URL()
|
apiServerURL, err := etcd.URL()
|
||||||
Expect(err).NotTo(HaveOccurred())
|
Expect(err).NotTo(HaveOccurred())
|
||||||
Expect(apiServerURL).To(Equal("http://the.host.for.etcd:6789"))
|
Expect(apiServerURL).To(Equal("http://the.host.for.etcd:6789"))
|
||||||
})
|
})
|
||||||
Context("when the addressmanager fails to give us a port", func() {
|
|
||||||
It("propagates the failure", func() {
|
|
||||||
fakeAddressManager.PortReturns(0, fmt.Errorf("zort"))
|
|
||||||
_, err := etcd.URL()
|
|
||||||
Expect(err).To(MatchError(ContainSubstring("zort")))
|
|
||||||
})
|
|
||||||
})
|
|
||||||
Context("when the addressmanager fails to give us a host", func() {
|
|
||||||
It("propagates the failure", func() {
|
|
||||||
fakeAddressManager.HostReturns("", fmt.Errorf("bam!"))
|
|
||||||
_, err := etcd.URL()
|
|
||||||
Expect(err).To(MatchError(ContainSubstring("bam!")))
|
|
||||||
})
|
|
||||||
})
|
|
||||||
Context("before starting etcd", func() {
|
Context("before starting etcd", func() {
|
||||||
Context("and therefore the addressmanager has not been initialized", func() {
|
Context("and therefore the addressmanager has not been initialized", func() {
|
||||||
BeforeEach(func() {
|
BeforeEach(func() {
|
||||||
|
|
|
||||||
Loading…
Reference in New Issue