Skip to content

Commit

Permalink
CI: Adds Ginkgo Resource Condition Assertions (#10398)
Browse files Browse the repository at this point in the history
Signed-off-by: Daneyon Hansen <[email protected]>
Co-authored-by: Nathan Fudenberg <[email protected]>
  • Loading branch information
danehans and nfuden authored Nov 25, 2024
1 parent 7af653c commit 483799e
Show file tree
Hide file tree
Showing 5 changed files with 116 additions and 3 deletions.
6 changes: 6 additions & 0 deletions changelog/v1.18.0-rc2/issue_10397.yaml
Original file line number Diff line number Diff line change
@@ -0,0 +1,6 @@
changelog:
- type: NON_USER_FACING
issueLink: https://github.com/solo-io/gloo/issues/10397
resolvesIssue: false
description: >-
Adds ginkgo assertions for resource status of TCPRoute e2e tests.
19 changes: 17 additions & 2 deletions test/kubernetes/e2e/features/services/tcproute/suite.go
Original file line number Diff line number Diff line change
Expand Up @@ -4,6 +4,8 @@ import (
"context"

"github.com/stretchr/testify/suite"
metav1 "k8s.io/apimachinery/pkg/apis/meta/v1"
v1 "sigs.k8s.io/gateway-api/apis/v1"

"github.com/solo-io/gloo/pkg/utils/kubeutils"
"github.com/solo-io/gloo/pkg/utils/requestutils/curl"
Expand Down Expand Up @@ -42,14 +44,19 @@ func (s *testingSuite) TestConfigureTCPRouteBackingDestinationsWithSingleService

err := s.testInstallation.Actions.Kubectl().ApplyFile(s.ctx, singleListenerGatewayAndClientManifest)
s.Assert().NoError(err, "can apply gateway and client manifest")
s.testInstallation.Assertions.EventuallyGatewayCondition(s.ctx, "tcp-gateway", "default", v1.GatewayConditionAccepted, metav1.ConditionTrue, timeout)

err = s.testInstallation.Actions.Kubectl().ApplyFile(s.ctx, multiBackendServiceManifest)
s.Assert().NoError(err, "can apply backend service manifest")
s.testInstallation.Assertions.EventuallyObjectsExist(s.ctx, proxyService, proxyDeployment)

err = s.testInstallation.Actions.Kubectl().ApplyFile(s.ctx, singleTcpRouteManifest)
s.Assert().NoError(err, "can apply tcproute manifest")
s.testInstallation.Assertions.EventuallyTCPRouteCondition(s.ctx, "tcp-app-1", "default", v1.RouteConditionAccepted, metav1.ConditionTrue, timeout)
s.testInstallation.Assertions.EventuallyTCPRouteCondition(s.ctx, "tcp-app-1", "default", v1.RouteConditionResolvedRefs, metav1.ConditionTrue, timeout)
s.testInstallation.Assertions.EventuallyGatewayCondition(s.ctx, "tcp-gateway", "default", v1.GatewayConditionProgrammed, metav1.ConditionTrue, timeout)
s.testInstallation.Assertions.EventuallyGatewayListenerAttachedRoutes(s.ctx, "tcp-gateway", "default", v1.SectionName("foo"), int32(1), timeout)

s.testInstallation.Assertions.EventuallyObjectsExist(s.ctx, proxyService, proxyDeployment)
s.testInstallation.Assertions.AssertEventualCurlResponse(
s.ctx,
defaults.CurlPodExecOpt,
Expand Down Expand Up @@ -81,14 +88,22 @@ func (s *testingSuite) TestConfigureTCPRouteBackingDestinationsWithMultiServices

err := s.testInstallation.Actions.Kubectl().ApplyFile(s.ctx, multiListenerGatewayAndClientManifest)
s.Assert().NoError(err, "can apply gateway and client manifest")
s.testInstallation.Assertions.EventuallyGatewayCondition(s.ctx, "tcp-gateway", "default", v1.GatewayConditionAccepted, metav1.ConditionTrue, timeout)

err = s.testInstallation.Actions.Kubectl().ApplyFile(s.ctx, multiBackendServiceManifest)
s.Assert().NoError(err, "can apply backend service manifest")
s.testInstallation.Assertions.EventuallyObjectsExist(s.ctx, proxyService, proxyDeployment)

err = s.testInstallation.Actions.Kubectl().ApplyFile(s.ctx, multiTcpRouteManifest)
s.Assert().NoError(err, "can apply tcproute manifest")
s.testInstallation.Assertions.EventuallyTCPRouteCondition(s.ctx, "tcp-app-1", "default", v1.RouteConditionAccepted, metav1.ConditionTrue, timeout)
s.testInstallation.Assertions.EventuallyTCPRouteCondition(s.ctx, "tcp-app-1", "default", v1.RouteConditionResolvedRefs, metav1.ConditionTrue, timeout)
s.testInstallation.Assertions.EventuallyTCPRouteCondition(s.ctx, "tcp-app-2", "default", v1.RouteConditionAccepted, metav1.ConditionTrue, timeout)
s.testInstallation.Assertions.EventuallyTCPRouteCondition(s.ctx, "tcp-app-2", "default", v1.RouteConditionResolvedRefs, metav1.ConditionTrue, timeout)
s.testInstallation.Assertions.EventuallyGatewayCondition(s.ctx, "tcp-gateway", "default", v1.GatewayConditionProgrammed, metav1.ConditionTrue, timeout)
s.testInstallation.Assertions.EventuallyGatewayListenerAttachedRoutes(s.ctx, "tcp-gateway", "default", v1.SectionName("foo"), int32(1), timeout)
s.testInstallation.Assertions.EventuallyGatewayListenerAttachedRoutes(s.ctx, "tcp-gateway", "default", v1.SectionName("bar"), int32(1), timeout)

s.testInstallation.Assertions.EventuallyObjectsExist(s.ctx, proxyService, proxyDeployment)
s.testInstallation.Assertions.AssertEventualCurlResponse(
s.ctx,
defaults.CurlPodExecOpt,
Expand Down
3 changes: 3 additions & 0 deletions test/kubernetes/e2e/features/services/tcproute/types.go
Original file line number Diff line number Diff line change
Expand Up @@ -3,6 +3,7 @@ package tcproute
import (
"net/http"
"path/filepath"
"time"

testmatchers "github.com/solo-io/gloo/test/gomega/matchers"

Expand All @@ -20,6 +21,8 @@ var (
multiTcpRouteManifest = filepath.Join(util.MustGetThisDir(), "testdata", "multi-tcproute.yaml")
singleListenerGatewayAndClientManifest = filepath.Join(util.MustGetThisDir(), "testdata", "single-listener-gateway-and-client.yaml")
singleTcpRouteManifest = filepath.Join(util.MustGetThisDir(), "testdata", "single-tcproute.yaml")
// Test assertion timeout
timeout = 30 * time.Second

// Proxy resource to be translated
glooProxyObjectMeta = metav1.ObjectMeta{
Expand Down
89 changes: 89 additions & 0 deletions test/kubernetes/testutils/assertions/status.go
Original file line number Diff line number Diff line change
Expand Up @@ -2,6 +2,7 @@ package assertions

import (
"context"
"fmt"
"time"

"github.com/onsi/ginkgo/v2"
Expand All @@ -15,6 +16,7 @@ import (
metav1 "k8s.io/apimachinery/pkg/apis/meta/v1"
"k8s.io/apimachinery/pkg/types"
gwv1 "sigs.k8s.io/gateway-api/apis/v1"
gwv1a2 "sigs.k8s.io/gateway-api/apis/v1alpha2"
)

// Checks GetNamespacedStatuses status for gloo installation namespace
Expand Down Expand Up @@ -162,3 +164,90 @@ func (p *Provider) EventuallyHTTPRouteStatusContainsReason(
g.Expect(route.Status.RouteStatus).To(gomega.HaveValue(matcher))
}, currentTimeout, pollingInterval).Should(gomega.Succeed())
}

// EventuallyGatewayCondition checks the provided Gateway condition is set to expect.
func (p *Provider) EventuallyGatewayCondition(
ctx context.Context,
gatewayName string,
gatewayNamespace string,
cond gwv1.GatewayConditionType,
expect metav1.ConditionStatus,
timeout ...time.Duration,
) {
ginkgo.GinkgoHelper()
currentTimeout, pollingInterval := helper.GetTimeouts(timeout...)
p.Gomega.Eventually(func(g gomega.Gomega) {
gateway := &gwv1.Gateway{}
err := p.clusterContext.Client.Get(ctx, types.NamespacedName{Name: gatewayName, Namespace: gatewayNamespace}, gateway)
g.Expect(err).NotTo(gomega.HaveOccurred(), "failed to get Gateway")

condition := getConditionByType(gateway.Status.Conditions, string(cond))
g.Expect(condition).NotTo(gomega.BeNil(), fmt.Sprintf("%v condition not found", cond))
g.Expect(condition.Status).To(gomega.Equal(expect), fmt.Sprintf("%v condition is not %v", cond, expect))
}, currentTimeout, pollingInterval).Should(gomega.Succeed())
}

// EventuallyGatewayListenerAttachedRoutes checks the provided Gateway contains the expected attached routes for the listener.
func (p *Provider) EventuallyGatewayListenerAttachedRoutes(
ctx context.Context,
gatewayName string,
gatewayNamespace string,
listener gwv1.SectionName,
routes int32,
timeout ...time.Duration,
) {
ginkgo.GinkgoHelper()
currentTimeout, pollingInterval := helper.GetTimeouts(timeout...)
p.Gomega.Eventually(func(g gomega.Gomega) {
gateway := &gwv1.Gateway{}
err := p.clusterContext.Client.Get(ctx, types.NamespacedName{Name: gatewayName, Namespace: gatewayNamespace}, gateway)
g.Expect(err).NotTo(gomega.HaveOccurred(), "failed to get Gateway")

found := false
for _, l := range gateway.Status.Listeners {
if l.Name == listener {
found = true
g.Expect(l.AttachedRoutes).To(gomega.Equal(routes), fmt.Sprintf("%v listener does not contain %d attached routes", l, routes))
}
}
g.Expect(found).To(gomega.BeTrue(), fmt.Sprintf("%v listener not found", listener))
}, currentTimeout, pollingInterval).Should(gomega.Succeed())
}

// EventuallyTCPRouteCondition checks that provided TCPRoute condition is set to expect.
func (p *Provider) EventuallyTCPRouteCondition(
ctx context.Context,
routeName string,
routeNamespace string,
cond gwv1.RouteConditionType,
expect metav1.ConditionStatus,
timeout ...time.Duration,
) {
ginkgo.GinkgoHelper()
currentTimeout, pollingInterval := helper.GetTimeouts(timeout...)
p.Gomega.Eventually(func(g gomega.Gomega) {
route := &gwv1a2.TCPRoute{}
err := p.clusterContext.Client.Get(ctx, types.NamespacedName{Name: routeName, Namespace: routeNamespace}, route)
g.Expect(err).NotTo(gomega.HaveOccurred(), "failed to get TCPRoute")

var conditionFound bool
for _, parentStatus := range route.Status.Parents {
condition := getConditionByType(parentStatus.Conditions, string(cond))
if condition != nil && condition.Status == expect {
conditionFound = true
break
}
}
g.Expect(conditionFound).To(gomega.BeTrue(), fmt.Sprintf("%v condition is not %v for any parent", cond, expect))
}, currentTimeout, pollingInterval).Should(gomega.Succeed())
}

// Helper function to retrieve a condition by type from a list of conditions.
func getConditionByType(conditions []metav1.Condition, conditionType string) *metav1.Condition {
for _, condition := range conditions {
if condition.Type == conditionType {
return &condition
}
}
return nil
}
2 changes: 1 addition & 1 deletion test/kubernetes/testutils/cluster/kind.go
Original file line number Diff line number Diff line change
Expand Up @@ -19,7 +19,7 @@ import (

// MustKindContext returns the Context for a KinD cluster with the given name
func MustKindContext(clusterName string) *Context {
return MustKindContextWithScheme(clusterName, schemes.DefaultScheme())
return MustKindContextWithScheme(clusterName, schemes.TestingScheme())
}

// MustKindContextWithScheme returns the Context for a KinD cluster with the given name and scheme
Expand Down

0 comments on commit 483799e

Please sign in to comment.