Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Add e2e test for configuring healthchecks via BackendConfig #1084

Merged
merged 1 commit into from
Apr 23, 2020
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
175 changes: 175 additions & 0 deletions cmd/e2e-test/healthcheck_test.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,175 @@
/*
Copyright 2020 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 main

import (
"context"
"testing"

"github.com/GoogleCloudPlatform/k8s-cloud-provider/pkg/cloud"
"k8s.io/apimachinery/pkg/util/intstr"
"k8s.io/ingress-gce/pkg/annotations"
backendconfig "k8s.io/ingress-gce/pkg/apis/backendconfig/v1"
"k8s.io/ingress-gce/pkg/e2e"
"k8s.io/ingress-gce/pkg/e2e/adapter"
"k8s.io/ingress-gce/pkg/fuzz"
"k8s.io/ingress-gce/pkg/fuzz/features"
)

func TestHealthCheck(t *testing.T) {
t.Parallel()
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Maybe in a followup PR but you may want to add a transition test.

Copy link
Member Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I will add follow on


pint64 := func(x int64) *int64 { return &x }
pstring := func(x string) *string { return &x }

for _, tc := range []struct {
desc string
beConfig *backendconfig.BackendConfig
want *backendconfig.HealthCheckConfig
}{
{
desc: "override healthcheck path",
beConfig: fuzz.NewBackendConfigBuilder("", "backendconfig-1").Build(),
want: &backendconfig.HealthCheckConfig{
CheckIntervalSec: pint64(7),
TimeoutSec: pint64(3),
HealthyThreshold: pint64(3),
UnhealthyThreshold: pint64(5),
RequestPath: pstring("/my-path"),
},
},
} {
tc := tc // Capture tc as we are running this in parallel.
Framework.RunWithSandbox(tc.desc, t, func(t *testing.T, s *e2e.Sandbox) {
t.Parallel()

ctx := context.Background()

backendConfigAnnotation := map[string]string{
annotations.BackendConfigKey: `{"default":"backendconfig-1"}`,
}
tc.beConfig.Spec.HealthCheck = tc.want

if _, err := Framework.BackendConfigClient.CloudV1().BackendConfigs(s.Namespace).Create(tc.beConfig); err != nil {
t.Fatalf("error creating BackendConfig: %v", err)
}
t.Logf("BackendConfig created (%s/%s) ", s.Namespace, tc.beConfig.Name)

_, err := e2e.CreateEchoService(s, "service-1", backendConfigAnnotation)
if err != nil {
t.Fatalf("error creating echo service: %v", err)
}
t.Logf("Echo service created (%s/%s)", s.Namespace, "service-1")

ing := fuzz.NewIngressBuilder(s.Namespace, "ingress-1", "").
DefaultBackend("service-1", intstr.FromInt(80)).
Build()
crud := adapter.IngressCRUD{C: Framework.Clientset}
if _, err := crud.Create(ing); err != nil {
t.Fatalf("error creating Ingress spec: %v", err)
}
t.Logf("Ingress created (%s/%s)", s.Namespace, ing.Name)

ing, err = e2e.WaitForIngress(s, ing, nil)
if err != nil {
t.Fatalf("error waiting for Ingress to stabilize: %v", err)
}
t.Logf("GCLB resources created (%s/%s)", s.Namespace, ing.Name)

vip := ing.Status.LoadBalancer.Ingress[0].IP
t.Logf("Ingress %s/%s VIP = %s", s.Namespace, ing.Name, vip)
params := &fuzz.GCLBForVIPParams{VIP: vip, Validators: fuzz.FeatureValidators(features.All)}
gclb, err := fuzz.GCLBForVIP(context.Background(), Framework.Cloud, params)
if err != nil {
t.Fatalf("Error getting GCP resources for LB with IP = %q: %v", vip, err)
}

verifyHealthCheck(t, gclb, tc.want)

// Wait for GCLB resources to be deleted.
if err := crud.Delete(s.Namespace, ing.Name); err != nil {
t.Errorf("Delete(%q) = %v, want nil", ing.Name, err)
}

deleteOptions := &fuzz.GCLBDeleteOptions{
SkipDefaultBackend: true,
}
t.Logf("Waiting for GCLB resources to be deleted (%s/%s)", s.Namespace, ing.Name)
if err := e2e.WaitForGCLBDeletion(ctx, Framework.Cloud, gclb, deleteOptions); err != nil {
t.Errorf("e2e.WaitForGCLBDeletion(...) = %v, want nil", err)
}
t.Logf("GCLB resources deleted (%s/%s)", s.Namespace, ing.Name)
})
}
}

func verifyHealthCheck(t *testing.T, gclb *fuzz.GCLB, want *backendconfig.HealthCheckConfig) {
// We assume there is a single service for now. The logic will have to be
// changed if there is more than one backend service.
for _, bs := range gclb.BackendService {
for _, hcURL := range bs.GA.HealthChecks {
rID, err := cloud.ParseResourceURL(hcURL)
if err != nil {
t.Fatalf("cloud.ParseResourceURL(%q) = _, %v; want _, nil", hcURL, err)
}
hc, ok := gclb.HealthCheck[*rID.Key]
if !ok {
t.Fatalf("HealthCheck %s not found in BackendService %+v", rID.Key, bs)
}

// Pull out the field that are in common among the different
// healthchecks per protocol.
common := struct {
port int64
requestPath string
}{}
switch {
case hc.GA.Http2HealthCheck != nil:
common.port = hc.GA.Http2HealthCheck.Port
common.requestPath = hc.GA.Http2HealthCheck.RequestPath
case hc.GA.HttpHealthCheck != nil:
common.port = hc.GA.HttpHealthCheck.Port
common.requestPath = hc.GA.HttpHealthCheck.RequestPath
case hc.GA.HttpsHealthCheck != nil:
common.port = hc.GA.HttpsHealthCheck.Port
common.requestPath = hc.GA.HttpsHealthCheck.RequestPath
}

if want.CheckIntervalSec != nil && hc.GA.CheckIntervalSec != *want.CheckIntervalSec {
t.Errorf("HealthCheck %v checkIntervalSec = %d, want %d", rID.Key, hc.GA.CheckIntervalSec, *want.CheckIntervalSec)
}
if want.TimeoutSec != nil && hc.GA.TimeoutSec != *want.TimeoutSec {
t.Errorf("HealthCheck %v timeoutSec = %d, want %d", rID.Key, hc.GA.TimeoutSec, *want.TimeoutSec)
}
if want.HealthyThreshold != nil && hc.GA.HealthyThreshold != *want.HealthyThreshold {
t.Errorf("HealthCheck %v healthyThreshold = %d, want %d", rID.Key, hc.GA.HealthyThreshold, *want.HealthyThreshold)
}
if want.UnhealthyThreshold != nil && hc.GA.UnhealthyThreshold != *want.UnhealthyThreshold {
t.Errorf("HealthCheck %v unhealthThreshold = %d, want %d", rID.Key, hc.GA.UnhealthyThreshold, *want.UnhealthyThreshold)
}
if want.Type != nil {
t.Errorf("HealthCheck %v type = %s, want %s", rID.Key, hc.GA.Type, *want.Type)
}
if want.Port != nil && common.port != *want.Port {
t.Errorf("HealthCheck %v port = %d, want %d", rID.Key, common.port, *want.Port)
}
if want.RequestPath != nil && common.requestPath != *want.RequestPath {
t.Errorf("HealthCheck %v requestPath = %q, want %q", rID.Key, common.requestPath, *want.RequestPath)
}
}
}
}
3 changes: 3 additions & 0 deletions cmd/e2e-test/main_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -24,6 +24,7 @@ import (
"testing"
"time"

"github.com/kr/pretty"
"k8s.io/client-go/rest"
"k8s.io/client-go/tools/clientcmd"
"k8s.io/ingress-gce/pkg/e2e"
Expand Down Expand Up @@ -78,6 +79,8 @@ func init() {
func TestMain(m *testing.M) {
flag.Parse()

fmt.Fprintf(os.Stderr, "Flags:\n%s\n", pretty.Sprint(flags))

if !flags.inCluster && !flags.run {
fmt.Fprintln(os.Stderr, "Set -run to run the tests.")
// Return 0 here so 'go test ./...' will succeed.
Expand Down
10 changes: 5 additions & 5 deletions pkg/e2e/adapter/beconfig.go
Original file line number Diff line number Diff line change
Expand Up @@ -22,7 +22,7 @@ import (
"errors"

metav1 "k8s.io/apimachinery/pkg/apis/meta/v1"
"k8s.io/ingress-gce/pkg/apis/backendconfig/v1"
v1 "k8s.io/ingress-gce/pkg/apis/backendconfig/v1"
"k8s.io/ingress-gce/pkg/apis/backendconfig/v1beta1"
client "k8s.io/ingress-gce/pkg/backendconfig/client/clientset/versioned"
"k8s.io/klog"
Expand All @@ -39,11 +39,11 @@ func (crud *BackendConfigCRUD) Get(ns, name string) (*v1.BackendConfig, error) {
if err != nil {
return nil, err
}
klog.V(2).Infof("Get BackendConfig %s/%s", ns, name)
klog.V(3).Infof("Get BackendConfig %s/%s", ns, name)
if isV1 {
return crud.C.CloudV1().BackendConfigs(ns).Get(name, metav1.GetOptions{})
}
klog.V(2).Info("Using BackendConfig V1beta1 API")
klog.V(4).Info("Using BackendConfig V1beta1 API")
bc, err := crud.C.CloudV1beta1().BackendConfigs(ns).Get(name, metav1.GetOptions{})
return toV1(bc), err
}
Expand Down Expand Up @@ -100,11 +100,11 @@ func (crud *BackendConfigCRUD) List(ns string) (*v1.BackendConfigList, error) {
if err != nil {
return nil, err
}
klog.V(2).Infof("List BackendConfigs in namespace(%s)", ns)
klog.V(3).Infof("List BackendConfigs in namespace(%s)", ns)
if isV1 {
return crud.C.CloudV1().BackendConfigs(ns).List(metav1.ListOptions{})
}
klog.V(2).Info("Using BackendConfig V1beta1 API")
klog.V(4).Info("Using BackendConfig V1beta1 API")
bcl, err := crud.C.CloudV1beta1().BackendConfigs(ns).List(metav1.ListOptions{})
return toV1List(bcl), err
}
Expand Down
34 changes: 34 additions & 0 deletions pkg/fuzz/gcp.go
Original file line number Diff line number Diff line change
Expand Up @@ -85,6 +85,11 @@ type BackendService struct {
Beta *computebeta.BackendService
}

// HealthCheck is a union of the API version types.
type HealthCheck struct {
GA *compute.HealthCheck
}

// NetworkEndpointGroup is a union of the API version types.
type NetworkEndpointGroup struct {
GA *compute.NetworkEndpointGroup
Expand Down Expand Up @@ -114,6 +119,7 @@ type GCLB struct {
BackendService map[meta.Key]*BackendService
NetworkEndpointGroup map[meta.Key]*NetworkEndpointGroup
InstanceGroup map[meta.Key]*InstanceGroup
HealthCheck map[meta.Key]*HealthCheck
}

// NewGCLB returns an empty GCLB.
Expand All @@ -127,6 +133,7 @@ func NewGCLB(vip string) *GCLB {
BackendService: map[meta.Key]*BackendService{},
NetworkEndpointGroup: map[meta.Key]*NetworkEndpointGroup{},
InstanceGroup: map[meta.Key]*InstanceGroup{},
HealthCheck: map[meta.Key]*HealthCheck{},
}
}

Expand Down Expand Up @@ -559,6 +566,20 @@ func GCLBForVIP(ctx context.Context, c cloud.Cloud, params *GCLBForVIPParams) (*
}
gclb.BackendService[*bsKey].Beta = bs
}

for _, hcURL := range bs.HealthChecks {
rID, err := cloud.ParseResourceURL(hcURL)
if err != nil {
return nil, err
}
hc, err := c.HealthChecks().Get(ctx, rID.Key)
if err != nil {
return nil, err
}
gclb.HealthCheck[*rID.Key] = &HealthCheck{
GA: hc,
}
}
}

var negKeys []*meta.Key
Expand Down Expand Up @@ -799,6 +820,19 @@ func RegionalGCLBForVIP(ctx context.Context, c cloud.Cloud, gclb *GCLB, params *
}
gclb.BackendService[*bsKey].Beta = bs
}
for _, hcURL := range bs.HealthChecks {
rID, err := cloud.ParseResourceURL(hcURL)
if err != nil {
return err
}
hc, err := c.RegionHealthChecks().Get(ctx, rID.Key)
if err != nil {
return err
}
gclb.HealthCheck[*rID.Key] = &HealthCheck{
GA: hc,
}
}
}

var negKeys []*meta.Key
Expand Down
12 changes: 11 additions & 1 deletion pkg/fuzz/helpers.go
Original file line number Diff line number Diff line change
Expand Up @@ -21,7 +21,7 @@ import (
"strconv"
"strings"

"k8s.io/api/core/v1"
v1 "k8s.io/api/core/v1"
"k8s.io/api/networking/v1beta1"
metav1 "k8s.io/apimachinery/pkg/apis/meta/v1"
"k8s.io/apimachinery/pkg/util/intstr"
Expand Down Expand Up @@ -394,10 +394,20 @@ func (b *BackendConfigBuilder) SetConnectionDrainingTimeout(timeout int64) *Back
return b
}

// AddCustomRequestHeader adds a custom request header to the BackendConfig.
func (b *BackendConfigBuilder) AddCustomRequestHeader(header string) *BackendConfigBuilder {
if b.backendConfig.Spec.CustomRequestHeaders == nil {
b.backendConfig.Spec.CustomRequestHeaders = &backendconfig.CustomRequestHeadersConfig{}
}
b.backendConfig.Spec.CustomRequestHeaders.Headers = append(b.backendConfig.Spec.CustomRequestHeaders.Headers, header)
return b
}

// SetHealthCheckPath adds a health check path override.
func (b *BackendConfigBuilder) SetHealthCheckPath(path string) *BackendConfigBuilder {
if b.backendConfig.Spec.HealthCheck == nil {
b.backendConfig.Spec.HealthCheck = &backendconfig.HealthCheckConfig{}
}
b.backendConfig.Spec.HealthCheck.RequestPath = &path
return b
}