From 2342349f5cf7cfd462144a33fe6f1a4a56efe0b2 Mon Sep 17 00:00:00 2001 From: Pasquale Congiusti Date: Mon, 4 Dec 2023 15:30:55 +0100 Subject: [PATCH] chore: syntethic Integration unit testing --- pkg/controller/integration/initialize_test.go | 189 +++++++ .../integration/monitor_synthetic_test.go | 489 ++++++++++++++++++ pkg/controller/synthetic/synthetic.go | 4 +- pkg/controller/synthetic/synthetic_test.go | 221 ++++++++ pkg/util/test/client.go | 15 +- 5 files changed, 915 insertions(+), 3 deletions(-) create mode 100644 pkg/controller/integration/initialize_test.go create mode 100644 pkg/controller/integration/monitor_synthetic_test.go create mode 100644 pkg/controller/synthetic/synthetic_test.go diff --git a/pkg/controller/integration/initialize_test.go b/pkg/controller/integration/initialize_test.go new file mode 100644 index 0000000000..2beaaa798b --- /dev/null +++ b/pkg/controller/integration/initialize_test.go @@ -0,0 +1,189 @@ +/* +Licensed to the Apache Software Foundation (ASF) under one or more +contributor license agreements. See the NOTICE file distributed with +this work for additional information regarding copyright ownership. +The ASF licenses this file to You under the Apache License, Version 2.0 +(the "License"); you may not use this file except in compliance with +the License. You may obtain a copy of the License at + + http://www.apache.org/licenses/LICENSE-2.0 + +Unless required by applicable law or agreed to in writing, software +distributed under the License is distributed on an "AS IS" BASIS, +WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +See the License for the specific language governing permissions and +limitations under the License. +*/ + +package integration + +import ( + "context" + "testing" + + corev1 "k8s.io/api/core/v1" + metav1 "k8s.io/apimachinery/pkg/apis/meta/v1" + + v1 "github.com/apache/camel-k/v2/pkg/apis/camel/v1" + + "github.com/apache/camel-k/v2/pkg/util/log" + "github.com/apache/camel-k/v2/pkg/util/test" + + "github.com/stretchr/testify/assert" +) + +func TestCamelImportDeployment(t *testing.T) { + importedIt := &v1.Integration{ + TypeMeta: metav1.TypeMeta{ + APIVersion: v1.SchemeGroupVersion.String(), + Kind: v1.IntegrationKind, + }, + ObjectMeta: metav1.ObjectMeta{ + Namespace: "ns", + Name: "my-imported-it", + Annotations: map[string]string{ + v1.IntegrationImportedNameLabel: "my-deploy", + v1.IntegrationSyntheticLabel: "true", + v1.IntegrationImportedKindLabel: "Deployment", + }, + }, + Status: v1.IntegrationStatus{ + Phase: v1.IntegrationPhaseInitialization, + }, + } + c, err := test.NewFakeClient(importedIt) + assert.Nil(t, err) + + a := initializeAction{} + a.InjectLogger(log.Log) + a.InjectClient(c) + assert.Equal(t, "initialize", a.Name()) + assert.True(t, a.CanHandle(importedIt)) + handledIt, err := a.Handle(context.TODO(), importedIt) + assert.Nil(t, err) + assert.Equal(t, v1.IntegrationPhaseRunning, handledIt.Status.Phase) + // Ready condition + assert.Equal(t, corev1.ConditionTrue, handledIt.Status.GetCondition(v1.IntegrationConditionReady).Status) + assert.Equal(t, v1.IntegrationConditionDeploymentReadyReason, handledIt.Status.GetCondition(v1.IntegrationConditionReady).Reason) + assert.Equal(t, "imported from my-deploy Deployment", handledIt.Status.GetCondition(v1.IntegrationConditionReady).Message) + // Deployment condition + assert.Equal(t, corev1.ConditionTrue, handledIt.Status.GetCondition(v1.IntegrationConditionDeploymentAvailable).Status) + assert.Equal(t, v1.IntegrationConditionDeploymentAvailableReason, handledIt.Status.GetCondition(v1.IntegrationConditionDeploymentAvailable).Reason) + assert.Equal(t, "imported from my-deploy Deployment", handledIt.Status.GetCondition(v1.IntegrationConditionDeploymentAvailable).Message) +} + +func TestCamelImportCronJob(t *testing.T) { + importedIt := &v1.Integration{ + TypeMeta: metav1.TypeMeta{ + APIVersion: v1.SchemeGroupVersion.String(), + Kind: v1.IntegrationKind, + }, + ObjectMeta: metav1.ObjectMeta{ + Namespace: "ns", + Name: "my-imported-it", + Annotations: map[string]string{ + v1.IntegrationImportedNameLabel: "my-cron", + v1.IntegrationSyntheticLabel: "true", + v1.IntegrationImportedKindLabel: "CronJob", + }, + }, + Status: v1.IntegrationStatus{ + Phase: v1.IntegrationPhaseInitialization, + }, + } + c, err := test.NewFakeClient(importedIt) + assert.Nil(t, err) + + a := initializeAction{} + a.InjectLogger(log.Log) + a.InjectClient(c) + assert.Equal(t, "initialize", a.Name()) + assert.True(t, a.CanHandle(importedIt)) + handledIt, err := a.Handle(context.TODO(), importedIt) + assert.Nil(t, err) + assert.Equal(t, v1.IntegrationPhaseRunning, handledIt.Status.Phase) + // Ready condition + assert.Equal(t, corev1.ConditionTrue, handledIt.Status.GetCondition(v1.IntegrationConditionReady).Status) + assert.Equal(t, v1.IntegrationConditionDeploymentReadyReason, handledIt.Status.GetCondition(v1.IntegrationConditionReady).Reason) + assert.Equal(t, "imported from my-cron CronJob", handledIt.Status.GetCondition(v1.IntegrationConditionReady).Message) + // CronJob condition + assert.Equal(t, corev1.ConditionTrue, handledIt.Status.GetCondition(v1.IntegrationConditionCronJobAvailable).Status) + assert.Equal(t, v1.IntegrationConditionCronJobCreatedReason, handledIt.Status.GetCondition(v1.IntegrationConditionCronJobAvailable).Reason) + assert.Equal(t, "imported from my-cron CronJob", handledIt.Status.GetCondition(v1.IntegrationConditionCronJobAvailable).Message) +} + +func TestCamelImportKnativeService(t *testing.T) { + importedIt := &v1.Integration{ + TypeMeta: metav1.TypeMeta{ + APIVersion: v1.SchemeGroupVersion.String(), + Kind: v1.IntegrationKind, + }, + ObjectMeta: metav1.ObjectMeta{ + Namespace: "ns", + Name: "my-imported-it", + Annotations: map[string]string{ + v1.IntegrationImportedNameLabel: "my-ksvc", + v1.IntegrationSyntheticLabel: "true", + v1.IntegrationImportedKindLabel: "KnativeService", + }, + }, + Status: v1.IntegrationStatus{ + Phase: v1.IntegrationPhaseInitialization, + }, + } + c, err := test.NewFakeClient(importedIt) + assert.Nil(t, err) + + a := initializeAction{} + a.InjectLogger(log.Log) + a.InjectClient(c) + assert.Equal(t, "initialize", a.Name()) + assert.True(t, a.CanHandle(importedIt)) + handledIt, err := a.Handle(context.TODO(), importedIt) + assert.Nil(t, err) + assert.Equal(t, v1.IntegrationPhaseRunning, handledIt.Status.Phase) + // Ready condition + assert.Equal(t, corev1.ConditionTrue, handledIt.Status.GetCondition(v1.IntegrationConditionReady).Status) + assert.Equal(t, v1.IntegrationConditionKnativeServiceReadyReason, handledIt.Status.GetCondition(v1.IntegrationConditionReady).Reason) + assert.Equal(t, "imported from my-ksvc KnativeService", handledIt.Status.GetCondition(v1.IntegrationConditionReady).Message) + // Knative Service condition + assert.Equal(t, corev1.ConditionTrue, handledIt.Status.GetCondition(v1.IntegrationConditionKnativeServiceAvailable).Status) + assert.Equal(t, v1.IntegrationConditionKnativeServiceAvailableReason, handledIt.Status.GetCondition(v1.IntegrationConditionKnativeServiceAvailable).Reason) + assert.Equal(t, "imported from my-ksvc KnativeService", handledIt.Status.GetCondition(v1.IntegrationConditionKnativeServiceAvailable).Message) +} + +func TestCamelImportUnsupportedKind(t *testing.T) { + importedIt := &v1.Integration{ + TypeMeta: metav1.TypeMeta{ + APIVersion: v1.SchemeGroupVersion.String(), + Kind: v1.IntegrationKind, + }, + ObjectMeta: metav1.ObjectMeta{ + Namespace: "ns", + Name: "my-imported-it", + Annotations: map[string]string{ + v1.IntegrationImportedNameLabel: "my-kind", + v1.IntegrationSyntheticLabel: "true", + v1.IntegrationImportedKindLabel: "SomeKind", + }, + }, + Status: v1.IntegrationStatus{ + Phase: v1.IntegrationPhaseInitialization, + }, + } + c, err := test.NewFakeClient(importedIt) + assert.Nil(t, err) + + a := initializeAction{} + a.InjectLogger(log.Log) + a.InjectClient(c) + assert.Equal(t, "initialize", a.Name()) + assert.True(t, a.CanHandle(importedIt)) + handledIt, err := a.Handle(context.TODO(), importedIt) + assert.Nil(t, err) + assert.Equal(t, v1.IntegrationPhaseError, handledIt.Status.Phase) + // Ready condition + assert.Equal(t, corev1.ConditionFalse, handledIt.Status.GetCondition(v1.IntegrationConditionReady).Status) + assert.Equal(t, v1.IntegrationConditionImportingKindAvailableReason, handledIt.Status.GetCondition(v1.IntegrationConditionReady).Reason) + assert.Equal(t, "Unsupported SomeKind import kind", handledIt.Status.GetCondition(v1.IntegrationConditionReady).Message) +} diff --git a/pkg/controller/integration/monitor_synthetic_test.go b/pkg/controller/integration/monitor_synthetic_test.go new file mode 100644 index 0000000000..c2217218a2 --- /dev/null +++ b/pkg/controller/integration/monitor_synthetic_test.go @@ -0,0 +1,489 @@ +/* +Licensed to the Apache Software Foundation (ASF) under one or more +contributor license agreements. See the NOTICE file distributed with +this work for additional information regarding copyright ownership. +The ASF licenses this file to You under the Apache License, Version 2.0 +(the "License"); you may not use this file except in compliance with +the License. You may obtain a copy of the License at + + http://www.apache.org/licenses/LICENSE-2.0 + +Unless required by applicable law or agreed to in writing, software +distributed under the License is distributed on an "AS IS" BASIS, +WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +See the License for the specific language governing permissions and +limitations under the License. +*/ + +package integration + +import ( + "context" + "testing" + + appsv1 "k8s.io/api/apps/v1" + batchv1 "k8s.io/api/batch/v1" + corev1 "k8s.io/api/core/v1" + metav1 "k8s.io/apimachinery/pkg/apis/meta/v1" + servingv1 "knative.dev/serving/pkg/apis/serving/v1" + + v1 "github.com/apache/camel-k/v2/pkg/apis/camel/v1" + "github.com/apache/camel-k/v2/pkg/apis/camel/v1/trait" + + "github.com/apache/camel-k/v2/pkg/util/log" + "github.com/apache/camel-k/v2/pkg/util/test" + + "github.com/stretchr/testify/assert" + "knative.dev/pkg/apis" + duckv1 "knative.dev/pkg/apis/duck/v1" +) + +func TestMonitorSyntheticIntegrationImportingKindUnavailable(t *testing.T) { + importedIt := &v1.Integration{ + TypeMeta: metav1.TypeMeta{ + APIVersion: v1.SchemeGroupVersion.String(), + Kind: v1.IntegrationKind, + }, + ObjectMeta: metav1.ObjectMeta{ + Namespace: "ns", + Name: "my-imported-it", + Annotations: map[string]string{ + v1.IntegrationImportedNameLabel: "my-deploy", + v1.IntegrationSyntheticLabel: "true", + v1.IntegrationImportedKindLabel: "SomeKind", + }, + }, + Status: v1.IntegrationStatus{ + Phase: v1.IntegrationPhaseRunning, + }, + } + c, err := test.NewFakeClient(importedIt) + assert.Nil(t, err) + + a := monitorSyntheticAction{} + a.InjectLogger(log.Log) + a.InjectClient(c) + assert.Equal(t, "monitor-synthetic", a.Name()) + assert.True(t, a.CanHandle(importedIt)) + handledIt, err := a.Handle(context.TODO(), importedIt) + assert.NotNil(t, err) + assert.Equal(t, v1.IntegrationPhaseError, handledIt.Status.Phase) + assert.Equal(t, corev1.ConditionFalse, handledIt.Status.GetCondition(v1.IntegrationConditionReady).Status) + assert.Equal(t, v1.IntegrationConditionImportingKindAvailableReason, handledIt.Status.GetCondition(v1.IntegrationConditionReady).Reason) + assert.Equal(t, "cannot create a synthetic environment for SomeKind kind", handledIt.Status.GetCondition(v1.IntegrationConditionReady).Message) +} + +func TestMonitorSyntheticIntegrationCannotMonitorPods(t *testing.T) { + importedIt := &v1.Integration{ + TypeMeta: metav1.TypeMeta{ + APIVersion: v1.SchemeGroupVersion.String(), + Kind: v1.IntegrationKind, + }, + ObjectMeta: metav1.ObjectMeta{ + Namespace: "ns", + Name: "my-imported-it", + Annotations: map[string]string{ + v1.IntegrationImportedNameLabel: "my-deploy", + v1.IntegrationSyntheticLabel: "true", + v1.IntegrationImportedKindLabel: "Deployment", + }, + }, + Status: v1.IntegrationStatus{ + Phase: v1.IntegrationPhaseRunning, + Conditions: []v1.IntegrationCondition{ + { + Type: v1.IntegrationConditionDeploymentAvailable, + Status: corev1.ConditionTrue, + }, + { + Type: v1.IntegrationConditionReady, + Status: corev1.ConditionTrue, + }, + }, + }, + } + deploy := &appsv1.Deployment{ + TypeMeta: metav1.TypeMeta{ + APIVersion: appsv1.SchemeGroupVersion.String(), + Kind: "Deployment", + }, + ObjectMeta: metav1.ObjectMeta{ + Namespace: "ns", + Name: "my-deploy", + Annotations: map[string]string{ + v1.IntegrationLabel: "my-imported-it", + }, + }, + } + c, err := test.NewFakeClient(importedIt, deploy) + assert.Nil(t, err) + + a := monitorSyntheticAction{} + a.InjectLogger(log.Log) + a.InjectClient(c) + assert.Equal(t, "monitor-synthetic", a.Name()) + assert.True(t, a.CanHandle(importedIt)) + handledIt, err := a.Handle(context.TODO(), importedIt) + assert.Nil(t, err) + assert.Equal(t, v1.IntegrationPhaseCannotMonitor, handledIt.Status.Phase) + // Ready condition should be still true + assert.Equal(t, corev1.ConditionTrue, handledIt.Status.GetCondition(v1.IntegrationConditionReady).Status) + // Check monitoring pods condition + assert.Equal(t, corev1.ConditionFalse, handledIt.Status.GetCondition(v1.IntegrationConditionMonitoringPodsAvailable).Status) + assert.Equal(t, v1.IntegrationConditionMonitoringPodsAvailableReason, handledIt.Status.GetCondition(v1.IntegrationConditionMonitoringPodsAvailable).Reason) + assert.Equal(t, "Could not find `camel.apache.org/integration: my-imported-it` label in the Deployment/my-deploy template. Make sure to include this label in the template for Pod monitoring purposes.", handledIt.Status.GetCondition(v1.IntegrationConditionMonitoringPodsAvailable).Message) +} + +func TestMonitorSyntheticIntegrationDeployment(t *testing.T) { + importedIt := &v1.Integration{ + TypeMeta: metav1.TypeMeta{ + APIVersion: v1.SchemeGroupVersion.String(), + Kind: v1.IntegrationKind, + }, + ObjectMeta: metav1.ObjectMeta{ + Namespace: "ns", + Name: "my-imported-it", + Annotations: map[string]string{ + v1.IntegrationImportedNameLabel: "my-deploy", + v1.IntegrationSyntheticLabel: "true", + v1.IntegrationImportedKindLabel: "Deployment", + }, + }, + Spec: v1.IntegrationSpec{ + Traits: v1.Traits{ + Container: &trait.ContainerTrait{ + Name: "my-cnt", + }, + }, + }, + Status: v1.IntegrationStatus{ + Phase: v1.IntegrationPhaseRunning, + Conditions: []v1.IntegrationCondition{ + { + Type: v1.IntegrationConditionDeploymentAvailable, + Status: corev1.ConditionTrue, + }, + { + Type: v1.IntegrationConditionReady, + Status: corev1.ConditionTrue, + }, + }, + }, + } + deploy := &appsv1.Deployment{ + TypeMeta: metav1.TypeMeta{ + APIVersion: appsv1.SchemeGroupVersion.String(), + Kind: "Deployment", + }, + ObjectMeta: metav1.ObjectMeta{ + Namespace: "ns", + Name: "my-deploy", + Annotations: map[string]string{ + v1.IntegrationLabel: "my-imported-it", + }, + }, + Spec: appsv1.DeploymentSpec{ + Template: corev1.PodTemplateSpec{ + ObjectMeta: metav1.ObjectMeta{ + Labels: map[string]string{ + v1.IntegrationLabel: "my-imported-it", + }, + }, + Spec: corev1.PodSpec{ + Containers: []corev1.Container{ + { + Name: "my-cnt", + Image: "my-img", + }, + }, + }, + }, + }, + } + pod := &corev1.Pod{ + TypeMeta: metav1.TypeMeta{ + APIVersion: appsv1.SchemeGroupVersion.String(), + Kind: "Pod", + }, + ObjectMeta: metav1.ObjectMeta{ + Namespace: "ns", + Name: "my-pod", + Labels: map[string]string{ + v1.IntegrationLabel: "my-imported-it", + }, + }, + Spec: corev1.PodSpec{ + Containers: []corev1.Container{ + { + Name: "my-cnt", + Image: "my-img", + }, + }, + }, + Status: corev1.PodStatus{ + Phase: corev1.PodRunning, + Conditions: []corev1.PodCondition{ + { + Type: corev1.PodReady, + Status: corev1.ConditionTrue, + }, + }, + }, + } + c, err := test.NewFakeClient(importedIt, deploy, pod) + assert.Nil(t, err) + + a := monitorSyntheticAction{} + a.InjectLogger(log.Log) + a.InjectClient(c) + assert.Equal(t, "monitor-synthetic", a.Name()) + assert.True(t, a.CanHandle(importedIt)) + handledIt, err := a.Handle(context.TODO(), importedIt) + assert.Nil(t, err) + assert.Equal(t, v1.IntegrationPhaseRunning, handledIt.Status.Phase) + assert.Equal(t, int32(1), *handledIt.Status.Replicas) + // Ready condition + assert.Equal(t, corev1.ConditionTrue, handledIt.Status.GetCondition(v1.IntegrationConditionReady).Status) + assert.Equal(t, v1.IntegrationConditionDeploymentReadyReason, handledIt.Status.GetCondition(v1.IntegrationConditionReady).Reason) + assert.Equal(t, "1/1 ready replicas", handledIt.Status.GetCondition(v1.IntegrationConditionReady).Message) + // Check monitoring pods condition + assert.Equal(t, corev1.ConditionTrue, handledIt.Status.GetCondition(v1.IntegrationConditionMonitoringPodsAvailable).Status) + assert.Equal(t, v1.IntegrationConditionMonitoringPodsAvailableReason, handledIt.Status.GetCondition(v1.IntegrationConditionMonitoringPodsAvailable).Reason) +} + +func TestMonitorSyntheticIntegrationCronJob(t *testing.T) { + importedIt := &v1.Integration{ + TypeMeta: metav1.TypeMeta{ + APIVersion: v1.SchemeGroupVersion.String(), + Kind: v1.IntegrationKind, + }, + ObjectMeta: metav1.ObjectMeta{ + Namespace: "ns", + Name: "my-imported-it", + Annotations: map[string]string{ + v1.IntegrationImportedNameLabel: "my-cron", + v1.IntegrationSyntheticLabel: "true", + v1.IntegrationImportedKindLabel: "CronJob", + }, + }, + Status: v1.IntegrationStatus{ + Phase: v1.IntegrationPhaseRunning, + Conditions: []v1.IntegrationCondition{ + { + Type: v1.IntegrationConditionCronJobAvailable, + Status: corev1.ConditionTrue, + }, + { + Type: v1.IntegrationConditionReady, + Status: corev1.ConditionTrue, + }, + }, + }, + } + cron := &batchv1.CronJob{ + TypeMeta: metav1.TypeMeta{ + APIVersion: appsv1.SchemeGroupVersion.String(), + Kind: "CronJob", + }, + ObjectMeta: metav1.ObjectMeta{ + Namespace: "ns", + Name: "my-cron", + Labels: map[string]string{ + v1.IntegrationLabel: "my-imported-it", + }, + }, + Spec: batchv1.CronJobSpec{ + JobTemplate: batchv1.JobTemplateSpec{ + Spec: batchv1.JobSpec{ + Template: corev1.PodTemplateSpec{ + ObjectMeta: metav1.ObjectMeta{ + Labels: map[string]string{ + v1.IntegrationLabel: "my-imported-it", + }, + }, + Spec: corev1.PodSpec{ + Containers: []corev1.Container{ + { + Name: "my-cnt", + Image: "my-img", + }, + }, + }, + }, + }, + }, + }, + } + pod := &corev1.Pod{ + TypeMeta: metav1.TypeMeta{ + APIVersion: appsv1.SchemeGroupVersion.String(), + Kind: "Pod", + }, + ObjectMeta: metav1.ObjectMeta{ + Namespace: "ns", + Name: "my-pod", + Labels: map[string]string{ + v1.IntegrationLabel: "my-imported-it", + }, + }, + Spec: corev1.PodSpec{ + Containers: []corev1.Container{ + { + Name: "my-cnt", + Image: "my-img", + }, + }, + }, + Status: corev1.PodStatus{ + Phase: corev1.PodRunning, + Conditions: []corev1.PodCondition{ + { + Type: corev1.PodReady, + Status: corev1.ConditionTrue, + }, + }, + }, + } + c, err := test.NewFakeClient(importedIt, cron, pod) + assert.Nil(t, err) + + a := monitorSyntheticAction{} + a.InjectLogger(log.Log) + a.InjectClient(c) + assert.Equal(t, "monitor-synthetic", a.Name()) + assert.True(t, a.CanHandle(importedIt)) + handledIt, err := a.Handle(context.TODO(), importedIt) + assert.Nil(t, err) + assert.Equal(t, v1.IntegrationPhaseRunning, handledIt.Status.Phase) + assert.Equal(t, int32(1), *handledIt.Status.Replicas) + // Ready condition + assert.Equal(t, corev1.ConditionTrue, handledIt.Status.GetCondition(v1.IntegrationConditionReady).Status) + assert.Equal(t, v1.IntegrationConditionCronJobCreatedReason, handledIt.Status.GetCondition(v1.IntegrationConditionReady).Reason) + assert.Equal(t, "cronjob created", handledIt.Status.GetCondition(v1.IntegrationConditionReady).Message) + // Check monitoring pods condition + assert.Equal(t, corev1.ConditionTrue, handledIt.Status.GetCondition(v1.IntegrationConditionMonitoringPodsAvailable).Status) + assert.Equal(t, v1.IntegrationConditionMonitoringPodsAvailableReason, handledIt.Status.GetCondition(v1.IntegrationConditionMonitoringPodsAvailable).Reason) +} + +func TestMonitorSyntheticIntegrationKnativeService(t *testing.T) { + importedIt := &v1.Integration{ + TypeMeta: metav1.TypeMeta{ + APIVersion: v1.SchemeGroupVersion.String(), + Kind: v1.IntegrationKind, + }, + ObjectMeta: metav1.ObjectMeta{ + Namespace: "ns", + Name: "my-imported-it", + Annotations: map[string]string{ + v1.IntegrationImportedNameLabel: "my-ksvc", + v1.IntegrationSyntheticLabel: "true", + v1.IntegrationImportedKindLabel: "KnativeService", + }, + }, + Status: v1.IntegrationStatus{ + Phase: v1.IntegrationPhaseRunning, + Conditions: []v1.IntegrationCondition{ + { + Type: v1.IntegrationConditionKnativeServiceAvailable, + Status: corev1.ConditionTrue, + }, + { + Type: v1.IntegrationConditionReady, + Status: corev1.ConditionTrue, + }, + }, + }, + } + ksvc := &servingv1.Service{ + TypeMeta: metav1.TypeMeta{ + APIVersion: appsv1.SchemeGroupVersion.String(), + Kind: "Service", + }, + ObjectMeta: metav1.ObjectMeta{ + Namespace: "ns", + Name: "my-ksvc", + Labels: map[string]string{ + v1.IntegrationLabel: "my-imported-it", + }, + }, + Spec: servingv1.ServiceSpec{ + ConfigurationSpec: servingv1.ConfigurationSpec{ + Template: servingv1.RevisionTemplateSpec{ + ObjectMeta: metav1.ObjectMeta{ + Labels: map[string]string{ + v1.IntegrationLabel: "my-imported-it", + }, + }, + Spec: servingv1.RevisionSpec{ + PodSpec: corev1.PodSpec{ + Containers: []corev1.Container{ + { + Name: "my-cnt", + Image: "my-img", + }, + }, + }, + }, + }, + }, + }, + Status: servingv1.ServiceStatus{ + Status: duckv1.Status{ + Conditions: duckv1.Conditions{ + apis.Condition{ + Type: servingv1.ServiceConditionReady, + Status: corev1.ConditionTrue, + }, + }, + }, + }, + } + pod := &corev1.Pod{ + TypeMeta: metav1.TypeMeta{ + APIVersion: appsv1.SchemeGroupVersion.String(), + Kind: "Pod", + }, + ObjectMeta: metav1.ObjectMeta{ + Namespace: "ns", + Name: "my-pod", + Labels: map[string]string{ + v1.IntegrationLabel: "my-imported-it", + }, + }, + Spec: corev1.PodSpec{ + Containers: []corev1.Container{ + { + Name: "my-cnt", + Image: "my-img", + }, + }, + }, + Status: corev1.PodStatus{ + Phase: corev1.PodRunning, + Conditions: []corev1.PodCondition{ + { + Type: corev1.PodReady, + Status: corev1.ConditionTrue, + }, + }, + }, + } + c, err := test.NewFakeClient(importedIt, ksvc, pod) + assert.Nil(t, err) + + a := monitorSyntheticAction{} + a.InjectLogger(log.Log) + a.InjectClient(c) + assert.Equal(t, "monitor-synthetic", a.Name()) + assert.True(t, a.CanHandle(importedIt)) + handledIt, err := a.Handle(context.TODO(), importedIt) + assert.Nil(t, err) + assert.Equal(t, v1.IntegrationPhaseRunning, handledIt.Status.Phase) + assert.Equal(t, int32(1), *handledIt.Status.Replicas) + // Ready condition + assert.Equal(t, corev1.ConditionTrue, handledIt.Status.GetCondition(v1.IntegrationConditionReady).Status) + assert.Equal(t, v1.IntegrationConditionKnativeServiceReadyReason, handledIt.Status.GetCondition(v1.IntegrationConditionReady).Reason) + // Check monitoring pods condition + assert.Equal(t, corev1.ConditionTrue, handledIt.Status.GetCondition(v1.IntegrationConditionMonitoringPodsAvailable).Status) + assert.Equal(t, v1.IntegrationConditionMonitoringPodsAvailableReason, handledIt.Status.GetCondition(v1.IntegrationConditionMonitoringPodsAvailable).Reason) +} diff --git a/pkg/controller/synthetic/synthetic.go b/pkg/controller/synthetic/synthetic.go index bd785d3185..c5f7bbb342 100644 --- a/pkg/controller/synthetic/synthetic.go +++ b/pkg/controller/synthetic/synthetic.go @@ -220,7 +220,7 @@ func nonManagedCamelApplicationFactory(obj ctrl.Object) (nonManagedCamelApplicat if ok { return &NonManagedCamelKnativeService{ksvc: ksvc}, nil } - return nil, fmt.Errorf("unsupported %s object kind", obj) + return nil, fmt.Errorf("unsupported %s object kind", obj.GetName()) } // NonManagedCamelDeployment represents a regular Camel application built and deployed outside the operator lifecycle. @@ -252,7 +252,7 @@ func (app *nonManagedCamelDeployment) getContainerNameFromDeployment() string { for _, ct := range app.deploy.Spec.Template.Spec.Containers { // set as fallback if no container is named as the deployment if firstContainerName == "" { - firstContainerName = app.deploy.Name + firstContainerName = ct.Name } if ct.Name == app.deploy.Name { return app.deploy.Name diff --git a/pkg/controller/synthetic/synthetic_test.go b/pkg/controller/synthetic/synthetic_test.go new file mode 100644 index 0000000000..c600f6d3e9 --- /dev/null +++ b/pkg/controller/synthetic/synthetic_test.go @@ -0,0 +1,221 @@ +/* +Licensed to the Apache Software Foundation (ASF) under one or more +contributor license agreements. See the NOTICE file distributed with +this work for additional information regarding copyright ownership. +The ASF licenses this file to You under the Apache License, Version 2.0 +(the "License"); you may not use this file except in compliance with +the License. You may obtain a copy of the License at + + http://www.apache.org/licenses/LICENSE-2.0 + +Unless required by applicable law or agreed to in writing, software +distributed under the License is distributed on an "AS IS" BASIS, +WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +See the License for the specific language governing permissions and +limitations under the License. +*/ + +package synthetic + +import ( + "testing" + + appsv1 "k8s.io/api/apps/v1" + batchv1 "k8s.io/api/batch/v1" + corev1 "k8s.io/api/core/v1" + metav1 "k8s.io/apimachinery/pkg/apis/meta/v1" + servingv1 "knative.dev/serving/pkg/apis/serving/v1" + + v1 "github.com/apache/camel-k/v2/pkg/apis/camel/v1" + "github.com/apache/camel-k/v2/pkg/apis/camel/v1/trait" + + "github.com/stretchr/testify/assert" +) + +func TestNonManagedUnsupported(t *testing.T) { + pod := &corev1.Pod{ + TypeMeta: metav1.TypeMeta{ + APIVersion: corev1.SchemeGroupVersion.String(), + Kind: "Pod", + }, + ObjectMeta: metav1.ObjectMeta{ + Namespace: "ns", + Name: "my-pod", + Labels: map[string]string{ + v1.IntegrationLabel: "my-imported-it", + }, + }, + Spec: corev1.PodSpec{ + Containers: []corev1.Container{ + { + Name: "my-cnt", + Image: "my-img", + }, + }, + }, + Status: corev1.PodStatus{ + Phase: corev1.PodRunning, + Conditions: []corev1.PodCondition{ + { + Type: corev1.PodReady, + Status: corev1.ConditionTrue, + }, + }, + }, + } + + nilAdapter, err := nonManagedCamelApplicationFactory(pod) + assert.NotNil(t, err) + assert.Equal(t, "unsupported my-pod object kind", err.Error()) + assert.Nil(t, nilAdapter) +} + +func TestNonManagedDeployment(t *testing.T) { + deploy := &appsv1.Deployment{ + TypeMeta: metav1.TypeMeta{ + APIVersion: appsv1.SchemeGroupVersion.String(), + Kind: "Deployment", + }, + ObjectMeta: metav1.ObjectMeta{ + Namespace: "ns", + Name: "my-deploy", + Labels: map[string]string{ + v1.IntegrationLabel: "my-imported-it", + }, + }, + Spec: appsv1.DeploymentSpec{ + Template: corev1.PodTemplateSpec{ + ObjectMeta: metav1.ObjectMeta{ + Labels: map[string]string{ + v1.IntegrationLabel: "my-imported-it", + }, + }, + Spec: corev1.PodSpec{ + Containers: []corev1.Container{ + { + Name: "my-cnt", + Image: "my-img", + }, + }, + }, + }, + }, + } + + expectedIt := v1.NewIntegration("ns", "my-imported-it") + expectedIt.SetAnnotations(map[string]string{ + v1.IntegrationImportedNameLabel: "my-deploy", + v1.IntegrationImportedKindLabel: "Deployment", + v1.IntegrationSyntheticLabel: "true", + }) + expectedIt.Spec = v1.IntegrationSpec{ + Traits: v1.Traits{ + Container: &trait.ContainerTrait{ + Name: "my-cnt", + }, + }, + } + + deploymentAdapter, err := nonManagedCamelApplicationFactory(deploy) + assert.Nil(t, err) + assert.NotNil(t, deploymentAdapter) + assert.Equal(t, expectedIt, *deploymentAdapter.Integration()) +} + +func TestNonManagedCronJob(t *testing.T) { + cron := &batchv1.CronJob{ + TypeMeta: metav1.TypeMeta{ + APIVersion: batchv1.SchemeGroupVersion.String(), + Kind: "CronJob", + }, + ObjectMeta: metav1.ObjectMeta{ + Namespace: "ns", + Name: "my-cron", + Labels: map[string]string{ + v1.IntegrationLabel: "my-imported-it", + }, + }, + Spec: batchv1.CronJobSpec{ + JobTemplate: batchv1.JobTemplateSpec{ + Spec: batchv1.JobSpec{ + Template: corev1.PodTemplateSpec{ + ObjectMeta: metav1.ObjectMeta{ + Labels: map[string]string{ + v1.IntegrationLabel: "my-imported-it", + }, + }, + Spec: corev1.PodSpec{ + Containers: []corev1.Container{ + { + Name: "my-cnt", + Image: "my-img", + }, + }, + }, + }, + }, + }, + }, + } + + expectedIt := v1.NewIntegration("ns", "my-imported-it") + expectedIt.SetAnnotations(map[string]string{ + v1.IntegrationImportedNameLabel: "my-cron", + v1.IntegrationImportedKindLabel: "CronJob", + v1.IntegrationSyntheticLabel: "true", + }) + + cronJobAdapter, err := nonManagedCamelApplicationFactory(cron) + assert.Nil(t, err) + assert.NotNil(t, cronJobAdapter) + assert.Equal(t, expectedIt, *cronJobAdapter.Integration()) +} + +func TestNonManagedKnativeService(t *testing.T) { + ksvc := &servingv1.Service{ + TypeMeta: metav1.TypeMeta{ + APIVersion: appsv1.SchemeGroupVersion.String(), + Kind: "Service", + }, + ObjectMeta: metav1.ObjectMeta{ + Namespace: "ns", + Name: "my-ksvc", + Labels: map[string]string{ + v1.IntegrationLabel: "my-imported-it", + }, + }, + Spec: servingv1.ServiceSpec{ + ConfigurationSpec: servingv1.ConfigurationSpec{ + Template: servingv1.RevisionTemplateSpec{ + ObjectMeta: metav1.ObjectMeta{ + Labels: map[string]string{ + v1.IntegrationLabel: "my-imported-it", + }, + }, + Spec: servingv1.RevisionSpec{ + PodSpec: corev1.PodSpec{ + Containers: []corev1.Container{ + { + Name: "my-cnt", + Image: "my-img", + }, + }, + }, + }, + }, + }, + }, + } + + expectedIt := v1.NewIntegration("ns", "my-imported-it") + expectedIt.SetAnnotations(map[string]string{ + v1.IntegrationImportedNameLabel: "my-ksvc", + v1.IntegrationImportedKindLabel: "KnativeService", + v1.IntegrationSyntheticLabel: "true", + }) + + knativeServiceAdapter, err := nonManagedCamelApplicationFactory(ksvc) + assert.Nil(t, err) + assert.NotNil(t, knativeServiceAdapter) + assert.Equal(t, expectedIt, *knativeServiceAdapter.Integration()) +} diff --git a/pkg/util/test/client.go b/pkg/util/test/client.go index 952e0da1f0..3f3658a09a 100644 --- a/pkg/util/test/client.go +++ b/pkg/util/test/client.go @@ -30,6 +30,7 @@ import ( camelv1alpha1 "github.com/apache/camel-k/v2/pkg/client/camel/clientset/versioned/typed/camel/v1alpha1" "github.com/apache/camel-k/v2/pkg/util" autoscalingv1 "k8s.io/api/autoscaling/v1" + corev1 "k8s.io/api/core/v1" k8serrors "k8s.io/apimachinery/pkg/api/errors" metav1 "k8s.io/apimachinery/pkg/apis/meta/v1" "k8s.io/apimachinery/pkg/runtime" @@ -55,7 +56,19 @@ func NewFakeClient(initObjs ...runtime.Object) (client.Client, error) { return nil, err } - c := fake.NewClientBuilder().WithScheme(scheme).WithRuntimeObjects(initObjs...).Build() + c := fake. + NewClientBuilder(). + WithScheme(scheme). + WithIndex( + &corev1.Pod{}, + "status.phase", + func(obj controller.Object) []string { + pod, _ := obj.(*corev1.Pod) + return []string{string(pod.Status.Phase)} + }, + ). + WithRuntimeObjects(initObjs...). + Build() camelClientset := fakecamelclientset.NewSimpleClientset(filterObjects(scheme, initObjs, func(gvk schema.GroupVersionKind) bool { return strings.Contains(gvk.Group, "camel")