From 5d5e174d7b1d7a7badba15b8fa1626fe51f3ee36 Mon Sep 17 00:00:00 2001 From: David Cheung Date: Tue, 8 Aug 2023 17:53:42 +0000 Subject: [PATCH] Create test for ingress for custom default backend. --- test/ingress_test.go | 115 ++++++++++++++++++++++++++++++++++++++++ test/utils.go | 14 ++++- test/utils/address.go | 56 +++++++++++++++++++ test/utils/resource.go | 2 +- test/utils/sslPolicy.go | 56 +++++++++++++++++++ 5 files changed, 241 insertions(+), 2 deletions(-) create mode 100644 test/ingress_test.go create mode 100644 test/utils/address.go create mode 100644 test/utils/sslPolicy.go diff --git a/test/ingress_test.go b/test/ingress_test.go new file mode 100644 index 00000000..faa7dadd --- /dev/null +++ b/test/ingress_test.go @@ -0,0 +1,115 @@ +/* +Copyright 2023 The Kubernetes Authors. + +Licensed 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 test + +import ( + "context" + "os" + "path" + "testing" + + "github.com/GoogleCloudPlatform/gke-networking-recipes/test/utils" + "k8s.io/apimachinery/pkg/runtime/schema" + "k8s.io/klog/v2" +) + +const ( + testInstanceName = "allow-ssh-rcp-instance" + proxyOnlyFirewallName = "allow-proxy-connection-rcp" + proxyOnlySubnetName = "proxy-only-subnet-rcp" + allowSSHFirewallName = "fw-allow-ssh-rcp" +) + +func TestIngressCustomDefaultBackendSingleCluster(t *testing.T) { + klog.Info("Running TestIngressCustomDefaultBackendSingleCluster.") + + testNamespace := "ing-custom-default-be" + if err := utils.SetupTestNamespace(testNamespace); err != nil { + t.Fatalf("Fail to set up test namespace %s: %v", testNamespace, err) + } + + ctx := context.Background() + client := Framework.Client + goPath := os.Getenv("GOPATH") + yamlPath := "ingress/single-cluster/ingress-custom-default-backend/ingress-custom-default-backend.yaml" + + // Set up proxy-only subnet and firewall for internal Ingress. + region := flags.zone[:len(flags.zone)-2] // Drop the zone + if err := utils.SetupProxyOnlySubnet(proxyOnlySubnetName, flags.testNetworkName, region); err != nil { + t.Fatalf("Failed to set up proxy-only subnet: %v", err) + } + if err := utils.SetupAllowProxyConnectionFirewall(proxyOnlyFirewallName, flags.testNetworkName); err != nil { + t.Fatalf("Failed to set up proxy-only-connection firewall: %v", err) + } + + // Create k8s resources from recipe yaml. + objects, err := utils.ParseK8sYaml(ctx, path.Join(goPath, flags.pkgDir, yamlPath), testNamespace) + if err != nil { + t.Fatalf("Failed to parse yaml: %v", err) + } + objectKeys, err := utils.CreateK8sResources(ctx, objects, client, testNamespace) + if err != nil { + t.Fatalf("Failed to create resources from yaml: %v", err) + } + t.Cleanup(func() { + t.Log("Start cleanup.") + err = utils.DeleteK8sResources(ctx, objects, client) + if err != nil { + t.Fatalf("Error during clean up: %v", err) + } + }) + + // Fetch ingress and validate its IP is ready. + ingressGVK := schema.GroupVersionKind{Group: "networking.k8s.io", Version: "v1", Kind: "Ingress"} + ingressObjectKeys := objectKeys[ingressGVK.String()] + if len(ingressObjectKeys) != 1 { + t.Fatalf("Expect 1 ingress, got %d", len(ingressObjectKeys)) + } + ingressIP, err := utils.WaitForIngress(ctx, client, ingressObjectKeys[0]) + if err != nil { + t.Fatalf("Fail to get IP for ingress %s/%s: %v", ingressObjectKeys[0].Namespace, ingressObjectKeys[0].Name, err) + } + klog.Infof("Ingress IP: %s", ingressIP) + + // Create a test instance that in the same znoe and network as the cluster. + if err := utils.SetupTestInstance(testInstanceName, flags.zone, flags.testNetworkName, flags.testSubnetName); err != nil { + t.Fatalf("Failed to set up test instance: %v", err) + } + if err := utils.SetupAllowSSHFirewall(allowSSHFirewallName, flags.testNetworkName); err != nil { + t.Fatalf("Failed to set up allow-ssh firewall: %v", err) + } + + // Send traffic to /foo. Should receive response from foo containers. + addr := ingressIP + "/foo" + response, err := sendTrafficToIngress(addr, testInstanceName, flags.zone, nil) + if err != nil { + t.Fatalf("Fail to send traffic to address %s: %v", addr, err) + } + if err := validateResponse(response, "foo"); err != nil { + t.Fatalf("Fail to validate response: %v", err) + } + + // Send traffic to any non-defined path. Should receive response from default-be containers. + addr = ingressIP + "/bar" + response, err = sendTrafficToIngress(addr, testInstanceName, flags.zone, nil) + if err != nil { + t.Fatalf("Fail to send traffic to address %s: %v", addr, err) + } + if err := validateResponse(response, "default-be"); err != nil { + t.Fatalf("Fail to validate response: %v", err) + } +} diff --git a/test/utils.go b/test/utils.go index 1a2a7c3b..7a7a302a 100644 --- a/test/utils.go +++ b/test/utils.go @@ -23,6 +23,7 @@ import ( "math/rand" "os" "os/exec" + "strings" "time" "github.com/GoogleCloudPlatform/k8s-cloud-provider/pkg/cloud" @@ -61,7 +62,7 @@ func getBoskosProject(resourceType string) *common.Resource { }, ) if err != nil { - klog.Fatalf("timed out trying to acquire boskos project") + klog.Fatalf("timed out trying to acquire boskos project: %v", err) } return project } @@ -167,3 +168,14 @@ func sendTrafficToIngress(address, instanceName, zone string, curlArgs []string) } return jsonMap, nil } + +func validateResponse(response map[string]string, expectPodName string) error { + gotPodName, ok := response["pod_name"] + if !ok { + return fmt.Errorf("cannot extract pod name from response") + } + if !strings.Contains(gotPodName, expectPodName) { + return fmt.Errorf("Expect response from %s, got %s", expectPodName, gotPodName) + } + return nil +} diff --git a/test/utils/address.go b/test/utils/address.go new file mode 100644 index 00000000..f3146389 --- /dev/null +++ b/test/utils/address.go @@ -0,0 +1,56 @@ +/* +Copyright 2023 The Kubernetes Authors. + +Licensed 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 + + https://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 utils + +import ( + "fmt" + "os/exec" + + "k8s.io/klog/v2" +) + +func SetupStaticIP(staticIPName string) error { + klog.Infof("Setting up static IP %s.", staticIPName) + params := []string{ + "compute", + "addresses", + "describe", + "--global", + staticIPName, + } + if _, err := exec.Command("gcloud", params...).CombinedOutput(); err != nil { + klog.Infof("Static IP %s does not exist, creating.", staticIPName) + return createStaticIP(staticIPName) + } + klog.Infof("Use existing static IP %s.", staticIPName) + return nil +} + +func createStaticIP(staticIPName string) error { + klog.Infof("Creating static IP %s.", staticIPName) + params := []string{ + "compute", + "addresses", + "create", + "--global", + staticIPName, + } + if out, err := exec.Command("gcloud", params...).CombinedOutput(); err != nil { + return fmt.Errorf("failed creating static IP %s: %s, err: %v", staticIPName, out, err) + } + return nil +} diff --git a/test/utils/resource.go b/test/utils/resource.go index f8902160..dfc923a3 100644 --- a/test/utils/resource.go +++ b/test/utils/resource.go @@ -53,12 +53,12 @@ func DeleteK8sResources(ctx context.Context, objects []ctrlClient.Object, cli ct for _, obj := range objects { gvk := obj.GetObjectKind().GroupVersionKind().String() err := cli.Delete(ctx, obj, &ctrlClient.DeleteOptions{}) + klog.Infof("Deleting %s %s/%s", gvk, obj.GetNamespace(), obj.GetName()) if err != nil { klog.Infof("Failed to delete %s %s/%s: %v", gvk, obj.GetNamespace(), obj.GetName(), err) return err } klog.Infof("Deleting %s %s/%s", gvk, obj.GetNamespace(), obj.GetName()) - } return nil } diff --git a/test/utils/sslPolicy.go b/test/utils/sslPolicy.go new file mode 100644 index 00000000..732c59b0 --- /dev/null +++ b/test/utils/sslPolicy.go @@ -0,0 +1,56 @@ +/* +Copyright 2023 The Kubernetes Authors. + +Licensed 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 + + https://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 utils + +import ( + "fmt" + "os/exec" + + "k8s.io/klog/v2" +) + +func SetupSslPolicy(sslPolicyName string) error { + klog.Infof("Setting up SSL Policy %s.", sslPolicyName) + params := []string{ + "compute", + "ssl-policies", + "describe", + sslPolicyName, + } + if _, err := exec.Command("gcloud", params...).CombinedOutput(); err != nil { + klog.Infof("SSL Policy %s does not exist, creating.", sslPolicyName) + return createSslPolicy(sslPolicyName) + } + klog.Infof("Use existing SSL policy %s.", sslPolicyName) + return nil +} + +func createSslPolicy(sslPolicyName string) error { + klog.Infof("Creating SSL Policy %s.", sslPolicyName) + params := []string{ + "compute", + "ssl-policies", + "create", + sslPolicyName, + "--profile", "MODERN", + "--min-tls-version", "1.2", + } + if out, err := exec.Command("gcloud", params...).CombinedOutput(); err != nil { + return fmt.Errorf("failed creating SSL policy %s: %s, err: %v", sslPolicyName, out, err) + } + return nil +}