Unverified Commit 22a040a1 authored by Kubernetes Submit Queue's avatar Kubernetes Submit Queue Committed by GitHub

Merge pull request #60459 from agau4779/gce-internal-lb-tests

Automatic merge from submit-queue (batch tested with PRs 57871, 61094, 60459, 61089, 61105). If you want to cherry-pick this change to another branch, please follow the instructions <a href="https://github.com/kubernetes/community/blob/master/contributors/devel/cherry-picks.md">here</a>. [GCE] Internal Loadbalancer Tests **What this PR does / why we need it**: Adds unit tests for the GCE Internal Loadbalancer. According to the test coverage tool, this PR brings test coverage of `gce_loadbalancer_internal.go` from 0% to 74.0%. ```release-note NONE ``` /assign nicksardo bowei
parents de8400c0 e07a944c
...@@ -101,14 +101,18 @@ go_test( ...@@ -101,14 +101,18 @@ go_test(
"gce_disks_test.go", "gce_disks_test.go",
"gce_healthchecks_test.go", "gce_healthchecks_test.go",
"gce_loadbalancer_external_test.go", "gce_loadbalancer_external_test.go",
"gce_loadbalancer_internal_test.go",
"gce_loadbalancer_utils_test.go",
"gce_test.go", "gce_test.go",
"gce_util_test.go", "gce_util_test.go",
"metrics_test.go", "metrics_test.go",
], ],
embed = [":go_default_library"], embed = [":go_default_library"],
deps = [ deps = [
"//pkg/api/v1/service:go_default_library",
"//pkg/cloudprovider:go_default_library", "//pkg/cloudprovider:go_default_library",
"//pkg/cloudprovider/providers/gce/cloud:go_default_library", "//pkg/cloudprovider/providers/gce/cloud:go_default_library",
"//pkg/cloudprovider/providers/gce/cloud/meta:go_default_library",
"//pkg/cloudprovider/providers/gce/cloud/mock:go_default_library", "//pkg/cloudprovider/providers/gce/cloud/mock:go_default_library",
"//pkg/kubelet/apis:go_default_library", "//pkg/kubelet/apis:go_default_library",
"//vendor/github.com/stretchr/testify/assert:go_default_library", "//vendor/github.com/stretchr/testify/assert:go_default_library",
......
...@@ -7,6 +7,7 @@ go_library( ...@@ -7,6 +7,7 @@ go_library(
visibility = ["//visibility:public"], visibility = ["//visibility:public"],
deps = [ deps = [
"//pkg/cloudprovider/providers/gce/cloud:go_default_library", "//pkg/cloudprovider/providers/gce/cloud:go_default_library",
"//pkg/cloudprovider/providers/gce/cloud/filter:go_default_library",
"//pkg/cloudprovider/providers/gce/cloud/meta:go_default_library", "//pkg/cloudprovider/providers/gce/cloud/meta:go_default_library",
"//vendor/google.golang.org/api/compute/v0.alpha:go_default_library", "//vendor/google.golang.org/api/compute/v0.alpha:go_default_library",
"//vendor/google.golang.org/api/compute/v0.beta:go_default_library", "//vendor/google.golang.org/api/compute/v0.beta:go_default_library",
......
...@@ -28,12 +28,14 @@ import ( ...@@ -28,12 +28,14 @@ import (
"encoding/json" "encoding/json"
"fmt" "fmt"
"net/http" "net/http"
"sync"
alpha "google.golang.org/api/compute/v0.alpha" alpha "google.golang.org/api/compute/v0.alpha"
beta "google.golang.org/api/compute/v0.beta" beta "google.golang.org/api/compute/v0.beta"
ga "google.golang.org/api/compute/v1" ga "google.golang.org/api/compute/v1"
"google.golang.org/api/googleapi" "google.golang.org/api/googleapi"
"k8s.io/kubernetes/pkg/cloudprovider/providers/gce/cloud" cloud "k8s.io/kubernetes/pkg/cloudprovider/providers/gce/cloud"
"k8s.io/kubernetes/pkg/cloudprovider/providers/gce/cloud/filter"
"k8s.io/kubernetes/pkg/cloudprovider/providers/gce/cloud/meta" "k8s.io/kubernetes/pkg/cloudprovider/providers/gce/cloud/meta"
) )
...@@ -226,3 +228,183 @@ func InsertAlphaAddressHook(ctx context.Context, key *meta.Key, obj *alpha.Addre ...@@ -226,3 +228,183 @@ func InsertAlphaAddressHook(ctx context.Context, key *meta.Key, obj *alpha.Addre
projectID := m.ProjectRouter.ProjectID(ctx, meta.VersionBeta, "addresses") projectID := m.ProjectRouter.ProjectID(ctx, meta.VersionBeta, "addresses")
return convertAndInsertAlphaAddress(key, obj, m.Objects, meta.VersionAlpha, projectID) return convertAndInsertAlphaAddress(key, obj, m.Objects, meta.VersionAlpha, projectID)
} }
// InstanceGroupAttributes maps from InstanceGroup key to a map of Instances
type InstanceGroupAttributes struct {
InstanceMap map[meta.Key]map[string]*ga.InstanceWithNamedPorts
Lock *sync.Mutex
}
// AddInstances adds a list of Instances passed by InstanceReference
func (igAttrs *InstanceGroupAttributes) AddInstances(key *meta.Key, instanceRefs []*ga.InstanceReference) error {
igAttrs.Lock.Lock()
defer igAttrs.Lock.Unlock()
instancesWithNamedPorts, ok := igAttrs.InstanceMap[*key]
if !ok {
instancesWithNamedPorts = make(map[string]*ga.InstanceWithNamedPorts)
}
for _, instance := range instanceRefs {
iWithPort := &ga.InstanceWithNamedPorts{
Instance: instance.Instance,
}
instancesWithNamedPorts[instance.Instance] = iWithPort
}
igAttrs.InstanceMap[*key] = instancesWithNamedPorts
return nil
}
// RemoveInstances removes a list of Instances passed by InstanceReference
func (igAttrs *InstanceGroupAttributes) RemoveInstances(key *meta.Key, instanceRefs []*ga.InstanceReference) error {
igAttrs.Lock.Lock()
defer igAttrs.Lock.Unlock()
instancesWithNamedPorts, ok := igAttrs.InstanceMap[*key]
if !ok {
instancesWithNamedPorts = make(map[string]*ga.InstanceWithNamedPorts)
}
for _, instanceToRemove := range instanceRefs {
if _, ok := instancesWithNamedPorts[instanceToRemove.Instance]; ok {
delete(instancesWithNamedPorts, instanceToRemove.Instance)
} else {
return &googleapi.Error{
Code: http.StatusBadRequest,
Message: fmt.Sprintf("%s is not a member of %s", instanceToRemove.Instance, key.String()),
}
}
}
igAttrs.InstanceMap[*key] = instancesWithNamedPorts
return nil
}
// List gets a list of InstanceWithNamedPorts
func (igAttrs *InstanceGroupAttributes) List(key *meta.Key) []*ga.InstanceWithNamedPorts {
igAttrs.Lock.Lock()
defer igAttrs.Lock.Unlock()
instancesWithNamedPorts, ok := igAttrs.InstanceMap[*key]
if !ok {
instancesWithNamedPorts = make(map[string]*ga.InstanceWithNamedPorts)
}
var instanceList []*ga.InstanceWithNamedPorts
for _, val := range instancesWithNamedPorts {
instanceList = append(instanceList, val)
}
return instanceList
}
// AddInstancesHook mocks adding instances from an InstanceGroup
func AddInstancesHook(ctx context.Context, key *meta.Key, req *ga.InstanceGroupsAddInstancesRequest, m *cloud.MockInstanceGroups) error {
_, err := m.Get(ctx, key)
if err != nil {
return &googleapi.Error{
Code: http.StatusNotFound,
Message: fmt.Sprintf("Key: %s was not found in InstanceGroups", key.String()),
}
}
var attrs InstanceGroupAttributes
attrs = m.X.(InstanceGroupAttributes)
attrs.AddInstances(key, req.Instances)
m.X = attrs
return nil
}
// ListInstancesHook mocks listing instances from an InstanceGroup
func ListInstancesHook(ctx context.Context, key *meta.Key, req *ga.InstanceGroupsListInstancesRequest, filter *filter.F, m *cloud.MockInstanceGroups) ([]*ga.InstanceWithNamedPorts, error) {
_, err := m.Get(ctx, key)
if err != nil {
return nil, &googleapi.Error{
Code: http.StatusNotFound,
Message: fmt.Sprintf("Key: %s was not found in InstanceGroups", key.String()),
}
}
var attrs InstanceGroupAttributes
attrs = m.X.(InstanceGroupAttributes)
instances := attrs.List(key)
return instances, nil
}
// RemoveInstancesHook mocks removing instances from an InstanceGroup
func RemoveInstancesHook(ctx context.Context, key *meta.Key, req *ga.InstanceGroupsRemoveInstancesRequest, m *cloud.MockInstanceGroups) error {
_, err := m.Get(ctx, key)
if err != nil {
return &googleapi.Error{
Code: http.StatusNotFound,
Message: fmt.Sprintf("Key: %s was not found in InstanceGroups", key.String()),
}
}
var attrs InstanceGroupAttributes
attrs = m.X.(InstanceGroupAttributes)
attrs.RemoveInstances(key, req.Instances)
m.X = attrs
return nil
}
// UpdateFirewallHook defines the hook for updating a Firewall. It replaces the
// object with the same key in the mock with the updated object.
func UpdateFirewallHook(ctx context.Context, key *meta.Key, obj *ga.Firewall, m *cloud.MockFirewalls) error {
_, err := m.Get(ctx, key)
if err != nil {
return &googleapi.Error{
Code: http.StatusNotFound,
Message: fmt.Sprintf("Key: %s was not found in Firewalls", key.String()),
}
}
obj.Name = key.Name
projectID := m.ProjectRouter.ProjectID(ctx, "ga", "firewalls")
obj.SelfLink = cloud.SelfLink(meta.VersionGA, projectID, "firewalls", key)
m.Objects[*key] = &cloud.MockFirewallsObj{Obj: obj}
return nil
}
// UpdateHealthCheckHook defines the hook for updating a HealthCheck. It
// replaces the object with the same key in the mock with the updated object.
func UpdateHealthCheckHook(ctx context.Context, key *meta.Key, obj *ga.HealthCheck, m *cloud.MockHealthChecks) error {
_, err := m.Get(ctx, key)
if err != nil {
return &googleapi.Error{
Code: http.StatusNotFound,
Message: fmt.Sprintf("Key: %s was not found in HealthChecks", key.String()),
}
}
obj.Name = key.Name
projectID := m.ProjectRouter.ProjectID(ctx, "ga", "healthChecks")
obj.SelfLink = cloud.SelfLink(meta.VersionGA, projectID, "healthChecks", key)
m.Objects[*key] = &cloud.MockHealthChecksObj{Obj: obj}
return nil
}
// UpdateRegionBackendServiceHook defines the hook for updating a Region
// BackendsService. It replaces the object with the same key in the mock with
// the updated object.
func UpdateRegionBackendServiceHook(ctx context.Context, key *meta.Key, obj *ga.BackendService, m *cloud.MockRegionBackendServices) error {
_, err := m.Get(ctx, key)
if err != nil {
return &googleapi.Error{
Code: http.StatusNotFound,
Message: fmt.Sprintf("Key: %s was not found in RegionBackendServices", key.String()),
}
}
obj.Name = key.Name
projectID := m.ProjectRouter.ProjectID(ctx, "ga", "backendServices")
obj.SelfLink = cloud.SelfLink(meta.VersionGA, projectID, "backendServices", key)
m.Objects[*key] = &cloud.MockRegionBackendServicesObj{Obj: obj}
return nil
}
/*
Copyright 2017 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.
*/
// This file contains shared functions and variables to set up for tests for
// ExternalLoadBalancer and InternalLoadBalancers. It currently cannot live in a
// separate package from GCE because then it would cause a circular import.
package gce
import (
"fmt"
"net/http"
"sync"
compute "google.golang.org/api/compute/v1"
"k8s.io/api/core/v1"
metav1 "k8s.io/apimachinery/pkg/apis/meta/v1"
"k8s.io/kubernetes/pkg/cloudprovider/providers/gce/cloud"
"k8s.io/kubernetes/pkg/cloudprovider/providers/gce/cloud/meta"
"k8s.io/kubernetes/pkg/cloudprovider/providers/gce/cloud/mock"
kubeletapis "k8s.io/kubernetes/pkg/kubelet/apis"
)
type TestClusterValues struct {
ProjectID string
Region string
ZoneName string
ClusterID string
ClusterName string
}
func DefaultTestClusterValues() TestClusterValues {
return TestClusterValues{
ProjectID: "test-project",
Region: "us-central1",
ZoneName: "us-central1-b",
ClusterID: "test-cluster-id",
ClusterName: "Test Cluster Name",
}
}
var fakeApiService = &v1.Service{
Spec: v1.ServiceSpec{
SessionAffinity: v1.ServiceAffinityClientIP,
Type: v1.ServiceTypeClusterIP,
Ports: []v1.ServicePort{{Protocol: v1.ProtocolTCP, Port: int32(123)}},
},
}
type fakeRoundTripper struct{}
func (*fakeRoundTripper) RoundTrip(*http.Request) (*http.Response, error) {
return nil, fmt.Errorf("err: test used fake http client")
}
func fakeGCECloud(vals TestClusterValues) (*GCECloud, error) {
client := &http.Client{Transport: &fakeRoundTripper{}}
service, err := compute.New(client)
if err != nil {
return nil, err
}
// Used in disk unit tests
fakeManager := newFakeManager(vals.ProjectID, vals.Region)
zonesWithNodes := createNodeZones([]string{vals.ZoneName})
alphaFeatureGate, err := NewAlphaFeatureGate([]string{})
if err != nil {
return nil, err
}
gce := &GCECloud{
region: vals.Region,
service: service,
manager: fakeManager,
managedZones: []string{vals.ZoneName},
projectID: vals.ProjectID,
networkProjectID: vals.ProjectID,
AlphaFeatureGate: alphaFeatureGate,
nodeZones: zonesWithNodes,
nodeInformerSynced: func() bool { return true },
}
c := cloud.NewMockGCE(&gceProjectRouter{gce})
c.MockTargetPools.AddInstanceHook = mock.AddInstanceHook
c.MockTargetPools.RemoveInstanceHook = mock.RemoveInstanceHook
c.MockForwardingRules.InsertHook = mock.InsertFwdRuleHook
c.MockAddresses.InsertHook = mock.InsertAddressHook
c.MockAlphaAddresses.InsertHook = mock.InsertAlphaAddressHook
c.MockInstanceGroups.X = mock.InstanceGroupAttributes{
InstanceMap: make(map[meta.Key]map[string]*compute.InstanceWithNamedPorts),
Lock: &sync.Mutex{},
}
c.MockInstanceGroups.AddInstancesHook = mock.AddInstancesHook
c.MockInstanceGroups.RemoveInstancesHook = mock.RemoveInstancesHook
c.MockInstanceGroups.ListInstancesHook = mock.ListInstancesHook
c.MockRegionBackendServices.UpdateHook = mock.UpdateRegionBackendServiceHook
c.MockHealthChecks.UpdateHook = mock.UpdateHealthCheckHook
c.MockFirewalls.UpdateHook = mock.UpdateFirewallHook
keyGA := meta.GlobalKey("key-ga")
c.MockZones.Objects[*keyGA] = &cloud.MockZonesObj{
Obj: &compute.Zone{Name: vals.ZoneName, Region: gce.getRegionLink(vals.Region)},
}
gce.c = c
return gce, nil
}
func createAndInsertNodes(gce *GCECloud, nodeNames []string, zoneName string) ([]*v1.Node, error) {
nodes := []*v1.Node{}
for _, name := range nodeNames {
// Inserting the same node name twice causes an error - here we check if
// the instance exists already before insertion.
// TestUpdateExternalLoadBalancer inserts a new node, and relies on an older
// node to already have been inserted.
instance, _ := gce.getInstanceByName(name)
if instance == nil {
err := gce.InsertInstance(
gce.ProjectID(),
zoneName,
&compute.Instance{
Name: name,
Tags: &compute.Tags{
Items: []string{name},
},
},
)
if err != nil {
return nodes, err
}
}
nodes = append(
nodes,
&v1.Node{
ObjectMeta: metav1.ObjectMeta{
Name: name,
Labels: map[string]string{
kubeletapis.LabelHostname: name,
kubeletapis.LabelZoneFailureDomain: zoneName,
},
},
Status: v1.NodeStatus{
NodeInfo: v1.NodeSystemInfo{
KubeProxyVersion: "v1.7.2",
},
},
},
)
}
return nodes, nil
}
Markdown is supported
0% or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment