Skip to content

Commit

Permalink
Merge pull request #980 from cadmuxe/asm-e2e
Browse files Browse the repository at this point in the history
Add e2e tests for NEG asm mode.
  • Loading branch information
k8s-ci-robot authored Jan 16, 2020
2 parents 66e133a + 66117b7 commit f2c8f40
Show file tree
Hide file tree
Showing 7 changed files with 470 additions and 13 deletions.
233 changes: 233 additions & 0 deletions cmd/e2e-test/asm_test.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,233 @@
package main

import (
"context"
"fmt"
"strconv"
"strings"
"testing"
"time"

istioV1alpha3 "istio.io/api/networking/v1alpha3"
apiv1 "k8s.io/api/core/v1"
"k8s.io/apimachinery/pkg/util/wait"
"k8s.io/ingress-gce/pkg/e2e"
"k8s.io/klog"
)

const (
asmConfigNamespace = "kube-system"
asmConfigName = "ingress-controller-asm-cm-config"
negControllerRestartTimeout = 5 * time.Minute
)

// TestASMConfig tests the ASM enable/disable, it can't run parallel with other tests.
func TestASMConfig(t *testing.T) {
Framework.RunWithSandbox("TestASMConfig", t, func(t *testing.T, s *e2e.Sandbox) {
for _, tc := range []struct {
desc string
configMap map[string]string
wantConfigMapEvents []string
}{
{
desc: "Invalid ConfigMap value equals to disable",
configMap: map[string]string{"enable-asm": "INVALID"},
wantConfigMapEvents: []string{"The map provided a unvalid value for field: enable-asm, value: INVALID"},
},
{
desc: "Invalid ConfigMap filed equals to disable",
configMap: map[string]string{"enable-unknow-feild": "INVALID1"},
wantConfigMapEvents: []string{"The map contains a unknown key-value pair: enable-unknow-feild:INVALID1"},
},
{
desc: "Set enable-asm to true should restart the controller",
configMap: map[string]string{"enable-asm": "true"},
wantConfigMapEvents: []string{"ConfigMapConfigController: Get a update on the ConfigMapConfig, Restarting Ingress controller"},
},
// TODO(koonwah): The below case is not fully tested, should update the neg controller to include a enable-asm event to the target CM.
{
desc: "Invalid ConfigMap value equals to disable",
configMap: map[string]string{"enable-asm": "INVALID2"},
wantConfigMapEvents: []string{"The map provided a unvalid value for field: enable-asm, value: INVALID2",
"ConfigMapConfigController: Get a update on the ConfigMapConfig, Restarting Ingress controller"},
},
} {
t.Logf("Running test case: %s", tc.desc)
if err := e2e.EnsureConfigMap(s, asmConfigNamespace, asmConfigName, tc.configMap); err != nil {
t.Errorf("Failed to ensure ConfigMap, error: %s", err)
}
if err := e2e.WaitConfigMapEvents(s, asmConfigNamespace, asmConfigName, tc.wantConfigMapEvents, negControllerRestartTimeout); err != nil {
t.Fatalf("Failed to get events: %v; Error %e", strings.Join(tc.wantConfigMapEvents, ";"), err)
}
}
})
}

// TestASMServiceAndDestinationRule tests the service/destinationrule, it can't run parallel with other tests.
func TestASMServiceAndDestinationRule(t *testing.T) {
_ = istioV1alpha3.DestinationRule{}
_ = apiv1.ComponentStatus{}

// This test case will need two namespaces, one will in asm-skip-namespaces.
Framework.RunWithSandbox("TestASMServiceAndDestinationRule", t, func(t *testing.T, sSkip *e2e.Sandbox) {
Framework.RunWithSandbox("TestASMServiceAndDestinationRule", t, func(t *testing.T, s *e2e.Sandbox) {
// Enable ASM mode
ctx := context.Background()

asmConfig := map[string]string{"enable-asm": "true",
"asm-skip-namespaces": fmt.Sprintf("kube-system,istio-system,%s", sSkip.Namespace)}
if err := e2e.EnsureConfigMap(s, asmConfigNamespace, asmConfigName,
asmConfig); err != nil {
t.Error(err)
}

var porterPort int32
porterPort = 80
svcName := "service"
svcSkipName := "service-skip"

// Create deployments used by the Service and DestinationRules.
// The deployment will contain two label pairs: {"app": "porter", "version": "*"}
// Different versions will be used as DestinationRule: subset
for _, deployment := range []struct {
deploymentName string
replics int32
version string
}{
{deploymentName: "deployment-v1", replics: 1, version: "v1"},
{deploymentName: "deployment-v2", replics: 2, version: "v2"},
{deploymentName: "deployment-v3", replics: 3, version: "v3"},
} {
if err := e2e.CreatePorterDeployment(s, deployment.deploymentName, deployment.replics, deployment.version); err != nil {
t.Errorf("Failed to create deployment, Error: %s", err)
}
}

// Create and validate DestinationRules level NEGs, NEG controller shouldn't create DestinationRules level NEGs for those services in the skip namespace.
for _, svc := range []struct {
desc string
svcName string
inSkipNamespace bool
}{
{desc: "NEG Controller should create NEGs for all ports for a service by default", svcName: svcName, inSkipNamespace: false},
{desc: "NEG Controller shouldn't create NEGs for all ports for a service if it's in a skip namespace", svcName: svcSkipName, inSkipNamespace: true},
} {
t.Logf("Running test case: %s", svc.desc)
sandbox := s
noPresentTest := false
if svc.inSkipNamespace {
sandbox = sSkip
noPresentTest = true
}
if err := e2e.CreatePorterService(sandbox, svc.svcName); err != nil {
t.Errorf("Failed to create service, Error: %s", err)
}

// Test the Service Annotations
negStatus, err := e2e.WaitForNegStatus(sandbox, svc.svcName, []string{strconv.Itoa(int(porterPort))}, noPresentTest)
if err != nil {
t.Errorf("Failed to wait for Service NEGAnnotation, error: %s", err)
}
if svc.inSkipNamespace {
if negStatus != nil {
t.Errorf("Service: %s/%s is in the ASM skip namespace, shoudln't have NEG Status. ASM Config: %v, NEGStatus got: %v",
sandbox.Namespace, svc.svcName, asmConfig, negStatus)
}
} else {
if negName, ok := negStatus.NetworkEndpointGroups[strconv.Itoa(int(porterPort))]; ok {
// No backend pod exists, so the NEG has 0 endpoint.
if err := e2e.WaitForNegs(ctx, Framework.Cloud, negName, negStatus.Zones, false, 0); err != nil {
t.Errorf("Failed to wait Negs, error: %s", err)
}
} else {
t.Fatalf("Service annotation doesn't contain the desired NEG status, want: %d, have: %v", porterPort, negStatus.NetworkEndpointGroups)
}
}
}

for _, tc := range []struct {
desc string
destinationRuleName string
subsetEndpointCountMap map[string]int
crossNamespace bool // If crossNamespace set, the DestinationRule will use full Host(Service) name to refer a service in a different namespace.
}{
{desc: "NEG controller should create NEGs for destinationrule", destinationRuleName: "porter-destinationrule", subsetEndpointCountMap: map[string]int{"v1": 1, "v2": 2, "v3": 3}, crossNamespace: false},
{desc: "NEG controller should update NEGs for destinationrule", destinationRuleName: "porter-destinationrule", subsetEndpointCountMap: map[string]int{"v1": 1, "v2": 2}, crossNamespace: false},
{desc: "NEG controller should create NEGs for cross namespace destinationrule", destinationRuleName: "porter-destinationrule-1", subsetEndpointCountMap: map[string]int{"v1": 1}, crossNamespace: true},
} {
t.Logf("Running test case: %s", tc.desc)
sandbox := s
drHost := svcName
// crossNamespace will test DestinationRules that refering a serive located in a different namespace
if tc.crossNamespace {
sandbox = sSkip
drHost = fmt.Sprintf("%s.%s.svc.cluster.local", svcName, s.Namespace)
}

versions := []string{}
for k := range tc.subsetEndpointCountMap {
versions = append(versions, k)
}
if err := e2e.EnsurePorterDestinationRule(sandbox, tc.destinationRuleName, drHost, versions); err != nil {
klog.Errorf("Failed to create destinationRule, error: %s", err)
}

// One DestinationRule should have count(NEGs) = count(subset)* count(port)
dsNEGStatus, err := e2e.WaitDestinationRuleAnnotation(sandbox, sandbox.Namespace, tc.destinationRuleName, len(tc.subsetEndpointCountMap)*1, 5*time.Minute)
if err != nil {
klog.Errorf("Failed to validate the NEG count. Error: %s", err)
}

zones := dsNEGStatus.Zones
for subsetVersion, endpointCount := range tc.subsetEndpointCountMap {
negNames, ok := dsNEGStatus.NetworkEndpointGroups[subsetVersion]
if !ok {
t.Fatalf("DestinationRule annotation doesn't contain the desired NEG status, want: %s, have: %v", subsetVersion, dsNEGStatus.NetworkEndpointGroups)
}
negName, ok := negNames[strconv.Itoa(int(porterPort))]
if !ok {
t.Fatalf("DestinationRule annotation doesn't contain the desired NEG status, want: %d, have: %v", porterPort, negNames)
}
if err := e2e.WaitForNegs(ctx, Framework.Cloud, negName, zones, false, endpointCount); err != nil {
t.Errorf("Failed to wait Negs, error: %s", err)
}
}

}

})
})
}

func TestNoIstioASM(t *testing.T) {

Framework.RunWithSandbox("TestASMConfigOnNoIstioCluster", t, func(t *testing.T, s *e2e.Sandbox) {

cm := map[string]string{"enable-asm": "true"}
wantConfigMapEvents := []string{"ConfigMapConfigController: Get a update on the ConfigMapConfig, Restarting Ingress controller",
"Cannot find DestinationRule CRD, disabling ASM Mode, please check Istio setup."}

if err := e2e.EnsureConfigMap(s, asmConfigNamespace, asmConfigName, cm); err != nil {
t.Error(err)
}
defer e2e.DeleteConfigMap(s, asmConfigNamespace, asmConfigName)
if err := e2e.WaitConfigMapEvents(s, asmConfigNamespace, asmConfigName, wantConfigMapEvents, negControllerRestartTimeout); err != nil {
t.Fatalf("Failed to get events: %v; Error %e", wantConfigMapEvents, err)
}

if err := wait.Poll(5*time.Second, 1*time.Minute, func() (bool, error) {
cmData, err := e2e.GetConfigMap(s, asmConfigNamespace, asmConfigName)
if err != nil {
return false, err
}
if val, ok := cmData["enable-asm"]; ok && val == "false" {
return true, nil
}
return false, fmt.Errorf("ConfigMap: %s/%s, nable-asm is not false. Value: %v", asmConfigNamespace, asmConfigName, cmData)

}); err != nil {
t.Fatalf("Failed to validate enable-asm = false. Error: %s", err)
}

})
}
3 changes: 3 additions & 0 deletions cmd/e2e-test/main_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -49,6 +49,7 @@ var (
handleSIGINT bool
gceEndpointOverride string
createILBSubnet bool
enableIstio bool
}

Framework *e2e.Framework
Expand All @@ -71,6 +72,7 @@ func init() {
flag.BoolVar(&flags.handleSIGINT, "handleSIGINT", true, "catch SIGINT to perform clean")
flag.StringVar(&flags.gceEndpointOverride, "gce-endpoint-override", "", "If set, talks to a different GCE API Endpoint. By default it talks to https://www.googleapis.com/compute/v1/")
flag.BoolVar(&flags.createILBSubnet, "createILBSubnet", false, "If set, creates a proxy subnet for the L7 ILB")
flag.BoolVar(&flags.enableIstio, "enable-istio", false, "set to true if Istio is enabled.")
}

// TestMain is the entrypoint for the end-to-end test suite. This is where
Expand Down Expand Up @@ -126,6 +128,7 @@ func TestMain(m *testing.M) {
DestroySandboxes: flags.destroySandboxes,
GceEndpointOverride: flags.gceEndpointOverride,
CreateILBSubnet: flags.createILBSubnet,
EnableIstio: flags.enableIstio,
})
if flags.handleSIGINT {
Framework.CatchSIGINT()
Expand Down
4 changes: 2 additions & 2 deletions cmd/e2e-test/neg_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -21,7 +21,7 @@ import (
"testing"

apps "k8s.io/api/apps/v1"
"k8s.io/api/core/v1"
v1 "k8s.io/api/core/v1"
"k8s.io/api/networking/v1beta1"
"k8s.io/apimachinery/pkg/util/intstr"
"k8s.io/apimachinery/pkg/util/sets"
Expand Down Expand Up @@ -338,7 +338,7 @@ func TestNEGSyncEndpoints(t *testing.T) {
}

// validate neg status
negStatus, err := e2e.WaitForNegStatus(s, svcName, tc.expectServicePort.List())
negStatus, err := e2e.WaitForNegStatus(s, svcName, tc.expectServicePort.List(), false)
if err != nil {
t.Fatalf("error waiting for NEG status to update: %v", err)
}
Expand Down
7 changes: 4 additions & 3 deletions cmd/e2e-test/upgrade/neg.go
Original file line number Diff line number Diff line change
Expand Up @@ -18,10 +18,11 @@ package upgrade

import (
"context"
"k8s.io/api/core/v1"
"testing"

v1 "k8s.io/api/core/v1"
"k8s.io/ingress-gce/pkg/annotations"
"k8s.io/ingress-gce/pkg/e2e"
"testing"
)

var (
Expand Down Expand Up @@ -105,7 +106,7 @@ func (sn *StandaloneNeg) scaleAndValidate(replicas int32) {
// validate check if the NEG status annotation and the corresponding NEGs are correctly configured.
func (sn *StandaloneNeg) validate(replicas int32) {
// validate neg status
negStatus, err := e2e.WaitForNegStatus(sn.s, svcName, expectServicePort)
negStatus, err := e2e.WaitForNegStatus(sn.s, svcName, expectServicePort, false)
if err != nil {
sn.t.Fatalf("error waiting for NEG status to update: %v", err)
}
Expand Down
Loading

0 comments on commit f2c8f40

Please sign in to comment.