[GLBC] Support backside re-encryption (#519)
Support backside re-encryption
This commit is contained in:
parent
7f3763590a
commit
642cb74cc7
21 changed files with 1046 additions and 433 deletions
|
|
@ -27,6 +27,7 @@ import (
|
|||
|
||||
compute "google.golang.org/api/compute/v1"
|
||||
"k8s.io/apimachinery/pkg/util/sets"
|
||||
api_v1 "k8s.io/client-go/pkg/api/v1"
|
||||
|
||||
"k8s.io/ingress/controllers/gce/healthchecks"
|
||||
"k8s.io/ingress/controllers/gce/instances"
|
||||
|
|
@ -75,6 +76,7 @@ type Backends struct {
|
|||
nodePool instances.NodePool
|
||||
healthChecker healthchecks.HealthChecker
|
||||
snapshotter storage.Snapshotter
|
||||
prober probeProvider
|
||||
// ignoredPorts are a set of ports excluded from GC, even
|
||||
// after the Ingress has been deleted. Note that invoking
|
||||
// a Delete() on these ports will still delete the backend.
|
||||
|
|
@ -86,6 +88,12 @@ func portKey(port int64) string {
|
|||
return fmt.Sprintf("%d", port)
|
||||
}
|
||||
|
||||
// ServicePort for tupling port and protocol
|
||||
type ServicePort struct {
|
||||
Port int64
|
||||
Protocol utils.AppProtocol
|
||||
}
|
||||
|
||||
// NewBackendPool returns a new backend pool.
|
||||
// - cloud: implements BackendServices and syncs backends with a cloud provider
|
||||
// - healthChecker: is capable of producing health checks for backends.
|
||||
|
|
@ -134,6 +142,11 @@ func NewBackendPool(
|
|||
return backendPool
|
||||
}
|
||||
|
||||
// Init sets the probeProvider interface value
|
||||
func (b *Backends) Init(pp probeProvider) {
|
||||
b.prober = pp
|
||||
}
|
||||
|
||||
// Get returns a single backend.
|
||||
func (b *Backends) Get(port int64) (*compute.BackendService, error) {
|
||||
be, err := b.cloud.GetBackendService(b.namer.BeName(port))
|
||||
|
|
@ -144,16 +157,24 @@ func (b *Backends) Get(port int64) (*compute.BackendService, error) {
|
|||
return be, nil
|
||||
}
|
||||
|
||||
func (b *Backends) create(igs []*compute.InstanceGroup, namedPort *compute.NamedPort, name string) (*compute.BackendService, error) {
|
||||
// Create a new health check
|
||||
if err := b.healthChecker.Add(namedPort.Port); err != nil {
|
||||
return nil, err
|
||||
func (b *Backends) ensureHealthCheck(sp ServicePort) (string, error) {
|
||||
hc := b.healthChecker.New(sp.Port, sp.Protocol)
|
||||
if b.prober != nil {
|
||||
probe, err := b.prober.GetProbe(sp)
|
||||
if err != nil {
|
||||
return "", err
|
||||
}
|
||||
if probe != nil {
|
||||
glog.V(2).Infof("Applying httpGet settings of readinessProbe to health check on port %+v", sp)
|
||||
applyProbeSettingsToHC(probe, hc)
|
||||
}
|
||||
}
|
||||
hc, err := b.healthChecker.Get(namedPort.Port)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
errs := []string{}
|
||||
|
||||
return b.healthChecker.Sync(hc)
|
||||
}
|
||||
|
||||
func (b *Backends) create(igs []*compute.InstanceGroup, namedPort *compute.NamedPort, hcLink string, protocol utils.AppProtocol, name string) (*compute.BackendService, error) {
|
||||
var errs []string
|
||||
// We first try to create the backend with balancingMode=RATE. If this
|
||||
// fails, it's mostly likely because there are existing backends with
|
||||
// balancingMode=UTILIZATION. This failure mode throws a googleapi error
|
||||
|
|
@ -161,27 +182,9 @@ func (b *Backends) create(igs []*compute.InstanceGroup, namedPort *compute.Named
|
|||
// and come around to retry with the right balancing mode. The goal is to
|
||||
// switch everyone to using RATE.
|
||||
for _, bm := range []BalancingMode{Rate, Utilization} {
|
||||
backends := getBackendsForIGs(igs)
|
||||
for _, b := range backends {
|
||||
switch bm {
|
||||
case Rate:
|
||||
b.MaxRate = maxRPS
|
||||
default:
|
||||
// TODO: Set utilization and connection limits when we accept them
|
||||
// as valid fields.
|
||||
}
|
||||
b.BalancingMode = string(bm)
|
||||
}
|
||||
// Create a new backend
|
||||
backend := &compute.BackendService{
|
||||
Name: name,
|
||||
Protocol: "HTTP",
|
||||
Backends: backends,
|
||||
HealthChecks: []string{hc.SelfLink},
|
||||
Port: namedPort.Port,
|
||||
PortName: namedPort.Name,
|
||||
}
|
||||
if err := b.cloud.CreateBackendService(backend); err != nil {
|
||||
bs := newBackendService(igs, bm, namedPort, []string{hcLink}, protocol, name)
|
||||
if err := b.cloud.CreateBackendService(bs); err != nil {
|
||||
// This is probably a failure because we tried to create the backend
|
||||
// with balancingMode=RATE when there are already backends with
|
||||
// balancingMode=UTILIZATION. Just ignore it and retry setting
|
||||
|
|
@ -198,31 +201,83 @@ func (b *Backends) create(igs []*compute.InstanceGroup, namedPort *compute.Named
|
|||
return nil, fmt.Errorf("%v", strings.Join(errs, "\n"))
|
||||
}
|
||||
|
||||
func newBackendService(igs []*compute.InstanceGroup, bm BalancingMode, namedPort *compute.NamedPort, healthCheckLinks []string, protocol utils.AppProtocol, name string) *compute.BackendService {
|
||||
backends := getBackendsForIGs(igs)
|
||||
for _, b := range backends {
|
||||
switch bm {
|
||||
case Rate:
|
||||
b.MaxRatePerInstance = maxRPS
|
||||
default:
|
||||
// TODO: Set utilization and connection limits when we accept them
|
||||
// as valid fields.
|
||||
}
|
||||
b.BalancingMode = string(bm)
|
||||
}
|
||||
|
||||
return &compute.BackendService{
|
||||
Name: name,
|
||||
Protocol: string(protocol),
|
||||
Backends: backends,
|
||||
HealthChecks: healthCheckLinks,
|
||||
Port: namedPort.Port,
|
||||
PortName: namedPort.Name,
|
||||
}
|
||||
}
|
||||
|
||||
// Add will get or create a Backend for the given port.
|
||||
func (b *Backends) Add(port int64) error {
|
||||
func (b *Backends) Add(p ServicePort) error {
|
||||
// We must track the port even if creating the backend failed, because
|
||||
// we might've created a health-check for it.
|
||||
be := &compute.BackendService{}
|
||||
defer func() { b.snapshotter.Add(portKey(port), be) }()
|
||||
defer func() { b.snapshotter.Add(portKey(p.Port), be) }()
|
||||
|
||||
igs, namedPort, err := b.nodePool.AddInstanceGroup(b.namer.IGName(), port)
|
||||
igs, namedPort, err := b.nodePool.AddInstanceGroup(b.namer.IGName(), p.Port)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
be, _ = b.Get(port)
|
||||
|
||||
// Ensure health check for backend service exists
|
||||
hcLink, err := b.ensureHealthCheck(p)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
pName := b.namer.BeName(p.Port)
|
||||
be, _ = b.Get(p.Port)
|
||||
if be == nil {
|
||||
glog.Infof("Creating backend for %d instance groups, port %v named port %v",
|
||||
len(igs), port, namedPort)
|
||||
be, err = b.create(igs, namedPort, b.namer.BeName(port))
|
||||
glog.V(2).Infof("Creating backend for %d instance groups, port %v named port %v", len(igs), p.Port, namedPort)
|
||||
be, err = b.create(igs, namedPort, hcLink, p.Protocol, pName)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
}
|
||||
|
||||
existingHCLink := ""
|
||||
if len(be.HealthChecks) == 1 {
|
||||
existingHCLink = be.HealthChecks[0]
|
||||
}
|
||||
|
||||
if be.Protocol != string(p.Protocol) || existingHCLink != hcLink {
|
||||
glog.V(2).Infof("Updating backend protocol %v (%v) for change in protocol (%v) or health check", pName, be.Protocol, string(p.Protocol))
|
||||
be.Protocol = string(p.Protocol)
|
||||
be.HealthChecks = []string{hcLink}
|
||||
if err = b.cloud.UpdateBackendService(be); err != nil {
|
||||
return err
|
||||
}
|
||||
}
|
||||
|
||||
// If previous health check was legacy type, we need to delete it.
|
||||
if existingHCLink != hcLink && strings.Contains(existingHCLink, "/httpHealthChecks/") {
|
||||
if err = b.healthChecker.DeleteLegacy(p.Port); err != nil {
|
||||
return err
|
||||
}
|
||||
}
|
||||
|
||||
// we won't find any igs till the node pool syncs nodes.
|
||||
if len(igs) == 0 {
|
||||
return nil
|
||||
}
|
||||
if err := b.edgeHop(be, igs); err != nil {
|
||||
if err = b.edgeHop(be, igs); err != nil {
|
||||
return err
|
||||
}
|
||||
return err
|
||||
|
|
@ -231,7 +286,7 @@ func (b *Backends) Add(port int64) error {
|
|||
// Delete deletes the Backend for the given port.
|
||||
func (b *Backends) Delete(port int64) (err error) {
|
||||
name := b.namer.BeName(port)
|
||||
glog.Infof("Deleting backend %v", name)
|
||||
glog.V(2).Infof("Deleting backend service %v", name)
|
||||
defer func() {
|
||||
if utils.IsHTTPErrorCode(err, http.StatusNotFound) {
|
||||
err = nil
|
||||
|
|
@ -241,15 +296,11 @@ func (b *Backends) Delete(port int64) (err error) {
|
|||
}
|
||||
}()
|
||||
// Try deleting health checks even if a backend is not found.
|
||||
if err = b.cloud.DeleteBackendService(name); err != nil &&
|
||||
!utils.IsHTTPErrorCode(err, http.StatusNotFound) {
|
||||
if err = b.cloud.DeleteBackendService(name); err != nil && !utils.IsHTTPErrorCode(err, http.StatusNotFound) {
|
||||
return err
|
||||
}
|
||||
if err = b.healthChecker.Delete(port); err != nil &&
|
||||
!utils.IsHTTPErrorCode(err, http.StatusNotFound) {
|
||||
return err
|
||||
}
|
||||
return nil
|
||||
|
||||
return b.healthChecker.Delete(port)
|
||||
}
|
||||
|
||||
// List lists all backends.
|
||||
|
|
@ -306,7 +357,7 @@ func (b *Backends) edgeHop(be *compute.BackendService, igs []*compute.InstanceGr
|
|||
}
|
||||
|
||||
// Sync syncs backend services corresponding to ports in the given list.
|
||||
func (b *Backends) Sync(svcNodePorts []int64) error {
|
||||
func (b *Backends) Sync(svcNodePorts []ServicePort) error {
|
||||
glog.V(3).Infof("Sync: backends %v", svcNodePorts)
|
||||
|
||||
// create backends for new ports, perform an edge hop for existing ports
|
||||
|
|
@ -319,14 +370,14 @@ func (b *Backends) Sync(svcNodePorts []int64) error {
|
|||
}
|
||||
|
||||
// GC garbage collects services corresponding to ports in the given list.
|
||||
func (b *Backends) GC(svcNodePorts []int64) error {
|
||||
func (b *Backends) GC(svcNodePorts []ServicePort) error {
|
||||
knownPorts := sets.NewString()
|
||||
for _, port := range svcNodePorts {
|
||||
knownPorts.Insert(portKey(port))
|
||||
for _, p := range svcNodePorts {
|
||||
knownPorts.Insert(portKey(p.Port))
|
||||
}
|
||||
pool := b.snapshotter.Snapshot()
|
||||
for port := range pool {
|
||||
p, err := strconv.Atoi(port)
|
||||
p, err := strconv.ParseUint(port, 10, 16)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
|
|
@ -345,7 +396,7 @@ func (b *Backends) GC(svcNodePorts []int64) error {
|
|||
// Shutdown deletes all backends and the default backend.
|
||||
// This will fail if one of the backends is being used by another resource.
|
||||
func (b *Backends) Shutdown() error {
|
||||
if err := b.GC([]int64{}); err != nil {
|
||||
if err := b.GC([]ServicePort{}); err != nil {
|
||||
return err
|
||||
}
|
||||
return nil
|
||||
|
|
@ -365,3 +416,17 @@ func (b *Backends) Status(name string) string {
|
|||
// TODO: State transition are important, not just the latest.
|
||||
return hs.HealthStatus[0].HealthState
|
||||
}
|
||||
|
||||
func applyProbeSettingsToHC(p *api_v1.Probe, hc *healthchecks.HealthCheck) {
|
||||
healthPath := p.Handler.HTTPGet.Path
|
||||
// GCE requires a leading "/" for health check urls.
|
||||
if !strings.HasPrefix(healthPath, "/") {
|
||||
healthPath = "/" + healthPath
|
||||
}
|
||||
|
||||
hc.RequestPath = healthPath
|
||||
hc.Host = p.Handler.HTTPGet.Host
|
||||
hc.Description = "Kubernetes L7 health check generated with readiness probe settings."
|
||||
hc.CheckIntervalSec = int64(p.PeriodSeconds) + int64(healthchecks.DefaultHealthCheckInterval.Seconds())
|
||||
hc.TimeoutSec = int64(p.TimeoutSeconds)
|
||||
}
|
||||
|
|
|
|||
|
|
@ -17,12 +17,15 @@ limitations under the License.
|
|||
package backends
|
||||
|
||||
import (
|
||||
"fmt"
|
||||
"net/http"
|
||||
"testing"
|
||||
|
||||
compute "google.golang.org/api/compute/v1"
|
||||
"google.golang.org/api/googleapi"
|
||||
"k8s.io/apimachinery/pkg/util/intstr"
|
||||
"k8s.io/apimachinery/pkg/util/sets"
|
||||
api_v1 "k8s.io/client-go/pkg/api/v1"
|
||||
|
||||
"k8s.io/ingress/controllers/gce/healthchecks"
|
||||
"k8s.io/ingress/controllers/gce/instances"
|
||||
|
|
@ -34,14 +37,28 @@ const defaultZone = "zone-a"
|
|||
|
||||
var noOpErrFunc = func(op int, be *compute.BackendService) error { return nil }
|
||||
|
||||
func newBackendPool(f BackendServices, fakeIGs instances.InstanceGroups, syncWithCloud bool) BackendPool {
|
||||
var existingProbe = &api_v1.Probe{
|
||||
Handler: api_v1.Handler{
|
||||
HTTPGet: &api_v1.HTTPGetAction{
|
||||
Scheme: api_v1.URISchemeHTTPS,
|
||||
Path: "/my-special-path",
|
||||
Port: intstr.IntOrString{
|
||||
Type: intstr.Int,
|
||||
IntVal: 443,
|
||||
},
|
||||
},
|
||||
},
|
||||
}
|
||||
|
||||
func newBackendPool(f BackendServices, fakeIGs instances.InstanceGroups, syncWithCloud bool) *Backends {
|
||||
namer := &utils.Namer{}
|
||||
nodePool := instances.NewNodePool(fakeIGs)
|
||||
nodePool.Init(&instances.FakeZoneLister{Zones: []string{defaultZone}})
|
||||
healthChecks := healthchecks.NewHealthChecker(healthchecks.NewFakeHealthChecks(), "/", namer)
|
||||
healthChecks.Init(&healthchecks.FakeHealthCheckGetter{DefaultHealthCheck: nil})
|
||||
return NewBackendPool(
|
||||
f, healthChecks, nodePool, namer, []int64{}, syncWithCloud)
|
||||
healthChecks := healthchecks.NewHealthChecker(healthchecks.NewFakeHealthCheckProvider(), "/", namer)
|
||||
bp := NewBackendPool(f, healthChecks, nodePool, namer, []int64{}, syncWithCloud)
|
||||
probes := map[ServicePort]*api_v1.Probe{{Port: 443, Protocol: utils.ProtocolHTTPS}: existingProbe}
|
||||
bp.Init(NewFakeProbeProvider(probes))
|
||||
return bp
|
||||
}
|
||||
|
||||
func TestBackendPoolAdd(t *testing.T) {
|
||||
|
|
@ -50,39 +67,125 @@ func TestBackendPoolAdd(t *testing.T) {
|
|||
pool := newBackendPool(f, fakeIGs, false)
|
||||
namer := utils.Namer{}
|
||||
|
||||
// Add a backend for a port, then re-add the same port and
|
||||
// make sure it corrects a broken link from the backend to
|
||||
// the instance group.
|
||||
nodePort := int64(8080)
|
||||
pool.Add(nodePort)
|
||||
beName := namer.BeName(nodePort)
|
||||
testCases := []ServicePort{
|
||||
{80, utils.ProtocolHTTP},
|
||||
{443, utils.ProtocolHTTPS},
|
||||
}
|
||||
|
||||
for _, nodePort := range testCases {
|
||||
// For simplicity, these tests use 80/443 as nodeports
|
||||
t.Run(fmt.Sprintf("Port:%v Protocol:%v", nodePort.Port, nodePort.Protocol), func(t *testing.T) {
|
||||
// Add a backend for a port, then re-add the same port and
|
||||
// make sure it corrects a broken link from the backend to
|
||||
// the instance group.
|
||||
err := pool.Add(nodePort)
|
||||
if err != nil {
|
||||
t.Fatalf("Did not find expect error when adding a nodeport: %v, err: %v", nodePort, err)
|
||||
}
|
||||
beName := namer.BeName(nodePort.Port)
|
||||
|
||||
// Check that the new backend has the right port
|
||||
be, err := f.GetBackendService(beName)
|
||||
if err != nil {
|
||||
t.Fatalf("Did not find expected backend %v", beName)
|
||||
}
|
||||
if be.Port != nodePort.Port {
|
||||
t.Fatalf("Backend %v has wrong port %v, expected %v", be.Name, be.Port, nodePort)
|
||||
}
|
||||
|
||||
// Check that the instance group has the new port
|
||||
var found bool
|
||||
for _, port := range fakeIGs.Ports {
|
||||
if port == nodePort.Port {
|
||||
found = true
|
||||
}
|
||||
}
|
||||
if !found {
|
||||
t.Fatalf("Port %v not added to instance group", nodePort)
|
||||
}
|
||||
|
||||
// Check the created healthcheck is the correct protocol
|
||||
// pool.healthChecker.
|
||||
hc, err := pool.healthChecker.Get(nodePort.Port)
|
||||
if err != nil {
|
||||
t.Fatalf("Unexpected err when querying fake healthchecker: %v", err)
|
||||
}
|
||||
|
||||
if hc.Protocol() != nodePort.Protocol {
|
||||
t.Fatalf("Healthcheck scheme does not match nodeport scheme: hc:%v np:%v", hc.Protocol(), nodePort.Protocol)
|
||||
}
|
||||
|
||||
if nodePort.Port == 443 && hc.RequestPath != "/my-special-path" {
|
||||
t.Fatalf("Healthcheck for 443 should have special request path from probe")
|
||||
}
|
||||
})
|
||||
}
|
||||
}
|
||||
|
||||
func TestBackendPoolUpdate(t *testing.T) {
|
||||
f := NewFakeBackendServices(noOpErrFunc)
|
||||
fakeIGs := instances.NewFakeInstanceGroups(sets.NewString())
|
||||
pool := newBackendPool(f, fakeIGs, false)
|
||||
namer := utils.Namer{}
|
||||
|
||||
p := ServicePort{Port: 3000, Protocol: utils.ProtocolHTTP}
|
||||
pool.Add(p)
|
||||
beName := namer.BeName(p.Port)
|
||||
|
||||
// Check that the new backend has the right port
|
||||
be, err := f.GetBackendService(beName)
|
||||
if err != nil {
|
||||
t.Fatalf("Did not find expected backend %v", beName)
|
||||
t.Fatalf("Unexpected err: %v", err)
|
||||
}
|
||||
if be.Port != nodePort {
|
||||
t.Fatalf("Backend %v has wrong port %v, expected %v", be.Name, be.Port, nodePort)
|
||||
|
||||
if utils.AppProtocol(be.Protocol) != p.Protocol {
|
||||
t.Fatalf("Expected scheme %v but got %v", p.Protocol, be.Protocol)
|
||||
}
|
||||
// Check that the instance group has the new port
|
||||
var found bool
|
||||
for _, port := range fakeIGs.Ports {
|
||||
if port == nodePort {
|
||||
found = true
|
||||
}
|
||||
|
||||
// Assert the proper health check was created
|
||||
hc, _ := pool.healthChecker.Get(p.Port)
|
||||
if hc == nil || hc.Protocol() != p.Protocol {
|
||||
t.Fatalf("Expected %s health check, received %v: ", p.Protocol, hc)
|
||||
}
|
||||
if !found {
|
||||
t.Fatalf("Port %v not added to instance group", nodePort)
|
||||
|
||||
// Update service port to encrypted
|
||||
p.Protocol = utils.ProtocolHTTPS
|
||||
pool.Sync([]ServicePort{p})
|
||||
|
||||
be, err = f.GetBackendService(beName)
|
||||
if err != nil {
|
||||
t.Fatalf("Unexpected err retrieving backend service after update: %v", err)
|
||||
}
|
||||
|
||||
// Assert the backend has the correct protocol
|
||||
if utils.AppProtocol(be.Protocol) != p.Protocol {
|
||||
t.Fatalf("Expected scheme %v but got %v", p.Protocol, utils.AppProtocol(be.Protocol))
|
||||
}
|
||||
|
||||
// Assert the proper health check was created
|
||||
hc, _ = pool.healthChecker.Get(p.Port)
|
||||
if hc == nil || hc.Protocol() != p.Protocol {
|
||||
t.Fatalf("Expected %s health check, received %v: ", p.Protocol, hc)
|
||||
}
|
||||
}
|
||||
|
||||
func TestBackendPoolChaosMonkey(t *testing.T) {
|
||||
f := NewFakeBackendServices(noOpErrFunc)
|
||||
fakeIGs := instances.NewFakeInstanceGroups(sets.NewString())
|
||||
pool := newBackendPool(f, fakeIGs, false)
|
||||
namer := utils.Namer{}
|
||||
|
||||
nodePort := ServicePort{Port: 8080, Protocol: utils.ProtocolHTTP}
|
||||
pool.Add(nodePort)
|
||||
beName := namer.BeName(nodePort.Port)
|
||||
|
||||
be, _ := f.GetBackendService(beName)
|
||||
|
||||
// Mess up the link between backend service and instance group.
|
||||
// This simulates a user doing foolish things through the UI.
|
||||
f.calls = []int{}
|
||||
be, err = f.GetBackendService(beName)
|
||||
be.Backends = []*compute.Backend{
|
||||
{Group: "test edge hop"},
|
||||
}
|
||||
f.calls = []int{}
|
||||
f.UpdateBackendService(be)
|
||||
|
||||
pool.Add(nodePort)
|
||||
|
|
@ -114,28 +217,35 @@ func TestBackendPoolAdd(t *testing.T) {
|
|||
func TestBackendPoolSync(t *testing.T) {
|
||||
// Call sync on a backend pool with a list of ports, make sure the pool
|
||||
// creates/deletes required ports.
|
||||
svcNodePorts := []int64{81, 82, 83}
|
||||
svcNodePorts := []ServicePort{{Port: 81, Protocol: utils.ProtocolHTTP}, {Port: 82, Protocol: utils.ProtocolHTTPS}, {Port: 83, Protocol: utils.ProtocolHTTP}}
|
||||
f := NewFakeBackendServices(noOpErrFunc)
|
||||
fakeIGs := instances.NewFakeInstanceGroups(sets.NewString())
|
||||
pool := newBackendPool(f, fakeIGs, true)
|
||||
pool.Add(81)
|
||||
pool.Add(90)
|
||||
pool.Sync(svcNodePorts)
|
||||
pool.GC(svcNodePorts)
|
||||
pool.Add(ServicePort{Port: 81})
|
||||
pool.Add(ServicePort{Port: 90})
|
||||
if err := pool.Sync(svcNodePorts); err != nil {
|
||||
t.Errorf("Expected backend pool to sync, err: %v", err)
|
||||
}
|
||||
if err := pool.GC(svcNodePorts); err != nil {
|
||||
t.Errorf("Expected backend pool to GC, err: %v", err)
|
||||
}
|
||||
if _, err := pool.Get(90); err == nil {
|
||||
t.Fatalf("Did not expect to find port 90")
|
||||
}
|
||||
for _, port := range svcNodePorts {
|
||||
if _, err := pool.Get(port); err != nil {
|
||||
if _, err := pool.Get(port.Port); err != nil {
|
||||
t.Fatalf("Expected to find port %v", port)
|
||||
}
|
||||
}
|
||||
|
||||
svcNodePorts = []int64{81}
|
||||
deletedPorts := []int64{82, 83}
|
||||
pool.GC(svcNodePorts)
|
||||
svcNodePorts = []ServicePort{{Port: 81}}
|
||||
deletedPorts := []ServicePort{{Port: 82}, {Port: 83}}
|
||||
if err := pool.GC(svcNodePorts); err != nil {
|
||||
t.Fatalf("Expected backend pool to GC, err: %v", err)
|
||||
}
|
||||
|
||||
for _, port := range deletedPorts {
|
||||
if _, err := pool.Get(port); err == nil {
|
||||
if _, err := pool.Get(port.Port); err == nil {
|
||||
t.Fatalf("Pool contains %v after deletion", port)
|
||||
}
|
||||
}
|
||||
|
|
@ -152,13 +262,13 @@ func TestBackendPoolSync(t *testing.T) {
|
|||
|
||||
namer := &utils.Namer{}
|
||||
// This backend should get deleted again since it is managed by this cluster.
|
||||
f.CreateBackendService(&compute.BackendService{Name: namer.BeName(deletedPorts[0])})
|
||||
f.CreateBackendService(&compute.BackendService{Name: namer.BeName(deletedPorts[0].Port)})
|
||||
|
||||
// TODO: Avoid casting.
|
||||
// Repopulate the pool with a cloud list, which now includes the 82 port
|
||||
// backend. This would happen if, say, an ingress backend is removed
|
||||
// while the controller is restarting.
|
||||
pool.(*Backends).snapshotter.(*storage.CloudListingPool).ReplenishPool()
|
||||
pool.snapshotter.(*storage.CloudListingPool).ReplenishPool()
|
||||
|
||||
pool.GC(svcNodePorts)
|
||||
|
||||
|
|
@ -178,13 +288,68 @@ func TestBackendPoolSync(t *testing.T) {
|
|||
}
|
||||
}
|
||||
|
||||
func TestBackendPoolDeleteLegacyHealthChecks(t *testing.T) {
|
||||
namer := &utils.Namer{}
|
||||
f := NewFakeBackendServices(noOpErrFunc)
|
||||
fakeIGs := instances.NewFakeInstanceGroups(sets.NewString())
|
||||
nodePool := instances.NewNodePool(fakeIGs)
|
||||
nodePool.Init(&instances.FakeZoneLister{Zones: []string{defaultZone}})
|
||||
hcp := healthchecks.NewFakeHealthCheckProvider()
|
||||
healthChecks := healthchecks.NewHealthChecker(hcp, "/", namer)
|
||||
bp := NewBackendPool(f, healthChecks, nodePool, namer, []int64{}, false)
|
||||
probes := map[ServicePort]*api_v1.Probe{}
|
||||
bp.Init(NewFakeProbeProvider(probes))
|
||||
|
||||
// Create a legacy HTTP health check
|
||||
beName := namer.BeName(80)
|
||||
if err := hcp.CreateHttpHealthCheck(&compute.HttpHealthCheck{
|
||||
Name: beName,
|
||||
Port: 80,
|
||||
}); err != nil {
|
||||
t.Fatalf("unexpected error creating http health check %v", err)
|
||||
}
|
||||
|
||||
// Verify health check exists
|
||||
hc, err := hcp.GetHttpHealthCheck(beName)
|
||||
if err != nil {
|
||||
t.Fatalf("unexpected error getting http health check %v", err)
|
||||
}
|
||||
|
||||
// Create backend service with expected name and link to legacy health check
|
||||
f.CreateBackendService(&compute.BackendService{
|
||||
Name: beName,
|
||||
HealthChecks: []string{hc.SelfLink},
|
||||
})
|
||||
|
||||
// Have pool sync the above backend service
|
||||
bp.Add(ServicePort{Port: 80, Protocol: utils.ProtocolHTTPS})
|
||||
|
||||
// Verify the legacy health check has been deleted
|
||||
_, err = hcp.GetHttpHealthCheck(beName)
|
||||
if err == nil {
|
||||
t.Fatalf("expected error getting http health check %v", err)
|
||||
}
|
||||
|
||||
// Verify a newer health check exists
|
||||
hcNew, err := hcp.GetHealthCheck(beName)
|
||||
if err != nil {
|
||||
t.Fatalf("unexpected error getting http health check %v", err)
|
||||
}
|
||||
|
||||
// Verify the newer health check is of type HTTPS
|
||||
if hcNew.Type != string(utils.ProtocolHTTPS) {
|
||||
t.Fatalf("expected health check type to be %v, actual %v", string(utils.ProtocolHTTPS), hcNew.Type)
|
||||
}
|
||||
}
|
||||
|
||||
func TestBackendPoolShutdown(t *testing.T) {
|
||||
f := NewFakeBackendServices(noOpErrFunc)
|
||||
fakeIGs := instances.NewFakeInstanceGroups(sets.NewString())
|
||||
pool := newBackendPool(f, fakeIGs, false)
|
||||
namer := utils.Namer{}
|
||||
|
||||
pool.Add(80)
|
||||
// Add a backend-service and verify that it doesn't exist after Shutdown()
|
||||
pool.Add(ServicePort{Port: 80})
|
||||
pool.Shutdown()
|
||||
if _, err := f.GetBackendService(namer.BeName(80)); err == nil {
|
||||
t.Fatalf("%v", err)
|
||||
|
|
@ -198,7 +363,7 @@ func TestBackendInstanceGroupClobbering(t *testing.T) {
|
|||
namer := utils.Namer{}
|
||||
|
||||
// This will add the instance group k8s-ig to the instance pool
|
||||
pool.Add(80)
|
||||
pool.Add(ServicePort{Port: 80})
|
||||
|
||||
be, err := f.GetBackendService(namer.BeName(80))
|
||||
if err != nil {
|
||||
|
|
@ -211,12 +376,12 @@ func TestBackendInstanceGroupClobbering(t *testing.T) {
|
|||
{Group: "k8s-ig-foo"},
|
||||
}
|
||||
be.Backends = append(be.Backends, newGroups...)
|
||||
if err := f.UpdateBackendService(be); err != nil {
|
||||
if err = f.UpdateBackendService(be); err != nil {
|
||||
t.Fatalf("Failed to update backend service %v", be.Name)
|
||||
}
|
||||
|
||||
// Make sure repeated adds don't clobber the inserted instance group
|
||||
pool.Add(80)
|
||||
pool.Add(ServicePort{Port: 80})
|
||||
be, err = f.GetBackendService(namer.BeName(80))
|
||||
if err != nil {
|
||||
t.Fatalf("%v", err)
|
||||
|
|
@ -242,7 +407,7 @@ func TestBackendCreateBalancingMode(t *testing.T) {
|
|||
fakeIGs := instances.NewFakeInstanceGroups(sets.NewString())
|
||||
pool := newBackendPool(f, fakeIGs, false)
|
||||
namer := utils.Namer{}
|
||||
nodePort := int64(8080)
|
||||
nodePort := ServicePort{Port: 8080}
|
||||
modes := []BalancingMode{Rate, Utilization}
|
||||
|
||||
// block the creation of Backends with the given balancingMode
|
||||
|
|
@ -259,7 +424,7 @@ func TestBackendCreateBalancingMode(t *testing.T) {
|
|||
}
|
||||
|
||||
pool.Add(nodePort)
|
||||
be, err := f.GetBackendService(namer.BeName(nodePort))
|
||||
be, err := f.GetBackendService(namer.BeName(nodePort.Port))
|
||||
if err != nil {
|
||||
t.Fatalf("%v", err)
|
||||
}
|
||||
|
|
@ -269,6 +434,32 @@ func TestBackendCreateBalancingMode(t *testing.T) {
|
|||
t.Fatalf("Wrong balancing mode, expected %v got %v", modes[(i+1)%len(modes)], b.BalancingMode)
|
||||
}
|
||||
}
|
||||
pool.GC([]int64{})
|
||||
pool.GC([]ServicePort{})
|
||||
}
|
||||
}
|
||||
|
||||
func TestApplyProbeSettingsToHC(t *testing.T) {
|
||||
p := "healthz"
|
||||
hc := healthchecks.DefaultHealthCheck(8080, utils.ProtocolHTTPS)
|
||||
probe := &api_v1.Probe{
|
||||
Handler: api_v1.Handler{
|
||||
HTTPGet: &api_v1.HTTPGetAction{
|
||||
Scheme: api_v1.URISchemeHTTP,
|
||||
Path: p,
|
||||
Port: intstr.IntOrString{
|
||||
Type: intstr.Int,
|
||||
IntVal: 80,
|
||||
},
|
||||
},
|
||||
},
|
||||
}
|
||||
|
||||
applyProbeSettingsToHC(probe, hc)
|
||||
|
||||
if hc.Protocol() != utils.ProtocolHTTPS || hc.Port != 8080 {
|
||||
t.Errorf("Basic HC settings changed")
|
||||
}
|
||||
if hc.RequestPath != "/"+p {
|
||||
t.Errorf("Failed to apply probe's requestpath")
|
||||
}
|
||||
}
|
||||
|
|
|
|||
|
|
@ -20,6 +20,7 @@ import (
|
|||
"fmt"
|
||||
|
||||
compute "google.golang.org/api/compute/v1"
|
||||
api_v1 "k8s.io/client-go/pkg/api/v1"
|
||||
"k8s.io/client-go/tools/cache"
|
||||
|
||||
"k8s.io/ingress/controllers/gce/utils"
|
||||
|
|
@ -118,3 +119,21 @@ func (f *FakeBackendServices) GetHealth(name, instanceGroupLink string) (*comput
|
|||
return &compute.BackendServiceGroupHealth{
|
||||
HealthStatus: states}, nil
|
||||
}
|
||||
|
||||
// FakeProbeProvider implements the probeProvider interface for tests.
|
||||
type FakeProbeProvider struct {
|
||||
probes map[ServicePort]*api_v1.Probe
|
||||
}
|
||||
|
||||
// NewFakeProbeProvider returns a struct which satifies probeProvider interface
|
||||
func NewFakeProbeProvider(probes map[ServicePort]*api_v1.Probe) *FakeProbeProvider {
|
||||
return &FakeProbeProvider{probes}
|
||||
}
|
||||
|
||||
// GetProbe returns the probe for a given nodePort
|
||||
func (pp *FakeProbeProvider) GetProbe(port ServicePort) (*api_v1.Probe, error) {
|
||||
if probe, exists := pp.probes[port]; exists && probe.HTTPGet != nil {
|
||||
return probe, nil
|
||||
}
|
||||
return nil, nil
|
||||
}
|
||||
|
|
|
|||
|
|
@ -18,16 +18,23 @@ package backends
|
|||
|
||||
import (
|
||||
compute "google.golang.org/api/compute/v1"
|
||||
api_v1 "k8s.io/client-go/pkg/api/v1"
|
||||
)
|
||||
|
||||
// ProbeProvider retrieves a probe struct given a nodePort
|
||||
type probeProvider interface {
|
||||
GetProbe(sp ServicePort) (*api_v1.Probe, error)
|
||||
}
|
||||
|
||||
// BackendPool is an interface to manage a pool of kubernetes nodePort services
|
||||
// as gce backendServices, and sync them through the BackendServices interface.
|
||||
type BackendPool interface {
|
||||
Add(port int64) error
|
||||
Init(p probeProvider)
|
||||
Add(port ServicePort) error
|
||||
Get(port int64) (*compute.BackendService, error)
|
||||
Delete(port int64) error
|
||||
Sync(ports []int64) error
|
||||
GC(ports []int64) error
|
||||
Sync(ports []ServicePort) error
|
||||
GC(ports []ServicePort) error
|
||||
Shutdown() error
|
||||
Status(name string) string
|
||||
List() ([]interface{}, error)
|
||||
|
|
|
|||
Loading…
Add table
Add a link
Reference in a new issue