Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

CDAP-19300 adding container injection feature and Dockerfile for unit testing on Docker image #89

Open
wants to merge 4 commits into
base: develop
Choose a base branch
from
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
1 change: 1 addition & 0 deletions .gitignore
Original file line number Diff line number Diff line change
Expand Up @@ -63,6 +63,7 @@ anaconda-mode/
*.dylib
# Test binary, build with 'go test -c'
*.test
!Dockerfile.test
# Output of the go coverage tool, specifically when used with LiteIDE
*.out
### Vim ###
Expand Down
27 changes: 27 additions & 0 deletions Dockerfile.test
Original file line number Diff line number Diff line change
@@ -0,0 +1,27 @@
# Build the manager binary
FROM golang:1.19 as tester

ENV version 1.0.8
ENV arch amd64

# Copy everything in the go src
WORKDIR /go/src/cdap.io/cdap-operator
COPY ./ ./

# Install Kubebuilder
RUN curl -L -O "https://github.com/kubernetes-sigs/kubebuilder/releases/download/v${version}/kubebuilder_${version}_linux_${arch}.tar.gz" && \
tar -zxvf kubebuilder_${version}_linux_${arch}.tar.gz && \
mv kubebuilder_${version}_linux_${arch} /usr/local/kubebuilder && \
cp /usr/local/kubebuilder/bin/* /usr/local/bin

# Install setup-envtest
RUN go install sigs.k8s.io/controller-runtime/tools/setup-envtest@latest

RUN go env -w GO111MODULE=auto

# download envtest 1.19.x for kubebuilder and to set KUBEBUILDER_ASSETS environment variable
RUN $(go env GOPATH)/bin/setup-envtest use -p env 1.19.x > /tmp/setup_envtest.sh && \
eval `$(go env GOPATH)/bin/setup-envtest use -p env 1.19.x` && \
rm /tmp/setup_envtest.sh

CMD make test
3 changes: 2 additions & 1 deletion Makefile
Original file line number Diff line number Diff line change
Expand Up @@ -3,6 +3,7 @@
IMG ?= controller:latest
# ENVTEST_K8S_VERSION refers to the version of kubebuilder assets to be downloaded by envtest binary.
ENVTEST_K8S_VERSION = 1.22
CRD_OPTIONS ?= "crd:maxDescLen=0"

# Get the currently used golang install path (in GOPATH/bin, unless GOBIN is set)
ifeq (,$(shell go env GOBIN))
Expand Down Expand Up @@ -41,7 +42,7 @@ help: ## Display this help.

.PHONY: manifests
manifests: controller-gen ## Generate WebhookConfiguration, ClusterRole and CustomResourceDefinition objects.
$(CONTROLLER_GEN) rbac:roleName=manager-role crd webhook paths="./..." output:crd:artifacts:config=config/crd/bases
$(CONTROLLER_GEN) $(CRD_OPTIONS) rbac:roleName=manager-role webhook paths="./..." output:crd:artifacts:config=config/crd/bases

.PHONY: generate
generate: controller-gen ## Generate code containing DeepCopy, DeepCopyInto, and DeepCopyObject method implementations.
Expand Down
7 changes: 7 additions & 0 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -82,3 +82,10 @@ rm /tmp/setup_envtest.sh
```

4. Run `make test`

#### Running Unit Tests in a Docker image

From the project root folder build, execute and then remove the test image by running the following
```
docker build -f Dockerfile.test . -t test && docker run --rm -it test; docker image rm test
```
4 changes: 4 additions & 0 deletions api/v1alpha1/cdapmaster_types.go
Original file line number Diff line number Diff line change
Expand Up @@ -168,6 +168,10 @@ type CDAPServiceSpec struct {
Lifecycle *corev1.Lifecycle `json:"lifecycle,omitempty"`
// Affinity describes node affinity scheduling rules for the service.
Affinity *corev1.Affinity `json:"affinity,omitempty"`
// Containers define any additional containers a service has
// This is a list of containers and can be left blank
// A typical use is to add sidecars for a deployment
Containers []*corev1.Container `json:"containers,omitempty"`
}

// CDAPScalableServiceSpec defines the base specification for master services that can have more than one instance.
Expand Down
11 changes: 11 additions & 0 deletions api/v1alpha1/zz_generated.deepcopy.go

Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.

17,458 changes: 17,458 additions & 0 deletions config/crd/bases/cdap.cdap.io_cdapmasters.yaml

Large diffs are not rendered by default.

56 changes: 44 additions & 12 deletions controllers/cdapmaster_controller.go
Original file line number Diff line number Diff line change
Expand Up @@ -128,9 +128,41 @@ func ApplyDefaults(resource interface{}) {
}

// Set the configMapCConf entry for the router and UI service and ports
spec.Config[confRouterServerAddress] = fmt.Sprintf("cdap-%s-%s", r.Name, strings.ToLower(string(serviceRouter)))
spec.Config[confRouterBindPort] = strconv.Itoa(int(*spec.Router.ServicePort))
spec.Config[confUserInterfaceBindPort] = strconv.Itoa(int(*spec.UserInterface.ServicePort))
if spec.Config[confRouterServerAddress] == "" {
spec.Config[confRouterServerAddress] = fmt.Sprintf("cdap-%s-%s", r.Name, strings.ToLower(string(serviceRouter)))
}
if spec.Config[confRouterBindPort] == "" {
spec.Config[confRouterBindPort] = strconv.Itoa(int(*spec.Router.ServicePort))
}
if spec.Config[confUserInterfaceBindPort] == "" {
spec.Config[confUserInterfaceBindPort] = strconv.Itoa(int(*spec.UserInterface.ServicePort))
}

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

// Set security secret disk names to be consistent with securitySecret if not overwritten.
if _, ok := spec.Config[confTwillSecurityMasterSecretDiskName]; !ok && spec.SecuritySecret != "" {
spec.Config[confTwillSecurityMasterSecretDiskName] = spec.SecuritySecret
}
if _, ok := spec.Config[confTwillSecurityMasterSecretDiskPath]; !ok && spec.SecuritySecret != "" {
spec.Config[confTwillSecurityMasterSecretDiskPath] = defaultSecuritySecretPath
}
// This configuration makes the default securitySecret available to the workers by default.
// TODO: Add support for secure-by-default configurations.
if _, ok := spec.Config[confTwillSecurityWorkerSecretDiskName]; !ok && spec.SecuritySecret != "" {
spec.Config[confTwillSecurityWorkerSecretDiskName] = spec.SecuritySecret
}
if _, ok := spec.Config[confTwillSecurityWorkerSecretDiskPath]; !ok && spec.SecuritySecret != "" {
spec.Config[confTwillSecurityWorkerSecretDiskPath] = defaultSecuritySecretPath
}

// Set the default JMX server port if not set and system metrics exporter sidecar is enabled
if _, ok := spec.Config[confJMXServerPort]; spec.SystemMetricsExporter != nil && !ok {
spec.Config[confJMXServerPort] = fmt.Sprint(defaultJMXport)
}

// Set the default local data directory if it is not set in cdap-cr.
if _, ok := spec.Config[confLocalDataDirKey]; !ok {
Expand Down Expand Up @@ -166,9 +198,9 @@ func ApplyDefaults(resource interface{}) {
finalizer.EnsureStandard(r)
}

/////////////////////////////////////////////////////////
///// Handling reconciling ConfigMapHandler objects /////
/////////////////////////////////////////////////////////
// ///////////////////////////////////////////////////////
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

nit: no additional space

// /// Handling reconciling ConfigMapHandler objects /////
// ///////////////////////////////////////////////////////
type ConfigMapHandler struct{}

func (h *ConfigMapHandler) Observables(rsrc interface{}, labels map[string]string, dependent []reconciler.Object) []reconciler.Observable {
Expand Down Expand Up @@ -257,9 +289,9 @@ func buildConfigMapObject(spec *ConfigMapSpec) reconciler.Object {
return obj
}

///////////////////////////////////////////////////////////
///// Handling reconciling deployment of all services /////
///////////////////////////////////////////////////////////
// /////////////////////////////////////////////////////////
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

nit: no spaces

// /// Handling reconciling deployment of all services /////
// /////////////////////////////////////////////////////////
type ServiceHandler struct{}

func (h *ServiceHandler) Observables(rsrc interface{}, labels map[string]string, dependent []reconciler.Object) []reconciler.Observable {
Expand Down Expand Up @@ -337,9 +369,9 @@ func CopyNodePortIfAny(expected, observed []reconciler.Object) {
}
}

///////////////////////////////////////////////////////
///// Handler for image version upgrade/downgrade /////
///////////////////////////////////////////////////////
// /////////////////////////////////////////////////////
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

nit: no spaces

// /// Handler for image version upgrade/downgrade /////
// /////////////////////////////////////////////////////
type VersionUpdateHandler struct{}

func (h *VersionUpdateHandler) Observables(rsrc interface{}, labels map[string]string, dependent []reconciler.Object) []reconciler.Observable {
Expand Down
49 changes: 45 additions & 4 deletions controllers/deployment.go
Original file line number Diff line number Diff line change
Expand Up @@ -202,6 +202,14 @@ func buildStatefulSets(master *v1alpha1.CDAPMaster, name string, services Servic
return nil, err
}
spec = spec.withContainer(c)

if ss.Containers != nil {
for _, container := range ss.Containers {
additionalContainer := containerSpecFromContainer(container, dataDir)
spec = spec.withContainer(additionalContainer)
}
}

if err := addSystemMetricsServiceIfEnabled(spec, master, ss, dataDir, c); err != nil {
return nil, err
}
Expand Down Expand Up @@ -344,6 +352,13 @@ func buildDeployment(master *v1alpha1.CDAPMaster, name string, services ServiceG
}
spec = spec.withContainer(c)

if ss.Containers != nil {
for _, container := range ss.Containers {
additionalContainer := containerSpecFromContainer(container, dataDir)
spec = spec.withContainer(additionalContainer)
}
}

// Adding a label to allow k8s service selector to easily find the pod
spec = spec.addLabel(labelContainerKeyPrefix+s, master.Name)

Expand Down Expand Up @@ -477,10 +492,15 @@ func buildStatefulSetsObject(spec *StatefulSpec) (*reconciler.Object, error) {
}
}
for index, _ := range statefulSetObj.Spec.Template.Spec.Containers {
if err := addVolumeMountToContainer(&statefulSetObj.Spec.Template.Spec.Containers[index], spec.Base.AdditionalVolumeMounts); err != nil {
container := &statefulSetObj.Spec.Template.Spec.Containers[index]
if err := addVolumeMountToContainer(container, spec.Base.AdditionalVolumeMounts); err != nil {
return nil, err
}
setLifecycleHookForContainer(&statefulSetObj.Spec.Template.Spec.Containers[index], spec.Containers[index].Lifecycle)
setEnvForContainer(container, spec.Containers[index].Env)
setLifecycleHookForContainer(container, spec.Containers[index].Lifecycle)
setLivenessProbeForContainer(container, spec.Containers[index].LivenessProbe)
setReadinessProbeForContainer(container, spec.Containers[index].ReadinessProbe)
setPortsForContainer(container, spec.Containers[index].Ports)
}
return obj, nil
}
Expand Down Expand Up @@ -511,10 +531,15 @@ func buildDeploymentObject(spec *DeploymentSpec) (*reconciler.Object, error) {
}
}
for index, _ := range deploymentObj.Spec.Template.Spec.Containers {
if err := addVolumeMountToContainer(&deploymentObj.Spec.Template.Spec.Containers[index], spec.Base.AdditionalVolumeMounts); err != nil {
container := &deploymentObj.Spec.Template.Spec.Containers[index]
if err := addVolumeMountToContainer(container, spec.Base.AdditionalVolumeMounts); err != nil {
return nil, err
}
setLifecycleHookForContainer(&deploymentObj.Spec.Template.Spec.Containers[index], spec.Containers[index].Lifecycle)
setEnvForContainer(container, spec.Containers[index].Env)
setLifecycleHookForContainer(container, spec.Containers[index].Lifecycle)
setLivenessProbeForContainer(container, spec.Containers[index].LivenessProbe)
setReadinessProbeForContainer(container, spec.Containers[index].ReadinessProbe)
setPortsForContainer(container, spec.Containers[index].Ports)
}
return obj, nil
}
Expand Down Expand Up @@ -543,10 +568,26 @@ func addVolumeMountToContainer(container *corev1.Container, volumeMountsToAdd []
return nil
}

func setEnvForContainer(container *corev1.Container, env []corev1.EnvVar) {
container.Env = env
}

func setLifecycleHookForContainer(container *corev1.Container, lifecycle *corev1.Lifecycle) {
container.Lifecycle = lifecycle
}

func setLivenessProbeForContainer(container *corev1.Container, livenessProbe *corev1.Probe) {
container.LivenessProbe = livenessProbe
}

func setReadinessProbeForContainer(container *corev1.Container, readinessProbe *corev1.Probe) {
container.ReadinessProbe = readinessProbe
}

func setPortsForContainer(container *corev1.Container, ports []corev1.ContainerPort) {
container.Ports = ports
}

// Return a NodePort service to expose the supplied target service
func buildNetworkService(master *v1alpha1.CDAPMaster, name NetworkServiceName, target ServiceName, labels map[string]string) (*NetworkServiceSpec, error) {
s, err := getCDAPExternalServiceSpec(master, target)
Expand Down
Loading