Merge pull request #155 from hoegaarden/proper_fake_for_apiserver
Proper fake for apiserver
This commit is contained in:
commit
88e83f4286
|
|
@ -5,6 +5,8 @@ import (
|
||||||
"os/exec"
|
"os/exec"
|
||||||
"time"
|
"time"
|
||||||
|
|
||||||
|
"io"
|
||||||
|
|
||||||
"github.com/onsi/gomega"
|
"github.com/onsi/gomega"
|
||||||
"github.com/onsi/gomega/gbytes"
|
"github.com/onsi/gomega/gbytes"
|
||||||
"github.com/onsi/gomega/gexec"
|
"github.com/onsi/gomega/gexec"
|
||||||
|
|
@ -15,10 +17,11 @@ type APIServer struct {
|
||||||
// The path to the apiserver binary
|
// The path to the apiserver binary
|
||||||
Path string
|
Path string
|
||||||
EtcdURL string
|
EtcdURL string
|
||||||
session *gexec.Session
|
ProcessStarter simpleSessionStarter
|
||||||
|
CertDirManager certDirManager
|
||||||
|
session SimpleSession
|
||||||
stdOut *gbytes.Buffer
|
stdOut *gbytes.Buffer
|
||||||
stdErr *gbytes.Buffer
|
stdErr *gbytes.Buffer
|
||||||
certDirManager certDirManager
|
|
||||||
}
|
}
|
||||||
|
|
||||||
type certDirManager interface {
|
type certDirManager interface {
|
||||||
|
|
@ -26,13 +29,29 @@ type certDirManager interface {
|
||||||
Destroy() error
|
Destroy() error
|
||||||
}
|
}
|
||||||
|
|
||||||
|
//go:generate counterfeiter . certDirManager
|
||||||
|
|
||||||
|
func NewAPIServer(pathToAPIServer, etcdURL string) *APIServer {
|
||||||
|
starter := func(command *exec.Cmd, out, err io.Writer) (SimpleSession, error) {
|
||||||
|
return gexec.Start(command, out, err)
|
||||||
|
}
|
||||||
|
|
||||||
|
apiserver := &APIServer{
|
||||||
|
Path: pathToAPIServer,
|
||||||
|
EtcdURL: etcdURL,
|
||||||
|
ProcessStarter: starter,
|
||||||
|
CertDirManager: NewTempDirManager(),
|
||||||
|
}
|
||||||
|
|
||||||
|
return apiserver
|
||||||
|
}
|
||||||
|
|
||||||
// Start starts the apiserver, waits for it to come up, and returns an error, if occoured.
|
// Start starts the apiserver, waits for it to come up, and returns an error, if occoured.
|
||||||
func (s *APIServer) Start() error {
|
func (s *APIServer) Start() error {
|
||||||
s.certDirManager = NewTempDirManager()
|
|
||||||
s.stdOut = gbytes.NewBuffer()
|
s.stdOut = gbytes.NewBuffer()
|
||||||
s.stdErr = gbytes.NewBuffer()
|
s.stdErr = gbytes.NewBuffer()
|
||||||
|
|
||||||
certDir, err := s.certDirManager.Create()
|
certDir, err := s.CertDirManager.Create()
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return err
|
return err
|
||||||
}
|
}
|
||||||
|
|
@ -55,7 +74,7 @@ func (s *APIServer) Start() error {
|
||||||
timedOut := time.After(20 * time.Second)
|
timedOut := time.After(20 * time.Second)
|
||||||
|
|
||||||
command := exec.Command(s.Path, args...)
|
command := exec.Command(s.Path, args...)
|
||||||
s.session, err = gexec.Start(command, s.stdOut, s.stdErr)
|
s.session, err = s.ProcessStarter(command, s.stdOut, s.stdErr)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return err
|
return err
|
||||||
}
|
}
|
||||||
|
|
@ -71,8 +90,9 @@ func (s *APIServer) Start() error {
|
||||||
// Stop stops this process gracefully, waits for its termination, and cleans up the cert directory.
|
// Stop stops this process gracefully, waits for its termination, and cleans up the cert directory.
|
||||||
func (s *APIServer) Stop() {
|
func (s *APIServer) Stop() {
|
||||||
if s.session != nil {
|
if s.session != nil {
|
||||||
s.session.Terminate().Wait(20 * time.Second)
|
s.session.Terminate()
|
||||||
err := s.certDirManager.Destroy()
|
s.session.Wait(20 * time.Second)
|
||||||
|
err := s.CertDirManager.Destroy()
|
||||||
gomega.Expect(err).NotTo(gomega.HaveOccurred())
|
gomega.Expect(err).NotTo(gomega.HaveOccurred())
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
||||||
|
|
@ -1,62 +1,106 @@
|
||||||
package test_test
|
package test_test
|
||||||
|
|
||||||
import (
|
import (
|
||||||
|
"io"
|
||||||
|
"os/exec"
|
||||||
|
|
||||||
. "k8s.io/kubectl/pkg/framework/test"
|
. "k8s.io/kubectl/pkg/framework/test"
|
||||||
|
|
||||||
|
"fmt"
|
||||||
|
|
||||||
. "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"
|
||||||
"github.com/onsi/gomega/gexec"
|
"github.com/onsi/gomega/gexec"
|
||||||
|
"k8s.io/kubectl/pkg/framework/test/testfakes"
|
||||||
)
|
)
|
||||||
|
|
||||||
var _ = Describe("Apiserver", func() {
|
var _ = Describe("Apiserver", func() {
|
||||||
|
var (
|
||||||
|
fakeSession *testfakes.FakeSimpleSession
|
||||||
|
fakeCertDirManager *testfakes.FakeCertDirManager
|
||||||
|
apiServer *APIServer
|
||||||
|
)
|
||||||
|
|
||||||
|
BeforeEach(func() {
|
||||||
|
fakeSession = &testfakes.FakeSimpleSession{}
|
||||||
|
fakeCertDirManager = &testfakes.FakeCertDirManager{}
|
||||||
|
|
||||||
|
apiServer = &APIServer{
|
||||||
|
Path: "",
|
||||||
|
EtcdURL: "the etcd url",
|
||||||
|
CertDirManager: fakeCertDirManager,
|
||||||
|
}
|
||||||
|
})
|
||||||
|
|
||||||
Context("when given a path to a binary that runs for a long time", func() {
|
Context("when given a path to a binary that runs for a long time", func() {
|
||||||
It("can start and stop that binary", func() {
|
It("can start and stop that binary", func() {
|
||||||
pathToFakeAPIServer, err := gexec.Build("k8s.io/kubectl/pkg/framework/test/assets/fakeapiserver")
|
sessionBuffer := gbytes.NewBuffer()
|
||||||
Expect(err).NotTo(HaveOccurred())
|
fmt.Fprint(sessionBuffer, "Everything is fine")
|
||||||
apiServer := &APIServer{
|
fakeSession.BufferReturns(sessionBuffer)
|
||||||
Path: pathToFakeAPIServer,
|
|
||||||
EtcdURL: "the etcd url",
|
fakeSession.ExitCodeReturnsOnCall(0, -1)
|
||||||
|
fakeSession.ExitCodeReturnsOnCall(1, 143)
|
||||||
|
|
||||||
|
apiServer.ProcessStarter = func(command *exec.Cmd, out, err io.Writer) (SimpleSession, error) {
|
||||||
|
fmt.Fprint(err, "Serving insecurely on 127.0.0.1:8080")
|
||||||
|
return fakeSession, nil
|
||||||
}
|
}
|
||||||
|
|
||||||
By("Starting the API Server")
|
By("Starting the API Server")
|
||||||
err = apiServer.Start()
|
err := apiServer.Start()
|
||||||
Expect(err).NotTo(HaveOccurred())
|
Expect(err).NotTo(HaveOccurred())
|
||||||
|
|
||||||
Eventually(apiServer).Should(gbytes.Say("Everything is fine"))
|
Eventually(apiServer).Should(gbytes.Say("Everything is fine"))
|
||||||
|
Expect(fakeSession.ExitCodeCallCount()).To(Equal(0))
|
||||||
Expect(apiServer).NotTo(gexec.Exit())
|
Expect(apiServer).NotTo(gexec.Exit())
|
||||||
|
Expect(fakeSession.ExitCodeCallCount()).To(Equal(1))
|
||||||
|
Expect(fakeCertDirManager.CreateCallCount()).To(Equal(1))
|
||||||
|
|
||||||
By("Stopping the API Server")
|
By("Stopping the API Server")
|
||||||
apiServer.Stop()
|
apiServer.Stop()
|
||||||
Expect(apiServer).To(gexec.Exit(143))
|
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 no path is given", func() {
|
|
||||||
It("fails with a helpful error", func() {
|
|
||||||
apiServer := &APIServer{}
|
|
||||||
err := apiServer.Start()
|
|
||||||
Expect(err).To(MatchError(ContainSubstring("no such file or directory")))
|
|
||||||
})
|
})
|
||||||
})
|
})
|
||||||
|
|
||||||
Context("when given a path to a non-executable", func() {
|
Context("when the certificate directory cannot be created", func() {
|
||||||
It("fails with a helpful error", func() {
|
It("propagates the error, and does not start the process", func() {
|
||||||
apiServer := &APIServer{
|
fakeCertDirManager.CreateReturnsOnCall(0, "", fmt.Errorf("Error on cert directory creation."))
|
||||||
Path: "./apiserver.go",
|
|
||||||
EtcdURL: "the etcd url",
|
processStarterCounter := 0
|
||||||
|
apiServer.ProcessStarter = func(Command *exec.Cmd, out, err io.Writer) (SimpleSession, error) {
|
||||||
|
processStarterCounter += 1
|
||||||
|
return fakeSession, nil
|
||||||
}
|
}
|
||||||
|
|
||||||
err := apiServer.Start()
|
err := apiServer.Start()
|
||||||
Expect(err).To(MatchError(ContainSubstring("./apiserver.go: permission denied")))
|
Expect(err).To(MatchError(ContainSubstring("Error on cert directory creation.")))
|
||||||
|
Expect(processStarterCounter).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() {
|
Context("when we try to stop a server that hasn't been started", func() {
|
||||||
It("does not panic", func() {
|
It("is a noop and does not call exit on the session", func() {
|
||||||
server := &APIServer{}
|
apiServer.ProcessStarter = func(command *exec.Cmd, out, err io.Writer) (SimpleSession, error) {
|
||||||
server.Stop()
|
return fakeSession, nil
|
||||||
|
}
|
||||||
|
apiServer.Stop()
|
||||||
|
Expect(fakeSession.ExitCodeCallCount()).To(Equal(0))
|
||||||
})
|
})
|
||||||
})
|
})
|
||||||
})
|
})
|
||||||
|
|
|
||||||
|
|
@ -1,44 +0,0 @@
|
||||||
package main
|
|
||||||
|
|
||||||
import (
|
|
||||||
"fmt"
|
|
||||||
"os"
|
|
||||||
"regexp"
|
|
||||||
"time"
|
|
||||||
)
|
|
||||||
|
|
||||||
func main() {
|
|
||||||
expectedArgs := []*regexp.Regexp{
|
|
||||||
regexp.MustCompile("^--authorization-mode=Node,RBAC$"),
|
|
||||||
regexp.MustCompile("^--runtime-config=admissionregistration.k8s.io/v1alpha1$"),
|
|
||||||
regexp.MustCompile("^--v=3$"),
|
|
||||||
regexp.MustCompile("^--vmodule=$"),
|
|
||||||
regexp.MustCompile("^--admission-control=Initializers,NamespaceLifecycle,LimitRanger,ServiceAccount,SecurityContextDeny,DefaultStorageClass,DefaultTolerationSeconds,GenericAdmissionWebhook,ResourceQuota$"),
|
|
||||||
regexp.MustCompile("^--admission-control-config-file=$"),
|
|
||||||
regexp.MustCompile("^--bind-address=0.0.0.0$"),
|
|
||||||
regexp.MustCompile("^--insecure-bind-address=127.0.0.1$"),
|
|
||||||
regexp.MustCompile("^--insecure-port=8080$"),
|
|
||||||
regexp.MustCompile("^--storage-backend=etcd3$"),
|
|
||||||
regexp.MustCompile("^--etcd-servers=the etcd url$"),
|
|
||||||
regexp.MustCompile("^--cert-dir=.*"),
|
|
||||||
}
|
|
||||||
numExpectedArgs := len(expectedArgs)
|
|
||||||
numGivenArgs := len(os.Args) - 1
|
|
||||||
|
|
||||||
if numGivenArgs < numExpectedArgs {
|
|
||||||
fmt.Printf("Expected at least %d args, only got %d\n", numExpectedArgs, numGivenArgs)
|
|
||||||
os.Exit(2)
|
|
||||||
}
|
|
||||||
|
|
||||||
for i, argRegexp := range expectedArgs {
|
|
||||||
givenArg := os.Args[i+1]
|
|
||||||
if !argRegexp.MatchString(givenArg) {
|
|
||||||
fmt.Printf("Expected arg '%s' to match '%s'\n", givenArg, argRegexp.String())
|
|
||||||
os.Exit(1)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
fmt.Println("Everything is fine")
|
|
||||||
fmt.Fprintln(os.Stderr, "Serving insecurely on 127.0.0.1:8080")
|
|
||||||
|
|
||||||
time.Sleep(10 * time.Minute)
|
|
||||||
}
|
|
||||||
|
|
@ -22,13 +22,9 @@ type FixtureProcess interface {
|
||||||
func NewFixtures(pathToEtcd, pathToAPIServer string) *Fixtures {
|
func NewFixtures(pathToEtcd, pathToAPIServer string) *Fixtures {
|
||||||
etcdURL := "http://127.0.0.1:2379"
|
etcdURL := "http://127.0.0.1:2379"
|
||||||
|
|
||||||
etcd := NewEtcd(pathToEtcd, etcdURL)
|
|
||||||
return &Fixtures{
|
return &Fixtures{
|
||||||
Etcd: etcd,
|
Etcd: NewEtcd(pathToEtcd, etcdURL),
|
||||||
APIServer: &APIServer{
|
APIServer: NewAPIServer(pathToAPIServer, etcdURL),
|
||||||
Path: pathToAPIServer,
|
|
||||||
EtcdURL: etcdURL,
|
|
||||||
},
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
||||||
|
|
@ -0,0 +1,140 @@
|
||||||
|
// Code generated by counterfeiter. DO NOT EDIT.
|
||||||
|
package testfakes
|
||||||
|
|
||||||
|
import (
|
||||||
|
"sync"
|
||||||
|
)
|
||||||
|
|
||||||
|
type FakeCertDirManager struct {
|
||||||
|
CreateStub func() (string, error)
|
||||||
|
createMutex sync.RWMutex
|
||||||
|
createArgsForCall []struct{}
|
||||||
|
createReturns struct {
|
||||||
|
result1 string
|
||||||
|
result2 error
|
||||||
|
}
|
||||||
|
createReturnsOnCall map[int]struct {
|
||||||
|
result1 string
|
||||||
|
result2 error
|
||||||
|
}
|
||||||
|
DestroyStub func() error
|
||||||
|
destroyMutex sync.RWMutex
|
||||||
|
destroyArgsForCall []struct{}
|
||||||
|
destroyReturns struct {
|
||||||
|
result1 error
|
||||||
|
}
|
||||||
|
destroyReturnsOnCall map[int]struct {
|
||||||
|
result1 error
|
||||||
|
}
|
||||||
|
invocations map[string][][]interface{}
|
||||||
|
invocationsMutex sync.RWMutex
|
||||||
|
}
|
||||||
|
|
||||||
|
func (fake *FakeCertDirManager) Create() (string, error) {
|
||||||
|
fake.createMutex.Lock()
|
||||||
|
ret, specificReturn := fake.createReturnsOnCall[len(fake.createArgsForCall)]
|
||||||
|
fake.createArgsForCall = append(fake.createArgsForCall, struct{}{})
|
||||||
|
fake.recordInvocation("Create", []interface{}{})
|
||||||
|
fake.createMutex.Unlock()
|
||||||
|
if fake.CreateStub != nil {
|
||||||
|
return fake.CreateStub()
|
||||||
|
}
|
||||||
|
if specificReturn {
|
||||||
|
return ret.result1, ret.result2
|
||||||
|
}
|
||||||
|
return fake.createReturns.result1, fake.createReturns.result2
|
||||||
|
}
|
||||||
|
|
||||||
|
func (fake *FakeCertDirManager) CreateCallCount() int {
|
||||||
|
fake.createMutex.RLock()
|
||||||
|
defer fake.createMutex.RUnlock()
|
||||||
|
return len(fake.createArgsForCall)
|
||||||
|
}
|
||||||
|
|
||||||
|
func (fake *FakeCertDirManager) CreateReturns(result1 string, result2 error) {
|
||||||
|
fake.CreateStub = nil
|
||||||
|
fake.createReturns = struct {
|
||||||
|
result1 string
|
||||||
|
result2 error
|
||||||
|
}{result1, result2}
|
||||||
|
}
|
||||||
|
|
||||||
|
func (fake *FakeCertDirManager) CreateReturnsOnCall(i int, result1 string, result2 error) {
|
||||||
|
fake.CreateStub = nil
|
||||||
|
if fake.createReturnsOnCall == nil {
|
||||||
|
fake.createReturnsOnCall = make(map[int]struct {
|
||||||
|
result1 string
|
||||||
|
result2 error
|
||||||
|
})
|
||||||
|
}
|
||||||
|
fake.createReturnsOnCall[i] = struct {
|
||||||
|
result1 string
|
||||||
|
result2 error
|
||||||
|
}{result1, result2}
|
||||||
|
}
|
||||||
|
|
||||||
|
func (fake *FakeCertDirManager) Destroy() error {
|
||||||
|
fake.destroyMutex.Lock()
|
||||||
|
ret, specificReturn := fake.destroyReturnsOnCall[len(fake.destroyArgsForCall)]
|
||||||
|
fake.destroyArgsForCall = append(fake.destroyArgsForCall, struct{}{})
|
||||||
|
fake.recordInvocation("Destroy", []interface{}{})
|
||||||
|
fake.destroyMutex.Unlock()
|
||||||
|
if fake.DestroyStub != nil {
|
||||||
|
return fake.DestroyStub()
|
||||||
|
}
|
||||||
|
if specificReturn {
|
||||||
|
return ret.result1
|
||||||
|
}
|
||||||
|
return fake.destroyReturns.result1
|
||||||
|
}
|
||||||
|
|
||||||
|
func (fake *FakeCertDirManager) DestroyCallCount() int {
|
||||||
|
fake.destroyMutex.RLock()
|
||||||
|
defer fake.destroyMutex.RUnlock()
|
||||||
|
return len(fake.destroyArgsForCall)
|
||||||
|
}
|
||||||
|
|
||||||
|
func (fake *FakeCertDirManager) DestroyReturns(result1 error) {
|
||||||
|
fake.DestroyStub = nil
|
||||||
|
fake.destroyReturns = struct {
|
||||||
|
result1 error
|
||||||
|
}{result1}
|
||||||
|
}
|
||||||
|
|
||||||
|
func (fake *FakeCertDirManager) DestroyReturnsOnCall(i int, result1 error) {
|
||||||
|
fake.DestroyStub = nil
|
||||||
|
if fake.destroyReturnsOnCall == nil {
|
||||||
|
fake.destroyReturnsOnCall = make(map[int]struct {
|
||||||
|
result1 error
|
||||||
|
})
|
||||||
|
}
|
||||||
|
fake.destroyReturnsOnCall[i] = struct {
|
||||||
|
result1 error
|
||||||
|
}{result1}
|
||||||
|
}
|
||||||
|
|
||||||
|
func (fake *FakeCertDirManager) Invocations() map[string][][]interface{} {
|
||||||
|
fake.invocationsMutex.RLock()
|
||||||
|
defer fake.invocationsMutex.RUnlock()
|
||||||
|
fake.createMutex.RLock()
|
||||||
|
defer fake.createMutex.RUnlock()
|
||||||
|
fake.destroyMutex.RLock()
|
||||||
|
defer fake.destroyMutex.RUnlock()
|
||||||
|
copiedInvocations := map[string][][]interface{}{}
|
||||||
|
for key, value := range fake.invocations {
|
||||||
|
copiedInvocations[key] = value
|
||||||
|
}
|
||||||
|
return copiedInvocations
|
||||||
|
}
|
||||||
|
|
||||||
|
func (fake *FakeCertDirManager) 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)
|
||||||
|
}
|
||||||
Loading…
Reference in New Issue