Skip to content

Commit 7e19847

Browse files
khewonctbavelier
andauthored
Add basic controller tests for DDAI (#2139)
Co-authored-by: Timothée Bavelier <[email protected]>
1 parent 7abdca0 commit 7e19847

File tree

4 files changed

+387
-3
lines changed

4 files changed

+387
-3
lines changed

internal/controller/datadogagent/controller_v2_test.go

Lines changed: 379 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -14,6 +14,7 @@ import (
1414
"time"
1515

1616
apicommon "github.com/DataDog/datadog-operator/api/datadoghq/common"
17+
"github.com/DataDog/datadog-operator/api/datadoghq/v1alpha1"
1718
"github.com/DataDog/datadog-operator/api/datadoghq/v2alpha1"
1819
apiutils "github.com/DataDog/datadog-operator/api/utils"
1920
common "github.com/DataDog/datadog-operator/internal/controller/datadogagent/common"
@@ -1550,3 +1551,381 @@ func verifyPDB(t *testing.T, c client.Client) error {
15501551
assert.Nil(t, ccrPDB.Spec.MinAvailable)
15511552
return nil
15521553
}
1554+
1555+
func Test_DDAI_ReconcileV3(t *testing.T) {
1556+
const resourcesName = "foo"
1557+
const resourcesNamespace = "bar"
1558+
1559+
// Register operator types with the runtime scheme.
1560+
s := agenttestutils.TestScheme()
1561+
// Load CRD from config folder
1562+
crd, err := getDDAICRDFromConfig(s)
1563+
assert.NoError(t, err)
1564+
eventBroadcaster := record.NewBroadcaster()
1565+
recorder := eventBroadcaster.NewRecorder(s, corev1.EventSource{Component: "Test_DDAI_ReconcileV3"})
1566+
1567+
forwarders := dummyManager{}
1568+
logf.SetLogger(zap.New(zap.UseDevMode(true)))
1569+
1570+
defaultRequeueDuration := 15 * time.Second
1571+
1572+
dda := testutils.NewInitializedDatadogAgentBuilder(resourcesNamespace, resourcesName).BuildWithDefaults()
1573+
1574+
tests := []struct {
1575+
name string
1576+
profilesEnabled bool
1577+
profile *v1alpha1.DatadogAgentProfile
1578+
loadFunc func(c client.Client) *v2alpha1.DatadogAgent
1579+
want reconcile.Result
1580+
wantErr bool
1581+
wantFunc func(t *testing.T, c client.Client) error
1582+
}{
1583+
{
1584+
name: "[ddai] Create DDAI from minimal DDA",
1585+
loadFunc: func(c client.Client) *v2alpha1.DatadogAgent {
1586+
_ = c.Create(context.TODO(), dda)
1587+
return dda
1588+
},
1589+
want: reconcile.Result{RequeueAfter: defaultRequeueDuration},
1590+
wantErr: false,
1591+
wantFunc: func(t *testing.T, c client.Client) error {
1592+
expectedDDAI := getBaseDDAI(dda)
1593+
expectedDDAI.Annotations = map[string]string{
1594+
constants.MD5DDAIDeploymentAnnotationKey: "db25da8b5c8cd681d92f0049101605d6",
1595+
}
1596+
1597+
return verifyDDAI(t, c, []v1alpha1.DatadogAgentInternal{expectedDDAI})
1598+
},
1599+
},
1600+
{
1601+
name: "[ddai] Create DDAI from customized DDA",
1602+
loadFunc: func(c client.Client) *v2alpha1.DatadogAgent {
1603+
ddaCustom := testutils.NewInitializedDatadogAgentBuilder(resourcesNamespace, resourcesName).
1604+
WithDCAToken("abcdefghijklmnopqrstuvwxyz").
1605+
WithCredentialsFromSecret("custom-secret", "api", "custom-secret2", "app").
1606+
WithComponentOverride(v2alpha1.NodeAgentComponentName, v2alpha1.DatadogAgentComponentOverride{
1607+
Labels: map[string]string{
1608+
"custom-label": "custom-value",
1609+
},
1610+
}).
1611+
WithClusterChecksEnabled(true).
1612+
WithClusterChecksUseCLCEnabled(true).
1613+
BuildWithDefaults()
1614+
_ = c.Create(context.TODO(), ddaCustom)
1615+
return ddaCustom
1616+
},
1617+
want: reconcile.Result{RequeueAfter: defaultRequeueDuration},
1618+
wantErr: false,
1619+
wantFunc: func(t *testing.T, c client.Client) error {
1620+
baseDDAI := getBaseDDAI(dda)
1621+
expectedDDAI := baseDDAI.DeepCopy()
1622+
expectedDDAI.Annotations = map[string]string{
1623+
constants.MD5DDAIDeploymentAnnotationKey: "ecf20e786d34265b5ad2a2e841837b55",
1624+
}
1625+
expectedDDAI.Spec.Features.ClusterChecks.UseClusterChecksRunners = apiutils.NewBoolPointer(true)
1626+
expectedDDAI.Spec.Global.Credentials = &v2alpha1.DatadogCredentials{
1627+
APISecret: &v2alpha1.SecretConfig{
1628+
SecretName: "custom-secret",
1629+
KeyName: "api",
1630+
},
1631+
AppSecret: &v2alpha1.SecretConfig{
1632+
SecretName: "custom-secret2",
1633+
KeyName: "app",
1634+
},
1635+
}
1636+
expectedDDAI.Spec.Global.ClusterAgentTokenSecret = &v2alpha1.SecretConfig{
1637+
SecretName: "foo-token",
1638+
KeyName: "token",
1639+
}
1640+
expectedDDAI.Spec.Override = map[v2alpha1.ComponentName]*v2alpha1.DatadogAgentComponentOverride{
1641+
v2alpha1.NodeAgentComponentName: {
1642+
Labels: map[string]string{
1643+
"custom-label": "custom-value",
1644+
constants.MD5AgentDeploymentProviderLabelKey: "",
1645+
},
1646+
Annotations: map[string]string{
1647+
"checksum/dca-token-custom-config": "0c85492446fadac292912bb6d5fc3efd",
1648+
},
1649+
},
1650+
v2alpha1.ClusterAgentComponentName: {
1651+
Annotations: map[string]string{
1652+
"checksum/dca-token-custom-config": "0c85492446fadac292912bb6d5fc3efd",
1653+
},
1654+
},
1655+
v2alpha1.ClusterChecksRunnerComponentName: {
1656+
Annotations: map[string]string{
1657+
"checksum/dca-token-custom-config": "0c85492446fadac292912bb6d5fc3efd",
1658+
},
1659+
},
1660+
}
1661+
1662+
return verifyDDAI(t, c, []v1alpha1.DatadogAgentInternal{*expectedDDAI})
1663+
},
1664+
},
1665+
{
1666+
name: "[ddai] Create DDAI from minimal DDA and default profile",
1667+
loadFunc: func(c client.Client) *v2alpha1.DatadogAgent {
1668+
_ = c.Create(context.TODO(), dda)
1669+
return dda
1670+
},
1671+
profilesEnabled: true,
1672+
want: reconcile.Result{RequeueAfter: defaultRequeueDuration},
1673+
wantErr: false,
1674+
wantFunc: func(t *testing.T, c client.Client) error {
1675+
return verifyDDAI(t, c, []v1alpha1.DatadogAgentInternal{getDefaultDDAI(dda)})
1676+
},
1677+
},
1678+
{
1679+
name: "[ddai] Create DDAI from minimal DDA and user created profile",
1680+
loadFunc: func(c client.Client) *v2alpha1.DatadogAgent {
1681+
_ = c.Create(context.TODO(), dda)
1682+
return dda
1683+
},
1684+
profilesEnabled: true,
1685+
profile: &v1alpha1.DatadogAgentProfile{
1686+
ObjectMeta: metav1.ObjectMeta{
1687+
Name: "foo-profile",
1688+
Namespace: resourcesNamespace,
1689+
},
1690+
Spec: v1alpha1.DatadogAgentProfileSpec{
1691+
ProfileAffinity: &v1alpha1.ProfileAffinity{
1692+
ProfileNodeAffinity: []corev1.NodeSelectorRequirement{
1693+
{
1694+
Key: "foo",
1695+
Operator: corev1.NodeSelectorOpIn,
1696+
Values: []string{"foo-profile"},
1697+
},
1698+
},
1699+
},
1700+
Config: &v2alpha1.DatadogAgentSpec{
1701+
Override: map[v2alpha1.ComponentName]*v2alpha1.DatadogAgentComponentOverride{
1702+
v2alpha1.NodeAgentComponentName: {
1703+
Labels: map[string]string{
1704+
"foo": "bar",
1705+
},
1706+
},
1707+
},
1708+
},
1709+
},
1710+
},
1711+
want: reconcile.Result{RequeueAfter: defaultRequeueDuration},
1712+
wantErr: false,
1713+
wantFunc: func(t *testing.T, c client.Client) error {
1714+
profileDDAI := getBaseDDAI(dda)
1715+
profileDDAI.Name = "foo-profile"
1716+
profileDDAI.Annotations = map[string]string{
1717+
constants.MD5DDAIDeploymentAnnotationKey: "74a9d7cc65524e555ad895710ab603dd",
1718+
}
1719+
profileDDAI.Labels[constants.ProfileLabelKey] = "foo-profile"
1720+
profileDDAI.Spec.Override = map[v2alpha1.ComponentName]*v2alpha1.DatadogAgentComponentOverride{
1721+
v2alpha1.ClusterAgentComponentName: {
1722+
Disabled: apiutils.NewBoolPointer(true),
1723+
},
1724+
v2alpha1.ClusterChecksRunnerComponentName: {
1725+
Disabled: apiutils.NewBoolPointer(true),
1726+
},
1727+
v2alpha1.NodeAgentComponentName: {
1728+
Labels: map[string]string{
1729+
constants.MD5AgentDeploymentProviderLabelKey: "",
1730+
"foo": "bar",
1731+
constants.ProfileLabelKey: "foo-profile",
1732+
},
1733+
Affinity: &corev1.Affinity{
1734+
NodeAffinity: &corev1.NodeAffinity{
1735+
RequiredDuringSchedulingIgnoredDuringExecution: &corev1.NodeSelector{
1736+
NodeSelectorTerms: []corev1.NodeSelectorTerm{
1737+
{
1738+
MatchExpressions: []corev1.NodeSelectorRequirement{
1739+
{
1740+
Key: "foo",
1741+
Operator: corev1.NodeSelectorOpIn,
1742+
Values: []string{"foo-profile"},
1743+
},
1744+
{
1745+
Key: constants.ProfileLabelKey,
1746+
Operator: corev1.NodeSelectorOpIn,
1747+
Values: []string{"foo-profile"},
1748+
},
1749+
},
1750+
},
1751+
},
1752+
},
1753+
},
1754+
PodAntiAffinity: &corev1.PodAntiAffinity{
1755+
RequiredDuringSchedulingIgnoredDuringExecution: []corev1.PodAffinityTerm{
1756+
{
1757+
LabelSelector: &metav1.LabelSelector{
1758+
MatchExpressions: []metav1.LabelSelectorRequirement{
1759+
{
1760+
Key: apicommon.AgentDeploymentComponentLabelKey,
1761+
Operator: metav1.LabelSelectorOpIn,
1762+
Values: []string{string(apicommon.CoreAgentContainerName)},
1763+
},
1764+
},
1765+
},
1766+
TopologyKey: "kubernetes.io/hostname",
1767+
},
1768+
},
1769+
},
1770+
},
1771+
},
1772+
}
1773+
1774+
return verifyDDAI(t, c, []v1alpha1.DatadogAgentInternal{getDefaultDDAI(dda), profileDDAI})
1775+
},
1776+
},
1777+
}
1778+
1779+
for _, tt := range tests {
1780+
t.Run(tt.name, func(t *testing.T) {
1781+
objs := []client.Object{crd}
1782+
if tt.profile != nil {
1783+
objs = append(objs, tt.profile)
1784+
}
1785+
r := &Reconciler{
1786+
client: fake.NewClientBuilder().WithStatusSubresource(&v2alpha1.DatadogAgent{}, &v1alpha1.DatadogAgentProfile{}, &v1alpha1.DatadogAgentInternal{}).WithObjects(objs...).Build(),
1787+
scheme: s,
1788+
recorder: recorder,
1789+
log: logf.Log.WithName(tt.name),
1790+
forwarders: forwarders,
1791+
options: ReconcilerOptions{
1792+
DatadogAgentInternalEnabled: true,
1793+
DatadogAgentProfileEnabled: tt.profilesEnabled,
1794+
},
1795+
}
1796+
1797+
var dda *v2alpha1.DatadogAgent
1798+
if tt.loadFunc != nil {
1799+
dda = tt.loadFunc(r.client)
1800+
}
1801+
1802+
got, err := r.Reconcile(context.TODO(), dda)
1803+
if tt.wantErr {
1804+
assert.Error(t, err, "ReconcileDatadogAgent.Reconcile() expected an error")
1805+
} else {
1806+
assert.NoError(t, err, "ReconcileDatadogAgent.Reconcile() unexpected error: %v", err)
1807+
}
1808+
1809+
assert.Equal(t, tt.want, got, "ReconcileDatadogAgent.Reconcile() unexpected result")
1810+
1811+
if tt.wantFunc != nil {
1812+
err := tt.wantFunc(t, r.client)
1813+
assert.NoError(t, err, "ReconcileDatadogAgent.Reconcile() wantFunc validation error: %v", err)
1814+
}
1815+
})
1816+
}
1817+
}
1818+
1819+
func verifyDDAI(t *testing.T, c client.Client, expectedDDAI []v1alpha1.DatadogAgentInternal) error {
1820+
ddaiList := v1alpha1.DatadogAgentInternalList{}
1821+
if err := c.List(context.TODO(), &ddaiList); err != nil {
1822+
return err
1823+
}
1824+
assert.Equal(t, len(expectedDDAI), len(ddaiList.Items))
1825+
for i := range ddaiList.Items {
1826+
// clear managed fields
1827+
ddaiList.Items[i].ObjectMeta.ManagedFields = nil
1828+
// type meta is only added when merging ddais
1829+
ddaiList.Items[i].TypeMeta = metav1.TypeMeta{}
1830+
}
1831+
assert.ElementsMatch(t, expectedDDAI, ddaiList.Items)
1832+
return nil
1833+
}
1834+
1835+
func getBaseDDAI(dda *v2alpha1.DatadogAgent) v1alpha1.DatadogAgentInternal {
1836+
expectedDDAI := v1alpha1.DatadogAgentInternal{
1837+
ObjectMeta: metav1.ObjectMeta{
1838+
Name: dda.Name,
1839+
Namespace: dda.Namespace,
1840+
ResourceVersion: "1",
1841+
Labels: map[string]string{
1842+
apicommon.DatadogAgentNameLabelKey: dda.Name,
1843+
},
1844+
OwnerReferences: []metav1.OwnerReference{
1845+
{
1846+
APIVersion: "datadoghq.com/v2alpha1",
1847+
Kind: "DatadogAgent",
1848+
Name: dda.Name,
1849+
Controller: apiutils.NewBoolPointer(true),
1850+
BlockOwnerDeletion: apiutils.NewBoolPointer(true),
1851+
},
1852+
},
1853+
},
1854+
Spec: v2alpha1.DatadogAgentSpec{
1855+
Features: dda.Spec.Features,
1856+
Global: dda.Spec.Global,
1857+
Override: map[v2alpha1.ComponentName]*v2alpha1.DatadogAgentComponentOverride{
1858+
v2alpha1.NodeAgentComponentName: {
1859+
Labels: map[string]string{
1860+
constants.MD5AgentDeploymentProviderLabelKey: "",
1861+
},
1862+
},
1863+
},
1864+
},
1865+
}
1866+
1867+
expectedDDAI.Spec.Global.Credentials = &v2alpha1.DatadogCredentials{
1868+
APISecret: &v2alpha1.SecretConfig{
1869+
SecretName: "foo-secret",
1870+
KeyName: "api_key",
1871+
},
1872+
AppSecret: &v2alpha1.SecretConfig{
1873+
SecretName: "foo-secret",
1874+
KeyName: "app_key",
1875+
},
1876+
}
1877+
1878+
expectedDDAI.Spec.Global.ClusterAgentTokenSecret = &v2alpha1.SecretConfig{
1879+
SecretName: "foo-token",
1880+
KeyName: "token",
1881+
}
1882+
1883+
return expectedDDAI
1884+
}
1885+
1886+
func getDefaultDDAI(dda *v2alpha1.DatadogAgent) v1alpha1.DatadogAgentInternal {
1887+
expectedDDAI := getBaseDDAI(dda)
1888+
expectedDDAI.Annotations = map[string]string{
1889+
constants.MD5DDAIDeploymentAnnotationKey: "8b985778b07536be633b3f49cb113e02",
1890+
}
1891+
expectedDDAI.Spec.Override = map[v2alpha1.ComponentName]*v2alpha1.DatadogAgentComponentOverride{
1892+
v2alpha1.NodeAgentComponentName: {
1893+
Labels: map[string]string{
1894+
constants.MD5AgentDeploymentProviderLabelKey: "",
1895+
},
1896+
Affinity: &corev1.Affinity{
1897+
NodeAffinity: &corev1.NodeAffinity{
1898+
RequiredDuringSchedulingIgnoredDuringExecution: &corev1.NodeSelector{
1899+
NodeSelectorTerms: []corev1.NodeSelectorTerm{
1900+
{
1901+
MatchExpressions: []corev1.NodeSelectorRequirement{
1902+
{
1903+
Key: constants.ProfileLabelKey,
1904+
Operator: corev1.NodeSelectorOpDoesNotExist,
1905+
},
1906+
},
1907+
},
1908+
},
1909+
},
1910+
},
1911+
PodAntiAffinity: &corev1.PodAntiAffinity{
1912+
RequiredDuringSchedulingIgnoredDuringExecution: []corev1.PodAffinityTerm{
1913+
{
1914+
LabelSelector: &metav1.LabelSelector{
1915+
MatchExpressions: []metav1.LabelSelectorRequirement{
1916+
{
1917+
Key: apicommon.AgentDeploymentComponentLabelKey,
1918+
Operator: metav1.LabelSelectorOpIn,
1919+
Values: []string{string(apicommon.CoreAgentContainerName)},
1920+
},
1921+
},
1922+
},
1923+
TopologyKey: "kubernetes.io/hostname",
1924+
},
1925+
},
1926+
},
1927+
},
1928+
},
1929+
}
1930+
return expectedDDAI
1931+
}

0 commit comments

Comments
 (0)