diff --git a/pkg/cloudprovider/providers/gce/cloud/project.go b/pkg/cloudprovider/providers/gce/cloud/project.go new file mode 100644 index 00000000000..74299e4a23e --- /dev/null +++ b/pkg/cloudprovider/providers/gce/cloud/project.go @@ -0,0 +1,44 @@ +/* +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 cloud + +import ( + "context" + + "k8s.io/kubernetes/pkg/cloudprovider/providers/gce/cloud/meta" +) + +// ProjectRouter routes service calls to the appropriate GCE project. +type ProjectRouter interface { + // ProjectID returns the project ID (non-numeric) to be used for a call + // to an API (version,service). Example tuples: ("ga", "ForwardingRules"), + // ("alpha", "GlobalAddresses"). + // + // This allows for plumbing different service calls to the appropriate + // project, for instance, networking services to a separate project + // than instance management. + ProjectID(ctx context.Context, version meta.Version, service string) string +} + +// SingleProjectRouter routes all service calls to the same project ID. +type SingleProjectRouter struct { + ID string +} + +func (r *SingleProjectRouter) ProjectID(ctx context.Context, version meta.Version, service string) string { + return r.ID +} diff --git a/pkg/cloudprovider/providers/gce/cloud/ratelimit.go b/pkg/cloudprovider/providers/gce/cloud/ratelimit.go new file mode 100644 index 00000000000..948f1d36d89 --- /dev/null +++ b/pkg/cloudprovider/providers/gce/cloud/ratelimit.go @@ -0,0 +1,67 @@ +/* +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 cloud + +import ( + "context" + "time" + + "k8s.io/kubernetes/pkg/cloudprovider/providers/gce/cloud/meta" +) + +// RateLimitKey is a key identifying the operation to be rate limited. The rate limit +// queue will be determined based on the contents of RateKey. +type RateLimitKey struct { + // ProjectID is the non-numeric ID of the project. + ProjectID string + // Operation is the specific method being invoked (e.g. "Get", "List"). + Operation string + // Version is the API version of the call. + Version meta.Version + // Service is the service being invoked (e.g. "Firewalls", "BackendServices") + Service string +} + +// RateLimiter is the interface for a rate limiting policy. +type RateLimiter interface { + // Accept uses the RateLimitKey to derive a sleep time for the calling + // goroutine. This call will block until the operation is ready for + // execution. + // + // Accept returns an error if the given context ctx was canceled + // while waiting for acceptance into the queue. + Accept(ctx context.Context, key *RateLimitKey) error +} + +// NopRateLimiter is a rate limiter that performs no rate limiting. +type NopRateLimiter struct { +} + +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 +} diff --git a/pkg/cloudprovider/providers/gce/cloud/service.go b/pkg/cloudprovider/providers/gce/cloud/service.go new file mode 100644 index 00000000000..8a6c0a6cf95 --- /dev/null +++ b/pkg/cloudprovider/providers/gce/cloud/service.go @@ -0,0 +1,79 @@ +/* +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 cloud + +import ( + "context" + "fmt" + + alpha "google.golang.org/api/compute/v0.alpha" + beta "google.golang.org/api/compute/v0.beta" + ga "google.golang.org/api/compute/v1" +) + +// Service is the top-level adapter for all of the different compute API +// versions. +type Service struct { + GA *ga.Service + Alpha *alpha.Service + Beta *beta.Service + ProjectRouter ProjectRouter + RateLimiter RateLimiter +} + +// wrapOperation wraps a GCE anyOP in a version generic operation type. +func (g *Service) wrapOperation(anyOp interface{}) (operation, error) { + switch o := anyOp.(type) { + case *ga.Operation: + r, err := ParseResourceURL(o.SelfLink) + if err != nil { + return nil, err + } + return &gaOperation{g, o, r.ProjectID}, nil + case *alpha.Operation: + r, err := ParseResourceURL(o.SelfLink) + if err != nil { + return nil, err + } + return &alphaOperation{g, o, r.ProjectID}, nil + case *beta.Operation: + r, err := ParseResourceURL(o.SelfLink) + if err != nil { + return nil, err + } + return &betaOperation{g, o, r.ProjectID}, nil + default: + return nil, fmt.Errorf("invalid type %T", anyOp) + } +} + +// WaitForCompletion of a long running operation. This will poll the state of +// GCE for the completion status of the given operation. genericOp can be one +// of alpha, beta, ga Operation types. +func (g *Service) WaitForCompletion(ctx context.Context, genericOp interface{}) error { + op, err := g.wrapOperation(genericOp) + if err != nil { + return err + } + for done, err := op.isDone(ctx); !done; done, err = op.isDone(ctx) { + if err != nil { + return err + } + g.RateLimiter.Accept(ctx, op.rateLimitKey()) + } + return nil +}