Make APIServer manage its own port allocations
This means we will no longer need to pass a free port into the APIServer constructor.
This commit is contained in:
		
							parent
							
								
									d62ff04228
								
							
						
					
					
						commit
						657963b319
					
				|  | @ -3,7 +3,6 @@ package test | |||
| import ( | ||||
| 	"fmt" | ||||
| 	"io" | ||||
| 	"net/url" | ||||
| 	"os/exec" | ||||
| 	"time" | ||||
| 
 | ||||
|  | @ -14,6 +13,7 @@ import ( | |||
| 
 | ||||
| // APIServer knows how to run a kubernetes apiserver. Set it up with the path to a precompiled binary.
 | ||||
| type APIServer struct { | ||||
| 	AddressManager AddressManager | ||||
| 	PathFinder     BinPathFinder | ||||
| 	ProcessStarter simpleSessionStarter | ||||
| 	CertDirManager certDirManager | ||||
|  | @ -52,7 +52,10 @@ func NewAPIServer(config *APIServerConfig) (*APIServer, error) { | |||
| 
 | ||||
| // URL returns the URL APIServer is listening on. Clients can use this to connect to APIServer.
 | ||||
| func (s *APIServer) URL() string { | ||||
| 	return s.Config.APIServerURL | ||||
| 	// TODO handle errors
 | ||||
| 	port, _ := s.AddressManager.Port() | ||||
| 	host, _ := s.AddressManager.Host() | ||||
| 	return fmt.Sprintf("http://%s:%d", host, port) | ||||
| } | ||||
| 
 | ||||
| // Start starts the apiserver, waits for it to come up, and returns an error, if occoured.
 | ||||
|  | @ -60,28 +63,31 @@ func (s *APIServer) Start() error { | |||
| 	if s.PathFinder == nil { | ||||
| 		s.PathFinder = DefaultBinPathFinder | ||||
| 	} | ||||
| 	if s.AddressManager == nil { | ||||
| 		s.AddressManager = &DefaultAddressManager{} | ||||
| 	} | ||||
| 	if err := s.Config.Validate(); err != nil { | ||||
| 		return err | ||||
| 	} | ||||
| 
 | ||||
| 	err := s.Etcd.Start() | ||||
| 	port, addr, err := s.AddressManager.Initialize("localhost") | ||||
| 	if err != nil { | ||||
| 		return err | ||||
| 	} | ||||
| 
 | ||||
| 	s.stdOut = gbytes.NewBuffer() | ||||
| 	s.stdErr = gbytes.NewBuffer() | ||||
| 
 | ||||
| 	certDir, err := s.CertDirManager.Create() | ||||
| 	if err != nil { | ||||
| 		return err | ||||
| 	} | ||||
| 
 | ||||
| 	clientURL, err := url.Parse(s.Config.APIServerURL) | ||||
| 	err = s.Etcd.Start() | ||||
| 	if err != nil { | ||||
| 		return err | ||||
| 	} | ||||
| 
 | ||||
| 	s.stdOut = gbytes.NewBuffer() | ||||
| 	s.stdErr = gbytes.NewBuffer() | ||||
| 
 | ||||
| 	args := []string{ | ||||
| 		"--authorization-mode=Node,RBAC", | ||||
| 		"--runtime-config=admissionregistration.k8s.io/v1alpha1", | ||||
|  | @ -92,11 +98,11 @@ func (s *APIServer) Start() error { | |||
| 		"--storage-backend=etcd3", | ||||
| 		fmt.Sprintf("--etcd-servers=%s", s.Etcd.URL()), | ||||
| 		fmt.Sprintf("--cert-dir=%s", certDir), | ||||
| 		fmt.Sprintf("--insecure-port=%s", clientURL.Port()), | ||||
| 		fmt.Sprintf("--insecure-bind-address=%s", clientURL.Hostname()), | ||||
| 		fmt.Sprintf("--insecure-port=%d", port), | ||||
| 		fmt.Sprintf("--insecure-bind-address=%s", addr), | ||||
| 	} | ||||
| 
 | ||||
| 	detectedStart := s.stdErr.Detect(fmt.Sprintf("Serving insecurely on %s", clientURL.Host)) | ||||
| 	detectedStart := s.stdErr.Detect(fmt.Sprintf("Serving insecurely on %s:%d", addr, port)) | ||||
| 	timedOut := time.After(20 * time.Second) | ||||
| 
 | ||||
| 	command := exec.Command(s.PathFinder("kube-apiserver"), args...) | ||||
|  |  | |||
|  | @ -4,7 +4,7 @@ import "github.com/asaskevich/govalidator" | |||
| 
 | ||||
| // APIServerConfig is a struct holding data to configure the API Server process
 | ||||
| type APIServerConfig struct { | ||||
| 	APIServerURL string `valid:"required,url"` | ||||
| 	APIServerURL string `valid:"url"` | ||||
| } | ||||
| 
 | ||||
| // Validate checks that the config contains only valid URLs
 | ||||
|  |  | |||
|  | @ -16,12 +16,6 @@ var _ = Describe("APIServerConfig", func() { | |||
| 		Expect(err).NotTo(HaveOccurred()) | ||||
| 	}) | ||||
| 
 | ||||
| 	It("errors on empty config", func() { | ||||
| 		conf := &APIServerConfig{} | ||||
| 		err := conf.Validate() | ||||
| 		Expect(err).To(MatchError(ContainSubstring("APIServerURL: non zero value required"))) | ||||
| 	}) | ||||
| 
 | ||||
| 	It("errors on malformed URLs", func() { | ||||
| 		conf := &APIServerConfig{ | ||||
| 			APIServerURL: "something not URLish", | ||||
|  |  | |||
|  | @ -23,6 +23,7 @@ var _ = Describe("Apiserver", func() { | |||
| 		apiServerConfig    *APIServerConfig | ||||
| 		fakeEtcdProcess    *testfakes.FakeFixtureProcess | ||||
| 		fakePathFinder     *testfakes.FakeBinPathFinder | ||||
| 		fakeAddressManager *testfakes.FakeAddressManager | ||||
| 	) | ||||
| 
 | ||||
| 	BeforeEach(func() { | ||||
|  | @ -30,11 +31,13 @@ var _ = Describe("Apiserver", func() { | |||
| 		fakeCertDirManager = &testfakes.FakeCertDirManager{} | ||||
| 		fakeEtcdProcess = &testfakes.FakeFixtureProcess{} | ||||
| 		fakePathFinder = &testfakes.FakeBinPathFinder{} | ||||
| 		fakeAddressManager = &testfakes.FakeAddressManager{} | ||||
| 
 | ||||
| 		apiServerConfig = &APIServerConfig{ | ||||
| 			APIServerURL: "http://this.is.the.API.server:8080", | ||||
| 			APIServerURL: "", | ||||
| 		} | ||||
| 		apiServer = &APIServer{ | ||||
| 			AddressManager: fakeAddressManager, | ||||
| 			PathFinder:     fakePathFinder.Spy, | ||||
| 			CertDirManager: fakeCertDirManager, | ||||
| 			Config:         apiServerConfig, | ||||
|  | @ -42,104 +45,150 @@ var _ = Describe("Apiserver", func() { | |||
| 		} | ||||
| 	}) | ||||
| 
 | ||||
| 	It("can be queried for the URL it listens on", func() { | ||||
| 		Expect(apiServer.URL()).To(Equal("http://this.is.the.API.server:8080")) | ||||
| 	}) | ||||
| 	Describe("starting and stopping the server", func() { | ||||
| 		Context("when given a path to a binary that runs for a long time", func() { | ||||
| 			It("can start and stop that binary", func() { | ||||
| 				sessionBuffer := gbytes.NewBuffer() | ||||
| 				fmt.Fprint(sessionBuffer, "Everything is fine") | ||||
| 				fakeSession.BufferReturns(sessionBuffer) | ||||
| 
 | ||||
| 	Context("when given a path to a binary that runs for a long time", func() { | ||||
| 		It("can start and stop that binary", func() { | ||||
| 			sessionBuffer := gbytes.NewBuffer() | ||||
| 			fmt.Fprint(sessionBuffer, "Everything is fine") | ||||
| 			fakeSession.BufferReturns(sessionBuffer) | ||||
| 				fakeSession.ExitCodeReturnsOnCall(0, -1) | ||||
| 				fakeSession.ExitCodeReturnsOnCall(1, 143) | ||||
| 
 | ||||
| 			fakeSession.ExitCodeReturnsOnCall(0, -1) | ||||
| 			fakeSession.ExitCodeReturnsOnCall(1, 143) | ||||
| 				fakePathFinder.Returns("/some/path/to/apiserver") | ||||
| 				fakeAddressManager.InitializeReturns(1234, "this.is.the.API.server", nil) | ||||
| 
 | ||||
| 			fakePathFinder.Returns("/some/path/to/apiserver") | ||||
| 				apiServer.ProcessStarter = func(command *exec.Cmd, out, err io.Writer) (SimpleSession, error) { | ||||
| 					Expect(command.Args).To(ContainElement("--insecure-port=1234")) | ||||
| 					Expect(command.Args).To(ContainElement("--insecure-bind-address=this.is.the.API.server")) | ||||
| 					Expect(command.Path).To(Equal("/some/path/to/apiserver")) | ||||
| 					fmt.Fprint(err, "Serving insecurely on this.is.the.API.server:1234") | ||||
| 					return fakeSession, nil | ||||
| 				} | ||||
| 
 | ||||
| 			apiServer.ProcessStarter = func(command *exec.Cmd, out, err io.Writer) (SimpleSession, error) { | ||||
| 				Expect(command.Path).To(Equal("/some/path/to/apiserver")) | ||||
| 				fmt.Fprint(err, "Serving insecurely on this.is.the.API.server:8080") | ||||
| 				return fakeSession, nil | ||||
| 			} | ||||
| 				By("Starting the API Server") | ||||
| 				err := apiServer.Start() | ||||
| 				Expect(err).NotTo(HaveOccurred()) | ||||
| 
 | ||||
| 			By("Starting the API Server") | ||||
| 			err := apiServer.Start() | ||||
| 			Expect(err).NotTo(HaveOccurred()) | ||||
| 				By("...in turn starting Etcd") | ||||
| 				Expect(fakeEtcdProcess.StartCallCount()).To(Equal(1), | ||||
| 					"the Etcd process should be started exactly once") | ||||
| 
 | ||||
| 			By("starting Etcd") | ||||
| 			Expect(fakeEtcdProcess.StartCallCount()).To(Equal(1), | ||||
| 				"the Etcd process should be started exactly once") | ||||
| 				By("...in turn calling the PathFinder") | ||||
| 				Expect(fakePathFinder.CallCount()).To(Equal(1)) | ||||
| 				Expect(fakePathFinder.ArgsForCall(0)).To(Equal("kube-apiserver")) | ||||
| 
 | ||||
| 			By("...in turn calls the PathFinder") | ||||
| 			Expect(fakePathFinder.CallCount()).To(Equal(1)) | ||||
| 			Expect(fakePathFinder.ArgsForCall(0)).To(Equal("kube-apiserver")) | ||||
| 				By("...in turn calling the PortFinder") | ||||
| 				Expect(fakeAddressManager.InitializeCallCount()).To(Equal(1)) | ||||
| 				Expect(fakeAddressManager.InitializeArgsForCall(0)).To(Equal("localhost")) | ||||
| 
 | ||||
| 			Eventually(apiServer).Should(gbytes.Say("Everything is fine")) | ||||
| 			Expect(fakeSession.ExitCodeCallCount()).To(Equal(0)) | ||||
| 			Expect(apiServer).NotTo(gexec.Exit()) | ||||
| 			Expect(fakeSession.ExitCodeCallCount()).To(Equal(1)) | ||||
| 			Expect(fakeCertDirManager.CreateCallCount()).To(Equal(1)) | ||||
| 				Eventually(apiServer).Should(gbytes.Say("Everything is fine")) | ||||
| 				Expect(fakeSession.ExitCodeCallCount()).To(Equal(0)) | ||||
| 				Expect(apiServer).NotTo(gexec.Exit()) | ||||
| 				Expect(fakeSession.ExitCodeCallCount()).To(Equal(1)) | ||||
| 				Expect(fakeCertDirManager.CreateCallCount()).To(Equal(1)) | ||||
| 
 | ||||
| 			By("Stopping the API Server") | ||||
| 			apiServer.Stop() | ||||
| 				By("Stopping the API Server") | ||||
| 				apiServer.Stop() | ||||
| 
 | ||||
| 			Expect(fakeEtcdProcess.StopCallCount()).To(Equal(1)) | ||||
| 			Expect(apiServer).To(gexec.Exit(143)) | ||||
| 			Expect(fakeSession.TerminateCallCount()).To(Equal(1)) | ||||
| 			Expect(fakeSession.WaitCallCount()).To(Equal(1)) | ||||
| 			Expect(fakeSession.ExitCodeCallCount()).To(Equal(2)) | ||||
| 			Expect(fakeCertDirManager.DestroyCallCount()).To(Equal(1)) | ||||
| 				Expect(fakeEtcdProcess.StopCallCount()).To(Equal(1)) | ||||
| 				Expect(apiServer).To(gexec.Exit(143)) | ||||
| 				Expect(fakeSession.TerminateCallCount()).To(Equal(1)) | ||||
| 				Expect(fakeSession.WaitCallCount()).To(Equal(1)) | ||||
| 				Expect(fakeSession.ExitCodeCallCount()).To(Equal(2)) | ||||
| 				Expect(fakeCertDirManager.DestroyCallCount()).To(Equal(1)) | ||||
| 			}) | ||||
| 		}) | ||||
| 	}) | ||||
| 
 | ||||
| 	Context("when starting etcd fails", func() { | ||||
| 		It("propagates the error, and does not start the process", func() { | ||||
| 			fakeEtcdProcess.StartReturnsOnCall(0, fmt.Errorf("starting etcd failed")) | ||||
| 			apiServer.ProcessStarter = func(Command *exec.Cmd, out, err io.Writer) (SimpleSession, error) { | ||||
| 				Expect(true).To(BeFalse(), | ||||
| 					"the api server process starter shouldn't be called if starting etcd fails") | ||||
| 				return nil, nil | ||||
| 			} | ||||
| 		Context("when starting etcd fails", func() { | ||||
| 			It("propagates the error, and does not start the process", func() { | ||||
| 				fakeEtcdProcess.StartReturnsOnCall(0, fmt.Errorf("starting etcd failed")) | ||||
| 				apiServer.ProcessStarter = func(Command *exec.Cmd, out, err io.Writer) (SimpleSession, error) { | ||||
| 					Expect(true).To(BeFalse(), | ||||
| 						"the api server process starter shouldn't be called if starting etcd fails") | ||||
| 					return nil, nil | ||||
| 				} | ||||
| 
 | ||||
| 			err := apiServer.Start() | ||||
| 			Expect(err).To(MatchError(ContainSubstring("starting etcd failed"))) | ||||
| 				err := apiServer.Start() | ||||
| 				Expect(err).To(MatchError(ContainSubstring("starting etcd failed"))) | ||||
| 			}) | ||||
| 		}) | ||||
| 	}) | ||||
| 
 | ||||
| 	Context("when the certificate directory cannot be created", func() { | ||||
| 		It("propagates the error, and does not start the process", func() { | ||||
| 			fakeCertDirManager.CreateReturnsOnCall(0, "", fmt.Errorf("Error on cert directory creation.")) | ||||
| 		Context("when the certificate directory cannot be created", func() { | ||||
| 			It("propagates the error, and does not start any process", func() { | ||||
| 				fakeCertDirManager.CreateReturnsOnCall(0, "", fmt.Errorf("Error on cert directory creation.")) | ||||
| 
 | ||||
| 			apiServer.ProcessStarter = func(Command *exec.Cmd, out, err io.Writer) (SimpleSession, error) { | ||||
| 				Expect(true).To(BeFalse(), | ||||
| 					"the api server process starter shouldn't be called if creating the cert dir fails") | ||||
| 				return nil, nil | ||||
| 			} | ||||
| 				apiServer.ProcessStarter = func(Command *exec.Cmd, out, err io.Writer) (SimpleSession, error) { | ||||
| 					Expect(true).To(BeFalse(), | ||||
| 						"the api server process starter shouldn't be called if creating the cert dir fails") | ||||
| 					return nil, nil | ||||
| 				} | ||||
| 
 | ||||
| 			err := apiServer.Start() | ||||
| 			Expect(err).To(MatchError(ContainSubstring("Error on cert directory creation."))) | ||||
| 				err := apiServer.Start() | ||||
| 				Expect(err).To(MatchError(ContainSubstring("Error on cert directory creation."))) | ||||
| 				Expect(fakeEtcdProcess.StartCallCount()).To(Equal(0)) | ||||
| 			}) | ||||
| 		}) | ||||
| 	}) | ||||
| 
 | ||||
| 	Context("when  the starter returns an error", func() { | ||||
| 		It("propagates the error", func() { | ||||
| 			apiServer.ProcessStarter = func(command *exec.Cmd, out, err io.Writer) (SimpleSession, error) { | ||||
| 				return nil, fmt.Errorf("Some error in the apiserver starter.") | ||||
| 			} | ||||
| 		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")) | ||||
| 
 | ||||
| 			err := apiServer.Start() | ||||
| 			Expect(err).To(MatchError(ContainSubstring("Some error in the apiserver starter."))) | ||||
| 				apiServer.ProcessStarter = func(Command *exec.Cmd, out, err io.Writer) (SimpleSession, error) { | ||||
| 					Expect(true).To(BeFalse(), | ||||
| 						"the api server process starter shouldn't be called if getting a free port fails") | ||||
| 					return nil, nil | ||||
| 				} | ||||
| 
 | ||||
| 				Expect(apiServer.Start()).To(MatchError(ContainSubstring("some error finding a free port"))) | ||||
| 				Expect(fakeEtcdProcess.StartCallCount()).To(Equal(0)) | ||||
| 			}) | ||||
| 		}) | ||||
| 
 | ||||
| 		Context("when  the starter returns an error", func() { | ||||
| 			It("propagates the error", func() { | ||||
| 				apiServer.ProcessStarter = func(command *exec.Cmd, out, err io.Writer) (SimpleSession, error) { | ||||
| 					return nil, fmt.Errorf("Some error in the apiserver starter.") | ||||
| 				} | ||||
| 
 | ||||
| 				err := apiServer.Start() | ||||
| 				Expect(err).To(MatchError(ContainSubstring("Some error in the apiserver starter."))) | ||||
| 			}) | ||||
| 		}) | ||||
| 
 | ||||
| 		Context("when we try to stop a server that hasn't been started", func() { | ||||
| 			It("is a noop and does not call exit on the session", func() { | ||||
| 				apiServer.ProcessStarter = func(command *exec.Cmd, out, err io.Writer) (SimpleSession, error) { | ||||
| 					return fakeSession, nil | ||||
| 				} | ||||
| 				apiServer.Stop() | ||||
| 				Expect(fakeSession.ExitCodeCallCount()).To(Equal(0)) | ||||
| 			}) | ||||
| 		}) | ||||
| 
 | ||||
| 	}) | ||||
| 
 | ||||
| 	Context("when we try to stop a server that hasn't been started", func() { | ||||
| 		It("is a noop and does not call exit on the session", func() { | ||||
| 			apiServer.ProcessStarter = func(command *exec.Cmd, out, err io.Writer) (SimpleSession, error) { | ||||
| 				return fakeSession, nil | ||||
| 			} | ||||
| 			apiServer.Stop() | ||||
| 			Expect(fakeSession.ExitCodeCallCount()).To(Equal(0)) | ||||
| 	Describe("querying the server for its URL", func() { | ||||
| 		It("can be queried for the URL it listens on", func() { | ||||
| 			fakeAddressManager.HostReturns("the.host.for.api.server", nil) | ||||
| 			fakeAddressManager.PortReturns(5678, nil) | ||||
| 			Expect(apiServer.URL()).To(Equal("http://the.host.for.api.server:5678")) | ||||
| 		}) | ||||
| 
 | ||||
| 		Context("when we query for the URL before starting the server", func() { | ||||
| 
 | ||||
| 			Context("and so the addressmanager fails to give us a port", func() { | ||||
| 				It("propagates the failure", func() { | ||||
| 					//TODO
 | ||||
| 				}) | ||||
| 			}) | ||||
| 
 | ||||
| 			Context("and so the addressmanager fails to give us a host", func() { | ||||
| 				It("propagates the failure", func() { | ||||
| 					//TODO
 | ||||
| 				}) | ||||
| 			}) | ||||
| 
 | ||||
| 		}) | ||||
| 	}) | ||||
| }) | ||||
|  |  | |||
|  | @ -5,11 +5,70 @@ import ( | |||
| 	"net" | ||||
| ) | ||||
| 
 | ||||
| // AddressManager knows how to generate and remember a single address on some
 | ||||
| // local interface for a service to listen on.
 | ||||
| type AddressManager interface { | ||||
| 	Initialize(host string) (port int, resolvedAddress string, err error) | ||||
| 	Host() (string, error) | ||||
| 	Port() (int, error) | ||||
| } | ||||
| 
 | ||||
| //go:generate counterfeiter . AddressManager
 | ||||
| 
 | ||||
| // DefaultAddressManager implements an AddressManager. It allocates a new address
 | ||||
| // (interface & port) a process can bind and keeps track of that.
 | ||||
| type DefaultAddressManager struct { | ||||
| 	port int | ||||
| 	host string | ||||
| } | ||||
| 
 | ||||
| // Initialize returns a address a process can listen on. Given a hostname it returns an address,
 | ||||
| // a tuple consisting of a free port and the hostname resolved to its IP.
 | ||||
| func (d *DefaultAddressManager) Initialize(host string) (port int, resolvedHost string, err error) { | ||||
| 	if d.port != 0 { | ||||
| 		return 0, "", fmt.Errorf("this DefaultAddressManager is already initialized") | ||||
| 	} | ||||
| 	addr, err := net.ResolveTCPAddr("tcp", fmt.Sprintf("%s:0", host)) | ||||
| 	if err != nil { | ||||
| 		return | ||||
| 	} | ||||
| 	l, err := net.ListenTCP("tcp", addr) | ||||
| 	if err != nil { | ||||
| 		return | ||||
| 	} | ||||
| 	d.port = l.Addr().(*net.TCPAddr).Port | ||||
| 	defer func() { | ||||
| 		err = l.Close() | ||||
| 	}() | ||||
| 	d.host = addr.IP.String() | ||||
| 	return d.port, d.host, nil | ||||
| } | ||||
| 
 | ||||
| // Port returns the port that this DefaultAddressManager is managing. Port returns an
 | ||||
| // error if this DefaultAddressManager has not yet been initialized.
 | ||||
| func (d *DefaultAddressManager) Port() (int, error) { | ||||
| 	if d.port == 0 { | ||||
| 		return 0, fmt.Errorf("this DefaultAdressManager has is not initialized yet") | ||||
| 	} | ||||
| 	return d.port, nil | ||||
| } | ||||
| 
 | ||||
| // Host returns the host that this DefaultAddressManager is managing. Host returns an
 | ||||
| // error if this DefaultAddressManager has not yet been initialized.
 | ||||
| func (d *DefaultAddressManager) Host() (string, error) { | ||||
| 	if d.host == "" { | ||||
| 		return "", fmt.Errorf("this DefaultAdressManager has is not initialized yet") | ||||
| 	} | ||||
| 	return d.host, nil | ||||
| } | ||||
| 
 | ||||
| // PortFinder is the signature of a function returning a free port and a resolved
 | ||||
| // address to listen/bind on, and an erro in case there were some problems finding
 | ||||
| // a free pair of address & port
 | ||||
| type PortFinder func(host string) (port int, resolvedAddress string, err error) | ||||
| 
 | ||||
| //go:generate counterfeiter . PortFinder
 | ||||
| 
 | ||||
| // DefaultPortFinder is the default implementation of PortFinder: It resolves that
 | ||||
| // hostname or IP handed in and asks the kernel for a random port. To make that a bit
 | ||||
| // safer, it also tries to bind to that port to make sure it is not in use. If all goes
 | ||||
|  |  | |||
|  | @ -3,10 +3,90 @@ package test_test | |||
| import ( | ||||
| 	. "k8s.io/kubectl/pkg/framework/test" | ||||
| 
 | ||||
| 	"fmt" | ||||
| 	"net" | ||||
| 
 | ||||
| 	. "github.com/onsi/ginkgo" | ||||
| 	. "github.com/onsi/gomega" | ||||
| ) | ||||
| 
 | ||||
| var _ = Describe("DefaultAddressManager", func() { | ||||
| 	var defaultAddressManager *DefaultAddressManager | ||||
| 	BeforeEach(func() { | ||||
| 		defaultAddressManager = &DefaultAddressManager{} | ||||
| 	}) | ||||
| 
 | ||||
| 	Describe("Initialize", func() { | ||||
| 		It("returns a free port and an address to bind to", func() { | ||||
| 			port, host, err := defaultAddressManager.Initialize("localhost") | ||||
| 
 | ||||
| 			Expect(err).NotTo(HaveOccurred()) | ||||
| 			Expect(host).To(Equal("127.0.0.1")) | ||||
| 			Expect(port).NotTo(Equal(0)) | ||||
| 
 | ||||
| 			addr, err := net.ResolveTCPAddr("tcp", fmt.Sprintf("%s:%d", host, port)) | ||||
| 			Expect(err).NotTo(HaveOccurred()) | ||||
| 			l, err := net.ListenTCP("tcp", addr) | ||||
| 			defer func() { | ||||
| 				Expect(l.Close()).To(Succeed()) | ||||
| 			}() | ||||
| 			Expect(err).NotTo(HaveOccurred()) | ||||
| 		}) | ||||
| 
 | ||||
| 		Context("when given an invalid hostname", func() { | ||||
| 			It("propagates the error", func() { | ||||
| 				_, _, err := defaultAddressManager.Initialize("this is not a hostname") | ||||
| 
 | ||||
| 				Expect(err).To(MatchError(ContainSubstring("no such host"))) | ||||
| 			}) | ||||
| 		}) | ||||
| 
 | ||||
| 		Context("when given a hostname that we don't have permission to listen on", func() { | ||||
| 			It("propagates the error", func() { | ||||
| 				_, _, err := defaultAddressManager.Initialize("example.com") | ||||
| 
 | ||||
| 				Expect(err).To(MatchError(ContainSubstring("bind: can't assign requested address"))) | ||||
| 			}) | ||||
| 		}) | ||||
| 
 | ||||
| 		Context("initialized multiple times", func() { | ||||
| 			It("fails", func() { | ||||
| 				_, _, err := defaultAddressManager.Initialize("localhost") | ||||
| 				Expect(err).NotTo(HaveOccurred()) | ||||
| 				_, _, err = defaultAddressManager.Initialize("localhost") | ||||
| 				Expect(err).To(MatchError(ContainSubstring("already initialized"))) | ||||
| 			}) | ||||
| 		}) | ||||
| 	}) | ||||
| 	Describe("Port", func() { | ||||
| 		It("returns an error if Initialize has not been called yet", func() { | ||||
| 			_, err := defaultAddressManager.Port() | ||||
| 			Expect(err).To(MatchError(ContainSubstring("not initialized yet"))) | ||||
| 		}) | ||||
| 		It("returns the same port as previously allocated by Initialize", func() { | ||||
| 			expectedPort, _, err := defaultAddressManager.Initialize("localhost") | ||||
| 			Expect(err).NotTo(HaveOccurred()) | ||||
| 			actualPort, err := defaultAddressManager.Port() | ||||
| 			Expect(err).NotTo(HaveOccurred()) | ||||
| 			Expect(actualPort).To(Equal(expectedPort)) | ||||
| 		}) | ||||
| 	}) | ||||
| 	Describe("Host", func() { | ||||
| 		It("returns an error if Initialize has not been called yet", func() { | ||||
| 			_, err := defaultAddressManager.Host() | ||||
| 			Expect(err).To(MatchError(ContainSubstring("not initialized yet"))) | ||||
| 		}) | ||||
| 		It("returns the same port as previously allocated by Initialize", func() { | ||||
| 			_, expectedHost, err := defaultAddressManager.Initialize("localhost") | ||||
| 			Expect(err).NotTo(HaveOccurred()) | ||||
| 			actualHost, err := defaultAddressManager.Host() | ||||
| 			Expect(err).NotTo(HaveOccurred()) | ||||
| 			Expect(actualHost).To(Equal(expectedHost)) | ||||
| 		}) | ||||
| 
 | ||||
| 	}) | ||||
| }) | ||||
| 
 | ||||
| var _ = Describe("DefaultPortFinder", func() { | ||||
| 	It("returns a free port and an address to bind to", func() { | ||||
| 		port, addr, err := DefaultPortFinder("127.0.0.1") | ||||
|  |  | |||
|  | @ -0,0 +1,220 @@ | |||
| // Code generated by counterfeiter. DO NOT EDIT.
 | ||||
| package testfakes | ||||
| 
 | ||||
| import ( | ||||
| 	"sync" | ||||
| 
 | ||||
| 	"k8s.io/kubectl/pkg/framework/test" | ||||
| ) | ||||
| 
 | ||||
| type FakeAddressManager struct { | ||||
| 	InitializeStub        func(host string) (port int, resolvedAddress string, err error) | ||||
| 	initializeMutex       sync.RWMutex | ||||
| 	initializeArgsForCall []struct { | ||||
| 		host string | ||||
| 	} | ||||
| 	initializeReturns struct { | ||||
| 		result1 int | ||||
| 		result2 string | ||||
| 		result3 error | ||||
| 	} | ||||
| 	initializeReturnsOnCall map[int]struct { | ||||
| 		result1 int | ||||
| 		result2 string | ||||
| 		result3 error | ||||
| 	} | ||||
| 	HostStub        func() (string, error) | ||||
| 	hostMutex       sync.RWMutex | ||||
| 	hostArgsForCall []struct{} | ||||
| 	hostReturns     struct { | ||||
| 		result1 string | ||||
| 		result2 error | ||||
| 	} | ||||
| 	hostReturnsOnCall map[int]struct { | ||||
| 		result1 string | ||||
| 		result2 error | ||||
| 	} | ||||
| 	PortStub        func() (int, error) | ||||
| 	portMutex       sync.RWMutex | ||||
| 	portArgsForCall []struct{} | ||||
| 	portReturns     struct { | ||||
| 		result1 int | ||||
| 		result2 error | ||||
| 	} | ||||
| 	portReturnsOnCall map[int]struct { | ||||
| 		result1 int | ||||
| 		result2 error | ||||
| 	} | ||||
| 	invocations      map[string][][]interface{} | ||||
| 	invocationsMutex sync.RWMutex | ||||
| } | ||||
| 
 | ||||
| func (fake *FakeAddressManager) Initialize(host string) (port int, resolvedAddress string, err error) { | ||||
| 	fake.initializeMutex.Lock() | ||||
| 	ret, specificReturn := fake.initializeReturnsOnCall[len(fake.initializeArgsForCall)] | ||||
| 	fake.initializeArgsForCall = append(fake.initializeArgsForCall, struct { | ||||
| 		host string | ||||
| 	}{host}) | ||||
| 	fake.recordInvocation("Initialize", []interface{}{host}) | ||||
| 	fake.initializeMutex.Unlock() | ||||
| 	if fake.InitializeStub != nil { | ||||
| 		return fake.InitializeStub(host) | ||||
| 	} | ||||
| 	if specificReturn { | ||||
| 		return ret.result1, ret.result2, ret.result3 | ||||
| 	} | ||||
| 	return fake.initializeReturns.result1, fake.initializeReturns.result2, fake.initializeReturns.result3 | ||||
| } | ||||
| 
 | ||||
| func (fake *FakeAddressManager) InitializeCallCount() int { | ||||
| 	fake.initializeMutex.RLock() | ||||
| 	defer fake.initializeMutex.RUnlock() | ||||
| 	return len(fake.initializeArgsForCall) | ||||
| } | ||||
| 
 | ||||
| func (fake *FakeAddressManager) InitializeArgsForCall(i int) string { | ||||
| 	fake.initializeMutex.RLock() | ||||
| 	defer fake.initializeMutex.RUnlock() | ||||
| 	return fake.initializeArgsForCall[i].host | ||||
| } | ||||
| 
 | ||||
| func (fake *FakeAddressManager) InitializeReturns(result1 int, result2 string, result3 error) { | ||||
| 	fake.InitializeStub = nil | ||||
| 	fake.initializeReturns = struct { | ||||
| 		result1 int | ||||
| 		result2 string | ||||
| 		result3 error | ||||
| 	}{result1, result2, result3} | ||||
| } | ||||
| 
 | ||||
| func (fake *FakeAddressManager) InitializeReturnsOnCall(i int, result1 int, result2 string, result3 error) { | ||||
| 	fake.InitializeStub = nil | ||||
| 	if fake.initializeReturnsOnCall == nil { | ||||
| 		fake.initializeReturnsOnCall = make(map[int]struct { | ||||
| 			result1 int | ||||
| 			result2 string | ||||
| 			result3 error | ||||
| 		}) | ||||
| 	} | ||||
| 	fake.initializeReturnsOnCall[i] = struct { | ||||
| 		result1 int | ||||
| 		result2 string | ||||
| 		result3 error | ||||
| 	}{result1, result2, result3} | ||||
| } | ||||
| 
 | ||||
| func (fake *FakeAddressManager) Host() (string, error) { | ||||
| 	fake.hostMutex.Lock() | ||||
| 	ret, specificReturn := fake.hostReturnsOnCall[len(fake.hostArgsForCall)] | ||||
| 	fake.hostArgsForCall = append(fake.hostArgsForCall, struct{}{}) | ||||
| 	fake.recordInvocation("Host", []interface{}{}) | ||||
| 	fake.hostMutex.Unlock() | ||||
| 	if fake.HostStub != nil { | ||||
| 		return fake.HostStub() | ||||
| 	} | ||||
| 	if specificReturn { | ||||
| 		return ret.result1, ret.result2 | ||||
| 	} | ||||
| 	return fake.hostReturns.result1, fake.hostReturns.result2 | ||||
| } | ||||
| 
 | ||||
| func (fake *FakeAddressManager) HostCallCount() int { | ||||
| 	fake.hostMutex.RLock() | ||||
| 	defer fake.hostMutex.RUnlock() | ||||
| 	return len(fake.hostArgsForCall) | ||||
| } | ||||
| 
 | ||||
| func (fake *FakeAddressManager) HostReturns(result1 string, result2 error) { | ||||
| 	fake.HostStub = nil | ||||
| 	fake.hostReturns = struct { | ||||
| 		result1 string | ||||
| 		result2 error | ||||
| 	}{result1, result2} | ||||
| } | ||||
| 
 | ||||
| func (fake *FakeAddressManager) HostReturnsOnCall(i int, result1 string, result2 error) { | ||||
| 	fake.HostStub = nil | ||||
| 	if fake.hostReturnsOnCall == nil { | ||||
| 		fake.hostReturnsOnCall = make(map[int]struct { | ||||
| 			result1 string | ||||
| 			result2 error | ||||
| 		}) | ||||
| 	} | ||||
| 	fake.hostReturnsOnCall[i] = struct { | ||||
| 		result1 string | ||||
| 		result2 error | ||||
| 	}{result1, result2} | ||||
| } | ||||
| 
 | ||||
| func (fake *FakeAddressManager) Port() (int, error) { | ||||
| 	fake.portMutex.Lock() | ||||
| 	ret, specificReturn := fake.portReturnsOnCall[len(fake.portArgsForCall)] | ||||
| 	fake.portArgsForCall = append(fake.portArgsForCall, struct{}{}) | ||||
| 	fake.recordInvocation("Port", []interface{}{}) | ||||
| 	fake.portMutex.Unlock() | ||||
| 	if fake.PortStub != nil { | ||||
| 		return fake.PortStub() | ||||
| 	} | ||||
| 	if specificReturn { | ||||
| 		return ret.result1, ret.result2 | ||||
| 	} | ||||
| 	return fake.portReturns.result1, fake.portReturns.result2 | ||||
| } | ||||
| 
 | ||||
| func (fake *FakeAddressManager) PortCallCount() int { | ||||
| 	fake.portMutex.RLock() | ||||
| 	defer fake.portMutex.RUnlock() | ||||
| 	return len(fake.portArgsForCall) | ||||
| } | ||||
| 
 | ||||
| func (fake *FakeAddressManager) PortReturns(result1 int, result2 error) { | ||||
| 	fake.PortStub = nil | ||||
| 	fake.portReturns = struct { | ||||
| 		result1 int | ||||
| 		result2 error | ||||
| 	}{result1, result2} | ||||
| } | ||||
| 
 | ||||
| func (fake *FakeAddressManager) PortReturnsOnCall(i int, result1 int, result2 error) { | ||||
| 	fake.PortStub = nil | ||||
| 	if fake.portReturnsOnCall == nil { | ||||
| 		fake.portReturnsOnCall = make(map[int]struct { | ||||
| 			result1 int | ||||
| 			result2 error | ||||
| 		}) | ||||
| 	} | ||||
| 	fake.portReturnsOnCall[i] = struct { | ||||
| 		result1 int | ||||
| 		result2 error | ||||
| 	}{result1, result2} | ||||
| } | ||||
| 
 | ||||
| func (fake *FakeAddressManager) Invocations() map[string][][]interface{} { | ||||
| 	fake.invocationsMutex.RLock() | ||||
| 	defer fake.invocationsMutex.RUnlock() | ||||
| 	fake.initializeMutex.RLock() | ||||
| 	defer fake.initializeMutex.RUnlock() | ||||
| 	fake.hostMutex.RLock() | ||||
| 	defer fake.hostMutex.RUnlock() | ||||
| 	fake.portMutex.RLock() | ||||
| 	defer fake.portMutex.RUnlock() | ||||
| 	copiedInvocations := map[string][][]interface{}{} | ||||
| 	for key, value := range fake.invocations { | ||||
| 		copiedInvocations[key] = value | ||||
| 	} | ||||
| 	return copiedInvocations | ||||
| } | ||||
| 
 | ||||
| func (fake *FakeAddressManager) recordInvocation(key string, args []interface{}) { | ||||
| 	fake.invocationsMutex.Lock() | ||||
| 	defer fake.invocationsMutex.Unlock() | ||||
| 	if fake.invocations == nil { | ||||
| 		fake.invocations = map[string][][]interface{}{} | ||||
| 	} | ||||
| 	if fake.invocations[key] == nil { | ||||
| 		fake.invocations[key] = [][]interface{}{} | ||||
| 	} | ||||
| 	fake.invocations[key] = append(fake.invocations[key], args) | ||||
| } | ||||
| 
 | ||||
| var _ test.AddressManager = new(FakeAddressManager) | ||||
		Loading…
	
		Reference in New Issue