diff --git a/cmd/glbc/main.go b/cmd/glbc/main.go index 61957f91b9..399fa63d28 100644 --- a/cmd/glbc/main.go +++ b/cmd/glbc/main.go @@ -26,7 +26,7 @@ import ( flag "github.com/spf13/pflag" metav1 "k8s.io/apimachinery/pkg/apis/meta/v1" "k8s.io/ingress-gce/pkg/frontendconfig" - "k8s.io/ingress-gce/pkg/healthchecks" + "k8s.io/ingress-gce/pkg/healthchecks_l4" "k8s.io/ingress-gce/pkg/ingparams" "k8s.io/ingress-gce/pkg/l4lb" "k8s.io/ingress-gce/pkg/psc" @@ -276,7 +276,7 @@ func runControllers(ctx *ingctx.ControllerContext) { fwc := firewalls.NewFirewallController(ctx, flags.F.NodePortRanges.Values()) - healthchecks.InitializeL4(ctx.Cloud, ctx) + healthchecks_l4.Initialize(ctx.Cloud, ctx) if flags.F.RunL4Controller { l4Controller := l4lb.NewILBController(ctx, stopCh) diff --git a/pkg/healthchecks/interfaces.go b/pkg/healthchecks/interfaces.go index 0b9956be13..60cb8d9f79 100644 --- a/pkg/healthchecks/interfaces.go +++ b/pkg/healthchecks/interfaces.go @@ -17,15 +17,13 @@ limitations under the License. package healthchecks import ( + "github.com/GoogleCloudPlatform/k8s-cloud-provider/pkg/cloud/meta" computealpha "google.golang.org/api/compute/v0.alpha" computebeta "google.golang.org/api/compute/v0.beta" - compute "google.golang.org/api/compute/v1" + "google.golang.org/api/compute/v1" v1 "k8s.io/api/core/v1" "k8s.io/ingress-gce/pkg/translator" "k8s.io/ingress-gce/pkg/utils" - "k8s.io/ingress-gce/pkg/utils/namer" - - "github.com/GoogleCloudPlatform/k8s-cloud-provider/pkg/cloud/meta" ) // HealthCheckProvider is an interface to manage a single GCE health check. @@ -58,19 +56,3 @@ type HealthChecker interface { Delete(name string, scope meta.KeyType) error Get(name string, version meta.Version, scope meta.KeyType) (*translator.HealthCheck, error) } - -// L4HealthChecks defines methods for creating and deleting health checks (and their firewall rules) for l4 services -type L4HealthChecks interface { - // EnsureL4HealthCheck creates health check (and firewall rule) for l4 service - EnsureL4HealthCheck(svc *v1.Service, namer namer.L4ResourcesNamer, sharedHC bool, scope meta.KeyType, l4Type utils.L4LBType, nodeNames []string) *EnsureL4HealthCheckResult - // DeleteHealthCheck deletes health check (and firewall rule) for l4 service - DeleteHealthCheck(svc *v1.Service, namer namer.L4ResourcesNamer, sharedHC bool, scope meta.KeyType, l4Type utils.L4LBType) (string, error) -} - -type EnsureL4HealthCheckResult struct { - HCName string - HCLink string - HCFirewallRuleName string - GceResourceInError string - Err error -} diff --git a/pkg/healthchecks/healthchecks_l4.go b/pkg/healthchecks_l4/healthchecks_l4.go similarity index 86% rename from pkg/healthchecks/healthchecks_l4.go rename to pkg/healthchecks_l4/healthchecks_l4.go index 6d7fc1effc..94c869d468 100644 --- a/pkg/healthchecks/healthchecks_l4.go +++ b/pkg/healthchecks_l4/healthchecks_l4.go @@ -14,14 +14,13 @@ See the License for the specific language governing permissions and limitations under the License. */ -package healthchecks +package healthchecks_l4 import ( "fmt" "strconv" "sync" - cloudprovider "github.com/GoogleCloudPlatform/k8s-cloud-provider/pkg/cloud" "github.com/GoogleCloudPlatform/k8s-cloud-provider/pkg/cloud/meta" corev1 "k8s.io/api/core/v1" "k8s.io/apimachinery/pkg/types" @@ -49,20 +48,21 @@ const ( var ( // instanceLock to prevent duplicate initialization. instanceLock = &sync.Mutex{} - // instance is a singleton instance, created by InitializeL4 + // instance is a singleton instance, created by Initialize instance *l4HealthChecks ) type l4HealthChecks struct { // sharedResourceLock serializes operations on the healthcheck and firewall // resources shared across multiple Services. + hcProvider HealthChecksProvider sharedResourcesLock sync.Mutex cloud *gce.Cloud recorderFactory events.RecorderProducer } -// InitializeL4 creates singleton instance, must be run before L4() func -func InitializeL4(cloud *gce.Cloud, recorderFactory events.RecorderProducer) { +// Initialize creates singleton instance, must be run before GetInstance() func +func Initialize(cloud *gce.Cloud, recorderFactory events.RecorderProducer) { instanceLock.Lock() defer instanceLock.Unlock() @@ -74,21 +74,23 @@ func InitializeL4(cloud *gce.Cloud, recorderFactory events.RecorderProducer) { instance = &l4HealthChecks{ cloud: cloud, recorderFactory: recorderFactory, + hcProvider: NewHealthChecks(cloud, meta.VersionGA), } klog.V(3).Infof("Initialized L4 Healthchecks") } -// FakeL4 creates instance of l4HealthChecks> USe for test only. -func FakeL4(cloud *gce.Cloud, recorderFactory events.RecorderProducer) *l4HealthChecks { +// Fake creates instance of l4HealthChecks. Use for test only. +func Fake(cloud *gce.Cloud, recorderFactory events.RecorderProducer) *l4HealthChecks { instance = &l4HealthChecks{ cloud: cloud, recorderFactory: recorderFactory, + hcProvider: NewHealthChecks(cloud, meta.VersionGA), } return instance } -// L4 returns singleton instance, must be run after InitializeL4 -func L4() *l4HealthChecks { +// GetInstance returns singleton instance, must be run after Initialize +func GetInstance() *l4HealthChecks { return instance } @@ -153,7 +155,7 @@ func (l4hc *l4HealthChecks) DeleteHealthCheck(svc *corev1.Service, namer namer.L defer l4hc.sharedResourcesLock.Unlock() } - err := utils.IgnoreHTTPNotFound(l4hc.deleteHealthCheck(hcName, scope)) + err := l4hc.hcProvider.Delete(hcName, scope) if err != nil { // Ignore deletion error due to health check in use by another resource. if !utils.IsInUsedByError(err) { @@ -168,17 +170,11 @@ func (l4hc *l4HealthChecks) DeleteHealthCheck(svc *corev1.Service, namer namer.L } func (l4hc *l4HealthChecks) ensureL4HealthCheckInternal(hcName string, svcName types.NamespacedName, shared bool, path string, port int32, scope meta.KeyType, l4Type utils.L4LBType) (*composite.HealthCheck, string, error) { - selfLink := "" - key, err := composite.CreateKey(l4hc.cloud, hcName, scope) + hc, err := l4hc.hcProvider.Get(hcName, scope) if err != nil { - return nil, selfLink, fmt.Errorf("Failed to create key for healthcheck with name %s for service %s", hcName, svcName.String()) - } - hc, err := composite.GetHealthCheck(l4hc.cloud, key, meta.VersionGA) - if err != nil { - if !utils.IsNotFoundError(err) { - return nil, selfLink, err - } + return nil, "", err } + var region string if scope == meta.Regional { region = l4hc.cloud.Region() @@ -188,14 +184,17 @@ func (l4hc *l4HealthChecks) ensureL4HealthCheckInternal(hcName string, svcName t if hc == nil { // Create the healthcheck klog.V(2).Infof("Creating healthcheck %s for service %s, shared = %v. Expected healthcheck: %v", hcName, svcName, shared, expectedHC) - err = composite.CreateHealthCheck(l4hc.cloud, key, expectedHC) + err = l4hc.hcProvider.Create(expectedHC) + if err != nil { + return nil, "", err + } + selfLink, err := l4hc.hcProvider.SelfLink(expectedHC.Name, scope) if err != nil { - return nil, selfLink, err + return nil, "", err } - selfLink = cloudprovider.SelfLink(meta.VersionGA, l4hc.cloud.ProjectID(), "healthChecks", key) return expectedHC, selfLink, nil } - selfLink = hc.SelfLink + selfLink := hc.SelfLink if !needToUpdateHealthChecks(hc, expectedHC) { // nothing to do klog.V(3).Infof("Healthcheck %v already exists", hcName) @@ -203,7 +202,7 @@ func (l4hc *l4HealthChecks) ensureL4HealthCheckInternal(hcName string, svcName t } mergeHealthChecks(hc, expectedHC) klog.V(2).Infof("Updating healthcheck %s for service %s, updated healthcheck: %v", hcName, svcName, expectedHC) - err = composite.UpdateHealthCheck(l4hc.cloud, key, expectedHC) + err = l4hc.hcProvider.Update(expectedHC.Name, scope, expectedHC) if err != nil { return nil, selfLink, err } @@ -226,14 +225,6 @@ func (l4hc *l4HealthChecks) ensureFirewall(svc *corev1.Service, hcFwName string, return firewalls.EnsureL4LBFirewallForHc(svc, sharedHC, &hcFWRParams, l4hc.cloud, l4hc.recorderFactory.Recorder(svc.Namespace)) } -func (l4hc *l4HealthChecks) deleteHealthCheck(name string, scope meta.KeyType) error { - key, err := composite.CreateKey(l4hc.cloud, name, scope) - if err != nil { - return fmt.Errorf("Failed to create composite key for healthcheck %s - %w", name, err) - } - return composite.DeleteHealthCheck(l4hc.cloud, key, meta.VersionGA) -} - func (l4hc *l4HealthChecks) deleteHealthCheckFirewall(svc *corev1.Service, hcName, hcFwName string, sharedHC bool, l4Type utils.L4LBType) (string, error) { namespacedName := types.NamespacedName{Name: svc.Name, Namespace: svc.Namespace} @@ -265,12 +256,12 @@ func (l4hc *l4HealthChecks) healthCheckFirewallSafeToDelete(hcName string, share if l4Type == utils.XLB { scopeToCheck = meta.Global } - key, err := composite.CreateKey(l4hc.cloud, hcName, scopeToCheck) + + hc, err := l4hc.hcProvider.Get(hcName, scopeToCheck) if err != nil { - return false, fmt.Errorf("Failed to create composite key for healthcheck %s - %w", hcName, err) + return false, fmt.Errorf("l4hc.hcProvider.Get(%s, %s) returned error %w, want nil", hcName, scopeToCheck, err) } - _, err = composite.GetHealthCheck(l4hc.cloud, key, meta.VersionGA) - return utils.IsNotFoundError(err), nil + return hc == nil, nil } func (l4hc *l4HealthChecks) deleteFirewall(name string, svc *corev1.Service) error { diff --git a/pkg/healthchecks/healthchecks_l4_test.go b/pkg/healthchecks_l4/healthchecks_l4_test.go similarity index 99% rename from pkg/healthchecks/healthchecks_l4_test.go rename to pkg/healthchecks_l4/healthchecks_l4_test.go index f0fccb53e1..d5e3c6a177 100644 --- a/pkg/healthchecks/healthchecks_l4_test.go +++ b/pkg/healthchecks_l4/healthchecks_l4_test.go @@ -14,7 +14,7 @@ See the License for the specific language governing permissions and limitations under the License. */ -package healthchecks +package healthchecks_l4 import ( "testing" @@ -109,7 +109,7 @@ func TestCompareHealthChecks(t *testing.T) { } } -func TestCreateHealthCheck(t *testing.T) { +func TestNewHealthCheck(t *testing.T) { t.Parallel() namespaceName := types.NamespacedName{Name: "svc", Namespace: "default"} diff --git a/pkg/healthchecks_l4/interfaces.go b/pkg/healthchecks_l4/interfaces.go new file mode 100644 index 0000000000..ed0cdf21d1 --- /dev/null +++ b/pkg/healthchecks_l4/interfaces.go @@ -0,0 +1,33 @@ +package healthchecks_l4 + +import ( + "github.com/GoogleCloudPlatform/k8s-cloud-provider/pkg/cloud/meta" + v1 "k8s.io/api/core/v1" + "k8s.io/ingress-gce/pkg/composite" + "k8s.io/ingress-gce/pkg/utils" + "k8s.io/ingress-gce/pkg/utils/namer" +) + +// L4HealthChecks defines methods for creating and deleting health checks (and their firewall rules) for l4 services +type L4HealthChecks interface { + // EnsureL4HealthCheck creates health check (and firewall rule) for l4 service + EnsureL4HealthCheck(svc *v1.Service, namer namer.L4ResourcesNamer, sharedHC bool, scope meta.KeyType, l4Type utils.L4LBType, nodeNames []string) *EnsureL4HealthCheckResult + // DeleteHealthCheck deletes health check (and firewall rule) for l4 service + DeleteHealthCheck(svc *v1.Service, namer namer.L4ResourcesNamer, sharedHC bool, scope meta.KeyType, l4Type utils.L4LBType) (string, error) +} + +type EnsureL4HealthCheckResult struct { + HCName string + HCLink string + HCFirewallRuleName string + GceResourceInError string + Err error +} + +type HealthChecksProvider interface { + Get(name string, scope meta.KeyType) (*composite.HealthCheck, error) + Create(healthCheck *composite.HealthCheck) error + Update(name string, scope meta.KeyType, updatedHealthCheck *composite.HealthCheck) error + Delete(name string, scope meta.KeyType) error + SelfLink(name string, scope meta.KeyType) (string, error) +} diff --git a/pkg/healthchecks_l4/provider.go b/pkg/healthchecks_l4/provider.go new file mode 100644 index 0000000000..15d0441ebe --- /dev/null +++ b/pkg/healthchecks_l4/provider.go @@ -0,0 +1,86 @@ +package healthchecks_l4 + +import ( + "fmt" + + cloudprovider "github.com/GoogleCloudPlatform/k8s-cloud-provider/pkg/cloud" + "github.com/GoogleCloudPlatform/k8s-cloud-provider/pkg/cloud/meta" + "k8s.io/ingress-gce/pkg/composite" + "k8s.io/ingress-gce/pkg/utils" + "k8s.io/legacy-cloud-providers/gce" +) + +type HealthChecks struct { + cloud *gce.Cloud + version meta.Version +} + +func NewHealthChecks(cloud *gce.Cloud, version meta.Version) *HealthChecks { + return &HealthChecks{ + cloud: cloud, + version: version, + } +} + +func (hc *HealthChecks) createKey(name string, scope meta.KeyType) (*meta.Key, error) { + return composite.CreateKey(hc.cloud, name, scope) +} + +func (hc *HealthChecks) Get(name string, scope meta.KeyType) (*composite.HealthCheck, error) { + key, err := hc.createKey(name, scope) + if err != nil { + return nil, fmt.Errorf("hc.createKey(%s, %s) returned error %w, want nil", name, scope, err) + } + healthCheck, err := composite.GetHealthCheck(hc.cloud, key, hc.version) + if err != nil { + if utils.IsNotFoundError(err) { + return nil, nil + } + return nil, fmt.Errorf("composite.GetHealthCheck(_, %v, %v) returned error %w, want nil", key, meta.VersionGA, err) + } + return healthCheck, nil +} + +func (hc *HealthChecks) Create(healthCheck *composite.HealthCheck) error { + key, err := hc.createKey(healthCheck.Name, healthCheck.Scope) + if err != nil { + return fmt.Errorf("hc.createKey(%s, %s) returned error: %w, want nil", healthCheck.Name, healthCheck.Scope, err) + } + + err = composite.CreateHealthCheck(hc.cloud, key, healthCheck) + if err != nil { + return fmt.Errorf("composite.CreateHealthCheck(_, %s, %v) returned error %w, want nil", key, healthCheck, err) + } + return nil +} + +func (hc *HealthChecks) Update(name string, scope meta.KeyType, updatedHealthCheck *composite.HealthCheck) error { + key, err := hc.createKey(name, scope) + if err != nil { + return fmt.Errorf("hc.createKey(%s, %s) returned error: %w, want nil", name, scope, err) + } + + err = composite.UpdateHealthCheck(hc.cloud, key, updatedHealthCheck) + if err != nil { + return fmt.Errorf("composite.UpdateHealthCheck(_, %s, %v) returned error %w, want nil", key, updatedHealthCheck, err) + } + return nil +} + +func (hc *HealthChecks) Delete(name string, scope meta.KeyType) error { + key, err := hc.createKey(name, scope) + if err != nil { + return fmt.Errorf("hc.createKey(%s, %s) returned error %w, want nil", name, scope, err) + } + + return utils.IgnoreHTTPNotFound(composite.DeleteHealthCheck(hc.cloud, key, hc.version)) +} + +func (hc *HealthChecks) SelfLink(name string, scope meta.KeyType) (string, error) { + key, err := hc.createKey(name, scope) + if err != nil { + return "", fmt.Errorf("hc.createKey(%s, %s) returned error %w, want nil", name, scope, err) + } + + return cloudprovider.SelfLink(meta.VersionGA, hc.cloud.ProjectID(), "healthChecks", key), nil +} diff --git a/pkg/healthchecks_l4/provider_test.go b/pkg/healthchecks_l4/provider_test.go new file mode 100644 index 0000000000..6263699aa3 --- /dev/null +++ b/pkg/healthchecks_l4/provider_test.go @@ -0,0 +1,256 @@ +package healthchecks_l4 + +import ( + "testing" + + "github.com/GoogleCloudPlatform/k8s-cloud-provider/pkg/cloud/meta" + "github.com/google/go-cmp/cmp" + "github.com/google/go-cmp/cmp/cmpopts" + "k8s.io/ingress-gce/pkg/composite" + "k8s.io/ingress-gce/pkg/utils" + "k8s.io/legacy-cloud-providers/gce" +) + +func TestCreateHealthCheck(t *testing.T) { + testCases := []struct { + healthCheck *composite.HealthCheck + desc string + }{ + { + healthCheck: &composite.HealthCheck{ + Name: "regional-hc", + Scope: meta.Regional, + }, + desc: "Test creating regional health check", + }, + { + healthCheck: &composite.HealthCheck{ + Name: "global-hc", + Scope: meta.Global, + }, + desc: "Test creating glbal health check", + }, + } + + for _, tc := range testCases { + t.Run(tc.desc, func(t *testing.T) { + fakeGCE := gce.NewFakeGCECloud(gce.DefaultTestClusterValues()) + hc := NewHealthChecks(fakeGCE, meta.VersionGA) + + err := hc.Create(tc.healthCheck) + if err != nil { + t.Fatalf("hc.Create(%v), returned error %v, want nil", tc.healthCheck, err) + } + + verifyHealthCheckExists(t, fakeGCE, tc.healthCheck.Name, tc.healthCheck.Scope) + }) + } +} + +func TestGetHealthCheck(t *testing.T) { + regionalHealthCheck := &composite.HealthCheck{ + Name: "regional-hc", + Version: meta.VersionGA, + Scope: meta.Regional, + } + globalHealthCheck := &composite.HealthCheck{ + Name: "global-hc", + Version: meta.VersionGA, + Scope: meta.Global, + } + + testCases := []struct { + existingHealthChecks []*composite.HealthCheck + getHCName string + getHCScope meta.KeyType + expectedHealthCheck *composite.HealthCheck + desc string + }{ + { + existingHealthChecks: []*composite.HealthCheck{regionalHealthCheck, globalHealthCheck}, + getHCName: regionalHealthCheck.Name, + getHCScope: regionalHealthCheck.Scope, + expectedHealthCheck: regionalHealthCheck, + desc: "Test getting regional health check", + }, + { + existingHealthChecks: []*composite.HealthCheck{regionalHealthCheck, globalHealthCheck}, + getHCName: globalHealthCheck.Name, + getHCScope: globalHealthCheck.Scope, + expectedHealthCheck: globalHealthCheck, + desc: "Test getting global health check", + }, + { + existingHealthChecks: []*composite.HealthCheck{regionalHealthCheck, globalHealthCheck}, + getHCName: "non-existent-hc", + getHCScope: meta.Global, + expectedHealthCheck: nil, + desc: "Test getting non existent global health check", + }, + { + existingHealthChecks: []*composite.HealthCheck{regionalHealthCheck, globalHealthCheck}, + getHCName: "non-existent-hc", + getHCScope: meta.Regional, + expectedHealthCheck: nil, + desc: "Test getting non existent regional health check", + }, + { + existingHealthChecks: []*composite.HealthCheck{regionalHealthCheck, globalHealthCheck}, + getHCName: regionalHealthCheck.Name, + getHCScope: meta.Global, + expectedHealthCheck: nil, + desc: "Test getting existent regional health check, but providing global scope", + }, + { + existingHealthChecks: []*composite.HealthCheck{regionalHealthCheck, globalHealthCheck}, + getHCName: globalHealthCheck.Name, + getHCScope: meta.Regional, + expectedHealthCheck: nil, + desc: "Test getting existent global health check, but providing regional scope", + }, + } + + for _, tc := range testCases { + t.Run(tc.desc, func(t *testing.T) { + fakeGCE := gce.NewFakeGCECloud(gce.DefaultTestClusterValues()) + for _, hc := range tc.existingHealthChecks { + mustCreateHealthCheck(t, fakeGCE, hc) + } + hcp := NewHealthChecks(fakeGCE, meta.VersionGA) + + hc, err := hcp.Get(tc.getHCName, tc.getHCScope) + if err != nil { + t.Fatalf("hcp.Get(%v), returned error %v, want nil", tc.getHCName, err) + } + + // Scope field gets removed (but region added), after creating health check + ignoreFields := cmpopts.IgnoreFields(composite.HealthCheck{}, "SelfLink", "Region", "Scope") + if !cmp.Equal(hc, tc.expectedHealthCheck, ignoreFields) { + diff := cmp.Diff(hc, tc.expectedHealthCheck, ignoreFields) + t.Errorf("hcp.Get(s) returned %v, not equal to expectedHealthCheck %v, diff: %v", hc, tc.expectedHealthCheck, diff) + } + }) + } +} + +func TestDeleteHealthCheck(t *testing.T) { + regionalHealthCheck := &composite.HealthCheck{ + Name: "regional-hc", + Version: meta.VersionGA, + Scope: meta.Regional, + } + globalHealthCheck := &composite.HealthCheck{ + Name: "global-hc", + Version: meta.VersionGA, + Scope: meta.Global, + } + + testCases := []struct { + existingHealthChecks []*composite.HealthCheck + deleteHCName string + deleteHCScope meta.KeyType + shouldExistHealthChecks []*composite.HealthCheck + desc string + }{ + { + existingHealthChecks: []*composite.HealthCheck{regionalHealthCheck, globalHealthCheck}, + deleteHCName: regionalHealthCheck.Name, + deleteHCScope: regionalHealthCheck.Scope, + shouldExistHealthChecks: []*composite.HealthCheck{globalHealthCheck}, + desc: "Delete regional health check", + }, + { + existingHealthChecks: []*composite.HealthCheck{regionalHealthCheck, globalHealthCheck}, + deleteHCName: globalHealthCheck.Name, + deleteHCScope: globalHealthCheck.Scope, + shouldExistHealthChecks: []*composite.HealthCheck{regionalHealthCheck}, + desc: "Delete global health check", + }, + { + existingHealthChecks: []*composite.HealthCheck{regionalHealthCheck, globalHealthCheck}, + deleteHCName: "non-existent", + deleteHCScope: meta.Regional, + shouldExistHealthChecks: []*composite.HealthCheck{regionalHealthCheck, globalHealthCheck}, + desc: "Delete non existent healthCheck", + }, + { + existingHealthChecks: []*composite.HealthCheck{regionalHealthCheck, globalHealthCheck}, + deleteHCName: globalHealthCheck.Name, + deleteHCScope: meta.Regional, + shouldExistHealthChecks: []*composite.HealthCheck{regionalHealthCheck, globalHealthCheck}, + desc: "Delete global health check name, but using regional scope", + }, + { + existingHealthChecks: []*composite.HealthCheck{regionalHealthCheck, globalHealthCheck}, + deleteHCName: regionalHealthCheck.Name, + deleteHCScope: meta.Global, + shouldExistHealthChecks: []*composite.HealthCheck{regionalHealthCheck, globalHealthCheck}, + desc: "Delete regional health check name, but using global scope", + }, + } + + for _, tc := range testCases { + t.Run(tc.desc, func(t *testing.T) { + fakeGCE := gce.NewFakeGCECloud(gce.DefaultTestClusterValues()) + for _, hc := range tc.existingHealthChecks { + mustCreateHealthCheck(t, fakeGCE, hc) + } + hc := NewHealthChecks(fakeGCE, meta.VersionGA) + + err := hc.Delete(tc.deleteHCName, tc.deleteHCScope) + if err != nil { + t.Fatalf("hc.Delete(%v), returned error %v, want nil", tc.deleteHCName, err) + } + + verifyHealthCheckNotExists(t, fakeGCE, tc.deleteHCName, tc.deleteHCScope) + for _, hc := range tc.shouldExistHealthChecks { + verifyHealthCheckExists(t, fakeGCE, hc.Name, hc.Scope) + } + }) + } +} + +func verifyHealthCheckExists(t *testing.T, cloud *gce.Cloud, name string, scope meta.KeyType) { + t.Helper() + verifyHealthCheckShouldExist(t, cloud, name, scope, true) +} + +func verifyHealthCheckNotExists(t *testing.T, cloud *gce.Cloud, name string, scope meta.KeyType) { + t.Helper() + verifyHealthCheckShouldExist(t, cloud, name, scope, false) +} + +func verifyHealthCheckShouldExist(t *testing.T, cloud *gce.Cloud, name string, scope meta.KeyType, shouldExist bool) { + t.Helper() + + key, err := composite.CreateKey(cloud, name, scope) + if err != nil { + t.Fatalf("Failed to create key for fetching health check %s, err: %v", name, err) + } + _, err = composite.GetHealthCheck(cloud, key, meta.VersionGA) + if err != nil { + if utils.IsNotFoundError(err) { + if shouldExist { + t.Errorf("Health check %s in scope %s was not found", name, scope) + } + return + } + t.Fatalf("composite.GetHealthCheck(_, %v, %v) returned error %v, want nil", key, meta.VersionGA, err) + } + if !shouldExist { + t.Errorf("Health Check %s in scope %s exists, expected to be not found", name, scope) + } +} + +func mustCreateHealthCheck(t *testing.T, cloud *gce.Cloud, hc *composite.HealthCheck) { + t.Helper() + + key, err := composite.CreateKey(cloud, hc.Name, hc.Scope) + if err != nil { + t.Fatalf("composite.CreateKey(_, %s, %s) returned error %v, want nil", hc.Name, hc.Scope, err) + } + err = composite.CreateHealthCheck(cloud, key, hc) + if err != nil { + t.Fatalf("composite.CreateHealthCheck(_, %s, %v) returned error %v, want nil", key, hc, err) + } +} diff --git a/pkg/l4lb/l4controller_test.go b/pkg/l4lb/l4controller_test.go index d49c989cb2..679a0e63d1 100644 --- a/pkg/l4lb/l4controller_test.go +++ b/pkg/l4lb/l4controller_test.go @@ -19,14 +19,13 @@ package l4lb import ( context2 "context" "fmt" - "k8s.io/ingress-gce/pkg/healthchecks" + "net/http" "testing" "time" + "k8s.io/ingress-gce/pkg/healthchecks_l4" "k8s.io/ingress-gce/pkg/loadbalancers" - "net/http" - "github.com/GoogleCloudPlatform/k8s-cloud-provider/pkg/cloud" "github.com/GoogleCloudPlatform/k8s-cloud-provider/pkg/cloud/meta" "github.com/GoogleCloudPlatform/k8s-cloud-provider/pkg/cloud/mock" @@ -71,7 +70,7 @@ func newServiceController(t *testing.T, fakeGCE *gce.Cloud) *L4Controller { for _, n := range nodes { ctx.NodeInformer.GetIndexer().Add(n) } - healthchecks.FakeL4(ctx.Cloud, ctx) + healthchecks_l4.Fake(ctx.Cloud, ctx) return NewILBController(ctx, stopCh) } diff --git a/pkg/l4lb/l4netlbcontroller_test.go b/pkg/l4lb/l4netlbcontroller_test.go index 1f6edf7021..03d4fe8ed2 100644 --- a/pkg/l4lb/l4netlbcontroller_test.go +++ b/pkg/l4lb/l4netlbcontroller_test.go @@ -34,7 +34,6 @@ import ( "google.golang.org/api/googleapi" v1 "k8s.io/api/core/v1" metav1 "k8s.io/apimachinery/pkg/apis/meta/v1" - "k8s.io/apimachinery/pkg/types" "k8s.io/client-go/kubernetes/fake" "k8s.io/client-go/tools/record" "k8s.io/client-go/util/retry" @@ -42,7 +41,7 @@ import ( "k8s.io/ingress-gce/pkg/annotations" "k8s.io/ingress-gce/pkg/composite" ingctx "k8s.io/ingress-gce/pkg/context" - "k8s.io/ingress-gce/pkg/healthchecks" + "k8s.io/ingress-gce/pkg/healthchecks_l4" "k8s.io/ingress-gce/pkg/loadbalancers" "k8s.io/ingress-gce/pkg/test" "k8s.io/ingress-gce/pkg/utils" @@ -240,7 +239,7 @@ func newL4NetLBServiceController() *L4NetLBController { for _, n := range nodes { ctx.NodeInformer.GetIndexer().Add(n) } - healthchecks.FakeL4(ctx.Cloud, ctx) + healthchecks_l4.Fake(ctx.Cloud, ctx) return NewL4NetLBController(ctx, stopCh) } @@ -873,7 +872,7 @@ func TestHealthCheckWhenExternalTrafficPolicyWasUpdated(t *testing.T) { // delete shared health check if is created, update service to Cluster and // check that non-shared health check was created hcNameShared, _ := lc.namer.L4HealthCheck(svc.Namespace, svc.Name, true) - healthchecks.FakeL4(lc.ctx.Cloud, lc.ctx).DeleteHealthCheck(svc, lc.namer, true, meta.Regional, utils.XLB) + healthchecks_l4.Fake(lc.ctx.Cloud, lc.ctx).DeleteHealthCheck(svc, lc.namer, true, meta.Regional, utils.XLB) // Update ExternalTrafficPolicy to Cluster check if shared HC was created err = updateAndAssertExternalTrafficPolicy(newSvc, lc, v1.ServiceExternalTrafficPolicyTypeCluster, hcNameShared) if err != nil { diff --git a/pkg/loadbalancers/l4.go b/pkg/loadbalancers/l4.go index 93d5f40f21..99e4ae7493 100644 --- a/pkg/loadbalancers/l4.go +++ b/pkg/loadbalancers/l4.go @@ -31,7 +31,7 @@ import ( "k8s.io/ingress-gce/pkg/backends" "k8s.io/ingress-gce/pkg/composite" "k8s.io/ingress-gce/pkg/firewalls" - "k8s.io/ingress-gce/pkg/healthchecks" + "k8s.io/ingress-gce/pkg/healthchecks_l4" "k8s.io/ingress-gce/pkg/metrics" "k8s.io/ingress-gce/pkg/utils" "k8s.io/ingress-gce/pkg/utils/namer" @@ -51,7 +51,7 @@ type L4 struct { Service *corev1.Service ServicePort utils.ServicePort NamespacedName types.NamespacedName - l4HealthChecks healthchecks.L4HealthChecks + l4HealthChecks healthchecks_l4.L4HealthChecks } // L4ILBSyncResult contains information about the outcome of an L4 ILB sync. It stores the list of resource name annotations, @@ -74,7 +74,7 @@ func NewL4Handler(service *corev1.Service, cloud *gce.Cloud, scope meta.KeyType, namer: namer, recorder: recorder, Service: service, - l4HealthChecks: healthchecks.L4(), + l4HealthChecks: healthchecks_l4.GetInstance(), } l.NamespacedName = types.NamespacedName{Name: service.Name, Namespace: service.Namespace} l.backendPool = backends.NewPool(l.cloud, l.namer) diff --git a/pkg/loadbalancers/l4_test.go b/pkg/loadbalancers/l4_test.go index 10604783ee..041d7b5b05 100644 --- a/pkg/loadbalancers/l4_test.go +++ b/pkg/loadbalancers/l4_test.go @@ -23,11 +23,10 @@ import ( "strings" "testing" - "k8s.io/ingress-gce/pkg/healthchecks" - "google.golang.org/api/compute/v1" "k8s.io/ingress-gce/pkg/backends" "k8s.io/ingress-gce/pkg/firewalls" + "k8s.io/ingress-gce/pkg/healthchecks_l4" "k8s.io/ingress-gce/pkg/utils" "github.com/GoogleCloudPlatform/k8s-cloud-provider/pkg/cloud" @@ -69,7 +68,7 @@ func TestEnsureInternalBackendServiceUpdates(t *testing.T) { svc := test.NewL4ILBService(false, 8080) namer := namer_util.NewL4Namer(kubeSystemUID, nil) l := NewL4Handler(svc, fakeGCE, meta.Regional, namer, record.NewFakeRecorder(100)) - l.l4HealthChecks = healthchecks.FakeL4(fakeGCE, &test.FakeRecorderSource{}) + l.l4HealthChecks = healthchecks_l4.Fake(fakeGCE, &test.FakeRecorderSource{}) bsName, _ := l.namer.L4Backend(l.Service.Namespace, l.Service.Name) _, err := l.backendPool.EnsureL4BackendService(bsName, "", "TCP", string(svc.Spec.SessionAffinity), string(cloud.SchemeInternal), l.NamespacedName, meta.VersionGA) @@ -120,7 +119,7 @@ func TestEnsureInternalLoadBalancer(t *testing.T) { svc := test.NewL4ILBService(false, 8080) namer := namer_util.NewL4Namer(kubeSystemUID, nil) l := NewL4Handler(svc, fakeGCE, meta.Regional, namer, record.NewFakeRecorder(100)) - l.l4HealthChecks = healthchecks.FakeL4(fakeGCE, &test.FakeRecorderSource{}) + l.l4HealthChecks = healthchecks_l4.Fake(fakeGCE, &test.FakeRecorderSource{}) if _, err := test.CreateAndInsertNodes(l.cloud, nodeNames, vals.ZoneName); err != nil { t.Errorf("Unexpected error when adding nodes %v", err) @@ -177,7 +176,7 @@ func TestEnsureInternalLoadBalancerTypeChange(t *testing.T) { svc := test.NewL4ILBService(false, 8080) namer := namer_util.NewL4Namer(kubeSystemUID, nil) l := NewL4Handler(svc, fakeGCE, meta.Regional, namer, record.NewFakeRecorder(100)) - l.l4HealthChecks = healthchecks.FakeL4(fakeGCE, &test.FakeRecorderSource{}) + l.l4HealthChecks = healthchecks_l4.Fake(fakeGCE, &test.FakeRecorderSource{}) if _, err := test.CreateAndInsertNodes(l.cloud, nodeNames, vals.ZoneName); err != nil { t.Errorf("Unexpected error when adding nodes %v", err) @@ -211,7 +210,7 @@ func TestEnsureInternalLoadBalancerWithExistingResources(t *testing.T) { namer := namer_util.NewL4Namer(kubeSystemUID, nil) l := NewL4Handler(svc, fakeGCE, meta.Regional, namer, record.NewFakeRecorder(100)) - l.l4HealthChecks = healthchecks.FakeL4(fakeGCE, &test.FakeRecorderSource{}) + l.l4HealthChecks = healthchecks_l4.Fake(fakeGCE, &test.FakeRecorderSource{}) if _, err := test.CreateAndInsertNodes(l.cloud, nodeNames, vals.ZoneName); err != nil { t.Errorf("Unexpected error when adding nodes %v", err) @@ -254,7 +253,7 @@ func TestEnsureInternalLoadBalancerClearPreviousResources(t *testing.T) { svc := test.NewL4ILBService(true, 8080) namer := namer_util.NewL4Namer(kubeSystemUID, nil) l := NewL4Handler(svc, fakeGCE, meta.Regional, namer, record.NewFakeRecorder(100)) - l.l4HealthChecks = healthchecks.FakeL4(fakeGCE, &test.FakeRecorderSource{}) + l.l4HealthChecks = healthchecks_l4.Fake(fakeGCE, &test.FakeRecorderSource{}) _, err := test.CreateAndInsertNodes(l.cloud, nodeNames, vals.ZoneName) if err != nil { @@ -374,7 +373,7 @@ func TestUpdateResourceLinks(t *testing.T) { svc := test.NewL4ILBService(true, 8080) namer := namer_util.NewL4Namer(kubeSystemUID, nil) l := NewL4Handler(svc, fakeGCE, meta.Regional, namer, record.NewFakeRecorder(100)) - l.l4HealthChecks = healthchecks.FakeL4(fakeGCE, &test.FakeRecorderSource{}) + l.l4HealthChecks = healthchecks_l4.Fake(fakeGCE, &test.FakeRecorderSource{}) _, err := test.CreateAndInsertNodes(l.cloud, nodeNames, vals.ZoneName) if err != nil { @@ -452,7 +451,7 @@ func TestEnsureInternalLoadBalancerHealthCheckConfigurable(t *testing.T) { svc := test.NewL4ILBService(true, 8080) namer := namer_util.NewL4Namer(kubeSystemUID, nil) l := NewL4Handler(svc, fakeGCE, meta.Regional, namer, record.NewFakeRecorder(100)) - l.l4HealthChecks = healthchecks.FakeL4(fakeGCE, &test.FakeRecorderSource{}) + l.l4HealthChecks = healthchecks_l4.Fake(fakeGCE, &test.FakeRecorderSource{}) _, err := test.CreateAndInsertNodes(l.cloud, nodeNames, vals.ZoneName) if err != nil { @@ -495,7 +494,7 @@ func TestEnsureInternalLoadBalancerDeleted(t *testing.T) { svc := test.NewL4ILBService(false, 8080) namer := namer_util.NewL4Namer(kubeSystemUID, nil) l := NewL4Handler(svc, fakeGCE, meta.Regional, namer, record.NewFakeRecorder(100)) - l.l4HealthChecks = healthchecks.FakeL4(fakeGCE, &test.FakeRecorderSource{}) + l.l4HealthChecks = healthchecks_l4.Fake(fakeGCE, &test.FakeRecorderSource{}) if _, err := test.CreateAndInsertNodes(l.cloud, nodeNames, vals.ZoneName); err != nil { t.Errorf("Unexpected error when adding nodes %v", err) @@ -527,7 +526,7 @@ func TestEnsureInternalLoadBalancerDeletedTwiceDoesNotError(t *testing.T) { svc := test.NewL4ILBService(false, 8080) namer := namer_util.NewL4Namer(kubeSystemUID, nil) l := NewL4Handler(svc, fakeGCE, meta.Regional, namer, record.NewFakeRecorder(100)) - l.l4HealthChecks = healthchecks.FakeL4(fakeGCE, &test.FakeRecorderSource{}) + l.l4HealthChecks = healthchecks_l4.Fake(fakeGCE, &test.FakeRecorderSource{}) if _, err := test.CreateAndInsertNodes(l.cloud, nodeNames, vals.ZoneName); err != nil { t.Errorf("Unexpected error when adding nodes %v", err) @@ -644,7 +643,7 @@ func TestHealthCheckFirewallDeletionWithNetLB(t *testing.T) { func ensureService(fakeGCE *gce.Cloud, namer *namer_util.L4Namer, nodeNames []string, zoneName string, port int, t *testing.T) (*v1.Service, *L4, *L4ILBSyncResult) { svc := test.NewL4ILBService(false, 8080) l := NewL4Handler(svc, fakeGCE, meta.Regional, namer, record.NewFakeRecorder(100)) - l.l4HealthChecks = healthchecks.FakeL4(fakeGCE, &test.FakeRecorderSource{}) + l.l4HealthChecks = healthchecks_l4.Fake(fakeGCE, &test.FakeRecorderSource{}) if _, err := test.CreateAndInsertNodes(l.cloud, nodeNames, zoneName); err != nil { return nil, nil, &L4ILBSyncResult{Error: fmt.Errorf("Unexpected error when adding nodes %v", err)} @@ -669,7 +668,7 @@ func TestEnsureInternalLoadBalancerWithSpecialHealthCheck(t *testing.T) { svc := test.NewL4ILBService(false, 8080) namer := namer_util.NewL4Namer(kubeSystemUID, nil) l := NewL4Handler(svc, fakeGCE, meta.Regional, namer, record.NewFakeRecorder(100)) - l.l4HealthChecks = healthchecks.FakeL4(fakeGCE, &test.FakeRecorderSource{}) + l.l4HealthChecks = healthchecks_l4.Fake(fakeGCE, &test.FakeRecorderSource{}) if _, err := test.CreateAndInsertNodes(l.cloud, nodeNames, vals.ZoneName); err != nil { t.Errorf("Unexpected error when adding nodes %v", err) @@ -776,7 +775,7 @@ func TestEnsureInternalLoadBalancerErrors(t *testing.T) { fakeGCE := getFakeGCECloud(gce.DefaultTestClusterValues()) l := NewL4Handler(params.service, fakeGCE, meta.Regional, namer, record.NewFakeRecorder(100)) - l.l4HealthChecks = healthchecks.FakeL4(fakeGCE, &test.FakeRecorderSource{}) + l.l4HealthChecks = healthchecks_l4.Fake(fakeGCE, &test.FakeRecorderSource{}) //lbName := l.namer.L4Backend(params.service.Namespace, params.service.Name) frName := l.GetFRName() @@ -859,7 +858,7 @@ func TestEnsureInternalLoadBalancerEnableGlobalAccess(t *testing.T) { svc := test.NewL4ILBService(false, 8080) namer := namer_util.NewL4Namer(kubeSystemUID, nil) l := NewL4Handler(svc, fakeGCE, meta.Regional, namer, record.NewFakeRecorder(100)) - l.l4HealthChecks = healthchecks.FakeL4(fakeGCE, &test.FakeRecorderSource{}) + l.l4HealthChecks = healthchecks_l4.Fake(fakeGCE, &test.FakeRecorderSource{}) if _, err := test.CreateAndInsertNodes(l.cloud, nodeNames, vals.ZoneName); err != nil { t.Errorf("Unexpected error when adding nodes %v", err) @@ -941,7 +940,7 @@ func TestEnsureInternalLoadBalancerCustomSubnet(t *testing.T) { svc := test.NewL4ILBService(false, 8080) namer := namer_util.NewL4Namer(kubeSystemUID, nil) l := NewL4Handler(svc, fakeGCE, meta.Regional, namer, record.NewFakeRecorder(100)) - l.l4HealthChecks = healthchecks.FakeL4(fakeGCE, &test.FakeRecorderSource{}) + l.l4HealthChecks = healthchecks_l4.Fake(fakeGCE, &test.FakeRecorderSource{}) if _, err := test.CreateAndInsertNodes(l.cloud, nodeNames, vals.ZoneName); err != nil { t.Errorf("Unexpected error when adding nodes %v", err) @@ -1039,7 +1038,7 @@ func TestEnsureInternalFirewallPortRanges(t *testing.T) { svc := test.NewL4ILBService(false, 8080) namer := namer_util.NewL4Namer(kubeSystemUID, nil) l := NewL4Handler(svc, fakeGCE, meta.Regional, namer, record.NewFakeRecorder(100)) - l.l4HealthChecks = healthchecks.FakeL4(fakeGCE, &test.FakeRecorderSource{}) + l.l4HealthChecks = healthchecks_l4.Fake(fakeGCE, &test.FakeRecorderSource{}) fwName, _ := l.namer.L4Backend(l.Service.Namespace, l.Service.Name) tc := struct { @@ -1094,7 +1093,7 @@ func TestEnsureInternalLoadBalancerModifyProtocol(t *testing.T) { svc := test.NewL4ILBService(false, 8080) namer := namer_util.NewL4Namer(kubeSystemUID, nil) l := NewL4Handler(svc, fakeGCE, meta.Regional, namer, record.NewFakeRecorder(100)) - l.l4HealthChecks = healthchecks.FakeL4(fakeGCE, &test.FakeRecorderSource{}) + l.l4HealthChecks = healthchecks_l4.Fake(fakeGCE, &test.FakeRecorderSource{}) _, err := test.CreateAndInsertNodes(l.cloud, nodeNames, vals.ZoneName) if err != nil { @@ -1186,7 +1185,7 @@ func TestEnsureInternalLoadBalancerAllPorts(t *testing.T) { svc := test.NewL4ILBService(false, 8080) namer := namer_util.NewL4Namer(kubeSystemUID, nil) l := NewL4Handler(svc, fakeGCE, meta.Regional, namer, record.NewFakeRecorder(100)) - l.l4HealthChecks = healthchecks.FakeL4(fakeGCE, &test.FakeRecorderSource{}) + l.l4HealthChecks = healthchecks_l4.Fake(fakeGCE, &test.FakeRecorderSource{}) if _, err := test.CreateAndInsertNodes(l.cloud, nodeNames, vals.ZoneName); err != nil { t.Errorf("Unexpected error when adding nodes %v", err) diff --git a/pkg/loadbalancers/l4netlb.go b/pkg/loadbalancers/l4netlb.go index 0e03a8af06..7f3c921cbc 100644 --- a/pkg/loadbalancers/l4netlb.go +++ b/pkg/loadbalancers/l4netlb.go @@ -30,7 +30,7 @@ import ( "k8s.io/ingress-gce/pkg/backends" "k8s.io/ingress-gce/pkg/composite" "k8s.io/ingress-gce/pkg/firewalls" - "k8s.io/ingress-gce/pkg/healthchecks" + "k8s.io/ingress-gce/pkg/healthchecks_l4" "k8s.io/ingress-gce/pkg/metrics" "k8s.io/ingress-gce/pkg/utils" "k8s.io/ingress-gce/pkg/utils/namer" @@ -49,7 +49,7 @@ type L4NetLB struct { Service *corev1.Service ServicePort utils.ServicePort NamespacedName types.NamespacedName - l4HealthChecks healthchecks.L4HealthChecks + l4HealthChecks healthchecks_l4.L4HealthChecks } // L4NetLBSyncResult contains information about the outcome of an L4 NetLB sync. It stores the list of resource name annotations, @@ -89,7 +89,7 @@ func NewL4NetLB(service *corev1.Service, cloud *gce.Cloud, scope meta.KeyType, n Service: service, NamespacedName: types.NamespacedName{Name: service.Name, Namespace: service.Namespace}, backendPool: backends.NewPool(cloud, namer), - l4HealthChecks: healthchecks.L4(), + l4HealthChecks: healthchecks_l4.GetInstance(), } portId := utils.ServicePortID{Service: l4netlb.NamespacedName} l4netlb.ServicePort = utils.ServicePort{ diff --git a/pkg/loadbalancers/l4netlb_test.go b/pkg/loadbalancers/l4netlb_test.go index 646383e61a..62b528e96f 100644 --- a/pkg/loadbalancers/l4netlb_test.go +++ b/pkg/loadbalancers/l4netlb_test.go @@ -5,7 +5,7 @@ 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 + 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, @@ -21,10 +21,6 @@ import ( "strings" "testing" - "k8s.io/ingress-gce/pkg/firewalls" - "k8s.io/ingress-gce/pkg/flags" - "k8s.io/ingress-gce/pkg/healthchecks" - "github.com/GoogleCloudPlatform/k8s-cloud-provider/pkg/cloud" "github.com/GoogleCloudPlatform/k8s-cloud-provider/pkg/cloud/meta" "github.com/GoogleCloudPlatform/k8s-cloud-provider/pkg/cloud/mock" @@ -34,6 +30,9 @@ import ( servicehelper "k8s.io/cloud-provider/service/helpers" "k8s.io/ingress-gce/pkg/annotations" "k8s.io/ingress-gce/pkg/composite" + "k8s.io/ingress-gce/pkg/firewalls" + "k8s.io/ingress-gce/pkg/flags" + "k8s.io/ingress-gce/pkg/healthchecks_l4" "k8s.io/ingress-gce/pkg/metrics" "k8s.io/ingress-gce/pkg/test" "k8s.io/ingress-gce/pkg/utils" @@ -57,7 +56,7 @@ func TestEnsureL4NetLoadBalancer(t *testing.T) { namer := namer_util.NewL4Namer(kubeSystemUID, namer_util.NewNamer(vals.ClusterName, "cluster-fw")) l4netlb := NewL4NetLB(svc, fakeGCE, meta.Regional, namer, record.NewFakeRecorder(100)) - l4netlb.l4HealthChecks = healthchecks.FakeL4(fakeGCE, &test.FakeRecorderSource{}) + l4netlb.l4HealthChecks = healthchecks_l4.Fake(fakeGCE, &test.FakeRecorderSource{}) if _, err := test.CreateAndInsertNodes(l4netlb.cloud, nodeNames, vals.ZoneName); err != nil { t.Errorf("Unexpected error when adding nodes %v", err) @@ -108,7 +107,7 @@ func TestDeleteL4NetLoadBalancer(t *testing.T) { namer := namer_util.NewL4Namer(kubeSystemUID, namer_util.NewNamer(vals.ClusterName, "cluster-fw")) l4NetLB := NewL4NetLB(svc, fakeGCE, meta.Regional, namer, record.NewFakeRecorder(100)) - l4NetLB.l4HealthChecks = healthchecks.FakeL4(fakeGCE, &test.FakeRecorderSource{}) + l4NetLB.l4HealthChecks = healthchecks_l4.Fake(fakeGCE, &test.FakeRecorderSource{}) if _, err := test.CreateAndInsertNodes(l4NetLB.cloud, nodeNames, vals.ZoneName); err != nil { t.Errorf("Unexpected error when adding nodes %v", err) @@ -210,7 +209,7 @@ func ensureLoadBalancer(port int, vals gce.TestClusterValues, fakeGCE *gce.Cloud emptyNodes := []string{} l4NetLB := NewL4NetLB(svc, fakeGCE, meta.Regional, namer, record.NewFakeRecorder(100)) - l4NetLB.l4HealthChecks = healthchecks.FakeL4(fakeGCE, &test.FakeRecorderSource{}) + l4NetLB.l4HealthChecks = healthchecks_l4.Fake(fakeGCE, &test.FakeRecorderSource{}) result := l4NetLB.EnsureFrontend(emptyNodes, svc) if result.Error != nil { @@ -353,7 +352,7 @@ func TestMetricsForStandardNetworkTier(t *testing.T) { namer := namer_util.NewL4Namer(kubeSystemUID, namer_util.NewNamer(vals.ClusterName, "cluster-fw")) l4netlb := NewL4NetLB(svc, fakeGCE, meta.Regional, namer, record.NewFakeRecorder(100)) - l4netlb.l4HealthChecks = healthchecks.FakeL4(fakeGCE, &test.FakeRecorderSource{}) + l4netlb.l4HealthChecks = healthchecks_l4.Fake(fakeGCE, &test.FakeRecorderSource{}) if _, err := test.CreateAndInsertNodes(l4netlb.cloud, nodeNames, vals.ZoneName); err != nil { t.Errorf("Unexpected error when adding nodes %v", err) @@ -400,7 +399,7 @@ func TestEnsureNetLBFirewallDestinations(t *testing.T) { svc := test.NewL4NetLBRBSService(8080) namer := namer_util.NewL4Namer(kubeSystemUID, nil) l4netlb := NewL4NetLB(svc, fakeGCE, meta.Regional, namer, record.NewFakeRecorder(100)) - l4netlb.l4HealthChecks = healthchecks.FakeL4(fakeGCE, &test.FakeRecorderSource{}) + l4netlb.l4HealthChecks = healthchecks_l4.Fake(fakeGCE, &test.FakeRecorderSource{}) if _, err := test.CreateAndInsertNodes(l4netlb.cloud, nodeNames, vals.ZoneName); err != nil { t.Errorf("Unexpected error when adding nodes %v", err) diff --git a/vendor/github.com/google/go-cmp/cmp/cmpopts/equate.go b/vendor/github.com/google/go-cmp/cmp/cmpopts/equate.go new file mode 100644 index 0000000000..e4ffca838a --- /dev/null +++ b/vendor/github.com/google/go-cmp/cmp/cmpopts/equate.go @@ -0,0 +1,148 @@ +// Copyright 2017, The Go Authors. All rights reserved. +// Use of this source code is governed by a BSD-style +// license that can be found in the LICENSE file. + +// Package cmpopts provides common options for the cmp package. +package cmpopts + +import ( + "math" + "reflect" + "time" + + "github.com/google/go-cmp/cmp" +) + +func equateAlways(_, _ interface{}) bool { return true } + +// EquateEmpty returns a Comparer option that determines all maps and slices +// with a length of zero to be equal, regardless of whether they are nil. +// +// EquateEmpty can be used in conjunction with SortSlices and SortMaps. +func EquateEmpty() cmp.Option { + return cmp.FilterValues(isEmpty, cmp.Comparer(equateAlways)) +} + +func isEmpty(x, y interface{}) bool { + vx, vy := reflect.ValueOf(x), reflect.ValueOf(y) + return (x != nil && y != nil && vx.Type() == vy.Type()) && + (vx.Kind() == reflect.Slice || vx.Kind() == reflect.Map) && + (vx.Len() == 0 && vy.Len() == 0) +} + +// EquateApprox returns a Comparer option that determines float32 or float64 +// values to be equal if they are within a relative fraction or absolute margin. +// This option is not used when either x or y is NaN or infinite. +// +// The fraction determines that the difference of two values must be within the +// smaller fraction of the two values, while the margin determines that the two +// values must be within some absolute margin. +// To express only a fraction or only a margin, use 0 for the other parameter. +// The fraction and margin must be non-negative. +// +// The mathematical expression used is equivalent to: +// |x-y| ≤ max(fraction*min(|x|, |y|), margin) +// +// EquateApprox can be used in conjunction with EquateNaNs. +func EquateApprox(fraction, margin float64) cmp.Option { + if margin < 0 || fraction < 0 || math.IsNaN(margin) || math.IsNaN(fraction) { + panic("margin or fraction must be a non-negative number") + } + a := approximator{fraction, margin} + return cmp.Options{ + cmp.FilterValues(areRealF64s, cmp.Comparer(a.compareF64)), + cmp.FilterValues(areRealF32s, cmp.Comparer(a.compareF32)), + } +} + +type approximator struct{ frac, marg float64 } + +func areRealF64s(x, y float64) bool { + return !math.IsNaN(x) && !math.IsNaN(y) && !math.IsInf(x, 0) && !math.IsInf(y, 0) +} +func areRealF32s(x, y float32) bool { + return areRealF64s(float64(x), float64(y)) +} +func (a approximator) compareF64(x, y float64) bool { + relMarg := a.frac * math.Min(math.Abs(x), math.Abs(y)) + return math.Abs(x-y) <= math.Max(a.marg, relMarg) +} +func (a approximator) compareF32(x, y float32) bool { + return a.compareF64(float64(x), float64(y)) +} + +// EquateNaNs returns a Comparer option that determines float32 and float64 +// NaN values to be equal. +// +// EquateNaNs can be used in conjunction with EquateApprox. +func EquateNaNs() cmp.Option { + return cmp.Options{ + cmp.FilterValues(areNaNsF64s, cmp.Comparer(equateAlways)), + cmp.FilterValues(areNaNsF32s, cmp.Comparer(equateAlways)), + } +} + +func areNaNsF64s(x, y float64) bool { + return math.IsNaN(x) && math.IsNaN(y) +} +func areNaNsF32s(x, y float32) bool { + return areNaNsF64s(float64(x), float64(y)) +} + +// EquateApproxTime returns a Comparer option that determines two non-zero +// time.Time values to be equal if they are within some margin of one another. +// If both times have a monotonic clock reading, then the monotonic time +// difference will be used. The margin must be non-negative. +func EquateApproxTime(margin time.Duration) cmp.Option { + if margin < 0 { + panic("margin must be a non-negative number") + } + a := timeApproximator{margin} + return cmp.FilterValues(areNonZeroTimes, cmp.Comparer(a.compare)) +} + +func areNonZeroTimes(x, y time.Time) bool { + return !x.IsZero() && !y.IsZero() +} + +type timeApproximator struct { + margin time.Duration +} + +func (a timeApproximator) compare(x, y time.Time) bool { + // Avoid subtracting times to avoid overflow when the + // difference is larger than the largest representible duration. + if x.After(y) { + // Ensure x is always before y + x, y = y, x + } + // We're within the margin if x+margin >= y. + // Note: time.Time doesn't have AfterOrEqual method hence the negation. + return !x.Add(a.margin).Before(y) +} + +// AnyError is an error that matches any non-nil error. +var AnyError anyError + +type anyError struct{} + +func (anyError) Error() string { return "any error" } +func (anyError) Is(err error) bool { return err != nil } + +// EquateErrors returns a Comparer option that determines errors to be equal +// if errors.Is reports them to match. The AnyError error can be used to +// match any non-nil error. +func EquateErrors() cmp.Option { + return cmp.FilterValues(areConcreteErrors, cmp.Comparer(compareErrors)) +} + +// areConcreteErrors reports whether x and y are types that implement error. +// The input types are deliberately of the interface{} type rather than the +// error type so that we can handle situations where the current type is an +// interface{}, but the underlying concrete types both happen to implement +// the error interface. +func areConcreteErrors(x, y interface{}) bool { + _, ok1 := x.(error) + _, ok2 := y.(error) + return ok1 && ok2 +} diff --git a/vendor/github.com/google/go-cmp/cmp/cmpopts/errors_go113.go b/vendor/github.com/google/go-cmp/cmp/cmpopts/errors_go113.go new file mode 100644 index 0000000000..26fe25d6af --- /dev/null +++ b/vendor/github.com/google/go-cmp/cmp/cmpopts/errors_go113.go @@ -0,0 +1,15 @@ +// Copyright 2021, The Go Authors. All rights reserved. +// Use of this source code is governed by a BSD-style +// license that can be found in the LICENSE file. + +// +build go1.13 + +package cmpopts + +import "errors" + +func compareErrors(x, y interface{}) bool { + xe := x.(error) + ye := y.(error) + return errors.Is(xe, ye) || errors.Is(ye, xe) +} diff --git a/vendor/github.com/google/go-cmp/cmp/cmpopts/errors_xerrors.go b/vendor/github.com/google/go-cmp/cmp/cmpopts/errors_xerrors.go new file mode 100644 index 0000000000..6eeb8d6e65 --- /dev/null +++ b/vendor/github.com/google/go-cmp/cmp/cmpopts/errors_xerrors.go @@ -0,0 +1,18 @@ +// Copyright 2021, The Go Authors. All rights reserved. +// Use of this source code is governed by a BSD-style +// license that can be found in the LICENSE file. + +// +build !go1.13 + +// TODO(≥go1.13): For support on = 0 && (ss.less(v, start, i-1) || ss.less(v, i-1, start)) { + panic(fmt.Sprintf("incomparable values detected: want equal elements: %v", v.Slice(start, i))) + } + start = -1 + } else if start == -1 { + start = i + } + } +} +func (ss sliceSorter) less(v reflect.Value, i, j int) bool { + vx, vy := v.Index(i), v.Index(j) + return ss.fnc.Call([]reflect.Value{vx, vy})[0].Bool() +} + +// SortMaps returns a Transformer option that flattens map[K]V types to be a +// sorted []struct{K, V}. The less function must be of the form +// "func(T, T) bool" which is used to sort any map with key K that is +// assignable to T. +// +// Flattening the map into a slice has the property that cmp.Equal is able to +// use Comparers on K or the K.Equal method if it exists. +// +// The less function must be: +// • Deterministic: less(x, y) == less(x, y) +// • Irreflexive: !less(x, x) +// • Transitive: if !less(x, y) and !less(y, z), then !less(x, z) +// • Total: if x != y, then either less(x, y) or less(y, x) +// +// SortMaps can be used in conjunction with EquateEmpty. +func SortMaps(lessFunc interface{}) cmp.Option { + vf := reflect.ValueOf(lessFunc) + if !function.IsType(vf.Type(), function.Less) || vf.IsNil() { + panic(fmt.Sprintf("invalid less function: %T", lessFunc)) + } + ms := mapSorter{vf.Type().In(0), vf} + return cmp.FilterValues(ms.filter, cmp.Transformer("cmpopts.SortMaps", ms.sort)) +} + +type mapSorter struct { + in reflect.Type // T + fnc reflect.Value // func(T, T) bool +} + +func (ms mapSorter) filter(x, y interface{}) bool { + vx, vy := reflect.ValueOf(x), reflect.ValueOf(y) + return (x != nil && y != nil && vx.Type() == vy.Type()) && + (vx.Kind() == reflect.Map && vx.Type().Key().AssignableTo(ms.in)) && + (vx.Len() != 0 || vy.Len() != 0) +} +func (ms mapSorter) sort(x interface{}) interface{} { + src := reflect.ValueOf(x) + outType := reflect.StructOf([]reflect.StructField{ + {Name: "K", Type: src.Type().Key()}, + {Name: "V", Type: src.Type().Elem()}, + }) + dst := reflect.MakeSlice(reflect.SliceOf(outType), src.Len(), src.Len()) + for i, k := range src.MapKeys() { + v := reflect.New(outType).Elem() + v.Field(0).Set(k) + v.Field(1).Set(src.MapIndex(k)) + dst.Index(i).Set(v) + } + sort.Slice(dst.Interface(), func(i, j int) bool { return ms.less(dst, i, j) }) + ms.checkSort(dst) + return dst.Interface() +} +func (ms mapSorter) checkSort(v reflect.Value) { + for i := 1; i < v.Len(); i++ { + if !ms.less(v, i-1, i) { + panic(fmt.Sprintf("partial order detected: want %v < %v", v.Index(i-1), v.Index(i))) + } + } +} +func (ms mapSorter) less(v reflect.Value, i, j int) bool { + vx, vy := v.Index(i).Field(0), v.Index(j).Field(0) + return ms.fnc.Call([]reflect.Value{vx, vy})[0].Bool() +} diff --git a/vendor/github.com/google/go-cmp/cmp/cmpopts/struct_filter.go b/vendor/github.com/google/go-cmp/cmp/cmpopts/struct_filter.go new file mode 100644 index 0000000000..a09829c3af --- /dev/null +++ b/vendor/github.com/google/go-cmp/cmp/cmpopts/struct_filter.go @@ -0,0 +1,187 @@ +// Copyright 2017, The Go Authors. All rights reserved. +// Use of this source code is governed by a BSD-style +// license that can be found in the LICENSE file. + +package cmpopts + +import ( + "fmt" + "reflect" + "strings" + + "github.com/google/go-cmp/cmp" +) + +// filterField returns a new Option where opt is only evaluated on paths that +// include a specific exported field on a single struct type. +// The struct type is specified by passing in a value of that type. +// +// The name may be a dot-delimited string (e.g., "Foo.Bar") to select a +// specific sub-field that is embedded or nested within the parent struct. +func filterField(typ interface{}, name string, opt cmp.Option) cmp.Option { + // TODO: This is currently unexported over concerns of how helper filters + // can be composed together easily. + // TODO: Add tests for FilterField. + + sf := newStructFilter(typ, name) + return cmp.FilterPath(sf.filter, opt) +} + +type structFilter struct { + t reflect.Type // The root struct type to match on + ft fieldTree // Tree of fields to match on +} + +func newStructFilter(typ interface{}, names ...string) structFilter { + // TODO: Perhaps allow * as a special identifier to allow ignoring any + // number of path steps until the next field match? + // This could be useful when a concrete struct gets transformed into + // an anonymous struct where it is not possible to specify that by type, + // but the transformer happens to provide guarantees about the names of + // the transformed fields. + + t := reflect.TypeOf(typ) + if t == nil || t.Kind() != reflect.Struct { + panic(fmt.Sprintf("%T must be a non-pointer struct", typ)) + } + var ft fieldTree + for _, name := range names { + cname, err := canonicalName(t, name) + if err != nil { + panic(fmt.Sprintf("%s: %v", strings.Join(cname, "."), err)) + } + ft.insert(cname) + } + return structFilter{t, ft} +} + +func (sf structFilter) filter(p cmp.Path) bool { + for i, ps := range p { + if ps.Type().AssignableTo(sf.t) && sf.ft.matchPrefix(p[i+1:]) { + return true + } + } + return false +} + +// fieldTree represents a set of dot-separated identifiers. +// +// For example, inserting the following selectors: +// Foo +// Foo.Bar.Baz +// Foo.Buzz +// Nuka.Cola.Quantum +// +// Results in a tree of the form: +// {sub: { +// "Foo": {ok: true, sub: { +// "Bar": {sub: { +// "Baz": {ok: true}, +// }}, +// "Buzz": {ok: true}, +// }}, +// "Nuka": {sub: { +// "Cola": {sub: { +// "Quantum": {ok: true}, +// }}, +// }}, +// }} +type fieldTree struct { + ok bool // Whether this is a specified node + sub map[string]fieldTree // The sub-tree of fields under this node +} + +// insert inserts a sequence of field accesses into the tree. +func (ft *fieldTree) insert(cname []string) { + if ft.sub == nil { + ft.sub = make(map[string]fieldTree) + } + if len(cname) == 0 { + ft.ok = true + return + } + sub := ft.sub[cname[0]] + sub.insert(cname[1:]) + ft.sub[cname[0]] = sub +} + +// matchPrefix reports whether any selector in the fieldTree matches +// the start of path p. +func (ft fieldTree) matchPrefix(p cmp.Path) bool { + for _, ps := range p { + switch ps := ps.(type) { + case cmp.StructField: + ft = ft.sub[ps.Name()] + if ft.ok { + return true + } + if len(ft.sub) == 0 { + return false + } + case cmp.Indirect: + default: + return false + } + } + return false +} + +// canonicalName returns a list of identifiers where any struct field access +// through an embedded field is expanded to include the names of the embedded +// types themselves. +// +// For example, suppose field "Foo" is not directly in the parent struct, +// but actually from an embedded struct of type "Bar". Then, the canonical name +// of "Foo" is actually "Bar.Foo". +// +// Suppose field "Foo" is not directly in the parent struct, but actually +// a field in two different embedded structs of types "Bar" and "Baz". +// Then the selector "Foo" causes a panic since it is ambiguous which one it +// refers to. The user must specify either "Bar.Foo" or "Baz.Foo". +func canonicalName(t reflect.Type, sel string) ([]string, error) { + var name string + sel = strings.TrimPrefix(sel, ".") + if sel == "" { + return nil, fmt.Errorf("name must not be empty") + } + if i := strings.IndexByte(sel, '.'); i < 0 { + name, sel = sel, "" + } else { + name, sel = sel[:i], sel[i:] + } + + // Type must be a struct or pointer to struct. + if t.Kind() == reflect.Ptr { + t = t.Elem() + } + if t.Kind() != reflect.Struct { + return nil, fmt.Errorf("%v must be a struct", t) + } + + // Find the canonical name for this current field name. + // If the field exists in an embedded struct, then it will be expanded. + sf, _ := t.FieldByName(name) + if !isExported(name) { + // Avoid using reflect.Type.FieldByName for unexported fields due to + // buggy behavior with regard to embeddeding and unexported fields. + // See https://golang.org/issue/4876 for details. + sf = reflect.StructField{} + for i := 0; i < t.NumField() && sf.Name == ""; i++ { + if t.Field(i).Name == name { + sf = t.Field(i) + } + } + } + if sf.Name == "" { + return []string{name}, fmt.Errorf("does not exist") + } + var ss []string + for i := range sf.Index { + ss = append(ss, t.FieldByIndex(sf.Index[:i+1]).Name) + } + if sel == "" { + return ss, nil + } + ssPost, err := canonicalName(sf.Type, sel) + return append(ss, ssPost...), err +} diff --git a/vendor/github.com/google/go-cmp/cmp/cmpopts/xform.go b/vendor/github.com/google/go-cmp/cmp/cmpopts/xform.go new file mode 100644 index 0000000000..4eb49d63db --- /dev/null +++ b/vendor/github.com/google/go-cmp/cmp/cmpopts/xform.go @@ -0,0 +1,35 @@ +// Copyright 2018, The Go Authors. All rights reserved. +// Use of this source code is governed by a BSD-style +// license that can be found in the LICENSE file. + +package cmpopts + +import ( + "github.com/google/go-cmp/cmp" +) + +type xformFilter struct{ xform cmp.Option } + +func (xf xformFilter) filter(p cmp.Path) bool { + for _, ps := range p { + if t, ok := ps.(cmp.Transform); ok && t.Option() == xf.xform { + return false + } + } + return true +} + +// AcyclicTransformer returns a Transformer with a filter applied that ensures +// that the transformer cannot be recursively applied upon its own output. +// +// An example use case is a transformer that splits a string by lines: +// AcyclicTransformer("SplitLines", func(s string) []string{ +// return strings.Split(s, "\n") +// }) +// +// Had this been an unfiltered Transformer instead, this would result in an +// infinite cycle converting a string to []string to [][]string and so on. +func AcyclicTransformer(name string, xformFunc interface{}) cmp.Option { + xf := xformFilter{cmp.Transformer(name, xformFunc)} + return cmp.FilterPath(xf.filter, xf.xform) +} diff --git a/vendor/golang.org/x/xerrors/LICENSE b/vendor/golang.org/x/xerrors/LICENSE new file mode 100644 index 0000000000..e4a47e17f1 --- /dev/null +++ b/vendor/golang.org/x/xerrors/LICENSE @@ -0,0 +1,27 @@ +Copyright (c) 2019 The Go Authors. All rights reserved. + +Redistribution and use in source and binary forms, with or without +modification, are permitted provided that the following conditions are +met: + + * Redistributions of source code must retain the above copyright +notice, this list of conditions and the following disclaimer. + * Redistributions in binary form must reproduce the above +copyright notice, this list of conditions and the following disclaimer +in the documentation and/or other materials provided with the +distribution. + * Neither the name of Google Inc. nor the names of its +contributors may be used to endorse or promote products derived from +this software without specific prior written permission. + +THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS +"AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT +LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR +A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT +OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, +SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT +LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, +DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY +THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT +(INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE +OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. diff --git a/vendor/golang.org/x/xerrors/PATENTS b/vendor/golang.org/x/xerrors/PATENTS new file mode 100644 index 0000000000..733099041f --- /dev/null +++ b/vendor/golang.org/x/xerrors/PATENTS @@ -0,0 +1,22 @@ +Additional IP Rights Grant (Patents) + +"This implementation" means the copyrightable works distributed by +Google as part of the Go project. + +Google hereby grants to You a perpetual, worldwide, non-exclusive, +no-charge, royalty-free, irrevocable (except as stated in this section) +patent license to make, have made, use, offer to sell, sell, import, +transfer and otherwise run, modify and propagate the contents of this +implementation of Go, where such license applies only to those patent +claims, both currently owned or controlled by Google and acquired in +the future, licensable by Google that are necessarily infringed by this +implementation of Go. This grant does not include claims that would be +infringed only as a consequence of further modification of this +implementation. If you or your agent or exclusive licensee institute or +order or agree to the institution of patent litigation against any +entity (including a cross-claim or counterclaim in a lawsuit) alleging +that this implementation of Go or any code incorporated within this +implementation of Go constitutes direct or contributory patent +infringement, or inducement of patent infringement, then any patent +rights granted to you under this License for this implementation of Go +shall terminate as of the date such litigation is filed. diff --git a/vendor/golang.org/x/xerrors/README b/vendor/golang.org/x/xerrors/README new file mode 100644 index 0000000000..aac7867a56 --- /dev/null +++ b/vendor/golang.org/x/xerrors/README @@ -0,0 +1,2 @@ +This repository holds the transition packages for the new Go 1.13 error values. +See golang.org/design/29934-error-values. diff --git a/vendor/golang.org/x/xerrors/adaptor.go b/vendor/golang.org/x/xerrors/adaptor.go new file mode 100644 index 0000000000..4317f24833 --- /dev/null +++ b/vendor/golang.org/x/xerrors/adaptor.go @@ -0,0 +1,193 @@ +// Copyright 2018 The Go Authors. All rights reserved. +// Use of this source code is governed by a BSD-style +// license that can be found in the LICENSE file. + +package xerrors + +import ( + "bytes" + "fmt" + "io" + "reflect" + "strconv" +) + +// FormatError calls the FormatError method of f with an errors.Printer +// configured according to s and verb, and writes the result to s. +func FormatError(f Formatter, s fmt.State, verb rune) { + // Assuming this function is only called from the Format method, and given + // that FormatError takes precedence over Format, it cannot be called from + // any package that supports errors.Formatter. It is therefore safe to + // disregard that State may be a specific printer implementation and use one + // of our choice instead. + + // limitations: does not support printing error as Go struct. + + var ( + sep = " " // separator before next error + p = &state{State: s} + direct = true + ) + + var err error = f + + switch verb { + // Note that this switch must match the preference order + // for ordinary string printing (%#v before %+v, and so on). + + case 'v': + if s.Flag('#') { + if stringer, ok := err.(fmt.GoStringer); ok { + io.WriteString(&p.buf, stringer.GoString()) + goto exit + } + // proceed as if it were %v + } else if s.Flag('+') { + p.printDetail = true + sep = "\n - " + } + case 's': + case 'q', 'x', 'X': + // Use an intermediate buffer in the rare cases that precision, + // truncation, or one of the alternative verbs (q, x, and X) are + // specified. + direct = false + + default: + p.buf.WriteString("%!") + p.buf.WriteRune(verb) + p.buf.WriteByte('(') + switch { + case err != nil: + p.buf.WriteString(reflect.TypeOf(f).String()) + default: + p.buf.WriteString("") + } + p.buf.WriteByte(')') + io.Copy(s, &p.buf) + return + } + +loop: + for { + switch v := err.(type) { + case Formatter: + err = v.FormatError((*printer)(p)) + case fmt.Formatter: + v.Format(p, 'v') + break loop + default: + io.WriteString(&p.buf, v.Error()) + break loop + } + if err == nil { + break + } + if p.needColon || !p.printDetail { + p.buf.WriteByte(':') + p.needColon = false + } + p.buf.WriteString(sep) + p.inDetail = false + p.needNewline = false + } + +exit: + width, okW := s.Width() + prec, okP := s.Precision() + + if !direct || (okW && width > 0) || okP { + // Construct format string from State s. + format := []byte{'%'} + if s.Flag('-') { + format = append(format, '-') + } + if s.Flag('+') { + format = append(format, '+') + } + if s.Flag(' ') { + format = append(format, ' ') + } + if okW { + format = strconv.AppendInt(format, int64(width), 10) + } + if okP { + format = append(format, '.') + format = strconv.AppendInt(format, int64(prec), 10) + } + format = append(format, string(verb)...) + fmt.Fprintf(s, string(format), p.buf.String()) + } else { + io.Copy(s, &p.buf) + } +} + +var detailSep = []byte("\n ") + +// state tracks error printing state. It implements fmt.State. +type state struct { + fmt.State + buf bytes.Buffer + + printDetail bool + inDetail bool + needColon bool + needNewline bool +} + +func (s *state) Write(b []byte) (n int, err error) { + if s.printDetail { + if len(b) == 0 { + return 0, nil + } + if s.inDetail && s.needColon { + s.needNewline = true + if b[0] == '\n' { + b = b[1:] + } + } + k := 0 + for i, c := range b { + if s.needNewline { + if s.inDetail && s.needColon { + s.buf.WriteByte(':') + s.needColon = false + } + s.buf.Write(detailSep) + s.needNewline = false + } + if c == '\n' { + s.buf.Write(b[k:i]) + k = i + 1 + s.needNewline = true + } + } + s.buf.Write(b[k:]) + if !s.inDetail { + s.needColon = true + } + } else if !s.inDetail { + s.buf.Write(b) + } + return len(b), nil +} + +// printer wraps a state to implement an xerrors.Printer. +type printer state + +func (s *printer) Print(args ...interface{}) { + if !s.inDetail || s.printDetail { + fmt.Fprint((*state)(s), args...) + } +} + +func (s *printer) Printf(format string, args ...interface{}) { + if !s.inDetail || s.printDetail { + fmt.Fprintf((*state)(s), format, args...) + } +} + +func (s *printer) Detail() bool { + s.inDetail = true + return s.printDetail +} diff --git a/vendor/golang.org/x/xerrors/codereview.cfg b/vendor/golang.org/x/xerrors/codereview.cfg new file mode 100644 index 0000000000..3f8b14b64e --- /dev/null +++ b/vendor/golang.org/x/xerrors/codereview.cfg @@ -0,0 +1 @@ +issuerepo: golang/go diff --git a/vendor/golang.org/x/xerrors/doc.go b/vendor/golang.org/x/xerrors/doc.go new file mode 100644 index 0000000000..eef99d9d54 --- /dev/null +++ b/vendor/golang.org/x/xerrors/doc.go @@ -0,0 +1,22 @@ +// Copyright 2019 The Go Authors. All rights reserved. +// Use of this source code is governed by a BSD-style +// license that can be found in the LICENSE file. + +// Package xerrors implements functions to manipulate errors. +// +// This package is based on the Go 2 proposal for error values: +// https://golang.org/design/29934-error-values +// +// These functions were incorporated into the standard library's errors package +// in Go 1.13: +// - Is +// - As +// - Unwrap +// +// Also, Errorf's %w verb was incorporated into fmt.Errorf. +// +// Use this package to get equivalent behavior in all supported Go versions. +// +// No other features of this package were included in Go 1.13, and at present +// there are no plans to include any of them. +package xerrors // import "golang.org/x/xerrors" diff --git a/vendor/golang.org/x/xerrors/errors.go b/vendor/golang.org/x/xerrors/errors.go new file mode 100644 index 0000000000..e88d3772d8 --- /dev/null +++ b/vendor/golang.org/x/xerrors/errors.go @@ -0,0 +1,33 @@ +// Copyright 2011 The Go Authors. All rights reserved. +// Use of this source code is governed by a BSD-style +// license that can be found in the LICENSE file. + +package xerrors + +import "fmt" + +// errorString is a trivial implementation of error. +type errorString struct { + s string + frame Frame +} + +// New returns an error that formats as the given text. +// +// The returned error contains a Frame set to the caller's location and +// implements Formatter to show this information when printed with details. +func New(text string) error { + return &errorString{text, Caller(1)} +} + +func (e *errorString) Error() string { + return e.s +} + +func (e *errorString) Format(s fmt.State, v rune) { FormatError(e, s, v) } + +func (e *errorString) FormatError(p Printer) (next error) { + p.Print(e.s) + e.frame.Format(p) + return nil +} diff --git a/vendor/golang.org/x/xerrors/fmt.go b/vendor/golang.org/x/xerrors/fmt.go new file mode 100644 index 0000000000..829862ddf6 --- /dev/null +++ b/vendor/golang.org/x/xerrors/fmt.go @@ -0,0 +1,187 @@ +// Copyright 2018 The Go Authors. All rights reserved. +// Use of this source code is governed by a BSD-style +// license that can be found in the LICENSE file. + +package xerrors + +import ( + "fmt" + "strings" + "unicode" + "unicode/utf8" + + "golang.org/x/xerrors/internal" +) + +const percentBangString = "%!" + +// Errorf formats according to a format specifier and returns the string as a +// value that satisfies error. +// +// The returned error includes the file and line number of the caller when +// formatted with additional detail enabled. If the last argument is an error +// the returned error's Format method will return it if the format string ends +// with ": %s", ": %v", or ": %w". If the last argument is an error and the +// format string ends with ": %w", the returned error implements an Unwrap +// method returning it. +// +// If the format specifier includes a %w verb with an error operand in a +// position other than at the end, the returned error will still implement an +// Unwrap method returning the operand, but the error's Format method will not +// return the wrapped error. +// +// It is invalid to include more than one %w verb or to supply it with an +// operand that does not implement the error interface. The %w verb is otherwise +// a synonym for %v. +func Errorf(format string, a ...interface{}) error { + format = formatPlusW(format) + // Support a ": %[wsv]" suffix, which works well with xerrors.Formatter. + wrap := strings.HasSuffix(format, ": %w") + idx, format2, ok := parsePercentW(format) + percentWElsewhere := !wrap && idx >= 0 + if !percentWElsewhere && (wrap || strings.HasSuffix(format, ": %s") || strings.HasSuffix(format, ": %v")) { + err := errorAt(a, len(a)-1) + if err == nil { + return &noWrapError{fmt.Sprintf(format, a...), nil, Caller(1)} + } + // TODO: this is not entirely correct. The error value could be + // printed elsewhere in format if it mixes numbered with unnumbered + // substitutions. With relatively small changes to doPrintf we can + // have it optionally ignore extra arguments and pass the argument + // list in its entirety. + msg := fmt.Sprintf(format[:len(format)-len(": %s")], a[:len(a)-1]...) + frame := Frame{} + if internal.EnableTrace { + frame = Caller(1) + } + if wrap { + return &wrapError{msg, err, frame} + } + return &noWrapError{msg, err, frame} + } + // Support %w anywhere. + // TODO: don't repeat the wrapped error's message when %w occurs in the middle. + msg := fmt.Sprintf(format2, a...) + if idx < 0 { + return &noWrapError{msg, nil, Caller(1)} + } + err := errorAt(a, idx) + if !ok || err == nil { + // Too many %ws or argument of %w is not an error. Approximate the Go + // 1.13 fmt.Errorf message. + return &noWrapError{fmt.Sprintf("%sw(%s)", percentBangString, msg), nil, Caller(1)} + } + frame := Frame{} + if internal.EnableTrace { + frame = Caller(1) + } + return &wrapError{msg, err, frame} +} + +func errorAt(args []interface{}, i int) error { + if i < 0 || i >= len(args) { + return nil + } + err, ok := args[i].(error) + if !ok { + return nil + } + return err +} + +// formatPlusW is used to avoid the vet check that will barf at %w. +func formatPlusW(s string) string { + return s +} + +// Return the index of the only %w in format, or -1 if none. +// Also return a rewritten format string with %w replaced by %v, and +// false if there is more than one %w. +// TODO: handle "%[N]w". +func parsePercentW(format string) (idx int, newFormat string, ok bool) { + // Loosely copied from golang.org/x/tools/go/analysis/passes/printf/printf.go. + idx = -1 + ok = true + n := 0 + sz := 0 + var isW bool + for i := 0; i < len(format); i += sz { + if format[i] != '%' { + sz = 1 + continue + } + // "%%" is not a format directive. + if i+1 < len(format) && format[i+1] == '%' { + sz = 2 + continue + } + sz, isW = parsePrintfVerb(format[i:]) + if isW { + if idx >= 0 { + ok = false + } else { + idx = n + } + // "Replace" the last character, the 'w', with a 'v'. + p := i + sz - 1 + format = format[:p] + "v" + format[p+1:] + } + n++ + } + return idx, format, ok +} + +// Parse the printf verb starting with a % at s[0]. +// Return how many bytes it occupies and whether the verb is 'w'. +func parsePrintfVerb(s string) (int, bool) { + // Assume only that the directive is a sequence of non-letters followed by a single letter. + sz := 0 + var r rune + for i := 1; i < len(s); i += sz { + r, sz = utf8.DecodeRuneInString(s[i:]) + if unicode.IsLetter(r) { + return i + sz, r == 'w' + } + } + return len(s), false +} + +type noWrapError struct { + msg string + err error + frame Frame +} + +func (e *noWrapError) Error() string { + return fmt.Sprint(e) +} + +func (e *noWrapError) Format(s fmt.State, v rune) { FormatError(e, s, v) } + +func (e *noWrapError) FormatError(p Printer) (next error) { + p.Print(e.msg) + e.frame.Format(p) + return e.err +} + +type wrapError struct { + msg string + err error + frame Frame +} + +func (e *wrapError) Error() string { + return fmt.Sprint(e) +} + +func (e *wrapError) Format(s fmt.State, v rune) { FormatError(e, s, v) } + +func (e *wrapError) FormatError(p Printer) (next error) { + p.Print(e.msg) + e.frame.Format(p) + return e.err +} + +func (e *wrapError) Unwrap() error { + return e.err +} diff --git a/vendor/golang.org/x/xerrors/format.go b/vendor/golang.org/x/xerrors/format.go new file mode 100644 index 0000000000..1bc9c26b97 --- /dev/null +++ b/vendor/golang.org/x/xerrors/format.go @@ -0,0 +1,34 @@ +// Copyright 2018 The Go Authors. All rights reserved. +// Use of this source code is governed by a BSD-style +// license that can be found in the LICENSE file. + +package xerrors + +// A Formatter formats error messages. +type Formatter interface { + error + + // FormatError prints the receiver's first error and returns the next error in + // the error chain, if any. + FormatError(p Printer) (next error) +} + +// A Printer formats error messages. +// +// The most common implementation of Printer is the one provided by package fmt +// during Printf (as of Go 1.13). Localization packages such as golang.org/x/text/message +// typically provide their own implementations. +type Printer interface { + // Print appends args to the message output. + Print(args ...interface{}) + + // Printf writes a formatted string. + Printf(format string, args ...interface{}) + + // Detail reports whether error detail is requested. + // After the first call to Detail, all text written to the Printer + // is formatted as additional detail, or ignored when + // detail has not been requested. + // If Detail returns false, the caller can avoid printing the detail at all. + Detail() bool +} diff --git a/vendor/golang.org/x/xerrors/frame.go b/vendor/golang.org/x/xerrors/frame.go new file mode 100644 index 0000000000..0de628ec50 --- /dev/null +++ b/vendor/golang.org/x/xerrors/frame.go @@ -0,0 +1,56 @@ +// Copyright 2018 The Go Authors. All rights reserved. +// Use of this source code is governed by a BSD-style +// license that can be found in the LICENSE file. + +package xerrors + +import ( + "runtime" +) + +// A Frame contains part of a call stack. +type Frame struct { + // Make room for three PCs: the one we were asked for, what it called, + // and possibly a PC for skipPleaseUseCallersFrames. See: + // https://go.googlesource.com/go/+/032678e0fb/src/runtime/extern.go#169 + frames [3]uintptr +} + +// Caller returns a Frame that describes a frame on the caller's stack. +// The argument skip is the number of frames to skip over. +// Caller(0) returns the frame for the caller of Caller. +func Caller(skip int) Frame { + var s Frame + runtime.Callers(skip+1, s.frames[:]) + return s +} + +// location reports the file, line, and function of a frame. +// +// The returned function may be "" even if file and line are not. +func (f Frame) location() (function, file string, line int) { + frames := runtime.CallersFrames(f.frames[:]) + if _, ok := frames.Next(); !ok { + return "", "", 0 + } + fr, ok := frames.Next() + if !ok { + return "", "", 0 + } + return fr.Function, fr.File, fr.Line +} + +// Format prints the stack as error detail. +// It should be called from an error's Format implementation +// after printing any other error detail. +func (f Frame) Format(p Printer) { + if p.Detail() { + function, file, line := f.location() + if function != "" { + p.Printf("%s\n ", function) + } + if file != "" { + p.Printf("%s:%d\n", file, line) + } + } +} diff --git a/vendor/golang.org/x/xerrors/go.mod b/vendor/golang.org/x/xerrors/go.mod new file mode 100644 index 0000000000..870d4f612d --- /dev/null +++ b/vendor/golang.org/x/xerrors/go.mod @@ -0,0 +1,3 @@ +module golang.org/x/xerrors + +go 1.11 diff --git a/vendor/golang.org/x/xerrors/internal/internal.go b/vendor/golang.org/x/xerrors/internal/internal.go new file mode 100644 index 0000000000..89f4eca5df --- /dev/null +++ b/vendor/golang.org/x/xerrors/internal/internal.go @@ -0,0 +1,8 @@ +// Copyright 2018 The Go Authors. All rights reserved. +// Use of this source code is governed by a BSD-style +// license that can be found in the LICENSE file. + +package internal + +// EnableTrace indicates whether stack information should be recorded in errors. +var EnableTrace = true diff --git a/vendor/golang.org/x/xerrors/wrap.go b/vendor/golang.org/x/xerrors/wrap.go new file mode 100644 index 0000000000..9a3b510374 --- /dev/null +++ b/vendor/golang.org/x/xerrors/wrap.go @@ -0,0 +1,106 @@ +// Copyright 2018 The Go Authors. All rights reserved. +// Use of this source code is governed by a BSD-style +// license that can be found in the LICENSE file. + +package xerrors + +import ( + "reflect" +) + +// A Wrapper provides context around another error. +type Wrapper interface { + // Unwrap returns the next error in the error chain. + // If there is no next error, Unwrap returns nil. + Unwrap() error +} + +// Opaque returns an error with the same error formatting as err +// but that does not match err and cannot be unwrapped. +func Opaque(err error) error { + return noWrapper{err} +} + +type noWrapper struct { + error +} + +func (e noWrapper) FormatError(p Printer) (next error) { + if f, ok := e.error.(Formatter); ok { + return f.FormatError(p) + } + p.Print(e.error) + return nil +} + +// Unwrap returns the result of calling the Unwrap method on err, if err implements +// Unwrap. Otherwise, Unwrap returns nil. +func Unwrap(err error) error { + u, ok := err.(Wrapper) + if !ok { + return nil + } + return u.Unwrap() +} + +// Is reports whether any error in err's chain matches target. +// +// An error is considered to match a target if it is equal to that target or if +// it implements a method Is(error) bool such that Is(target) returns true. +func Is(err, target error) bool { + if target == nil { + return err == target + } + + isComparable := reflect.TypeOf(target).Comparable() + for { + if isComparable && err == target { + return true + } + if x, ok := err.(interface{ Is(error) bool }); ok && x.Is(target) { + return true + } + // TODO: consider supporing target.Is(err). This would allow + // user-definable predicates, but also may allow for coping with sloppy + // APIs, thereby making it easier to get away with them. + if err = Unwrap(err); err == nil { + return false + } + } +} + +// As finds the first error in err's chain that matches the type to which target +// points, and if so, sets the target to its value and returns true. An error +// matches a type if it is assignable to the target type, or if it has a method +// As(interface{}) bool such that As(target) returns true. As will panic if target +// is not a non-nil pointer to a type which implements error or is of interface type. +// +// The As method should set the target to its value and return true if err +// matches the type to which target points. +func As(err error, target interface{}) bool { + if target == nil { + panic("errors: target cannot be nil") + } + val := reflect.ValueOf(target) + typ := val.Type() + if typ.Kind() != reflect.Ptr || val.IsNil() { + panic("errors: target must be a non-nil pointer") + } + if e := typ.Elem(); e.Kind() != reflect.Interface && !e.Implements(errorType) { + panic("errors: *target must be interface or implement error") + } + targetType := typ.Elem() + for err != nil { + if reflect.TypeOf(err).AssignableTo(targetType) { + val.Elem().Set(reflect.ValueOf(err)) + return true + } + if x, ok := err.(interface{ As(interface{}) bool }); ok && x.As(target) { + return true + } + err = Unwrap(err) + } + return false +} + +var errorType = reflect.TypeOf((*error)(nil)).Elem() diff --git a/vendor/modules.txt b/vendor/modules.txt index d07f10743c..f8ee347ead 100644 --- a/vendor/modules.txt +++ b/vendor/modules.txt @@ -112,6 +112,7 @@ github.com/golang/protobuf/ptypes/wrappers # github.com/google/go-cmp v0.5.6 ## explicit github.com/google/go-cmp/cmp +github.com/google/go-cmp/cmp/cmpopts github.com/google/go-cmp/cmp/internal/diff github.com/google/go-cmp/cmp/internal/flags github.com/google/go-cmp/cmp/internal/function @@ -225,6 +226,9 @@ golang.org/x/text/unicode/norm golang.org/x/text/width # golang.org/x/time v0.0.0-20210723032227-1f47c861a9ac golang.org/x/time/rate +# golang.org/x/xerrors v0.0.0-20200804184101-5ec99f83aff1 +golang.org/x/xerrors +golang.org/x/xerrors/internal # google.golang.org/api v0.60.0 ## explicit google.golang.org/api/compute/v0.alpha