Skip to content

Commit c0ca959

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 e8204ec commit c0ca959

File tree

7 files changed

+1045
-3
lines changed

7 files changed

+1045
-3
lines changed

pkg/internal/testing/controlplane/apiserver.go

Lines changed: 33 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.
@@ -398,7 +407,9 @@ func (s *APIServer) populateAPIServerCerts() error {
398407
// the CertDir if necessary.
399408
func (s *APIServer) Stop() error {
400409
if s.processState != nil {
401-
return s.processState.Stop()
410+
if err := s.processState.Stop(); err != nil {
411+
return err
412+
}
402413
}
403414
return s.Authn.Stop()
404415
}
@@ -425,3 +436,23 @@ var APIServerDefaultArgs = []string{
425436
"--allow-privileged=true",
426437
// NB(directxman12): we also enable RBAC if nothing else was enabled
427438
}
439+
440+
// PrepareAPIServer is an internal-only (NEVER SHOULD BE EXPOSED)
441+
// function that sets up the API server just before starting it,
442+
// without actually starting it. This saves time on tests.
443+
//
444+
// NB(directxman12): do not expose this outside of internal -- it's unsafe to
445+
// use, because things like port allocation could race even more than they
446+
// currently do if you later call start!
447+
func PrepareAPIServer(s *APIServer) error {
448+
return s.prepare()
449+
}
450+
451+
// APIServerArguments is an internal-only (NEVER SHOULD BE EXPOSED)
452+
// function that sets up the API server just before starting it,
453+
// without actually starting it. It's public to make testing easier.
454+
//
455+
// NB(directxman12): do not expose this outside of internal
456+
func APIServerArguments(s *APIServer) []string {
457+
return s.processState.Args
458+
}
Lines changed: 279 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,279 @@
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+
Context("when given legacy-style template arguments", func() {
163+
BeforeEach(func() {
164+
server.Args = []string{"--foo=bar", "--baz={{ .Port }}"}
165+
})
166+
It("should use the passed in args with the minimal required defaults", func() {
167+
Expect(APIServerArguments(server)).To(ConsistOf(
168+
"--foo=bar",
169+
MatchRegexp(`--baz=\d+`),
170+
"--service-cluster-ip-range=10.0.0.0/24",
171+
MatchRegexp("--client-ca-file=.+"),
172+
"--authorization-mode=RBAC",
173+
))
174+
})
175+
})
176+
})
177+
178+
Describe("setting up auth", func() {
179+
var auth *fakeAuthn
180+
BeforeEach(func() {
181+
auth = &fakeAuthn{
182+
setFlag: true,
183+
}
184+
server.Authn = auth
185+
})
186+
It("should configure with the cert dir", func() {
187+
Expect(auth.workDir).To(Equal(server.CertDir))
188+
})
189+
It("should pass its args to be configured", func() {
190+
Expect(server.Configure().Get("configure-called").Get(nil)).To(ConsistOf("true"))
191+
})
192+
193+
Context("when configuring auth errors out", func() {
194+
It("should fail to configure", func() {
195+
server := &APIServer{
196+
EtcdURL: &url.URL{},
197+
SecureServing: SecureServing{
198+
Authn: auth,
199+
},
200+
}
201+
auth.configureErr = errors.New("Oh no")
202+
Expect(PrepareAPIServer(server)).NotTo(Succeed())
203+
})
204+
})
205+
})
206+
207+
Describe("managing", func() {
208+
// some of these tests are combined for speed reasons -- starting the apiserver
209+
// takes a while, relatively speaking
210+
211+
var (
212+
auth *fakeAuthn
213+
etcd *Etcd
214+
)
215+
BeforeEach(func() {
216+
etcd = &Etcd{}
217+
Expect(etcd.Start()).To(Succeed())
218+
server.EtcdURL = etcd.URL
219+
220+
auth = &fakeAuthn{}
221+
server.Authn = auth
222+
})
223+
AfterEach(func() {
224+
Expect(etcd.Stop()).To(Succeed())
225+
})
226+
227+
Context("after starting", func() {
228+
BeforeEach(func() {
229+
Expect(server.Start()).To(Succeed())
230+
})
231+
232+
It("should stop successfully, and stop auth", func() {
233+
Expect(server.Stop()).To(Succeed())
234+
Expect(auth.stopCalled).To(BeTrue())
235+
})
236+
})
237+
238+
It("should fail to start when auth fails to start", func() {
239+
auth.startErr = errors.New("Oh no")
240+
Expect(server.Start()).NotTo(Succeed())
241+
})
242+
243+
It("should start successfully & start auth", func() {
244+
Expect(server.Start()).To(Succeed())
245+
defer func() { Expect(server.Stop()).To(Succeed()) }()
246+
Expect(auth.startCalled).To(BeTrue())
247+
})
248+
})
249+
})
250+
251+
type fakeAuthn struct {
252+
workDir string
253+
254+
startCalled bool
255+
stopCalled bool
256+
setFlag bool
257+
258+
configureErr error
259+
startErr error
260+
}
261+
262+
func (f *fakeAuthn) Configure(workDir string, args *process.Arguments) error {
263+
f.workDir = workDir
264+
if f.setFlag {
265+
args.Set("configure-called", "true")
266+
}
267+
return f.configureErr
268+
}
269+
func (f *fakeAuthn) Start() error {
270+
f.startCalled = true
271+
return f.startErr
272+
}
273+
func (f *fakeAuthn) AddUser(user User, baseCfg *rest.Config) (*rest.Config, error) {
274+
return nil, nil
275+
}
276+
func (f *fakeAuthn) Stop() error {
277+
f.stopCalled = true
278+
return nil
279+
}
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+
})

pkg/internal/testing/controlplane/kubectl_test.go

Lines changed: 2 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -51,7 +51,7 @@ var _ = Describe("KubeConfigFromREST", func() {
5151

5252
BeforeEach(func() {
5353
restCfg = &rest.Config{
54-
Host: "some-host:8675",
54+
Host: "https://some-host:8675",
5555
APIPath: "/some-prefix",
5656
TLSClientConfig: rest.TLSClientConfig{
5757
CertData: []byte("cert"),
@@ -91,6 +91,7 @@ var _ = Describe("KubeConfigFromREST", func() {
9191

9292
Context("when no TLS is enabled", func() {
9393
BeforeEach(func() {
94+
restCfg.Host = "http://some-host:8675"
9495
restCfg.TLSClientConfig = rest.TLSClientConfig{}
9596
})
9697

0 commit comments

Comments
 (0)