Skip to content

Commit

Permalink
add functionals tests for ipv6
Browse files Browse the repository at this point in the history
  • Loading branch information
salonichf5 committed Jul 15, 2024
1 parent 506c197 commit 5696041
Show file tree
Hide file tree
Showing 7 changed files with 409 additions and 1 deletion.
2 changes: 1 addition & 1 deletion tests/Makefile
Original file line number Diff line number Diff line change
Expand Up @@ -109,7 +109,7 @@ stop-longevity-test: nfr-test ## Stop the longevity test and collects results

.PHONY: .vm-nfr-test
.vm-nfr-test: ## Runs the NFR tests on the GCP VM (called by `nfr-test`)
go run github.com/onsi/ginkgo/v2/ginkgo --randomize-all --randomize-suites --keep-going --fail-on-pending --trace -r -v \
go run github.com/onsi/ginkgo/v2/ginkgo --randomize-all --randomize-suites --keep-going --fail-on-pending --trace -r -v -vv \
--label-filter "nfr" $(GINKGO_FLAGS) ./suite -- --gateway-api-version=$(GW_API_VERSION) \
--gateway-api-prev-version=$(GW_API_PREV_VERSION) --image-tag=$(TAG) --version-under-test=$(NGF_VERSION) \
--plus-enabled=$(PLUS_ENABLED) --ngf-image-repo=$(PREFIX) --nginx-image-repo=$(NGINX_PREFIX) --nginx-plus-image-repo=$(NGINX_PLUS_PREFIX) \
Expand Down
13 changes: 13 additions & 0 deletions tests/framework/resourcemanager.go
Original file line number Diff line number Diff line change
Expand Up @@ -397,6 +397,19 @@ func (rm *ResourceManager) waitForHTTPRoutesToBeReady(ctx context.Context, names
)
}

func (rm *ResourceManager) GetHTTPRoute(namespace string, httpRouteName string) (*v1.HTTPRoute, error) {
ctx, cancel := context.WithTimeout(context.Background(), rm.TimeoutConfig.GetTimeout)
defer cancel()

route := &v1.HTTPRoute{}
key := types.NamespacedName{Name: httpRouteName, Namespace: namespace}
if err := rm.K8sClient.Get(ctx, key, route); err != nil {
return nil, fmt.Errorf("error getting HTTPRoute %s: %w", httpRouteName, err)
}

return route, nil
}

func (rm *ResourceManager) waitForGRPCRoutesToBeReady(ctx context.Context, namespace string) error {
// First, check if grpcroute even exists for v1. If not, ignore.
var routeList v1.GRPCRouteList
Expand Down
275 changes: 275 additions & 0 deletions tests/suite/ip_family_test.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,275 @@
package suite

import (
"context"
"errors"
"fmt"
"net"
"net/http"
"strings"
"time"

. "github.com/onsi/ginkgo/v2"
. "github.com/onsi/gomega"
core "k8s.io/api/core/v1"
metav1 "k8s.io/apimachinery/pkg/apis/meta/v1"
"k8s.io/apimachinery/pkg/types"
"k8s.io/apimachinery/pkg/util/wait"
"sigs.k8s.io/controller-runtime/pkg/client"
v1 "sigs.k8s.io/gateway-api/apis/v1"

"github.com/nginxinc/nginx-gateway-fabric/tests/framework"
)

var _ = Describe("IPFamily", Ordered, Label("functional", "ip-family"), func() {
var (
files = []string{
"ip-family/gateway.yaml",
"ip-family/cafe.yaml",
"ip-family/cafe-routes.yaml",
}

namespace = "ip-family"
ns core.Namespace

baseHTTPURL = "http://cafe.example.com"
teaURL = baseHTTPURL + "/tea"
coffeeURL = baseHTTPURL + "/coffee"
)

BeforeEach(func() {
ns = core.Namespace{
ObjectMeta: metav1.ObjectMeta{
Name: namespace,
},
}

Expect(resourceManager.Apply([]client.Object{&ns})).To(Succeed())
})

AfterEach(func() {
Expect(resourceManager.DeleteFromFiles(files, ns.Name)).To(Succeed())
Expect(resourceManager.DeleteNamespace(ns.Name)).To(Succeed())

teardown(releaseName)
})

Describe("NGF is configured with Dual, IPv4 and IPv6 IPFamily", func() {
// It("Successfully configures coffee with IPv4 and tea with IPv6, NGF installed with Dual IPFamily", func() {
// setup(
// getDefaultSetupCfg(),
// "--set", "nginx.config.ipFamily=dual",
// )
// Expect(resourceManager.ApplyFromFiles(files, ns.Name)).To(Succeed())
// Expect(resourceManager.WaitForAppsToBeReady(ns.Name)).To(Succeed())

// if portFwdPort != 0 {
// coffeeURL = fmt.Sprintf("%s:%d/coffee", baseHTTPURL, portFwdPort)
// teaURL = fmt.Sprintf("%s:%d/tea", baseHTTPURL, portFwdPort)
// }

// Eventually(
// func() error {
// return checkWorkingTraffic(teaURL, address, true)
// }).
// WithTimeout(timeoutConfig.RequestTimeout).
// WithPolling(2 * time.Second).
// Should(Succeed())

// Eventually(
// func() error {
// return checkWorkingTraffic(coffeeURL, address, false)
// }).
// WithTimeout(timeoutConfig.RequestTimeout).
// WithPolling(2 * time.Second).
// Should(Succeed())

// teardown(releaseName)
// })
It("Successfully configures coffee route(IPv4), tea route(IPv6) has accepted condition set to InvalidIPFamily", func() {
setup(
getDefaultSetupCfg(),
"--set", "nginx.config.ipFamily=ipv4",
)
Expect(resourceManager.ApplyFromFiles(files, ns.Name)).To(Succeed())
Expect(resourceManager.WaitForAppsToBeReady(ns.Name)).To(Succeed())

if portFwdPort != 0 {
coffeeURL = fmt.Sprintf("%s:%d/coffee", baseHTTPURL, portFwdPort)
teaURL = fmt.Sprintf("%s:%d/tea", baseHTTPURL, portFwdPort)
}

Eventually(
func() error {
return verifyRequestFailureAndRouteConditionToBeInvalidIPFamily(
teaURL,
address,
"tea",
ns.Name,
"service configured with IPv6 family but NginxProxy is configured with IPv4")
}).
WithTimeout(timeoutConfig.RequestTimeout).
WithPolling(2 * time.Second).
Should(Succeed())

Eventually(
func() error {
return checkWorkingTraffic(coffeeURL, address, false)
}).
WithTimeout(timeoutConfig.RequestTimeout).
WithPolling(2 * time.Second).
Should(Succeed())
})
It("Successfully configures tea route(IPv6), coffee route(IPv4) has accepted condition set to InvalidIPFamily", func() {
setup(
getDefaultSetupCfg(),
"--set", "nginx.config.ipFamily=ipv6",
)
Expect(resourceManager.ApplyFromFiles(files, ns.Name)).To(Succeed())
Expect(resourceManager.WaitForAppsToBeReady(ns.Name)).To(Succeed())

if portFwdPort != 0 {
coffeeURL = fmt.Sprintf("%s:%d/coffee", baseHTTPURL, portFwdPort)
teaURL = fmt.Sprintf("%s:%d/tea", baseHTTPURL, portFwdPort)
}

Eventually(
func() error {
return verifyRequestFailureAndRouteConditionToBeInvalidIPFamily(
coffeeURL,
address,
"coffee",
ns.Name,
"service configured with IPv4 family but NginxProxy is configured with IPv6")
}).
WithTimeout(timeoutConfig.RequestTimeout).
Should(Succeed())

Eventually(
func() error {
return checkWorkingTraffic(teaURL, address, false)
}).
WithTimeout(timeoutConfig.RequestTimeout).
WithPolling(2 * time.Second).
Should(Succeed())
})
})
})

func verifyRequestFailureAndRouteConditionToBeInvalidIPFamily(appURL, address, routeName, namespace, expectedErrMessage string) error {
err := expectRequestFailureInternalError(appURL, address)
if err != nil {
return err
}

err = verifyHTTPRouteConditionToBeInvalidIPFamily(routeName, namespace, expectedErrMessage)
if err != nil {
return err
}

return nil
}

func expectRequestFailureInternalError(appURL, address string) error {
status, body, err := framework.Get(appURL, address, timeoutConfig.RequestTimeout)
if status != http.StatusInternalServerError {
return errors.New("expected http status to be 500")
}

if body != "" && !strings.Contains(body, "500 Internal Server Error") {
return fmt.Errorf("expected response body to have Internal Server Error, instead received: %s", body)
}
if err != nil {
return fmt.Errorf("error while sending request: %s", err.Error())
}

return nil
}

func checkWorkingTraffic(url, address string, ipv6Expected bool) error {
status, body, err := framework.Get(url, address, timeoutConfig.RequestTimeout)
if status != http.StatusOK {
return errors.New("http response status is not 200")
}

if err != nil {
return err
}

err = verifyIPType(body, ipv6Expected)
return err
}

func verifyIPType(rspBody string, ipv6Expected bool) error {
lines := strings.Split(rspBody, "\n")
dataMap := make(map[string]string)

for _, line := range lines {
parts := strings.Split(line, ": ")
if len(parts) == 2 {
dataMap[parts[0]] = parts[1]
}
}

ip := dataMap["Server address"]
if ip == "" {
return errors.New("server address not found in response body")
}

if ipv6Expected && isIPv4(ip) {
return errors.New("expected IPv6 address, got IPv4")
}
if !ipv6Expected && !isIPv4(ip) {
return errors.New("expected IPv4 address, got IPv6")
}
return nil
}

func isIPv4(str string) bool {
ip := net.ParseIP(str)
if ip != nil && ip.To4() == nil {
return false
}

return true
}

func verifyHTTPRouteConditionToBeInvalidIPFamily(httpRouteName, namespace, expectedErrMessage string) error {
ctx, cancel := context.WithTimeout(context.Background(), timeoutConfig.GetStatusTimeout)
defer cancel()
route := &v1.HTTPRoute{}

return wait.PollUntilContextCancel(
ctx,
200*time.Millisecond,
true,
func(ctx context.Context) (bool, error) {
httpRouteNsName := types.NamespacedName{Name: httpRouteName, Namespace: namespace}
if err := k8sClient.Get(ctx, httpRouteNsName, route); err != nil {
return false, fmt.Errorf("error getting HTTPRoute %s: %w", httpRouteName, err)
}

if len(route.Status.Parents) == 0 {
GinkgoWriter.Printf("HTTPRoute %q does not status yet \n", httpRouteName)
return false, nil
}

if len(route.Status.Parents) >= 1 && len(route.Status.Parents[0].Conditions) == 0 {
GinkgoWriter.Printf("HTTPRoute %q does not have conditions yet \n", httpRouteName)
return false, nil
}

for _, parent := range route.Status.Parents {
for _, condition := range parent.Conditions {
if condition.Type == string(v1.RouteConditionResolvedRefs) &&
condition.Status == metav1.ConditionFalse &&
condition.Message == expectedErrMessage {
return true, nil
}
}
}

return false, nil
},
)
}
37 changes: 37 additions & 0 deletions tests/suite/manifests/ip-family/cafe-routes.yaml
Original file line number Diff line number Diff line change
@@ -0,0 +1,37 @@
apiVersion: gateway.networking.k8s.io/v1
kind: HTTPRoute
metadata:
name: coffee
spec:
parentRefs:
- name: gateway
sectionName: http
hostnames:
- "cafe.example.com"
rules:
- matches:
- path:
type: PathPrefix
value: /coffee
backendRefs:
- name: coffee
port: 80
---
apiVersion: gateway.networking.k8s.io/v1
kind: HTTPRoute
metadata:
name: tea
spec:
parentRefs:
- name: gateway
sectionName: http
hostnames:
- "cafe.example.com"
rules:
- matches:
- path:
type: Exact
value: /tea
backendRefs:
- name: tea
port: 80
Loading

0 comments on commit 5696041

Please sign in to comment.