Skip to content

Commit fafa419

Browse files
committed
Tests for testing/controlplane components
This adds fairly comprehensive tests for the control plane components, which either didn't exist before the refactor or existed very loosely in the form of general integration tests.
1 parent 75286c5 commit fafa419

File tree

6 files changed

+1030
-2
lines changed

6 files changed

+1030
-2
lines changed

pkg/internal/testing/controlplane/apiserver.go

Lines changed: 35 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -15,6 +15,8 @@ import (
1515
"sigs.k8s.io/controller-runtime/pkg/internal/testing/process"
1616
)
1717

18+
// TODO: listenaddr needs to be exposes publically so we can set insecureserving
19+
1820
const (
1921
// saKeyFile is the name of the service account signing private key file
2022
saKeyFile = "sa-signer.key"
@@ -144,6 +146,13 @@ func (s *APIServer) Configure() *process.Arguments {
144146
// Start starts the apiserver, waits for it to come up, and returns an error,
145147
// if occurred.
146148
func (s *APIServer) Start() error {
149+
if err := s.prepare(); err != nil {
150+
return err
151+
}
152+
return s.processState.Start(s.Out, s.Err)
153+
}
154+
155+
func (s *APIServer) prepare() error {
147156
if s.processState == nil {
148157
if err := s.setProcessState(); err != nil {
149158
return err
@@ -152,7 +161,7 @@ func (s *APIServer) Start() error {
152161
if err := s.Authn.Start(); err != nil {
153162
return err
154163
}
155-
return s.processState.Start(s.Out, s.Err)
164+
return nil
156165
}
157166

158167
// configurePorts configures the serving ports for this API server.
@@ -389,13 +398,17 @@ func (s *APIServer) populateAPIServerCerts() error {
389398
// the CertDir if necessary.
390399
func (s *APIServer) Stop() error {
391400
if s.processState != nil {
392-
return s.processState.Stop()
401+
if err := s.processState.Stop(); err != nil {
402+
return err
403+
}
393404
}
394405
return s.Authn.Stop()
395406
}
396407

397408
// APIServerDefaultArgs exposes the default args for the APIServer so that you
398409
// can use those to append your own additional arguments.
410+
//
411+
// Deprecated: use APIServer.Configure()
399412
var APIServerDefaultArgs = []string{
400413
"--advertise-address=127.0.0.1",
401414
"--etcd-servers={{ if .EtcdURL }}{{ .EtcdURL.String }}{{ end }}",
@@ -409,3 +422,23 @@ var APIServerDefaultArgs = []string{
409422
"--service-cluster-ip-range=10.0.0.0/24",
410423
"--allow-privileged=true",
411424
}
425+
426+
// PrepareAPIServer is an internal-only (NEVER SHOULD BE EXPOSED)
427+
// function that sets up the API server just before starting it,
428+
// without actually starting it. This saves time on tests.
429+
//
430+
// NB(directxman12): do not expose this outside of internal -- it's unsafe to
431+
// use, because things like port allocation could race even more than they
432+
// currently do if you later call start!
433+
func PrepareAPIServer(s *APIServer) error {
434+
return s.prepare()
435+
}
436+
437+
// APIServerArguments is an internal-only (NEVER SHOULD BE EXPOSED)
438+
// function that sets up the API server just before starting it,
439+
// without actually starting it. It's public to make testing easier.
440+
//
441+
// NB(directxman12): do not expose this outside of internal
442+
func APIServerArguments(s *APIServer) []string {
443+
return s.processState.Args
444+
}
Lines changed: 264 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,264 @@
1+
package controlplane_test
2+
3+
import (
4+
"errors"
5+
"net/url"
6+
7+
. "github.com/onsi/ginkgo"
8+
. "github.com/onsi/gomega"
9+
"k8s.io/client-go/rest"
10+
11+
. "sigs.k8s.io/controller-runtime/pkg/internal/testing/controlplane"
12+
"sigs.k8s.io/controller-runtime/pkg/internal/testing/process"
13+
)
14+
15+
var _ = Describe("APIServer", func() {
16+
var server *APIServer
17+
BeforeEach(func() {
18+
server = &APIServer{
19+
EtcdURL: &url.URL{},
20+
}
21+
})
22+
JustBeforeEach(func() {
23+
Expect(PrepareAPIServer(server)).To(Succeed())
24+
})
25+
Describe("setting up serving hosts & ports", func() {
26+
Context("when URL is set", func() {
27+
BeforeEach(func() {
28+
server.URL = &url.URL{Scheme: "http", Host: "localhost:8675", Path: "/some-path"}
29+
})
30+
31+
Context("when insecure serving is also set", func() {
32+
BeforeEach(func() {
33+
server.InsecureServing = &process.ListenAddr{
34+
Address: "localhost",
35+
Port: "1234",
36+
}
37+
})
38+
39+
It("should override the existing insecure serving", func() {
40+
Expect(server.InsecureServing).To(Equal(&process.ListenAddr{
41+
Address: "localhost",
42+
Port: "8675",
43+
}))
44+
})
45+
})
46+
47+
It("should set insecure serving off of that", func() {
48+
Expect(server.InsecureServing).To(Equal(&process.ListenAddr{
49+
Address: "localhost",
50+
Port: "8675",
51+
}))
52+
})
53+
54+
It("should keep URL as-is", func() {
55+
Expect(server.URL.String()).To(Equal("http://localhost:8675/some-path"))
56+
})
57+
})
58+
59+
Context("when URL is not set but InsecureServing is set", func() {
60+
BeforeEach(func() {
61+
server.InsecureServing = &process.ListenAddr{}
62+
})
63+
64+
Context("when host and port are set", func() {
65+
BeforeEach(func() {
66+
server.InsecureServing.Address = "localhost"
67+
server.InsecureServing.Port = "8675"
68+
})
69+
It("should set URL from InsecureServing", func() {
70+
Expect(server.URL.String()).To(Equal("http://localhost:8675"))
71+
})
72+
73+
It("should leave InsecureServing as-is if address and port are filled out", func() {
74+
Expect(server.InsecureServing).To(Equal(&process.ListenAddr{
75+
Address: "localhost",
76+
Port: "8675",
77+
}))
78+
})
79+
})
80+
81+
Context("when address and port are not filled out", func() {
82+
BeforeEach(func() {
83+
server.InsecureServing = &process.ListenAddr{}
84+
})
85+
It("should default an insecure port", func() {
86+
Expect(server.InsecureServing.Port).NotTo(BeEmpty())
87+
})
88+
It("should set URL from InsecureServing", func() {
89+
Expect(server.URL.String()).To(Equal("http://" + server.InsecureServing.Address + ":" + server.InsecureServing.Port))
90+
})
91+
})
92+
})
93+
94+
Context("when neither URL or InsecureServing are set", func() {
95+
It("should not default either of them", func() {
96+
Expect(server.URL).To(BeNil(), "no URL should be set")
97+
Expect(server.InsecureServing).To(BeNil(), "no insecure serving details should be set")
98+
})
99+
})
100+
101+
Context("when SecureServing host & port are set", func() {
102+
BeforeEach(func() {
103+
server.Address = "localhost"
104+
server.Port = "8675"
105+
})
106+
107+
It("should leave SecureServing as-is", func() {
108+
Expect(server.SecureServing.Address).To(Equal("localhost"))
109+
Expect(server.SecureServing.Port).To(Equal("8675"))
110+
})
111+
})
112+
113+
Context("when SecureServing is not set", func() {
114+
It("should be defaulted with a random port", func() {
115+
Expect(server.Port).NotTo(Equal(0))
116+
})
117+
})
118+
})
119+
120+
It("should default authn if not set", func() {
121+
Expect(server.Authn).NotTo(BeNil())
122+
})
123+
124+
Describe("argument defaulting", func() {
125+
// NB(directxman12): most of the templating vs configure logic is tested
126+
// in arguments/arguments_test.go, so just test secure vs insecure port logic here
127+
128+
Context("when insecure serving is set, on a binary that supports it", func() {
129+
BeforeEach(func() {
130+
server.InsecureServing = &process.ListenAddr{
131+
Address: "localhost",
132+
Port: "8675",
133+
}
134+
server.Path = "./testdata/fake-1.19-apiserver.sh"
135+
})
136+
It("should set the insecure-port and insecure-bind-address fields from insecureserving", func() {
137+
Expect(APIServerArguments(server)).To(ContainElements(
138+
"--insecure-port=8675",
139+
"--insecure-bind-address=localhost",
140+
))
141+
})
142+
})
143+
144+
Context("when insecureserving is disabled, on binaries with no insecure-port flag", func() {
145+
BeforeEach(func() {
146+
server.Path = "./testdata/fake-1.20-apiserver.sh"
147+
})
148+
It("should not try to explicitly disable the insecure port", func() {
149+
Expect(APIServerArguments(server)).NotTo(ContainElement(HavePrefix("--insecure-port")))
150+
})
151+
})
152+
153+
Context("when insecureserving is disabled, on binaries with an insecure-port flag", func() {
154+
BeforeEach(func() {
155+
server.Path = "./testdata/fake-1.19-apiserver.sh"
156+
})
157+
It("should explicitly disable the insecure port", func() {
158+
Expect(APIServerArguments(server)).To(ContainElement("--insecure-port=0"))
159+
})
160+
})
161+
})
162+
163+
Describe("setting up auth", func() {
164+
var auth *fakeAuthn
165+
BeforeEach(func() {
166+
auth = &fakeAuthn{
167+
setFlag: true,
168+
}
169+
server.Authn = auth
170+
})
171+
It("should configure with the cert dir", func() {
172+
Expect(auth.workDir).To(Equal(server.CertDir))
173+
})
174+
It("should pass its args to be configured", func() {
175+
Expect(server.Configure().Get("configure-called").Get(nil)).To(ConsistOf("true"))
176+
})
177+
178+
Context("when configuring auth errors out", func() {
179+
It("should fail to configure", func() {
180+
server := &APIServer{
181+
EtcdURL: &url.URL{},
182+
SecureServing: SecureServing{
183+
Authn: auth,
184+
},
185+
}
186+
auth.configureErr = errors.New("Oh no")
187+
Expect(PrepareAPIServer(server)).NotTo(Succeed())
188+
})
189+
})
190+
})
191+
192+
Describe("managing", func() {
193+
// some of these tests are combined for speed reasons -- starting the apiserver
194+
// takes a while, relatively speaking
195+
196+
var (
197+
auth *fakeAuthn
198+
etcd *Etcd
199+
)
200+
BeforeEach(func() {
201+
etcd = &Etcd{}
202+
Expect(etcd.Start()).To(Succeed())
203+
server.EtcdURL = etcd.URL
204+
205+
auth = &fakeAuthn{}
206+
server.Authn = auth
207+
})
208+
AfterEach(func() {
209+
Expect(etcd.Stop()).To(Succeed())
210+
})
211+
212+
Context("after starting", func() {
213+
BeforeEach(func() {
214+
Expect(server.Start()).To(Succeed())
215+
})
216+
217+
It("should stop successfully, and stop auth", func() {
218+
Expect(server.Stop()).To(Succeed())
219+
Expect(auth.stopCalled).To(BeTrue())
220+
})
221+
})
222+
223+
It("should fail to start when auth fails to start", func() {
224+
auth.startErr = errors.New("Oh no")
225+
Expect(server.Start()).NotTo(Succeed())
226+
})
227+
228+
It("should start successfully & start auth", func() {
229+
Expect(server.Start()).To(Succeed())
230+
defer func() { Expect(server.Stop()).To(Succeed()) }()
231+
Expect(auth.startCalled).To(BeTrue())
232+
})
233+
})
234+
})
235+
236+
type fakeAuthn struct {
237+
workDir string
238+
239+
startCalled bool
240+
stopCalled bool
241+
setFlag bool
242+
243+
configureErr error
244+
startErr error
245+
}
246+
247+
func (f *fakeAuthn) Configure(workDir string, args *process.Arguments) error {
248+
f.workDir = workDir
249+
if f.setFlag {
250+
args.Set("configure-called", "true")
251+
}
252+
return f.configureErr
253+
}
254+
func (f *fakeAuthn) Start() error {
255+
f.startCalled = true
256+
return f.startErr
257+
}
258+
func (f *fakeAuthn) AddUser(user User, baseCfg *rest.Config) (*rest.Config, error) {
259+
return nil, nil
260+
}
261+
func (f *fakeAuthn) Stop() error {
262+
f.stopCalled = true
263+
return nil
264+
}
Lines changed: 20 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,20 @@
1+
package controlplane_test
2+
3+
import (
4+
. "github.com/onsi/ginkgo"
5+
. "github.com/onsi/gomega"
6+
7+
. "sigs.k8s.io/controller-runtime/pkg/internal/testing/controlplane"
8+
)
9+
10+
var _ = Describe("etcd", func() {
11+
// basic coherence test
12+
It("should start and stop successfully", func() {
13+
etcd := &Etcd{}
14+
Expect(etcd.Start()).To(Succeed())
15+
defer func() {
16+
Expect(etcd.Stop()).To(Succeed())
17+
}()
18+
Expect(etcd.URL).NotTo(BeNil())
19+
})
20+
})

0 commit comments

Comments
 (0)