diff --git a/internal/controller/ai_gateway_route_test.go b/internal/controller/ai_gateway_route_test.go index 082f6408..80bc2abe 100644 --- a/internal/controller/ai_gateway_route_test.go +++ b/internal/controller/ai_gateway_route_test.go @@ -33,9 +33,11 @@ func Test_extProcName(t *testing.T) { func TestAIGatewayRouteController_ensuresExtProcConfigMapExists(t *testing.T) { c := &aiGatewayRouteController{client: fake.NewClientBuilder().WithScheme(scheme).Build()} c.kube = fake2.NewClientset() - - ownerRef := []metav1.OwnerReference{{APIVersion: "v1", Kind: "Kind", Name: "Name"}} - aiGatewayRoute := &aigv1a1.AIGatewayRoute{ObjectMeta: metav1.ObjectMeta{Name: "myroute", Namespace: "default"}} + name := "myroute" + ownerRef := []metav1.OwnerReference{ + {APIVersion: "aigateway.envoyproxy.io/v1alpha1", Kind: "AIGatewayRoute", Name: name, Controller: ptr.To(true), BlockOwnerDeletion: ptr.To(true)}, + } + aiGatewayRoute := &aigv1a1.AIGatewayRoute{ObjectMeta: metav1.ObjectMeta{Name: name, Namespace: "default"}} err := c.ensuresExtProcConfigMapExists(context.Background(), aiGatewayRoute) require.NoError(t, err) @@ -54,10 +56,13 @@ func TestAIGatewayRouteController_ensuresExtProcConfigMapExists(t *testing.T) { func TestAIGatewayRouteController_reconcileExtProcExtensionPolicy(t *testing.T) { c := &aiGatewayRouteController{client: fake.NewClientBuilder().WithScheme(scheme).Build()} - ownerRef := []metav1.OwnerReference{{APIVersion: "v1", Kind: "Kind", Name: "Name"}} + name := "myroute" + ownerRef := []metav1.OwnerReference{ + {APIVersion: "aigateway.envoyproxy.io/v1alpha1", Kind: "AIGatewayRoute", Name: name, Controller: ptr.To(true), BlockOwnerDeletion: ptr.To(true)}, + } aiGatewayRoute := &aigv1a1.AIGatewayRoute{ ObjectMeta: metav1.ObjectMeta{ - Name: "myroute", + Name: name, Namespace: "default", }, Spec: aigv1a1.AIGatewayRouteSpec{ diff --git a/tests/controller/controller_test.go b/tests/controller/controller_test.go index d23c3ac6..711bc1c0 100644 --- a/tests/controller/controller_test.go +++ b/tests/controller/controller_test.go @@ -16,10 +16,11 @@ import ( egv1a1 "github.com/envoyproxy/gateway/api/v1alpha1" "github.com/go-logr/logr" "github.com/stretchr/testify/require" + appsv1 "k8s.io/api/apps/v1" corev1 "k8s.io/api/core/v1" "k8s.io/apimachinery/pkg/api/resource" metav1 "k8s.io/apimachinery/pkg/apis/meta/v1" - klog "k8s.io/klog/v2" + "k8s.io/klog/v2" "k8s.io/utils/ptr" ctrl "sigs.k8s.io/controller-runtime" "sigs.k8s.io/controller-runtime/pkg/client" @@ -141,6 +142,7 @@ func TestStartControllers(t *testing.T) { require.Len(t, deployment.OwnerReferences, 1) require.Equal(t, aiGatewayRoute.Name, deployment.OwnerReferences[0].Name) require.Equal(t, "AIGatewayRoute", deployment.OwnerReferences[0].Kind) + require.True(t, *deployment.OwnerReferences[0].Controller) require.Equal(t, int32(5), *deployment.Spec.Replicas) require.Equal(t, resourceReq, &deployment.Spec.Template.Spec.Containers[0].Resources) @@ -154,6 +156,7 @@ func TestStartControllers(t *testing.T) { require.Len(t, service.OwnerReferences, 1) require.Equal(t, aiGatewayRoute.Name, service.OwnerReferences[0].Name) require.Equal(t, "AIGatewayRoute", service.OwnerReferences[0].Kind) + require.True(t, *service.OwnerReferences[0].Controller) extPolicy := egv1a1.EnvoyExtensionPolicy{} err = c.Get(ctx, client.ObjectKey{Name: extProcName(route), Namespace: "default"}, &extPolicy) @@ -163,6 +166,7 @@ func TestStartControllers(t *testing.T) { } require.Len(t, extPolicy.OwnerReferences, 1) require.Equal(t, aiGatewayRoute.Name, extPolicy.OwnerReferences[0].Name) + require.True(t, *extPolicy.OwnerReferences[0].Controller) configMap, err := k.CoreV1().ConfigMaps("default").Get(ctx, extProcName(route), metav1.GetOptions{}) if err != nil { @@ -171,6 +175,7 @@ func TestStartControllers(t *testing.T) { } require.Len(t, configMap.OwnerReferences, 1) require.Equal(t, aiGatewayRoute.Name, configMap.OwnerReferences[0].Name) + require.True(t, *configMap.OwnerReferences[0].Controller) require.Contains(t, configMap.Data, "extproc-config.yaml") return true }, 30*time.Second, 200*time.Millisecond) @@ -238,15 +243,32 @@ func TestStartControllers(t *testing.T) { }, 30*time.Second, 200*time.Millisecond) }) - t.Run("verify http route 'route1' is recreated if deleted", func(t *testing.T) { - // When the HTTPRoute resource is deleted, the AIGatewayRoute controller should recreate it. + t.Run("verify resources created by AIGatewayRoute controller are recreated if deleted", func(t *testing.T) { routeName := "route1" routeNamespace := "default" - // Delete the HTTPRoute resource. - err := c.Delete(ctx, &gwapiv1.HTTPRoute{ObjectMeta: metav1.ObjectMeta{Name: routeName, Namespace: routeNamespace}}) + // When the EnvoyExtensionPolicy is deleted, the controller should recreate it. + policyName := extProcName(routeName) + policyNamespace := routeNamespace + err := c.Delete(ctx, &egv1a1.EnvoyExtensionPolicy{ObjectMeta: metav1.ObjectMeta{Name: policyName, Namespace: policyNamespace}}) require.NoError(t, err) + // Verify that the HTTPRoute resource is recreated. + require.Eventually(t, func() bool { + var egExtPolicy egv1a1.EnvoyExtensionPolicy + err := c.Get(ctx, client.ObjectKey{Name: policyName, Namespace: policyNamespace}, &egExtPolicy) + if err != nil { + t.Logf("failed to get envoy extension policy %s: %v", policyName, err) + return false + } else if egExtPolicy.DeletionTimestamp != nil { + // Make sure it is not the EnvoyExtensionPolicy resource that is being deleted. + return false + } + return true + }, 30*time.Second, 200*time.Millisecond) + // When the HTTPRoute resource is deleted, the controller should recreate it. + err = c.Delete(ctx, &gwapiv1.HTTPRoute{ObjectMeta: metav1.ObjectMeta{Name: routeName, Namespace: routeNamespace}}) + require.NoError(t, err) // Verify that the HTTPRoute resource is recreated. require.Eventually(t, func() bool { var httpRoute gwapiv1.HTTPRoute @@ -260,6 +282,44 @@ func TestStartControllers(t *testing.T) { } return true }, 30*time.Second, 200*time.Millisecond) + + // When extproc deployment is deleted, the controller should recreate it. + deployName := extProcName(routeName) + deployNamespace := routeNamespace + err = c.Delete(ctx, &appsv1.Deployment{ObjectMeta: metav1.ObjectMeta{Name: deployName, Namespace: deployNamespace}}) + require.NoError(t, err) + // Verify that the deployment is recreated. + require.Eventually(t, func() bool { + var deployment appsv1.Deployment + err := c.Get(ctx, client.ObjectKey{Name: deployName, Namespace: deployNamespace}, &deployment) + if err != nil { + t.Logf("failed to get deployment %s: %v", deployName, err) + return false + } else if deployment.DeletionTimestamp != nil { + // Make sure it is not the deployment resource that is being deleted. + return false + } + return true + }, 30*time.Second, 200*time.Millisecond) + + // When extproc service is deleted, the controller should recreate it. + serviceName := extProcName(routeName) + serviceNamespace := routeNamespace + err = c.Delete(ctx, &corev1.Service{ObjectMeta: metav1.ObjectMeta{Name: serviceName, Namespace: serviceNamespace}}) + require.NoError(t, err) + // Verify that the service is recreated. + require.Eventually(t, func() bool { + var service corev1.Service + err := c.Get(ctx, client.ObjectKey{Name: serviceName, Namespace: serviceNamespace}, &service) + if err != nil { + t.Logf("failed to get service %s: %v", serviceName, err) + return false + } else if service.DeletionTimestamp != nil { + // Make sure it is not the service resource that is being deleted. + return false + } + return true + }, 30*time.Second, 200*time.Millisecond) }) }