Skip to content

Commit 3e2c1a8

Browse files
[CDAP-19300] Added Containers Injection, added Dockerfile.test and updated the README
1 parent 21cf8d0 commit 3e2c1a8

20 files changed

+1830
-16
lines changed

.gitignore

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -63,6 +63,7 @@ anaconda-mode/
6363
*.dylib
6464
# Test binary, build with 'go test -c'
6565
*.test
66+
!Dockerfile.test
6667
# Output of the go coverage tool, specifically when used with LiteIDE
6768
*.out
6869
### Vim ###

Dockerfile.test

Lines changed: 25 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,25 @@
1+
# Build the manager binary
2+
FROM golang:1.16 as tester
3+
4+
ENV version 1.0.8
5+
ENV arch amd64
6+
7+
# Copy everything in the go src
8+
WORKDIR /go/src/cdap.io/cdap-operator
9+
COPY ./ ./
10+
11+
# Install Kubebuilder
12+
RUN curl -L -O "https://github.com/kubernetes-sigs/kubebuilder/releases/download/v${version}/kubebuilder_${version}_linux_${arch}.tar.gz" && \
13+
tar -zxvf kubebuilder_${version}_linux_${arch}.tar.gz && \
14+
mv kubebuilder_${version}_linux_${arch} /usr/local/kubebuilder && \
15+
cp /usr/local/kubebuilder/bin/* /usr/local/bin
16+
17+
# Install setup-envtest
18+
RUN go install sigs.k8s.io/controller-runtime/tools/setup-envtest@latest
19+
20+
# download envtest 1.19.x for kubebuilder and to set KUBEBUILDER_ASSETS environment variable
21+
RUN $(go env GOPATH)/bin/setup-envtest use -p env 1.19.x > /tmp/setup_envtest.sh && \
22+
eval `$(go env GOPATH)/bin/setup-envtest use -p env 1.19.x` && \
23+
rm /tmp/setup_envtest.sh
24+
25+
CMD make test

README.md

Lines changed: 11 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -82,3 +82,14 @@ rm /tmp/setup_envtest.sh
8282
```
8383

8484
4. Run `make test`
85+
86+
#### Running Unit Tests in a Docker image
87+
88+
From the project root folder build the test image by running the following
89+
```
90+
docker build -f Dockerfile.test . -t test
91+
```
92+
Execute the image with
93+
```
94+
docker run test
95+
```

api/v1alpha1/cdapmaster_types.go

Lines changed: 8 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -172,6 +172,10 @@ type CDAPScalableServiceSpec struct {
172172
CDAPServiceSpec `json:",inline"`
173173
// Replicas is number of replicas for the service.
174174
Replicas *int32 `json:"replicas,omitempty"`
175+
// Containers define any additional containers a service has
176+
// This is a list of containers and can be left blank
177+
// A typical use is to add sidecars for a deployment
178+
Containers []*corev1.Container `json:"containers,omitempty"`
175179
}
176180

177181
// CDAPExternalServiceSpec defines the base specification for master services that expose to outside of the cluster.
@@ -194,6 +198,10 @@ type CDAPStatefulServiceSpec struct {
194198
StorageSize string `json:"storageSize,omitempty"`
195199
// StorageClassName is the name of the StorageClass for the persistent volume used by the service.
196200
StorageClassName *string `json:"storageClassName,omitempty"`
201+
// Containers define any additional containers a service has
202+
// This is a list of containers and can be left blank
203+
// A typical use is to add sidecars for a stateful set
204+
Containers []*corev1.Container `json:"containers,omitempty"`
197205
}
198206

199207
// AppFabricSpec defines the specification for the AppFabric service.

api/v1alpha1/zz_generated.deepcopy.go

Lines changed: 22 additions & 0 deletions
Some generated files are not rendered by default. Learn more about customizing how changed files appear on GitHub.

controllers/cdapmaster_controller.go

Lines changed: 37 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -127,9 +127,43 @@ func ApplyDefaults(resource interface{}) {
127127
}
128128

129129
// Set the configMapCConf entry for the router and UI service and ports
130-
spec.Config[confRouterServerAddress] = fmt.Sprintf("cdap-%s-%s", r.Name, strings.ToLower(string(serviceRouter)))
131-
spec.Config[confRouterBindPort] = strconv.Itoa(int(*spec.Router.ServicePort))
132-
spec.Config[confUserInterfaceBindPort] = strconv.Itoa(int(*spec.UserInterface.ServicePort))
130+
if spec.Config[confRouterServerAddress] == "" {
131+
spec.Config[confRouterServerAddress] = fmt.Sprintf("cdap-%s-%s", r.Name, strings.ToLower(string(serviceRouter)))
132+
}
133+
134+
if spec.Config[confRouterBindPort] == "" {
135+
spec.Config[confRouterBindPort] = strconv.Itoa(int(*spec.Router.ServicePort))
136+
}
137+
138+
if spec.Config[confUserInterfaceBindPort] == "" {
139+
spec.Config[confUserInterfaceBindPort] = strconv.Itoa(int(*spec.UserInterface.ServicePort))
140+
}
141+
142+
// Set the default local data directory if it is not set in cdap-cr.
143+
if _, ok := spec.Config[confLocalDataDirKey]; !ok {
144+
spec.Config[confLocalDataDirKey] = confLocalDataDirVal
145+
}
146+
147+
// Set security secret disk names to be consistent with securitySecret if not overwritten.
148+
if _, ok := spec.Config[confTwillSecurityMasterSecretDiskName]; !ok && spec.SecuritySecret != "" {
149+
spec.Config[confTwillSecurityMasterSecretDiskName] = spec.SecuritySecret
150+
}
151+
if _, ok := spec.Config[confTwillSecurityMasterSecretDiskPath]; !ok && spec.SecuritySecret != "" {
152+
spec.Config[confTwillSecurityMasterSecretDiskPath] = defaultSecuritySecretPath
153+
}
154+
// This configuration makes the default securitySecret available to the workers by default.
155+
// TODO: Add support for secure-by-default configurations.
156+
if _, ok := spec.Config[confTwillSecurityWorkerSecretDiskName]; !ok && spec.SecuritySecret != "" {
157+
spec.Config[confTwillSecurityWorkerSecretDiskName] = spec.SecuritySecret
158+
}
159+
if _, ok := spec.Config[confTwillSecurityWorkerSecretDiskPath]; !ok && spec.SecuritySecret != "" {
160+
spec.Config[confTwillSecurityWorkerSecretDiskPath] = defaultSecuritySecretPath
161+
}
162+
163+
// Set the default JMX server port if not set and system metrics exporter sidecar is enabled
164+
if _, ok := spec.Config[confJMXServerPort]; spec.SystemMetricsExporter != nil && !ok {
165+
spec.Config[confJMXServerPort] = fmt.Sprint(defaultJMXport)
166+
}
133167

134168
// Set the default local data directory if it is not set in cdap-cr.
135169
if _, ok := spec.Config[confLocalDataDirKey]; !ok {

controllers/deployment.go

Lines changed: 38 additions & 11 deletions
Original file line numberDiff line numberDiff line change
@@ -177,33 +177,47 @@ func buildStatefulSets(master *v1alpha1.CDAPMaster, name string, services Servic
177177

178178
// Add each service as a container
179179
for _, s := range services {
180-
ss, err := getCDAPServiceSpec(master, s)
180+
statefulSet, err := getCDAPStatefulServiceSpec(master, s)
181181
if err != nil {
182182
return nil, err
183183
}
184184
// This happens when the service is optional and disabled in CR
185185
// (i.e. service spec is set to nil)
186-
if ss == nil {
186+
if statefulSet == nil {
187187
continue
188188
}
189189

190-
c, err := serviceContainerSpec(ss, master, dataDir, s)
190+
c, err := serviceContainerSpec(&statefulSet.CDAPServiceSpec, master, dataDir, s)
191191
if err != nil {
192192
return nil, err
193193
}
194194
spec = spec.withContainer(c)
195-
if err := addSystemMetricsServiceIfEnabled(spec, master, ss, dataDir, c); err != nil {
195+
196+
if statefulSet.Containers != nil {
197+
for _, container := range statefulSet.Containers {
198+
additionalContainer := containerSpecFromContainer(container, dataDir)
199+
spec = spec.withContainer(additionalContainer)
200+
}
201+
}
202+
203+
if err := addSystemMetricsServiceIfEnabled(spec, master, &statefulSet.CDAPServiceSpec, dataDir, c); err != nil {
196204
return nil, err
197205
}
198206

199207
// Adding a label to allow NodePort service selector to find the pod
200208
spec = spec.addLabel(labelContainerKeyPrefix+s, master.Name)
201209

202210
// Mount extra volumes from ConfigMap and Secret
203-
if _, err := spec.addConfigMapVolumes(ss.ConfigMapVolumes); err != nil {
211+
if _, err := spec.addConfigMapVolumes(statefulSet.CDAPServiceSpec.ConfigMapVolumes); err != nil {
212+
return nil, err
213+
}
214+
if _, err := spec.addSecretVolumes(statefulSet.CDAPServiceSpec.SecretVolumes); err != nil {
215+
return nil, err
216+
}
217+
if _, err := spec.addAdditionalVolumes(statefulSet.CDAPServiceSpec.AdditionalVolumes); err != nil {
204218
return nil, err
205219
}
206-
if _, err := spec.addSecretVolumes(ss.SecretVolumes); err != nil {
220+
if _, err := spec.addAdditionalVolumeMounts(statefulSet.CDAPServiceSpec.AdditionalVolumeMounts); err != nil {
207221
return nil, err
208222
}
209223
if _, err := spec.addAdditionalVolumes(ss.AdditionalVolumes); err != nil {
@@ -313,30 +327,43 @@ func buildDeployment(master *v1alpha1.CDAPMaster, name string, services ServiceG
313327

314328
// Add each service as a container
315329
for _, s := range services {
316-
ss, err := getCDAPServiceSpec(master, s)
330+
scalableSpec, err := getCDAPScalableServiceSpec(master, s)
317331
if err != nil {
318332
return nil, err
319333
}
320334
// This happens when the service is optional and disabled in CR
321335
// (i.e. service spec is set to nil)
322-
if ss == nil {
336+
if scalableSpec == nil {
323337
continue
324338
}
325339

326-
c, err := serviceContainerSpec(ss, master, dataDir, s)
340+
c, err := serviceContainerSpec(&scalableSpec.CDAPServiceSpec, master, dataDir, s)
327341
if err != nil {
328342
return nil, err
329343
}
330344
spec = spec.withContainer(c)
331345

346+
if scalableSpec.Containers != nil {
347+
for _, container := range scalableSpec.Containers {
348+
additionalContainer := containerSpecFromContainer(container, dataDir)
349+
spec = spec.withContainer(additionalContainer)
350+
}
351+
}
352+
332353
// Adding a label to allow k8s service selector to easily find the pod
333354
spec = spec.addLabel(labelContainerKeyPrefix+s, master.Name)
334355

335356
// Mount extra volumes from ConfigMap and Secret
336-
if _, err := spec.addConfigMapVolumes(ss.ConfigMapVolumes); err != nil {
357+
if _, err := spec.addConfigMapVolumes(scalableSpec.CDAPServiceSpec.ConfigMapVolumes); err != nil {
358+
return nil, err
359+
}
360+
if _, err := spec.addSecretVolumes(scalableSpec.CDAPServiceSpec.SecretVolumes); err != nil {
361+
return nil, err
362+
}
363+
if _, err := spec.addAdditionalVolumes(scalableSpec.CDAPServiceSpec.AdditionalVolumes); err != nil {
337364
return nil, err
338365
}
339-
if _, err := spec.addSecretVolumes(ss.SecretVolumes); err != nil {
366+
if _, err := spec.addAdditionalVolumeMounts(scalableSpec.CDAPServiceSpec.AdditionalVolumeMounts); err != nil {
340367
return nil, err
341368
}
342369
if _, err := spec.addAdditionalVolumes(ss.AdditionalVolumes); err != nil {

controllers/deployment_test.go

Lines changed: 125 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -256,6 +256,131 @@ var _ = Describe("Controller Suite", func() {
256256
}
257257
})
258258
})
259+
260+
Describe("Add additional generic container", func() {
261+
readMaster := func(fileName string) *v1alpha1.CDAPMaster {
262+
master := &v1alpha1.CDAPMaster{}
263+
err := fromJson(fileName, master)
264+
Expect(err).To(BeNil())
265+
return master
266+
}
267+
readExpectedJson := func(fileName string) []byte {
268+
json, err := ioutil.ReadFile("testdata/" + fileName)
269+
Expect(err).To(BeNil())
270+
return json
271+
}
272+
diffJson := func(expected, actual []byte) {
273+
opts := jsondiff.DefaultConsoleOptions()
274+
diff, text := jsondiff.Compare(expected, actual, &opts)
275+
Expect(diff.String()).To(Equal(jsondiff.SupersetMatch.String()), text)
276+
}
277+
diffAdditionalContainers := func(containers []corev1.Container, containerName, expectedJsonFilename string) {
278+
var additionalContainer *corev1.Container
279+
for _, c := range containers {
280+
if c.Name == containerName {
281+
additionalContainer = &c
282+
break
283+
}
284+
}
285+
286+
actualContainerJson, _ := json.Marshal(additionalContainer)
287+
expectedContainerJson := readExpectedJson(expectedJsonFilename)
288+
diffJson(actualContainerJson, expectedContainerJson)
289+
}
290+
It("Multiple Additional containers for Router", func() {
291+
master := readMaster("testdata/cdap_master_cr_multi_additional_containers.json")
292+
emptyLabels := make(map[string]string)
293+
spec, err := buildDeploymentPlanSpec(master, emptyLabels)
294+
Expect(err).To(BeNil())
295+
objs, err := buildObjectsForDeploymentPlan(spec)
296+
Expect(err).To(BeNil())
297+
298+
var strategyHandler DeploymentPlan
299+
strategyHandler.Init()
300+
301+
for _, obj := range objs {
302+
if o, ok := obj.Obj.(*k8s.Object).Obj.(*appsv1.Deployment); ok {
303+
304+
if o.Name == getObjName(master, "router") {
305+
containers := o.Spec.Template.Spec.Containers
306+
307+
Expect(len(containers)).To(BeIdenticalTo(3))
308+
309+
diffAdditionalContainers(containers, "test-router-container-a", "additional_router_container_a.json")
310+
diffAdditionalContainers(containers, "test-router-container-b", "additional_router_container_b.json")
311+
}
312+
}
313+
}
314+
})
315+
It("Multiple Additional container for AppFabric", func() {
316+
master := readMaster("testdata/cdap_master_cr_multi_additional_containers.json")
317+
emptyLabels := make(map[string]string)
318+
spec, err := buildDeploymentPlanSpec(master, emptyLabels)
319+
Expect(err).To(BeNil())
320+
objs, err := buildObjectsForDeploymentPlan(spec)
321+
Expect(err).To(BeNil())
322+
323+
var strategyHandler DeploymentPlan
324+
strategyHandler.Init()
325+
326+
for _, obj := range objs {
327+
if o, ok := obj.Obj.(*k8s.Object).Obj.(*appsv1.StatefulSet); ok {
328+
if o.Name == getObjName(master, "appFabric") {
329+
containers := o.Spec.Template.Spec.Containers
330+
331+
Expect(len(containers)).To(BeIdenticalTo(3))
332+
333+
diffAdditionalContainers(containers, "test-appfabric-container-a", "additional_appfabric_container_a.json")
334+
diffAdditionalContainers(containers, "test-appfabric-container-b", "additional_appfabric_container_b.json")
335+
}
336+
}
337+
}
338+
})
339+
It("No Additional container for Router", func() {
340+
master := readMaster("testdata/cdap_master_cr.json")
341+
emptyLabels := make(map[string]string)
342+
spec, err := buildDeploymentPlanSpec(master, emptyLabels)
343+
Expect(err).To(BeNil())
344+
objs, err := buildObjectsForDeploymentPlan(spec)
345+
Expect(err).To(BeNil())
346+
347+
var strategyHandler DeploymentPlan
348+
strategyHandler.Init()
349+
350+
for _, obj := range objs {
351+
if o, ok := obj.Obj.(*k8s.Object).Obj.(*appsv1.Deployment); ok {
352+
if o.Name == getObjName(master, "router") {
353+
containers := o.Spec.Template.Spec.Containers
354+
355+
Expect(len(containers)).To(BeIdenticalTo(1))
356+
Expect(containers[0].Name).To(BeIdenticalTo("router"))
357+
}
358+
}
359+
}
360+
})
361+
It("No Additional container for AppFabric", func() {
362+
master := readMaster("testdata/cdap_master_cr.json")
363+
emptyLabels := make(map[string]string)
364+
spec, err := buildDeploymentPlanSpec(master, emptyLabels)
365+
Expect(err).To(BeNil())
366+
objs, err := buildObjectsForDeploymentPlan(spec)
367+
Expect(err).To(BeNil())
368+
369+
var strategyHandler DeploymentPlan
370+
strategyHandler.Init()
371+
372+
for _, obj := range objs {
373+
if o, ok := obj.Obj.(*k8s.Object).Obj.(*appsv1.StatefulSet); ok {
374+
if o.Name == getObjName(master, "appFabric") {
375+
containers := o.Spec.Template.Spec.Containers
376+
377+
Expect(len(containers)).To(BeIdenticalTo(1))
378+
Expect(containers[0].Name).To(BeIdenticalTo("appfabric"))
379+
}
380+
}
381+
}
382+
})
383+
})
259384
})
260385

261386
func TestMergeEnvVars(t *testing.T) {

0 commit comments

Comments
 (0)