Skip to content

Commit

Permalink
Create unit-test helper functions (#301)
Browse files Browse the repository at this point in the history
  • Loading branch information
clamoriniere committed Aug 2, 2021
1 parent 7f05429 commit 1ded384
Show file tree
Hide file tree
Showing 5 changed files with 367 additions and 0 deletions.
64 changes: 64 additions & 0 deletions controllers/datadogagent/agent_eds_test.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,64 @@
package datadogagent

import (
"testing"

datadoghqv1alpha1 "github.com/DataDog/datadog-operator/api/v1alpha1"
test "github.com/DataDog/datadog-operator/api/v1alpha1/test"
assert "github.com/stretchr/testify/require"
logf "sigs.k8s.io/controller-runtime/pkg/log"
"sigs.k8s.io/controller-runtime/pkg/log/zap"

testutils "github.com/DataDog/datadog-operator/controllers/datadogagent/testutils"

metav1 "k8s.io/apimachinery/pkg/apis/meta/v1"
)

type extendeddaemonsetFromInstanceTest struct {
name string
agentdeployment *datadoghqv1alpha1.DatadogAgent
selector *metav1.LabelSelector
checkEDSFuncs []testutils.CheckExtendedDaemonSetFunc
wantErr bool
}

func (test extendeddaemonsetFromInstanceTest) Run(t *testing.T) {
t.Helper()
logf.SetLogger(zap.New(zap.UseDevMode(true)))
logger := logf.Log.WithName(t.Name())
got, _, err := newExtendedDaemonSetFromInstance(logger, test.agentdeployment, test.selector)
if test.wantErr {
assert.Error(t, err, "newExtendedDaemonSetFromInstance() expected an error")
} else {
assert.NoError(t, err, "newExtendedDaemonSetFromInstance() unexpected error: %v", err)
}

// Remove the generated hash before comparison because it is not easy generate it in the test definition.
delete(got.Annotations, datadoghqv1alpha1.MD5AgentDeploymentAnnotationKey)

for _, checkFunc := range test.checkEDSFuncs {
checkFunc(t, got)
}
}

func Test_EDSFromDefaultAgent(t *testing.T) {
defaultDatadogAgent := test.NewDefaultedDatadogAgent("bar", "foo", &test.NewDatadogAgentOptions{UseEDS: true, ClusterAgentEnabled: true})

test1 := extendeddaemonsetFromInstanceTest{
name: "defaulted case",
agentdeployment: defaultDatadogAgent,
checkEDSFuncs: []testutils.CheckExtendedDaemonSetFunc{
testutils.CheckMetadaInEDS(&testutils.CheckNameNamespace{Namespace: "bar", Name: "foo-agent"}),
// check labels
testutils.CheckMetadaInEDS(&testutils.CheckLabelIsPresent{Key: "agent.datadoghq.com/name", Value: "foo"}),
testutils.CheckMetadaInEDS(&testutils.CheckLabelIsPresent{Key: "agent.datadoghq.com/component", Value: "agent"}),
testutils.CheckMetadaInEDS(&testutils.CheckLabelIsPresent{Key: "app.kubernetes.io/instance", Value: "agent"}),
testutils.CheckMetadaInEDS(&testutils.CheckLabelIsPresent{Key: "app.kubernetes.io/managed-by", Value: "datadog-operator"}),
// check containers creation
testutils.CheckPodTemplateInEDS(&testutils.CheckContainerNameIsPresentFunc{Name: "agent"}),
testutils.CheckPodTemplateInEDS(&testutils.CheckContainerNameIsPresentFunc{Name: "process-agent"}),
},
wantErr: false,
}
test1.Run(t)
}
8 changes: 8 additions & 0 deletions controllers/datadogagent/testutils/doc.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,8 @@
// Unless explicitly stated otherwise all files in this repository are licensed
// under the Apache License Version 2.0.
// This product includes software developed at Datadog (https://www.datadoghq.com/).
// Copyright 2016-present Datadog, Inc.

// Package testutils_test contains a set of unit-test helper functions
// to ease the creation of unit-test around the DatadogAgent CRD controller
package testutils_test
35 changes: 35 additions & 0 deletions controllers/datadogagent/testutils/extendeddaemonset_utils.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,35 @@
// Unless explicitly stated otherwise all files in this repository are licensed
// under the Apache License Version 2.0.
// This product includes software developed at Datadog (https://www.datadoghq.com/).
// Copyright 2016-present Datadog, Inc.

package testutils_test

import (
"testing"

edsdatadoghqv1alpha1 "github.com/DataDog/extendeddaemonset/api/v1alpha1"
)

// CheckExtendedDaemonSetFunc define the signature of ExtendedDaemonSet's Check function.
type CheckExtendedDaemonSetFunc func(t *testing.T, eds *edsdatadoghqv1alpha1.ExtendedDaemonSet)

// CheckPodTemplateInEDS used to execute a CheckPodTemplateFunc function on an ExtendedDaemonSet instance
func CheckPodTemplateInEDS(templateCheck PodTemplateSpecCheckInterface) CheckExtendedDaemonSetFunc {
check := func(t *testing.T, eds *edsdatadoghqv1alpha1.ExtendedDaemonSet) {
if err := templateCheck.Check(t, &eds.Spec.Template); err != nil {
t.Error(err)
}
}
return check
}

// CheckMetadaInEDS used to execute a CheckExtendedDaemonSetFunc function on an ExtendedDaemonSet instance
func CheckMetadaInEDS(metaCheck ObjetMetaCheckInterface) CheckExtendedDaemonSetFunc {
check := func(t *testing.T, eds *edsdatadoghqv1alpha1.ExtendedDaemonSet) {
if err := metaCheck.Check(t, &eds.ObjectMeta); err != nil {
t.Error(err)
}
}
return check
}
95 changes: 95 additions & 0 deletions controllers/datadogagent/testutils/metadata_utils.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,95 @@
// Unless explicitly stated otherwise all files in this repository are licensed
// under the Apache License Version 2.0.
// This product includes software developed at Datadog (https://www.datadoghq.com/).
// Copyright 2016-present Datadog, Inc.

package testutils_test

import (
"fmt"
"testing"

metav1 "k8s.io/apimachinery/pkg/apis/meta/v1"
)

// CheckObjectMeta define the function signature of a check that runs again a metav1.ObjectMeta object.
type CheckObjectMeta func(t *testing.T, obj *metav1.ObjectMeta)

// ObjetMetaCheckInterface use as ObjectMeta check interface
type ObjetMetaCheckInterface interface {
Check(t *testing.T, obj *metav1.ObjectMeta) error
}

// CheckNameNamespace used to check if the namespace and name are the expected value on a metav1.ObjectMeta.
type CheckNameNamespace struct {
Namespace string
Name string
}

// Check used to check if the namespace and name are the expected value on a metav1.ObjectMeta.
func (c *CheckNameNamespace) Check(t *testing.T, obj *metav1.ObjectMeta) error {
if obj.Namespace != c.Namespace || obj.Name != c.Name {
return fmt.Errorf("wrong object NS/Name, want [%s/%s], got [%s/%s]", c.Namespace, c.Name, obj.Namespace, obj.Name)
}
return nil
}

// CheckLabelIsPresent used to check if a label (key,value) is present on a metav1.ObjectMeta object.
type CheckLabelIsPresent struct {
Key string
Value string
}

// Check used to check if a label (key,value) is present on a metav1.ObjectMeta object.
func (c *CheckLabelIsPresent) Check(t *testing.T, obj *metav1.ObjectMeta) error {
return checkIsPresentInMap(t, c.Key, c.Value, obj.Labels, "obj.Labels")
}

// CheckAnnotationIsPresent used to check if an annotation (key,value) is present on a metav1.ObjectMeta object.
type CheckAnnotationIsPresent struct {
Key string
Value string
}

// Check used to check if an annotation (key,value) is present on a metav1.ObjectMeta object.
func (c *CheckAnnotationIsPresent) Check(t *testing.T, obj *metav1.ObjectMeta) error {
return checkIsPresentInMap(t, c.Key, c.Value, obj.Annotations, "obj.Annotations")
}

// CheckLabelIsNotPresent used to check if a label key is not present on a metav1.ObjectMeta object.
type CheckLabelIsNotPresent struct {
Key string
}

// Check used to check if a label key is not present on a metav1.ObjectMeta object.
func (c *CheckLabelIsNotPresent) Check(t *testing.T, obj *metav1.ObjectMeta) error {
return checkLabelIsNotPresent(t, c.Key, obj.Labels, "obj.Labels")
}

// CheckAnnotationsIsNotPresent used to check if a annotation key is not present on a metav1.ObjectMeta object.
type CheckAnnotationsIsNotPresent struct {
Key string
}

// Check used to check if an annotation (key,value) is not present on a metav1.ObjectMeta object.
func (c *CheckAnnotationsIsNotPresent) Check(t *testing.T, obj *metav1.ObjectMeta) error {
return checkLabelIsNotPresent(t, c.Key, obj.Annotations, "obj.Annotations")
}

func checkIsPresentInMap(t *testing.T, key, value string, entries map[string]string, msgPrefix string) error {
if val, found := entries[key]; found {
if val == value {
t.Logf("[%s] key [%s] founded with value value [%s]", msgPrefix, key, value)
return nil
}
return fmt.Errorf("[%s] key %s founded, but wrong value, got [%s], want [%s]", msgPrefix, key, val, value)
}
return fmt.Errorf("[%s] key %s not founded", msgPrefix, key)
}

func checkLabelIsNotPresent(_ *testing.T, key string, entries map[string]string, msgPrefix string) error {
if value, found := entries[key]; found {
return fmt.Errorf("[%s] key [%s] founded with value value [%s]", msgPrefix, key, value)
}
return nil
}
165 changes: 165 additions & 0 deletions controllers/datadogagent/testutils/podtemplate_utils.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,165 @@
// Unless explicitly stated otherwise all files in this repository are licensed
// under the Apache License Version 2.0.
// This product includes software developed at Datadog (https://www.datadoghq.com/).
// Copyright 2016-present Datadog, Inc.

package testutils_test

import (
"fmt"
"testing"

corev1 "k8s.io/api/core/v1"

apiequality "k8s.io/apimachinery/pkg/api/equality"

"github.com/DataDog/datadog-operator/pkg/testutils"
)

// ContainerCheckInterface interface used by container checks.
type ContainerCheckInterface interface {
Check(t *testing.T, container *corev1.Container) error
}

// PodTemplateSpecCheckInterface interface use by PodTemplateSpec checks.
type PodTemplateSpecCheckInterface interface {
Check(t *testing.T, podTemplate *corev1.PodTemplateSpec) error
}

// CheckPodTemplateFunc define the signature of a check function against a corev1.PodTemplateSpec
type CheckPodTemplateFunc func(t *testing.T, podTemplate *corev1.PodTemplateSpec)

// CheckContainerInPodTemplate used to execute a ContainerCheckInterface.Check(...) function again a specific container in a corev1.PodTemplateSpec
// object.
func CheckContainerInPodTemplate(containerName string, checkFunc ContainerCheckInterface) CheckPodTemplateFunc {
check := func(t *testing.T, podTemplate *corev1.PodTemplateSpec) {
for _, container := range podTemplate.Spec.Containers {
if container.Name == containerName {
if err := checkFunc.Check(t, &container); err != nil {
t.Error(err)
}
}
}
t.Errorf("Container %s not founded", containerName)
}
return check
}

// CheckVolumeIsPresent used to check if a corev1.Volume is present in a corev1.PodTemplateSpec object.
type CheckVolumeIsPresent struct {
Volume *corev1.Volume
}

// Check used to check if a corev1.Volume is present in a corev1.PodTemplateSpec object.
func (c *CheckVolumeIsPresent) Check(t *testing.T, podTemplate *corev1.PodTemplateSpec) error {
for _, volumeIn := range podTemplate.Spec.Volumes {
if apiequality.Semantic.DeepEqual(c.Volume, &volumeIn) {
t.Logf("Volume %s founded", c.Volume.Name)
return nil
}
}
return fmt.Errorf("volume %s not founded", c.Volume.Name)
}

// CheckVolumeIsNotPresent used to check if a corev1.Volume is not present in a corev1.PodTemplateSpec object.
type CheckVolumeIsNotPresent struct {
Volume *corev1.Volume
}

// Check used to check if a corev1.Volume is not present in a corev1.PodTemplateSpec object.
func (c *CheckVolumeIsNotPresent) Check(t *testing.T, podTemplate *corev1.PodTemplateSpec) error {
found := false
for _, volumeIn := range podTemplate.Spec.Volumes {
if diff := testutils.CompareKubeResource(c.Volume, &volumeIn); diff == "" {
found = true
break
}
}
if found {
t.Logf("Volume %s founded", c.Volume.Name)
return nil
}
return fmt.Errorf("volume %s not founded", c.Volume.Name)
}

// CheckContainerDeepEqualIsPresent used to check if corev1.Container is equal to a container inside a corev1.PodTemplateSpec object.
type CheckContainerDeepEqualIsPresent struct {
Container *corev1.Container
}

// Check used to check if corev1.Container is equal to a container inside a corev1.PodTemplateSpec object.
func (c *CheckContainerDeepEqualIsPresent) Check(t *testing.T, podTemplate *corev1.PodTemplateSpec) error {
for _, containerIn := range podTemplate.Spec.Containers {
if diff := testutils.CompareKubeResource(c.Container, &containerIn); diff != "" {
t.Logf("container %s founded", c.Container.Name)
return nil
}
}
return fmt.Errorf("container %s not founded", c.Container.Name)
}

// CheckContainerNameIsPresentFunc used to check if container name is equal to a container name
// present in a corev1.PodTemplateSpec object.
type CheckContainerNameIsPresentFunc struct {
Name string
}

// Check used to check if container name is equal to a container name
// present in a corev1.PodTemplateSpec object.
func (c *CheckContainerNameIsPresentFunc) Check(t *testing.T, podTemplate *corev1.PodTemplateSpec) error {
for _, containerIn := range podTemplate.Spec.Containers {
if containerIn.Name == c.Name {
t.Logf("container %s founded", containerIn.Name)
return nil
}
}
return fmt.Errorf("container %s not founded", c.Name)
}

// CheckEnvVarIsPresent used to check if an corev1.EnvVar is present in a corev1.Container object
type CheckEnvVarIsPresent struct {
EnvVar *corev1.EnvVar
}

// Check used to check if an corev1.EnvVar is present in a corev1.Container object
func (c *CheckEnvVarIsPresent) Check(t *testing.T, container *corev1.Container) error {
for _, envVarIn := range container.Env {
if diff := testutils.CompareKubeResource(&envVarIn, c.EnvVar); diff == "" {
t.Logf("EnvVar %s founded in container %s", c.EnvVar.Name, container.Name)
return nil
}
}
return fmt.Errorf("EnvVar %s not founded in container %s", c.EnvVar.Name, container.Name)
}

// CheckEnvFromIsPresent used to check if an corev1.EnvFromSource is present in a corev1.Container object
type CheckEnvFromIsPresent struct {
EnvFrom *corev1.EnvFromSource
}

// Check used to check if an corev1.EnvFromSource is present in a corev1.Container object
func (c *CheckEnvFromIsPresent) Check(t *testing.T, container *corev1.Container) error {
for _, envVarIn := range container.EnvFrom {
if diff := testutils.CompareKubeResource(&envVarIn, c.EnvFrom); diff == "" {
t.Logf("EnvVar [%s] founded in container %s", c.EnvFrom.String(), container.Name)
return nil
}
}
return fmt.Errorf("envVar [%s] not founded in container %s", c.EnvFrom.String(), container.Name)
}

// CheckVolumeMountIsPresent used to check if an corev1.VolumeMount is present in a corev1.Container object
type CheckVolumeMountIsPresent struct {
VolumeMount *corev1.VolumeMount
}

// Check used to check if an corev1.VolumeMount is present in a corev1.Container object
func (c *CheckVolumeMountIsPresent) Check(t *testing.T, container *corev1.Container) error {
for _, volumeMountIn := range container.VolumeMounts {
if diff := testutils.CompareKubeResource(&volumeMountIn, c.VolumeMount); diff == "" {
t.Logf("VolumeMount [%s] founded in container %s", c.VolumeMount.String(), container.Name)
return nil
}
}
return fmt.Errorf("volumeMount [%s] not founded in container %s", c.VolumeMount.String(), container.Name)
}

0 comments on commit 1ded384

Please sign in to comment.