From 684f5c1b4ed8130a5a10d81b6c15fee033228870 Mon Sep 17 00:00:00 2001 From: Drew Malin Date: Wed, 8 Jul 2026 16:13:21 -0700 Subject: [PATCH 1/2] fix: ensure AWS does not terminate idle TCP connections over load balancer --- v1/providers/testkube/instance.go | 34 +++++++++++++++++--------- v1/providers/testkube/instance_test.go | 6 +++++ 2 files changed, 29 insertions(+), 11 deletions(-) diff --git a/v1/providers/testkube/instance.go b/v1/providers/testkube/instance.go index ef89f75..9d981bb 100644 --- a/v1/providers/testkube/instance.go +++ b/v1/providers/testkube/instance.go @@ -6,6 +6,7 @@ import ( "encoding/hex" "encoding/json" "fmt" + "maps" "regexp" "slices" "strings" @@ -29,16 +30,18 @@ const ( labelNameValue = "test-kubernetes" labelManagedByValue = "brev-cloud-sdk" - annotationRefID = "testkube.brev.dev/ref-id" - annotationCloudCredRefID = "testkube.brev.dev/cloud-cred-ref-id" //nolint:gosec // this is a valid annotation - annotationName = "testkube.brev.dev/name" - annotationLocation = "testkube.brev.dev/location" - annotationSubLocation = "testkube.brev.dev/sub-location" - annotationInstanceType = "testkube.brev.dev/instance-type" - annotationImageID = "testkube.brev.dev/image-id" - annotationCreatedAt = "testkube.brev.dev/created-at" - annotationScenario = "testkube.brev.dev/scenario" - annotationTagsJSON = "testkube.brev.dev/tags-json" + annotationRefID = "testkube.brev.dev/ref-id" + annotationCloudCredRefID = "testkube.brev.dev/cloud-cred-ref-id" //nolint:gosec // this is a valid annotation + annotationName = "testkube.brev.dev/name" + annotationLocation = "testkube.brev.dev/location" + annotationSubLocation = "testkube.brev.dev/sub-location" + annotationInstanceType = "testkube.brev.dev/instance-type" + annotationImageID = "testkube.brev.dev/image-id" + annotationCreatedAt = "testkube.brev.dev/created-at" + annotationScenario = "testkube.brev.dev/scenario" + annotationTagsJSON = "testkube.brev.dev/tags-json" + annotationAWSLoadBalancerConnectionIdleTimeout = "service.beta.kubernetes.io/aws-load-balancer-connection-idle-timeout" + awsLoadBalancerConnectionIdleTimeout = "1800" // 30 minutes envInstanceType = "TESTKUBE_INSTANCE_TYPE" envScenario = "TESTKUBE_SCENARIO" @@ -79,6 +82,15 @@ func (c *TestKubeClient) createInstanceAsK8sResources(ctx context.Context, attrs location := c.resourceLocation(attrs) annotations := c.resourceAnnotations(cloudID, attrs, instanceTypeSpec) + var serviceAnnotations map[string]string + if instanceTypeSpec.serviceType == corev1.ServiceTypeLoadBalancer { + serviceAnnotations = maps.Clone(annotations) + // Setup scripts can run without producing SSH traffic. Keep the load balancer from closing an otherwise healthy, idle SSH connection. + serviceAnnotations[annotationAWSLoadBalancerConnectionIdleTimeout] = awsLoadBalancerConnectionIdleTimeout + } else { + serviceAnnotations = annotations + } + // Create the service. k8sService, err := c.k8sClient. CoreV1(). @@ -88,7 +100,7 @@ func (c *TestKubeClient) createInstanceAsK8sResources(ctx context.Context, attrs Name: string(cloudID), Namespace: c.namespace, Labels: objectLabels(string(cloudID), location), - Annotations: annotations, + Annotations: serviceAnnotations, }, Spec: corev1.ServiceSpec{ Type: instanceTypeSpec.serviceType, diff --git a/v1/providers/testkube/instance_test.go b/v1/providers/testkube/instance_test.go index 148b960..5dd4136 100644 --- a/v1/providers/testkube/instance_test.go +++ b/v1/providers/testkube/instance_test.go @@ -73,6 +73,12 @@ func TestInstanceLifecycle(t *testing.T) { spec, ok := getInstanceTypeSpec(InstanceTypeOKCPU) require.True(t, ok) require.Equal(t, spec.imageID, instance.ImageID) + service, err := client.k8sClient.CoreV1().Services(client.namespace).Get(ctx, string(instance.CloudID), metav1.GetOptions{}) + require.NoError(t, err) + require.Equal(t, awsLoadBalancerConnectionIdleTimeout, service.Annotations[annotationAWSLoadBalancerConnectionIdleTimeout]) + pod, err := client.k8sClient.CoreV1().Pods(client.namespace).Get(ctx, string(instance.CloudID), metav1.GetOptions{}) + require.NoError(t, err) + require.NotContains(t, pod.Annotations, annotationAWSLoadBalancerConnectionIdleTimeout) listed, err := client.ListInstances(ctx, cloudv1.ListInstancesArgs{ TagFilters: map[string][]string{ From 1580bccd9c30f8fbac63ab66b754c2039834716e Mon Sep 17 00:00:00 2001 From: Drew Malin Date: Wed, 8 Jul 2026 16:22:58 -0700 Subject: [PATCH 2/2] lint --- v1/providers/testkube/instance_test.go | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/v1/providers/testkube/instance_test.go b/v1/providers/testkube/instance_test.go index 5dd4136..10aab7c 100644 --- a/v1/providers/testkube/instance_test.go +++ b/v1/providers/testkube/instance_test.go @@ -52,7 +52,7 @@ func TestCreateInstanceProvisionFailures(t *testing.T) { } } -func TestInstanceLifecycle(t *testing.T) { +func TestInstanceLifecycle(t *testing.T) { //nolint:funlen // ok ctx := context.Background() client := newTestClient(t)