Skip to content

Commit fdf5f68

Browse files
committed
Add new e2e2 test for new controllers
Signed-off-by: jose.vazquez <[email protected]>
1 parent 0eea118 commit fdf5f68

File tree

11 files changed

+245
-87
lines changed

11 files changed

+245
-87
lines changed

internal/controller/atlasthirdpartyintegrations/handler.go

Lines changed: 2 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -53,7 +53,8 @@ func (h *AtlasThirdPartyIntegrationHandler) HandleUpdated(ctx context.Context, i
5353
func (h *AtlasThirdPartyIntegrationHandler) HandleDeletionRequested(ctx context.Context, integration *akov2next.AtlasThirdPartyIntegration) (ctrlstate.Result, error) {
5454
req, err := h.newReconcileRequest(ctx, integration)
5555
if err != nil {
56-
return result.Error(state.StateDeletionRequested, fmt.Errorf("failed to build reconcile request: %w", err))
56+
// TODO is this good for all cases?
57+
return h.unmanage(integration.Spec.Type)
5758
}
5859

5960
if !h.deletionProtection {

test/e2e/dry_run_test.go

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -39,7 +39,7 @@ import (
3939
"github.com/mongodb/mongodb-atlas-kubernetes/v2/test/helper/e2e/api/atlas"
4040
e2e_config "github.com/mongodb/mongodb-atlas-kubernetes/v2/test/helper/e2e/config"
4141
"github.com/mongodb/mongodb-atlas-kubernetes/v2/test/helper/e2e/model"
42-
"github.com/mongodb/mongodb-atlas-kubernetes/v2/test/helper/e2e/operator"
42+
"github.com/mongodb/mongodb-atlas-kubernetes/v2/test/helper/e2e2/operator"
4343
)
4444

4545
type (

test/e2e2/configs/datadog.sample.yml

Lines changed: 32 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,32 @@
1+
apiVersion: atlas.mongodb.com/v1
2+
kind: AtlasProject
3+
metadata:
4+
name: atlas-project-test-datadog
5+
spec:
6+
name: atlas-project-test-datadog
7+
---
8+
apiVersion: v1
9+
kind: Secret
10+
metadata:
11+
name: datadog-secret
12+
namespace:
13+
labels:
14+
atlas.mongodb.com/type: credentials
15+
stringData:
16+
apiKey: 1117e51ce6725368c37c3535959a3a75
17+
---
18+
apiVersion: atlas.nextapi.mongodb.com/v1
19+
kind: AtlasThirdPartyIntegration
20+
metadata:
21+
name: atlas-datadog-integ
22+
spec:
23+
projectRef:
24+
name: atlas-project-test-datadog
25+
type: DATADOG
26+
datadog:
27+
apiKeySecretRef:
28+
name: datadog-secret
29+
region: US
30+
sendCollectionLatencyMetrics: enabled
31+
sendDatabaseMetrics: enabled
32+

test/e2e2/integration_test.go

Lines changed: 43 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -16,32 +16,72 @@ package e2e2_test
1616

1717
import (
1818
"context"
19+
"embed"
20+
"os"
1921
"testing"
22+
"time"
2023

24+
"github.com/stretchr/testify/assert"
2125
"github.com/stretchr/testify/require"
26+
v1 "k8s.io/apimachinery/pkg/apis/meta/v1"
2227
"sigs.k8s.io/controller-runtime/pkg/client"
2328

24-
"github.com/mongodb/mongodb-atlas-kubernetes/v2/test/e2e2"
29+
akov2next "github.com/mongodb/mongodb-atlas-kubernetes/v2/internal/nextapi/v1"
2530
"github.com/mongodb/mongodb-atlas-kubernetes/v2/test/helper/control"
31+
"github.com/mongodb/mongodb-atlas-kubernetes/v2/test/helper/e2e2/kube"
32+
"github.com/mongodb/mongodb-atlas-kubernetes/v2/test/helper/e2e2/operator"
33+
"github.com/mongodb/mongodb-atlas-kubernetes/v2/test/helper/e2e2/yml"
2634
)
2735

36+
//go:embed configs/*
37+
var configs embed.FS
38+
2839
const (
2940
AtlasThirdPartyIntegrationsCRD = "atlasthirdpartyintegrations.atlas.nextapi.mongodb.com"
3041
)
3142

3243
func TestAtlasThirdPartyIntegrationsCreate(t *testing.T) {
3344
control.SkipTestUnless(t, "AKO_E2E2_TEST")
45+
ns := control.MustEnvVar("OPERATOR_NAMESPACE")
3446
ctx := context.Background()
3547
for _, tc := range []struct {
3648
name string
3749
objs []client.Object
3850
wantReady string
3951
}{
40-
{},
52+
{
53+
name: "simple datadog sample",
54+
objs: yml.MustParseCRs(yml.MustOpen(configs, "configs/datadog.sample.yml")),
55+
wantReady: "atlas-datadog-integ",
56+
},
4157
} {
4258
t.Run(tc.name, func(t *testing.T) {
43-
_, err := e2e2.InitK8sTest(ctx, AtlasThirdPartyIntegrationsCRD)
59+
kubeClient, err := kube.NewK8sTest(ctx, AtlasThirdPartyIntegrationsCRD)
4460
require.NoError(t, err, "Kubernetes test env is not available")
61+
ako := runTestAKO()
62+
ako.Start(t)
63+
defer ako.Stop(t)
64+
65+
require.NoError(t, kube.Apply(ctx, kubeClient, ns, tc.objs...))
66+
67+
integration := akov2next.AtlasThirdPartyIntegration{
68+
ObjectMeta: v1.ObjectMeta{
69+
Name: tc.wantReady,
70+
Namespace: ns,
71+
},
72+
}
73+
key := client.ObjectKeyFromObject(&integration)
74+
assert.NoError(t, kube.WaitConditionOrFailure(time.Minute, func() (bool, error) {
75+
return kube.AssertObjReady(ctx, kubeClient, key, &integration)
76+
}))
4577
})
4678
}
4779
}
80+
81+
func runTestAKO() *operator.Operator {
82+
return operator.NewOperator(control.MustEnvVar("OPERATOR_NAMESPACE"), os.Stdout, os.Stderr,
83+
"--log-level=-9",
84+
"--global-api-secret-name=mongodb-atlas-operator-api-key",
85+
`--atlas-domain=https://cloud-qa.mongodb.com`,
86+
)
87+
}

test/helper/control/enable.go

Lines changed: 12 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -15,14 +15,23 @@
1515
package control
1616

1717
import (
18+
"fmt"
1819
"os"
19-
"strings"
20+
"strconv"
2021
"testing"
2122
)
2223

2324
func Enabled(envvar string) bool {
24-
value := strings.ToLower(os.Getenv(envvar))
25-
return value == "1"
25+
envSet, _ := strconv.ParseBool(os.Getenv(envvar))
26+
return envSet
27+
}
28+
29+
func MustEnvVar(envvar string) string {
30+
value, ok := os.LookupEnv(envvar)
31+
if !ok {
32+
panic(fmt.Errorf("missing required environment variable: %v", envvar))
33+
}
34+
return value
2635
}
2736

2837
func SkipTestUnless(t *testing.T, envvar string) {

test/e2e2/kube.go renamed to test/helper/e2e2/kube/kube.go

Lines changed: 88 additions & 67 deletions
Original file line numberDiff line numberDiff line change
@@ -12,16 +12,18 @@
1212
// See the License for the specific language governing permissions and
1313
// limitations under the License.
1414

15-
package e2e2
15+
package kube
1616

1717
import (
1818
"context"
19+
"errors"
1920
"fmt"
20-
"os"
21-
"strconv"
21+
"time"
2222

2323
corev1 "k8s.io/api/core/v1"
2424
apiextensionsv1 "k8s.io/apiextensions-apiserver/pkg/apis/apiextensions/v1"
25+
apierrors "k8s.io/apimachinery/pkg/api/errors"
26+
metav1 "k8s.io/apimachinery/pkg/apis/meta/v1"
2527
"k8s.io/apimachinery/pkg/runtime"
2628
ctrl "sigs.k8s.io/controller-runtime"
2729
"sigs.k8s.io/controller-runtime/pkg/client"
@@ -30,30 +32,30 @@ import (
3032
akov2next "github.com/mongodb/mongodb-atlas-kubernetes/v2/internal/nextapi/v1"
3133
)
3234

33-
// InitK8sTest initializes a test enviroment on Kubernetes.
34-
// It requires:
35-
// - A running kubernetes cluster with a local configuration accessing it.
36-
// - The given target CRD being installed in that cluster
37-
// - Certain environment variables, such as OPERATOR_NAMESPACE, being set
38-
// - The operator running on a given Pod or as a process with a given PID
39-
func InitK8sTest(ctx context.Context, targetCRD string) (client.Client, error) {
40-
if err := assertRequiredEnvVars("OPERATOR_NAMESPACE"); err != nil {
41-
return nil, fmt.Errorf("missing required test Kubernetes env vars: %w", err)
42-
}
35+
const (
36+
Pause = time.Second
37+
)
4338

39+
type ObjectWithStatus interface {
40+
client.Object
41+
GetConditions() []metav1.Condition
42+
}
43+
44+
// NewK8sTest initializes a test environment on Kubernetes.
45+
// It requires:
46+
// - A running Kubernetes cluster with a local configuration bound to it.
47+
// - The given set CRDs installed in that cluster
48+
func NewK8sTest(ctx context.Context, crds ...string) (client.Client, error) {
4449
kubeClient, err := TestKubeClient()
4550
if err != nil {
4651
return nil, fmt.Errorf("failed to setup Kubernetes test env client: %w", err)
4752
}
4853

49-
if err := assertCRD(ctx, kubeClient, targetCRD); err != nil {
50-
return nil, fmt.Errorf("failed to asert for test-required CRD: %w", err)
51-
}
52-
53-
if err := assertOperator(ctx, kubeClient); err != nil {
54-
return nil, fmt.Errorf("failed to asert for test operator running: %w", err)
54+
for _, targetCRD := range crds {
55+
if err := assertCRD(ctx, kubeClient, targetCRD); err != nil {
56+
return nil, fmt.Errorf("failed to asert for test-required CRD: %w", err)
57+
}
5558
}
56-
5759
return kubeClient, nil
5860
}
5961

@@ -72,6 +74,72 @@ func TestKubeClient() (client.Client, error) {
7274
return getKubeClient(testScheme)
7375
}
7476

77+
func Apply(ctx context.Context, kubeClient client.Client, defaultNamespace string, objs ...client.Object) error {
78+
for i, obj := range objs {
79+
if obj.GetNamespace() == "" {
80+
obj = obj.DeepCopyObject().(client.Object)
81+
obj.SetNamespace(defaultNamespace)
82+
}
83+
if err := apply(ctx, kubeClient, obj); err != nil {
84+
return fmt.Errorf("failed to apply object %d: %w", (i + 1), err)
85+
}
86+
}
87+
return nil
88+
}
89+
90+
func apply(ctx context.Context, kubeClient client.Client, obj client.Object) error {
91+
key := client.ObjectKeyFromObject(obj)
92+
old := obj.DeepCopyObject().(client.Object)
93+
err := kubeClient.Get(ctx, key, old)
94+
switch {
95+
case err == nil:
96+
obj = obj.DeepCopyObject().(client.Object)
97+
obj.SetResourceVersion(old.GetResourceVersion())
98+
if err := kubeClient.Update(ctx, obj); err != nil {
99+
return fmt.Errorf("failed to update %s: %w", key, err)
100+
}
101+
case apierrors.IsNotFound(err):
102+
if err := kubeClient.Create(ctx, obj); err != nil {
103+
return fmt.Errorf("failed to create %s: %w", key, err)
104+
}
105+
default:
106+
return fmt.Errorf("failed to apply %s: %w", key, err)
107+
}
108+
return nil
109+
}
110+
111+
type OKOrFailureFunc func() (bool, error)
112+
113+
func WaitConditionOrFailure(timeout time.Duration, okOrFailFn OKOrFailureFunc) error {
114+
start := time.Now()
115+
for {
116+
ok, err := okOrFailFn()
117+
if ok {
118+
return nil
119+
}
120+
if err != nil {
121+
return fmt.Errorf("failed to check condition: %w", err)
122+
}
123+
if time.Since(start) > timeout {
124+
return errors.New("wait condition timed out")
125+
}
126+
time.Sleep(Pause)
127+
}
128+
}
129+
130+
func AssertObjReady(ctx context.Context, kubeClient client.Client, key client.ObjectKey, obj ObjectWithStatus) (bool, error) {
131+
err := kubeClient.Get(ctx, key, obj)
132+
if err != nil {
133+
return false, fmt.Errorf("failed to get object %v: %w", key, err)
134+
}
135+
for _, condition := range obj.GetConditions() {
136+
if condition.Type == "Ready" && condition.Status == metav1.ConditionTrue {
137+
return true, nil
138+
}
139+
}
140+
return false, nil
141+
}
142+
75143
func getTestScheme(addToSchemeFunctions ...func(*runtime.Scheme) error) (*runtime.Scheme, error) {
76144
testScheme := runtime.NewScheme()
77145
for _, addToSchemeFn := range addToSchemeFunctions {
@@ -106,50 +174,3 @@ func assertCRD(ctx context.Context, kubeClient client.Client, targetCRD string)
106174
}
107175
return fmt.Errorf("%s not found", targetCRD)
108176
}
109-
110-
func assertRequiredEnvVars(envVars ...string) error {
111-
missing := make([]string, 0, len(envVars))
112-
for _, envVar := range envVars {
113-
_, ok := os.LookupEnv(envVar)
114-
if !ok {
115-
missing = append(missing, envVar)
116-
}
117-
}
118-
if len(missing) > 0 {
119-
return fmt.Errorf("missing required env vars: %v", missing)
120-
}
121-
return nil
122-
}
123-
124-
func assertOperator(ctx context.Context, kubeClient client.Client) error {
125-
pid := os.Getenv("OPERATOR_PID")
126-
if pid != "" {
127-
return assertProcessIsRunning(pid)
128-
}
129-
pod := os.Getenv("OPERATOR_POD_NAME")
130-
if pod != "" {
131-
ns := os.Getenv("OPERATOR_NAMESPACE")
132-
return assertPod(ctx, kubeClient, pod, ns)
133-
}
134-
return fmt.Errorf("please set OPERATOR_PID or OPERATOR_POD_NAME to allow to check he operator is running")
135-
}
136-
137-
func assertProcessIsRunning(pidString string) error {
138-
pid, err := strconv.Atoi(pidString)
139-
if err != nil {
140-
return fmt.Errorf("failed to convert %s to a numeric PID: %w", pidString, err)
141-
}
142-
if _, err := os.FindProcess(pid); err != nil {
143-
return fmt.Errorf("failed to find process for PID %d: %w", pid, err)
144-
}
145-
return nil
146-
}
147-
148-
func assertPod(ctx context.Context, kubeClient client.Client, pod, ns string) error {
149-
podObj := corev1.Pod{}
150-
key := client.ObjectKey{Name: pod, Namespace: ns}
151-
if err := kubeClient.Get(ctx, key, &podObj, &client.GetOptions{}); err != nil {
152-
return fmt.Errorf("failed to get POD %s/%s: %w", ns, pod, err)
153-
}
154-
return nil
155-
}
File renamed without changes.

0 commit comments

Comments
 (0)