vendor update for CSI 0.3.0

This commit is contained in:
gman
2018-07-18 16:47:22 +02:00
parent 6f484f92fc
commit 8ea659f0d5
6810 changed files with 438061 additions and 193861 deletions

View File

@ -13,7 +13,6 @@ go_library(
"gce.go",
"gce_address_manager.go",
"gce_addresses.go",
"gce_addresses_fakes.go",
"gce_alpha.go",
"gce_annotations.go",
"gce_backendservice.go",
@ -32,8 +31,8 @@ go_library(
"gce_loadbalancer_internal.go",
"gce_loadbalancer_naming.go",
"gce_networkendpointgroup.go",
"gce_op.go",
"gce_routes.go",
"gce_securitypolicy.go",
"gce_targetpool.go",
"gce_targetproxy.go",
"gce_tpu.go",
@ -70,7 +69,7 @@ go_library(
"//vendor/google.golang.org/api/compute/v1:go_default_library",
"//vendor/google.golang.org/api/container/v1:go_default_library",
"//vendor/google.golang.org/api/googleapi:go_default_library",
"//vendor/google.golang.org/api/tpu/v1alpha1:go_default_library",
"//vendor/google.golang.org/api/tpu/v1:go_default_library",
"//vendor/gopkg.in/gcfg.v1:go_default_library",
"//vendor/k8s.io/api/core/v1:go_default_library",
"//vendor/k8s.io/apimachinery/pkg/api/resource:go_default_library",
@ -101,16 +100,22 @@ go_test(
"gce_disks_test.go",
"gce_healthchecks_test.go",
"gce_loadbalancer_external_test.go",
"gce_loadbalancer_internal_test.go",
"gce_loadbalancer_test.go",
"gce_loadbalancer_utils_test.go",
"gce_test.go",
"gce_util_test.go",
"metrics_test.go",
],
embed = [":go_default_library"],
deps = [
"//pkg/api/v1/service:go_default_library",
"//pkg/cloudprovider:go_default_library",
"//pkg/cloudprovider/providers/gce/cloud:go_default_library",
"//pkg/cloudprovider/providers/gce/cloud/meta:go_default_library",
"//pkg/cloudprovider/providers/gce/cloud/mock:go_default_library",
"//pkg/kubelet/apis:go_default_library",
"//pkg/util/net/sets:go_default_library",
"//vendor/github.com/stretchr/testify/assert:go_default_library",
"//vendor/github.com/stretchr/testify/require:go_default_library",
"//vendor/golang.org/x/oauth2/google:go_default_library",
@ -122,6 +127,8 @@ go_test(
"//vendor/k8s.io/apimachinery/pkg/apis/meta/v1:go_default_library",
"//vendor/k8s.io/apimachinery/pkg/types:go_default_library",
"//vendor/k8s.io/apimachinery/pkg/util/sets:go_default_library",
"//vendor/k8s.io/client-go/tools/cache:go_default_library",
"//vendor/k8s.io/client-go/tools/record:go_default_library",
],
)

View File

@ -4,7 +4,9 @@ go_library(
name = "go_default_library",
srcs = [
"constants.go",
"context.go",
"doc.go",
"errors.go",
"gce_projects.go",
"gen.go",
"op.go",
@ -31,6 +33,8 @@ go_test(
srcs = [
"gen_test.go",
"mock_test.go",
"ratelimit_test.go",
"service_test.go",
"utils_test.go",
],
embed = [":go_default_library"],

View File

@ -0,0 +1,31 @@
/*
Copyright 2018 The Kubernetes Authors.
Licensed under the Apache License, Version 2.0 (the "License");
you may not use this file except in compliance with the License.
You may obtain a copy of the License at
http://www.apache.org/licenses/LICENSE-2.0
Unless required by applicable law or agreed to in writing, software
distributed under the License is distributed on an "AS IS" BASIS,
WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
See the License for the specific language governing permissions and
limitations under the License.
*/
package cloud
import (
"context"
"time"
)
const (
defaultCallTimeout = 1 * time.Hour
)
// ContextWithCallTimeout returns a context with a default timeout, used for generated client calls.
func ContextWithCallTimeout() (context.Context, context.CancelFunc) {
return context.WithTimeout(context.Background(), defaultCallTimeout)
}

View File

@ -109,4 +109,9 @@ limitations under the License.
// func (gce *GCEInstanceGroups) MyMethod() {
// // Custom implementation.
// }
//
// Update generated codes
//
// Run hack/update-cloudprovider-gce.sh to update the generated codes.
//
package cloud

View File

@ -0,0 +1,48 @@
/*
Copyright 2018 The Kubernetes Authors.
Licensed under the Apache License, Version 2.0 (the "License");
you may not use this file except in compliance with the License.
You may obtain a copy of the License at
http://www.apache.org/licenses/LICENSE-2.0
Unless required by applicable law or agreed to in writing, software
distributed under the License is distributed on an "AS IS" BASIS,
WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
See the License for the specific language governing permissions and
limitations under the License.
*/
package cloud
import "fmt"
// OperationPollingError occurs when the GCE Operation cannot be retrieved for a prolonged period.
type OperationPollingError struct {
LastPollError error
}
// Error returns a string representation including the last poll error encountered.
func (e *OperationPollingError) Error() string {
return fmt.Sprintf("GCE operation polling error: %v", e.LastPollError)
}
// GCEOperationError occurs when the GCE Operation finishes with an error.
type GCEOperationError struct {
// HTTPStatusCode is the HTTP status code of the final error.
// For example, a failed operation may have 400 - BadRequest.
HTTPStatusCode int
// Code is GCE's code of what went wrong.
// For example, RESOURCE_IN_USE_BY_ANOTHER_RESOURCE
Code string
// Message is a human readable message.
// For example, "The network resource 'xxx' is already being used by 'xxx'"
Message string
}
// Error returns a string representation including the HTTP Status code, GCE's error code
// and a human readable message.
func (e *GCEOperationError) Error() string {
return fmt.Sprintf("GCE %v - %v: %v", e.HTTPStatusCode, e.Code, e.Message)
}

File diff suppressed because it is too large Load Diff

View File

@ -28,7 +28,6 @@ import (
"log"
"os"
"os/exec"
"sort"
"text/template"
"time"
@ -986,6 +985,38 @@ func (g *{{.GCEWrapType}}) {{.FcnArgs}} {
}
}
// genTypes generates the type wrappers.
func genResourceIDs(wr io.Writer) {
const text = `
// New{{.Service}}ResourceID creates a ResourceID for the {{.Service}} resource.
{{- if .KeyIsProject}}
func New{{.Service}}ResourceID(project string) *ResourceID {
var key *meta.Key
{{- else}}
{{- if .KeyIsGlobal}}
func New{{.Service}}ResourceID(project, name string) *ResourceID {
key := meta.GlobalKey(name)
{{- end}}
{{- if .KeyIsRegional}}
func New{{.Service}}ResourceID(project, region, name string) *ResourceID {
key := meta.RegionalKey(name, region)
{{- end}}
{{- if .KeyIsZonal}}
func New{{.Service}}ResourceID(project, zone, name string) *ResourceID {
key := meta.ZonalKey(name, zone)
{{- end -}}
{{end}}
return &ResourceID{project, "{{.Resource}}", key}
}
`
tmpl := template.Must(template.New("resourceIDs").Parse(text))
for _, sg := range meta.SortedServicesGroups {
if err := tmpl.Execute(wr, sg.ServiceInfo()); err != nil {
panic(err)
}
}
}
func genUnitTestHeader(wr io.Writer) {
const text = `/*
Copyright {{.Year}} The Kubernetes Authors.
@ -1238,20 +1269,86 @@ func Test{{.Service}}Group(t *testing.T) {
}
`
tmpl := template.Must(template.New("unittest").Parse(text))
// Sort keys so the output will be stable.
var keys []string
for k := range meta.AllServicesByGroup {
keys = append(keys, k)
}
sort.Strings(keys)
for _, k := range keys {
s := meta.AllServicesByGroup[k]
if err := tmpl.Execute(wr, s); err != nil {
for _, sg := range meta.SortedServicesGroups {
if err := tmpl.Execute(wr, sg); err != nil {
panic(err)
}
}
}
func genUnitTestResourceIDConversion(wr io.Writer) {
const text = `
func TestResourceIDConversion(t *testing.T) {
t.Parallel()
for _, id := range []*ResourceID{
{{- range .Groups}}
{{- with .ServiceInfo}}
{{- if .KeyIsProject}}
New{{.Service}}ResourceID("my-{{.Resource}}-resource"),
{{- else}}
{{- if .KeyIsGlobal}}
New{{.Service}}ResourceID("some-project", "my-{{.Resource}}-resource"),
{{- end}}
{{- if .KeyIsRegional}}
New{{.Service}}ResourceID("some-project", "us-central1", "my-{{.Resource}}-resource"),
{{- end}}
{{- if .KeyIsZonal}}
New{{.Service}}ResourceID("some-project", "us-east1-b", "my-{{.Resource}}-resource"),
{{- end -}}
{{end -}}
{{end -}}
{{end}}
} {
t.Run(id.Resource, func(t *testing.T) {
// Test conversion to and from full URL.
fullURL := id.SelfLink(meta.VersionGA)
parsedID, err := ParseResourceURL(fullURL)
if err != nil {
t.Errorf("ParseResourceURL(%s) = _, %v, want nil", fullURL, err)
}
if !reflect.DeepEqual(id, parsedID) {
t.Errorf("SelfLink(%+v) -> ParseResourceURL(%s) = %+v, want original ID", id, fullURL, parsedID)
}
// Test conversion to and from relative resource name.
relativeName := id.RelativeResourceName()
parsedID, err = ParseResourceURL(relativeName)
if err != nil {
t.Errorf("ParseResourceURL(%s) = _, %v, want nil", relativeName, err)
}
if !reflect.DeepEqual(id, parsedID) {
t.Errorf("RelativeResourceName(%+v) -> ParseResourceURL(%s) = %+v, want original ID", id, relativeName, parsedID)
}
// Do not test ResourcePath for projects.
if id.Resource == "projects" {
return
}
// Test conversion to and from resource path.
resourcePath := id.ResourcePath()
parsedID, err = ParseResourceURL(resourcePath)
if err != nil {
t.Errorf("ParseResourceURL(%s) = _, %v, want nil", resourcePath, err)
}
id.ProjectID = ""
if !reflect.DeepEqual(id, parsedID) {
t.Errorf("ResourcePath(%+v) -> ParseResourceURL(%s) = %+v, want %+v", id, resourcePath, parsedID, id)
}
})
}
}
`
data := struct {
Groups []*meta.ServiceGroup
}{meta.SortedServicesGroups}
tmpl := template.Must(template.New("unittest-resourceIDs").Parse(text))
if err := tmpl.Execute(wr, data); err != nil {
panic(err)
}
}
func main() {
flag.Parse()
@ -1262,9 +1359,11 @@ func main() {
genHeader(out)
genStubs(out)
genTypes(out)
genResourceIDs(out)
case "test":
genUnitTestHeader(out)
genUnitTestServices(out)
genUnitTestResourceIDConversion(out)
default:
log.Fatalf("Invalid -mode: %q", flags.mode)
}

View File

@ -179,6 +179,8 @@ func TestBackendServicesGroup(t *testing.T) {
var key *meta.Key
keyAlpha := meta.GlobalKey("key-alpha")
key = keyAlpha
keyBeta := meta.GlobalKey("key-beta")
key = keyBeta
keyGA := meta.GlobalKey("key-ga")
key = keyGA
// Ignore unused variables.
@ -188,6 +190,9 @@ func TestBackendServicesGroup(t *testing.T) {
if _, err := mock.AlphaBackendServices().Get(ctx, key); err == nil {
t.Errorf("AlphaBackendServices().Get(%v, %v) = _, nil; want error", ctx, key)
}
if _, err := mock.BetaBackendServices().Get(ctx, key); err == nil {
t.Errorf("BetaBackendServices().Get(%v, %v) = _, nil; want error", ctx, key)
}
if _, err := mock.BackendServices().Get(ctx, key); err == nil {
t.Errorf("BackendServices().Get(%v, %v) = _, nil; want error", ctx, key)
}
@ -199,6 +204,12 @@ func TestBackendServicesGroup(t *testing.T) {
t.Errorf("AlphaBackendServices().Insert(%v, %v, %v) = %v; want nil", ctx, keyAlpha, obj, err)
}
}
{
obj := &beta.BackendService{}
if err := mock.BetaBackendServices().Insert(ctx, keyBeta, obj); err != nil {
t.Errorf("BetaBackendServices().Insert(%v, %v, %v) = %v; want nil", ctx, keyBeta, obj, err)
}
}
{
obj := &ga.BackendService{}
if err := mock.BackendServices().Insert(ctx, keyGA, obj); err != nil {
@ -210,15 +221,20 @@ func TestBackendServicesGroup(t *testing.T) {
if obj, err := mock.AlphaBackendServices().Get(ctx, key); err != nil {
t.Errorf("AlphaBackendServices().Get(%v, %v) = %v, %v; want nil", ctx, key, obj, err)
}
if obj, err := mock.BetaBackendServices().Get(ctx, key); err != nil {
t.Errorf("BetaBackendServices().Get(%v, %v) = %v, %v; want nil", ctx, key, obj, err)
}
if obj, err := mock.BackendServices().Get(ctx, key); err != nil {
t.Errorf("BackendServices().Get(%v, %v) = %v, %v; want nil", ctx, key, obj, err)
}
// List.
mock.MockAlphaBackendServices.Objects[*keyAlpha] = mock.MockAlphaBackendServices.Obj(&alpha.BackendService{Name: keyAlpha.Name})
mock.MockBetaBackendServices.Objects[*keyBeta] = mock.MockBetaBackendServices.Obj(&beta.BackendService{Name: keyBeta.Name})
mock.MockBackendServices.Objects[*keyGA] = mock.MockBackendServices.Obj(&ga.BackendService{Name: keyGA.Name})
want := map[string]bool{
"key-alpha": true,
"key-beta": true,
"key-ga": true,
}
_ = want // ignore unused variables.
@ -236,6 +252,20 @@ func TestBackendServicesGroup(t *testing.T) {
}
}
}
{
objs, err := mock.BetaBackendServices().List(ctx, filter.None)
if err != nil {
t.Errorf("BetaBackendServices().List(%v, %v, %v) = %v, %v; want _, nil", ctx, location, filter.None, objs, err)
} else {
got := map[string]bool{}
for _, obj := range objs {
got[obj.Name] = true
}
if !reflect.DeepEqual(got, want) {
t.Errorf("AlphaBackendServices().List(); got %+v, want %+v", got, want)
}
}
}
{
objs, err := mock.BackendServices().List(ctx, filter.None)
if err != nil {
@ -255,6 +285,9 @@ func TestBackendServicesGroup(t *testing.T) {
if err := mock.AlphaBackendServices().Delete(ctx, keyAlpha); err != nil {
t.Errorf("AlphaBackendServices().Delete(%v, %v) = %v; want nil", ctx, keyAlpha, err)
}
if err := mock.BetaBackendServices().Delete(ctx, keyBeta); err != nil {
t.Errorf("BetaBackendServices().Delete(%v, %v) = %v; want nil", ctx, keyBeta, err)
}
if err := mock.BackendServices().Delete(ctx, keyGA); err != nil {
t.Errorf("BackendServices().Delete(%v, %v) = %v; want nil", ctx, keyGA, err)
}
@ -263,6 +296,9 @@ func TestBackendServicesGroup(t *testing.T) {
if err := mock.AlphaBackendServices().Delete(ctx, keyAlpha); err == nil {
t.Errorf("AlphaBackendServices().Delete(%v, %v) = nil; want error", ctx, keyAlpha)
}
if err := mock.BetaBackendServices().Delete(ctx, keyBeta); err == nil {
t.Errorf("BetaBackendServices().Delete(%v, %v) = nil; want error", ctx, keyBeta)
}
if err := mock.BackendServices().Delete(ctx, keyGA); err == nil {
t.Errorf("BackendServices().Delete(%v, %v) = nil; want error", ctx, keyGA)
}
@ -276,28 +312,17 @@ func TestDisksGroup(t *testing.T) {
mock := NewMockGCE(pr)
var key *meta.Key
keyAlpha := meta.ZonalKey("key-alpha", "location")
key = keyAlpha
keyGA := meta.ZonalKey("key-ga", "location")
key = keyGA
// Ignore unused variables.
_, _, _ = ctx, mock, key
// Get not found.
if _, err := mock.AlphaDisks().Get(ctx, key); err == nil {
t.Errorf("AlphaDisks().Get(%v, %v) = _, nil; want error", ctx, key)
}
if _, err := mock.Disks().Get(ctx, key); err == nil {
t.Errorf("Disks().Get(%v, %v) = _, nil; want error", ctx, key)
}
// Insert.
{
obj := &alpha.Disk{}
if err := mock.AlphaDisks().Insert(ctx, keyAlpha, obj); err != nil {
t.Errorf("AlphaDisks().Insert(%v, %v, %v) = %v; want nil", ctx, keyAlpha, obj, err)
}
}
{
obj := &ga.Disk{}
if err := mock.Disks().Insert(ctx, keyGA, obj); err != nil {
@ -306,35 +331,16 @@ func TestDisksGroup(t *testing.T) {
}
// Get across versions.
if obj, err := mock.AlphaDisks().Get(ctx, key); err != nil {
t.Errorf("AlphaDisks().Get(%v, %v) = %v, %v; want nil", ctx, key, obj, err)
}
if obj, err := mock.Disks().Get(ctx, key); err != nil {
t.Errorf("Disks().Get(%v, %v) = %v, %v; want nil", ctx, key, obj, err)
}
// List.
mock.MockAlphaDisks.Objects[*keyAlpha] = mock.MockAlphaDisks.Obj(&alpha.Disk{Name: keyAlpha.Name})
mock.MockDisks.Objects[*keyGA] = mock.MockDisks.Obj(&ga.Disk{Name: keyGA.Name})
want := map[string]bool{
"key-alpha": true,
"key-ga": true,
"key-ga": true,
}
_ = want // ignore unused variables.
{
objs, err := mock.AlphaDisks().List(ctx, location, filter.None)
if err != nil {
t.Errorf("AlphaDisks().List(%v, %v, %v) = %v, %v; want _, nil", ctx, location, filter.None, objs, err)
} else {
got := map[string]bool{}
for _, obj := range objs {
got[obj.Name] = true
}
if !reflect.DeepEqual(got, want) {
t.Errorf("AlphaDisks().List(); got %+v, want %+v", got, want)
}
}
}
{
objs, err := mock.Disks().List(ctx, location, filter.None)
if err != nil {
@ -351,17 +357,11 @@ func TestDisksGroup(t *testing.T) {
}
// Delete across versions.
if err := mock.AlphaDisks().Delete(ctx, keyAlpha); err != nil {
t.Errorf("AlphaDisks().Delete(%v, %v) = %v; want nil", ctx, keyAlpha, err)
}
if err := mock.Disks().Delete(ctx, keyGA); err != nil {
t.Errorf("Disks().Delete(%v, %v) = %v; want nil", ctx, keyGA, err)
}
// Delete not found.
if err := mock.AlphaDisks().Delete(ctx, keyAlpha); err == nil {
t.Errorf("AlphaDisks().Delete(%v, %v) = nil; want error", ctx, keyAlpha)
}
if err := mock.Disks().Delete(ctx, keyGA); err == nil {
t.Errorf("Disks().Delete(%v, %v) = nil; want error", ctx, keyGA)
}
@ -1279,39 +1279,39 @@ func TestRegionDisksGroup(t *testing.T) {
mock := NewMockGCE(pr)
var key *meta.Key
keyAlpha := meta.RegionalKey("key-alpha", "location")
key = keyAlpha
keyBeta := meta.RegionalKey("key-beta", "location")
key = keyBeta
// Ignore unused variables.
_, _, _ = ctx, mock, key
// Get not found.
if _, err := mock.AlphaRegionDisks().Get(ctx, key); err == nil {
t.Errorf("AlphaRegionDisks().Get(%v, %v) = _, nil; want error", ctx, key)
if _, err := mock.BetaRegionDisks().Get(ctx, key); err == nil {
t.Errorf("BetaRegionDisks().Get(%v, %v) = _, nil; want error", ctx, key)
}
// Insert.
{
obj := &alpha.Disk{}
if err := mock.AlphaRegionDisks().Insert(ctx, keyAlpha, obj); err != nil {
t.Errorf("AlphaRegionDisks().Insert(%v, %v, %v) = %v; want nil", ctx, keyAlpha, obj, err)
obj := &beta.Disk{}
if err := mock.BetaRegionDisks().Insert(ctx, keyBeta, obj); err != nil {
t.Errorf("BetaRegionDisks().Insert(%v, %v, %v) = %v; want nil", ctx, keyBeta, obj, err)
}
}
// Get across versions.
if obj, err := mock.AlphaRegionDisks().Get(ctx, key); err != nil {
t.Errorf("AlphaRegionDisks().Get(%v, %v) = %v, %v; want nil", ctx, key, obj, err)
if obj, err := mock.BetaRegionDisks().Get(ctx, key); err != nil {
t.Errorf("BetaRegionDisks().Get(%v, %v) = %v, %v; want nil", ctx, key, obj, err)
}
// List.
mock.MockAlphaRegionDisks.Objects[*keyAlpha] = mock.MockAlphaRegionDisks.Obj(&alpha.Disk{Name: keyAlpha.Name})
mock.MockBetaRegionDisks.Objects[*keyBeta] = mock.MockBetaRegionDisks.Obj(&beta.Disk{Name: keyBeta.Name})
want := map[string]bool{
"key-alpha": true,
"key-beta": true,
}
_ = want // ignore unused variables.
{
objs, err := mock.AlphaRegionDisks().List(ctx, location, filter.None)
objs, err := mock.BetaRegionDisks().List(ctx, location, filter.None)
if err != nil {
t.Errorf("AlphaRegionDisks().List(%v, %v, %v) = %v, %v; want _, nil", ctx, location, filter.None, objs, err)
t.Errorf("BetaRegionDisks().List(%v, %v, %v) = %v, %v; want _, nil", ctx, location, filter.None, objs, err)
} else {
got := map[string]bool{}
for _, obj := range objs {
@ -1324,13 +1324,13 @@ func TestRegionDisksGroup(t *testing.T) {
}
// Delete across versions.
if err := mock.AlphaRegionDisks().Delete(ctx, keyAlpha); err != nil {
t.Errorf("AlphaRegionDisks().Delete(%v, %v) = %v; want nil", ctx, keyAlpha, err)
if err := mock.BetaRegionDisks().Delete(ctx, keyBeta); err != nil {
t.Errorf("BetaRegionDisks().Delete(%v, %v) = %v; want nil", ctx, keyBeta, err)
}
// Delete not found.
if err := mock.AlphaRegionDisks().Delete(ctx, keyAlpha); err == nil {
t.Errorf("AlphaRegionDisks().Delete(%v, %v) = nil; want error", ctx, keyAlpha)
if err := mock.BetaRegionDisks().Delete(ctx, keyBeta); err == nil {
t.Errorf("BetaRegionDisks().Delete(%v, %v) = nil; want error", ctx, keyBeta)
}
}
@ -1445,6 +1445,69 @@ func TestRoutesGroup(t *testing.T) {
}
}
func TestSecurityPoliciesGroup(t *testing.T) {
t.Parallel()
ctx := context.Background()
pr := &SingleProjectRouter{"mock-project"}
mock := NewMockGCE(pr)
var key *meta.Key
keyBeta := meta.GlobalKey("key-beta")
key = keyBeta
// Ignore unused variables.
_, _, _ = ctx, mock, key
// Get not found.
if _, err := mock.BetaSecurityPolicies().Get(ctx, key); err == nil {
t.Errorf("BetaSecurityPolicies().Get(%v, %v) = _, nil; want error", ctx, key)
}
// Insert.
{
obj := &beta.SecurityPolicy{}
if err := mock.BetaSecurityPolicies().Insert(ctx, keyBeta, obj); err != nil {
t.Errorf("BetaSecurityPolicies().Insert(%v, %v, %v) = %v; want nil", ctx, keyBeta, obj, err)
}
}
// Get across versions.
if obj, err := mock.BetaSecurityPolicies().Get(ctx, key); err != nil {
t.Errorf("BetaSecurityPolicies().Get(%v, %v) = %v, %v; want nil", ctx, key, obj, err)
}
// List.
mock.MockBetaSecurityPolicies.Objects[*keyBeta] = mock.MockBetaSecurityPolicies.Obj(&beta.SecurityPolicy{Name: keyBeta.Name})
want := map[string]bool{
"key-beta": true,
}
_ = want // ignore unused variables.
{
objs, err := mock.BetaSecurityPolicies().List(ctx, filter.None)
if err != nil {
t.Errorf("BetaSecurityPolicies().List(%v, %v, %v) = %v, %v; want _, nil", ctx, location, filter.None, objs, err)
} else {
got := map[string]bool{}
for _, obj := range objs {
got[obj.Name] = true
}
if !reflect.DeepEqual(got, want) {
t.Errorf("AlphaSecurityPolicies().List(); got %+v, want %+v", got, want)
}
}
}
// Delete across versions.
if err := mock.BetaSecurityPolicies().Delete(ctx, keyBeta); err != nil {
t.Errorf("BetaSecurityPolicies().Delete(%v, %v) = %v; want nil", ctx, keyBeta, err)
}
// Delete not found.
if err := mock.BetaSecurityPolicies().Delete(ctx, keyBeta); err == nil {
t.Errorf("BetaSecurityPolicies().Delete(%v, %v) = nil; want error", ctx, keyBeta)
}
}
func TestSslCertificatesGroup(t *testing.T) {
t.Parallel()
@ -1807,3 +1870,73 @@ func TestZonesGroup(t *testing.T) {
// Delete not found.
}
func TestResourceIDConversion(t *testing.T) {
t.Parallel()
for _, id := range []*ResourceID{
NewAddressesResourceID("some-project", "us-central1", "my-addresses-resource"),
NewBackendServicesResourceID("some-project", "my-backendServices-resource"),
NewDisksResourceID("some-project", "us-east1-b", "my-disks-resource"),
NewFirewallsResourceID("some-project", "my-firewalls-resource"),
NewForwardingRulesResourceID("some-project", "us-central1", "my-forwardingRules-resource"),
NewGlobalAddressesResourceID("some-project", "my-addresses-resource"),
NewGlobalForwardingRulesResourceID("some-project", "my-forwardingRules-resource"),
NewHealthChecksResourceID("some-project", "my-healthChecks-resource"),
NewHttpHealthChecksResourceID("some-project", "my-httpHealthChecks-resource"),
NewHttpsHealthChecksResourceID("some-project", "my-httpsHealthChecks-resource"),
NewInstanceGroupsResourceID("some-project", "us-east1-b", "my-instanceGroups-resource"),
NewInstancesResourceID("some-project", "us-east1-b", "my-instances-resource"),
NewNetworkEndpointGroupsResourceID("some-project", "us-east1-b", "my-networkEndpointGroups-resource"),
NewProjectsResourceID("my-projects-resource"),
NewRegionBackendServicesResourceID("some-project", "us-central1", "my-backendServices-resource"),
NewRegionDisksResourceID("some-project", "us-central1", "my-disks-resource"),
NewRegionsResourceID("some-project", "my-regions-resource"),
NewRoutesResourceID("some-project", "my-routes-resource"),
NewSecurityPoliciesResourceID("some-project", "my-securityPolicies-resource"),
NewSslCertificatesResourceID("some-project", "my-sslCertificates-resource"),
NewTargetHttpProxiesResourceID("some-project", "my-targetHttpProxies-resource"),
NewTargetHttpsProxiesResourceID("some-project", "my-targetHttpsProxies-resource"),
NewTargetPoolsResourceID("some-project", "us-central1", "my-targetPools-resource"),
NewUrlMapsResourceID("some-project", "my-urlMaps-resource"),
NewZonesResourceID("some-project", "my-zones-resource"),
} {
t.Run(id.Resource, func(t *testing.T) {
// Test conversion to and from full URL.
fullURL := id.SelfLink(meta.VersionGA)
parsedID, err := ParseResourceURL(fullURL)
if err != nil {
t.Errorf("ParseResourceURL(%s) = _, %v, want nil", fullURL, err)
}
if !reflect.DeepEqual(id, parsedID) {
t.Errorf("SelfLink(%+v) -> ParseResourceURL(%s) = %+v, want original ID", id, fullURL, parsedID)
}
// Test conversion to and from relative resource name.
relativeName := id.RelativeResourceName()
parsedID, err = ParseResourceURL(relativeName)
if err != nil {
t.Errorf("ParseResourceURL(%s) = _, %v, want nil", relativeName, err)
}
if !reflect.DeepEqual(id, parsedID) {
t.Errorf("RelativeResourceName(%+v) -> ParseResourceURL(%s) = %+v, want original ID", id, relativeName, parsedID)
}
// Do not test ResourcePath for projects.
if id.Resource == "projects" {
return
}
// Test conversion to and from resource path.
resourcePath := id.ResourcePath()
parsedID, err = ParseResourceURL(resourcePath)
if err != nil {
t.Errorf("ParseResourceURL(%s) = _, %v, want nil", resourcePath, err)
}
id.ProjectID = ""
if !reflect.DeepEqual(id, parsedID) {
t.Errorf("ResourcePath(%+v) -> ParseResourceURL(%s) = %+v, want %+v", id, resourcePath, parsedID, id)
}
})
}
}

View File

@ -103,17 +103,32 @@ var AllServices = []*ServiceInfo{
serviceType: reflect.TypeOf(&ga.BackendServicesService{}),
additionalMethods: []string{
"GetHealth",
"Patch",
"Update",
},
},
{
Object: "BackendService",
Service: "BackendServices",
Resource: "backendServices",
version: VersionAlpha,
keyType: Global,
serviceType: reflect.TypeOf(&alpha.BackendServicesService{}),
additionalMethods: []string{"Update"},
Object: "BackendService",
Service: "BackendServices",
Resource: "backendServices",
version: VersionBeta,
keyType: Global,
serviceType: reflect.TypeOf(&beta.BackendServicesService{}),
additionalMethods: []string{
"SetSecurityPolicy",
},
},
{
Object: "BackendService",
Service: "BackendServices",
Resource: "backendServices",
version: VersionAlpha,
keyType: Global,
serviceType: reflect.TypeOf(&alpha.BackendServicesService{}),
additionalMethods: []string{
"Update",
"SetSecurityPolicy",
},
},
{
Object: "BackendService",
@ -145,22 +160,20 @@ var AllServices = []*ServiceInfo{
Resource: "disks",
keyType: Zonal,
serviceType: reflect.TypeOf(&ga.DisksService{}),
},
{
Object: "Disk",
Service: "Disks",
Resource: "disks",
version: VersionAlpha,
keyType: Zonal,
serviceType: reflect.TypeOf(&alpha.DisksService{}),
additionalMethods: []string{
"Resize",
},
},
{
Object: "Disk",
Service: "RegionDisks",
Resource: "disks",
version: VersionAlpha,
version: VersionBeta,
keyType: Regional,
serviceType: reflect.TypeOf(&alpha.DisksService{}),
serviceType: reflect.TypeOf(&beta.RegionDisksService{}),
additionalMethods: []string{
"Resize",
},
},
{
Object: "Firewall",
@ -326,6 +339,21 @@ var AllServices = []*ServiceInfo{
keyType: Global,
serviceType: reflect.TypeOf(&ga.RoutesService{}),
},
{
Object: "SecurityPolicy",
Service: "SecurityPolicies",
Resource: "securityPolicies",
version: VersionBeta,
keyType: Global,
serviceType: reflect.TypeOf(&beta.SecurityPoliciesService{}),
additionalMethods: []string{
"AddRule",
"GetRule",
"Patch",
"PatchRule",
"RemoveRule",
},
},
{
Object: "SslCertificate",
Service: "SslCertificates",

View File

@ -20,6 +20,7 @@ import (
"errors"
"fmt"
"reflect"
"sort"
)
// ServiceInfo defines the entry for a Service that code will be generated for.
@ -159,6 +160,13 @@ func (i *ServiceInfo) KeyIsZonal() bool {
return i.keyType == Zonal
}
// KeyIsProject is true if the key represents the project resource.
func (i *ServiceInfo) KeyIsProject() bool {
// Projects are a special resource for ResourceId because there is no 'key' value. This func
// is used by the generator to not accept a key parameter.
return i.Service == "Projects"
}
// MakeKey returns the call used to create the appropriate key type.
func (i *ServiceInfo) MakeKey(name, location string) string {
switch i.keyType {
@ -220,15 +228,20 @@ type ServiceGroup struct {
GA *ServiceInfo
}
// Service returns any ServiceInfo object belonging to the ServiceGroup.
// Service returns any ServiceInfo string belonging to the ServiceGroup.
func (sg *ServiceGroup) Service() string {
return sg.ServiceInfo().Service
}
// ServiceInfo returns any ServiceInfo object belonging to the ServiceGroup.
func (sg *ServiceGroup) ServiceInfo() *ServiceInfo {
switch {
case sg.GA != nil:
return sg.GA.Service
return sg.GA
case sg.Alpha != nil:
return sg.Alpha.Service
return sg.Alpha
case sg.Beta != nil:
return sg.Beta.Service
return sg.Beta
default:
panic(errors.New("service group is empty"))
}
@ -272,6 +285,16 @@ func groupServices(services []*ServiceInfo) map[string]*ServiceGroup {
// AllServicesByGroup is a map of service name to ServicesGroup.
var AllServicesByGroup map[string]*ServiceGroup
// SortedServicesGroups is a slice of Servicegroup sorted by Service name.
var SortedServicesGroups []*ServiceGroup
func init() {
AllServicesByGroup = groupServices(AllServices)
for _, sg := range AllServicesByGroup {
SortedServicesGroups = append(SortedServicesGroups, sg)
}
sort.Slice(SortedServicesGroups, func(i, j int) bool {
return SortedServicesGroups[i].Service() < SortedServicesGroups[j].Service()
})
}

View File

@ -7,6 +7,7 @@ go_library(
visibility = ["//visibility:public"],
deps = [
"//pkg/cloudprovider/providers/gce/cloud:go_default_library",
"//pkg/cloudprovider/providers/gce/cloud/filter:go_default_library",
"//pkg/cloudprovider/providers/gce/cloud/meta:go_default_library",
"//vendor/google.golang.org/api/compute/v0.alpha:go_default_library",
"//vendor/google.golang.org/api/compute/v0.beta:go_default_library",

View File

@ -28,15 +28,24 @@ import (
"encoding/json"
"fmt"
"net/http"
"sync"
alpha "google.golang.org/api/compute/v0.alpha"
beta "google.golang.org/api/compute/v0.beta"
ga "google.golang.org/api/compute/v1"
"google.golang.org/api/googleapi"
"k8s.io/kubernetes/pkg/cloudprovider/providers/gce/cloud"
cloud "k8s.io/kubernetes/pkg/cloudprovider/providers/gce/cloud"
"k8s.io/kubernetes/pkg/cloudprovider/providers/gce/cloud/filter"
"k8s.io/kubernetes/pkg/cloudprovider/providers/gce/cloud/meta"
)
var (
// InUseError is a shared variable with error code StatusBadRequest for error verification.
InUseError = &googleapi.Error{Code: http.StatusBadRequest, Message: "It's being used by god."}
// InternalServerError is shared variable with error code StatusInternalServerError for error verification.
InternalServerError = &googleapi.Error{Code: http.StatusInternalServerError}
)
// gceObject is an abstraction of all GCE API object in go client
type gceObject interface {
MarshalJSON() ([]byte, error)
@ -85,7 +94,7 @@ func RemoveInstanceHook(ctx context.Context, key *meta.Key, req *ga.TargetPoolsR
func convertAndInsertAlphaForwardingRule(key *meta.Key, obj gceObject, mRules map[meta.Key]*cloud.MockForwardingRulesObj, version meta.Version, projectID string) (bool, error) {
if !key.Valid() {
return false, fmt.Errorf("invalid GCE key (%+v)", key)
return true, fmt.Errorf("invalid GCE key (%+v)", key)
}
if _, ok := mRules[*key]; ok {
@ -93,16 +102,16 @@ func convertAndInsertAlphaForwardingRule(key *meta.Key, obj gceObject, mRules ma
Code: http.StatusConflict,
Message: fmt.Sprintf("MockForwardingRule %v exists", key),
}
return false, err
return true, err
}
enc, err := obj.MarshalJSON()
if err != nil {
return false, err
return true, err
}
var fwdRule alpha.ForwardingRule
if err := json.Unmarshal(enc, &fwdRule); err != nil {
return false, err
return true, err
}
// Set the default values for the Alpha fields.
if fwdRule.NetworkTier == "" {
@ -121,28 +130,39 @@ func convertAndInsertAlphaForwardingRule(key *meta.Key, obj gceObject, mRules ma
// InsertFwdRuleHook mocks inserting a ForwardingRule. ForwardingRules are
// expected to default to Premium tier if no NetworkTier is specified.
func InsertFwdRuleHook(ctx context.Context, key *meta.Key, obj *ga.ForwardingRule, m *cloud.MockForwardingRules) (bool, error) {
m.Lock.Lock()
defer m.Lock.Unlock()
projectID := m.ProjectRouter.ProjectID(ctx, meta.VersionGA, "forwardingRules")
return convertAndInsertAlphaForwardingRule(key, obj, m.Objects, meta.VersionGA, projectID)
}
// InsertBetaFwdRuleHook mocks inserting a BetaForwardingRule.
func InsertBetaFwdRuleHook(ctx context.Context, key *meta.Key, obj *beta.ForwardingRule, m *cloud.MockForwardingRules) (bool, error) {
m.Lock.Lock()
defer m.Lock.Unlock()
projectID := m.ProjectRouter.ProjectID(ctx, meta.VersionBeta, "forwardingRules")
return convertAndInsertAlphaForwardingRule(key, obj, m.Objects, meta.VersionBeta, projectID)
}
// InsertAlphaFwdRuleHook mocks inserting an AlphaForwardingRule.
func InsertAlphaFwdRuleHook(ctx context.Context, key *meta.Key, obj *alpha.ForwardingRule, m *cloud.MockForwardingRules) (bool, error) {
m.Lock.Lock()
defer m.Lock.Unlock()
projectID := m.ProjectRouter.ProjectID(ctx, meta.VersionAlpha, "forwardingRules")
return convertAndInsertAlphaForwardingRule(key, obj, m.Objects, meta.VersionAlpha, projectID)
}
// Used to assign Addresses with no IP a unique IP address
var ipCounter = 1
// AddressAttributes maps from Address key to a map of Instances
type AddressAttributes struct {
IPCounter int // Used to assign Addresses with no IP a unique IP address
}
func convertAndInsertAlphaAddress(key *meta.Key, obj gceObject, mAddrs map[meta.Key]*cloud.MockAddressesObj, version meta.Version, projectID string) (bool, error) {
func convertAndInsertAlphaAddress(key *meta.Key, obj gceObject, mAddrs map[meta.Key]*cloud.MockAddressesObj, version meta.Version, projectID string, addressAttrs AddressAttributes) (bool, error) {
if !key.Valid() {
return false, fmt.Errorf("invalid GCE key (%+v)", key)
return true, fmt.Errorf("invalid GCE key (%+v)", key)
}
if _, ok := mAddrs[*key]; ok {
@ -150,16 +170,16 @@ func convertAndInsertAlphaAddress(key *meta.Key, obj gceObject, mAddrs map[meta.
Code: http.StatusConflict,
Message: fmt.Sprintf("MockAddresses %v exists", key),
}
return false, err
return true, err
}
enc, err := obj.MarshalJSON()
if err != nil {
return false, err
return true, err
}
var addr alpha.Address
if err := json.Unmarshal(enc, &addr); err != nil {
return false, err
return true, err
}
// Set default address type if not present.
@ -184,7 +204,7 @@ func convertAndInsertAlphaAddress(key *meta.Key, obj gceObject, mAddrs map[meta.
errorCode = http.StatusBadRequest
}
return false, &googleapi.Error{Code: errorCode, Message: msg}
return true, &googleapi.Error{Code: errorCode, Message: msg}
}
}
@ -195,8 +215,8 @@ func convertAndInsertAlphaAddress(key *meta.Key, obj gceObject, mAddrs map[meta.
}
if addr.Address == "" {
addr.Address = fmt.Sprintf("1.2.3.%d", ipCounter)
ipCounter++
addr.Address = fmt.Sprintf("1.2.3.%d", addressAttrs.IPCounter)
addressAttrs.IPCounter++
}
// Set the default values for the Alpha fields.
@ -210,19 +230,323 @@ func convertAndInsertAlphaAddress(key *meta.Key, obj gceObject, mAddrs map[meta.
// InsertAddressHook mocks inserting an Address.
func InsertAddressHook(ctx context.Context, key *meta.Key, obj *ga.Address, m *cloud.MockAddresses) (bool, error) {
m.Lock.Lock()
defer m.Lock.Unlock()
projectID := m.ProjectRouter.ProjectID(ctx, meta.VersionGA, "addresses")
return convertAndInsertAlphaAddress(key, obj, m.Objects, meta.VersionGA, projectID)
return convertAndInsertAlphaAddress(key, obj, m.Objects, meta.VersionGA, projectID, m.X.(AddressAttributes))
}
// InsertBetaAddressHook mocks inserting a BetaAddress.
func InsertBetaAddressHook(ctx context.Context, key *meta.Key, obj *beta.Address, m *cloud.MockAddresses) (bool, error) {
m.Lock.Lock()
defer m.Lock.Unlock()
projectID := m.ProjectRouter.ProjectID(ctx, meta.VersionBeta, "addresses")
return convertAndInsertAlphaAddress(key, obj, m.Objects, meta.VersionBeta, projectID)
return convertAndInsertAlphaAddress(key, obj, m.Objects, meta.VersionBeta, projectID, m.X.(AddressAttributes))
}
// InsertAlphaAddressHook mocks inserting an Address. Addresses are expected to
// default to Premium tier if no NetworkTier is specified.
func InsertAlphaAddressHook(ctx context.Context, key *meta.Key, obj *alpha.Address, m *cloud.MockAlphaAddresses) (bool, error) {
m.Lock.Lock()
defer m.Lock.Unlock()
projectID := m.ProjectRouter.ProjectID(ctx, meta.VersionBeta, "addresses")
return convertAndInsertAlphaAddress(key, obj, m.Objects, meta.VersionAlpha, projectID)
return convertAndInsertAlphaAddress(key, obj, m.Objects, meta.VersionAlpha, projectID, m.X.(AddressAttributes))
}
// InstanceGroupAttributes maps from InstanceGroup key to a map of Instances
type InstanceGroupAttributes struct {
InstanceMap map[meta.Key]map[string]*ga.InstanceWithNamedPorts
Lock *sync.Mutex
}
// AddInstances adds a list of Instances passed by InstanceReference
func (igAttrs *InstanceGroupAttributes) AddInstances(key *meta.Key, instanceRefs []*ga.InstanceReference) error {
igAttrs.Lock.Lock()
defer igAttrs.Lock.Unlock()
instancesWithNamedPorts, ok := igAttrs.InstanceMap[*key]
if !ok {
instancesWithNamedPorts = make(map[string]*ga.InstanceWithNamedPorts)
}
for _, instance := range instanceRefs {
iWithPort := &ga.InstanceWithNamedPorts{
Instance: instance.Instance,
}
instancesWithNamedPorts[instance.Instance] = iWithPort
}
igAttrs.InstanceMap[*key] = instancesWithNamedPorts
return nil
}
// RemoveInstances removes a list of Instances passed by InstanceReference
func (igAttrs *InstanceGroupAttributes) RemoveInstances(key *meta.Key, instanceRefs []*ga.InstanceReference) error {
igAttrs.Lock.Lock()
defer igAttrs.Lock.Unlock()
instancesWithNamedPorts, ok := igAttrs.InstanceMap[*key]
if !ok {
instancesWithNamedPorts = make(map[string]*ga.InstanceWithNamedPorts)
}
for _, instanceToRemove := range instanceRefs {
if _, ok := instancesWithNamedPorts[instanceToRemove.Instance]; ok {
delete(instancesWithNamedPorts, instanceToRemove.Instance)
} else {
return &googleapi.Error{
Code: http.StatusBadRequest,
Message: fmt.Sprintf("%s is not a member of %s", instanceToRemove.Instance, key.String()),
}
}
}
igAttrs.InstanceMap[*key] = instancesWithNamedPorts
return nil
}
// List gets a list of InstanceWithNamedPorts
func (igAttrs *InstanceGroupAttributes) List(key *meta.Key) []*ga.InstanceWithNamedPorts {
igAttrs.Lock.Lock()
defer igAttrs.Lock.Unlock()
instancesWithNamedPorts, ok := igAttrs.InstanceMap[*key]
if !ok {
instancesWithNamedPorts = make(map[string]*ga.InstanceWithNamedPorts)
}
var instanceList []*ga.InstanceWithNamedPorts
for _, val := range instancesWithNamedPorts {
instanceList = append(instanceList, val)
}
return instanceList
}
// AddInstancesHook mocks adding instances from an InstanceGroup
func AddInstancesHook(ctx context.Context, key *meta.Key, req *ga.InstanceGroupsAddInstancesRequest, m *cloud.MockInstanceGroups) error {
_, err := m.Get(ctx, key)
if err != nil {
return &googleapi.Error{
Code: http.StatusNotFound,
Message: fmt.Sprintf("Key: %s was not found in InstanceGroups", key.String()),
}
}
var attrs InstanceGroupAttributes
attrs = m.X.(InstanceGroupAttributes)
attrs.AddInstances(key, req.Instances)
m.X = attrs
return nil
}
// ListInstancesHook mocks listing instances from an InstanceGroup
func ListInstancesHook(ctx context.Context, key *meta.Key, req *ga.InstanceGroupsListInstancesRequest, filter *filter.F, m *cloud.MockInstanceGroups) ([]*ga.InstanceWithNamedPorts, error) {
_, err := m.Get(ctx, key)
if err != nil {
return nil, &googleapi.Error{
Code: http.StatusNotFound,
Message: fmt.Sprintf("Key: %s was not found in InstanceGroups", key.String()),
}
}
var attrs InstanceGroupAttributes
attrs = m.X.(InstanceGroupAttributes)
instances := attrs.List(key)
return instances, nil
}
// RemoveInstancesHook mocks removing instances from an InstanceGroup
func RemoveInstancesHook(ctx context.Context, key *meta.Key, req *ga.InstanceGroupsRemoveInstancesRequest, m *cloud.MockInstanceGroups) error {
_, err := m.Get(ctx, key)
if err != nil {
return &googleapi.Error{
Code: http.StatusNotFound,
Message: fmt.Sprintf("Key: %s was not found in InstanceGroups", key.String()),
}
}
var attrs InstanceGroupAttributes
attrs = m.X.(InstanceGroupAttributes)
attrs.RemoveInstances(key, req.Instances)
m.X = attrs
return nil
}
// UpdateFirewallHook defines the hook for updating a Firewall. It replaces the
// object with the same key in the mock with the updated object.
func UpdateFirewallHook(ctx context.Context, key *meta.Key, obj *ga.Firewall, m *cloud.MockFirewalls) error {
_, err := m.Get(ctx, key)
if err != nil {
return &googleapi.Error{
Code: http.StatusNotFound,
Message: fmt.Sprintf("Key: %s was not found in Firewalls", key.String()),
}
}
obj.Name = key.Name
projectID := m.ProjectRouter.ProjectID(ctx, "ga", "firewalls")
obj.SelfLink = cloud.SelfLink(meta.VersionGA, projectID, "firewalls", key)
m.Objects[*key] = &cloud.MockFirewallsObj{Obj: obj}
return nil
}
// UpdateHealthCheckHook defines the hook for updating a HealthCheck. It
// replaces the object with the same key in the mock with the updated object.
func UpdateHealthCheckHook(ctx context.Context, key *meta.Key, obj *ga.HealthCheck, m *cloud.MockHealthChecks) error {
_, err := m.Get(ctx, key)
if err != nil {
return &googleapi.Error{
Code: http.StatusNotFound,
Message: fmt.Sprintf("Key: %s was not found in HealthChecks", key.String()),
}
}
obj.Name = key.Name
projectID := m.ProjectRouter.ProjectID(ctx, "ga", "healthChecks")
obj.SelfLink = cloud.SelfLink(meta.VersionGA, projectID, "healthChecks", key)
m.Objects[*key] = &cloud.MockHealthChecksObj{Obj: obj}
return nil
}
// UpdateRegionBackendServiceHook defines the hook for updating a Region
// BackendsService. It replaces the object with the same key in the mock with
// the updated object.
func UpdateRegionBackendServiceHook(ctx context.Context, key *meta.Key, obj *ga.BackendService, m *cloud.MockRegionBackendServices) error {
_, err := m.Get(ctx, key)
if err != nil {
return &googleapi.Error{
Code: http.StatusNotFound,
Message: fmt.Sprintf("Key: %s was not found in RegionBackendServices", key.String()),
}
}
obj.Name = key.Name
projectID := m.ProjectRouter.ProjectID(ctx, "ga", "backendServices")
obj.SelfLink = cloud.SelfLink(meta.VersionGA, projectID, "backendServices", key)
m.Objects[*key] = &cloud.MockRegionBackendServicesObj{Obj: obj}
return nil
}
// InsertFirewallsUnauthorizedErrHook mocks firewall insertion. A forbidden error will be thrown as return.
func InsertFirewallsUnauthorizedErrHook(ctx context.Context, key *meta.Key, obj *ga.Firewall, m *cloud.MockFirewalls) (bool, error) {
return true, &googleapi.Error{Code: http.StatusForbidden}
}
// UpdateFirewallsUnauthorizedErrHook mocks firewall updating. A forbidden error will be thrown as return.
func UpdateFirewallsUnauthorizedErrHook(ctx context.Context, key *meta.Key, obj *ga.Firewall, m *cloud.MockFirewalls) error {
return &googleapi.Error{Code: http.StatusForbidden}
}
// DeleteFirewallsUnauthorizedErrHook mocks firewall deletion. A forbidden error will be thrown as return.
func DeleteFirewallsUnauthorizedErrHook(ctx context.Context, key *meta.Key, m *cloud.MockFirewalls) (bool, error) {
return true, &googleapi.Error{Code: http.StatusForbidden}
}
// GetFirewallsUnauthorizedErrHook mocks firewall information retrival. A forbidden error will be thrown as return.
func GetFirewallsUnauthorizedErrHook(ctx context.Context, key *meta.Key, m *cloud.MockFirewalls) (bool, *ga.Firewall, error) {
return true, nil, &googleapi.Error{Code: http.StatusForbidden}
}
// GetTargetPoolInternalErrHook mocks getting target pool. It returns a internal server error.
func GetTargetPoolInternalErrHook(ctx context.Context, key *meta.Key, m *cloud.MockTargetPools) (bool, *ga.TargetPool, error) {
return true, nil, InternalServerError
}
// GetForwardingRulesInternalErrHook mocks getting forwarding rules and returns an internal server error.
func GetForwardingRulesInternalErrHook(ctx context.Context, key *meta.Key, m *cloud.MockForwardingRules) (bool, *ga.ForwardingRule, error) {
return true, nil, InternalServerError
}
// GetAddressesInternalErrHook mocks getting network address and returns an internal server error.
func GetAddressesInternalErrHook(ctx context.Context, key *meta.Key, m *cloud.MockAddresses) (bool, *ga.Address, error) {
return true, nil, InternalServerError
}
// GetHTTPHealthChecksInternalErrHook mocks getting http health check and returns an internal server error.
func GetHTTPHealthChecksInternalErrHook(ctx context.Context, key *meta.Key, m *cloud.MockHttpHealthChecks) (bool, *ga.HttpHealthCheck, error) {
return true, nil, InternalServerError
}
// InsertTargetPoolsInternalErrHook mocks getting target pool and returns an internal server error.
func InsertTargetPoolsInternalErrHook(ctx context.Context, key *meta.Key, obj *ga.TargetPool, m *cloud.MockTargetPools) (bool, error) {
return true, InternalServerError
}
// InsertForwardingRulesInternalErrHook mocks getting forwarding rule and returns an internal server error.
func InsertForwardingRulesInternalErrHook(ctx context.Context, key *meta.Key, obj *ga.ForwardingRule, m *cloud.MockForwardingRules) (bool, error) {
return true, InternalServerError
}
// DeleteAddressesNotFoundErrHook mocks deleting network address and returns a not found error.
func DeleteAddressesNotFoundErrHook(ctx context.Context, key *meta.Key, m *cloud.MockAddresses) (bool, error) {
return true, &googleapi.Error{Code: http.StatusNotFound}
}
// DeleteAddressesInternalErrHook mocks deleting address and returns an internal server error.
func DeleteAddressesInternalErrHook(ctx context.Context, key *meta.Key, m *cloud.MockAddresses) (bool, error) {
return true, InternalServerError
}
// GetRegionBackendServicesErrHook mocks getting region backend service and returns an internal server error.
func GetRegionBackendServicesErrHook(ctx context.Context, key *meta.Key, m *cloud.MockRegionBackendServices) (bool, *ga.BackendService, error) {
return true, nil, InternalServerError
}
// UpdateRegionBackendServicesErrHook mocks updating a reegion backend service and returns an internal server error.
func UpdateRegionBackendServicesErrHook(ctx context.Context, key *meta.Key, svc *ga.BackendService, m *cloud.MockRegionBackendServices) error {
return InternalServerError
}
// DeleteRegionBackendServicesErrHook mocks deleting region backend service and returns an internal server error.
func DeleteRegionBackendServicesErrHook(ctx context.Context, key *meta.Key, m *cloud.MockRegionBackendServices) (bool, error) {
return true, InternalServerError
}
// DeleteRegionBackendServicesInUseErrHook mocks deleting region backend service and returns an InUseError.
func DeleteRegionBackendServicesInUseErrHook(ctx context.Context, key *meta.Key, m *cloud.MockRegionBackendServices) (bool, error) {
return true, InUseError
}
// GetInstanceGroupInternalErrHook mocks getting instance group and returns an internal server error.
func GetInstanceGroupInternalErrHook(ctx context.Context, key *meta.Key, m *cloud.MockInstanceGroups) (bool, *ga.InstanceGroup, error) {
return true, nil, InternalServerError
}
// GetHealthChecksInternalErrHook mocks getting health check and returns an internal server erorr.
func GetHealthChecksInternalErrHook(ctx context.Context, key *meta.Key, m *cloud.MockHealthChecks) (bool, *ga.HealthCheck, error) {
return true, nil, InternalServerError
}
// DeleteHealthChecksInternalErrHook mocks deleting health check and returns an internal server error.
func DeleteHealthChecksInternalErrHook(ctx context.Context, key *meta.Key, m *cloud.MockHealthChecks) (bool, error) {
return true, InternalServerError
}
// DeleteHealthChecksInuseErrHook mocks deleting health check and returns an in use error.
func DeleteHealthChecksInuseErrHook(ctx context.Context, key *meta.Key, m *cloud.MockHealthChecks) (bool, error) {
return true, InUseError
}
// DeleteForwardingRuleErrHook mocks deleting forwarding rule and returns an internal server error.
func DeleteForwardingRuleErrHook(ctx context.Context, key *meta.Key, m *cloud.MockForwardingRules) (bool, error) {
return true, InternalServerError
}
// ListZonesInternalErrHook mocks listing zone and returns an internal server error.
func ListZonesInternalErrHook(ctx context.Context, fl *filter.F, m *cloud.MockZones) (bool, []*ga.Zone, error) {
return true, []*ga.Zone{}, InternalServerError
}
// DeleteInstanceGroupInternalErrHook mocks deleting instance group and returns an internal server error.
func DeleteInstanceGroupInternalErrHook(ctx context.Context, key *meta.Key, m *cloud.MockInstanceGroups) (bool, error) {
return true, InternalServerError
}

View File

@ -29,10 +29,17 @@ import (
"k8s.io/kubernetes/pkg/cloudprovider/providers/gce/cloud/meta"
)
const (
operationStatusDone = "DONE"
)
// operation is a GCE operation that can be watied on.
type operation interface {
// isDone queries GCE for the done status. This call can block.
isDone(ctx context.Context) (bool, error)
// error returns the resulting error of the operation. This may be nil if the operations
// was successful.
error() error
// rateLimitKey returns the rate limit key to use for the given operation.
// This rate limit will govern how fast the server will be polled for
// operation completion status.
@ -43,6 +50,7 @@ type gaOperation struct {
s *Service
projectID string
key *meta.Key
err error
}
func (o *gaOperation) String() string {
@ -71,7 +79,15 @@ func (o *gaOperation) isDone(ctx context.Context) (bool, error) {
if err != nil {
return false, err
}
return op != nil && op.Status == "DONE", nil
if op == nil || op.Status != operationStatusDone {
return false, nil
}
if op.Error != nil && len(op.Error.Errors) > 0 && op.Error.Errors[0] != nil {
e := op.Error.Errors[0]
o.err = &GCEOperationError{HTTPStatusCode: op.HTTPStatusCode, Code: e.Code, Message: e.Message}
}
return true, nil
}
func (o *gaOperation) rateLimitKey() *RateLimitKey {
@ -83,10 +99,15 @@ func (o *gaOperation) rateLimitKey() *RateLimitKey {
}
}
func (o *gaOperation) error() error {
return o.err
}
type alphaOperation struct {
s *Service
projectID string
key *meta.Key
err error
}
func (o *alphaOperation) String() string {
@ -115,7 +136,15 @@ func (o *alphaOperation) isDone(ctx context.Context) (bool, error) {
if err != nil {
return false, err
}
return op != nil && op.Status == "DONE", nil
if op == nil || op.Status != operationStatusDone {
return false, nil
}
if op.Error != nil && len(op.Error.Errors) > 0 && op.Error.Errors[0] != nil {
e := op.Error.Errors[0]
o.err = &GCEOperationError{HTTPStatusCode: op.HTTPStatusCode, Code: e.Code, Message: e.Message}
}
return true, nil
}
func (o *alphaOperation) rateLimitKey() *RateLimitKey {
@ -127,10 +156,15 @@ func (o *alphaOperation) rateLimitKey() *RateLimitKey {
}
}
func (o *alphaOperation) error() error {
return o.err
}
type betaOperation struct {
s *Service
projectID string
key *meta.Key
err error
}
func (o *betaOperation) String() string {
@ -159,7 +193,15 @@ func (o *betaOperation) isDone(ctx context.Context) (bool, error) {
if err != nil {
return false, err
}
return op != nil && op.Status == "DONE", nil
if op == nil || op.Status != operationStatusDone {
return false, nil
}
if op.Error != nil && len(op.Error.Errors) > 0 && op.Error.Errors[0] != nil {
e := op.Error.Errors[0]
o.err = &GCEOperationError{HTTPStatusCode: op.HTTPStatusCode, Code: e.Code, Message: e.Message}
}
return true, nil
}
func (o *betaOperation) rateLimitKey() *RateLimitKey {
@ -170,3 +212,7 @@ func (o *betaOperation) rateLimitKey() *RateLimitKey {
Version: meta.VersionBeta,
}
}
func (o *betaOperation) error() error {
return o.err
}

View File

@ -47,22 +47,60 @@ type RateLimiter interface {
Accept(ctx context.Context, key *RateLimitKey) error
}
// acceptor is an object which blocks within Accept until a call is allowed to run.
// Accept is a behavior of the flowcontrol.RateLimiter interface.
type acceptor interface {
// Accept blocks until a call is allowed to run.
Accept()
}
// AcceptRateLimiter wraps an Acceptor with RateLimiter parameters.
type AcceptRateLimiter struct {
// Acceptor is the underlying rate limiter.
Acceptor acceptor
}
// Accept wraps an Acceptor and blocks on Accept or context.Done(). Key is ignored.
func (rl *AcceptRateLimiter) Accept(ctx context.Context, key *RateLimitKey) error {
ch := make(chan struct{})
go func() {
rl.Acceptor.Accept()
close(ch)
}()
select {
case <-ch:
break
case <-ctx.Done():
return ctx.Err()
}
return nil
}
// NopRateLimiter is a rate limiter that performs no rate limiting.
type NopRateLimiter struct {
}
// Accept the operation to be rate limited.
// Accept everything immediately.
func (*NopRateLimiter) Accept(ctx context.Context, key *RateLimitKey) error {
// Rate limit polling of the Operation status to avoid hammering GCE
// for the status of an operation.
const pollTime = time.Duration(1) * time.Second
if key.Operation == "Get" && key.Service == "Operations" {
select {
case <-time.NewTimer(pollTime).C:
break
case <-ctx.Done():
return ctx.Err()
}
}
return nil
}
// MinimumRateLimiter wraps a RateLimiter and will only call its Accept until the minimum
// duration has been met or the context is cancelled.
type MinimumRateLimiter struct {
// RateLimiter is the underlying ratelimiter which is called after the mininum time is reacehd.
RateLimiter RateLimiter
// Minimum is the minimum wait time before the underlying ratelimiter is called.
Minimum time.Duration
}
// Accept blocks on the minimum duration and context. Once the minimum duration is met,
// the func is blocked on the underlying ratelimiter.
func (m *MinimumRateLimiter) Accept(ctx context.Context, key *RateLimitKey) error {
select {
case <-time.After(m.Minimum):
return m.RateLimiter.Accept(ctx, key)
case <-ctx.Done():
return ctx.Err()
}
}

View File

@ -0,0 +1,80 @@
/*
Copyright 2018 The Kubernetes Authors.
Licensed under the Apache License, Version 2.0 (the "License");
you may not use this file except in compliance with the License.
You may obtain a copy of the License at
http://www.apache.org/licenses/LICENSE-2.0
Unless required by applicable law or agreed to in writing, software
distributed under the License is distributed on an "AS IS" BASIS,
WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
See the License for the specific language governing permissions and
limitations under the License.
*/
package cloud
import (
"context"
"testing"
"time"
)
type FakeAcceptor struct{ accept func() }
func (f *FakeAcceptor) Accept() {
f.accept()
}
func TestAcceptRateLimiter(t *testing.T) {
fa := &FakeAcceptor{accept: func() {}}
arl := &AcceptRateLimiter{fa}
err := arl.Accept(context.Background(), nil)
if err != nil {
t.Errorf("AcceptRateLimiter.Accept() = %v, want nil", err)
}
// Use context that has been cancelled and expect a context error returned.
ctxCancelled, cancelled := context.WithCancel(context.Background())
cancelled()
// Verify context is cancelled by now.
<-ctxCancelled.Done()
fa.accept = func() { time.Sleep(1 * time.Second) }
err = arl.Accept(ctxCancelled, nil)
if err != ctxCancelled.Err() {
t.Errorf("AcceptRateLimiter.Accept() = %v, want %v", err, ctxCancelled.Err())
}
}
func TestMinimumRateLimiter(t *testing.T) {
fa := &FakeAcceptor{accept: func() {}}
arl := &AcceptRateLimiter{fa}
var called bool
fa.accept = func() { called = true }
m := &MinimumRateLimiter{RateLimiter: arl, Minimum: 10 * time.Millisecond}
err := m.Accept(context.Background(), nil)
if err != nil {
t.Errorf("MinimumRateLimiter.Accept = %v, want nil", err)
}
if !called {
t.Errorf("`called` = false, want true")
}
// Use context that has been cancelled and expect a context error returned.
ctxCancelled, cancelled := context.WithCancel(context.Background())
cancelled()
// Verify context is cancelled by now.
<-ctxCancelled.Done()
called = false
err = m.Accept(ctxCancelled, nil)
if err != ctxCancelled.Err() {
t.Errorf("AcceptRateLimiter.Accept() = %v, want %v", err, ctxCancelled.Err())
}
if called {
t.Errorf("`called` = true, want false")
}
}

View File

@ -45,19 +45,19 @@ func (s *Service) wrapOperation(anyOp interface{}) (operation, error) {
if err != nil {
return nil, err
}
return &gaOperation{s, r.ProjectID, r.Key}, nil
return &gaOperation{s: s, projectID: r.ProjectID, key: r.Key}, nil
case *alpha.Operation:
r, err := ParseResourceURL(o.SelfLink)
if err != nil {
return nil, err
}
return &alphaOperation{s, r.ProjectID, r.Key}, nil
return &alphaOperation{s: s, projectID: r.ProjectID, key: r.Key}, nil
case *beta.Operation:
r, err := ParseResourceURL(o.SelfLink)
if err != nil {
return nil, err
}
return &betaOperation{s, r.ProjectID, r.Key}, nil
return &betaOperation{s: s, projectID: r.ProjectID, key: r.Key}, nil
default:
return nil, fmt.Errorf("invalid type %T", anyOp)
}
@ -72,14 +72,39 @@ func (s *Service) WaitForCompletion(ctx context.Context, genericOp interface{})
glog.Errorf("wrapOperation(%+v) error: %v", genericOp, err)
return err
}
for done, err := op.isDone(ctx); !done; done, err = op.isDone(ctx) {
if err != nil {
glog.V(4).Infof("op.isDone(%v) error; op = %v, err = %v", ctx, op, err)
return err
}
glog.V(5).Infof("op.isDone(%v) waiting; op = %v", ctx, op)
s.RateLimiter.Accept(ctx, op.rateLimitKey())
}
glog.V(5).Infof("op.isDone(%v) complete; op = %v", ctx, op)
return nil
return s.pollOperation(ctx, op)
}
// pollOperation calls operations.isDone until the function comes back true or context is Done.
// If an error occurs retrieving the operation, the loop will continue until the context is done.
// This is to prevent a transient error from bubbling up to controller-level logic.
func (s *Service) pollOperation(ctx context.Context, op operation) error {
var pollCount int
for {
// Check if context has been cancelled. Note that ctx.Done() must be checked before
// returning ctx.Err().
select {
case <-ctx.Done():
glog.V(5).Infof("op.pollOperation(%v, %v) not completed, poll count = %d, ctx.Err = %v", ctx, op, pollCount, ctx.Err())
return ctx.Err()
default:
// ctx is not canceled, continue immediately
}
pollCount++
glog.V(5).Infof("op.isDone(%v) waiting; op = %v, poll count = %d", ctx, op, pollCount)
s.RateLimiter.Accept(ctx, op.rateLimitKey())
done, err := op.isDone(ctx)
if err != nil {
glog.V(5).Infof("op.isDone(%v) error; op = %v, poll count = %d, err = %v, retrying", ctx, op, pollCount, err)
}
if done {
break
}
}
glog.V(5).Infof("op.isDone(%v) complete; op = %v, poll count = %d, op.err = %v", ctx, op, pollCount, op.error())
return op.error()
}

View File

@ -0,0 +1,84 @@
/*
Copyright 2018 The Kubernetes Authors.
Licensed under the Apache License, Version 2.0 (the "License");
you may not use this file except in compliance with the License.
You may obtain a copy of the License at
http://www.apache.org/licenses/LICENSE-2.0
Unless required by applicable law or agreed to in writing, software
distributed under the License is distributed on an "AS IS" BASIS,
WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
See the License for the specific language governing permissions and
limitations under the License.
*/
package cloud
import (
"context"
"fmt"
"testing"
)
func TestPollOperation(t *testing.T) {
const totalAttempts = 10
var attempts int
fo := &fakeOperation{isDoneFunc: func(ctx context.Context) (bool, error) {
attempts++
if attempts < totalAttempts {
return false, nil
}
return true, nil
}}
s := Service{RateLimiter: &NopRateLimiter{}}
// Check that pollOperation will retry the operation multiple times.
err := s.pollOperation(context.Background(), fo)
if err != nil {
t.Errorf("pollOperation() = %v, want nil", err)
}
if attempts != totalAttempts {
t.Errorf("`attempts` = %d, want %d", attempts, totalAttempts)
}
// Check that the operation's error is returned.
fo.err = fmt.Errorf("test operation failed")
err = s.pollOperation(context.Background(), fo)
if err != fo.err {
t.Errorf("pollOperation() = %v, want %v", err, fo.err)
}
fo.err = nil
fo.isDoneFunc = func(ctx context.Context) (bool, error) {
return false, nil
}
// Use context that has been cancelled and expect a context error returned.
ctxCancelled, cancelled := context.WithCancel(context.Background())
cancelled()
// Verify context is cancelled by now.
<-ctxCancelled.Done()
// Check that pollOperation returns because the context is cancelled.
err = s.pollOperation(ctxCancelled, fo)
if err == nil {
t.Errorf("pollOperation() = nil, want: %v", ctxCancelled.Err())
}
}
type fakeOperation struct {
isDoneFunc func(ctx context.Context) (bool, error)
err error
rateKey *RateLimitKey
}
func (f *fakeOperation) isDone(ctx context.Context) (bool, error) {
return f.isDoneFunc(ctx)
}
func (f *fakeOperation) error() error {
return f.err
}
func (f *fakeOperation) rateLimitKey() *RateLimitKey {
return f.rateKey
}

View File

@ -25,9 +25,9 @@ import (
)
const (
gaPrefix = "https://www.googleapis.com/compute/v1/"
alphaPrefix = "https://www.googleapis.com/compute/alpha/"
betaPrefix = "https://www.googleapis.com/compute/beta/"
gaPrefix = "https://www.googleapis.com/compute/v1"
alphaPrefix = "https://www.googleapis.com/compute/alpha"
betaPrefix = "https://www.googleapis.com/compute/beta"
)
// ResourceID identifies a GCE resource as parsed from compute resource URL.
@ -51,8 +51,27 @@ func (r *ResourceID) Equal(other *ResourceID) bool {
return false
}
// RelativeResourceName returns the relative resource name string
// representing this ResourceID.
func (r *ResourceID) RelativeResourceName() string {
return RelativeResourceName(r.ProjectID, r.Resource, r.Key)
}
// ResourcePath returns the resource path representing this ResourceID.
func (r *ResourceID) ResourcePath() string {
return ResourcePath(r.Resource, r.Key)
}
func (r *ResourceID) SelfLink(ver meta.Version) string {
return SelfLink(ver, r.ProjectID, r.Resource, r.Key)
}
// ParseResourceURL parses resource URLs of the following formats:
//
// global/<res>/<name>
// regions/<region>/<res>/<name>
// zones/<zone>/<res>/<name>
// projects/<proj>
// projects/<proj>/global/<res>/<name>
// projects/<proj>/regions/<region>/<res>/<name>
// projects/<proj>/zones/<zone>/<res>/<name>
@ -62,64 +81,63 @@ func (r *ResourceID) Equal(other *ResourceID) bool {
func ParseResourceURL(url string) (*ResourceID, error) {
errNotValid := fmt.Errorf("%q is not a valid resource URL", url)
// Remove the prefix up to ...projects/
// Trim prefix off URL leaving "projects/..."
projectsIndex := strings.Index(url, "/projects/")
if projectsIndex >= 0 {
url = url[projectsIndex+1:]
}
parts := strings.Split(url, "/")
if len(parts) < 2 || parts[0] != "projects" {
if len(parts) < 2 || len(parts) > 6 {
return nil, errNotValid
}
ret := &ResourceID{ProjectID: parts[1]}
if len(parts) == 2 {
ret := &ResourceID{}
scopedName := parts
if parts[0] == "projects" {
ret.Resource = "projects"
return ret, nil
}
ret.ProjectID = parts[1]
scopedName = parts[2:]
if len(parts) < 4 {
return nil, errNotValid
}
if len(parts) == 4 {
switch parts[2] {
case "regions":
ret.Resource = "regions"
ret.Key = meta.GlobalKey(parts[3])
if len(scopedName) == 0 {
return ret, nil
case "zones":
ret.Resource = "zones"
ret.Key = meta.GlobalKey(parts[3])
}
}
switch scopedName[0] {
case "global":
if len(scopedName) != 3 {
return nil, errNotValid
}
ret.Resource = scopedName[1]
ret.Key = meta.GlobalKey(scopedName[2])
return ret, nil
case "regions":
switch len(scopedName) {
case 2:
ret.Resource = "regions"
ret.Key = meta.GlobalKey(scopedName[1])
return ret, nil
case 4:
ret.Resource = scopedName[2]
ret.Key = meta.RegionalKey(scopedName[3], scopedName[1])
return ret, nil
default:
return nil, errNotValid
}
}
switch parts[2] {
case "global":
if len(parts) != 5 {
return nil, errNotValid
}
ret.Resource = parts[3]
ret.Key = meta.GlobalKey(parts[4])
return ret, nil
case "regions":
if len(parts) != 6 {
return nil, errNotValid
}
ret.Resource = parts[4]
ret.Key = meta.RegionalKey(parts[5], parts[3])
return ret, nil
case "zones":
if len(parts) != 6 {
switch len(scopedName) {
case 2:
ret.Resource = "zones"
ret.Key = meta.GlobalKey(scopedName[1])
return ret, nil
case 4:
ret.Resource = scopedName[2]
ret.Key = meta.ZonalKey(scopedName[3], scopedName[1])
return ret, nil
default:
return nil, errNotValid
}
ret.Resource = parts[4]
ret.Key = meta.ZonalKey(parts[5], parts[3])
return ret, nil
}
return nil, errNotValid
}
@ -132,6 +150,38 @@ func copyViaJSON(dest, src interface{}) error {
return json.Unmarshal(bytes, dest)
}
// ResourcePath returns the path starting from the location.
// Example: regions/us-central1/subnetworks/my-subnet
func ResourcePath(resource string, key *meta.Key) string {
switch resource {
case "zones", "regions":
return fmt.Sprintf("%s/%s", resource, key.Name)
case "projects":
return "invalid-resource"
}
switch key.Type() {
case meta.Zonal:
return fmt.Sprintf("zones/%s/%s/%s", key.Zone, resource, key.Name)
case meta.Regional:
return fmt.Sprintf("regions/%s/%s/%s", key.Region, resource, key.Name)
case meta.Global:
return fmt.Sprintf("global/%s/%s", resource, key.Name)
}
return "invalid-key-type"
}
// RelativeResourceName returns the path starting from project.
// Example: projects/my-project/regions/us-central1/subnetworks/my-subnet
func RelativeResourceName(project, resource string, key *meta.Key) string {
switch resource {
case "projects":
return fmt.Sprintf("projects/%s", project)
default:
return fmt.Sprintf("projects/%s/%s", project, ResourcePath(resource, key))
}
}
// SelfLink returns the self link URL for the given object.
func SelfLink(ver meta.Version, project, resource string, key *meta.Key) string {
var prefix string
@ -146,13 +196,6 @@ func SelfLink(ver meta.Version, project, resource string, key *meta.Key) string
prefix = "invalid-prefix"
}
switch key.Type() {
case meta.Zonal:
return fmt.Sprintf("%sprojects/%s/zones/%s/%s/%s", prefix, project, key.Zone, resource, key.Name)
case meta.Regional:
return fmt.Sprintf("%sprojects/%s/regions/%s/%s/%s", prefix, project, key.Region, resource, key.Name)
case meta.Global:
return fmt.Sprintf("%sprojects/%s/%s/%s", prefix, project, resource, key.Name)
}
return "invalid-self-link"
return fmt.Sprintf("%s/%s", prefix, RelativeResourceName(project, resource, key))
}

View File

@ -23,6 +23,54 @@ import (
"k8s.io/kubernetes/pkg/cloudprovider/providers/gce/cloud/meta"
)
func TestEqualResourceID(t *testing.T) {
t.Parallel()
for _, tc := range []struct {
a *ResourceID
b *ResourceID
}{
{
a: &ResourceID{"some-gce-project", "projects", nil},
b: &ResourceID{"some-gce-project", "projects", nil},
},
{
a: &ResourceID{"", "networks", meta.GlobalKey("my-net")},
b: &ResourceID{"", "networks", meta.GlobalKey("my-net")},
},
{
a: &ResourceID{"some-gce-project", "projects", meta.GlobalKey("us-central1")},
b: &ResourceID{"some-gce-project", "projects", meta.GlobalKey("us-central1")},
},
} {
if !tc.a.Equal(tc.b) {
t.Errorf("%v.Equal(%v) = false, want true", tc.a, tc.b)
}
}
for _, tc := range []struct {
a *ResourceID
b *ResourceID
}{
{
a: &ResourceID{"some-gce-project", "projects", nil},
b: &ResourceID{"some-other-project", "projects", nil},
},
{
a: &ResourceID{"some-gce-project", "projects", nil},
b: &ResourceID{"some-gce-project", "projects", meta.GlobalKey("us-central1")},
},
{
a: &ResourceID{"some-gce-project", "networks", meta.GlobalKey("us-central1")},
b: &ResourceID{"some-gce-project", "projects", meta.GlobalKey("us-central1")},
},
} {
if tc.a.Equal(tc.b) {
t.Errorf("%v.Equal(%v) = true, want false", tc.a, tc.b)
}
}
}
func TestParseResourceURL(t *testing.T) {
t.Parallel()
@ -90,6 +138,18 @@ func TestParseResourceURL(t *testing.T) {
"projects/some-gce-project/zones/us-central1-c/instances/instance-1",
&ResourceID{"some-gce-project", "instances", meta.ZonalKey("instance-1", "us-central1-c")},
},
{
"global/networks/my-network",
&ResourceID{"", "networks", meta.GlobalKey("my-network")},
},
{
"regions/us-central1/subnetworks/my-subnet",
&ResourceID{"", "subnetworks", meta.RegionalKey("my-subnet", "us-central1")},
},
{
"zones/us-central1-c/instances/instance-1",
&ResourceID{"", "instances", meta.ZonalKey("instance-1", "us-central1-c")},
},
} {
r, err := ParseResourceURL(tc.in)
if err != nil {
@ -112,7 +172,9 @@ func TestParseResourceURL(t *testing.T) {
"/a/b/c/d/e/f",
"https://www.googleapis.com/compute/v1/projects/some-gce-project/global",
"projects/some-gce-project/global",
"projects/some-gce-project/global/foo",
"projects/some-gce-project/global/foo/bar/baz",
"projects/some-gce-project/regions/us-central1/res",
"projects/some-gce-project/zones/us-central1-c/res",
"projects/some-gce-project/zones/us-central1-c/res/name/extra",
} {
@ -198,7 +260,28 @@ func TestSelfLink(t *testing.T) {
"proj4",
"urlMaps",
meta.GlobalKey("key3"),
"https://www.googleapis.com/compute/v1/projects/proj4/urlMaps/key3",
"https://www.googleapis.com/compute/v1/projects/proj4/global/urlMaps/key3",
},
{
meta.VersionGA,
"proj4",
"projects",
nil,
"https://www.googleapis.com/compute/v1/projects/proj4",
},
{
meta.VersionGA,
"proj4",
"regions",
meta.GlobalKey("us-central1"),
"https://www.googleapis.com/compute/v1/projects/proj4/regions/us-central1",
},
{
meta.VersionGA,
"proj4",
"zones",
meta.GlobalKey("us-central1-a"),
"https://www.googleapis.com/compute/v1/projects/proj4/zones/us-central1-a",
},
} {
if link := SelfLink(tc.ver, tc.project, tc.resource, tc.key); link != tc.want {

View File

@ -68,7 +68,7 @@ const (
// AffinityTypeClientIPProto - affinity based on Client IP and port.
gceAffinityTypeClientIPProto = "CLIENT_IP_PROTO"
operationPollInterval = 3 * time.Second
operationPollInterval = time.Second
// Creating Route in very large clusters, may take more than half an hour.
operationPollTimeoutDuration = time.Hour
@ -88,8 +88,8 @@ const (
// Defaults to 5 * 2 = 10 seconds before the LB will steer traffic away
gceHcUnhealthyThreshold = int64(5)
gceComputeAPIEndpoint = "https://www.googleapis.com/compute/v1/"
gceComputeAPIEndpointAlpha = "https://www.googleapis.com/compute/alpha/"
gceComputeAPIEndpoint = "https://www.googleapis.com/compute/v1/"
gceComputeAPIEndpointBeta = "https://www.googleapis.com/compute/beta/"
)
// gceObject is an abstraction of all GCE API object in go client
@ -152,6 +152,9 @@ type GCECloud struct {
// New code generated interface to the GCE compute library.
c cloud.Cloud
// Keep a reference of this around so we can inject a new cloud.RateLimiter implementation.
s *cloud.Service
}
// TODO: replace gcfg with json
@ -269,9 +272,7 @@ func generateCloudConfig(configFile *ConfigFile) (cloudConfig *CloudConfig, err
// By default, fetch token from GCE metadata server
cloudConfig.TokenSource = google.ComputeTokenSource("")
cloudConfig.UseMetadataServer = true
featureMap := make(map[string]bool)
cloudConfig.AlphaFeatureGate = &AlphaFeatureGate{featureMap}
cloudConfig.AlphaFeatureGate = NewAlphaFeatureGate([]string{})
if configFile != nil {
if configFile.Global.ApiEndpoint != "" {
cloudConfig.ApiEndpoint = configFile.Global.ApiEndpoint
@ -289,19 +290,7 @@ func generateCloudConfig(configFile *ConfigFile) (cloudConfig *CloudConfig, err
cloudConfig.NodeTags = configFile.Global.NodeTags
cloudConfig.NodeInstancePrefix = configFile.Global.NodeInstancePrefix
alphaFeatureGate, err := NewAlphaFeatureGate(configFile.Global.AlphaFeatures)
if err != nil {
glog.Errorf("Encountered error for creating alpha feature gate: %v", err)
}
cloudConfig.AlphaFeatureGate = alphaFeatureGate
} else {
// initialize AlphaFeatureGate when no AlphaFeatures are configured.
alphaFeatureGate, err := NewAlphaFeatureGate([]string{})
if err != nil {
glog.Errorf("Encountered error for initializing alpha feature gate: %v", err)
}
cloudConfig.AlphaFeatureGate = alphaFeatureGate
cloudConfig.AlphaFeatureGate = NewAlphaFeatureGate(configFile.Global.AlphaFeatures)
}
// retrieve projectID and zone
@ -495,7 +484,7 @@ func CreateGCECloud(config *CloudConfig) (*GCECloud, error) {
glog.Infof("managing multiple zones: %v", config.ManagedZones)
}
operationPollRateLimiter := flowcontrol.NewTokenBucketRateLimiter(10, 100) // 10 qps, 100 bucket size.
operationPollRateLimiter := flowcontrol.NewTokenBucketRateLimiter(5, 5) // 5 qps, 5 burst.
gce := &GCECloud{
service: service,
@ -522,17 +511,27 @@ func CreateGCECloud(config *CloudConfig) (*GCECloud, error) {
}
gce.manager = &gceServiceManager{gce}
gce.c = cloud.NewGCE(&cloud.Service{
gce.s = &cloud.Service{
GA: service,
Alpha: serviceAlpha,
Beta: serviceBeta,
ProjectRouter: &gceProjectRouter{gce},
RateLimiter: &gceRateLimiter{gce},
})
}
gce.c = cloud.NewGCE(gce.s)
return gce, nil
}
// SetRateLimiter adds a custom cloud.RateLimiter implementation.
// WARNING: Calling this could have unexpected behavior if you have in-flight
// requests. It is best to use this immediately after creating a GCECloud.
func (g *GCECloud) SetRateLimiter(rl cloud.RateLimiter) {
if rl != nil {
g.s.RateLimiter = rl
}
}
// determineSubnetURL queries for all subnetworks in a region for a given network and returns
// the URL of the subnetwork which exists in the auto-subnet range.
func determineSubnetURL(service *compute.Service, networkProjectID, networkName, region string) (string, error) {
@ -592,7 +591,7 @@ func (gce *GCECloud) Initialize(clientBuilder controller.ControllerClientBuilder
if gce.OnXPN() {
gce.eventBroadcaster = record.NewBroadcaster()
gce.eventBroadcaster.StartRecordingToSink(&v1core.EventSinkImpl{Interface: v1core.New(gce.client.CoreV1().RESTClient()).Events("")})
gce.eventBroadcaster.StartRecordingToSink(&v1core.EventSinkImpl{Interface: gce.client.CoreV1().Events("")})
gce.eventRecorder = gce.eventBroadcaster.NewRecorder(scheme.Scheme, v1.EventSource{Component: "gce-cloudprovider"})
}
@ -878,10 +877,10 @@ func (manager *gceServiceManager) getProjectsAPIEndpoint() string {
return projectsApiEndpoint
}
func (manager *gceServiceManager) getProjectsAPIEndpointAlpha() string {
projectsApiEndpoint := gceComputeAPIEndpointAlpha + "projects/"
func (manager *gceServiceManager) getProjectsAPIEndpointBeta() string {
projectsApiEndpoint := gceComputeAPIEndpointBeta + "projects/"
if manager.gce.service != nil {
projectsApiEndpoint = manager.gce.serviceAlpha.BasePath
projectsApiEndpoint = manager.gce.serviceBeta.BasePath
}
return projectsApiEndpoint

View File

@ -26,95 +26,104 @@ import (
)
const testSvcName = "my-service"
const testRegion = "us-central1"
const testSubnet = "/projects/x/testRegions/us-central1/testSubnetworks/customsub"
const testLBName = "a111111111111111"
var vals = DefaultTestClusterValues()
// TestAddressManagerNoRequestedIP tests the typical case of passing in no requested IP
func TestAddressManagerNoRequestedIP(t *testing.T) {
svc := NewFakeCloudAddressService()
svc, err := fakeGCECloud(vals)
require.NoError(t, err)
targetIP := ""
mgr := newAddressManager(svc, testSvcName, testRegion, testSubnet, testLBName, targetIP, cloud.SchemeInternal)
testHoldAddress(t, mgr, svc, testLBName, testRegion, targetIP, string(cloud.SchemeInternal))
testReleaseAddress(t, mgr, svc, testLBName, testRegion)
mgr := newAddressManager(svc, testSvcName, vals.Region, testSubnet, testLBName, targetIP, cloud.SchemeInternal)
testHoldAddress(t, mgr, svc, testLBName, vals.Region, targetIP, string(cloud.SchemeInternal))
testReleaseAddress(t, mgr, svc, testLBName, vals.Region)
}
// TestAddressManagerBasic tests the typical case of reserving and unreserving an address.
func TestAddressManagerBasic(t *testing.T) {
svc := NewFakeCloudAddressService()
svc, err := fakeGCECloud(vals)
require.NoError(t, err)
targetIP := "1.1.1.1"
mgr := newAddressManager(svc, testSvcName, testRegion, testSubnet, testLBName, targetIP, cloud.SchemeInternal)
testHoldAddress(t, mgr, svc, testLBName, testRegion, targetIP, string(cloud.SchemeInternal))
testReleaseAddress(t, mgr, svc, testLBName, testRegion)
mgr := newAddressManager(svc, testSvcName, vals.Region, testSubnet, testLBName, targetIP, cloud.SchemeInternal)
testHoldAddress(t, mgr, svc, testLBName, vals.Region, targetIP, string(cloud.SchemeInternal))
testReleaseAddress(t, mgr, svc, testLBName, vals.Region)
}
// TestAddressManagerOrphaned tests the case where the address exists with the IP being equal
// to the requested address (forwarding rule or loadbalancer IP).
func TestAddressManagerOrphaned(t *testing.T) {
svc := NewFakeCloudAddressService()
svc, err := fakeGCECloud(vals)
require.NoError(t, err)
targetIP := "1.1.1.1"
addr := &compute.Address{Name: testLBName, Address: targetIP, AddressType: string(cloud.SchemeInternal)}
err := svc.ReserveRegionAddress(addr, testRegion)
err = svc.ReserveRegionAddress(addr, vals.Region)
require.NoError(t, err)
mgr := newAddressManager(svc, testSvcName, testRegion, testSubnet, testLBName, targetIP, cloud.SchemeInternal)
testHoldAddress(t, mgr, svc, testLBName, testRegion, targetIP, string(cloud.SchemeInternal))
testReleaseAddress(t, mgr, svc, testLBName, testRegion)
mgr := newAddressManager(svc, testSvcName, vals.Region, testSubnet, testLBName, targetIP, cloud.SchemeInternal)
testHoldAddress(t, mgr, svc, testLBName, vals.Region, targetIP, string(cloud.SchemeInternal))
testReleaseAddress(t, mgr, svc, testLBName, vals.Region)
}
// TestAddressManagerOutdatedOrphan tests the case where an address exists but points to
// an IP other than the forwarding rule or loadbalancer IP.
func TestAddressManagerOutdatedOrphan(t *testing.T) {
svc := NewFakeCloudAddressService()
svc, err := fakeGCECloud(vals)
require.NoError(t, err)
previousAddress := "1.1.0.0"
targetIP := "1.1.1.1"
addr := &compute.Address{Name: testLBName, Address: previousAddress, AddressType: string(cloud.SchemeExternal)}
err := svc.ReserveRegionAddress(addr, testRegion)
err = svc.ReserveRegionAddress(addr, vals.Region)
require.NoError(t, err)
mgr := newAddressManager(svc, testSvcName, testRegion, testSubnet, testLBName, targetIP, cloud.SchemeInternal)
testHoldAddress(t, mgr, svc, testLBName, testRegion, targetIP, string(cloud.SchemeInternal))
testReleaseAddress(t, mgr, svc, testLBName, testRegion)
mgr := newAddressManager(svc, testSvcName, vals.Region, testSubnet, testLBName, targetIP, cloud.SchemeInternal)
testHoldAddress(t, mgr, svc, testLBName, vals.Region, targetIP, string(cloud.SchemeInternal))
testReleaseAddress(t, mgr, svc, testLBName, vals.Region)
}
// TestAddressManagerExternallyOwned tests the case where the address exists but isn't
// owned by the controller.
func TestAddressManagerExternallyOwned(t *testing.T) {
svc := NewFakeCloudAddressService()
svc, err := fakeGCECloud(vals)
require.NoError(t, err)
targetIP := "1.1.1.1"
addr := &compute.Address{Name: "my-important-address", Address: targetIP, AddressType: string(cloud.SchemeInternal)}
err := svc.ReserveRegionAddress(addr, testRegion)
err = svc.ReserveRegionAddress(addr, vals.Region)
require.NoError(t, err)
mgr := newAddressManager(svc, testSvcName, testRegion, testSubnet, testLBName, targetIP, cloud.SchemeInternal)
mgr := newAddressManager(svc, testSvcName, vals.Region, testSubnet, testLBName, targetIP, cloud.SchemeInternal)
ipToUse, err := mgr.HoldAddress()
require.NoError(t, err)
assert.NotEmpty(t, ipToUse)
_, err = svc.GetRegionAddress(testLBName, testRegion)
ad, err := svc.GetRegionAddress(testLBName, vals.Region)
assert.True(t, isNotFound(err))
require.Nil(t, ad)
testReleaseAddress(t, mgr, svc, testLBName, testRegion)
testReleaseAddress(t, mgr, svc, testLBName, vals.Region)
}
// TestAddressManagerExternallyOwned tests the case where the address exists but isn't
// owned by the controller. However, this address has the wrong type.
func TestAddressManagerBadExternallyOwned(t *testing.T) {
svc := NewFakeCloudAddressService()
svc, err := fakeGCECloud(vals)
require.NoError(t, err)
targetIP := "1.1.1.1"
addr := &compute.Address{Name: "my-important-address", Address: targetIP, AddressType: string(cloud.SchemeExternal)}
err := svc.ReserveRegionAddress(addr, testRegion)
err = svc.ReserveRegionAddress(addr, vals.Region)
require.NoError(t, err)
mgr := newAddressManager(svc, testSvcName, testRegion, testSubnet, testLBName, targetIP, cloud.SchemeInternal)
_, err = mgr.HoldAddress()
assert.NotNil(t, err)
mgr := newAddressManager(svc, testSvcName, vals.Region, testSubnet, testLBName, targetIP, cloud.SchemeInternal)
ad, err := mgr.HoldAddress()
assert.NotNil(t, err) // FIXME
require.Equal(t, ad, "")
}
func testHoldAddress(t *testing.T, mgr *addressManager, svc CloudAddressService, name, region, targetIP, scheme string) {

View File

@ -17,7 +17,6 @@ limitations under the License.
package gce
import (
"context"
"fmt"
"github.com/golang/glog"
@ -44,72 +43,105 @@ func newAddressMetricContextWithVersion(request, region, version string) *metric
// ipAddress is specified, it must belong to the current project, eg: an
// ephemeral IP associated with a global forwarding rule.
func (gce *GCECloud) ReserveGlobalAddress(addr *compute.Address) error {
ctx, cancel := cloud.ContextWithCallTimeout()
defer cancel()
mc := newAddressMetricContext("reserve", "")
return mc.Observe(gce.c.GlobalAddresses().Insert(context.Background(), meta.GlobalKey(addr.Name), addr))
return mc.Observe(gce.c.GlobalAddresses().Insert(ctx, meta.GlobalKey(addr.Name), addr))
}
// DeleteGlobalAddress deletes a global address by name.
func (gce *GCECloud) DeleteGlobalAddress(name string) error {
ctx, cancel := cloud.ContextWithCallTimeout()
defer cancel()
mc := newAddressMetricContext("delete", "")
return mc.Observe(gce.c.GlobalAddresses().Delete(context.Background(), meta.GlobalKey(name)))
return mc.Observe(gce.c.GlobalAddresses().Delete(ctx, meta.GlobalKey(name)))
}
// GetGlobalAddress returns the global address by name.
func (gce *GCECloud) GetGlobalAddress(name string) (*compute.Address, error) {
ctx, cancel := cloud.ContextWithCallTimeout()
defer cancel()
mc := newAddressMetricContext("get", "")
v, err := gce.c.GlobalAddresses().Get(context.Background(), meta.GlobalKey(name))
v, err := gce.c.GlobalAddresses().Get(ctx, meta.GlobalKey(name))
return v, mc.Observe(err)
}
// ReserveRegionAddress creates a region address
func (gce *GCECloud) ReserveRegionAddress(addr *compute.Address, region string) error {
ctx, cancel := cloud.ContextWithCallTimeout()
defer cancel()
mc := newAddressMetricContext("reserve", region)
return mc.Observe(gce.c.Addresses().Insert(context.Background(), meta.RegionalKey(addr.Name, region), addr))
return mc.Observe(gce.c.Addresses().Insert(ctx, meta.RegionalKey(addr.Name, region), addr))
}
// ReserveAlphaRegionAddress creates an Alpha, regional address.
func (gce *GCECloud) ReserveAlphaRegionAddress(addr *computealpha.Address, region string) error {
ctx, cancel := cloud.ContextWithCallTimeout()
defer cancel()
mc := newAddressMetricContext("reserve", region)
return mc.Observe(gce.c.AlphaAddresses().Insert(context.Background(), meta.RegionalKey(addr.Name, region), addr))
return mc.Observe(gce.c.AlphaAddresses().Insert(ctx, meta.RegionalKey(addr.Name, region), addr))
}
// ReserveBetaRegionAddress creates a beta region address
func (gce *GCECloud) ReserveBetaRegionAddress(addr *computebeta.Address, region string) error {
ctx, cancel := cloud.ContextWithCallTimeout()
defer cancel()
mc := newAddressMetricContext("reserve", region)
return mc.Observe(gce.c.BetaAddresses().Insert(context.Background(), meta.RegionalKey(addr.Name, region), addr))
return mc.Observe(gce.c.BetaAddresses().Insert(ctx, meta.RegionalKey(addr.Name, region), addr))
}
// DeleteRegionAddress deletes a region address by name.
func (gce *GCECloud) DeleteRegionAddress(name, region string) error {
ctx, cancel := cloud.ContextWithCallTimeout()
defer cancel()
mc := newAddressMetricContext("delete", region)
return mc.Observe(gce.c.Addresses().Delete(context.Background(), meta.RegionalKey(name, region)))
return mc.Observe(gce.c.Addresses().Delete(ctx, meta.RegionalKey(name, region)))
}
// GetRegionAddress returns the region address by name
func (gce *GCECloud) GetRegionAddress(name, region string) (*compute.Address, error) {
ctx, cancel := cloud.ContextWithCallTimeout()
defer cancel()
mc := newAddressMetricContext("get", region)
v, err := gce.c.Addresses().Get(context.Background(), meta.RegionalKey(name, region))
v, err := gce.c.Addresses().Get(ctx, meta.RegionalKey(name, region))
return v, mc.Observe(err)
}
// GetAlphaRegionAddress returns the Alpha, regional address by name.
func (gce *GCECloud) GetAlphaRegionAddress(name, region string) (*computealpha.Address, error) {
ctx, cancel := cloud.ContextWithCallTimeout()
defer cancel()
mc := newAddressMetricContext("get", region)
v, err := gce.c.AlphaAddresses().Get(context.Background(), meta.RegionalKey(name, region))
v, err := gce.c.AlphaAddresses().Get(ctx, meta.RegionalKey(name, region))
return v, mc.Observe(err)
}
// GetBetaRegionAddress returns the beta region address by name
func (gce *GCECloud) GetBetaRegionAddress(name, region string) (*computebeta.Address, error) {
ctx, cancel := cloud.ContextWithCallTimeout()
defer cancel()
mc := newAddressMetricContext("get", region)
v, err := gce.c.BetaAddresses().Get(context.Background(), meta.RegionalKey(name, region))
v, err := gce.c.BetaAddresses().Get(ctx, meta.RegionalKey(name, region))
return v, mc.Observe(err)
}
// GetRegionAddressByIP returns the regional address matching the given IP address.
func (gce *GCECloud) GetRegionAddressByIP(region, ipAddress string) (*compute.Address, error) {
ctx, cancel := cloud.ContextWithCallTimeout()
defer cancel()
mc := newAddressMetricContext("list", region)
addrs, err := gce.c.Addresses().List(context.Background(), region, filter.Regexp("address", ipAddress))
addrs, err := gce.c.Addresses().List(ctx, region, filter.Regexp("address", ipAddress))
mc.Observe(err)
if err != nil {
@ -129,8 +161,11 @@ func (gce *GCECloud) GetRegionAddressByIP(region, ipAddress string) (*compute.Ad
// GetBetaRegionAddressByIP returns the beta regional address matching the given IP address.
func (gce *GCECloud) GetBetaRegionAddressByIP(region, ipAddress string) (*computebeta.Address, error) {
ctx, cancel := cloud.ContextWithCallTimeout()
defer cancel()
mc := newAddressMetricContext("list", region)
addrs, err := gce.c.BetaAddresses().List(context.Background(), region, filter.Regexp("address", ipAddress))
addrs, err := gce.c.BetaAddresses().List(ctx, region, filter.Regexp("address", ipAddress))
mc.Observe(err)
if err != nil {

View File

@ -1,239 +0,0 @@
/*
Copyright 2017 The Kubernetes Authors.
Licensed under the Apache License, Version 2.0 (the "License");
you may not use this file except in compliance with the License.
You may obtain a copy of the License at
http://www.apache.org/licenses/LICENSE-2.0
Unless required by applicable law or agreed to in writing, software
distributed under the License is distributed on an "AS IS" BASIS,
WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
See the License for the specific language governing permissions and
limitations under the License.
*/
package gce
import (
"bytes"
"encoding/json"
"fmt"
"net/http"
computealpha "google.golang.org/api/compute/v0.alpha"
computebeta "google.golang.org/api/compute/v0.beta"
compute "google.golang.org/api/compute/v1"
"k8s.io/kubernetes/pkg/cloudprovider/providers/gce/cloud"
)
// test
type FakeCloudAddressService struct {
count int
// reservedAddrs tracks usage of IP addresses
// Key is the IP address as a string
reservedAddrs map[string]bool
// addrsByRegionAndName
// Outer key is for region string; inner key is for address name.
addrsByRegionAndName map[string]map[string]*computealpha.Address
}
// FakeCloudAddressService Implements CloudAddressService
var _ CloudAddressService = &FakeCloudAddressService{}
func NewFakeCloudAddressService() *FakeCloudAddressService {
return &FakeCloudAddressService{
reservedAddrs: make(map[string]bool),
addrsByRegionAndName: make(map[string]map[string]*computealpha.Address),
}
}
// SetRegionalAddresses sets the addresses of there region. This is used for
// setting the test environment.
func (cas *FakeCloudAddressService) SetRegionalAddresses(region string, addrs []*computealpha.Address) {
// Reset addresses in the region.
cas.addrsByRegionAndName[region] = make(map[string]*computealpha.Address)
for _, addr := range addrs {
cas.reservedAddrs[addr.Address] = true
cas.addrsByRegionAndName[region][addr.Name] = addr
}
}
func (cas *FakeCloudAddressService) ReserveAlphaRegionAddress(addr *computealpha.Address, region string) error {
if addr.Address == "" {
addr.Address = fmt.Sprintf("1.2.3.%d", cas.count)
cas.count++
}
if addr.AddressType == "" {
addr.AddressType = string(cloud.SchemeExternal)
}
if cas.reservedAddrs[addr.Address] {
msg := "IP in use"
// When the IP is already in use, this call returns an error code based
// on the type (internal vs external) of the address. This is to be
// consistent with actual GCE API.
switch cloud.LbScheme(addr.AddressType) {
case cloud.SchemeExternal:
return makeGoogleAPIError(http.StatusBadRequest, msg)
default:
return makeGoogleAPIError(http.StatusConflict, msg)
}
}
if _, exists := cas.addrsByRegionAndName[region]; !exists {
cas.addrsByRegionAndName[region] = make(map[string]*computealpha.Address)
}
if _, exists := cas.addrsByRegionAndName[region][addr.Name]; exists {
return makeGoogleAPIError(http.StatusConflict, "name in use")
}
cas.addrsByRegionAndName[region][addr.Name] = addr
cas.reservedAddrs[addr.Address] = true
return nil
}
func (cas *FakeCloudAddressService) ReserveBetaRegionAddress(addr *computebeta.Address, region string) error {
alphaAddr := convertToAlphaAddress(addr)
return cas.ReserveAlphaRegionAddress(alphaAddr, region)
}
func (cas *FakeCloudAddressService) ReserveRegionAddress(addr *compute.Address, region string) error {
alphaAddr := convertToAlphaAddress(addr)
return cas.ReserveAlphaRegionAddress(alphaAddr, region)
}
func (cas *FakeCloudAddressService) GetAlphaRegionAddress(name, region string) (*computealpha.Address, error) {
if _, exists := cas.addrsByRegionAndName[region]; !exists {
return nil, makeGoogleAPINotFoundError("")
}
if addr, exists := cas.addrsByRegionAndName[region][name]; !exists {
return nil, makeGoogleAPINotFoundError("")
} else {
return addr, nil
}
}
func (cas *FakeCloudAddressService) GetBetaRegionAddress(name, region string) (*computebeta.Address, error) {
addr, err := cas.GetAlphaRegionAddress(name, region)
if addr != nil {
return convertToBetaAddress(addr), err
}
return nil, err
}
func (cas *FakeCloudAddressService) GetRegionAddress(name, region string) (*compute.Address, error) {
addr, err := cas.GetAlphaRegionAddress(name, region)
if addr != nil {
return convertToV1Address(addr), err
}
return nil, err
}
func (cas *FakeCloudAddressService) DeleteRegionAddress(name, region string) error {
if _, exists := cas.addrsByRegionAndName[region]; !exists {
return makeGoogleAPINotFoundError("")
}
addr, exists := cas.addrsByRegionAndName[region][name]
if !exists {
return makeGoogleAPINotFoundError("")
}
delete(cas.reservedAddrs, addr.Address)
delete(cas.addrsByRegionAndName[region], name)
return nil
}
func (cas *FakeCloudAddressService) GetAlphaRegionAddressByIP(region, ipAddress string) (*computealpha.Address, error) {
if _, exists := cas.addrsByRegionAndName[region]; !exists {
return nil, makeGoogleAPINotFoundError("")
}
for _, addr := range cas.addrsByRegionAndName[region] {
if addr.Address == ipAddress {
return addr, nil
}
}
return nil, makeGoogleAPINotFoundError("")
}
func (cas *FakeCloudAddressService) GetBetaRegionAddressByIP(name, region string) (*computebeta.Address, error) {
addr, err := cas.GetAlphaRegionAddressByIP(name, region)
if addr != nil {
return convertToBetaAddress(addr), nil
}
return nil, err
}
func (cas *FakeCloudAddressService) GetRegionAddressByIP(name, region string) (*compute.Address, error) {
addr, err := cas.GetAlphaRegionAddressByIP(name, region)
if addr != nil {
return convertToV1Address(addr), nil
}
return nil, err
}
func (cas *FakeCloudAddressService) getNetworkTierFromAddress(name, region string) (string, error) {
addr, err := cas.GetAlphaRegionAddress(name, region)
if err != nil {
return "", err
}
return addr.NetworkTier, nil
}
func convertToV1Address(object gceObject) *compute.Address {
enc, err := object.MarshalJSON()
if err != nil {
panic(fmt.Sprintf("Failed to encode to json: %v", err))
}
var addr compute.Address
if err := json.Unmarshal(enc, &addr); err != nil {
panic(fmt.Sprintf("Failed to convert GCE apiObject %v to v1 address: %v", object, err))
}
return &addr
}
func convertToAlphaAddress(object gceObject) *computealpha.Address {
enc, err := object.MarshalJSON()
if err != nil {
panic(fmt.Sprintf("Failed to encode to json: %v", err))
}
var addr computealpha.Address
if err := json.Unmarshal(enc, &addr); err != nil {
panic(fmt.Sprintf("Failed to convert GCE apiObject %v to alpha address: %v", object, err))
}
// Set the default values for the Alpha fields.
addr.NetworkTier = cloud.NetworkTierDefault.ToGCEValue()
return &addr
}
func convertToBetaAddress(object gceObject) *computebeta.Address {
enc, err := object.MarshalJSON()
if err != nil {
panic(fmt.Sprintf("Failed to encode to json: %v", err))
}
var addr computebeta.Address
if err := json.Unmarshal(enc, &addr); err != nil {
panic(fmt.Sprintf("Failed to convert GCE apiObject %v to beta address: %v", object, err))
}
return &addr
}
func (cas *FakeCloudAddressService) String() string {
var b bytes.Buffer
for region, regAddresses := range cas.addrsByRegionAndName {
b.WriteString(fmt.Sprintf("%v:\n", region))
for name, addr := range regAddresses {
b.WriteString(fmt.Sprintf(" %v: %v\n", name, addr.Address))
}
}
return b.String()
}

View File

@ -18,8 +18,6 @@ package gce
import (
"fmt"
utilerrors "k8s.io/apimachinery/pkg/util/errors"
)
const (
@ -32,12 +30,6 @@ const (
AlphaFeatureNetworkEndpointGroup = "NetworkEndpointGroup"
)
// All known alpha features
var knownAlphaFeatures = map[string]bool{
AlphaFeatureNetworkTiers: true,
AlphaFeatureNetworkEndpointGroup: true,
}
type AlphaFeatureGate struct {
features map[string]bool
}
@ -46,17 +38,12 @@ func (af *AlphaFeatureGate) Enabled(key string) bool {
return af.features[key]
}
func NewAlphaFeatureGate(features []string) (*AlphaFeatureGate, error) {
errList := []error{}
func NewAlphaFeatureGate(features []string) *AlphaFeatureGate {
featureMap := make(map[string]bool)
for _, name := range features {
if _, ok := knownAlphaFeatures[name]; !ok {
errList = append(errList, fmt.Errorf("alpha feature %q is not supported.", name))
} else {
featureMap[name] = true
}
featureMap[name] = true
}
return &AlphaFeatureGate{featureMap}, utilerrors.NewAggregate(errList)
return &AlphaFeatureGate{featureMap}
}
func (gce *GCECloud) alphaFeatureEnabled(feature string) error {

View File

@ -17,11 +17,11 @@ limitations under the License.
package gce
import (
"context"
computealpha "google.golang.org/api/compute/v0.alpha"
computebeta "google.golang.org/api/compute/v0.beta"
compute "google.golang.org/api/compute/v1"
"k8s.io/kubernetes/pkg/cloudprovider/providers/gce/cloud"
"k8s.io/kubernetes/pkg/cloudprovider/providers/gce/cloud/filter"
"k8s.io/kubernetes/pkg/cloudprovider/providers/gce/cloud/meta"
)
@ -36,54 +36,88 @@ func newBackendServiceMetricContextWithVersion(request, region, version string)
// GetGlobalBackendService retrieves a backend by name.
func (gce *GCECloud) GetGlobalBackendService(name string) (*compute.BackendService, error) {
ctx, cancel := cloud.ContextWithCallTimeout()
defer cancel()
mc := newBackendServiceMetricContext("get", "")
v, err := gce.c.BackendServices().Get(context.Background(), meta.GlobalKey(name))
v, err := gce.c.BackendServices().Get(ctx, meta.GlobalKey(name))
return v, mc.Observe(err)
}
// GetBetaGlobalBackendService retrieves beta backend by name.
func (gce *GCECloud) GetBetaGlobalBackendService(name string) (*computebeta.BackendService, error) {
ctx, cancel := cloud.ContextWithCallTimeout()
defer cancel()
mc := newBackendServiceMetricContextWithVersion("get", "", computeBetaVersion)
v, err := gce.c.BetaBackendServices().Get(ctx, meta.GlobalKey(name))
return v, mc.Observe(err)
}
// GetAlphaGlobalBackendService retrieves alpha backend by name.
func (gce *GCECloud) GetAlphaGlobalBackendService(name string) (*computealpha.BackendService, error) {
ctx, cancel := cloud.ContextWithCallTimeout()
defer cancel()
mc := newBackendServiceMetricContextWithVersion("get", "", computeAlphaVersion)
v, err := gce.c.AlphaBackendServices().Get(context.Background(), meta.GlobalKey(name))
v, err := gce.c.AlphaBackendServices().Get(ctx, meta.GlobalKey(name))
return v, mc.Observe(err)
}
// UpdateGlobalBackendService applies the given BackendService as an update to
// an existing service.
func (gce *GCECloud) UpdateGlobalBackendService(bg *compute.BackendService) error {
ctx, cancel := cloud.ContextWithCallTimeout()
defer cancel()
mc := newBackendServiceMetricContext("update", "")
return mc.Observe(gce.c.BackendServices().Update(context.Background(), meta.GlobalKey(bg.Name), bg))
return mc.Observe(gce.c.BackendServices().Update(ctx, meta.GlobalKey(bg.Name), bg))
}
// UpdateAlphaGlobalBackendService applies the given alpha BackendService as an
// update to an existing service.
func (gce *GCECloud) UpdateAlphaGlobalBackendService(bg *computealpha.BackendService) error {
ctx, cancel := cloud.ContextWithCallTimeout()
defer cancel()
mc := newBackendServiceMetricContext("update", "")
return mc.Observe(gce.c.AlphaBackendServices().Update(context.Background(), meta.GlobalKey(bg.Name), bg))
return mc.Observe(gce.c.AlphaBackendServices().Update(ctx, meta.GlobalKey(bg.Name), bg))
}
// DeleteGlobalBackendService deletes the given BackendService by name.
func (gce *GCECloud) DeleteGlobalBackendService(name string) error {
ctx, cancel := cloud.ContextWithCallTimeout()
defer cancel()
mc := newBackendServiceMetricContext("delete", "")
return mc.Observe(gce.c.BackendServices().Delete(context.Background(), meta.GlobalKey(name)))
return mc.Observe(gce.c.BackendServices().Delete(ctx, meta.GlobalKey(name)))
}
// CreateGlobalBackendService creates the given BackendService.
func (gce *GCECloud) CreateGlobalBackendService(bg *compute.BackendService) error {
ctx, cancel := cloud.ContextWithCallTimeout()
defer cancel()
mc := newBackendServiceMetricContext("create", "")
return mc.Observe(gce.c.BackendServices().Insert(context.Background(), meta.GlobalKey(bg.Name), bg))
return mc.Observe(gce.c.BackendServices().Insert(ctx, meta.GlobalKey(bg.Name), bg))
}
// CreateAlphaGlobalBackendService creates the given alpha BackendService.
func (gce *GCECloud) CreateAlphaGlobalBackendService(bg *computealpha.BackendService) error {
ctx, cancel := cloud.ContextWithCallTimeout()
defer cancel()
mc := newBackendServiceMetricContext("create", "")
return mc.Observe(gce.c.AlphaBackendServices().Insert(context.Background(), meta.GlobalKey(bg.Name), bg))
return mc.Observe(gce.c.AlphaBackendServices().Insert(ctx, meta.GlobalKey(bg.Name), bg))
}
// ListGlobalBackendServices lists all backend services in the project.
func (gce *GCECloud) ListGlobalBackendServices() ([]*compute.BackendService, error) {
ctx, cancel := cloud.ContextWithCallTimeout()
defer cancel()
mc := newBackendServiceMetricContext("list", "")
v, err := gce.c.BackendServices().List(context.Background(), filter.None)
v, err := gce.c.BackendServices().List(ctx, filter.None)
return v, mc.Observe(err)
}
@ -91,42 +125,60 @@ func (gce *GCECloud) ListGlobalBackendServices() ([]*compute.BackendService, err
// identified by the given name, in the given instanceGroup. The
// instanceGroupLink is the fully qualified self link of an instance group.
func (gce *GCECloud) GetGlobalBackendServiceHealth(name string, instanceGroupLink string) (*compute.BackendServiceGroupHealth, error) {
ctx, cancel := cloud.ContextWithCallTimeout()
defer cancel()
mc := newBackendServiceMetricContext("get_health", "")
groupRef := &compute.ResourceGroupReference{Group: instanceGroupLink}
v, err := gce.c.BackendServices().GetHealth(context.Background(), meta.GlobalKey(name), groupRef)
v, err := gce.c.BackendServices().GetHealth(ctx, meta.GlobalKey(name), groupRef)
return v, mc.Observe(err)
}
// GetRegionBackendService retrieves a backend by name.
func (gce *GCECloud) GetRegionBackendService(name, region string) (*compute.BackendService, error) {
ctx, cancel := cloud.ContextWithCallTimeout()
defer cancel()
mc := newBackendServiceMetricContext("get", region)
v, err := gce.c.RegionBackendServices().Get(context.Background(), meta.RegionalKey(name, region))
v, err := gce.c.RegionBackendServices().Get(ctx, meta.RegionalKey(name, region))
return v, mc.Observe(err)
}
// UpdateRegionBackendService applies the given BackendService as an update to
// an existing service.
func (gce *GCECloud) UpdateRegionBackendService(bg *compute.BackendService, region string) error {
ctx, cancel := cloud.ContextWithCallTimeout()
defer cancel()
mc := newBackendServiceMetricContext("update", region)
return mc.Observe(gce.c.RegionBackendServices().Update(context.Background(), meta.RegionalKey(bg.Name, region), bg))
return mc.Observe(gce.c.RegionBackendServices().Update(ctx, meta.RegionalKey(bg.Name, region), bg))
}
// DeleteRegionBackendService deletes the given BackendService by name.
func (gce *GCECloud) DeleteRegionBackendService(name, region string) error {
ctx, cancel := cloud.ContextWithCallTimeout()
defer cancel()
mc := newBackendServiceMetricContext("delete", region)
return mc.Observe(gce.c.RegionBackendServices().Delete(context.Background(), meta.RegionalKey(name, region)))
return mc.Observe(gce.c.RegionBackendServices().Delete(ctx, meta.RegionalKey(name, region)))
}
// CreateRegionBackendService creates the given BackendService.
func (gce *GCECloud) CreateRegionBackendService(bg *compute.BackendService, region string) error {
ctx, cancel := cloud.ContextWithCallTimeout()
defer cancel()
mc := newBackendServiceMetricContext("create", region)
return mc.Observe(gce.c.RegionBackendServices().Insert(context.Background(), meta.RegionalKey(bg.Name, region), bg))
return mc.Observe(gce.c.RegionBackendServices().Insert(ctx, meta.RegionalKey(bg.Name, region), bg))
}
// ListRegionBackendServices lists all backend services in the project.
func (gce *GCECloud) ListRegionBackendServices(region string) ([]*compute.BackendService, error) {
ctx, cancel := cloud.ContextWithCallTimeout()
defer cancel()
mc := newBackendServiceMetricContext("list", region)
v, err := gce.c.RegionBackendServices().List(context.Background(), region, filter.None)
v, err := gce.c.RegionBackendServices().List(ctx, region, filter.None)
return v, mc.Observe(err)
}
@ -134,8 +186,31 @@ func (gce *GCECloud) ListRegionBackendServices(region string) ([]*compute.Backen
// identified by the given name, in the given instanceGroup. The
// instanceGroupLink is the fully qualified self link of an instance group.
func (gce *GCECloud) GetRegionalBackendServiceHealth(name, region string, instanceGroupLink string) (*compute.BackendServiceGroupHealth, error) {
ctx, cancel := cloud.ContextWithCallTimeout()
defer cancel()
mc := newBackendServiceMetricContext("get_health", region)
ref := &compute.ResourceGroupReference{Group: instanceGroupLink}
v, err := gce.c.RegionBackendServices().GetHealth(context.Background(), meta.RegionalKey(name, region), ref)
v, err := gce.c.RegionBackendServices().GetHealth(ctx, meta.RegionalKey(name, region), ref)
return v, mc.Observe(err)
}
// SetSecurityPolicyForBetaGlobalBackendService sets the given
// SecurityPolicyReference for the BackendService identified by the given name.
func (gce *GCECloud) SetSecurityPolicyForBetaGlobalBackendService(backendServiceName string, securityPolicyReference *computebeta.SecurityPolicyReference) error {
ctx, cancel := cloud.ContextWithCallTimeout()
defer cancel()
mc := newBackendServiceMetricContextWithVersion("set_security_policy", "", computeBetaVersion)
return mc.Observe(gce.c.BetaBackendServices().SetSecurityPolicy(ctx, meta.GlobalKey(backendServiceName), securityPolicyReference))
}
// SetSecurityPolicyForAlphaGlobalBackendService sets the given
// SecurityPolicyReference for the BackendService identified by the given name.
func (gce *GCECloud) SetSecurityPolicyForAlphaGlobalBackendService(backendServiceName string, securityPolicyReference *computealpha.SecurityPolicyReference) error {
ctx, cancel := cloud.ContextWithCallTimeout()
defer cancel()
mc := newBackendServiceMetricContextWithVersion("set_security_policy", "", computeAlphaVersion)
return mc.Observe(gce.c.AlphaBackendServices().SetSecurityPolicy(ctx, meta.GlobalKey(backendServiceName), securityPolicyReference))
}

View File

@ -17,10 +17,9 @@ limitations under the License.
package gce
import (
"context"
compute "google.golang.org/api/compute/v1"
"k8s.io/kubernetes/pkg/cloudprovider/providers/gce/cloud"
"k8s.io/kubernetes/pkg/cloudprovider/providers/gce/cloud/filter"
"k8s.io/kubernetes/pkg/cloudprovider/providers/gce/cloud/meta"
)
@ -31,15 +30,21 @@ func newCertMetricContext(request string) *metricContext {
// GetSslCertificate returns the SslCertificate by name.
func (gce *GCECloud) GetSslCertificate(name string) (*compute.SslCertificate, error) {
ctx, cancel := cloud.ContextWithCallTimeout()
defer cancel()
mc := newCertMetricContext("get")
v, err := gce.c.SslCertificates().Get(context.Background(), meta.GlobalKey(name))
v, err := gce.c.SslCertificates().Get(ctx, meta.GlobalKey(name))
return v, mc.Observe(err)
}
// CreateSslCertificate creates and returns a SslCertificate.
func (gce *GCECloud) CreateSslCertificate(sslCerts *compute.SslCertificate) (*compute.SslCertificate, error) {
ctx, cancel := cloud.ContextWithCallTimeout()
defer cancel()
mc := newCertMetricContext("create")
err := gce.c.SslCertificates().Insert(context.Background(), meta.GlobalKey(sslCerts.Name), sslCerts)
err := gce.c.SslCertificates().Insert(ctx, meta.GlobalKey(sslCerts.Name), sslCerts)
if err != nil {
return nil, mc.Observe(err)
}
@ -48,13 +53,19 @@ func (gce *GCECloud) CreateSslCertificate(sslCerts *compute.SslCertificate) (*co
// DeleteSslCertificate deletes the SslCertificate by name.
func (gce *GCECloud) DeleteSslCertificate(name string) error {
ctx, cancel := cloud.ContextWithCallTimeout()
defer cancel()
mc := newCertMetricContext("delete")
return mc.Observe(gce.c.SslCertificates().Delete(context.Background(), meta.GlobalKey(name)))
return mc.Observe(gce.c.SslCertificates().Delete(ctx, meta.GlobalKey(name)))
}
// ListSslCertificates lists all SslCertificates in the project.
func (gce *GCECloud) ListSslCertificates() ([]*compute.SslCertificate, error) {
ctx, cancel := cloud.ContextWithCallTimeout()
defer cancel()
mc := newCertMetricContext("list")
v, err := gce.c.SslCertificates().List(context.Background(), filter.None)
v, err := gce.c.SslCertificates().List(ctx, filter.None)
return v, mc.Observe(err)
}

View File

@ -34,10 +34,12 @@ import (
volumeutil "k8s.io/kubernetes/pkg/volume/util"
"github.com/golang/glog"
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/googleapi"
utilfeature "k8s.io/apiserver/pkg/util/feature"
"k8s.io/kubernetes/pkg/cloudprovider/providers/gce/cloud"
"k8s.io/kubernetes/pkg/cloudprovider/providers/gce/cloud/meta"
"k8s.io/kubernetes/pkg/features"
)
@ -65,7 +67,7 @@ type diskServiceManager interface {
sizeGb int64,
tagsStr string,
diskType string,
zone string) (gceObject, error)
zone string) error
// Creates a new regional persistent disk on GCE with the given disk spec.
CreateRegionalDiskOnCloudProvider(
@ -73,41 +75,35 @@ type diskServiceManager interface {
sizeGb int64,
tagsStr string,
diskType string,
zones sets.String) (gceObject, error)
zones sets.String) error
// Deletes the persistent disk from GCE with the given diskName.
DeleteDiskOnCloudProvider(zone string, disk string) (gceObject, error)
DeleteDiskOnCloudProvider(zone string, disk string) error
// Deletes the regional persistent disk from GCE with the given diskName.
DeleteRegionalDiskOnCloudProvider(diskName string) (gceObject, error)
DeleteRegionalDiskOnCloudProvider(diskName string) error
// Attach a persistent disk on GCE with the given disk spec to the specified instance.
AttachDiskOnCloudProvider(
disk *GCEDisk,
readWrite string,
instanceZone string,
instanceName string) (gceObject, error)
instanceName string) error
// Detach a persistent disk on GCE with the given disk spec from the specified instance.
DetachDiskOnCloudProvider(
instanceZone string,
instanceName string,
devicePath string) (gceObject, error)
devicePath string) error
ResizeDiskOnCloudProvider(disk *GCEDisk, sizeGb int64, zone string) (gceObject, error)
RegionalResizeDiskOnCloudProvider(disk *GCEDisk, sizeGb int64) (gceObject, error)
ResizeDiskOnCloudProvider(disk *GCEDisk, sizeGb int64, zone string) error
RegionalResizeDiskOnCloudProvider(disk *GCEDisk, sizeGb int64) error
// Gets the persistent disk from GCE with the given diskName.
GetDiskFromCloudProvider(zone string, diskName string) (*GCEDisk, error)
// Gets the regional persistent disk from GCE with the given diskName.
GetRegionalDiskFromCloudProvider(diskName string) (*GCEDisk, error)
// Waits until GCE reports the given operation in the given zone as done.
WaitForZoneOp(op gceObject, zone string, mc *metricContext) error
// Waits until GCE reports the given operation in the given region is done.
WaitForRegionalOp(op gceObject, mc *metricContext) error
}
type gceServiceManager struct {
@ -121,11 +117,11 @@ func (manager *gceServiceManager) CreateDiskOnCloudProvider(
sizeGb int64,
tagsStr string,
diskType string,
zone string) (gceObject, error) {
zone string) error {
diskTypeURI, err := manager.getDiskTypeURI(
manager.gce.region /* diskRegion */, singleZone{zone}, diskType, false /* useAlphaAPI */)
manager.gce.region /* diskRegion */, singleZone{zone}, diskType, false /* useBetaAPI */)
if err != nil {
return nil, err
return err
}
diskToCreateV1 := &compute.Disk{
@ -134,8 +130,10 @@ func (manager *gceServiceManager) CreateDiskOnCloudProvider(
Description: tagsStr,
Type: diskTypeURI,
}
return manager.gce.service.Disks.Insert(
manager.gce.projectID, zone, diskToCreateV1).Do()
ctx, cancel := cloud.ContextWithCallTimeout()
defer cancel()
return manager.gce.c.Disks().Insert(ctx, meta.ZonalKey(name, zone), diskToCreateV1)
}
func (manager *gceServiceManager) CreateRegionalDiskOnCloudProvider(
@ -143,42 +141,44 @@ func (manager *gceServiceManager) CreateRegionalDiskOnCloudProvider(
sizeGb int64,
tagsStr string,
diskType string,
replicaZones sets.String) (gceObject, error) {
replicaZones sets.String) error {
if utilfeature.DefaultFeatureGate.Enabled(features.GCERegionalPersistentDisk) {
diskTypeURI, err := manager.getDiskTypeURI(
manager.gce.region /* diskRegion */, multiZone{replicaZones}, diskType, true /* useAlphaAPI */)
if err != nil {
return nil, err
}
fullyQualifiedReplicaZones := []string{}
for _, replicaZone := range replicaZones.UnsortedList() {
fullyQualifiedReplicaZones = append(
fullyQualifiedReplicaZones, manager.getReplicaZoneURI(replicaZone, true))
}
diskToCreateAlpha := &computealpha.Disk{
Name: name,
SizeGb: sizeGb,
Description: tagsStr,
Type: diskTypeURI,
ReplicaZones: fullyQualifiedReplicaZones,
}
return manager.gce.serviceAlpha.RegionDisks.Insert(
manager.gce.projectID, manager.gce.region, diskToCreateAlpha).Do()
if !utilfeature.DefaultFeatureGate.Enabled(features.GCERegionalPersistentDisk) {
return fmt.Errorf("the regional PD feature is only available with the %s Kubernetes feature gate enabled", features.GCERegionalPersistentDisk)
}
return nil, fmt.Errorf("the regional PD feature is only available with the %s Kubernetes feature gate enabled", features.GCERegionalPersistentDisk)
diskTypeURI, err := manager.getDiskTypeURI(
manager.gce.region /* diskRegion */, multiZone{replicaZones}, diskType, true /* useBetaAPI */)
if err != nil {
return err
}
fullyQualifiedReplicaZones := []string{}
for _, replicaZone := range replicaZones.UnsortedList() {
fullyQualifiedReplicaZones = append(
fullyQualifiedReplicaZones, manager.getReplicaZoneURI(replicaZone, true))
}
diskToCreateBeta := &computebeta.Disk{
Name: name,
SizeGb: sizeGb,
Description: tagsStr,
Type: diskTypeURI,
ReplicaZones: fullyQualifiedReplicaZones,
}
ctx, cancel := cloud.ContextWithCallTimeout()
defer cancel()
return manager.gce.c.BetaRegionDisks().Insert(ctx, meta.RegionalKey(name, manager.gce.region), diskToCreateBeta)
}
func (manager *gceServiceManager) AttachDiskOnCloudProvider(
disk *GCEDisk,
readWrite string,
instanceZone string,
instanceName string) (gceObject, error) {
instanceName string) error {
source, err := manager.getDiskSourceURI(disk)
if err != nil {
return nil, err
return err
}
attachedDiskV1 := &compute.AttachedDisk{
@ -188,16 +188,19 @@ func (manager *gceServiceManager) AttachDiskOnCloudProvider(
Source: source,
Type: diskTypePersistent,
}
return manager.gce.service.Instances.AttachDisk(
manager.gce.projectID, instanceZone, instanceName, attachedDiskV1).Do()
ctx, cancel := cloud.ContextWithCallTimeout()
defer cancel()
return manager.gce.c.Instances().AttachDisk(ctx, meta.ZonalKey(instanceName, instanceZone), attachedDiskV1)
}
func (manager *gceServiceManager) DetachDiskOnCloudProvider(
instanceZone string,
instanceName string,
devicePath string) (gceObject, error) {
return manager.gce.service.Instances.DetachDisk(
manager.gce.projectID, instanceZone, instanceName, devicePath).Do()
devicePath string) error {
ctx, cancel := cloud.ContextWithCallTimeout()
defer cancel()
return manager.gce.c.Instances().DetachDisk(ctx, meta.ZonalKey(instanceName, instanceZone), devicePath)
}
func (manager *gceServiceManager) GetDiskFromCloudProvider(
@ -211,8 +214,9 @@ func (manager *gceServiceManager) GetDiskFromCloudProvider(
return nil, fmt.Errorf("Can not fetch disk. Zone is specified (%q). But disk name is empty.", zone)
}
diskStable, err := manager.gce.service.Disks.Get(
manager.gce.projectID, zone, diskName).Do()
ctx, cancel := cloud.ContextWithCallTimeout()
defer cancel()
diskStable, err := manager.gce.c.Disks().Get(ctx, meta.ZonalKey(diskName, zone))
if err != nil {
return nil, err
}
@ -240,56 +244,50 @@ func (manager *gceServiceManager) GetDiskFromCloudProvider(
func (manager *gceServiceManager) GetRegionalDiskFromCloudProvider(
diskName string) (*GCEDisk, error) {
if utilfeature.DefaultFeatureGate.Enabled(features.GCERegionalPersistentDisk) {
diskAlpha, err := manager.gce.serviceAlpha.RegionDisks.Get(
manager.gce.projectID, manager.gce.region, diskName).Do()
if err != nil {
return nil, err
}
zones := sets.NewString()
for _, zoneURI := range diskAlpha.ReplicaZones {
zones.Insert(lastComponent(zoneURI))
}
return &GCEDisk{
ZoneInfo: multiZone{zones},
Region: lastComponent(diskAlpha.Region),
Name: diskAlpha.Name,
Kind: diskAlpha.Kind,
Type: diskAlpha.Type,
SizeGb: diskAlpha.SizeGb,
}, nil
if !utilfeature.DefaultFeatureGate.Enabled(features.GCERegionalPersistentDisk) {
return nil, fmt.Errorf("the regional PD feature is only available with the %s Kubernetes feature gate enabled", features.GCERegionalPersistentDisk)
}
return nil, fmt.Errorf("the regional PD feature is only available with the %s Kubernetes feature gate enabled", features.GCERegionalPersistentDisk)
ctx, cancel := cloud.ContextWithCallTimeout()
defer cancel()
diskBeta, err := manager.gce.c.BetaRegionDisks().Get(ctx, meta.RegionalKey(diskName, manager.gce.region))
if err != nil {
return nil, err
}
zones := sets.NewString()
for _, zoneURI := range diskBeta.ReplicaZones {
zones.Insert(lastComponent(zoneURI))
}
return &GCEDisk{
ZoneInfo: multiZone{zones},
Region: lastComponent(diskBeta.Region),
Name: diskBeta.Name,
Kind: diskBeta.Kind,
Type: diskBeta.Type,
SizeGb: diskBeta.SizeGb,
}, nil
}
func (manager *gceServiceManager) DeleteDiskOnCloudProvider(
zone string,
diskName string) (gceObject, error) {
return manager.gce.service.Disks.Delete(
manager.gce.projectID, zone, diskName).Do()
diskName string) error {
ctx, cancel := cloud.ContextWithCallTimeout()
defer cancel()
return manager.gce.c.Disks().Delete(ctx, meta.ZonalKey(diskName, zone))
}
func (manager *gceServiceManager) DeleteRegionalDiskOnCloudProvider(
diskName string) (gceObject, error) {
if utilfeature.DefaultFeatureGate.Enabled(features.GCERegionalPersistentDisk) {
return manager.gce.serviceAlpha.RegionDisks.Delete(
manager.gce.projectID, manager.gce.region, diskName).Do()
diskName string) error {
if !utilfeature.DefaultFeatureGate.Enabled(features.GCERegionalPersistentDisk) {
return fmt.Errorf("the regional PD feature is only available with the %s Kubernetes feature gate enabled", features.GCERegionalPersistentDisk)
}
return nil, fmt.Errorf("the regional PD feature is only available with the %s Kubernetes feature gate enabled", features.GCERegionalPersistentDisk)
}
func (manager *gceServiceManager) WaitForZoneOp(
op gceObject, zone string, mc *metricContext) error {
return manager.gce.waitForZoneOp(op, zone, mc)
}
func (manager *gceServiceManager) WaitForRegionalOp(
op gceObject, mc *metricContext) error {
return manager.gce.waitForRegionOp(op, manager.gce.region, mc)
ctx, cancel := cloud.ContextWithCallTimeout()
defer cancel()
return manager.gce.c.BetaRegionDisks().Delete(ctx, meta.RegionalKey(diskName, manager.gce.region))
}
func (manager *gceServiceManager) getDiskSourceURI(disk *GCEDisk) (string, error) {
@ -327,11 +325,11 @@ func (manager *gceServiceManager) getDiskSourceURI(disk *GCEDisk) (string, error
}
func (manager *gceServiceManager) getDiskTypeURI(
diskRegion string, diskZoneInfo zoneType, diskType string, useAlphaAPI bool) (string, error) {
diskRegion string, diskZoneInfo zoneType, diskType string, useBetaAPI bool) (string, error) {
var getProjectsAPIEndpoint string
if useAlphaAPI {
getProjectsAPIEndpoint = manager.getProjectsAPIEndpointAlpha()
if useBetaAPI {
getProjectsAPIEndpoint = manager.getProjectsAPIEndpointBeta()
} else {
getProjectsAPIEndpoint = manager.getProjectsAPIEndpoint()
}
@ -363,10 +361,10 @@ func (manager *gceServiceManager) getDiskTypeURI(
}
}
func (manager *gceServiceManager) getReplicaZoneURI(zone string, useAlphaAPI bool) string {
func (manager *gceServiceManager) getReplicaZoneURI(zone string, useBetaAPI bool) string {
var getProjectsAPIEndpoint string
if useAlphaAPI {
getProjectsAPIEndpoint = manager.getProjectsAPIEndpointAlpha()
if useBetaAPI {
getProjectsAPIEndpoint = manager.getProjectsAPIEndpointBeta()
} else {
getProjectsAPIEndpoint = manager.getProjectsAPIEndpoint()
}
@ -411,21 +409,28 @@ func (manager *gceServiceManager) getRegionFromZone(zoneInfo zoneType) (string,
return region, nil
}
func (manager *gceServiceManager) ResizeDiskOnCloudProvider(disk *GCEDisk, sizeGb int64, zone string) (gceObject, error) {
func (manager *gceServiceManager) ResizeDiskOnCloudProvider(disk *GCEDisk, sizeGb int64, zone string) error {
resizeServiceRequest := &compute.DisksResizeRequest{
SizeGb: sizeGb,
}
return manager.gce.service.Disks.Resize(manager.gce.projectID, zone, disk.Name, resizeServiceRequest).Do()
ctx, cancel := cloud.ContextWithCallTimeout()
defer cancel()
return manager.gce.c.Disks().Resize(ctx, meta.ZonalKey(disk.Name, zone), resizeServiceRequest)
}
func (manager *gceServiceManager) RegionalResizeDiskOnCloudProvider(disk *GCEDisk, sizeGb int64) (gceObject, error) {
if utilfeature.DefaultFeatureGate.Enabled(features.GCERegionalPersistentDisk) {
resizeServiceRequest := &computealpha.RegionDisksResizeRequest{
SizeGb: sizeGb,
}
return manager.gce.serviceAlpha.RegionDisks.Resize(manager.gce.projectID, disk.Region, disk.Name, resizeServiceRequest).Do()
func (manager *gceServiceManager) RegionalResizeDiskOnCloudProvider(disk *GCEDisk, sizeGb int64) error {
if !utilfeature.DefaultFeatureGate.Enabled(features.GCERegionalPersistentDisk) {
return fmt.Errorf("the regional PD feature is only available with the %s Kubernetes feature gate enabled", features.GCERegionalPersistentDisk)
}
return nil, fmt.Errorf("the regional PD feature is only available with the %s Kubernetes feature gate enabled", features.GCERegionalPersistentDisk)
resizeServiceRequest := &computebeta.RegionDisksResizeRequest{
SizeGb: sizeGb,
}
ctx, cancel := cloud.ContextWithCallTimeout()
defer cancel()
return manager.gce.c.BetaRegionDisks().Resize(ctx, meta.RegionalKey(disk.Name, disk.Region), resizeServiceRequest)
}
// Disks is interface for manipulation with GCE PDs.
@ -535,14 +540,10 @@ func (gce *GCECloud) AttachDisk(diskName string, nodeName types.NodeName, readOn
if regional && utilfeature.DefaultFeatureGate.Enabled(features.GCERegionalPersistentDisk) {
disk, err = gce.getRegionalDiskByName(diskName)
if err != nil {
glog.V(5).Infof("Could not find regional PD named %q to Attach. Will look for a zonal PD", diskName)
err = nil
} else {
mc = newDiskMetricContextRegional("attach", gce.region)
return err
}
}
if disk == nil {
mc = newDiskMetricContextRegional("attach", gce.region)
} else {
disk, err = gce.getDiskByName(diskName, instance.Zone)
if err != nil {
return err
@ -555,14 +556,7 @@ func (gce *GCECloud) AttachDisk(diskName string, nodeName types.NodeName, readOn
readWrite = "READ_ONLY"
}
attachOp, err := gce.manager.AttachDiskOnCloudProvider(
disk, readWrite, instance.Zone, instance.Name)
if err != nil {
return mc.Observe(err)
}
return gce.manager.WaitForZoneOp(attachOp, instance.Zone, mc)
return mc.Observe(gce.manager.AttachDiskOnCloudProvider(disk, readWrite, instance.Zone, instance.Name))
}
func (gce *GCECloud) DetachDisk(devicePath string, nodeName types.NodeName) error {
@ -582,12 +576,7 @@ func (gce *GCECloud) DetachDisk(devicePath string, nodeName types.NodeName) erro
}
mc := newDiskMetricContextZonal("detach", gce.region, inst.Zone)
detachOp, err := gce.manager.DetachDiskOnCloudProvider(inst.Zone, inst.Name, devicePath)
if err != nil {
return mc.Observe(err)
}
return gce.manager.WaitForZoneOp(detachOp, inst.Zone, mc)
return mc.Observe(gce.manager.DetachDiskOnCloudProvider(inst.Zone, inst.Name, devicePath))
}
func (gce *GCECloud) DiskIsAttached(diskName string, nodeName types.NodeName) (bool, error) {
@ -675,17 +664,10 @@ func (gce *GCECloud) CreateDisk(
mc := newDiskMetricContextZonal("create", gce.region, zone)
createOp, err := gce.manager.CreateDiskOnCloudProvider(
err = gce.manager.CreateDiskOnCloudProvider(
name, sizeGb, tagsStr, diskType, zone)
if isGCEError(err, "alreadyExists") {
glog.Warningf("GCE PD %q already exists, reusing", name)
return nil
} else if err != nil {
return mc.Observe(err)
}
err = gce.manager.WaitForZoneOp(createOp, zone, mc)
mc.Observe(err)
if isGCEError(err, "alreadyExists") {
glog.Warningf("GCE PD %q already exists, reusing", name)
return nil
@ -723,17 +705,10 @@ func (gce *GCECloud) CreateRegionalDisk(
mc := newDiskMetricContextRegional("create", gce.region)
createOp, err := gce.manager.CreateRegionalDiskOnCloudProvider(
err = gce.manager.CreateRegionalDiskOnCloudProvider(
name, sizeGb, tagsStr, diskType, replicaZones)
if isGCEError(err, "alreadyExists") {
glog.Warningf("GCE PD %q already exists, reusing", name)
return nil
} else if err != nil {
return mc.Observe(err)
}
err = gce.manager.WaitForRegionalOp(createOp, mc)
mc.Observe(err)
if isGCEError(err, "alreadyExists") {
glog.Warningf("GCE PD %q already exists, reusing", name)
return nil
@ -786,31 +761,26 @@ func (gce *GCECloud) ResizeDisk(diskToResize string, oldSize resource.Quantity,
switch zoneInfo := disk.ZoneInfo.(type) {
case singleZone:
mc = newDiskMetricContextZonal("resize", disk.Region, zoneInfo.zone)
resizeOp, err := gce.manager.ResizeDiskOnCloudProvider(disk, requestGB, zoneInfo.zone)
err := gce.manager.ResizeDiskOnCloudProvider(disk, requestGB, zoneInfo.zone)
if err != nil {
return oldSize, mc.Observe(err)
} else {
return newSizeQuant, mc.Observe(err)
}
waitErr := gce.manager.WaitForZoneOp(resizeOp, zoneInfo.zone, mc)
if waitErr != nil {
return oldSize, waitErr
}
return newSizeQuant, nil
case multiZone:
if utilfeature.DefaultFeatureGate.Enabled(features.GCERegionalPersistentDisk) {
mc = newDiskMetricContextRegional("resize", disk.Region)
resizeOp, err := gce.manager.RegionalResizeDiskOnCloudProvider(disk, requestGB)
if err != nil {
return oldSize, mc.Observe(err)
}
waitErr := gce.manager.WaitForRegionalOp(resizeOp, mc)
if waitErr != nil {
return oldSize, waitErr
}
return newSizeQuant, nil
if !utilfeature.DefaultFeatureGate.Enabled(features.GCERegionalPersistentDisk) {
return oldSize, fmt.Errorf("disk.ZoneInfo has unexpected type %T", zoneInfo)
}
mc = newDiskMetricContextRegional("resize", disk.Region)
err := gce.manager.RegionalResizeDiskOnCloudProvider(disk, requestGB)
if err != nil {
return oldSize, mc.Observe(err)
} else {
return newSizeQuant, mc.Observe(err)
}
return oldSize, fmt.Errorf("disk.ZoneInfo has unexpected type %T", zoneInfo)
case nil:
return oldSize, fmt.Errorf("PD has nil ZoneInfo: %v", disk)
default:
@ -1026,21 +996,14 @@ func (gce *GCECloud) doDeleteDisk(diskToDelete string) error {
switch zoneInfo := disk.ZoneInfo.(type) {
case singleZone:
mc = newDiskMetricContextZonal("delete", disk.Region, zoneInfo.zone)
deleteOp, err := gce.manager.DeleteDiskOnCloudProvider(zoneInfo.zone, disk.Name)
if err != nil {
return mc.Observe(err)
}
return gce.manager.WaitForZoneOp(deleteOp, zoneInfo.zone, mc)
return mc.Observe(gce.manager.DeleteDiskOnCloudProvider(zoneInfo.zone, disk.Name))
case multiZone:
if utilfeature.DefaultFeatureGate.Enabled(features.GCERegionalPersistentDisk) {
mc = newDiskMetricContextRegional("delete", disk.Region)
deleteOp, err := gce.manager.DeleteRegionalDiskOnCloudProvider(disk.Name)
if err != nil {
return mc.Observe(err)
}
return gce.manager.WaitForRegionalOp(deleteOp, mc)
if !utilfeature.DefaultFeatureGate.Enabled(features.GCERegionalPersistentDisk) {
return fmt.Errorf("disk.ZoneInfo has unexpected type %T", zoneInfo)
}
return fmt.Errorf("disk.ZoneInfo has unexpected type %T", zoneInfo)
mc = newDiskMetricContextRegional("delete", disk.Region)
return mc.Observe(gce.manager.DeleteRegionalDiskOnCloudProvider(disk.Name))
case nil:
return fmt.Errorf("PD has nil ZoneInfo: %v", disk)
default:

View File

@ -39,10 +39,7 @@ func TestCreateDisk_Basic(t *testing.T) {
gceRegion := "fake-region"
zonesWithNodes := []string{"zone1"}
fakeManager := newFakeManager(gceProjectId, gceRegion)
alphaFeatureGate, featureGateErr := NewAlphaFeatureGate([]string{})
if featureGateErr != nil {
t.Error(featureGateErr)
}
alphaFeatureGate := NewAlphaFeatureGate([]string{})
gce := GCECloud{
manager: fakeManager,
managedZones: []string{"zone1"},
@ -73,9 +70,6 @@ func TestCreateDisk_Basic(t *testing.T) {
if !fakeManager.createDiskCalled {
t.Error("Never called GCE disk create.")
}
if !fakeManager.doesOpMatch {
t.Error("Ops used in WaitForZoneOp does not match what's returned by CreateDisk.")
}
// Partial check of equality between disk description sent to GCE and parameters of method.
diskToCreate := fakeManager.diskToCreateStable
@ -116,7 +110,7 @@ func TestCreateRegionalDisk_Basic(t *testing.T) {
tags := make(map[string]string)
tags["test-tag"] = "test-value"
expectedDiskTypeURI := gceComputeAPIEndpointAlpha + "projects/" + fmt.Sprintf(
expectedDiskTypeURI := gceComputeAPIEndpointBeta + "projects/" + fmt.Sprintf(
diskTypeURITemplateRegional, gceProjectId, gceRegion, diskType)
expectedDescription := "{\"test-tag\":\"test-value\"}"
@ -130,9 +124,6 @@ func TestCreateRegionalDisk_Basic(t *testing.T) {
if !fakeManager.createDiskCalled {
t.Error("Never called GCE disk create.")
}
if !fakeManager.doesOpMatch {
t.Error("Ops used in WaitForZoneOp does not match what's returned by CreateDisk.")
}
// Partial check of equality between disk description sent to GCE and parameters of method.
diskToCreate := fakeManager.diskToCreateStable
@ -157,10 +148,7 @@ func TestCreateDisk_DiskAlreadyExists(t *testing.T) {
gceRegion := "fake-region"
zonesWithNodes := []string{"zone1"}
fakeManager := newFakeManager(gceProjectId, gceRegion)
alphaFeatureGate, featureGateErr := NewAlphaFeatureGate([]string{})
if featureGateErr != nil {
t.Error(featureGateErr)
}
alphaFeatureGate := NewAlphaFeatureGate([]string{})
gce := GCECloud{
manager: fakeManager,
managedZones: zonesWithNodes,
@ -171,7 +159,7 @@ func TestCreateDisk_DiskAlreadyExists(t *testing.T) {
// Inject disk AlreadyExists error.
alreadyExistsError := googleapi.ErrorItem{Reason: "alreadyExists"}
fakeManager.waitForOpError = &googleapi.Error{
fakeManager.opError = &googleapi.Error{
Errors: []googleapi.ErrorItem{alreadyExistsError},
}
@ -266,10 +254,7 @@ func TestCreateDisk_MultiZone(t *testing.T) {
gceRegion := "fake-region"
zonesWithNodes := []string{"zone1", "zone2", "zone3"}
fakeManager := newFakeManager(gceProjectId, gceRegion)
alphaFeatureGate, featureGateErr := NewAlphaFeatureGate([]string{})
if featureGateErr != nil {
t.Error(featureGateErr)
}
alphaFeatureGate := NewAlphaFeatureGate([]string{})
gce := GCECloud{
manager: fakeManager,
managedZones: zonesWithNodes,
@ -298,10 +283,7 @@ func TestDeleteDisk_Basic(t *testing.T) {
gceRegion := "fake-region"
zonesWithNodes := []string{"zone1"}
fakeManager := newFakeManager(gceProjectId, gceRegion)
alphaFeatureGate, featureGateErr := NewAlphaFeatureGate([]string{})
if featureGateErr != nil {
t.Error(featureGateErr)
}
alphaFeatureGate := NewAlphaFeatureGate([]string{})
gce := GCECloud{
manager: fakeManager,
managedZones: zonesWithNodes,
@ -326,9 +308,6 @@ func TestDeleteDisk_Basic(t *testing.T) {
if !fakeManager.deleteDiskCalled {
t.Error("Never called GCE disk delete.")
}
if !fakeManager.doesOpMatch {
t.Error("Ops used in WaitForZoneOp does not match what's returned by DeleteDisk.")
}
}
@ -338,10 +317,7 @@ func TestDeleteDisk_NotFound(t *testing.T) {
gceRegion := "fake-region"
zonesWithNodes := []string{"zone1"}
fakeManager := newFakeManager(gceProjectId, gceRegion)
alphaFeatureGate, featureGateErr := NewAlphaFeatureGate([]string{})
if featureGateErr != nil {
t.Error(featureGateErr)
}
alphaFeatureGate := NewAlphaFeatureGate([]string{})
gce := GCECloud{
manager: fakeManager,
managedZones: zonesWithNodes,
@ -366,10 +342,7 @@ func TestDeleteDisk_ResourceBeingUsed(t *testing.T) {
gceRegion := "fake-region"
zonesWithNodes := []string{"zone1"}
fakeManager := newFakeManager(gceProjectId, gceRegion)
alphaFeatureGate, featureGateErr := NewAlphaFeatureGate([]string{})
if featureGateErr != nil {
t.Error(featureGateErr)
}
alphaFeatureGate := NewAlphaFeatureGate([]string{})
gce := GCECloud{
manager: fakeManager,
managedZones: zonesWithNodes,
@ -400,10 +373,7 @@ func TestDeleteDisk_SameDiskMultiZone(t *testing.T) {
gceRegion := "fake-region"
zonesWithNodes := []string{"zone1", "zone2", "zone3"}
fakeManager := newFakeManager(gceProjectId, gceRegion)
alphaFeatureGate, featureGateErr := NewAlphaFeatureGate([]string{})
if featureGateErr != nil {
t.Error(featureGateErr)
}
alphaFeatureGate := NewAlphaFeatureGate([]string{})
gce := GCECloud{
manager: fakeManager,
managedZones: zonesWithNodes,
@ -437,10 +407,7 @@ func TestDeleteDisk_DiffDiskMultiZone(t *testing.T) {
gceRegion := "fake-region"
zonesWithNodes := []string{"zone1"}
fakeManager := newFakeManager(gceProjectId, gceRegion)
alphaFeatureGate, featureGateErr := NewAlphaFeatureGate([]string{})
if featureGateErr != nil {
t.Error(featureGateErr)
}
alphaFeatureGate := NewAlphaFeatureGate([]string{})
gce := GCECloud{
manager: fakeManager,
managedZones: zonesWithNodes,
@ -478,10 +445,7 @@ func TestGetAutoLabelsForPD_Basic(t *testing.T) {
diskName := "disk"
diskType := DiskTypeSSD
const sizeGb int64 = 128
alphaFeatureGate, featureGateErr := NewAlphaFeatureGate([]string{})
if featureGateErr != nil {
t.Error(featureGateErr)
}
alphaFeatureGate := NewAlphaFeatureGate([]string{})
gce := GCECloud{
manager: fakeManager,
managedZones: zonesWithNodes,
@ -518,10 +482,7 @@ func TestGetAutoLabelsForPD_NoZone(t *testing.T) {
diskName := "disk"
diskType := DiskTypeStandard
const sizeGb int64 = 128
alphaFeatureGate, featureGateErr := NewAlphaFeatureGate([]string{})
if featureGateErr != nil {
t.Error(featureGateErr)
}
alphaFeatureGate := NewAlphaFeatureGate([]string{})
gce := GCECloud{
manager: fakeManager,
managedZones: zonesWithNodes,
@ -576,10 +537,7 @@ func TestGetAutoLabelsForPD_DiskNotFoundAndNoZone(t *testing.T) {
zonesWithNodes := []string{}
fakeManager := newFakeManager(gceProjectId, gceRegion)
diskName := "disk"
alphaFeatureGate, featureGateErr := NewAlphaFeatureGate([]string{})
if featureGateErr != nil {
t.Error(featureGateErr)
}
alphaFeatureGate := NewAlphaFeatureGate([]string{})
gce := GCECloud{
manager: fakeManager,
managedZones: zonesWithNodes,
@ -608,10 +566,7 @@ func TestGetAutoLabelsForPD_DupDisk(t *testing.T) {
zone := "us-west1-b"
const sizeGb int64 = 128
alphaFeatureGate, featureGateErr := NewAlphaFeatureGate([]string{})
if featureGateErr != nil {
t.Error(featureGateErr)
}
alphaFeatureGate := NewAlphaFeatureGate([]string{})
gce := GCECloud{
manager: fakeManager,
managedZones: zonesWithNodes,
@ -649,10 +604,7 @@ func TestGetAutoLabelsForPD_DupDiskNoZone(t *testing.T) {
diskType := DiskTypeStandard
const sizeGb int64 = 128
alphaFeatureGate, featureGateErr := NewAlphaFeatureGate([]string{})
if featureGateErr != nil {
t.Error(featureGateErr)
}
alphaFeatureGate := NewAlphaFeatureGate([]string{})
gce := GCECloud{
manager: fakeManager,
managedZones: zonesWithNodes,
@ -683,16 +635,12 @@ const (
type FakeServiceManager struct {
// Common fields shared among tests
targetAPI targetClientAPI
gceProjectID string
gceRegion string
opAlpha *computealpha.Operation // Mocks an operation returned by GCE API calls
opBeta *computebeta.Operation // Mocks an operation returned by GCE API calls
opStable *compute.Operation // Mocks an operation returned by GCE API calls
doesOpMatch bool
zonalDisks map[string]string // zone: diskName
regionalDisks map[string]sets.String // diskName: zones
waitForOpError error // Error to be returned by WaitForZoneOp or WaitForRegionalOp
targetAPI targetClientAPI
gceProjectID string
gceRegion string
zonalDisks map[string]string // zone: diskName
regionalDisks map[string]sets.String // diskName: zones
opError error
// Fields for TestCreateDisk
createDiskCalled bool
@ -723,12 +671,11 @@ func (manager *FakeServiceManager) CreateDiskOnCloudProvider(
sizeGb int64,
tagsStr string,
diskType string,
zone string) (gceObject, error) {
zone string) error {
manager.createDiskCalled = true
switch t := manager.targetAPI; t {
case targetStable:
manager.opStable = &compute.Operation{}
diskTypeURI := gceComputeAPIEndpoint + "projects/" + fmt.Sprintf(diskTypeURITemplateSingleZone, manager.gceProjectID, zone, diskType)
diskToCreateV1 := &compute.Disk{
Name: name,
@ -738,9 +685,8 @@ func (manager *FakeServiceManager) CreateDiskOnCloudProvider(
}
manager.diskToCreateStable = diskToCreateV1
manager.zonalDisks[zone] = diskToCreateV1.Name
return manager.opStable, nil
return nil
case targetBeta:
manager.opBeta = &computebeta.Operation{}
diskTypeURI := gceComputeAPIEndpoint + "projects/" + fmt.Sprintf(diskTypeURITemplateSingleZone, manager.gceProjectID, zone, diskType)
diskToCreateBeta := &computebeta.Disk{
Name: name,
@ -750,10 +696,9 @@ func (manager *FakeServiceManager) CreateDiskOnCloudProvider(
}
manager.diskToCreateBeta = diskToCreateBeta
manager.zonalDisks[zone] = diskToCreateBeta.Name
return manager.opBeta, nil
return nil
case targetAlpha:
manager.opAlpha = &computealpha.Operation{}
diskTypeURI := gceComputeAPIEndpointAlpha + "projects/" + fmt.Sprintf(diskTypeURITemplateSingleZone, manager.gceProjectID, zone, diskType)
diskTypeURI := gceComputeAPIEndpointBeta + "projects/" + fmt.Sprintf(diskTypeURITemplateSingleZone, manager.gceProjectID, zone, diskType)
diskToCreateAlpha := &computealpha.Disk{
Name: name,
SizeGb: sizeGb,
@ -762,9 +707,9 @@ func (manager *FakeServiceManager) CreateDiskOnCloudProvider(
}
manager.diskToCreateAlpha = diskToCreateAlpha
manager.zonalDisks[zone] = diskToCreateAlpha.Name
return manager.opAlpha, nil
return nil
default:
return nil, fmt.Errorf("unexpected type: %T", t)
return fmt.Errorf("unexpected type: %T", t)
}
}
@ -777,13 +722,12 @@ func (manager *FakeServiceManager) CreateRegionalDiskOnCloudProvider(
sizeGb int64,
tagsStr string,
diskType string,
zones sets.String) (gceObject, error) {
zones sets.String) error {
manager.createDiskCalled = true
diskTypeURI := gceComputeAPIEndpointAlpha + "projects/" + fmt.Sprintf(diskTypeURITemplateRegional, manager.gceProjectID, manager.gceRegion, diskType)
diskTypeURI := gceComputeAPIEndpointBeta + "projects/" + fmt.Sprintf(diskTypeURITemplateRegional, manager.gceProjectID, manager.gceRegion, diskType)
switch t := manager.targetAPI; t {
case targetStable:
manager.opStable = &compute.Operation{}
diskToCreateV1 := &compute.Disk{
Name: name,
SizeGb: sizeGb,
@ -792,13 +736,13 @@ func (manager *FakeServiceManager) CreateRegionalDiskOnCloudProvider(
}
manager.diskToCreateStable = diskToCreateV1
manager.regionalDisks[diskToCreateV1.Name] = zones
return manager.opStable, nil
return nil
case targetBeta:
return nil, fmt.Errorf("RegionalDisk CreateDisk op not supported in beta.")
return fmt.Errorf("RegionalDisk CreateDisk op not supported in beta.")
case targetAlpha:
return nil, fmt.Errorf("RegionalDisk CreateDisk op not supported in alpha.")
return fmt.Errorf("RegionalDisk CreateDisk op not supported in alpha.")
default:
return nil, fmt.Errorf("unexpected type: %T", t)
return fmt.Errorf("unexpected type: %T", t)
}
}
@ -806,39 +750,33 @@ func (manager *FakeServiceManager) AttachDiskOnCloudProvider(
disk *GCEDisk,
readWrite string,
instanceZone string,
instanceName string) (gceObject, error) {
instanceName string) error {
switch t := manager.targetAPI; t {
case targetStable:
manager.opStable = &compute.Operation{}
return manager.opStable, nil
return nil
case targetBeta:
manager.opBeta = &computebeta.Operation{}
return manager.opBeta, nil
return nil
case targetAlpha:
manager.opAlpha = &computealpha.Operation{}
return manager.opAlpha, nil
return nil
default:
return nil, fmt.Errorf("unexpected type: %T", t)
return fmt.Errorf("unexpected type: %T", t)
}
}
func (manager *FakeServiceManager) DetachDiskOnCloudProvider(
instanceZone string,
instanceName string,
devicePath string) (gceObject, error) {
devicePath string) error {
switch t := manager.targetAPI; t {
case targetStable:
manager.opStable = &compute.Operation{}
return manager.opStable, nil
return nil
case targetBeta:
manager.opBeta = &computebeta.Operation{}
return manager.opBeta, nil
return nil
case targetAlpha:
manager.opAlpha = &computealpha.Operation{}
return manager.opAlpha, nil
return nil
default:
return nil, fmt.Errorf("unexpected type: %T", t)
return fmt.Errorf("unexpected type: %T", t)
}
}
@ -895,13 +833,13 @@ func (manager *FakeServiceManager) GetRegionalDiskFromCloudProvider(
func (manager *FakeServiceManager) ResizeDiskOnCloudProvider(
disk *GCEDisk,
size int64,
zone string) (gceObject, error) {
zone string) error {
panic("Not implmented")
}
func (manager *FakeServiceManager) RegionalResizeDiskOnCloudProvider(
disk *GCEDisk,
size int64) (gceObject, error) {
size int64) error {
panic("Not implemented")
}
@ -910,91 +848,41 @@ func (manager *FakeServiceManager) RegionalResizeDiskOnCloudProvider(
*/
func (manager *FakeServiceManager) DeleteDiskOnCloudProvider(
zone string,
disk string) (gceObject, error) {
disk string) error {
manager.deleteDiskCalled = true
delete(manager.zonalDisks, zone)
switch t := manager.targetAPI; t {
case targetStable:
manager.opStable = &compute.Operation{}
return manager.opStable, nil
return nil
case targetBeta:
manager.opBeta = &computebeta.Operation{}
return manager.opBeta, nil
return nil
case targetAlpha:
manager.opAlpha = &computealpha.Operation{}
return manager.opAlpha, nil
return nil
default:
return nil, fmt.Errorf("unexpected type: %T", t)
return fmt.Errorf("unexpected type: %T", t)
}
}
func (manager *FakeServiceManager) DeleteRegionalDiskOnCloudProvider(
disk string) (gceObject, error) {
disk string) error {
manager.deleteDiskCalled = true
delete(manager.regionalDisks, disk)
switch t := manager.targetAPI; t {
case targetStable:
manager.opStable = &compute.Operation{}
return manager.opStable, nil
return nil
case targetBeta:
manager.opBeta = &computebeta.Operation{}
return manager.opBeta, nil
return nil
case targetAlpha:
manager.opAlpha = &computealpha.Operation{}
return manager.opAlpha, nil
return nil
default:
return nil, fmt.Errorf("unexpected type: %T", t)
return fmt.Errorf("unexpected type: %T", t)
}
}
func (manager *FakeServiceManager) WaitForZoneOp(
op gceObject,
zone string,
mc *metricContext) error {
switch v := op.(type) {
case *computealpha.Operation:
if op.(*computealpha.Operation) == manager.opAlpha {
manager.doesOpMatch = true
}
case *computebeta.Operation:
if op.(*computebeta.Operation) == manager.opBeta {
manager.doesOpMatch = true
}
case *compute.Operation:
if op.(*compute.Operation) == manager.opStable {
manager.doesOpMatch = true
}
default:
return fmt.Errorf("unexpected type: %T", v)
}
return manager.waitForOpError
}
func (manager *FakeServiceManager) WaitForRegionalOp(
op gceObject, mc *metricContext) error {
switch v := op.(type) {
case *computealpha.Operation:
if op.(*computealpha.Operation) == manager.opAlpha {
manager.doesOpMatch = true
}
case *computebeta.Operation:
if op.(*computebeta.Operation) == manager.opBeta {
manager.doesOpMatch = true
}
case *compute.Operation:
if op.(*compute.Operation) == manager.opStable {
manager.doesOpMatch = true
}
default:
return fmt.Errorf("unexpected type: %T", v)
}
return manager.waitForOpError
}
func createNodeZones(zones []string) map[string]sets.String {
nodeZones := map[string]sets.String{}
for _, zone := range zones {

View File

@ -17,10 +17,9 @@ limitations under the License.
package gce
import (
"context"
compute "google.golang.org/api/compute/v1"
"k8s.io/kubernetes/pkg/cloudprovider/providers/gce/cloud"
"k8s.io/kubernetes/pkg/cloudprovider/providers/gce/cloud/meta"
)
@ -30,25 +29,37 @@ func newFirewallMetricContext(request string) *metricContext {
// GetFirewall returns the Firewall by name.
func (gce *GCECloud) GetFirewall(name string) (*compute.Firewall, error) {
ctx, cancel := cloud.ContextWithCallTimeout()
defer cancel()
mc := newFirewallMetricContext("get")
v, err := gce.c.Firewalls().Get(context.Background(), meta.GlobalKey(name))
v, err := gce.c.Firewalls().Get(ctx, meta.GlobalKey(name))
return v, mc.Observe(err)
}
// CreateFirewall creates the passed firewall
func (gce *GCECloud) CreateFirewall(f *compute.Firewall) error {
ctx, cancel := cloud.ContextWithCallTimeout()
defer cancel()
mc := newFirewallMetricContext("create")
return mc.Observe(gce.c.Firewalls().Insert(context.Background(), meta.GlobalKey(f.Name), f))
return mc.Observe(gce.c.Firewalls().Insert(ctx, meta.GlobalKey(f.Name), f))
}
// DeleteFirewall deletes the given firewall rule.
func (gce *GCECloud) DeleteFirewall(name string) error {
ctx, cancel := cloud.ContextWithCallTimeout()
defer cancel()
mc := newFirewallMetricContext("delete")
return mc.Observe(gce.c.Firewalls().Delete(context.Background(), meta.GlobalKey(name)))
return mc.Observe(gce.c.Firewalls().Delete(ctx, meta.GlobalKey(name)))
}
// UpdateFirewall applies the given firewall as an update to an existing service.
func (gce *GCECloud) UpdateFirewall(f *compute.Firewall) error {
ctx, cancel := cloud.ContextWithCallTimeout()
defer cancel()
mc := newFirewallMetricContext("update")
return mc.Observe(gce.c.Firewalls().Update(context.Background(), meta.GlobalKey(f.Name), f))
return mc.Observe(gce.c.Firewalls().Update(ctx, meta.GlobalKey(f.Name), f))
}

View File

@ -17,8 +17,6 @@ limitations under the License.
package gce
import (
"context"
computealpha "google.golang.org/api/compute/v0.alpha"
compute "google.golang.org/api/compute/v1"
"k8s.io/kubernetes/pkg/cloudprovider/providers/gce/cloud"
@ -35,84 +33,120 @@ func newForwardingRuleMetricContextWithVersion(request, region, version string)
// CreateGlobalForwardingRule creates the passed GlobalForwardingRule
func (gce *GCECloud) CreateGlobalForwardingRule(rule *compute.ForwardingRule) error {
ctx, cancel := cloud.ContextWithCallTimeout()
defer cancel()
mc := newForwardingRuleMetricContext("create", "")
return mc.Observe(gce.c.GlobalForwardingRules().Insert(context.Background(), meta.GlobalKey(rule.Name), rule))
return mc.Observe(gce.c.GlobalForwardingRules().Insert(ctx, meta.GlobalKey(rule.Name), rule))
}
// SetProxyForGlobalForwardingRule links the given TargetHttp(s)Proxy with the given GlobalForwardingRule.
// targetProxyLink is the SelfLink of a TargetHttp(s)Proxy.
func (gce *GCECloud) SetProxyForGlobalForwardingRule(forwardingRuleName, targetProxyLink string) error {
ctx, cancel := cloud.ContextWithCallTimeout()
defer cancel()
mc := newForwardingRuleMetricContext("set_proxy", "")
target := &compute.TargetReference{Target: targetProxyLink}
return mc.Observe(gce.c.GlobalForwardingRules().SetTarget(context.Background(), meta.GlobalKey(forwardingRuleName), target))
return mc.Observe(gce.c.GlobalForwardingRules().SetTarget(ctx, meta.GlobalKey(forwardingRuleName), target))
}
// DeleteGlobalForwardingRule deletes the GlobalForwardingRule by name.
func (gce *GCECloud) DeleteGlobalForwardingRule(name string) error {
ctx, cancel := cloud.ContextWithCallTimeout()
defer cancel()
mc := newForwardingRuleMetricContext("delete", "")
return mc.Observe(gce.c.GlobalForwardingRules().Delete(context.Background(), meta.GlobalKey(name)))
return mc.Observe(gce.c.GlobalForwardingRules().Delete(ctx, meta.GlobalKey(name)))
}
// GetGlobalForwardingRule returns the GlobalForwardingRule by name.
func (gce *GCECloud) GetGlobalForwardingRule(name string) (*compute.ForwardingRule, error) {
ctx, cancel := cloud.ContextWithCallTimeout()
defer cancel()
mc := newForwardingRuleMetricContext("get", "")
v, err := gce.c.GlobalForwardingRules().Get(context.Background(), meta.GlobalKey(name))
v, err := gce.c.GlobalForwardingRules().Get(ctx, meta.GlobalKey(name))
return v, mc.Observe(err)
}
// ListGlobalForwardingRules lists all GlobalForwardingRules in the project.
func (gce *GCECloud) ListGlobalForwardingRules() ([]*compute.ForwardingRule, error) {
ctx, cancel := cloud.ContextWithCallTimeout()
defer cancel()
mc := newForwardingRuleMetricContext("list", "")
v, err := gce.c.GlobalForwardingRules().List(context.Background(), filter.None)
v, err := gce.c.GlobalForwardingRules().List(ctx, filter.None)
return v, mc.Observe(err)
}
// GetRegionForwardingRule returns the RegionalForwardingRule by name & region.
func (gce *GCECloud) GetRegionForwardingRule(name, region string) (*compute.ForwardingRule, error) {
ctx, cancel := cloud.ContextWithCallTimeout()
defer cancel()
mc := newForwardingRuleMetricContext("get", region)
v, err := gce.c.ForwardingRules().Get(context.Background(), meta.RegionalKey(name, region))
v, err := gce.c.ForwardingRules().Get(ctx, meta.RegionalKey(name, region))
return v, mc.Observe(err)
}
// GetAlphaRegionForwardingRule returns the Alpha forwarding rule by name & region.
func (gce *GCECloud) GetAlphaRegionForwardingRule(name, region string) (*computealpha.ForwardingRule, error) {
ctx, cancel := cloud.ContextWithCallTimeout()
defer cancel()
mc := newForwardingRuleMetricContextWithVersion("get", region, computeAlphaVersion)
v, err := gce.c.AlphaForwardingRules().Get(context.Background(), meta.RegionalKey(name, region))
v, err := gce.c.AlphaForwardingRules().Get(ctx, meta.RegionalKey(name, region))
return v, mc.Observe(err)
}
// ListRegionForwardingRules lists all RegionalForwardingRules in the project & region.
func (gce *GCECloud) ListRegionForwardingRules(region string) ([]*compute.ForwardingRule, error) {
ctx, cancel := cloud.ContextWithCallTimeout()
defer cancel()
mc := newForwardingRuleMetricContext("list", region)
v, err := gce.c.ForwardingRules().List(context.Background(), region, filter.None)
v, err := gce.c.ForwardingRules().List(ctx, region, filter.None)
return v, mc.Observe(err)
}
// ListAlphaRegionForwardingRules lists all RegionalForwardingRules in the project & region.
func (gce *GCECloud) ListAlphaRegionForwardingRules(region string) ([]*computealpha.ForwardingRule, error) {
ctx, cancel := cloud.ContextWithCallTimeout()
defer cancel()
mc := newForwardingRuleMetricContextWithVersion("list", region, computeAlphaVersion)
v, err := gce.c.AlphaForwardingRules().List(context.Background(), region, filter.None)
v, err := gce.c.AlphaForwardingRules().List(ctx, region, filter.None)
return v, mc.Observe(err)
}
// CreateRegionForwardingRule creates and returns a
// RegionalForwardingRule that points to the given BackendService
func (gce *GCECloud) CreateRegionForwardingRule(rule *compute.ForwardingRule, region string) error {
ctx, cancel := cloud.ContextWithCallTimeout()
defer cancel()
mc := newForwardingRuleMetricContext("create", region)
return mc.Observe(gce.c.ForwardingRules().Insert(context.Background(), meta.RegionalKey(rule.Name, region), rule))
return mc.Observe(gce.c.ForwardingRules().Insert(ctx, meta.RegionalKey(rule.Name, region), rule))
}
// CreateAlphaRegionForwardingRule creates and returns an Alpha
// forwarding fule in the given region.
func (gce *GCECloud) CreateAlphaRegionForwardingRule(rule *computealpha.ForwardingRule, region string) error {
ctx, cancel := cloud.ContextWithCallTimeout()
defer cancel()
mc := newForwardingRuleMetricContextWithVersion("create", region, computeAlphaVersion)
return mc.Observe(gce.c.AlphaForwardingRules().Insert(context.Background(), meta.RegionalKey(rule.Name, region), rule))
return mc.Observe(gce.c.AlphaForwardingRules().Insert(ctx, meta.RegionalKey(rule.Name, region), rule))
}
// DeleteRegionForwardingRule deletes the RegionalForwardingRule by name & region.
func (gce *GCECloud) DeleteRegionForwardingRule(name, region string) error {
ctx, cancel := cloud.ContextWithCallTimeout()
defer cancel()
mc := newForwardingRuleMetricContext("delete", region)
return mc.Observe(gce.c.ForwardingRules().Delete(context.Background(), meta.RegionalKey(name, region)))
return mc.Observe(gce.c.ForwardingRules().Delete(ctx, meta.RegionalKey(name, region)))
}
// TODO(#51665): retire this function once Network Tiers becomes Beta in GCP.

View File

@ -17,14 +17,13 @@ limitations under the License.
package gce
import (
"context"
"github.com/golang/glog"
computealpha "google.golang.org/api/compute/v0.alpha"
compute "google.golang.org/api/compute/v1"
"k8s.io/api/core/v1"
"k8s.io/kubernetes/pkg/cloudprovider/providers/gce/cloud"
"k8s.io/kubernetes/pkg/cloudprovider/providers/gce/cloud/filter"
"k8s.io/kubernetes/pkg/cloudprovider/providers/gce/cloud/meta"
"k8s.io/kubernetes/pkg/master/ports"
@ -58,33 +57,48 @@ func newHealthcheckMetricContextWithVersion(request, version string) *metricCont
// GetHttpHealthCheck returns the given HttpHealthCheck by name.
func (gce *GCECloud) GetHttpHealthCheck(name string) (*compute.HttpHealthCheck, error) {
ctx, cancel := cloud.ContextWithCallTimeout()
defer cancel()
mc := newHealthcheckMetricContext("get_legacy")
v, err := gce.c.HttpHealthChecks().Get(context.Background(), meta.GlobalKey(name))
v, err := gce.c.HttpHealthChecks().Get(ctx, meta.GlobalKey(name))
return v, mc.Observe(err)
}
// UpdateHttpHealthCheck applies the given HttpHealthCheck as an update.
func (gce *GCECloud) UpdateHttpHealthCheck(hc *compute.HttpHealthCheck) error {
ctx, cancel := cloud.ContextWithCallTimeout()
defer cancel()
mc := newHealthcheckMetricContext("update_legacy")
return mc.Observe(gce.c.HttpHealthChecks().Update(context.Background(), meta.GlobalKey(hc.Name), hc))
return mc.Observe(gce.c.HttpHealthChecks().Update(ctx, meta.GlobalKey(hc.Name), hc))
}
// DeleteHttpHealthCheck deletes the given HttpHealthCheck by name.
func (gce *GCECloud) DeleteHttpHealthCheck(name string) error {
ctx, cancel := cloud.ContextWithCallTimeout()
defer cancel()
mc := newHealthcheckMetricContext("delete_legacy")
return mc.Observe(gce.c.HttpHealthChecks().Delete(context.Background(), meta.GlobalKey(name)))
return mc.Observe(gce.c.HttpHealthChecks().Delete(ctx, meta.GlobalKey(name)))
}
// CreateHttpHealthCheck creates the given HttpHealthCheck.
func (gce *GCECloud) CreateHttpHealthCheck(hc *compute.HttpHealthCheck) error {
ctx, cancel := cloud.ContextWithCallTimeout()
defer cancel()
mc := newHealthcheckMetricContext("create_legacy")
return mc.Observe(gce.c.HttpHealthChecks().Insert(context.Background(), meta.GlobalKey(hc.Name), hc))
return mc.Observe(gce.c.HttpHealthChecks().Insert(ctx, meta.GlobalKey(hc.Name), hc))
}
// ListHttpHealthChecks lists all HttpHealthChecks in the project.
func (gce *GCECloud) ListHttpHealthChecks() ([]*compute.HttpHealthCheck, error) {
ctx, cancel := cloud.ContextWithCallTimeout()
defer cancel()
mc := newHealthcheckMetricContext("list_legacy")
v, err := gce.c.HttpHealthChecks().List(context.Background(), filter.None)
v, err := gce.c.HttpHealthChecks().List(ctx, filter.None)
return v, mc.Observe(err)
}
@ -92,33 +106,48 @@ func (gce *GCECloud) ListHttpHealthChecks() ([]*compute.HttpHealthCheck, error)
// GetHttpsHealthCheck returns the given HttpsHealthCheck by name.
func (gce *GCECloud) GetHttpsHealthCheck(name string) (*compute.HttpsHealthCheck, error) {
ctx, cancel := cloud.ContextWithCallTimeout()
defer cancel()
mc := newHealthcheckMetricContext("get_legacy")
v, err := gce.c.HttpsHealthChecks().Get(context.Background(), meta.GlobalKey(name))
v, err := gce.c.HttpsHealthChecks().Get(ctx, meta.GlobalKey(name))
return v, mc.Observe(err)
}
// UpdateHttpsHealthCheck applies the given HttpsHealthCheck as an update.
func (gce *GCECloud) UpdateHttpsHealthCheck(hc *compute.HttpsHealthCheck) error {
ctx, cancel := cloud.ContextWithCallTimeout()
defer cancel()
mc := newHealthcheckMetricContext("update_legacy")
return mc.Observe(gce.c.HttpsHealthChecks().Update(context.Background(), meta.GlobalKey(hc.Name), hc))
return mc.Observe(gce.c.HttpsHealthChecks().Update(ctx, meta.GlobalKey(hc.Name), hc))
}
// DeleteHttpsHealthCheck deletes the given HttpsHealthCheck by name.
func (gce *GCECloud) DeleteHttpsHealthCheck(name string) error {
ctx, cancel := cloud.ContextWithCallTimeout()
defer cancel()
mc := newHealthcheckMetricContext("delete_legacy")
return mc.Observe(gce.c.HttpsHealthChecks().Delete(context.Background(), meta.GlobalKey(name)))
return mc.Observe(gce.c.HttpsHealthChecks().Delete(ctx, meta.GlobalKey(name)))
}
// CreateHttpsHealthCheck creates the given HttpsHealthCheck.
func (gce *GCECloud) CreateHttpsHealthCheck(hc *compute.HttpsHealthCheck) error {
ctx, cancel := cloud.ContextWithCallTimeout()
defer cancel()
mc := newHealthcheckMetricContext("create_legacy")
return mc.Observe(gce.c.HttpsHealthChecks().Insert(context.Background(), meta.GlobalKey(hc.Name), hc))
return mc.Observe(gce.c.HttpsHealthChecks().Insert(ctx, meta.GlobalKey(hc.Name), hc))
}
// ListHttpsHealthChecks lists all HttpsHealthChecks in the project.
func (gce *GCECloud) ListHttpsHealthChecks() ([]*compute.HttpsHealthCheck, error) {
ctx, cancel := cloud.ContextWithCallTimeout()
defer cancel()
mc := newHealthcheckMetricContext("list_legacy")
v, err := gce.c.HttpsHealthChecks().List(context.Background(), filter.None)
v, err := gce.c.HttpsHealthChecks().List(ctx, filter.None)
return v, mc.Observe(err)
}
@ -126,52 +155,76 @@ func (gce *GCECloud) ListHttpsHealthChecks() ([]*compute.HttpsHealthCheck, error
// GetHealthCheck returns the given HealthCheck by name.
func (gce *GCECloud) GetHealthCheck(name string) (*compute.HealthCheck, error) {
ctx, cancel := cloud.ContextWithCallTimeout()
defer cancel()
mc := newHealthcheckMetricContext("get")
v, err := gce.c.HealthChecks().Get(context.Background(), meta.GlobalKey(name))
v, err := gce.c.HealthChecks().Get(ctx, meta.GlobalKey(name))
return v, mc.Observe(err)
}
// GetAlphaHealthCheck returns the given alpha HealthCheck by name.
func (gce *GCECloud) GetAlphaHealthCheck(name string) (*computealpha.HealthCheck, error) {
ctx, cancel := cloud.ContextWithCallTimeout()
defer cancel()
mc := newHealthcheckMetricContextWithVersion("get", computeAlphaVersion)
v, err := gce.c.AlphaHealthChecks().Get(context.Background(), meta.GlobalKey(name))
v, err := gce.c.AlphaHealthChecks().Get(ctx, meta.GlobalKey(name))
return v, mc.Observe(err)
}
// UpdateHealthCheck applies the given HealthCheck as an update.
func (gce *GCECloud) UpdateHealthCheck(hc *compute.HealthCheck) error {
ctx, cancel := cloud.ContextWithCallTimeout()
defer cancel()
mc := newHealthcheckMetricContext("update")
return mc.Observe(gce.c.HealthChecks().Update(context.Background(), meta.GlobalKey(hc.Name), hc))
return mc.Observe(gce.c.HealthChecks().Update(ctx, meta.GlobalKey(hc.Name), hc))
}
// UpdateAlphaHealthCheck applies the given alpha HealthCheck as an update.
func (gce *GCECloud) UpdateAlphaHealthCheck(hc *computealpha.HealthCheck) error {
ctx, cancel := cloud.ContextWithCallTimeout()
defer cancel()
mc := newHealthcheckMetricContextWithVersion("update", computeAlphaVersion)
return mc.Observe(gce.c.AlphaHealthChecks().Update(context.Background(), meta.GlobalKey(hc.Name), hc))
return mc.Observe(gce.c.AlphaHealthChecks().Update(ctx, meta.GlobalKey(hc.Name), hc))
}
// DeleteHealthCheck deletes the given HealthCheck by name.
func (gce *GCECloud) DeleteHealthCheck(name string) error {
ctx, cancel := cloud.ContextWithCallTimeout()
defer cancel()
mc := newHealthcheckMetricContext("delete")
return mc.Observe(gce.c.HealthChecks().Delete(context.Background(), meta.GlobalKey(name)))
return mc.Observe(gce.c.HealthChecks().Delete(ctx, meta.GlobalKey(name)))
}
// CreateHealthCheck creates the given HealthCheck.
func (gce *GCECloud) CreateHealthCheck(hc *compute.HealthCheck) error {
ctx, cancel := cloud.ContextWithCallTimeout()
defer cancel()
mc := newHealthcheckMetricContext("create")
return mc.Observe(gce.c.HealthChecks().Insert(context.Background(), meta.GlobalKey(hc.Name), hc))
return mc.Observe(gce.c.HealthChecks().Insert(ctx, meta.GlobalKey(hc.Name), hc))
}
// CreateAlphaHealthCheck creates the given alpha HealthCheck.
func (gce *GCECloud) CreateAlphaHealthCheck(hc *computealpha.HealthCheck) error {
ctx, cancel := cloud.ContextWithCallTimeout()
defer cancel()
mc := newHealthcheckMetricContextWithVersion("create", computeAlphaVersion)
return mc.Observe(gce.c.AlphaHealthChecks().Insert(context.Background(), meta.GlobalKey(hc.Name), hc))
return mc.Observe(gce.c.AlphaHealthChecks().Insert(ctx, meta.GlobalKey(hc.Name), hc))
}
// ListHealthChecks lists all HealthCheck in the project.
func (gce *GCECloud) ListHealthChecks() ([]*compute.HealthCheck, error) {
ctx, cancel := cloud.ContextWithCallTimeout()
defer cancel()
mc := newHealthcheckMetricContext("list")
v, err := gce.c.HealthChecks().List(context.Background(), filter.None)
v, err := gce.c.HealthChecks().List(ctx, filter.None)
return v, mc.Observe(err)
}

View File

@ -17,10 +17,9 @@ limitations under the License.
package gce
import (
"context"
compute "google.golang.org/api/compute/v1"
"k8s.io/kubernetes/pkg/cloudprovider/providers/gce/cloud"
"k8s.io/kubernetes/pkg/cloudprovider/providers/gce/cloud/filter"
"k8s.io/kubernetes/pkg/cloudprovider/providers/gce/cloud/meta"
)
@ -32,36 +31,51 @@ func newInstanceGroupMetricContext(request string, zone string) *metricContext {
// CreateInstanceGroup creates an instance group with the given
// instances. It is the callers responsibility to add named ports.
func (gce *GCECloud) CreateInstanceGroup(ig *compute.InstanceGroup, zone string) error {
ctx, cancel := cloud.ContextWithCallTimeout()
defer cancel()
mc := newInstanceGroupMetricContext("create", zone)
return mc.Observe(gce.c.InstanceGroups().Insert(context.Background(), meta.ZonalKey(ig.Name, zone), ig))
return mc.Observe(gce.c.InstanceGroups().Insert(ctx, meta.ZonalKey(ig.Name, zone), ig))
}
// DeleteInstanceGroup deletes an instance group.
func (gce *GCECloud) DeleteInstanceGroup(name string, zone string) error {
ctx, cancel := cloud.ContextWithCallTimeout()
defer cancel()
mc := newInstanceGroupMetricContext("delete", zone)
return mc.Observe(gce.c.InstanceGroups().Delete(context.Background(), meta.ZonalKey(name, zone)))
return mc.Observe(gce.c.InstanceGroups().Delete(ctx, meta.ZonalKey(name, zone)))
}
// ListInstanceGroups lists all InstanceGroups in the project and
// zone.
func (gce *GCECloud) ListInstanceGroups(zone string) ([]*compute.InstanceGroup, error) {
ctx, cancel := cloud.ContextWithCallTimeout()
defer cancel()
mc := newInstanceGroupMetricContext("list", zone)
v, err := gce.c.InstanceGroups().List(context.Background(), zone, filter.None)
v, err := gce.c.InstanceGroups().List(ctx, zone, filter.None)
return v, mc.Observe(err)
}
// ListInstancesInInstanceGroup lists all the instances in a given
// instance group and state.
func (gce *GCECloud) ListInstancesInInstanceGroup(name string, zone string, state string) ([]*compute.InstanceWithNamedPorts, error) {
ctx, cancel := cloud.ContextWithCallTimeout()
defer cancel()
mc := newInstanceGroupMetricContext("list_instances", zone)
req := &compute.InstanceGroupsListInstancesRequest{InstanceState: state}
v, err := gce.c.InstanceGroups().ListInstances(context.Background(), meta.ZonalKey(name, zone), req, filter.None)
v, err := gce.c.InstanceGroups().ListInstances(ctx, meta.ZonalKey(name, zone), req, filter.None)
return v, mc.Observe(err)
}
// AddInstancesToInstanceGroup adds the given instances to the given
// instance group.
func (gce *GCECloud) AddInstancesToInstanceGroup(name string, zone string, instanceRefs []*compute.InstanceReference) error {
ctx, cancel := cloud.ContextWithCallTimeout()
defer cancel()
mc := newInstanceGroupMetricContext("add_instances", zone)
// TODO: should cull operation above this layer.
if len(instanceRefs) == 0 {
@ -70,12 +84,15 @@ func (gce *GCECloud) AddInstancesToInstanceGroup(name string, zone string, insta
req := &compute.InstanceGroupsAddInstancesRequest{
Instances: instanceRefs,
}
return mc.Observe(gce.c.InstanceGroups().AddInstances(context.Background(), meta.ZonalKey(name, zone), req))
return mc.Observe(gce.c.InstanceGroups().AddInstances(ctx, meta.ZonalKey(name, zone), req))
}
// RemoveInstancesFromInstanceGroup removes the given instances from
// the instance group.
func (gce *GCECloud) RemoveInstancesFromInstanceGroup(name string, zone string, instanceRefs []*compute.InstanceReference) error {
ctx, cancel := cloud.ContextWithCallTimeout()
defer cancel()
mc := newInstanceGroupMetricContext("remove_instances", zone)
// TODO: should cull operation above this layer.
if len(instanceRefs) == 0 {
@ -84,19 +101,25 @@ func (gce *GCECloud) RemoveInstancesFromInstanceGroup(name string, zone string,
req := &compute.InstanceGroupsRemoveInstancesRequest{
Instances: instanceRefs,
}
return mc.Observe(gce.c.InstanceGroups().RemoveInstances(context.Background(), meta.ZonalKey(name, zone), req))
return mc.Observe(gce.c.InstanceGroups().RemoveInstances(ctx, meta.ZonalKey(name, zone), req))
}
// SetNamedPortsOfInstanceGroup sets the list of named ports on a given instance group
func (gce *GCECloud) SetNamedPortsOfInstanceGroup(igName, zone string, namedPorts []*compute.NamedPort) error {
ctx, cancel := cloud.ContextWithCallTimeout()
defer cancel()
mc := newInstanceGroupMetricContext("set_namedports", zone)
req := &compute.InstanceGroupsSetNamedPortsRequest{NamedPorts: namedPorts}
return mc.Observe(gce.c.InstanceGroups().SetNamedPorts(context.Background(), meta.ZonalKey(igName, zone), req))
return mc.Observe(gce.c.InstanceGroups().SetNamedPorts(ctx, meta.ZonalKey(igName, zone), req))
}
// GetInstanceGroup returns an instance group by name.
func (gce *GCECloud) GetInstanceGroup(name string, zone string) (*compute.InstanceGroup, error) {
ctx, cancel := cloud.ContextWithCallTimeout()
defer cancel()
mc := newInstanceGroupMetricContext("get", zone)
v, err := gce.c.InstanceGroups().Get(context.Background(), meta.ZonalKey(name, zone))
v, err := gce.c.InstanceGroups().Get(ctx, meta.ZonalKey(name, zone))
return v, mc.Observe(err)
}

View File

@ -21,7 +21,6 @@ import (
"fmt"
"net"
"net/http"
"strconv"
"strings"
"time"
@ -35,6 +34,7 @@ import (
"k8s.io/apimachinery/pkg/util/sets"
"k8s.io/apimachinery/pkg/util/wait"
"k8s.io/kubernetes/pkg/cloudprovider"
"k8s.io/kubernetes/pkg/cloudprovider/providers/gce/cloud"
"k8s.io/kubernetes/pkg/cloudprovider/providers/gce/cloud/filter"
"k8s.io/kubernetes/pkg/cloudprovider/providers/gce/cloud/meta"
kubeletapis "k8s.io/kubernetes/pkg/kubelet/apis"
@ -100,12 +100,15 @@ func (gce *GCECloud) NodeAddresses(_ context.Context, _ types.NodeName) ([]v1.No
// NodeAddressesByProviderID will not be called from the node that is requesting this ID.
// i.e. metadata service and other local methods cannot be used here
func (gce *GCECloud) NodeAddressesByProviderID(ctx context.Context, providerID string) ([]v1.NodeAddress, error) {
ctx, cancel := cloud.ContextWithCallTimeout()
defer cancel()
_, zone, name, err := splitProviderID(providerID)
if err != nil {
return []v1.NodeAddress{}, err
}
instance, err := gce.c.Instances().Get(context.Background(), meta.ZonalKey(canonicalizeInstanceName(name), zone))
instance, err := gce.c.Instances().Get(ctx, meta.ZonalKey(canonicalizeInstanceName(name), zone))
if err != nil {
return []v1.NodeAddress{}, fmt.Errorf("error while querying for providerID %q: %v", providerID, err)
}
@ -142,6 +145,11 @@ func (gce *GCECloud) instanceByProviderID(providerID string) (*gceInstance, erro
return instance, nil
}
// InstanceShutdownByProviderID returns true if the instance is in safe state to detach volumes
func (gce *GCECloud) InstanceShutdownByProviderID(ctx context.Context, providerID string) (bool, error) {
return false, cloudprovider.NotImplemented
}
// InstanceTypeByProviderID returns the cloudprovider instance type of the node
// with the specified unique providerID This method will not be called from the
// node that is requesting this ID. i.e. metadata service and other local
@ -155,27 +163,6 @@ func (gce *GCECloud) InstanceTypeByProviderID(ctx context.Context, providerID st
return instance.Type, nil
}
// ExternalID returns the cloud provider ID of the node with the specified NodeName (deprecated).
func (gce *GCECloud) ExternalID(ctx context.Context, nodeName types.NodeName) (string, error) {
instanceName := mapNodeNameToInstanceName(nodeName)
if gce.useMetadataServer {
// Use metadata, if possible, to fetch ID. See issue #12000
if gce.isCurrentInstance(instanceName) {
externalInstanceID, err := getCurrentExternalIDViaMetadata()
if err == nil {
return externalInstanceID, nil
}
}
}
// Fallback to GCE API call if metadata server fails to retrieve ID
inst, err := gce.getInstanceByName(instanceName)
if err != nil {
return "", err
}
return strconv.FormatUint(inst.ID, 10), nil
}
// InstanceExistsByProviderID returns true if the instance with the given provider id still exists and is running.
// If false is returned with no error, the instance will be immediately deleted by the cloud controller manager.
func (gce *GCECloud) InstanceExistsByProviderID(ctx context.Context, providerID string) (bool, error) {
@ -229,8 +216,11 @@ func (gce *GCECloud) InstanceType(ctx context.Context, nodeName types.NodeName)
}
func (gce *GCECloud) AddSSHKeyToAllInstances(ctx context.Context, user string, keyData []byte) error {
ctx, cancel := cloud.ContextWithCallTimeout()
defer cancel()
return wait.Poll(2*time.Second, 30*time.Second, func() (bool, error) {
project, err := gce.c.Projects().Get(context.Background(), gce.projectID)
project, err := gce.c.Projects().Get(ctx, gce.projectID)
if err != nil {
glog.Errorf("Could not get project: %v", err)
return false, nil
@ -261,7 +251,7 @@ func (gce *GCECloud) AddSSHKeyToAllInstances(ctx context.Context, user string, k
}
mc := newInstancesMetricContext("add_ssh_key", "")
err = gce.c.Projects().SetCommonInstanceMetadata(context.Background(), gce.projectID, project.CommonInstanceMetadata)
err = gce.c.Projects().SetCommonInstanceMetadata(ctx, gce.projectID, project.CommonInstanceMetadata)
mc.Observe(err)
if err != nil {
@ -301,9 +291,12 @@ func (gce *GCECloud) GetAllCurrentZones() (sets.String, error) {
//
// TODO: this should be removed from the cloud provider.
func (gce *GCECloud) GetAllZonesFromCloudProvider() (sets.String, error) {
ctx, cancel := cloud.ContextWithCallTimeout()
defer cancel()
zones := sets.NewString()
for _, zone := range gce.managedZones {
instances, err := gce.c.Instances().List(context.Background(), zone, filter.None)
instances, err := gce.c.Instances().List(ctx, zone, filter.None)
if err != nil {
return sets.NewString(), err
}
@ -316,15 +309,21 @@ func (gce *GCECloud) GetAllZonesFromCloudProvider() (sets.String, error) {
// InsertInstance creates a new instance on GCP
func (gce *GCECloud) InsertInstance(project string, zone string, i *compute.Instance) error {
ctx, cancel := cloud.ContextWithCallTimeout()
defer cancel()
mc := newInstancesMetricContext("create", zone)
return mc.Observe(gce.c.Instances().Insert(context.Background(), meta.ZonalKey(i.Name, zone), i))
return mc.Observe(gce.c.Instances().Insert(ctx, meta.ZonalKey(i.Name, zone), i))
}
// ListInstanceNames returns a string of instance names separated by spaces.
// This method should only be used for e2e testing.
// TODO: remove this method.
func (gce *GCECloud) ListInstanceNames(project, zone string) (string, error) {
l, err := gce.c.Instances().List(context.Background(), zone, filter.None)
ctx, cancel := cloud.ContextWithCallTimeout()
defer cancel()
l, err := gce.c.Instances().List(ctx, zone, filter.None)
if err != nil {
return "", err
}
@ -337,7 +336,10 @@ func (gce *GCECloud) ListInstanceNames(project, zone string) (string, error) {
// DeleteInstance deletes an instance specified by project, zone, and name
func (gce *GCECloud) DeleteInstance(project, zone, name string) error {
return gce.c.Instances().Delete(context.Background(), meta.ZonalKey(name, zone))
ctx, cancel := cloud.ContextWithCallTimeout()
defer cancel()
return gce.c.Instances().Delete(ctx, meta.ZonalKey(name, zone))
}
// Implementation of Instances.CurrentNodeName
@ -349,6 +351,9 @@ func (gce *GCECloud) CurrentNodeName(ctx context.Context, hostname string) (type
// `node` for allocation to pods. Returns a list of the form
// "<ip>/<netmask>".
func (gce *GCECloud) AliasRanges(nodeName types.NodeName) (cidrs []string, err error) {
ctx, cancel := cloud.ContextWithCallTimeout()
defer cancel()
var instance *gceInstance
instance, err = gce.getInstanceByName(mapNodeNameToInstanceName(nodeName))
if err != nil {
@ -356,7 +361,7 @@ func (gce *GCECloud) AliasRanges(nodeName types.NodeName) (cidrs []string, err e
}
var res *computebeta.Instance
res, err = gce.c.BetaInstances().Get(context.Background(), meta.ZonalKey(instance.Name, lastComponent(instance.Zone)))
res, err = gce.c.BetaInstances().Get(ctx, meta.ZonalKey(instance.Name, lastComponent(instance.Zone)))
if err != nil {
return
}
@ -372,12 +377,14 @@ func (gce *GCECloud) AliasRanges(nodeName types.NodeName) (cidrs []string, err e
// AddAliasToInstance adds an alias to the given instance from the named
// secondary range.
func (gce *GCECloud) AddAliasToInstance(nodeName types.NodeName, alias *net.IPNet) error {
ctx, cancel := cloud.ContextWithCallTimeout()
defer cancel()
v1instance, err := gce.getInstanceByName(mapNodeNameToInstanceName(nodeName))
if err != nil {
return err
}
instance, err := gce.c.BetaInstances().Get(context.Background(), meta.ZonalKey(v1instance.Name, lastComponent(v1instance.Zone)))
instance, err := gce.c.BetaInstances().Get(ctx, meta.ZonalKey(v1instance.Name, lastComponent(v1instance.Zone)))
if err != nil {
return err
}
@ -400,13 +407,16 @@ func (gce *GCECloud) AddAliasToInstance(nodeName types.NodeName, alias *net.IPNe
})
mc := newInstancesMetricContext("add_alias", v1instance.Zone)
err = gce.c.BetaInstances().UpdateNetworkInterface(context.Background(), meta.ZonalKey(instance.Name, lastComponent(instance.Zone)), iface.Name, iface)
err = gce.c.BetaInstances().UpdateNetworkInterface(ctx, meta.ZonalKey(instance.Name, lastComponent(instance.Zone)), iface.Name, iface)
return mc.Observe(err)
}
// Gets the named instances, returning cloudprovider.InstanceNotFound if any
// instance is not found
func (gce *GCECloud) getInstancesByNames(names []string) ([]*gceInstance, error) {
ctx, cancel := cloud.ContextWithCallTimeout()
defer cancel()
found := map[string]*gceInstance{}
remaining := len(names)
@ -424,7 +434,7 @@ func (gce *GCECloud) getInstancesByNames(names []string) ([]*gceInstance, error)
if remaining == 0 {
break
}
instances, err := gce.c.Instances().List(context.Background(), zone, filter.Regexp("name", nodeInstancePrefix+".*"))
instances, err := gce.c.Instances().List(ctx, zone, filter.Regexp("name", nodeInstancePrefix+".*"))
if err != nil {
return nil, err
}
@ -488,9 +498,12 @@ func (gce *GCECloud) getInstanceByName(name string) (*gceInstance, error) {
}
func (gce *GCECloud) getInstanceFromProjectInZoneByName(project, zone, name string) (*gceInstance, error) {
ctx, cancel := cloud.ContextWithCallTimeout()
defer cancel()
name = canonicalizeInstanceName(name)
mc := newInstancesMetricContext("get", zone)
res, err := gce.c.Instances().Get(context.Background(), meta.ZonalKey(name, zone))
res, err := gce.c.Instances().Get(ctx, meta.ZonalKey(name, zone))
mc.Observe(err)
if err != nil {
return nil, err
@ -516,14 +529,6 @@ func getInstanceIDViaMetadata() (string, error) {
return parts[0], nil
}
func getCurrentExternalIDViaMetadata() (string, error) {
externalID, err := metadata.Get("instance/id")
if err != nil {
return "", fmt.Errorf("couldn't get external ID: %v", err)
}
return externalID, nil
}
func getCurrentMachineTypeViaMetadata() (string, error) {
mType, err := metadata.Get("instance/machine-type")
if err != nil {
@ -557,6 +562,9 @@ func (gce *GCECloud) isCurrentInstance(instanceID string) bool {
// format of the host names in the cluster. Only use it as a fallback if
// gce.nodeTags is unspecified
func (gce *GCECloud) computeHostTags(hosts []*gceInstance) ([]string, error) {
ctx, cancel := cloud.ContextWithCallTimeout()
defer cancel()
// TODO: We could store the tags in gceInstance, so we could have already fetched it
hostNamesByZone := make(map[string]map[string]bool) // map of zones -> map of names -> bool (for easy lookup)
nodeInstancePrefix := gce.nodeInstancePrefix
@ -581,7 +589,7 @@ func (gce *GCECloud) computeHostTags(hosts []*gceInstance) ([]string, error) {
filt = filter.Regexp("name", nodeInstancePrefix+".*")
}
for zone, hostNames := range hostNamesByZone {
instances, err := gce.c.Instances().List(context.Background(), zone, filt)
instances, err := gce.c.Instances().List(ctx, zone, filt)
if err != nil {
return nil, err
}

File diff suppressed because it is too large Load Diff

View File

@ -563,6 +563,9 @@ func (gce *GCECloud) ensureInternalBackendServiceGroups(name string, igLinks []s
return nil
}
// Set the backend service's backends to the updated list.
bs.Backends = backends
glog.V(2).Infof("ensureInternalBackendServiceGroups: updating backend service %v", name)
if err := gce.UpdateRegionBackendService(bs, gce.region); err != nil {
return err
@ -575,8 +578,7 @@ func shareBackendService(svc *v1.Service) bool {
return GetLoadBalancerAnnotationBackendShare(svc) && !v1_service.RequestsOnlyLocalTraffic(svc)
}
func backendsFromGroupLinks(igLinks []string) []*compute.Backend {
var backends []*compute.Backend
func backendsFromGroupLinks(igLinks []string) (backends []*compute.Backend) {
for _, igLink := range igLinks {
backends = append(backends, &compute.Backend{
Group: igLink,

View File

@ -0,0 +1,739 @@
/*
Copyright 2017 The Kubernetes Authors.
Licensed under the Apache License, Version 2.0 (the "License");
you may not use this file except in compliance with the License.
You may obtain a copy of the License at
http://www.apache.org/licenses/LICENSE-2.0
Unless required by applicable law or agreed to in writing, software
distributed under the License is distributed on an "AS IS" BASIS,
WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
See the License for the specific language governing permissions and
limitations under the License.
*/
package gce
import (
"fmt"
"strings"
"testing"
"github.com/stretchr/testify/assert"
"github.com/stretchr/testify/require"
compute "google.golang.org/api/compute/v1"
"k8s.io/api/core/v1"
"k8s.io/apimachinery/pkg/types"
"k8s.io/client-go/tools/record"
v1_service "k8s.io/kubernetes/pkg/api/v1/service"
"k8s.io/kubernetes/pkg/cloudprovider"
"k8s.io/kubernetes/pkg/cloudprovider/providers/gce/cloud"
"k8s.io/kubernetes/pkg/cloudprovider/providers/gce/cloud/mock"
)
func createInternalLoadBalancer(gce *GCECloud, svc *v1.Service, existingFwdRule *compute.ForwardingRule, nodeNames []string, clusterName, clusterID, zoneName string) (*v1.LoadBalancerStatus, error) {
nodes, err := createAndInsertNodes(gce, nodeNames, zoneName)
if err != nil {
return nil, err
}
return gce.ensureInternalLoadBalancer(
clusterName,
clusterID,
svc,
existingFwdRule,
nodes,
)
}
func TestEnsureInternalBackendServiceUpdates(t *testing.T) {
t.Parallel()
vals := DefaultTestClusterValues()
nodeNames := []string{"test-node-1"}
gce, err := fakeGCECloud(vals)
require.NoError(t, err)
svc := fakeLoadbalancerService(string(LBTypeInternal))
lbName := cloudprovider.GetLoadBalancerName(svc)
nodes, err := createAndInsertNodes(gce, nodeNames, vals.ZoneName)
igName := makeInstanceGroupName(vals.ClusterID)
igLinks, err := gce.ensureInternalInstanceGroups(igName, nodes)
require.NoError(t, err)
sharedBackend := shareBackendService(svc)
bsName := makeBackendServiceName(lbName, vals.ClusterID, sharedBackend, cloud.SchemeInternal, "TCP", svc.Spec.SessionAffinity)
err = gce.ensureInternalBackendService(bsName, "description", svc.Spec.SessionAffinity, cloud.SchemeInternal, "TCP", igLinks, "")
require.NoError(t, err)
// Update the Internal Backend Service with a new ServiceAffinity
err = gce.ensureInternalBackendService(bsName, "description", v1.ServiceAffinityNone, cloud.SchemeInternal, "TCP", igLinks, "")
require.NoError(t, err)
bs, err := gce.GetRegionBackendService(bsName, gce.region)
assert.NoError(t, err)
assert.Equal(t, bs.SessionAffinity, strings.ToUpper(string(v1.ServiceAffinityNone)))
}
func TestEnsureInternalBackendServiceGroups(t *testing.T) {
t.Parallel()
for desc, tc := range map[string]struct {
mockModifier func(*cloud.MockGCE)
}{
"Basic workflow": {},
"GetRegionBackendService failed": {
mockModifier: func(c *cloud.MockGCE) {
c.MockRegionBackendServices.GetHook = mock.GetRegionBackendServicesErrHook
},
},
"UpdateRegionBackendServices failed": {
mockModifier: func(c *cloud.MockGCE) {
c.MockRegionBackendServices.UpdateHook = mock.UpdateRegionBackendServicesErrHook
},
},
} {
t.Run(desc, func(t *testing.T) {
vals := DefaultTestClusterValues()
nodeNames := []string{"test-node-1"}
gce, err := fakeGCECloud(vals)
require.NoError(t, err)
svc := fakeLoadbalancerService(string(LBTypeInternal))
lbName := cloudprovider.GetLoadBalancerName(svc)
nodes, err := createAndInsertNodes(gce, nodeNames, vals.ZoneName)
igName := makeInstanceGroupName(vals.ClusterID)
igLinks, err := gce.ensureInternalInstanceGroups(igName, nodes)
require.NoError(t, err)
sharedBackend := shareBackendService(svc)
bsName := makeBackendServiceName(lbName, vals.ClusterID, sharedBackend, cloud.SchemeInternal, "TCP", svc.Spec.SessionAffinity)
err = gce.ensureInternalBackendService(bsName, "description", svc.Spec.SessionAffinity, cloud.SchemeInternal, "TCP", igLinks, "")
require.NoError(t, err)
// Update the BackendService with new InstanceGroups
if tc.mockModifier != nil {
tc.mockModifier(gce.c.(*cloud.MockGCE))
}
newIGLinks := []string{"new-test-ig-1", "new-test-ig-2"}
err = gce.ensureInternalBackendServiceGroups(bsName, newIGLinks)
if tc.mockModifier != nil {
assert.Error(t, err)
return
}
assert.NoError(t, err)
bs, err := gce.GetRegionBackendService(bsName, gce.region)
assert.NoError(t, err)
// Check that the Backends reflect the new InstanceGroups
backends := backendsFromGroupLinks(newIGLinks)
assert.Equal(t, bs.Backends, backends)
})
}
}
func TestEnsureInternalLoadBalancer(t *testing.T) {
t.Parallel()
vals := DefaultTestClusterValues()
nodeNames := []string{"test-node-1"}
gce, err := fakeGCECloud(vals)
require.NoError(t, err)
svc := fakeLoadbalancerService(string(LBTypeInternal))
status, err := createInternalLoadBalancer(gce, svc, nil, nodeNames, vals.ClusterName, vals.ClusterID, vals.ZoneName)
assert.NoError(t, err)
assert.NotEmpty(t, status.Ingress)
assertInternalLbResources(t, gce, svc, vals, nodeNames)
}
func TestEnsureInternalLoadBalancerWithExistingResources(t *testing.T) {
t.Parallel()
vals := DefaultTestClusterValues()
nodeNames := []string{"test-node-1"}
gce, err := fakeGCECloud(vals)
require.NoError(t, err)
svc := fakeLoadbalancerService(string(LBTypeInternal))
// Create the expected resources necessary for an Internal Load Balancer
nm := types.NamespacedName{Name: svc.Name, Namespace: svc.Namespace}
lbName := cloudprovider.GetLoadBalancerName(svc)
sharedHealthCheck := !v1_service.RequestsOnlyLocalTraffic(svc)
hcName := makeHealthCheckName(lbName, vals.ClusterID, sharedHealthCheck)
hcPath, hcPort := GetNodesHealthCheckPath(), GetNodesHealthCheckPort()
existingHC := newInternalLBHealthCheck(hcName, nm, sharedHealthCheck, hcPath, hcPort)
err = gce.CreateHealthCheck(existingHC)
require.NoError(t, err)
nodes, err := createAndInsertNodes(gce, nodeNames, vals.ZoneName)
igName := makeInstanceGroupName(vals.ClusterID)
igLinks, err := gce.ensureInternalInstanceGroups(igName, nodes)
require.NoError(t, err)
sharedBackend := shareBackendService(svc)
bsDescription := makeBackendServiceDescription(nm, sharedBackend)
bsName := makeBackendServiceName(lbName, vals.ClusterID, sharedBackend, cloud.SchemeInternal, "TCP", svc.Spec.SessionAffinity)
err = gce.ensureInternalBackendService(bsName, bsDescription, svc.Spec.SessionAffinity, cloud.SchemeInternal, "TCP", igLinks, existingHC.SelfLink)
require.NoError(t, err)
_, err = createInternalLoadBalancer(gce, svc, nil, nodeNames, vals.ClusterName, vals.ClusterID, vals.ZoneName)
assert.NoError(t, err)
}
func TestEnsureInternalLoadBalancerClearPreviousResources(t *testing.T) {
t.Parallel()
vals := DefaultTestClusterValues()
gce, err := fakeGCECloud(vals)
require.NoError(t, err)
svc := fakeLoadbalancerService(string(LBTypeInternal))
lbName := cloudprovider.GetLoadBalancerName(svc)
// Create a ForwardingRule that's missing an IP address
existingFwdRule := &compute.ForwardingRule{
Name: lbName,
IPAddress: "",
Ports: []string{"123"},
IPProtocol: "TCP",
LoadBalancingScheme: string(cloud.SchemeInternal),
}
gce.CreateRegionForwardingRule(existingFwdRule, gce.region)
// Create a Firewall that's missing a Description
existingFirewall := &compute.Firewall{
Name: lbName,
Network: gce.networkURL,
Allowed: []*compute.FirewallAllowed{
{
IPProtocol: "tcp",
Ports: []string{"123"},
},
},
}
gce.CreateFirewall(existingFirewall)
sharedHealthCheck := !v1_service.RequestsOnlyLocalTraffic(svc)
hcName := makeHealthCheckName(lbName, vals.ClusterID, sharedHealthCheck)
hcPath, hcPort := GetNodesHealthCheckPath(), GetNodesHealthCheckPort()
nm := types.NamespacedName{Name: svc.Name, Namespace: svc.Namespace}
// Create a healthcheck with an incorrect threshold
existingHC := newInternalLBHealthCheck(hcName, nm, sharedHealthCheck, hcPath, hcPort)
existingHC.HealthyThreshold = gceHcHealthyThreshold * 10
gce.CreateHealthCheck(existingHC)
// Create a backend Service that's missing Description and Backends
sharedBackend := shareBackendService(svc)
backendServiceName := makeBackendServiceName(lbName, vals.ClusterID, sharedBackend, cloud.SchemeInternal, "TCP", svc.Spec.SessionAffinity)
existingBS := &compute.BackendService{
Name: lbName,
Protocol: "TCP",
HealthChecks: []string{existingHC.SelfLink},
SessionAffinity: translateAffinityType(svc.Spec.SessionAffinity),
LoadBalancingScheme: string(cloud.SchemeInternal),
}
gce.CreateRegionBackendService(existingBS, gce.region)
existingFwdRule.BackendService = existingBS.Name
_, err = createInternalLoadBalancer(gce, svc, existingFwdRule, []string{"test-node-1"}, vals.ClusterName, vals.ClusterID, vals.ZoneName)
assert.NoError(t, err)
// Expect new resources with the correct attributes to be created
rule, _ := gce.GetRegionForwardingRule(lbName, gce.region)
assert.NotEqual(t, existingFwdRule, rule)
firewall, err := gce.GetFirewall(lbName)
require.NoError(t, err)
assert.NotEqual(t, firewall, existingFirewall)
healthcheck, err := gce.GetHealthCheck(hcName)
require.NoError(t, err)
assert.NotEqual(t, healthcheck, existingHC)
bs, err := gce.GetRegionBackendService(backendServiceName, gce.region)
require.NoError(t, err)
assert.NotEqual(t, bs, existingBS)
}
func TestUpdateInternalLoadBalancerBackendServices(t *testing.T) {
t.Parallel()
vals := DefaultTestClusterValues()
nodeName := "test-node-1"
gce, err := fakeGCECloud(vals)
require.NoError(t, err)
svc := fakeLoadbalancerService(string(LBTypeInternal))
_, err = createInternalLoadBalancer(gce, svc, nil, []string{"test-node-1"}, vals.ClusterName, vals.ClusterID, vals.ZoneName)
assert.NoError(t, err)
// BackendService exists prior to updateInternalLoadBalancer call, but has
// incorrect (missing) attributes.
// ensureInternalBackendServiceGroups is called and creates the correct
// BackendService
lbName := cloudprovider.GetLoadBalancerName(svc)
sharedBackend := shareBackendService(svc)
backendServiceName := makeBackendServiceName(lbName, vals.ClusterID, sharedBackend, cloud.SchemeInternal, "TCP", svc.Spec.SessionAffinity)
existingBS := &compute.BackendService{
Name: backendServiceName,
Protocol: "TCP",
SessionAffinity: translateAffinityType(svc.Spec.SessionAffinity),
LoadBalancingScheme: string(cloud.SchemeInternal),
}
gce.CreateRegionBackendService(existingBS, gce.region)
nodes, err := createAndInsertNodes(gce, []string{nodeName}, vals.ZoneName)
require.NoError(t, err)
err = gce.updateInternalLoadBalancer(vals.ClusterName, vals.ClusterID, svc, nodes)
assert.NoError(t, err)
bs, err := gce.GetRegionBackendService(backendServiceName, gce.region)
require.NoError(t, err)
// Check that the new BackendService has the correct attributes
url_base := fmt.Sprintf("https://www.googleapis.com/compute/v1/projects/%s", vals.ProjectID)
assert.NotEqual(t, existingBS, bs)
assert.Equal(
t,
bs.SelfLink,
fmt.Sprintf("%s/regions/%s/backendServices/%s", url_base, vals.Region, bs.Name),
)
assert.Equal(t, bs.Description, `{"kubernetes.io/service-name":"/"}`)
assert.Equal(
t,
bs.HealthChecks,
[]string{fmt.Sprintf("%s/global/healthChecks/k8s-%s-node", url_base, vals.ClusterID)},
)
}
func TestUpdateInternalLoadBalancerNodes(t *testing.T) {
t.Parallel()
vals := DefaultTestClusterValues()
gce, err := fakeGCECloud(vals)
require.NoError(t, err)
node1Name := []string{"test-node-1"}
svc := fakeLoadbalancerService(string(LBTypeInternal))
nodes, err := createAndInsertNodes(gce, node1Name, vals.ZoneName)
require.NoError(t, err)
_, err = gce.ensureInternalLoadBalancer(vals.ClusterName, vals.ClusterID, svc, nil, nodes)
assert.NoError(t, err)
// Replace the node in initial zone; add new node in a new zone.
node2Name, node3Name := "test-node-2", "test-node-3"
newNodesZoneA, err := createAndInsertNodes(gce, []string{node2Name}, vals.ZoneName)
require.NoError(t, err)
newNodesZoneB, err := createAndInsertNodes(gce, []string{node3Name}, vals.SecondaryZoneName)
require.NoError(t, err)
nodes = append(newNodesZoneA, newNodesZoneB...)
err = gce.updateInternalLoadBalancer(vals.ClusterName, vals.ClusterID, svc, nodes)
assert.NoError(t, err)
lbName := cloudprovider.GetLoadBalancerName(svc)
sharedBackend := shareBackendService(svc)
backendServiceName := makeBackendServiceName(lbName, vals.ClusterID, sharedBackend, cloud.SchemeInternal, "TCP", svc.Spec.SessionAffinity)
bs, err := gce.GetRegionBackendService(backendServiceName, gce.region)
require.NoError(t, err)
assert.Equal(t, 2, len(bs.Backends), "Want two backends referencing two instances groups")
for _, zone := range []string{vals.ZoneName, vals.SecondaryZoneName} {
var found bool
for _, be := range bs.Backends {
if strings.Contains(be.Group, zone) {
found = true
break
}
}
assert.True(t, found, "Expected list of backends to have zone %q", zone)
}
// Expect initial zone to have test-node-2
igName := makeInstanceGroupName(vals.ClusterID)
instances, err := gce.ListInstancesInInstanceGroup(igName, vals.ZoneName, "ALL")
require.NoError(t, err)
assert.Equal(t, 1, len(instances))
assert.Contains(
t,
instances[0].Instance,
fmt.Sprintf("projects/%s/zones/%s/instances/%s", vals.ProjectID, vals.ZoneName, node2Name),
)
// Expect initial zone to have test-node-3
instances, err = gce.ListInstancesInInstanceGroup(igName, vals.SecondaryZoneName, "ALL")
require.NoError(t, err)
assert.Equal(t, 1, len(instances))
assert.Contains(
t,
instances[0].Instance,
fmt.Sprintf("projects/%s/zones/%s/instances/%s", vals.ProjectID, vals.SecondaryZoneName, node3Name),
)
}
func TestEnsureInternalLoadBalancerDeleted(t *testing.T) {
t.Parallel()
vals := DefaultTestClusterValues()
gce, err := fakeGCECloud(vals)
require.NoError(t, err)
svc := fakeLoadbalancerService(string(LBTypeInternal))
_, err = createInternalLoadBalancer(gce, svc, nil, []string{"test-node-1"}, vals.ClusterName, vals.ClusterID, vals.ZoneName)
assert.NoError(t, err)
err = gce.ensureInternalLoadBalancerDeleted(vals.ClusterName, vals.ClusterID, svc)
assert.NoError(t, err)
assertInternalLbResourcesDeleted(t, gce, svc, vals, true)
}
func TestEnsureInternalLoadBalancerDeletedTwiceDoesNotError(t *testing.T) {
t.Parallel()
vals := DefaultTestClusterValues()
gce, err := fakeGCECloud(vals)
require.NoError(t, err)
svc := fakeLoadbalancerService(string(LBTypeInternal))
_, err = createInternalLoadBalancer(gce, svc, nil, []string{"test-node-1"}, vals.ClusterName, vals.ClusterID, vals.ZoneName)
assert.NoError(t, err)
err = gce.ensureInternalLoadBalancerDeleted(vals.ClusterName, vals.ClusterID, svc)
assert.NoError(t, err)
// Deleting the loadbalancer and resources again should not cause an error.
err = gce.ensureInternalLoadBalancerDeleted(vals.ClusterName, vals.ClusterID, svc)
assert.NoError(t, err)
assertInternalLbResourcesDeleted(t, gce, svc, vals, true)
}
func TestEnsureInternalLoadBalancerWithSpecialHealthCheck(t *testing.T) {
vals := DefaultTestClusterValues()
nodeName := "test-node-1"
gce, err := fakeGCECloud(vals)
require.NoError(t, err)
healthCheckNodePort := int32(10101)
svc := fakeLoadbalancerService(string(LBTypeInternal))
svc.Spec.HealthCheckNodePort = healthCheckNodePort
svc.Spec.Type = v1.ServiceTypeLoadBalancer
svc.Spec.ExternalTrafficPolicy = v1.ServiceExternalTrafficPolicyTypeLocal
status, err := createInternalLoadBalancer(gce, svc, nil, []string{nodeName}, vals.ClusterName, vals.ClusterID, vals.ZoneName)
assert.NoError(t, err)
assert.NotEmpty(t, status.Ingress)
loadBalancerName := cloudprovider.GetLoadBalancerName(svc)
hc, err := gce.GetHealthCheck(loadBalancerName)
assert.NoError(t, err)
assert.NotNil(t, hc)
assert.Equal(t, int64(healthCheckNodePort), hc.HttpHealthCheck.Port)
}
func TestClearPreviousInternalResources(t *testing.T) {
// Configure testing environment.
vals := DefaultTestClusterValues()
svc := fakeLoadbalancerService(string(LBTypeInternal))
loadBalancerName := cloudprovider.GetLoadBalancerName(svc)
nm := types.NamespacedName{Name: svc.Name, Namespace: svc.Namespace}
gce, err := fakeGCECloud(vals)
c := gce.c.(*cloud.MockGCE)
require.NoError(t, err)
hc_1, err := gce.ensureInternalHealthCheck("hc_1", nm, false, "healthz", 12345)
require.NoError(t, err)
hc_2, err := gce.ensureInternalHealthCheck("hc_2", nm, false, "healthz", 12346)
require.NoError(t, err)
err = gce.ensureInternalBackendService(svc.ObjectMeta.Name, "", svc.Spec.SessionAffinity, cloud.SchemeInternal, v1.ProtocolTCP, []string{}, "")
require.NoError(t, err)
backendSvc, err := gce.GetRegionBackendService(svc.ObjectMeta.Name, gce.region)
backendSvc.HealthChecks = []string{hc_1.SelfLink, hc_2.SelfLink}
c.MockRegionBackendServices.DeleteHook = mock.DeleteRegionBackendServicesErrHook
c.MockHealthChecks.DeleteHook = mock.DeleteHealthChecksInternalErrHook
gce.clearPreviousInternalResources(svc, loadBalancerName, backendSvc, "expectedBSName", "expectedHCName")
backendSvc, err = gce.GetRegionBackendService(svc.ObjectMeta.Name, gce.region)
assert.NoError(t, err)
assert.NotNil(t, backendSvc, "BackendService should not be deleted when api is mocked out.")
hc_1, err = gce.GetHealthCheck("hc_1")
assert.NoError(t, err)
assert.NotNil(t, hc_1, "HealthCheck should not be deleted when there are more than one healthcheck attached.")
hc_2, err = gce.GetHealthCheck("hc_2")
assert.NoError(t, err)
assert.NotNil(t, hc_2, "HealthCheck should not be deleted when there are more than one healthcheck attached.")
c.MockRegionBackendServices.DeleteHook = mock.DeleteRegionBackendServicesInUseErrHook
backendSvc.HealthChecks = []string{hc_1.SelfLink}
gce.clearPreviousInternalResources(svc, loadBalancerName, backendSvc, "expectedBSName", "expectedHCName")
hc_1, err = gce.GetHealthCheck("hc_1")
assert.NoError(t, err)
assert.NotNil(t, hc_1, "HealthCheck should not be deleted when api is mocked out.")
c.MockHealthChecks.DeleteHook = mock.DeleteHealthChecksInuseErrHook
gce.clearPreviousInternalResources(svc, loadBalancerName, backendSvc, "expectedBSName", "expectedHCName")
hc_1, err = gce.GetHealthCheck("hc_1")
assert.NoError(t, err)
assert.NotNil(t, hc_1, "HealthCheck should not be deleted when api is mocked out.")
c.MockRegionBackendServices.DeleteHook = nil
c.MockHealthChecks.DeleteHook = nil
gce.clearPreviousInternalResources(svc, loadBalancerName, backendSvc, "expectedBSName", "expectedHCName")
backendSvc, err = gce.GetRegionBackendService(svc.ObjectMeta.Name, gce.region)
assert.Error(t, err)
assert.Nil(t, backendSvc, "BackendService should be deleted.")
hc_1, err = gce.GetHealthCheck("hc_1")
assert.Error(t, err)
assert.Nil(t, hc_1, "HealthCheck should be deleted.")
}
func TestEnsureInternalFirewallSucceedsOnXPN(t *testing.T) {
gce, err := fakeGCECloud(DefaultTestClusterValues())
require.NoError(t, err)
vals := DefaultTestClusterValues()
svc := fakeLoadbalancerService(string(LBTypeInternal))
fwName := cloudprovider.GetLoadBalancerName(svc)
c := gce.c.(*cloud.MockGCE)
c.MockFirewalls.InsertHook = mock.InsertFirewallsUnauthorizedErrHook
c.MockFirewalls.UpdateHook = mock.UpdateFirewallsUnauthorizedErrHook
gce.onXPN = true
require.True(t, gce.OnXPN())
recorder := record.NewFakeRecorder(1024)
gce.eventRecorder = recorder
nodes, err := createAndInsertNodes(gce, []string{"test-node-1"}, vals.ZoneName)
require.NoError(t, err)
sourceRange := []string{"10.0.0.0/20"}
gce.ensureInternalFirewall(
svc,
fwName,
"A sad little firewall",
sourceRange,
[]string{"123"},
v1.ProtocolTCP,
nodes)
require.Nil(t, err, "Should success when XPN is on.")
checkEvent(t, recorder, FilewallChangeMsg, true)
// Create a firewall.
c.MockFirewalls.InsertHook = nil
c.MockFirewalls.UpdateHook = nil
gce.onXPN = false
gce.ensureInternalFirewall(
svc,
fwName,
"A sad little firewall",
sourceRange,
[]string{"123"},
v1.ProtocolTCP,
nodes)
require.Nil(t, err)
existingFirewall, err := gce.GetFirewall(fwName)
require.Nil(t, err)
require.NotNil(t, existingFirewall)
gce.onXPN = true
c.MockFirewalls.InsertHook = mock.InsertFirewallsUnauthorizedErrHook
c.MockFirewalls.UpdateHook = mock.UpdateFirewallsUnauthorizedErrHook
// Try to update the firewall just created.
gce.ensureInternalFirewall(
svc,
fwName,
"A happy little firewall",
sourceRange,
[]string{"123"},
v1.ProtocolTCP,
nodes)
require.Nil(t, err, "Should success when XPN is on.")
checkEvent(t, recorder, FilewallChangeMsg, true)
}
func TestEnsureLoadBalancerDeletedSucceedsOnXPN(t *testing.T) {
vals := DefaultTestClusterValues()
gce, err := fakeGCECloud(vals)
c := gce.c.(*cloud.MockGCE)
recorder := record.NewFakeRecorder(1024)
gce.eventRecorder = recorder
require.NoError(t, err)
_, err = createInternalLoadBalancer(gce, fakeLoadbalancerService(string(LBTypeInternal)), nil, []string{"test-node-1"}, vals.ClusterName, vals.ClusterID, vals.ZoneName)
assert.NoError(t, err)
c.MockFirewalls.DeleteHook = mock.DeleteFirewallsUnauthorizedErrHook
gce.onXPN = true
err = gce.ensureInternalLoadBalancerDeleted(vals.ClusterName, vals.ClusterID, fakeLoadbalancerService(string(LBTypeInternal)))
assert.NoError(t, err)
checkEvent(t, recorder, FilewallChangeMsg, true)
}
func TestEnsureInternalInstanceGroupsDeleted(t *testing.T) {
vals := DefaultTestClusterValues()
gce, err := fakeGCECloud(vals)
c := gce.c.(*cloud.MockGCE)
recorder := record.NewFakeRecorder(1024)
gce.eventRecorder = recorder
require.NoError(t, err)
igName := makeInstanceGroupName(vals.ClusterID)
svc := fakeLoadbalancerService(string(LBTypeInternal))
_, err = createInternalLoadBalancer(gce, svc, nil, []string{"test-node-1"}, vals.ClusterName, vals.ClusterID, vals.ZoneName)
assert.NoError(t, err)
c.MockZones.ListHook = mock.ListZonesInternalErrHook
err = gce.ensureInternalLoadBalancerDeleted(igName, vals.ClusterID, svc)
assert.Error(t, err, mock.InternalServerError)
ig, err := gce.GetInstanceGroup(igName, vals.ZoneName)
assert.NoError(t, err)
assert.NotNil(t, ig)
c.MockZones.ListHook = nil
c.MockInstanceGroups.DeleteHook = mock.DeleteInstanceGroupInternalErrHook
err = gce.ensureInternalInstanceGroupsDeleted(igName)
assert.Error(t, err, mock.InternalServerError)
ig, err = gce.GetInstanceGroup(igName, vals.ZoneName)
assert.NoError(t, err)
assert.NotNil(t, ig)
c.MockInstanceGroups.DeleteHook = nil
err = gce.ensureInternalInstanceGroupsDeleted(igName)
assert.NoError(t, err)
ig, err = gce.GetInstanceGroup(igName, vals.ZoneName)
assert.Error(t, err)
assert.Nil(t, ig)
}
type EnsureILBParams struct {
clusterName string
clusterID string
service *v1.Service
existingFwdRule *compute.ForwardingRule
nodes []*v1.Node
}
// newEnsureILBParams is the constructor of EnsureILBParams.
func newEnsureILBParams(nodes []*v1.Node) *EnsureILBParams {
vals := DefaultTestClusterValues()
return &EnsureILBParams{
vals.ClusterName,
vals.ClusterID,
fakeLoadbalancerService(string(LBTypeInternal)),
nil,
nodes,
}
}
// TestEnsureInternalLoadBalancerErrors tests the function
// ensureInternalLoadBalancer, making sure the system won't panic when
// exceptions raised by gce.
func TestEnsureInternalLoadBalancerErrors(t *testing.T) {
vals := DefaultTestClusterValues()
var params *EnsureILBParams
for desc, tc := range map[string]struct {
adjustParams func(*EnsureILBParams)
injectMock func(*cloud.MockGCE)
}{
"Create internal instance groups failed": {
injectMock: func(c *cloud.MockGCE) {
c.MockInstanceGroups.GetHook = mock.GetInstanceGroupInternalErrHook
},
},
"Invalid existing forwarding rules given": {
adjustParams: func(params *EnsureILBParams) {
params.existingFwdRule = &compute.ForwardingRule{BackendService: "badBackendService"}
},
injectMock: func(c *cloud.MockGCE) {
c.MockRegionBackendServices.GetHook = mock.GetRegionBackendServicesErrHook
},
},
"EnsureInternalBackendService failed": {
injectMock: func(c *cloud.MockGCE) {
c.MockRegionBackendServices.GetHook = mock.GetRegionBackendServicesErrHook
},
},
"Create internal health check failed": {
injectMock: func(c *cloud.MockGCE) {
c.MockHealthChecks.GetHook = mock.GetHealthChecksInternalErrHook
},
},
"Create firewall failed": {
injectMock: func(c *cloud.MockGCE) {
c.MockFirewalls.InsertHook = mock.InsertFirewallsUnauthorizedErrHook
},
},
"Create region forwarding rule failed": {
injectMock: func(c *cloud.MockGCE) {
c.MockForwardingRules.InsertHook = mock.InsertForwardingRulesInternalErrHook
},
},
"Get region forwarding rule failed": {
injectMock: func(c *cloud.MockGCE) {
c.MockForwardingRules.GetHook = mock.GetForwardingRulesInternalErrHook
},
},
"Delete region forwarding rule failed": {
adjustParams: func(params *EnsureILBParams) {
params.existingFwdRule = &compute.ForwardingRule{BackendService: "badBackendService"}
},
injectMock: func(c *cloud.MockGCE) {
c.MockForwardingRules.DeleteHook = mock.DeleteForwardingRuleErrHook
},
},
} {
t.Run(desc, func(t *testing.T) {
gce, err := fakeGCECloud(DefaultTestClusterValues())
nodes, err := createAndInsertNodes(gce, []string{"test-node-1"}, vals.ZoneName)
require.NoError(t, err)
params = newEnsureILBParams(nodes)
if tc.adjustParams != nil {
tc.adjustParams(params)
}
if tc.injectMock != nil {
tc.injectMock(gce.c.(*cloud.MockGCE))
}
status, err := gce.ensureInternalLoadBalancer(
params.clusterName,
params.clusterID,
params.service,
params.existingFwdRule,
params.nodes,
)
assert.Error(t, err, "Should return an error when "+desc)
assert.Nil(t, status, "Should not return a status when "+desc)
})
}
}

View File

@ -0,0 +1,171 @@
/*
Copyright 2017 The Kubernetes Authors.
Licensed under the Apache License, Version 2.0 (the "License");
you may not use this file except in compliance with the License.
You may obtain a copy of the License at
http://www.apache.org/licenses/LICENSE-2.0
Unless required by applicable law or agreed to in writing, software
distributed under the License is distributed on an "AS IS" BASIS,
WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
See the License for the specific language governing permissions and
limitations under the License.
*/
package gce
import (
"context"
"testing"
"github.com/stretchr/testify/assert"
"github.com/stretchr/testify/require"
)
func TestGetLoadBalancer(t *testing.T) {
t.Parallel()
vals := DefaultTestClusterValues()
gce, err := fakeGCECloud(vals)
require.NoError(t, err)
apiService := fakeLoadbalancerService("")
// When a loadbalancer has not been created
status, found, err := gce.GetLoadBalancer(context.Background(), vals.ClusterName, apiService)
assert.Nil(t, status)
assert.False(t, found)
assert.Nil(t, err)
nodeNames := []string{"test-node-1"}
nodes, err := createAndInsertNodes(gce, nodeNames, vals.ZoneName)
require.NoError(t, err)
expectedStatus, err := gce.EnsureLoadBalancer(context.Background(), vals.ClusterName, apiService, nodes)
require.NoError(t, err)
status, found, err = gce.GetLoadBalancer(context.Background(), vals.ClusterName, apiService)
assert.Equal(t, expectedStatus, status)
assert.True(t, found)
assert.Nil(t, err)
}
func TestEnsureLoadBalancerCreatesExternalLb(t *testing.T) {
t.Parallel()
vals := DefaultTestClusterValues()
gce, err := fakeGCECloud(vals)
require.NoError(t, err)
nodeNames := []string{"test-node-1"}
nodes, err := createAndInsertNodes(gce, nodeNames, vals.ZoneName)
require.NoError(t, err)
apiService := fakeLoadbalancerService("")
status, err := gce.EnsureLoadBalancer(context.Background(), vals.ClusterName, apiService, nodes)
assert.NoError(t, err)
assert.NotEmpty(t, status.Ingress)
assertExternalLbResources(t, gce, apiService, vals, nodeNames)
}
func TestEnsureLoadBalancerCreatesInternalLb(t *testing.T) {
t.Parallel()
vals := DefaultTestClusterValues()
gce, err := fakeGCECloud(vals)
require.NoError(t, err)
nodeNames := []string{"test-node-1"}
nodes, err := createAndInsertNodes(gce, nodeNames, vals.ZoneName)
require.NoError(t, err)
apiService := fakeLoadbalancerService(string(LBTypeInternal))
status, err := gce.EnsureLoadBalancer(context.Background(), vals.ClusterName, apiService, nodes)
assert.NoError(t, err)
assert.NotEmpty(t, status.Ingress)
assertInternalLbResources(t, gce, apiService, vals, nodeNames)
}
func TestEnsureLoadBalancerDeletesExistingInternalLb(t *testing.T) {
t.Parallel()
vals := DefaultTestClusterValues()
gce, err := fakeGCECloud(vals)
require.NoError(t, err)
nodeNames := []string{"test-node-1"}
nodes, err := createAndInsertNodes(gce, nodeNames, vals.ZoneName)
require.NoError(t, err)
apiService := fakeLoadbalancerService("")
createInternalLoadBalancer(gce, apiService, nil, nodeNames, vals.ClusterName, vals.ClusterID, vals.ZoneName)
status, err := gce.EnsureLoadBalancer(context.Background(), vals.ClusterName, apiService, nodes)
assert.NoError(t, err)
assert.NotEmpty(t, status.Ingress)
assertExternalLbResources(t, gce, apiService, vals, nodeNames)
assertInternalLbResourcesDeleted(t, gce, apiService, vals, false)
}
func TestEnsureLoadBalancerDeletesExistingExternalLb(t *testing.T) {
t.Parallel()
vals := DefaultTestClusterValues()
gce, err := fakeGCECloud(vals)
require.NoError(t, err)
nodeNames := []string{"test-node-1"}
nodes, err := createAndInsertNodes(gce, nodeNames, vals.ZoneName)
require.NoError(t, err)
apiService := fakeLoadbalancerService("")
createExternalLoadBalancer(gce, apiService, nodeNames, vals.ClusterName, vals.ClusterID, vals.ZoneName)
apiService = fakeLoadbalancerService(string(LBTypeInternal))
status, err := gce.EnsureLoadBalancer(context.Background(), vals.ClusterName, apiService, nodes)
assert.NoError(t, err)
assert.NotEmpty(t, status.Ingress)
assertInternalLbResources(t, gce, apiService, vals, nodeNames)
assertExternalLbResourcesDeleted(t, gce, apiService, vals, false)
}
func TestEnsureLoadBalancerDeletedDeletesExternalLb(t *testing.T) {
t.Parallel()
vals := DefaultTestClusterValues()
gce, err := fakeGCECloud(vals)
require.NoError(t, err)
nodeNames := []string{"test-node-1"}
_, err = createAndInsertNodes(gce, nodeNames, vals.ZoneName)
require.NoError(t, err)
apiService := fakeLoadbalancerService("")
createExternalLoadBalancer(gce, apiService, nodeNames, vals.ClusterName, vals.ClusterID, vals.ZoneName)
err = gce.EnsureLoadBalancerDeleted(context.Background(), vals.ClusterName, apiService)
assert.NoError(t, err)
assertExternalLbResourcesDeleted(t, gce, apiService, vals, true)
}
func TestEnsureLoadBalancerDeletedDeletesInternalLb(t *testing.T) {
t.Parallel()
vals := DefaultTestClusterValues()
gce, err := fakeGCECloud(vals)
require.NoError(t, err)
nodeNames := []string{"test-node-1"}
_, err = createAndInsertNodes(gce, nodeNames, vals.ZoneName)
require.NoError(t, err)
apiService := fakeLoadbalancerService(string(LBTypeInternal))
createInternalLoadBalancer(gce, apiService, nil, nodeNames, vals.ClusterName, vals.ClusterID, vals.ZoneName)
err = gce.EnsureLoadBalancerDeleted(context.Background(), vals.ClusterName, apiService)
assert.NoError(t, err)
assertInternalLbResourcesDeleted(t, gce, apiService, vals, true)
}

View File

@ -0,0 +1,403 @@
/*
Copyright 2017 The Kubernetes Authors.
Licensed under the Apache License, Version 2.0 (the "License");
you may not use this file except in compliance with the License.
You may obtain a copy of the License at
http://www.apache.org/licenses/LICENSE-2.0
Unless required by applicable law or agreed to in writing, software
distributed under the License is distributed on an "AS IS" BASIS,
WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
See the License for the specific language governing permissions and
limitations under the License.
*/
// This file contains shared functions and variables to set up for tests for
// ExternalLoadBalancer and InternalLoadBalancers. It currently cannot live in a
// separate package from GCE because then it would cause a circular import.
package gce
import (
"fmt"
"net/http"
"strings"
"sync"
"testing"
"time"
"github.com/stretchr/testify/assert"
"github.com/stretchr/testify/require"
compute "google.golang.org/api/compute/v1"
"k8s.io/api/core/v1"
metav1 "k8s.io/apimachinery/pkg/apis/meta/v1"
"k8s.io/client-go/tools/cache"
"k8s.io/client-go/tools/record"
v1_service "k8s.io/kubernetes/pkg/api/v1/service"
"k8s.io/kubernetes/pkg/cloudprovider"
"k8s.io/kubernetes/pkg/cloudprovider/providers/gce/cloud"
"k8s.io/kubernetes/pkg/cloudprovider/providers/gce/cloud/meta"
"k8s.io/kubernetes/pkg/cloudprovider/providers/gce/cloud/mock"
kubeletapis "k8s.io/kubernetes/pkg/kubelet/apis"
)
// TODO(yankaiz): Create shared error types for both test/non-test codes.
const (
eventReasonManualChange = "LoadBalancerManualChange"
eventMsgFirewallChange = "Firewall change required by network admin"
errPrefixGetTargetPool = "error getting load balancer's target pool:"
errStrLbNoHosts = "Cannot EnsureLoadBalancer() with no hosts"
wrongTier = "SupremeLuxury"
errStrUnsupportedTier = "unsupported network tier: \"" + wrongTier + "\""
)
type TestClusterValues struct {
ProjectID string
Region string
ZoneName string
SecondaryZoneName string
ClusterID string
ClusterName string
}
func DefaultTestClusterValues() TestClusterValues {
return TestClusterValues{
ProjectID: "test-project",
Region: "us-central1",
ZoneName: "us-central1-b",
SecondaryZoneName: "us-central1-c",
ClusterID: "test-cluster-id",
ClusterName: "Test Cluster Name",
}
}
func fakeLoadbalancerService(lbType string) *v1.Service {
return &v1.Service{
ObjectMeta: metav1.ObjectMeta{
Name: "",
Annotations: map[string]string{ServiceAnnotationLoadBalancerType: lbType},
},
Spec: v1.ServiceSpec{
SessionAffinity: v1.ServiceAffinityClientIP,
Type: v1.ServiceTypeLoadBalancer,
Ports: []v1.ServicePort{{Protocol: v1.ProtocolTCP, Port: int32(123)}},
},
}
}
var (
FilewallChangeMsg = fmt.Sprintf("%s %s %s", v1.EventTypeNormal, eventReasonManualChange, eventMsgFirewallChange)
)
type fakeRoundTripper struct{}
func (*fakeRoundTripper) RoundTrip(*http.Request) (*http.Response, error) {
return nil, fmt.Errorf("err: test used fake http client")
}
func fakeGCECloud(vals TestClusterValues) (*GCECloud, error) {
client := &http.Client{Transport: &fakeRoundTripper{}}
service, err := compute.New(client)
if err != nil {
return nil, err
}
// Used in disk unit tests
fakeManager := newFakeManager(vals.ProjectID, vals.Region)
zonesWithNodes := createNodeZones([]string{vals.ZoneName})
alphaFeatureGate := NewAlphaFeatureGate([]string{})
if err != nil {
return nil, err
}
gce := &GCECloud{
region: vals.Region,
service: service,
manager: fakeManager,
managedZones: []string{vals.ZoneName},
projectID: vals.ProjectID,
networkProjectID: vals.ProjectID,
AlphaFeatureGate: alphaFeatureGate,
nodeZones: zonesWithNodes,
nodeInformerSynced: func() bool { return true },
ClusterID: fakeClusterID(vals.ClusterID),
}
c := cloud.NewMockGCE(&gceProjectRouter{gce})
c.MockTargetPools.AddInstanceHook = mock.AddInstanceHook
c.MockTargetPools.RemoveInstanceHook = mock.RemoveInstanceHook
c.MockForwardingRules.InsertHook = mock.InsertFwdRuleHook
c.MockAddresses.InsertHook = mock.InsertAddressHook
c.MockAlphaAddresses.InsertHook = mock.InsertAlphaAddressHook
c.MockAlphaAddresses.X = mock.AddressAttributes{}
c.MockAddresses.X = mock.AddressAttributes{}
c.MockInstanceGroups.X = mock.InstanceGroupAttributes{
InstanceMap: make(map[meta.Key]map[string]*compute.InstanceWithNamedPorts),
Lock: &sync.Mutex{},
}
c.MockInstanceGroups.AddInstancesHook = mock.AddInstancesHook
c.MockInstanceGroups.RemoveInstancesHook = mock.RemoveInstancesHook
c.MockInstanceGroups.ListInstancesHook = mock.ListInstancesHook
c.MockRegionBackendServices.UpdateHook = mock.UpdateRegionBackendServiceHook
c.MockHealthChecks.UpdateHook = mock.UpdateHealthCheckHook
c.MockFirewalls.UpdateHook = mock.UpdateFirewallHook
keyGA := meta.GlobalKey("key-ga")
c.MockZones.Objects[*keyGA] = &cloud.MockZonesObj{
Obj: &compute.Zone{Name: vals.ZoneName, Region: gce.getRegionLink(vals.Region)},
}
gce.c = c
return gce, nil
}
func createAndInsertNodes(gce *GCECloud, nodeNames []string, zoneName string) ([]*v1.Node, error) {
nodes := []*v1.Node{}
for _, name := range nodeNames {
// Inserting the same node name twice causes an error - here we check if
// the instance exists already before insertion.
// TestUpdateExternalLoadBalancer inserts a new node, and relies on an older
// node to already have been inserted.
instance, _ := gce.getInstanceByName(name)
if instance == nil {
err := gce.InsertInstance(
gce.ProjectID(),
zoneName,
&compute.Instance{
Name: name,
Tags: &compute.Tags{
Items: []string{name},
},
},
)
if err != nil {
return nodes, err
}
}
nodes = append(
nodes,
&v1.Node{
ObjectMeta: metav1.ObjectMeta{
Name: name,
Labels: map[string]string{
kubeletapis.LabelHostname: name,
kubeletapis.LabelZoneFailureDomain: zoneName,
},
},
Status: v1.NodeStatus{
NodeInfo: v1.NodeSystemInfo{
KubeProxyVersion: "v1.7.2",
},
},
},
)
}
return nodes, nil
}
// Stubs ClusterID so that ClusterID.getOrInitialize() does not require calling
// gce.Initialize()
func fakeClusterID(clusterID string) ClusterID {
return ClusterID{
clusterID: &clusterID,
store: cache.NewStore(func(obj interface{}) (string, error) {
return "", nil
}),
}
}
func assertExternalLbResources(t *testing.T, gce *GCECloud, apiService *v1.Service, vals TestClusterValues, nodeNames []string) {
lbName := cloudprovider.GetLoadBalancerName(apiService)
hcName := MakeNodesHealthCheckName(vals.ClusterID)
// Check that Firewalls are created for the LoadBalancer and the HealthCheck
fwNames := []string{
MakeFirewallName(lbName), // Firewalls for external LBs are prefixed with k8s-fw-
MakeHealthCheckFirewallName(vals.ClusterID, hcName, true),
}
for _, fwName := range fwNames {
firewall, err := gce.GetFirewall(fwName)
require.NoError(t, err)
assert.Equal(t, nodeNames, firewall.TargetTags)
assert.NotEmpty(t, firewall.SourceRanges)
}
// Check that TargetPool is Created
pool, err := gce.GetTargetPool(lbName, gce.region)
require.NoError(t, err)
assert.Equal(t, lbName, pool.Name)
assert.NotEmpty(t, pool.HealthChecks)
assert.Equal(t, 1, len(pool.Instances))
// Check that HealthCheck is created
healthcheck, err := gce.GetHttpHealthCheck(hcName)
require.NoError(t, err)
assert.Equal(t, hcName, healthcheck.Name)
// Check that ForwardingRule is created
fwdRule, err := gce.GetRegionForwardingRule(lbName, gce.region)
require.NoError(t, err)
assert.Equal(t, lbName, fwdRule.Name)
assert.Equal(t, "TCP", fwdRule.IPProtocol)
assert.Equal(t, "123-123", fwdRule.PortRange)
}
func assertExternalLbResourcesDeleted(t *testing.T, gce *GCECloud, apiService *v1.Service, vals TestClusterValues, firewallsDeleted bool) {
lbName := cloudprovider.GetLoadBalancerName(apiService)
hcName := MakeNodesHealthCheckName(vals.ClusterID)
if firewallsDeleted {
// Check that Firewalls are deleted for the LoadBalancer and the HealthCheck
fwNames := []string{
MakeFirewallName(lbName),
MakeHealthCheckFirewallName(vals.ClusterID, hcName, true),
}
for _, fwName := range fwNames {
firewall, err := gce.GetFirewall(fwName)
require.Error(t, err)
assert.Nil(t, firewall)
}
// Check forwarding rule is deleted
fwdRule, err := gce.GetRegionForwardingRule(lbName, gce.region)
require.Error(t, err)
assert.Nil(t, fwdRule)
}
// Check that TargetPool is deleted
pool, err := gce.GetTargetPool(lbName, gce.region)
require.Error(t, err)
assert.Nil(t, pool)
// Check that HealthCheck is deleted
healthcheck, err := gce.GetHttpHealthCheck(hcName)
require.Error(t, err)
assert.Nil(t, healthcheck)
}
func assertInternalLbResources(t *testing.T, gce *GCECloud, apiService *v1.Service, vals TestClusterValues, nodeNames []string) {
lbName := cloudprovider.GetLoadBalancerName(apiService)
// Check that Instance Group is created
igName := makeInstanceGroupName(vals.ClusterID)
ig, err := gce.GetInstanceGroup(igName, vals.ZoneName)
assert.NoError(t, err)
assert.Equal(t, igName, ig.Name)
// Check that Firewalls are created for the LoadBalancer and the HealthCheck
fwNames := []string{
lbName, // Firewalls for internal LBs are named the same name as the loadbalancer.
makeHealthCheckFirewallName(lbName, vals.ClusterID, true),
}
for _, fwName := range fwNames {
firewall, err := gce.GetFirewall(fwName)
require.NoError(t, err)
assert.Equal(t, nodeNames, firewall.TargetTags)
assert.NotEmpty(t, firewall.SourceRanges)
}
// Check that HealthCheck is created
sharedHealthCheck := !v1_service.RequestsOnlyLocalTraffic(apiService)
hcName := makeHealthCheckName(lbName, vals.ClusterID, sharedHealthCheck)
healthcheck, err := gce.GetHealthCheck(hcName)
require.NoError(t, err)
assert.Equal(t, hcName, healthcheck.Name)
// Check that BackendService exists
sharedBackend := shareBackendService(apiService)
backendServiceName := makeBackendServiceName(lbName, vals.ClusterID, sharedBackend, cloud.SchemeInternal, "TCP", apiService.Spec.SessionAffinity)
backendServiceLink := gce.getBackendServiceLink(backendServiceName)
bs, err := gce.GetRegionBackendService(backendServiceName, gce.region)
require.NoError(t, err)
assert.Equal(t, "TCP", bs.Protocol)
assert.Equal(
t,
[]string{healthcheck.SelfLink},
bs.HealthChecks,
)
// Check that ForwardingRule is created
fwdRule, err := gce.GetRegionForwardingRule(lbName, gce.region)
require.NoError(t, err)
assert.Equal(t, lbName, fwdRule.Name)
assert.Equal(t, "TCP", fwdRule.IPProtocol)
assert.Equal(t, backendServiceLink, fwdRule.BackendService)
// if no Subnetwork specified, defaults to the GCE NetworkURL
assert.Equal(t, gce.NetworkURL(), fwdRule.Subnetwork)
}
func assertInternalLbResourcesDeleted(t *testing.T, gce *GCECloud, apiService *v1.Service, vals TestClusterValues, firewallsDeleted bool) {
lbName := cloudprovider.GetLoadBalancerName(apiService)
sharedHealthCheck := !v1_service.RequestsOnlyLocalTraffic(apiService)
hcName := makeHealthCheckName(lbName, vals.ClusterID, sharedHealthCheck)
// ensureExternalLoadBalancer and ensureInternalLoadBalancer both create
// Firewalls with the same name.
if firewallsDeleted {
// Check that Firewalls are deleted for the LoadBalancer and the HealthCheck
fwNames := []string{
MakeFirewallName(lbName),
MakeHealthCheckFirewallName(vals.ClusterID, hcName, true),
}
for _, fwName := range fwNames {
firewall, err := gce.GetFirewall(fwName)
require.Error(t, err)
assert.Nil(t, firewall)
}
// Check forwarding rule is deleted
fwdRule, err := gce.GetRegionForwardingRule(lbName, gce.region)
require.Error(t, err)
assert.Nil(t, fwdRule)
}
// Check that Instance Group is deleted
igName := makeInstanceGroupName(vals.ClusterID)
ig, err := gce.GetInstanceGroup(igName, vals.ZoneName)
assert.Error(t, err)
assert.Nil(t, ig)
// Check that HealthCheck is deleted
healthcheck, err := gce.GetHealthCheck(hcName)
require.Error(t, err)
assert.Nil(t, healthcheck)
}
func checkEvent(t *testing.T, recorder *record.FakeRecorder, expected string, shouldMatch bool) bool {
select {
case received := <-recorder.Events:
if strings.HasPrefix(received, expected) != shouldMatch {
t.Errorf(received)
if shouldMatch {
t.Errorf("Should receive message \"%v\" but got \"%v\".", expected, received)
} else {
t.Errorf("Unexpected event \"%v\".", received)
}
}
return false
case <-time.After(2 * time.Second):
if shouldMatch {
t.Errorf("Should receive message \"%v\" but got timed out.", expected)
}
return true
}
}

View File

@ -17,12 +17,12 @@ limitations under the License.
package gce
import (
"context"
"fmt"
"strings"
computealpha "google.golang.org/api/compute/v0.alpha"
"k8s.io/kubernetes/pkg/cloudprovider/providers/gce/cloud"
"k8s.io/kubernetes/pkg/cloudprovider/providers/gce/cloud/filter"
"k8s.io/kubernetes/pkg/cloudprovider/providers/gce/cloud/meta"
)
@ -37,31 +37,40 @@ func newNetworkEndpointGroupMetricContext(request string, zone string) *metricCo
}
func (gce *GCECloud) GetNetworkEndpointGroup(name string, zone string) (*computealpha.NetworkEndpointGroup, error) {
ctx, cancel := cloud.ContextWithCallTimeout()
defer cancel()
mc := newNetworkEndpointGroupMetricContext("get", zone)
if err := gce.alphaFeatureEnabled(AlphaFeatureNetworkEndpointGroup); err != nil {
return nil, mc.Observe(err)
}
v, err := gce.c.AlphaNetworkEndpointGroups().Get(context.Background(), meta.ZonalKey(name, zone))
v, err := gce.c.AlphaNetworkEndpointGroups().Get(ctx, meta.ZonalKey(name, zone))
return v, mc.Observe(err)
}
func (gce *GCECloud) ListNetworkEndpointGroup(zone string) ([]*computealpha.NetworkEndpointGroup, error) {
ctx, cancel := cloud.ContextWithCallTimeout()
defer cancel()
mc := newNetworkEndpointGroupMetricContext("list", zone)
if err := gce.alphaFeatureEnabled(AlphaFeatureNetworkEndpointGroup); err != nil {
return nil, mc.Observe(err)
}
negs, err := gce.c.AlphaNetworkEndpointGroups().List(context.Background(), zone, filter.None)
negs, err := gce.c.AlphaNetworkEndpointGroups().List(ctx, zone, filter.None)
return negs, mc.Observe(err)
}
// AggregatedListNetworkEndpointGroup returns a map of zone -> endpoint group.
func (gce *GCECloud) AggregatedListNetworkEndpointGroup() (map[string][]*computealpha.NetworkEndpointGroup, error) {
ctx, cancel := cloud.ContextWithCallTimeout()
defer cancel()
mc := newNetworkEndpointGroupMetricContext("aggregated_list", "")
if err := gce.alphaFeatureEnabled(AlphaFeatureNetworkEndpointGroup); err != nil {
return nil, mc.Observe(err)
}
// TODO: filter for the region the cluster is in.
all, err := gce.c.AlphaNetworkEndpointGroups().AggregatedList(context.Background(), filter.None)
all, err := gce.c.AlphaNetworkEndpointGroups().AggregatedList(ctx, filter.None)
if err != nil {
return nil, mc.Observe(err)
}
@ -79,22 +88,31 @@ func (gce *GCECloud) AggregatedListNetworkEndpointGroup() (map[string][]*compute
}
func (gce *GCECloud) CreateNetworkEndpointGroup(neg *computealpha.NetworkEndpointGroup, zone string) error {
ctx, cancel := cloud.ContextWithCallTimeout()
defer cancel()
if err := gce.alphaFeatureEnabled(AlphaFeatureNetworkEndpointGroup); err != nil {
return err
}
mc := newNetworkEndpointGroupMetricContext("create", zone)
return mc.Observe(gce.c.AlphaNetworkEndpointGroups().Insert(context.Background(), meta.ZonalKey(neg.Name, zone), neg))
return mc.Observe(gce.c.AlphaNetworkEndpointGroups().Insert(ctx, meta.ZonalKey(neg.Name, zone), neg))
}
func (gce *GCECloud) DeleteNetworkEndpointGroup(name string, zone string) error {
ctx, cancel := cloud.ContextWithCallTimeout()
defer cancel()
if err := gce.alphaFeatureEnabled(AlphaFeatureNetworkEndpointGroup); err != nil {
return err
}
mc := newNetworkEndpointGroupMetricContext("delete", zone)
return mc.Observe(gce.c.AlphaNetworkEndpointGroups().Delete(context.Background(), meta.ZonalKey(name, zone)))
return mc.Observe(gce.c.AlphaNetworkEndpointGroups().Delete(ctx, meta.ZonalKey(name, zone)))
}
func (gce *GCECloud) AttachNetworkEndpoints(name, zone string, endpoints []*computealpha.NetworkEndpoint) error {
ctx, cancel := cloud.ContextWithCallTimeout()
defer cancel()
mc := newNetworkEndpointGroupMetricContext("attach", zone)
if err := gce.alphaFeatureEnabled(AlphaFeatureNetworkEndpointGroup); err != nil {
return mc.Observe(err)
@ -102,10 +120,13 @@ func (gce *GCECloud) AttachNetworkEndpoints(name, zone string, endpoints []*comp
req := &computealpha.NetworkEndpointGroupsAttachEndpointsRequest{
NetworkEndpoints: endpoints,
}
return mc.Observe(gce.c.AlphaNetworkEndpointGroups().AttachNetworkEndpoints(context.Background(), meta.ZonalKey(name, zone), req))
return mc.Observe(gce.c.AlphaNetworkEndpointGroups().AttachNetworkEndpoints(ctx, meta.ZonalKey(name, zone), req))
}
func (gce *GCECloud) DetachNetworkEndpoints(name, zone string, endpoints []*computealpha.NetworkEndpoint) error {
ctx, cancel := cloud.ContextWithCallTimeout()
defer cancel()
mc := newNetworkEndpointGroupMetricContext("detach", zone)
if err := gce.alphaFeatureEnabled(AlphaFeatureNetworkEndpointGroup); err != nil {
return mc.Observe(err)
@ -113,10 +134,13 @@ func (gce *GCECloud) DetachNetworkEndpoints(name, zone string, endpoints []*comp
req := &computealpha.NetworkEndpointGroupsDetachEndpointsRequest{
NetworkEndpoints: endpoints,
}
return mc.Observe(gce.c.AlphaNetworkEndpointGroups().DetachNetworkEndpoints(context.Background(), meta.ZonalKey(name, zone), req))
return mc.Observe(gce.c.AlphaNetworkEndpointGroups().DetachNetworkEndpoints(ctx, meta.ZonalKey(name, zone), req))
}
func (gce *GCECloud) ListNetworkEndpoints(name, zone string, showHealthStatus bool) ([]*computealpha.NetworkEndpointWithHealthStatus, error) {
ctx, cancel := cloud.ContextWithCallTimeout()
defer cancel()
mc := newNetworkEndpointGroupMetricContext("list_networkendpoints", zone)
if err := gce.alphaFeatureEnabled(AlphaFeatureNetworkEndpointGroup); err != nil {
return nil, mc.Observe(err)
@ -128,6 +152,6 @@ func (gce *GCECloud) ListNetworkEndpoints(name, zone string, showHealthStatus bo
req := &computealpha.NetworkEndpointGroupsListEndpointsRequest{
HealthStatus: healthStatus,
}
l, err := gce.c.AlphaNetworkEndpointGroups().ListNetworkEndpoints(context.Background(), meta.ZonalKey(name, zone), req, filter.None)
l, err := gce.c.AlphaNetworkEndpointGroups().ListNetworkEndpoints(ctx, meta.ZonalKey(name, zone), req, filter.None)
return l, mc.Observe(err)
}

View File

@ -1,180 +0,0 @@
/*
Copyright 2017 The Kubernetes Authors.
Licensed under the Apache License, Version 2.0 (the "License");
you may not use this file except in compliance with the License.
You may obtain a copy of the License at
http://www.apache.org/licenses/LICENSE-2.0
Unless required by applicable law or agreed to in writing, software
distributed under the License is distributed on an "AS IS" BASIS,
WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
See the License for the specific language governing permissions and
limitations under the License.
*/
package gce
import (
"encoding/json"
"fmt"
"time"
"k8s.io/apimachinery/pkg/util/wait"
"github.com/golang/glog"
computealpha "google.golang.org/api/compute/v0.alpha"
computebeta "google.golang.org/api/compute/v0.beta"
computev1 "google.golang.org/api/compute/v1"
"google.golang.org/api/googleapi"
)
func (gce *GCECloud) waitForOp(op *computev1.Operation, getOperation func(operationName string) (*computev1.Operation, error), mc *metricContext) error {
if op == nil {
return mc.Observe(fmt.Errorf("operation must not be nil"))
}
if opIsDone(op) {
return getErrorFromOp(op)
}
opStart := time.Now()
opName := op.Name
return wait.Poll(operationPollInterval, operationPollTimeoutDuration, func() (bool, error) {
start := time.Now()
gce.operationPollRateLimiter.Accept()
duration := time.Now().Sub(start)
if duration > 5*time.Second {
glog.V(2).Infof("pollOperation: throttled %v for %v", duration, opName)
}
pollOp, err := getOperation(opName)
if err != nil {
glog.Warningf("GCE poll operation %s failed: pollOp: [%v] err: [%v] getErrorFromOp: [%v]",
opName, pollOp, err, getErrorFromOp(pollOp))
}
done := opIsDone(pollOp)
if done {
duration := time.Now().Sub(opStart)
if duration > 1*time.Minute {
// Log the JSON. It's cleaner than the %v structure.
enc, err := pollOp.MarshalJSON()
if err != nil {
glog.Warningf("waitForOperation: long operation (%v): %v (failed to encode to JSON: %v)",
duration, pollOp, err)
} else {
glog.V(2).Infof("waitForOperation: long operation (%v): %v",
duration, string(enc))
}
}
}
return done, mc.Observe(getErrorFromOp(pollOp))
})
}
func opIsDone(op *computev1.Operation) bool {
return op != nil && op.Status == "DONE"
}
func getErrorFromOp(op *computev1.Operation) error {
if op != nil && op.Error != nil && len(op.Error.Errors) > 0 {
err := &googleapi.Error{
Code: int(op.HttpErrorStatusCode),
Message: op.Error.Errors[0].Message,
}
glog.Errorf("GCE operation failed: %v", err)
return err
}
return nil
}
func (gce *GCECloud) waitForGlobalOp(op gceObject, mc *metricContext) error {
return gce.waitForGlobalOpInProject(op, gce.ProjectID(), mc)
}
func (gce *GCECloud) waitForRegionOp(op gceObject, region string, mc *metricContext) error {
return gce.waitForRegionOpInProject(op, gce.ProjectID(), region, mc)
}
func (gce *GCECloud) waitForZoneOp(op gceObject, zone string, mc *metricContext) error {
return gce.waitForZoneOpInProject(op, gce.ProjectID(), zone, mc)
}
func (gce *GCECloud) waitForGlobalOpInProject(op gceObject, projectID string, mc *metricContext) error {
switch v := op.(type) {
case *computealpha.Operation:
return gce.waitForOp(convertToV1Operation(op), func(operationName string) (*computev1.Operation, error) {
op, err := gce.serviceAlpha.GlobalOperations.Get(projectID, operationName).Do()
return convertToV1Operation(op), err
}, mc)
case *computebeta.Operation:
return gce.waitForOp(convertToV1Operation(op), func(operationName string) (*computev1.Operation, error) {
op, err := gce.serviceBeta.GlobalOperations.Get(projectID, operationName).Do()
return convertToV1Operation(op), err
}, mc)
case *computev1.Operation:
return gce.waitForOp(op.(*computev1.Operation), func(operationName string) (*computev1.Operation, error) {
return gce.service.GlobalOperations.Get(projectID, operationName).Do()
}, mc)
default:
return fmt.Errorf("unexpected type: %T", v)
}
}
func (gce *GCECloud) waitForRegionOpInProject(op gceObject, projectID, region string, mc *metricContext) error {
switch v := op.(type) {
case *computealpha.Operation:
return gce.waitForOp(convertToV1Operation(op), func(operationName string) (*computev1.Operation, error) {
op, err := gce.serviceAlpha.RegionOperations.Get(projectID, region, operationName).Do()
return convertToV1Operation(op), err
}, mc)
case *computebeta.Operation:
return gce.waitForOp(convertToV1Operation(op), func(operationName string) (*computev1.Operation, error) {
op, err := gce.serviceBeta.RegionOperations.Get(projectID, region, operationName).Do()
return convertToV1Operation(op), err
}, mc)
case *computev1.Operation:
return gce.waitForOp(op.(*computev1.Operation), func(operationName string) (*computev1.Operation, error) {
return gce.service.RegionOperations.Get(projectID, region, operationName).Do()
}, mc)
default:
return fmt.Errorf("unexpected type: %T", v)
}
}
func (gce *GCECloud) waitForZoneOpInProject(op gceObject, projectID, zone string, mc *metricContext) error {
switch v := op.(type) {
case *computealpha.Operation:
return gce.waitForOp(convertToV1Operation(op), func(operationName string) (*computev1.Operation, error) {
op, err := gce.serviceAlpha.ZoneOperations.Get(projectID, zone, operationName).Do()
return convertToV1Operation(op), err
}, mc)
case *computebeta.Operation:
return gce.waitForOp(convertToV1Operation(op), func(operationName string) (*computev1.Operation, error) {
op, err := gce.serviceBeta.ZoneOperations.Get(projectID, zone, operationName).Do()
return convertToV1Operation(op), err
}, mc)
case *computev1.Operation:
return gce.waitForOp(op.(*computev1.Operation), func(operationName string) (*computev1.Operation, error) {
return gce.service.ZoneOperations.Get(projectID, zone, operationName).Do()
}, mc)
default:
return fmt.Errorf("unexpected type: %T", v)
}
}
func convertToV1Operation(object gceObject) *computev1.Operation {
enc, err := object.MarshalJSON()
if err != nil {
panic(fmt.Sprintf("Failed to encode to json: %v", err))
}
var op computev1.Operation
if err := json.Unmarshal(enc, &op); err != nil {
panic(fmt.Sprintf("Failed to convert GCE apiObject %v to v1 operation: %v", object, err))
}
return &op
}

View File

@ -27,6 +27,7 @@ import (
"k8s.io/apimachinery/pkg/types"
"k8s.io/kubernetes/pkg/cloudprovider"
"k8s.io/kubernetes/pkg/cloudprovider/providers/gce/cloud"
"k8s.io/kubernetes/pkg/cloudprovider/providers/gce/cloud/filter"
"k8s.io/kubernetes/pkg/cloudprovider/providers/gce/cloud/meta"
)
@ -37,10 +38,13 @@ func newRoutesMetricContext(request string) *metricContext {
// ListRoutes in the cloud environment.
func (gce *GCECloud) ListRoutes(ctx context.Context, clusterName string) ([]*cloudprovider.Route, error) {
ctx, cancel := cloud.ContextWithCallTimeout()
defer cancel()
mc := newRoutesMetricContext("list")
prefix := truncateClusterName(clusterName)
f := filter.Regexp("name", prefix+"-.*").AndRegexp("network", gce.NetworkURL()).AndRegexp("description", k8sNodeRouteTag)
routes, err := gce.c.Routes().List(context.Background(), f)
routes, err := gce.c.Routes().List(ctx, f)
if err != nil {
return nil, mc.Observe(err)
}
@ -60,6 +64,9 @@ func (gce *GCECloud) ListRoutes(ctx context.Context, clusterName string) ([]*clo
// CreateRoute in the cloud environment.
func (gce *GCECloud) CreateRoute(ctx context.Context, clusterName string, nameHint string, route *cloudprovider.Route) error {
ctx, cancel := cloud.ContextWithCallTimeout()
defer cancel()
mc := newRoutesMetricContext("create")
targetInstance, err := gce.getInstanceByName(mapNodeNameToInstanceName(route.TargetNode))
@ -74,7 +81,7 @@ func (gce *GCECloud) CreateRoute(ctx context.Context, clusterName string, nameHi
Priority: 1000,
Description: k8sNodeRouteTag,
}
err = gce.c.Routes().Insert(context.Background(), meta.GlobalKey(cr.Name), cr)
err = gce.c.Routes().Insert(ctx, meta.GlobalKey(cr.Name), cr)
if isHTTPErrorCode(err, http.StatusConflict) {
glog.Infof("Route %q already exists.", cr.Name)
err = nil
@ -84,8 +91,11 @@ func (gce *GCECloud) CreateRoute(ctx context.Context, clusterName string, nameHi
// DeleteRoute from the cloud environment.
func (gce *GCECloud) DeleteRoute(ctx context.Context, clusterName string, route *cloudprovider.Route) error {
ctx, cancel := cloud.ContextWithCallTimeout()
defer cancel()
mc := newRoutesMetricContext("delete")
return mc.Observe(gce.c.Routes().Delete(context.Background(), meta.GlobalKey(route.Name)))
return mc.Observe(gce.c.Routes().Delete(ctx, meta.GlobalKey(route.Name)))
}
func truncateClusterName(clusterName string) string {

View File

@ -0,0 +1,116 @@
/*
Copyright 2018 The Kubernetes Authors.
Licensed under the Apache License, Version 2.0 (the "License");
you may not use this file except in compliance with the License.
You may obtain a copy of the License at
http://www.apache.org/licenses/LICENSE-2.0
Unless required by applicable law or agreed to in writing, software
distributed under the License is distributed on an "AS IS" BASIS,
WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
See the License for the specific language governing permissions and
limitations under the License.
*/
package gce
import (
computebeta "google.golang.org/api/compute/v0.beta"
"k8s.io/kubernetes/pkg/cloudprovider/providers/gce/cloud"
"k8s.io/kubernetes/pkg/cloudprovider/providers/gce/cloud/filter"
"k8s.io/kubernetes/pkg/cloudprovider/providers/gce/cloud/meta"
)
func newSecurityPolicyMetricContextWithVersion(request, version string) *metricContext {
return newGenericMetricContext("securitypolicy", request, "", unusedMetricLabel, version)
}
// GetBetaSecurityPolicy retrieves a security policy.
func (gce *GCECloud) GetBetaSecurityPolicy(name string) (*computebeta.SecurityPolicy, error) {
ctx, cancel := cloud.ContextWithCallTimeout()
defer cancel()
mc := newSecurityPolicyMetricContextWithVersion("get", computeBetaVersion)
v, err := gce.c.BetaSecurityPolicies().Get(ctx, meta.GlobalKey(name))
return v, mc.Observe(err)
}
// ListBetaSecurityPolicy lists all security policies in the project.
func (gce *GCECloud) ListBetaSecurityPolicy() ([]*computebeta.SecurityPolicy, error) {
ctx, cancel := cloud.ContextWithCallTimeout()
defer cancel()
mc := newSecurityPolicyMetricContextWithVersion("list", computeBetaVersion)
v, err := gce.c.BetaSecurityPolicies().List(ctx, filter.None)
return v, mc.Observe(err)
}
// CreateBetaSecurityPolicy creates the given security policy.
func (gce *GCECloud) CreateBetaSecurityPolicy(sp *computebeta.SecurityPolicy) error {
ctx, cancel := cloud.ContextWithCallTimeout()
defer cancel()
mc := newSecurityPolicyMetricContextWithVersion("create", computeBetaVersion)
return mc.Observe(gce.c.BetaSecurityPolicies().Insert(ctx, meta.GlobalKey(sp.Name), sp))
}
// DeleteBetaSecurityPolicy deletes the given security policy.
func (gce *GCECloud) DeleteBetaSecurityPolicy(name string) error {
ctx, cancel := cloud.ContextWithCallTimeout()
defer cancel()
mc := newSecurityPolicyMetricContextWithVersion("delete", computeBetaVersion)
return mc.Observe(gce.c.BetaSecurityPolicies().Delete(ctx, meta.GlobalKey(name)))
}
// PatchBetaSecurityPolicy applies the given security policy as a
// patch to an existing security policy.
func (gce *GCECloud) PatchBetaSecurityPolicy(sp *computebeta.SecurityPolicy) error {
ctx, cancel := cloud.ContextWithCallTimeout()
defer cancel()
mc := newSecurityPolicyMetricContextWithVersion("patch", computeBetaVersion)
return mc.Observe(gce.c.BetaSecurityPolicies().Patch(ctx, meta.GlobalKey(sp.Name), sp))
}
// GetRuleForBetaSecurityPolicy gets rule from a security policy.
func (gce *GCECloud) GetRuleForBetaSecurityPolicy(name string) (*computebeta.SecurityPolicyRule, error) {
ctx, cancel := cloud.ContextWithCallTimeout()
defer cancel()
mc := newSecurityPolicyMetricContextWithVersion("get_rule", computeBetaVersion)
v, err := gce.c.BetaSecurityPolicies().GetRule(ctx, meta.GlobalKey(name))
return v, mc.Observe(err)
}
// AddRuletoBetaSecurityPolicy adds the given security policy rule to
// a security policy.
func (gce *GCECloud) AddRuletoBetaSecurityPolicy(name string, spr *computebeta.SecurityPolicyRule) error {
ctx, cancel := cloud.ContextWithCallTimeout()
defer cancel()
mc := newSecurityPolicyMetricContextWithVersion("add_rule", computeBetaVersion)
return mc.Observe(gce.c.BetaSecurityPolicies().AddRule(ctx, meta.GlobalKey(name), spr))
}
// PatchRuleForBetaSecurityPolicy patches the given security policy
// rule to a security policy.
func (gce *GCECloud) PatchRuleForBetaSecurityPolicy(name string, spr *computebeta.SecurityPolicyRule) error {
ctx, cancel := cloud.ContextWithCallTimeout()
defer cancel()
mc := newSecurityPolicyMetricContextWithVersion("patch_rule", computeBetaVersion)
return mc.Observe(gce.c.BetaSecurityPolicies().PatchRule(ctx, meta.GlobalKey(name), spr))
}
// RemoveRuleFromBetaSecurityPolicy removes rule from a security policy.
func (gce *GCECloud) RemoveRuleFromBetaSecurityPolicy(name string) error {
ctx, cancel := cloud.ContextWithCallTimeout()
defer cancel()
mc := newSecurityPolicyMetricContextWithVersion("remove_rule", computeBetaVersion)
return mc.Observe(gce.c.BetaSecurityPolicies().RemoveRule(ctx, meta.GlobalKey(name)))
}

View File

@ -17,10 +17,9 @@ limitations under the License.
package gce
import (
"context"
compute "google.golang.org/api/compute/v1"
"k8s.io/kubernetes/pkg/cloudprovider/providers/gce/cloud"
"k8s.io/kubernetes/pkg/cloudprovider/providers/gce/cloud/meta"
)
@ -30,37 +29,52 @@ func newTargetPoolMetricContext(request, region string) *metricContext {
// GetTargetPool returns the TargetPool by name.
func (gce *GCECloud) GetTargetPool(name, region string) (*compute.TargetPool, error) {
ctx, cancel := cloud.ContextWithCallTimeout()
defer cancel()
mc := newTargetPoolMetricContext("get", region)
v, err := gce.c.TargetPools().Get(context.Background(), meta.RegionalKey(name, region))
v, err := gce.c.TargetPools().Get(ctx, meta.RegionalKey(name, region))
return v, mc.Observe(err)
}
// CreateTargetPool creates the passed TargetPool
func (gce *GCECloud) CreateTargetPool(tp *compute.TargetPool, region string) error {
ctx, cancel := cloud.ContextWithCallTimeout()
defer cancel()
mc := newTargetPoolMetricContext("create", region)
return mc.Observe(gce.c.TargetPools().Insert(context.Background(), meta.RegionalKey(tp.Name, region), tp))
return mc.Observe(gce.c.TargetPools().Insert(ctx, meta.RegionalKey(tp.Name, region), tp))
}
// DeleteTargetPool deletes TargetPool by name.
func (gce *GCECloud) DeleteTargetPool(name, region string) error {
ctx, cancel := cloud.ContextWithCallTimeout()
defer cancel()
mc := newTargetPoolMetricContext("delete", region)
return mc.Observe(gce.c.TargetPools().Delete(context.Background(), meta.RegionalKey(name, region)))
return mc.Observe(gce.c.TargetPools().Delete(ctx, meta.RegionalKey(name, region)))
}
// AddInstancesToTargetPool adds instances by link to the TargetPool
func (gce *GCECloud) AddInstancesToTargetPool(name, region string, instanceRefs []*compute.InstanceReference) error {
ctx, cancel := cloud.ContextWithCallTimeout()
defer cancel()
req := &compute.TargetPoolsAddInstanceRequest{
Instances: instanceRefs,
}
mc := newTargetPoolMetricContext("add_instances", region)
return mc.Observe(gce.c.TargetPools().AddInstance(context.Background(), meta.RegionalKey(name, region), req))
return mc.Observe(gce.c.TargetPools().AddInstance(ctx, meta.RegionalKey(name, region), req))
}
// RemoveInstancesFromTargetPool removes instances by link to the TargetPool
func (gce *GCECloud) RemoveInstancesFromTargetPool(name, region string, instanceRefs []*compute.InstanceReference) error {
ctx, cancel := cloud.ContextWithCallTimeout()
defer cancel()
req := &compute.TargetPoolsRemoveInstanceRequest{
Instances: instanceRefs,
}
mc := newTargetPoolMetricContext("remove_instances", region)
return mc.Observe(gce.c.TargetPools().RemoveInstance(context.Background(), meta.RegionalKey(name, region), req))
return mc.Observe(gce.c.TargetPools().RemoveInstance(ctx, meta.RegionalKey(name, region), req))
}

View File

@ -17,10 +17,9 @@ limitations under the License.
package gce
import (
"context"
compute "google.golang.org/api/compute/v1"
"k8s.io/kubernetes/pkg/cloudprovider/providers/gce/cloud"
"k8s.io/kubernetes/pkg/cloudprovider/providers/gce/cloud/filter"
"k8s.io/kubernetes/pkg/cloudprovider/providers/gce/cloud/meta"
)
@ -31,34 +30,49 @@ func newTargetProxyMetricContext(request string) *metricContext {
// GetTargetHttpProxy returns the UrlMap by name.
func (gce *GCECloud) GetTargetHttpProxy(name string) (*compute.TargetHttpProxy, error) {
ctx, cancel := cloud.ContextWithCallTimeout()
defer cancel()
mc := newTargetProxyMetricContext("get")
v, err := gce.c.TargetHttpProxies().Get(context.Background(), meta.GlobalKey(name))
v, err := gce.c.TargetHttpProxies().Get(ctx, meta.GlobalKey(name))
return v, mc.Observe(err)
}
// CreateTargetHttpProxy creates a TargetHttpProxy
func (gce *GCECloud) CreateTargetHttpProxy(proxy *compute.TargetHttpProxy) error {
ctx, cancel := cloud.ContextWithCallTimeout()
defer cancel()
mc := newTargetProxyMetricContext("create")
return mc.Observe(gce.c.TargetHttpProxies().Insert(context.Background(), meta.GlobalKey(proxy.Name), proxy))
return mc.Observe(gce.c.TargetHttpProxies().Insert(ctx, meta.GlobalKey(proxy.Name), proxy))
}
// SetUrlMapForTargetHttpProxy sets the given UrlMap for the given TargetHttpProxy.
func (gce *GCECloud) SetUrlMapForTargetHttpProxy(proxy *compute.TargetHttpProxy, urlMap *compute.UrlMap) error {
ctx, cancel := cloud.ContextWithCallTimeout()
defer cancel()
ref := &compute.UrlMapReference{UrlMap: urlMap.SelfLink}
mc := newTargetProxyMetricContext("set_url_map")
return mc.Observe(gce.c.TargetHttpProxies().SetUrlMap(context.Background(), meta.GlobalKey(proxy.Name), ref))
return mc.Observe(gce.c.TargetHttpProxies().SetUrlMap(ctx, meta.GlobalKey(proxy.Name), ref))
}
// DeleteTargetHttpProxy deletes the TargetHttpProxy by name.
func (gce *GCECloud) DeleteTargetHttpProxy(name string) error {
ctx, cancel := cloud.ContextWithCallTimeout()
defer cancel()
mc := newTargetProxyMetricContext("delete")
return mc.Observe(gce.c.TargetHttpProxies().Delete(context.Background(), meta.GlobalKey(name)))
return mc.Observe(gce.c.TargetHttpProxies().Delete(ctx, meta.GlobalKey(name)))
}
// ListTargetHttpProxies lists all TargetHttpProxies in the project.
func (gce *GCECloud) ListTargetHttpProxies() ([]*compute.TargetHttpProxy, error) {
ctx, cancel := cloud.ContextWithCallTimeout()
defer cancel()
mc := newTargetProxyMetricContext("list")
v, err := gce.c.TargetHttpProxies().List(context.Background(), filter.None)
v, err := gce.c.TargetHttpProxies().List(ctx, filter.None)
return v, mc.Observe(err)
}
@ -66,42 +80,60 @@ func (gce *GCECloud) ListTargetHttpProxies() ([]*compute.TargetHttpProxy, error)
// GetTargetHttpsProxy returns the UrlMap by name.
func (gce *GCECloud) GetTargetHttpsProxy(name string) (*compute.TargetHttpsProxy, error) {
ctx, cancel := cloud.ContextWithCallTimeout()
defer cancel()
mc := newTargetProxyMetricContext("get")
v, err := gce.c.TargetHttpsProxies().Get(context.Background(), meta.GlobalKey(name))
v, err := gce.c.TargetHttpsProxies().Get(ctx, meta.GlobalKey(name))
return v, mc.Observe(err)
}
// CreateTargetHttpsProxy creates a TargetHttpsProxy
func (gce *GCECloud) CreateTargetHttpsProxy(proxy *compute.TargetHttpsProxy) error {
ctx, cancel := cloud.ContextWithCallTimeout()
defer cancel()
mc := newTargetProxyMetricContext("create")
return mc.Observe(gce.c.TargetHttpsProxies().Insert(context.Background(), meta.GlobalKey(proxy.Name), proxy))
return mc.Observe(gce.c.TargetHttpsProxies().Insert(ctx, meta.GlobalKey(proxy.Name), proxy))
}
// SetUrlMapForTargetHttpsProxy sets the given UrlMap for the given TargetHttpsProxy.
func (gce *GCECloud) SetUrlMapForTargetHttpsProxy(proxy *compute.TargetHttpsProxy, urlMap *compute.UrlMap) error {
ctx, cancel := cloud.ContextWithCallTimeout()
defer cancel()
mc := newTargetProxyMetricContext("set_url_map")
ref := &compute.UrlMapReference{UrlMap: urlMap.SelfLink}
return mc.Observe(gce.c.TargetHttpsProxies().SetUrlMap(context.Background(), meta.GlobalKey(proxy.Name), ref))
return mc.Observe(gce.c.TargetHttpsProxies().SetUrlMap(ctx, meta.GlobalKey(proxy.Name), ref))
}
// SetSslCertificateForTargetHttpsProxy sets the given SslCertificate for the given TargetHttpsProxy.
func (gce *GCECloud) SetSslCertificateForTargetHttpsProxy(proxy *compute.TargetHttpsProxy, sslCert *compute.SslCertificate) error {
func (gce *GCECloud) SetSslCertificateForTargetHttpsProxy(proxy *compute.TargetHttpsProxy, sslCertURLs []string) error {
ctx, cancel := cloud.ContextWithCallTimeout()
defer cancel()
mc := newTargetProxyMetricContext("set_ssl_cert")
req := &compute.TargetHttpsProxiesSetSslCertificatesRequest{
SslCertificates: []string{sslCert.SelfLink},
SslCertificates: sslCertURLs,
}
return mc.Observe(gce.c.TargetHttpsProxies().SetSslCertificates(context.Background(), meta.GlobalKey(proxy.Name), req))
return mc.Observe(gce.c.TargetHttpsProxies().SetSslCertificates(ctx, meta.GlobalKey(proxy.Name), req))
}
// DeleteTargetHttpsProxy deletes the TargetHttpsProxy by name.
func (gce *GCECloud) DeleteTargetHttpsProxy(name string) error {
ctx, cancel := cloud.ContextWithCallTimeout()
defer cancel()
mc := newTargetProxyMetricContext("delete")
return mc.Observe(gce.c.TargetHttpsProxies().Delete(context.Background(), meta.GlobalKey(name)))
return mc.Observe(gce.c.TargetHttpsProxies().Delete(ctx, meta.GlobalKey(name)))
}
// ListTargetHttpsProxies lists all TargetHttpsProxies in the project.
func (gce *GCECloud) ListTargetHttpsProxies() ([]*compute.TargetHttpsProxy, error) {
ctx, cancel := cloud.ContextWithCallTimeout()
defer cancel()
mc := newTargetProxyMetricContext("list")
v, err := gce.c.TargetHttpsProxies().List(context.Background(), filter.None)
v, err := gce.c.TargetHttpsProxies().List(ctx, filter.None)
return v, mc.Observe(err)
}

View File

@ -18,16 +18,12 @@ package gce
import (
"context"
"encoding/json"
"reflect"
"strings"
"testing"
"golang.org/x/oauth2/google"
computealpha "google.golang.org/api/compute/v0.alpha"
computebeta "google.golang.org/api/compute/v0.beta"
computev1 "google.golang.org/api/compute/v1"
"k8s.io/kubernetes/pkg/cloudprovider"
)
@ -480,102 +476,40 @@ func TestGenerateCloudConfigs(t *testing.T) {
}
}
func TestConvertToV1Operation(t *testing.T) {
v1Op := getTestOperation()
enc, _ := v1Op.MarshalJSON()
var op interface{}
var alphaOp computealpha.Operation
var betaOp computebeta.Operation
if err := json.Unmarshal(enc, &alphaOp); err != nil {
t.Errorf("Failed to unmarshal operation: %v", err)
}
if err := json.Unmarshal(enc, &betaOp); err != nil {
t.Errorf("Failed to unmarshal operation: %v", err)
}
op = convertToV1Operation(&alphaOp)
if _, ok := op.(*computev1.Operation); ok {
if !reflect.DeepEqual(op, v1Op) {
t.Errorf("Failed to maintain consistency across conversion")
}
} else {
t.Errorf("Expect output to be type v1 operation, but got %v", op)
}
op = convertToV1Operation(&betaOp)
if _, ok := op.(*computev1.Operation); ok {
if !reflect.DeepEqual(op, v1Op) {
t.Errorf("Failed to maintain consistency across conversion")
}
} else {
t.Errorf("Expect output to be type v1 operation, but got %v", op)
}
}
func getTestOperation() *computev1.Operation {
return &computev1.Operation{
Name: "test",
Description: "test",
Id: uint64(12345),
Error: &computev1.OperationError{
Errors: []*computev1.OperationErrorErrors{
{
Code: "555",
Message: "error",
},
},
},
}
}
func TestNewAlphaFeatureGate(t *testing.T) {
knownAlphaFeatures["foo"] = true
knownAlphaFeatures["bar"] = true
testCases := []struct {
alphaFeatures []string
expectEnabled []string
expectDisabled []string
expectError bool
}{
// enable foo bar
{
alphaFeatures: []string{"foo", "bar"},
expectEnabled: []string{"foo", "bar"},
expectDisabled: []string{"aaa"},
expectError: false,
},
// no alpha feature
{
alphaFeatures: []string{},
expectEnabled: []string{},
expectDisabled: []string{"foo", "bar"},
expectError: false,
},
// unsupported alpha feature
{
alphaFeatures: []string{"aaa", "foo"},
expectError: true,
expectEnabled: []string{"foo"},
expectDisabled: []string{"aaa"},
expectDisabled: []string{},
},
// enable foo
{
alphaFeatures: []string{"foo"},
expectEnabled: []string{"foo"},
expectDisabled: []string{"bar"},
expectError: false,
},
}
for _, tc := range testCases {
featureGate, err := NewAlphaFeatureGate(tc.alphaFeatures)
if (tc.expectError && err == nil) || (!tc.expectError && err != nil) {
t.Errorf("Expect error to be %v, but got error %v", tc.expectError, err)
}
featureGate := NewAlphaFeatureGate(tc.alphaFeatures)
for _, key := range tc.expectEnabled {
if !featureGate.Enabled(key) {
@ -588,8 +522,6 @@ func TestNewAlphaFeatureGate(t *testing.T) {
}
}
}
delete(knownAlphaFeatures, "foo")
delete(knownAlphaFeatures, "bar")
}
func TestGetRegionInURL(t *testing.T) {

View File

@ -25,7 +25,7 @@ import (
"github.com/golang/glog"
"google.golang.org/api/googleapi"
tpuapi "google.golang.org/api/tpu/v1alpha1"
tpuapi "google.golang.org/api/tpu/v1"
"k8s.io/apimachinery/pkg/util/wait"
)
@ -165,7 +165,7 @@ func (gce *GCECloud) waitForTPUOp(interval, timeout time.Duration, op *tpuapi.Op
// newTPUMetricContext returns a new metricContext used for recording metrics
// of Cloud TPU API calls.
func newTPUMetricContext(request, zone string) *metricContext {
return newGenericMetricContext("tpus", request, unusedMetricLabel, zone, "alpha")
return newGenericMetricContext("tpus", request, unusedMetricLabel, zone, "v1")
}
// getErrorFromTPUOp returns the error in the failed op, or nil if the op

View File

@ -17,10 +17,9 @@ limitations under the License.
package gce
import (
"context"
compute "google.golang.org/api/compute/v1"
"k8s.io/kubernetes/pkg/cloudprovider/providers/gce/cloud"
"k8s.io/kubernetes/pkg/cloudprovider/providers/gce/cloud/filter"
"k8s.io/kubernetes/pkg/cloudprovider/providers/gce/cloud/meta"
)
@ -31,32 +30,47 @@ func newUrlMapMetricContext(request string) *metricContext {
// GetUrlMap returns the UrlMap by name.
func (gce *GCECloud) GetUrlMap(name string) (*compute.UrlMap, error) {
ctx, cancel := cloud.ContextWithCallTimeout()
defer cancel()
mc := newUrlMapMetricContext("get")
v, err := gce.c.UrlMaps().Get(context.Background(), meta.GlobalKey(name))
v, err := gce.c.UrlMaps().Get(ctx, meta.GlobalKey(name))
return v, mc.Observe(err)
}
// CreateUrlMap creates a url map
func (gce *GCECloud) CreateUrlMap(urlMap *compute.UrlMap) error {
ctx, cancel := cloud.ContextWithCallTimeout()
defer cancel()
mc := newUrlMapMetricContext("create")
return mc.Observe(gce.c.UrlMaps().Insert(context.Background(), meta.GlobalKey(urlMap.Name), urlMap))
return mc.Observe(gce.c.UrlMaps().Insert(ctx, meta.GlobalKey(urlMap.Name), urlMap))
}
// UpdateUrlMap applies the given UrlMap as an update
func (gce *GCECloud) UpdateUrlMap(urlMap *compute.UrlMap) error {
ctx, cancel := cloud.ContextWithCallTimeout()
defer cancel()
mc := newUrlMapMetricContext("update")
return mc.Observe(gce.c.UrlMaps().Update(context.Background(), meta.GlobalKey(urlMap.Name), urlMap))
return mc.Observe(gce.c.UrlMaps().Update(ctx, meta.GlobalKey(urlMap.Name), urlMap))
}
// DeleteUrlMap deletes a url map by name.
func (gce *GCECloud) DeleteUrlMap(name string) error {
ctx, cancel := cloud.ContextWithCallTimeout()
defer cancel()
mc := newUrlMapMetricContext("delete")
return mc.Observe(gce.c.UrlMaps().Delete(context.Background(), meta.GlobalKey(name)))
return mc.Observe(gce.c.UrlMaps().Delete(ctx, meta.GlobalKey(name)))
}
// ListUrlMaps lists all UrlMaps in the project.
func (gce *GCECloud) ListUrlMaps() ([]*compute.UrlMap, error) {
ctx, cancel := cloud.ContextWithCallTimeout()
defer cancel()
mc := newUrlMapMetricContext("list")
v, err := gce.c.UrlMaps().List(context.Background(), filter.None)
v, err := gce.c.UrlMaps().List(ctx, filter.None)
return v, mc.Observe(err)
}

View File

@ -24,6 +24,7 @@ import (
"k8s.io/apimachinery/pkg/types"
"k8s.io/kubernetes/pkg/cloudprovider"
"k8s.io/kubernetes/pkg/cloudprovider/providers/gce/cloud"
"k8s.io/kubernetes/pkg/cloudprovider/providers/gce/cloud/filter"
)
@ -72,8 +73,11 @@ func (gce *GCECloud) GetZoneByNodeName(ctx context.Context, nodeName types.NodeN
// ListZonesInRegion returns all zones in a GCP region
func (gce *GCECloud) ListZonesInRegion(region string) ([]*compute.Zone, error) {
ctx, cancel := cloud.ContextWithCallTimeout()
defer cancel()
mc := newZonesMetricContext("list", region)
list, err := gce.c.Zones().List(context.Background(), filter.Regexp("region", gce.getRegionLink(region)))
list, err := gce.c.Zones().List(ctx, filter.Regexp("region", gce.getRegionLink(region)))
if err != nil {
return nil, mc.Observe(err)
}

View File

@ -50,17 +50,26 @@ type gceRateLimiter struct {
// operations.
func (l *gceRateLimiter) Accept(ctx context.Context, key *cloud.RateLimitKey) error {
if key.Operation == "Get" && key.Service == "Operations" {
ch := make(chan struct{})
go func() {
l.gce.operationPollRateLimiter.Accept()
close(ch)
}()
select {
case <-ch:
break
case <-ctx.Done():
return ctx.Err()
// Wait a minimum amount of time regardless of rate limiter.
rl := &cloud.MinimumRateLimiter{
// Convert flowcontrol.RateLimiter into cloud.RateLimiter
RateLimiter: &cloud.AcceptRateLimiter{
Acceptor: l.gce.operationPollRateLimiter,
},
Minimum: operationPollInterval,
}
return rl.Accept(ctx, key)
}
return nil
}
// CreateGCECloudWithCloud is a helper function to create an instance of GCECloud with the
// given Cloud interface implementation. Typical usage is to use cloud.NewMockGCE to get a
// handle to a mock Cloud instance and then use that for testing.
func CreateGCECloudWithCloud(config *CloudConfig, c cloud.Cloud) (*GCECloud, error) {
gceCloud, err := CreateGCECloud(config)
if err == nil {
gceCloud.c = c
}
return gceCloud, err
}