Removing cloud directory

pull/564/head
Bowei Du 2018-12-13 17:28:16 -08:00
parent b97092ca8f
commit 7f8dae1abc
30 changed files with 0 additions and 22702 deletions

View File

@ -1,67 +0,0 @@
load("@io_bazel_rules_go//go:def.bzl", "go_library", "go_test")
go_library(
name = "go_default_library",
srcs = [
"constants.go",
"context.go",
"doc.go",
"gce_projects.go",
"gen.go",
"op.go",
"project.go",
"ratelimit.go",
"service.go",
"utils.go",
],
importpath = "k8s.io/kubernetes/pkg/cloudprovider/providers/gce/cloud",
visibility = ["//visibility:public"],
deps = [
"//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",
"//vendor/google.golang.org/api/compute/v1:go_default_library",
"//vendor/google.golang.org/api/googleapi:go_default_library",
"//vendor/k8s.io/klog:go_default_library",
],
)
go_test(
name = "go_default_test",
srcs = [
"gen_test.go",
"mock_test.go",
"ratelimit_test.go",
"service_test.go",
"utils_test.go",
],
embed = [":go_default_library"],
deps = [
"//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",
"//vendor/google.golang.org/api/compute/v1:go_default_library",
],
)
filegroup(
name = "package-srcs",
srcs = glob(["**"]),
tags = ["automanaged"],
visibility = ["//visibility:private"],
)
filegroup(
name = "all-srcs",
srcs = [
":package-srcs",
"//pkg/cloudprovider/providers/gce/cloud/filter:all-srcs",
"//pkg/cloudprovider/providers/gce/cloud/gen:all-srcs",
"//pkg/cloudprovider/providers/gce/cloud/meta:all-srcs",
"//pkg/cloudprovider/providers/gce/cloud/mock:all-srcs",
],
tags = ["automanaged"],
visibility = ["//visibility:public"],
)

View File

@ -1,55 +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 cloud
import (
"strings"
)
// NetworkTier represents the Network Service Tier used by a resource
type NetworkTier string
// LbScheme represents the possible types of load balancers
type LbScheme string
const (
NetworkTierStandard NetworkTier = "Standard"
NetworkTierPremium NetworkTier = "Premium"
NetworkTierDefault NetworkTier = NetworkTierPremium
SchemeExternal LbScheme = "EXTERNAL"
SchemeInternal LbScheme = "INTERNAL"
)
// ToGCEValue converts NetworkTier to a string that we can populate the
// NetworkTier field of GCE objects, including ForwardingRules and Addresses.
func (n NetworkTier) ToGCEValue() string {
return strings.ToUpper(string(n))
}
// NetworkTierGCEValueToType converts the value of the NetworkTier field of a
// GCE object to the NetworkTier type.
func NetworkTierGCEValueToType(s string) NetworkTier {
switch s {
case NetworkTierStandard.ToGCEValue():
return NetworkTierStandard
case NetworkTierPremium.ToGCEValue():
return NetworkTierPremium
default:
return NetworkTier(s)
}
}

View File

@ -1,31 +0,0 @@
/*
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

@ -1,117 +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 cloud implements a more golang friendly interface to the GCE compute
// API. The code in this package is generated automatically via the generator
// implemented in "gen/main.go". The code generator creates the basic CRUD
// actions for the given resource: "Insert", "Get", "List" and "Delete".
// Additional methods by customizing the ServiceInfo object (see below).
// Generated code includes a full mock of the GCE compute API.
//
// Usage
//
// The root of the GCE compute API is the interface "Cloud". Code written using
// Cloud can be used against the actual implementation "GCE" or "MockGCE".
//
// func foo(cloud Cloud) {
// igs, err := cloud.InstanceGroups().List(ctx, "us-central1-b", filter.None)
// ...
// }
// // Run foo against the actual cloud.
// foo(NewGCE(&Service{...}))
// // Run foo with a mock.
// foo(NewMockGCE())
//
// Rate limiting and routing
//
// The generated code allows for custom policies for operation rate limiting
// and GCE project routing. See RateLimiter and ProjectRouter for more details.
//
// Mocks
//
// Mocks are automatically generated for each type implementing basic logic for
// resource manipulation. This eliminates the boilerplate required to mock GCE
// functionality. Each method will also have a corresponding "xxxHook"
// function generated in the mock structure where unit test code can hook the
// execution of the method.
//
// Mocks for different versions of the same service will share the same set of
// objects, i.e. an alpha object will be visible with beta and GA methods.
// Note that translation is done with JSON serialization between the API versions.
//
// Changing service code generation
//
// The list of services to generate is contained in "meta/meta.go". To add a
// service, add an entry to the list "meta.AllServices". An example entry:
//
// &ServiceInfo{
// Object: "InstanceGroup", // Name of the object type.
// Service: "InstanceGroups", // Name of the service.
// Resource: "instanceGroups", // Lowercase resource name (as appears in the URL).
// version: meta.VersionAlpha, // API version (one entry per version is needed).
// keyType: Zonal, // What kind of resource this is.
// serviceType: reflect.TypeOf(&alpha.InstanceGroupsService{}), // Associated golang type.
// additionalMethods: []string{ // Additional methods to generate code for.
// "SetNamedPorts",
// },
// options: <options> // Or'd ("|") together.
// }
//
// Read-only objects
//
// Services such as Regions and Zones do not allow for mutations. Specify
// "ReadOnly" in ServiceInfo.options to omit the mutation methods.
//
// Adding custom methods
//
// Some methods that may not be properly handled by the generated code. To enable
// addition of custom code to the generated mocks, set the "CustomOps" option
// in "meta.ServiceInfo" entry. This will make the generated service interface
// embed a "<ServiceName>Ops" interface. This interface MUST be written by hand
// and contain the custom method logic. Corresponding methods must be added to
// the corresponding Mockxxx and GCExxx struct types.
//
// // In "meta/meta.go":
// &ServiceInfo{
// Object: "InstanceGroup",
// ...
// options: CustomOps,
// }
//
// // In the generated code "gen.go":
// type InstanceGroups interface {
// InstanceGroupsOps // Added by CustomOps option.
// ...
// }
//
// // In hand written file:
// type InstanceGroupsOps interface {
// MyMethod()
// }
//
// func (mock *MockInstanceGroups) MyMethod() {
// // Custom mock implementation.
// }
//
// func (gce *GCEInstanceGroups) MyMethod() {
// // Custom implementation.
// }
//
// Update generated codes
//
// Run hack/update-cloudprovider-gce.sh to update the generated codes.
//
package cloud

View File

@ -1,29 +0,0 @@
load("@io_bazel_rules_go//go:def.bzl", "go_library", "go_test")
go_library(
name = "go_default_library",
srcs = ["filter.go"],
importpath = "k8s.io/kubernetes/pkg/cloudprovider/providers/gce/cloud/filter",
visibility = ["//visibility:public"],
deps = ["//vendor/k8s.io/klog:go_default_library"],
)
go_test(
name = "go_default_test",
srcs = ["filter_test.go"],
embed = [":go_default_library"],
)
filegroup(
name = "package-srcs",
srcs = glob(["**"]),
tags = ["automanaged"],
visibility = ["//visibility:private"],
)
filegroup(
name = "all-srcs",
srcs = [":package-srcs"],
tags = ["automanaged"],
visibility = ["//visibility:public"],
)

View File

@ -1,303 +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 filter encapsulates the filter argument to compute API calls.
//
// // List all global addresses (no filter).
// c.GlobalAddresses().List(ctx, filter.None)
//
// // List global addresses filtering for name matching "abc.*".
// c.GlobalAddresses().List(ctx, filter.Regexp("name", "abc.*"))
//
// // List on multiple conditions.
// f := filter.Regexp("name", "homer.*").AndNotRegexp("name", "homers")
// c.GlobalAddresses().List(ctx, f)
package filter
import (
"errors"
"fmt"
"reflect"
"regexp"
"strings"
"k8s.io/klog"
)
var (
// None indicates that the List result set should not be filter (i.e.
// return all values).
None *F
)
// Regexp returns a filter for fieldName matches regexp v.
func Regexp(fieldName, v string) *F {
return (&F{}).AndRegexp(fieldName, v)
}
// NotRegexp returns a filter for fieldName not matches regexp v.
func NotRegexp(fieldName, v string) *F {
return (&F{}).AndNotRegexp(fieldName, v)
}
// EqualInt returns a filter for fieldName ~ v.
func EqualInt(fieldName string, v int) *F {
return (&F{}).AndEqualInt(fieldName, v)
}
// NotEqualInt returns a filter for fieldName != v.
func NotEqualInt(fieldName string, v int) *F {
return (&F{}).AndNotEqualInt(fieldName, v)
}
// EqualBool returns a filter for fieldName == v.
func EqualBool(fieldName string, v bool) *F {
return (&F{}).AndEqualBool(fieldName, v)
}
// NotEqualBool returns a filter for fieldName != v.
func NotEqualBool(fieldName string, v bool) *F {
return (&F{}).AndNotEqualBool(fieldName, v)
}
// F is a filter to be used with List() operations.
//
// From the compute API description:
//
// Sets a filter {expression} for filtering listed resources. Your {expression}
// must be in the format: field_name comparison_string literal_string.
//
// The field_name is the name of the field you want to compare. Only atomic field
// types are supported (string, number, boolean). The comparison_string must be
// either eq (equals) or ne (not equals). The literal_string is the string value
// to filter to. The literal value must be valid for the type of field you are
// filtering by (string, number, boolean). For string fields, the literal value is
// interpreted as a regular expression using RE2 syntax. The literal value must
// match the entire field.
//
// For example, to filter for instances that do not have a name of
// example-instance, you would use name ne example-instance.
//
// You can filter on nested fields. For example, you could filter on instances
// that have set the scheduling.automaticRestart field to true. Use filtering on
// nested fields to take advantage of labels to organize and search for results
// based on label values.
//
// To filter on multiple expressions, provide each separate expression within
// parentheses. For example, (scheduling.automaticRestart eq true)
// (zone eq us-central1-f). Multiple expressions are treated as AND expressions,
// meaning that resources must match all expressions to pass the filters.
type F struct {
predicates []filterPredicate
}
// And joins two filters together.
func (fl *F) And(rest *F) *F {
fl.predicates = append(fl.predicates, rest.predicates...)
return fl
}
// AndRegexp adds a field match string predicate.
func (fl *F) AndRegexp(fieldName, v string) *F {
fl.predicates = append(fl.predicates, filterPredicate{fieldName: fieldName, op: equals, s: &v})
return fl
}
// AndNotRegexp adds a field not match string predicate.
func (fl *F) AndNotRegexp(fieldName, v string) *F {
fl.predicates = append(fl.predicates, filterPredicate{fieldName: fieldName, op: notEquals, s: &v})
return fl
}
// AndEqualInt adds a field == int predicate.
func (fl *F) AndEqualInt(fieldName string, v int) *F {
fl.predicates = append(fl.predicates, filterPredicate{fieldName: fieldName, op: equals, i: &v})
return fl
}
// AndNotEqualInt adds a field != int predicate.
func (fl *F) AndNotEqualInt(fieldName string, v int) *F {
fl.predicates = append(fl.predicates, filterPredicate{fieldName: fieldName, op: notEquals, i: &v})
return fl
}
// AndEqualBool adds a field == bool predicate.
func (fl *F) AndEqualBool(fieldName string, v bool) *F {
fl.predicates = append(fl.predicates, filterPredicate{fieldName: fieldName, op: equals, b: &v})
return fl
}
// AndNotEqualBool adds a field != bool predicate.
func (fl *F) AndNotEqualBool(fieldName string, v bool) *F {
fl.predicates = append(fl.predicates, filterPredicate{fieldName: fieldName, op: notEquals, b: &v})
return fl
}
func (fl *F) String() string {
if len(fl.predicates) == 1 {
return fl.predicates[0].String()
}
var pl []string
for _, p := range fl.predicates {
pl = append(pl, "("+p.String()+")")
}
return strings.Join(pl, " ")
}
// Match returns true if the F as specifies matches the given object. This
// is used by the Mock implementations to perform filtering and SHOULD NOT be
// used in production code as it is not well-tested to be equivalent to the
// actual compute API.
func (fl *F) Match(obj interface{}) bool {
if fl == nil {
return true
}
for _, p := range fl.predicates {
if !p.match(obj) {
return false
}
}
return true
}
type filterOp int
const (
equals filterOp = iota
notEquals filterOp = iota
)
// filterPredicate is an individual predicate for a fieldName and value.
type filterPredicate struct {
fieldName string
op filterOp
s *string
i *int
b *bool
}
func (fp *filterPredicate) String() string {
var op string
switch fp.op {
case equals:
op = "eq"
case notEquals:
op = "ne"
default:
op = "invalidOp"
}
var value string
switch {
case fp.s != nil:
// There does not seem to be any sort of escaping as specified in the
// document. This means it's possible to create malformed expressions.
value = *fp.s
case fp.i != nil:
value = fmt.Sprintf("%d", *fp.i)
case fp.b != nil:
value = fmt.Sprintf("%t", *fp.b)
default:
value = "invalidValue"
}
return fmt.Sprintf("%s %s %s", fp.fieldName, op, value)
}
func (fp *filterPredicate) match(o interface{}) bool {
v, err := extractValue(fp.fieldName, o)
klog.V(6).Infof("extractValue(%q, %#v) = %v, %v", fp.fieldName, o, v, err)
if err != nil {
return false
}
var match bool
switch x := v.(type) {
case string:
if fp.s == nil {
return false
}
re, err := regexp.Compile(*fp.s)
if err != nil {
klog.Errorf("Match regexp %q is invalid: %v", *fp.s, err)
return false
}
match = re.Match([]byte(x))
case int:
if fp.i == nil {
return false
}
match = x == *fp.i
case bool:
if fp.b == nil {
return false
}
match = x == *fp.b
}
switch fp.op {
case equals:
return match
case notEquals:
return !match
}
return false
}
// snakeToCamelCase converts from "names_like_this" to "NamesLikeThis" to
// interoperate between proto and Golang naming conventions.
func snakeToCamelCase(s string) string {
parts := strings.Split(s, "_")
var ret string
for _, x := range parts {
ret += strings.Title(x)
}
return ret
}
// extractValue returns the value of the field named by path in object o if it exists.
func extractValue(path string, o interface{}) (interface{}, error) {
parts := strings.Split(path, ".")
for _, f := range parts {
v := reflect.ValueOf(o)
// Dereference Ptr to handle *struct.
if v.Kind() == reflect.Ptr {
if v.IsNil() {
return nil, errors.New("field is nil")
}
v = v.Elem()
}
if v.Kind() != reflect.Struct {
return nil, fmt.Errorf("cannot get field from non-struct (%T)", o)
}
v = v.FieldByName(snakeToCamelCase(f))
if !v.IsValid() {
return nil, fmt.Errorf("cannot get field %q as it is not a valid field in %T", f, o)
}
if !v.CanInterface() {
return nil, fmt.Errorf("cannot get field %q in obj of type %T", f, o)
}
o = v.Interface()
}
switch o.(type) {
case string, int, bool:
return o, nil
}
return nil, fmt.Errorf("unhandled object of type %T", o)
}

View File

@ -1,176 +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 filter
import (
"reflect"
"testing"
)
func TestFilterToString(t *testing.T) {
t.Parallel()
for _, tc := range []struct {
f *F
want string
}{
{Regexp("field1", "abc"), `field1 eq abc`},
{NotRegexp("field1", "abc"), `field1 ne abc`},
{EqualInt("field1", 13), "field1 eq 13"},
{NotEqualInt("field1", 13), "field1 ne 13"},
{EqualBool("field1", true), "field1 eq true"},
{NotEqualBool("field1", true), "field1 ne true"},
{Regexp("field1", "abc").AndRegexp("field2", "def"), `(field1 eq abc) (field2 eq def)`},
{Regexp("field1", "abc").AndNotEqualInt("field2", 17), `(field1 eq abc) (field2 ne 17)`},
{Regexp("field1", "abc").And(EqualInt("field2", 17)), `(field1 eq abc) (field2 eq 17)`},
} {
if tc.f.String() != tc.want {
t.Errorf("filter %#v String() = %q, want %q", tc.f, tc.f.String(), tc.want)
}
}
}
func TestFilterMatch(t *testing.T) {
t.Parallel()
type inner struct {
X string
}
type S struct {
S string
I int
B bool
Unhandled struct{}
NestedField *inner
}
for _, tc := range []struct {
f *F
o interface{}
want bool
}{
{f: None, o: &S{}, want: true},
{f: Regexp("s", "abc"), o: &S{}},
{f: EqualInt("i", 10), o: &S{}},
{f: EqualBool("b", true), o: &S{}},
{f: NotRegexp("s", "abc"), o: &S{}, want: true},
{f: NotEqualInt("i", 10), o: &S{}, want: true},
{f: NotEqualBool("b", true), o: &S{}, want: true},
{f: Regexp("s", "abc").AndEqualBool("b", true), o: &S{}},
{f: Regexp("s", "abc"), o: &S{S: "abc"}, want: true},
{f: Regexp("s", "a.*"), o: &S{S: "abc"}, want: true},
{f: Regexp("s", "a((("), o: &S{S: "abc"}},
{f: NotRegexp("s", "abc"), o: &S{S: "abc"}},
{f: EqualInt("i", 10), o: &S{I: 11}},
{f: EqualInt("i", 10), o: &S{I: 10}, want: true},
{f: Regexp("s", "abc").AndEqualBool("b", true), o: &S{S: "abc"}},
{f: Regexp("s", "abcd").AndEqualBool("b", true), o: &S{S: "abc"}},
{f: Regexp("s", "abc").AndEqualBool("b", true), o: &S{S: "abc", B: true}, want: true},
{f: Regexp("s", "abc").And(EqualBool("b", true)), o: &S{S: "abc", B: true}, want: true},
{f: Regexp("unhandled", "xyz"), o: &S{}},
{f: Regexp("nested_field.x", "xyz"), o: &S{}},
{f: Regexp("nested_field.x", "xyz"), o: &S{NestedField: &inner{"xyz"}}, want: true},
{f: NotRegexp("nested_field.x", "xyz"), o: &S{NestedField: &inner{"xyz"}}},
{f: Regexp("nested_field.y", "xyz"), o: &S{NestedField: &inner{"xyz"}}},
{f: Regexp("nested_field", "xyz"), o: &S{NestedField: &inner{"xyz"}}},
} {
got := tc.f.Match(tc.o)
if got != tc.want {
t.Errorf("%v: Match(%+v) = %v, want %v", tc.f, tc.o, got, tc.want)
}
}
}
func TestFilterSnakeToCamelCase(t *testing.T) {
t.Parallel()
for _, tc := range []struct {
s string
want string
}{
{"", ""},
{"abc", "Abc"},
{"_foo", "Foo"},
{"a_b_c", "ABC"},
{"a_BC_def", "ABCDef"},
{"a_Bc_def", "ABcDef"},
} {
got := snakeToCamelCase(tc.s)
if got != tc.want {
t.Errorf("snakeToCamelCase(%q) = %q, want %q", tc.s, got, tc.want)
}
}
}
func TestFilterExtractValue(t *testing.T) {
t.Parallel()
type nest2 struct {
Y string
}
type nest struct {
X string
Nest2 nest2
}
st := &struct {
S string
I int
F bool
Nest nest
NestPtr *nest
Unhandled float64
}{
"abc",
13,
true,
nest{"xyz", nest2{"zzz"}},
&nest{"yyy", nest2{}},
0.0,
}
for _, tc := range []struct {
path string
o interface{}
want interface{}
wantErr bool
}{
{path: "s", o: st, want: "abc"},
{path: "i", o: st, want: 13},
{path: "f", o: st, want: true},
{path: "nest.x", o: st, want: "xyz"},
{path: "nest_ptr.x", o: st, want: "yyy"},
// Error cases.
{path: "", o: st, wantErr: true},
{path: "no_such_field", o: st, wantErr: true},
{path: "s.invalid_type", o: st, wantErr: true},
{path: "unhandled", o: st, wantErr: true},
{path: "nest.x", o: &struct{ Nest *nest }{}, wantErr: true},
} {
o, err := extractValue(tc.path, tc.o)
gotErr := err != nil
if gotErr != tc.wantErr {
t.Errorf("extractValue(%v, %+v) = %v, %v; gotErr = %v, tc.wantErr = %v", tc.path, tc.o, o, err, gotErr, tc.wantErr)
}
if err != nil {
continue
}
if !reflect.DeepEqual(o, tc.want) {
t.Errorf("extractValue(%v, %+v) = %v, nil; want %v, nil", tc.path, tc.o, o, tc.want)
}
}
}

View File

@ -1,99 +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 cloud
import (
"context"
"fmt"
"net/http"
compute "google.golang.org/api/compute/v1"
"google.golang.org/api/googleapi"
"k8s.io/kubernetes/pkg/cloudprovider/providers/gce/cloud/meta"
)
// ProjectsOps is the manually implemented methods for the Projects service.
type ProjectsOps interface {
Get(ctx context.Context, projectID string) (*compute.Project, error)
SetCommonInstanceMetadata(ctx context.Context, projectID string, m *compute.Metadata) error
}
// MockProjectOpsState is stored in the mock.X field.
type MockProjectOpsState struct {
metadata map[string]*compute.Metadata
}
// Get a project by projectID.
func (m *MockProjects) Get(ctx context.Context, projectID string) (*compute.Project, error) {
m.Lock.Lock()
defer m.Lock.Unlock()
if p, ok := m.Objects[*meta.GlobalKey(projectID)]; ok {
return p.ToGA(), nil
}
return nil, &googleapi.Error{
Code: http.StatusNotFound,
Message: fmt.Sprintf("MockProjects %v not found", projectID),
}
}
// Get a project by projectID.
func (g *GCEProjects) Get(ctx context.Context, projectID string) (*compute.Project, error) {
rk := &RateLimitKey{
ProjectID: projectID,
Operation: "Get",
Version: meta.Version("ga"),
Service: "Projects",
}
if err := g.s.RateLimiter.Accept(ctx, rk); err != nil {
return nil, err
}
call := g.s.GA.Projects.Get(projectID)
call.Context(ctx)
return call.Do()
}
// SetCommonInstanceMetadata for a given project.
func (m *MockProjects) SetCommonInstanceMetadata(ctx context.Context, projectID string, meta *compute.Metadata) error {
if m.X == nil {
m.X = &MockProjectOpsState{metadata: map[string]*compute.Metadata{}}
}
state := m.X.(*MockProjectOpsState)
state.metadata[projectID] = meta
return nil
}
// SetCommonInstanceMetadata for a given project.
func (g *GCEProjects) SetCommonInstanceMetadata(ctx context.Context, projectID string, m *compute.Metadata) error {
rk := &RateLimitKey{
ProjectID: projectID,
Operation: "SetCommonInstanceMetadata",
Version: meta.Version("ga"),
Service: "Projects",
}
if err := g.s.RateLimiter.Accept(ctx, rk); err != nil {
return err
}
call := g.s.GA.Projects.SetCommonInstanceMetadata(projectID, m)
call.Context(ctx)
op, err := call.Do()
if err != nil {
return err
}
return g.s.WaitForCompletion(ctx, op)
}

File diff suppressed because it is too large Load Diff

View File

@ -1,29 +0,0 @@
load("@io_bazel_rules_go//go:def.bzl", "go_binary", "go_library")
go_library(
name = "go_default_library",
srcs = ["main.go"],
importpath = "k8s.io/kubernetes/pkg/cloudprovider/providers/gce/cloud/gen",
visibility = ["//visibility:private"],
deps = ["//pkg/cloudprovider/providers/gce/cloud/meta:go_default_library"],
)
go_binary(
name = "gen",
embed = [":go_default_library"],
visibility = ["//visibility:public"],
)
filegroup(
name = "package-srcs",
srcs = glob(["**"]),
tags = ["automanaged"],
visibility = ["//visibility:private"],
)
filegroup(
name = "all-srcs",
srcs = [":package-srcs"],
tags = ["automanaged"],
visibility = ["//visibility:public"],
)

File diff suppressed because it is too large Load Diff

File diff suppressed because it is too large Load Diff

View File

@ -1,39 +0,0 @@
load("@io_bazel_rules_go//go:def.bzl", "go_library", "go_test")
go_library(
name = "go_default_library",
srcs = [
"doc.go",
"key.go",
"meta.go",
"method.go",
"service.go",
],
importpath = "k8s.io/kubernetes/pkg/cloudprovider/providers/gce/cloud/meta",
visibility = ["//visibility:public"],
deps = [
"//vendor/google.golang.org/api/compute/v0.alpha:go_default_library",
"//vendor/google.golang.org/api/compute/v0.beta:go_default_library",
"//vendor/google.golang.org/api/compute/v1:go_default_library",
],
)
go_test(
name = "go_default_test",
srcs = ["key_test.go"],
embed = [":go_default_library"],
)
filegroup(
name = "package-srcs",
srcs = glob(["**"]),
tags = ["automanaged"],
visibility = ["//visibility:private"],
)
filegroup(
name = "all-srcs",
srcs = [":package-srcs"],
tags = ["automanaged"],
visibility = ["//visibility:public"],
)

View File

@ -1,19 +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 meta contains the meta description of the GCE cloud types to
// generate code for.
package meta

View File

@ -1,108 +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 meta
import (
"fmt"
"regexp"
)
// Key for a GCP resource.
type Key struct {
Name string
Zone string
Region string
}
// KeyType is the type of the key.
type KeyType string
const (
// Zonal key type.
Zonal = "zonal"
// Regional key type.
Regional = "regional"
// Global key type.
Global = "global"
)
var (
// locationRegexp is the format of regions/zone names in GCE.
locationRegexp = regexp.MustCompile("^[a-z](?:[-a-z0-9]+)?$")
)
// ZonalKey returns the key for a zonal resource.
func ZonalKey(name, zone string) *Key {
return &Key{name, zone, ""}
}
// RegionalKey returns the key for a regional resource.
func RegionalKey(name, region string) *Key {
return &Key{name, "", region}
}
// GlobalKey returns the key for a global resource.
func GlobalKey(name string) *Key {
return &Key{name, "", ""}
}
// Type returns the type of the key.
func (k *Key) Type() KeyType {
switch {
case k.Zone != "":
return Zonal
case k.Region != "":
return Regional
default:
return Global
}
}
// String returns a string representation of the key.
func (k Key) String() string {
switch k.Type() {
case Zonal:
return fmt.Sprintf("Key{%q, zone: %q}", k.Name, k.Zone)
case Regional:
return fmt.Sprintf("Key{%q, region: %q}", k.Name, k.Region)
default:
return fmt.Sprintf("Key{%q}", k.Name)
}
}
// Valid is true if the key is valid.
func (k *Key) Valid() bool {
if k.Zone != "" && k.Region != "" {
return false
}
switch {
case k.Region != "":
return locationRegexp.Match([]byte(k.Region))
case k.Zone != "":
return locationRegexp.Match([]byte(k.Zone))
}
return true
}
// KeysToMap creates a map[Key]bool from a list of keys.
func KeysToMap(keys ...Key) map[Key]bool {
ret := map[Key]bool{}
for _, k := range keys {
ret[k] = true
}
return ret
}

View File

@ -1,76 +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 meta
import (
"testing"
)
func TestKeyType(t *testing.T) {
t.Parallel()
for _, tc := range []struct {
key *Key
want KeyType
}{
{GlobalKey("abc"), Global},
{ZonalKey("abc", "us-central1-b"), Zonal},
{RegionalKey("abc", "us-central1"), Regional},
} {
if tc.key.Type() != tc.want {
t.Errorf("key.Type() == %v, want %v", tc.key.Type(), tc.want)
}
}
}
func TestKeyString(t *testing.T) {
t.Parallel()
for _, k := range []*Key{
GlobalKey("abc"),
RegionalKey("abc", "us-central1"),
ZonalKey("abc", "us-central1-b"),
} {
if k.String() == "" {
t.Errorf(`k.String() = "", want non-empty`)
}
}
}
func TestKeyValid(t *testing.T) {
t.Parallel()
region := "us-central1"
zone := "us-central1-b"
for _, tc := range []struct {
key *Key
want bool
}{
{GlobalKey("abc"), true},
{RegionalKey("abc", region), true},
{ZonalKey("abc", zone), true},
{RegionalKey("abc", "/invalid/"), false},
{ZonalKey("abc", "/invalid/"), false},
{&Key{"abc", zone, region}, false},
} {
got := tc.key.Valid()
if got != tc.want {
t.Errorf("key %+v; key.Valid() = %v, want %v", tc.key, got, tc.want)
}
}
}

View File

@ -1,440 +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 meta
import (
"reflect"
alpha "google.golang.org/api/compute/v0.alpha"
beta "google.golang.org/api/compute/v0.beta"
ga "google.golang.org/api/compute/v1"
)
// Version of the API (ga, alpha, beta).
type Version string
const (
// NoGet prevents the Get() method from being generated.
NoGet = 1 << iota
// NoList prevents the List() method from being generated.
NoList = 1 << iota
// NoDelete prevents the Delete() method from being generated.
NoDelete = 1 << iota
// NoInsert prevents the Insert() method from being generated.
NoInsert = 1 << iota
// CustomOps specifies that an empty interface xxxOps will be generated to
// enable custom method calls to be attached to the generated service
// interface.
CustomOps = 1 << iota
// AggregatedList will generated a method for AggregatedList().
AggregatedList = 1 << iota
// ReadOnly specifies that the given resource is read-only and should not
// have insert() or delete() methods generated for the wrapper.
ReadOnly = NoDelete | NoInsert
// VersionGA is the API version in compute.v1.
VersionGA Version = "ga"
// VersionAlpha is the API version in computer.v0.alpha.
VersionAlpha Version = "alpha"
// VersionBeta is the API version in computer.v0.beta.
VersionBeta Version = "beta"
)
// AllVersions is a list of all versions of the GCE API.
var AllVersions = []Version{
VersionGA,
VersionAlpha,
VersionBeta,
}
// AllServices are a list of all the services to generate code for. Keep
// this list in lexiographical order by object type.
var AllServices = []*ServiceInfo{
{
Object: "Address",
Service: "Addresses",
Resource: "addresses",
keyType: Regional,
serviceType: reflect.TypeOf(&ga.AddressesService{}),
},
{
Object: "Address",
Service: "Addresses",
Resource: "addresses",
version: VersionAlpha,
keyType: Regional,
serviceType: reflect.TypeOf(&alpha.AddressesService{}),
},
{
Object: "Address",
Service: "Addresses",
Resource: "addresses",
version: VersionBeta,
keyType: Regional,
serviceType: reflect.TypeOf(&beta.AddressesService{}),
},
{
Object: "Address",
Service: "GlobalAddresses",
Resource: "addresses",
keyType: Global,
serviceType: reflect.TypeOf(&ga.GlobalAddressesService{}),
},
{
Object: "BackendService",
Service: "BackendServices",
Resource: "backendServices",
keyType: Global,
serviceType: reflect.TypeOf(&ga.BackendServicesService{}),
additionalMethods: []string{
"GetHealth",
"Patch",
"Update",
},
},
{
Object: "BackendService",
Service: "BackendServices",
Resource: "backendServices",
version: VersionBeta,
keyType: Global,
serviceType: reflect.TypeOf(&beta.BackendServicesService{}),
additionalMethods: []string{
"Update",
"SetSecurityPolicy",
},
},
{
Object: "BackendService",
Service: "BackendServices",
Resource: "backendServices",
version: VersionAlpha,
keyType: Global,
serviceType: reflect.TypeOf(&alpha.BackendServicesService{}),
additionalMethods: []string{
"Update",
"SetSecurityPolicy",
},
},
{
Object: "BackendService",
Service: "RegionBackendServices",
Resource: "backendServices",
version: VersionGA,
keyType: Regional,
serviceType: reflect.TypeOf(&ga.RegionBackendServicesService{}),
additionalMethods: []string{
"GetHealth",
"Update",
},
},
{
Object: "BackendService",
Service: "RegionBackendServices",
Resource: "backendServices",
version: VersionAlpha,
keyType: Regional,
serviceType: reflect.TypeOf(&alpha.RegionBackendServicesService{}),
additionalMethods: []string{
"GetHealth",
"Update",
},
},
{
Object: "Disk",
Service: "Disks",
Resource: "disks",
keyType: Zonal,
serviceType: reflect.TypeOf(&ga.DisksService{}),
additionalMethods: []string{
"Resize",
},
},
{
Object: "Disk",
Service: "RegionDisks",
Resource: "disks",
version: VersionGA,
keyType: Regional,
serviceType: reflect.TypeOf(&ga.RegionDisksService{}),
additionalMethods: []string{
"Resize",
},
},
{
Object: "Firewall",
Service: "Firewalls",
Resource: "firewalls",
keyType: Global,
serviceType: reflect.TypeOf(&ga.FirewallsService{}),
additionalMethods: []string{
"Update",
},
},
{
Object: "ForwardingRule",
Service: "ForwardingRules",
Resource: "forwardingRules",
keyType: Regional,
serviceType: reflect.TypeOf(&ga.ForwardingRulesService{}),
},
{
Object: "ForwardingRule",
Service: "ForwardingRules",
Resource: "forwardingRules",
version: VersionAlpha,
keyType: Regional,
serviceType: reflect.TypeOf(&alpha.ForwardingRulesService{}),
},
{
Object: "ForwardingRule",
Service: "GlobalForwardingRules",
Resource: "forwardingRules",
keyType: Global,
serviceType: reflect.TypeOf(&ga.GlobalForwardingRulesService{}),
additionalMethods: []string{
"SetTarget",
},
},
{
Object: "HealthCheck",
Service: "HealthChecks",
Resource: "healthChecks",
keyType: Global,
serviceType: reflect.TypeOf(&ga.HealthChecksService{}),
additionalMethods: []string{
"Update",
},
},
{
Object: "HealthCheck",
Service: "HealthChecks",
Resource: "healthChecks",
version: VersionBeta,
keyType: Global,
serviceType: reflect.TypeOf(&beta.HealthChecksService{}),
additionalMethods: []string{
"Update",
},
},
{
Object: "HealthCheck",
Service: "HealthChecks",
Resource: "healthChecks",
version: VersionAlpha,
keyType: Global,
serviceType: reflect.TypeOf(&alpha.HealthChecksService{}),
additionalMethods: []string{
"Update",
},
},
{
Object: "HttpHealthCheck",
Service: "HttpHealthChecks",
Resource: "httpHealthChecks",
keyType: Global,
serviceType: reflect.TypeOf(&ga.HttpHealthChecksService{}),
additionalMethods: []string{
"Update",
},
},
{
Object: "HttpsHealthCheck",
Service: "HttpsHealthChecks",
Resource: "httpsHealthChecks",
keyType: Global,
serviceType: reflect.TypeOf(&ga.HttpsHealthChecksService{}),
additionalMethods: []string{
"Update",
},
},
{
Object: "InstanceGroup",
Service: "InstanceGroups",
Resource: "instanceGroups",
keyType: Zonal,
serviceType: reflect.TypeOf(&ga.InstanceGroupsService{}),
additionalMethods: []string{
"AddInstances",
"ListInstances",
"RemoveInstances",
"SetNamedPorts",
},
},
{
Object: "Instance",
Service: "Instances",
Resource: "instances",
keyType: Zonal,
serviceType: reflect.TypeOf(&ga.InstancesService{}),
additionalMethods: []string{
"AttachDisk",
"DetachDisk",
},
},
{
Object: "Instance",
Service: "Instances",
Resource: "instances",
version: VersionBeta,
keyType: Zonal,
serviceType: reflect.TypeOf(&beta.InstancesService{}),
additionalMethods: []string{
"AttachDisk",
"DetachDisk",
"UpdateNetworkInterface",
},
},
{
Object: "Instance",
Service: "Instances",
Resource: "instances",
version: VersionAlpha,
keyType: Zonal,
serviceType: reflect.TypeOf(&alpha.InstancesService{}),
additionalMethods: []string{
"AttachDisk",
"DetachDisk",
"UpdateNetworkInterface",
},
},
{
Object: "NetworkEndpointGroup",
Service: "NetworkEndpointGroups",
Resource: "networkEndpointGroups",
version: VersionAlpha,
keyType: Zonal,
serviceType: reflect.TypeOf(&alpha.NetworkEndpointGroupsService{}),
additionalMethods: []string{
"AttachNetworkEndpoints",
"DetachNetworkEndpoints",
"ListNetworkEndpoints",
},
options: AggregatedList,
},
{
Object: "NetworkEndpointGroup",
Service: "NetworkEndpointGroups",
Resource: "networkEndpointGroups",
version: VersionBeta,
keyType: Zonal,
serviceType: reflect.TypeOf(&beta.NetworkEndpointGroupsService{}),
additionalMethods: []string{
"AttachNetworkEndpoints",
"DetachNetworkEndpoints",
"ListNetworkEndpoints",
},
options: AggregatedList,
},
{
Object: "Project",
Service: "Projects",
Resource: "projects",
keyType: Global,
// Generate only the stub with no methods.
options: NoGet | NoList | NoInsert | NoDelete | CustomOps,
serviceType: reflect.TypeOf(&ga.ProjectsService{}),
},
{
Object: "Region",
Service: "Regions",
Resource: "regions",
keyType: Global,
options: ReadOnly,
serviceType: reflect.TypeOf(&ga.RegionsService{}),
},
{
Object: "Route",
Service: "Routes",
Resource: "routes",
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",
Resource: "sslCertificates",
keyType: Global,
serviceType: reflect.TypeOf(&ga.SslCertificatesService{}),
},
{
Object: "TargetHttpProxy",
Service: "TargetHttpProxies",
Resource: "targetHttpProxies",
keyType: Global,
serviceType: reflect.TypeOf(&ga.TargetHttpProxiesService{}),
additionalMethods: []string{
"SetUrlMap",
},
},
{
Object: "TargetHttpsProxy",
Service: "TargetHttpsProxies",
Resource: "targetHttpsProxies",
keyType: Global,
serviceType: reflect.TypeOf(&ga.TargetHttpsProxiesService{}),
additionalMethods: []string{
"SetSslCertificates",
"SetUrlMap",
},
},
{
Object: "TargetPool",
Service: "TargetPools",
Resource: "targetPools",
keyType: Regional,
serviceType: reflect.TypeOf(&ga.TargetPoolsService{}),
additionalMethods: []string{
"AddInstance",
"RemoveInstance",
},
},
{
Object: "UrlMap",
Service: "UrlMaps",
Resource: "urlMaps",
keyType: Global,
serviceType: reflect.TypeOf(&ga.UrlMapsService{}),
additionalMethods: []string{
"Update",
},
},
{
Object: "Zone",
Service: "Zones",
Resource: "zones",
keyType: Global,
options: ReadOnly,
serviceType: reflect.TypeOf(&ga.ZonesService{}),
},
}

View File

@ -1,337 +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 meta
import (
"fmt"
"reflect"
"strings"
)
func newArg(t reflect.Type) *arg {
ret := &arg{}
// Dereference the pointer types to get at the underlying concrete type.
Loop:
for {
switch t.Kind() {
case reflect.Ptr:
ret.numPtr++
t = t.Elem()
default:
ret.pkg = t.PkgPath()
ret.typeName += t.Name()
break Loop
}
}
return ret
}
type arg struct {
pkg, typeName string
numPtr int
}
func (a *arg) normalizedPkg() string {
if a.pkg == "" {
return ""
}
// Strip the repo.../vendor/ prefix from the package path if present.
parts := strings.Split(a.pkg, "/")
// Remove vendor prefix.
for i := 0; i < len(parts); i++ {
if parts[i] == "vendor" {
parts = parts[i+1:]
break
}
}
switch strings.Join(parts, "/") {
case "google.golang.org/api/compute/v1":
return "ga."
case "google.golang.org/api/compute/v0.alpha":
return "alpha."
case "google.golang.org/api/compute/v0.beta":
return "beta."
default:
panic(fmt.Errorf("unhandled package %q", a.pkg))
}
}
func (a *arg) String() string {
var ret string
for i := 0; i < a.numPtr; i++ {
ret += "*"
}
ret += a.normalizedPkg()
ret += a.typeName
return ret
}
// newMethod returns a newly initialized method.
func newMethod(s *ServiceInfo, m reflect.Method) *Method {
ret := &Method{
ServiceInfo: s,
m: m,
kind: MethodOperation,
ReturnType: "",
}
ret.init()
return ret
}
// MethodKind is the type of method that we are generated code for.
type MethodKind int
const (
// MethodOperation is a long running method that returns an operation.
MethodOperation MethodKind = iota
// MethodGet is a method that immediately returns some data.
MethodGet MethodKind = iota
// MethodPaged is a method that returns a paged set of data.
MethodPaged MethodKind = iota
)
// Method is used to generate the calling code for non-standard methods.
type Method struct {
*ServiceInfo
m reflect.Method
kind MethodKind
// ReturnType is the return type for the method.
ReturnType string
// ItemType is the type of the individual elements returns from a
// Pages() call. This is only applicable for MethodPaged kind.
ItemType string
}
// IsOperation is true if the method is an Operation.
func (m *Method) IsOperation() bool {
return m.kind == MethodOperation
}
// IsPaged is true if the method paged.
func (m *Method) IsPaged() bool {
return m.kind == MethodPaged
}
// IsGet is true if the method simple get.
func (m *Method) IsGet() bool {
return m.kind == MethodGet
}
// argsSkip is the number of arguments to skip when generating the
// synthesized method.
func (m *Method) argsSkip() int {
switch m.keyType {
case Zonal:
return 4
case Regional:
return 4
case Global:
return 3
}
panic(fmt.Errorf("invalid KeyType %v", m.keyType))
}
// args return a list of arguments to the method, skipping the first skip
// elements. If nameArgs is true, then the arguments will include a generated
// parameter name (arg<N>). prefix will be added to the parameters.
func (m *Method) args(skip int, nameArgs bool, prefix []string) []string {
var args []*arg
fType := m.m.Func.Type()
for i := 0; i < fType.NumIn(); i++ {
t := fType.In(i)
args = append(args, newArg(t))
}
var a []string
for i := skip; i < fType.NumIn(); i++ {
if nameArgs {
a = append(a, fmt.Sprintf("arg%d %s", i-skip, args[i]))
} else {
a = append(a, args[i].String())
}
}
return append(prefix, a...)
}
// init the method. This performs some rudimentary static checking as well as
// determines the kind of method by looking at the shape (method signature) of
// the object.
func (m *Method) init() {
fType := m.m.Func.Type()
if fType.NumIn() < m.argsSkip() {
err := fmt.Errorf("method %q.%q, arity = %d which is less than required (< %d)",
m.Service, m.Name(), fType.NumIn(), m.argsSkip())
panic(err)
}
// Skipped args should all be string (they will be projectID, zone, region etc).
for i := 1; i < m.argsSkip(); i++ {
if fType.In(i).Kind() != reflect.String {
panic(fmt.Errorf("method %q.%q: skipped args can only be strings", m.Service, m.Name()))
}
}
// Return of the method must return a single value of type *xxxCall.
if fType.NumOut() != 1 || fType.Out(0).Kind() != reflect.Ptr || !strings.HasSuffix(fType.Out(0).Elem().Name(), "Call") {
panic(fmt.Errorf("method %q.%q: generator only supports methods returning an *xxxCall object",
m.Service, m.Name()))
}
returnType := fType.Out(0)
returnTypeName := fType.Out(0).Elem().Name()
// xxxCall must have a Do() method.
doMethod, ok := returnType.MethodByName("Do")
if !ok {
panic(fmt.Errorf("method %q.%q: return type %q does not have a Do() method",
m.Service, m.Name(), returnTypeName))
}
_, hasPages := returnType.MethodByName("Pages")
// Do() method must return (*T, error).
switch doMethod.Func.Type().NumOut() {
case 2:
out0 := doMethod.Func.Type().Out(0)
if out0.Kind() != reflect.Ptr {
panic(fmt.Errorf("method %q.%q: return type %q of Do() = S, _; S must be pointer type (%v)",
m.Service, m.Name(), returnTypeName, out0))
}
m.ReturnType = out0.Elem().Name()
switch {
case out0.Elem().Name() == "Operation":
m.kind = MethodOperation
case hasPages:
m.kind = MethodPaged
// Pages() returns a xxxList that has the actual list
// of objects in the xxxList.Items field.
listType := out0.Elem()
itemsField, ok := listType.FieldByName("Items")
if !ok {
panic(fmt.Errorf("method %q.%q: paged return type %q does not have a .Items field", m.Service, m.Name(), listType.Name()))
}
// itemsField will be a []*ItemType. Dereference to
// extract the ItemType.
itemsType := itemsField.Type
if itemsType.Kind() != reflect.Slice && itemsType.Elem().Kind() != reflect.Ptr {
panic(fmt.Errorf("method %q.%q: paged return type %q.Items is not an array of pointers", m.Service, m.Name(), listType.Name()))
}
m.ItemType = itemsType.Elem().Elem().Name()
default:
m.kind = MethodGet
}
// Second argument must be "error".
if doMethod.Func.Type().Out(1).Name() != "error" {
panic(fmt.Errorf("method %q.%q: return type %q of Do() = S, T; T must be 'error'",
m.Service, m.Name(), returnTypeName))
}
break
default:
panic(fmt.Errorf("method %q.%q: %q Do() return type is not handled by the generator",
m.Service, m.Name(), returnTypeName))
}
}
// Name is the name of the method.
func (m *Method) Name() string {
return m.m.Name
}
// CallArgs is a list of comma separated "argN" used for calling the method.
// For example, if the method has two additional arguments, this will return
// "arg0, arg1".
func (m *Method) CallArgs() string {
var args []string
for i := m.argsSkip(); i < m.m.Func.Type().NumIn(); i++ {
args = append(args, fmt.Sprintf("arg%d", i-m.argsSkip()))
}
if len(args) == 0 {
return ""
}
return fmt.Sprintf(", %s", strings.Join(args, ", "))
}
// MockHookName is the name of the hook function in the mock.
func (m *Method) MockHookName() string {
return m.m.Name + "Hook"
}
// MockHook is the definition of the hook function.
func (m *Method) MockHook() string {
args := m.args(m.argsSkip(), false, []string{
"context.Context",
"*meta.Key",
})
if m.kind == MethodPaged {
args = append(args, "*filter.F")
}
args = append(args, fmt.Sprintf("*%s", m.MockWrapType()))
switch m.kind {
case MethodOperation:
return fmt.Sprintf("%v func(%v) error", m.MockHookName(), strings.Join(args, ", "))
case MethodGet:
return fmt.Sprintf("%v func(%v) (*%v.%v, error)", m.MockHookName(), strings.Join(args, ", "), m.Version(), m.ReturnType)
case MethodPaged:
return fmt.Sprintf("%v func(%v) ([]*%v.%v, error)", m.MockHookName(), strings.Join(args, ", "), m.Version(), m.ItemType)
default:
panic(fmt.Errorf("invalid method kind: %v", m.kind))
}
}
// FcnArgs is the function signature for the definition of the method.
func (m *Method) FcnArgs() string {
args := m.args(m.argsSkip(), true, []string{
"ctx context.Context",
"key *meta.Key",
})
if m.kind == MethodPaged {
args = append(args, "fl *filter.F")
}
switch m.kind {
case MethodOperation:
return fmt.Sprintf("%v(%v) error", m.m.Name, strings.Join(args, ", "))
case MethodGet:
return fmt.Sprintf("%v(%v) (*%v.%v, error)", m.m.Name, strings.Join(args, ", "), m.Version(), m.ReturnType)
case MethodPaged:
return fmt.Sprintf("%v(%v) ([]*%v.%v, error)", m.m.Name, strings.Join(args, ", "), m.Version(), m.ItemType)
default:
panic(fmt.Errorf("invalid method kind: %v", m.kind))
}
}
// InterfaceFunc is the function declaration of the method in the interface.
func (m *Method) InterfaceFunc() string {
args := []string{
"context.Context",
"*meta.Key",
}
args = m.args(m.argsSkip(), false, args)
if m.kind == MethodPaged {
args = append(args, "*filter.F")
}
switch m.kind {
case MethodOperation:
return fmt.Sprintf("%v(%v) error", m.m.Name, strings.Join(args, ", "))
case MethodGet:
return fmt.Sprintf("%v(%v) (*%v.%v, error)", m.m.Name, strings.Join(args, ", "), m.Version(), m.ReturnType)
case MethodPaged:
return fmt.Sprintf("%v(%v) ([]*%v.%v, error)", m.m.Name, strings.Join(args, ", "), m.Version(), m.ItemType)
default:
panic(fmt.Errorf("invalid method kind: %v", m.kind))
}
}

View File

@ -1,300 +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 meta
import (
"errors"
"fmt"
"reflect"
"sort"
)
// ServiceInfo defines the entry for a Service that code will be generated for.
type ServiceInfo struct {
// Object is the Go name of the object type that the service deals
// with. Example: "ForwardingRule".
Object string
// Service is the Go name of the service struct i.e. where the methods
// are defined. Examples: "GlobalForwardingRules".
Service string
// Resource is the plural noun of the resource in the compute API URL (e.g.
// "forwardingRules").
Resource string
// version if unspecified will be assumed to be VersionGA.
version Version
keyType KeyType
serviceType reflect.Type
additionalMethods []string
options int
aggregatedListField string
}
// Version returns the version of the Service, defaulting to GA if APIVersion
// is empty.
func (i *ServiceInfo) Version() Version {
if i.version == "" {
return VersionGA
}
return i.version
}
// VersionTitle returns the capitalized golang CamelCase name for the version.
func (i *ServiceInfo) VersionTitle() string {
switch i.Version() {
case VersionGA:
return "GA"
case VersionAlpha:
return "Alpha"
case VersionBeta:
return "Beta"
}
panic(fmt.Errorf("invalid version %q", i.Version()))
}
// WrapType is the name of the wrapper service type.
func (i *ServiceInfo) WrapType() string {
switch i.Version() {
case VersionGA:
return i.Service
case VersionAlpha:
return "Alpha" + i.Service
case VersionBeta:
return "Beta" + i.Service
}
return "Invalid"
}
// WrapTypeOps is the name of the additional operations type.
func (i *ServiceInfo) WrapTypeOps() string {
return i.WrapType() + "Ops"
}
// FQObjectType is fully qualified name of the object (e.g. compute.Instance).
func (i *ServiceInfo) FQObjectType() string {
return fmt.Sprintf("%v.%v", i.Version(), i.Object)
}
// ObjectListType is the compute List type for the object (contains Items field).
func (i *ServiceInfo) ObjectListType() string {
return fmt.Sprintf("%v.%vList", i.Version(), i.Object)
}
// ObjectAggregatedListType is the compute List type for the object (contains Items field).
func (i *ServiceInfo) ObjectAggregatedListType() string {
return fmt.Sprintf("%v.%vAggregatedList", i.Version(), i.Object)
}
// MockWrapType is the name of the concrete mock for this type.
func (i *ServiceInfo) MockWrapType() string {
return "Mock" + i.WrapType()
}
// MockField is the name of the field in the mock struct.
func (i *ServiceInfo) MockField() string {
return "Mock" + i.WrapType()
}
// GCEWrapType is the name of the GCE wrapper type.
func (i *ServiceInfo) GCEWrapType() string {
return "GCE" + i.WrapType()
}
// Field is the name of the GCE struct.
func (i *ServiceInfo) Field() string {
return "gce" + i.WrapType()
}
// Methods returns a list of additional methods to generate code for.
func (i *ServiceInfo) Methods() []*Method {
methods := map[string]bool{}
for _, m := range i.additionalMethods {
methods[m] = true
}
var ret []*Method
for j := 0; j < i.serviceType.NumMethod(); j++ {
m := i.serviceType.Method(j)
if _, ok := methods[m.Name]; !ok {
continue
}
ret = append(ret, newMethod(i, m))
methods[m.Name] = false
}
for k, b := range methods {
if b {
panic(fmt.Errorf("method %q was not found in service %q", k, i.Service))
}
}
return ret
}
// KeyIsGlobal is true if the key is global.
func (i *ServiceInfo) KeyIsGlobal() bool {
return i.keyType == Global
}
// KeyIsRegional is true if the key is regional.
func (i *ServiceInfo) KeyIsRegional() bool {
return i.keyType == Regional
}
// KeyIsZonal is true if the key is zonal.
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 {
case Global:
return fmt.Sprintf("GlobalKey(%q)", name)
case Regional:
return fmt.Sprintf("RegionalKey(%q, %q)", name, location)
case Zonal:
return fmt.Sprintf("ZonalKey(%q, %q)", name, location)
}
return "Invalid"
}
// GenerateGet is true if the method is to be generated.
func (i *ServiceInfo) GenerateGet() bool {
return i.options&NoGet == 0
}
// GenerateList is true if the method is to be generated.
func (i *ServiceInfo) GenerateList() bool {
return i.options&NoList == 0
}
// GenerateDelete is true if the method is to be generated.
func (i *ServiceInfo) GenerateDelete() bool {
return i.options&NoDelete == 0
}
// GenerateInsert is true if the method is to be generated.
func (i *ServiceInfo) GenerateInsert() bool {
return i.options&NoInsert == 0
}
// GenerateCustomOps is true if we should generated a xxxOps interface for
// adding additional methods to the generated interface.
func (i *ServiceInfo) GenerateCustomOps() bool {
return i.options&CustomOps != 0
}
// AggregatedList is true if the method is to be generated.
func (i *ServiceInfo) AggregatedList() bool {
return i.options&AggregatedList != 0
}
// AggregatedListField is the name of the field used for the aggregated list
// call. This is typically the same as the name of the service, but can be
// customized by setting the aggregatedListField field.
func (i *ServiceInfo) AggregatedListField() string {
if i.aggregatedListField == "" {
return i.Service
}
return i.aggregatedListField
}
// ServiceGroup is a grouping of the same service but at different API versions.
type ServiceGroup struct {
Alpha *ServiceInfo
Beta *ServiceInfo
GA *ServiceInfo
}
// 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
case sg.Alpha != nil:
return sg.Alpha
case sg.Beta != nil:
return sg.Beta
default:
panic(errors.New("service group is empty"))
}
}
// HasGA returns true if this object has a GA representation.
func (sg *ServiceGroup) HasGA() bool {
return sg.GA != nil
}
// HasAlpha returns true if this object has a Alpha representation.
func (sg *ServiceGroup) HasAlpha() bool {
return sg.Alpha != nil
}
// HasBeta returns true if this object has a Beta representation.
func (sg *ServiceGroup) HasBeta() bool {
return sg.Beta != nil
}
// groupServices together by version.
func groupServices(services []*ServiceInfo) map[string]*ServiceGroup {
ret := map[string]*ServiceGroup{}
for _, si := range services {
if _, ok := ret[si.Service]; !ok {
ret[si.Service] = &ServiceGroup{}
}
group := ret[si.Service]
switch si.Version() {
case VersionAlpha:
group.Alpha = si
case VersionBeta:
group.Beta = si
case VersionGA:
group.GA = si
}
}
return ret
}
// 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

@ -1,31 +0,0 @@
load("@io_bazel_rules_go//go:def.bzl", "go_library")
go_library(
name = "go_default_library",
srcs = ["mock.go"],
importpath = "k8s.io/kubernetes/pkg/cloudprovider/providers/gce/cloud/mock",
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",
"//vendor/google.golang.org/api/compute/v1:go_default_library",
"//vendor/google.golang.org/api/googleapi:go_default_library",
],
)
filegroup(
name = "package-srcs",
srcs = glob(["**"]),
tags = ["automanaged"],
visibility = ["//visibility:private"],
)
filegroup(
name = "all-srcs",
srcs = [":package-srcs"],
tags = ["automanaged"],
visibility = ["//visibility:public"],
)

View File

@ -1,640 +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 mock encapsulates mocks for testing GCE provider functionality.
// These methods are used to override the mock objects' methods in order to
// intercept the standard processing and to add custom logic for test purposes.
//
// // Example usage:
// cloud := cloud.NewMockGCE()
// cloud.MockTargetPools.AddInstanceHook = mock.AddInstanceHook
package mock
import (
"context"
"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"
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}
// UnauthorizedErr wraps a Google API error with code StatusForbidden.
UnauthorizedErr = &googleapi.Error{Code: http.StatusForbidden}
)
// gceObject is an abstraction of all GCE API object in go client
type gceObject interface {
MarshalJSON() ([]byte, error)
}
// AddInstanceHook mocks adding a Instance to MockTargetPools
func AddInstanceHook(ctx context.Context, key *meta.Key, req *ga.TargetPoolsAddInstanceRequest, m *cloud.MockTargetPools) error {
pool, err := m.Get(ctx, key)
if err != nil {
return &googleapi.Error{
Code: http.StatusNotFound,
Message: fmt.Sprintf("Key: %s was not found in TargetPools", key.String()),
}
}
for _, instance := range req.Instances {
pool.Instances = append(pool.Instances, instance.Instance)
}
return nil
}
// RemoveInstanceHook mocks removing a Instance from MockTargetPools
func RemoveInstanceHook(ctx context.Context, key *meta.Key, req *ga.TargetPoolsRemoveInstanceRequest, m *cloud.MockTargetPools) error {
pool, err := m.Get(ctx, key)
if err != nil {
return &googleapi.Error{
Code: http.StatusNotFound,
Message: fmt.Sprintf("Key: %s was not found in TargetPools", key.String()),
}
}
for _, instanceToRemove := range req.Instances {
for i, instance := range pool.Instances {
if instanceToRemove.Instance == instance {
// Delete instance from pool.Instances without preserving order
pool.Instances[i] = pool.Instances[len(pool.Instances)-1]
pool.Instances = pool.Instances[:len(pool.Instances)-1]
break
}
}
}
return nil
}
func convertAndInsertAlphaForwardingRule(key *meta.Key, obj gceObject, mRules map[meta.Key]*cloud.MockForwardingRulesObj, version meta.Version, projectID string) (bool, error) {
if !key.Valid() {
return true, fmt.Errorf("invalid GCE key (%+v)", key)
}
if _, ok := mRules[*key]; ok {
err := &googleapi.Error{
Code: http.StatusConflict,
Message: fmt.Sprintf("MockForwardingRule %v exists", key),
}
return true, err
}
enc, err := obj.MarshalJSON()
if err != nil {
return true, err
}
var fwdRule alpha.ForwardingRule
if err := json.Unmarshal(enc, &fwdRule); err != nil {
return true, err
}
// Set the default values for the Alpha fields.
if fwdRule.NetworkTier == "" {
fwdRule.NetworkTier = cloud.NetworkTierDefault.ToGCEValue()
}
fwdRule.Name = key.Name
if fwdRule.SelfLink == "" {
fwdRule.SelfLink = cloud.SelfLink(version, projectID, "forwardingRules", key)
}
mRules[*key] = &cloud.MockForwardingRulesObj{Obj: fwdRule}
return true, nil
}
// 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)
}
// 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, addressAttrs AddressAttributes) (bool, error) {
if !key.Valid() {
return true, fmt.Errorf("invalid GCE key (%+v)", key)
}
if _, ok := mAddrs[*key]; ok {
err := &googleapi.Error{
Code: http.StatusConflict,
Message: fmt.Sprintf("MockAddresses %v exists", key),
}
return true, err
}
enc, err := obj.MarshalJSON()
if err != nil {
return true, err
}
var addr alpha.Address
if err := json.Unmarshal(enc, &addr); err != nil {
return true, err
}
// Set default address type if not present.
if addr.AddressType == "" {
addr.AddressType = string(cloud.SchemeExternal)
}
var existingAddresses []*ga.Address
for _, obj := range mAddrs {
existingAddresses = append(existingAddresses, obj.ToGA())
}
for _, existingAddr := range existingAddresses {
if addr.Address == existingAddr.Address {
msg := fmt.Sprintf("MockAddresses IP %v in use", addr.Address)
// When the IP is already in use, this call returns a StatusBadRequest
// if the address is an external address, and StatusConflict if an
// internal address. This is to be consistent with actual GCE API.
errorCode := http.StatusConflict
if addr.AddressType == string(cloud.SchemeExternal) {
errorCode = http.StatusBadRequest
}
return true, &googleapi.Error{Code: errorCode, Message: msg}
}
}
// Set default values used in tests
addr.Name = key.Name
if addr.SelfLink == "" {
addr.SelfLink = cloud.SelfLink(version, projectID, "addresses", key)
}
if addr.Address == "" {
addr.Address = fmt.Sprintf("1.2.3.%d", addressAttrs.IPCounter)
addressAttrs.IPCounter++
}
// Set the default values for the Alpha fields.
if addr.NetworkTier == "" {
addr.NetworkTier = cloud.NetworkTierDefault.ToGCEValue()
}
mAddrs[*key] = &cloud.MockAddressesObj{Obj: addr}
return true, nil
}
// 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, 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, 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, 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
}
// UpdateBackendServiceHook defines the hook for updating a BackendService.
// It replaces the object with the same key in the mock with the updated object.
func UpdateBackendServiceHook(ctx context.Context, key *meta.Key, obj *ga.BackendService, m *cloud.MockBackendServices) error {
_, err := m.Get(ctx, key)
if err != nil {
return &googleapi.Error{
Code: http.StatusNotFound,
Message: fmt.Sprintf("Key: %s was not found in BackendServices", 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.MockBackendServicesObj{Obj: obj}
return nil
}
// UpdateAlphaBackendServiceHook defines the hook for updating an alpha BackendService.
// It replaces the object with the same key in the mock with the updated object.
func UpdateAlphaBackendServiceHook(ctx context.Context, key *meta.Key, obj *alpha.BackendService, m *cloud.MockAlphaBackendServices) error {
_, err := m.Get(ctx, key)
if err != nil {
return &googleapi.Error{
Code: http.StatusNotFound,
Message: fmt.Sprintf("Key: %s was not found in BackendServices", key.String()),
}
}
obj.Name = key.Name
projectID := m.ProjectRouter.ProjectID(ctx, "alpha", "backendServices")
obj.SelfLink = cloud.SelfLink(meta.VersionAlpha, projectID, "backendServices", key)
m.Objects[*key] = &cloud.MockBackendServicesObj{Obj: obj}
return nil
}
// UpdateBetaBackendServiceHook defines the hook for updating an beta BackendService.
// It replaces the object with the same key in the mock with the updated object.
func UpdateBetaBackendServiceHook(ctx context.Context, key *meta.Key, obj *beta.BackendService, m *cloud.MockBetaBackendServices) error {
_, err := m.Get(ctx, key)
if err != nil {
return &googleapi.Error{
Code: http.StatusNotFound,
Message: fmt.Sprintf("Key: %s was not found in BackendServices", key.String()),
}
}
obj.Name = key.Name
projectID := m.ProjectRouter.ProjectID(ctx, "beta", "backendServices")
obj.SelfLink = cloud.SelfLink(meta.VersionBeta, projectID, "backendServices", key)
m.Objects[*key] = &cloud.MockBackendServicesObj{Obj: obj}
return nil
}
// UpdateURLMapHook defines the hook for updating a UrlMap.
// It replaces the object with the same key in the mock with the updated object.
func UpdateURLMapHook(ctx context.Context, key *meta.Key, obj *ga.UrlMap, m *cloud.MockUrlMaps) error {
_, err := m.Get(ctx, key)
if err != nil {
return &googleapi.Error{
Code: http.StatusNotFound,
Message: fmt.Sprintf("Key: %s was not found in UrlMaps", key.String()),
}
}
obj.Name = key.Name
projectID := m.ProjectRouter.ProjectID(ctx, "ga", "urlMaps")
obj.SelfLink = cloud.SelfLink(meta.VersionGA, projectID, "urlMaps", key)
m.Objects[*key] = &cloud.MockUrlMapsObj{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
}
// InsertAlphaBackendServiceUnauthorizedErrHook mocks inserting an alpha BackendService and returns a forbidden error.
func InsertAlphaBackendServiceUnauthorizedErrHook(ctx context.Context, key *meta.Key, obj *alpha.BackendService, m *cloud.MockAlphaBackendServices) (bool, error) {
return true, UnauthorizedErr
}
// UpdateAlphaBackendServiceUnauthorizedErrHook mocks updating an alpha BackendService and returns a forbidden error.
func UpdateAlphaBackendServiceUnauthorizedErrHook(ctx context.Context, key *meta.Key, obj *alpha.BackendService, m *cloud.MockAlphaBackendServices) error {
return UnauthorizedErr
}
// 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

@ -1,151 +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 cloud
import (
"context"
"reflect"
"testing"
alpha "google.golang.org/api/compute/v0.alpha"
beta "google.golang.org/api/compute/v0.beta"
ga "google.golang.org/api/compute/v1"
"k8s.io/kubernetes/pkg/cloudprovider/providers/gce/cloud/filter"
"k8s.io/kubernetes/pkg/cloudprovider/providers/gce/cloud/meta"
)
func TestMocks(t *testing.T) {
t.Parallel()
// This test uses Addresses, but the logic that is generated is the same for
// other basic objects.
const region = "us-central1"
ctx := context.Background()
pr := &SingleProjectRouter{"mock-project"}
mock := NewMockGCE(pr)
keyAlpha := meta.RegionalKey("key-alpha", region)
keyBeta := meta.RegionalKey("key-beta", region)
keyGA := meta.RegionalKey("key-ga", region)
key := keyAlpha
// Get not found.
if _, err := mock.AlphaAddresses().Get(ctx, key); err == nil {
t.Errorf("AlphaAddresses().Get(%v, %v) = _, nil; want error", ctx, key)
}
if _, err := mock.BetaAddresses().Get(ctx, key); err == nil {
t.Errorf("BetaAddresses().Get(%v, %v) = _, nil; want error", ctx, key)
}
if _, err := mock.Addresses().Get(ctx, key); err == nil {
t.Errorf("Addresses().Get(%v, %v) = _, nil; want error", ctx, key)
}
// Insert.
{
obj := &alpha.Address{}
if err := mock.AlphaAddresses().Insert(ctx, keyAlpha, obj); err != nil {
t.Errorf("AlphaAddresses().Insert(%v, %v, %v) = %v; want nil", ctx, key, obj, err)
}
}
{
obj := &beta.Address{}
if err := mock.BetaAddresses().Insert(ctx, keyBeta, obj); err != nil {
t.Errorf("BetaAddresses().Insert(%v, %v, %v) = %v; want nil", ctx, key, obj, err)
}
}
{
obj := &ga.Address{}
if err := mock.Addresses().Insert(ctx, keyGA, &ga.Address{Name: "ga"}); err != nil {
t.Errorf("Addresses().Insert(%v, %v, %v) = %v; want nil", ctx, key, obj, err)
}
}
// Get across versions.
if obj, err := mock.AlphaAddresses().Get(ctx, key); err != nil {
t.Errorf("AlphaAddresses().Get(%v, %v) = %v, %v; want nil", ctx, key, obj, err)
}
if obj, err := mock.BetaAddresses().Get(ctx, key); err != nil {
t.Errorf("BetaAddresses().Get(%v, %v) = %v, %v; want nil", ctx, key, obj, err)
}
if obj, err := mock.Addresses().Get(ctx, key); err != nil {
t.Errorf("Addresses().Get(%v, %v) = %v, %v; want nil", ctx, key, obj, err)
}
// List across versions.
want := map[string]bool{"key-alpha": true, "key-beta": true, "key-ga": true}
{
objs, err := mock.AlphaAddresses().List(ctx, region, filter.None)
if err != nil {
t.Errorf("AlphaAddresses().List(%v, %v, %v) = %v, %v; want _, nil", ctx, region, filter.None, objs, err)
} else {
got := map[string]bool{}
for _, obj := range objs {
got[obj.Name] = true
}
if !reflect.DeepEqual(got, want) {
t.Errorf("AlphaAddresses().List(); got %+v, want %+v", got, want)
}
}
}
{
objs, err := mock.BetaAddresses().List(ctx, region, filter.None)
if err != nil {
t.Errorf("BetaAddresses().List(%v, %v, %v) = %v, %v; want _, nil", ctx, region, filter.None, objs, err)
} else {
got := map[string]bool{}
for _, obj := range objs {
got[obj.Name] = true
}
if !reflect.DeepEqual(got, want) {
t.Errorf("AlphaAddresses().List(); got %+v, want %+v", got, want)
}
}
}
{
objs, err := mock.Addresses().List(ctx, region, filter.None)
if err != nil {
t.Errorf("Addresses().List(%v, %v, %v) = %v, %v; want _, nil", ctx, region, filter.None, objs, err)
} else {
got := map[string]bool{}
for _, obj := range objs {
got[obj.Name] = true
}
if !reflect.DeepEqual(got, want) {
t.Errorf("AlphaAddresses().List(); got %+v, want %+v", got, want)
}
}
}
// Delete across versions.
if err := mock.AlphaAddresses().Delete(ctx, keyAlpha); err != nil {
t.Errorf("AlphaAddresses().Delete(%v, %v) = %v; want nil", ctx, key, err)
}
if err := mock.BetaAddresses().Delete(ctx, keyBeta); err != nil {
t.Errorf("BetaAddresses().Delete(%v, %v) = %v; want nil", ctx, key, err)
}
if err := mock.Addresses().Delete(ctx, keyGA); err != nil {
t.Errorf("Addresses().Delete(%v, %v) = %v; want nil", ctx, key, err)
}
// Delete not found.
if err := mock.AlphaAddresses().Delete(ctx, keyAlpha); err == nil {
t.Errorf("AlphaAddresses().Delete(%v, %v) = nil; want error", ctx, key)
}
if err := mock.BetaAddresses().Delete(ctx, keyBeta); err == nil {
t.Errorf("BetaAddresses().Delete(%v, %v) = nil; want error", ctx, key)
}
if err := mock.Addresses().Delete(ctx, keyGA); err == nil {
t.Errorf("Addresses().Delete(%v, %v) = nil; want error", ctx, key)
}
}

View File

@ -1,219 +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 cloud
import (
"context"
"fmt"
"k8s.io/klog"
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/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.
rateLimitKey() *RateLimitKey
}
type gaOperation struct {
s *Service
projectID string
key *meta.Key
err error
}
func (o *gaOperation) String() string {
return fmt.Sprintf("gaOperation{%q, %v}", o.projectID, o.key)
}
func (o *gaOperation) isDone(ctx context.Context) (bool, error) {
var (
op *ga.Operation
err error
)
switch o.key.Type() {
case meta.Regional:
op, err = o.s.GA.RegionOperations.Get(o.projectID, o.key.Region, o.key.Name).Context(ctx).Do()
klog.V(5).Infof("GA.RegionOperations.Get(%v, %v, %v) = %+v, %v; ctx = %v", o.projectID, o.key.Region, o.key.Name, op, err, ctx)
case meta.Zonal:
op, err = o.s.GA.ZoneOperations.Get(o.projectID, o.key.Zone, o.key.Name).Context(ctx).Do()
klog.V(5).Infof("GA.ZoneOperations.Get(%v, %v, %v) = %+v, %v; ctx = %v", o.projectID, o.key.Zone, o.key.Name, op, err, ctx)
case meta.Global:
op, err = o.s.GA.GlobalOperations.Get(o.projectID, o.key.Name).Context(ctx).Do()
klog.V(5).Infof("GA.GlobalOperations.Get(%v, %v) = %+v, %v; ctx = %v", o.projectID, o.key.Name, op, err, ctx)
default:
return false, fmt.Errorf("invalid key type: %#v", o.key)
}
if err != nil {
return false, err
}
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 = &googleapi.Error{Code: int(op.HttpErrorStatusCode), Message: fmt.Sprintf("%v - %v", e.Code, e.Message)}
}
return true, nil
}
func (o *gaOperation) rateLimitKey() *RateLimitKey {
return &RateLimitKey{
ProjectID: o.projectID,
Operation: "Get",
Service: "Operations",
Version: meta.VersionGA,
}
}
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 {
return fmt.Sprintf("alphaOperation{%q, %v}", o.projectID, o.key)
}
func (o *alphaOperation) isDone(ctx context.Context) (bool, error) {
var (
op *alpha.Operation
err error
)
switch o.key.Type() {
case meta.Regional:
op, err = o.s.Alpha.RegionOperations.Get(o.projectID, o.key.Region, o.key.Name).Context(ctx).Do()
klog.V(5).Infof("Alpha.RegionOperations.Get(%v, %v, %v) = %+v, %v; ctx = %v", o.projectID, o.key.Region, o.key.Name, op, err, ctx)
case meta.Zonal:
op, err = o.s.Alpha.ZoneOperations.Get(o.projectID, o.key.Zone, o.key.Name).Context(ctx).Do()
klog.V(5).Infof("Alpha.ZoneOperations.Get(%v, %v, %v) = %+v, %v; ctx = %v", o.projectID, o.key.Zone, o.key.Name, op, err, ctx)
case meta.Global:
op, err = o.s.Alpha.GlobalOperations.Get(o.projectID, o.key.Name).Context(ctx).Do()
klog.V(5).Infof("Alpha.GlobalOperations.Get(%v, %v) = %+v, %v; ctx = %v", o.projectID, o.key.Name, op, err, ctx)
default:
return false, fmt.Errorf("invalid key type: %#v", o.key)
}
if err != nil {
return false, err
}
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 = &googleapi.Error{Code: int(op.HttpErrorStatusCode), Message: fmt.Sprintf("%v - %v", e.Code, e.Message)}
}
return true, nil
}
func (o *alphaOperation) rateLimitKey() *RateLimitKey {
return &RateLimitKey{
ProjectID: o.projectID,
Operation: "Get",
Service: "Operations",
Version: meta.VersionAlpha,
}
}
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 {
return fmt.Sprintf("betaOperation{%q, %v}", o.projectID, o.key)
}
func (o *betaOperation) isDone(ctx context.Context) (bool, error) {
var (
op *beta.Operation
err error
)
switch o.key.Type() {
case meta.Regional:
op, err = o.s.Beta.RegionOperations.Get(o.projectID, o.key.Region, o.key.Name).Context(ctx).Do()
klog.V(5).Infof("Beta.RegionOperations.Get(%v, %v, %v) = %+v, %v; ctx = %v", o.projectID, o.key.Region, o.key.Name, op, err, ctx)
case meta.Zonal:
op, err = o.s.Beta.ZoneOperations.Get(o.projectID, o.key.Zone, o.key.Name).Context(ctx).Do()
klog.V(5).Infof("Beta.ZoneOperations.Get(%v, %v, %v) = %+v, %v; ctx = %v", o.projectID, o.key.Zone, o.key.Name, op, err, ctx)
case meta.Global:
op, err = o.s.Beta.GlobalOperations.Get(o.projectID, o.key.Name).Context(ctx).Do()
klog.V(5).Infof("Beta.GlobalOperations.Get(%v, %v) = %+v, %v; ctx = %v", o.projectID, o.key.Name, op, err, ctx)
default:
return false, fmt.Errorf("invalid key type: %#v", o.key)
}
if err != nil {
return false, err
}
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 = &googleapi.Error{Code: int(op.HttpErrorStatusCode), Message: fmt.Sprintf("%v - %v", e.Code, e.Message)}
}
return true, nil
}
func (o *betaOperation) rateLimitKey() *RateLimitKey {
return &RateLimitKey{
ProjectID: o.projectID,
Operation: "Get",
Service: "Operations",
Version: meta.VersionBeta,
}
}
func (o *betaOperation) error() error {
return o.err
}

View File

@ -1,45 +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 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
}
// ProjectID returns the project ID to be used for a call to the API.
func (r *SingleProjectRouter) ProjectID(ctx context.Context, version meta.Version, service string) string {
return r.ID
}

View File

@ -1,106 +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 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
}
// 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 everything immediately.
func (*NopRateLimiter) Accept(ctx context.Context, key *RateLimitKey) error {
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

@ -1,80 +0,0 @@
/*
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

@ -1,110 +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 cloud
import (
"context"
"fmt"
"k8s.io/klog"
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 (s *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{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: 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: s, projectID: r.ProjectID, key: r.Key}, 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 (s *Service) WaitForCompletion(ctx context.Context, genericOp interface{}) error {
op, err := s.wrapOperation(genericOp)
if err != nil {
klog.Errorf("wrapOperation(%+v) error: %v", genericOp, err)
return err
}
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():
klog.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++
klog.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 {
klog.V(5).Infof("op.isDone(%v) error; op = %v, poll count = %d, err = %v, retrying", ctx, op, pollCount, err)
}
if done {
break
}
}
klog.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

@ -1,84 +0,0 @@
/*
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

@ -1,201 +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 cloud
import (
"encoding/json"
"fmt"
"strings"
"k8s.io/kubernetes/pkg/cloudprovider/providers/gce/cloud/meta"
)
const (
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.
type ResourceID struct {
ProjectID string
Resource string
Key *meta.Key
}
// Equal returns true if two resource IDs are equal.
func (r *ResourceID) Equal(other *ResourceID) bool {
if r.ProjectID != other.ProjectID || r.Resource != other.Resource {
return false
}
if r.Key != nil && other.Key != nil {
return *r.Key == *other.Key
}
if r.Key == nil && other.Key == nil {
return true
}
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>
// [https://www.googleapis.com/compute/<ver>]/projects/<proj>/global/<res>/<name>
// [https://www.googleapis.com/compute/<ver>]/projects/<proj>/regions/<region>/<res>/<name>
// [https://www.googleapis.com/compute/<ver>]/projects/<proj>/zones/<zone>/<res>/<name>
func ParseResourceURL(url string) (*ResourceID, error) {
errNotValid := fmt.Errorf("%q is not a valid resource URL", url)
// 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 || len(parts) > 6 {
return nil, errNotValid
}
ret := &ResourceID{}
scopedName := parts
if parts[0] == "projects" {
ret.Resource = "projects"
ret.ProjectID = parts[1]
scopedName = parts[2:]
if len(scopedName) == 0 {
return ret, nil
}
}
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
}
case "zones":
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
}
}
return nil, errNotValid
}
func copyViaJSON(dest, src interface{}) error {
bytes, err := json.Marshal(src)
if err != nil {
return err
}
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
switch ver {
case meta.VersionAlpha:
prefix = alphaPrefix
case meta.VersionBeta:
prefix = betaPrefix
case meta.VersionGA:
prefix = gaPrefix
default:
prefix = "invalid-prefix"
}
return fmt.Sprintf("%s/%s", prefix, RelativeResourceName(project, resource, key))
}

View File

@ -1,291 +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 cloud
import (
"errors"
"testing"
"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()
for _, tc := range []struct {
in string
r *ResourceID
}{
{
"https://www.googleapis.com/compute/v1/projects/some-gce-project",
&ResourceID{"some-gce-project", "projects", nil},
},
{
"https://www.googleapis.com/compute/v1/projects/some-gce-project/regions/us-central1",
&ResourceID{"some-gce-project", "regions", meta.GlobalKey("us-central1")},
},
{
"https://www.googleapis.com/compute/v1/projects/some-gce-project/zones/us-central1-b",
&ResourceID{"some-gce-project", "zones", meta.GlobalKey("us-central1-b")},
},
{
"https://www.googleapis.com/compute/v1/projects/some-gce-project/global/operations/operation-1513289952196-56054460af5a0-b1dae0c3-9bbf9dbf",
&ResourceID{"some-gce-project", "operations", meta.GlobalKey("operation-1513289952196-56054460af5a0-b1dae0c3-9bbf9dbf")},
},
{
"https://www.googleapis.com/compute/alpha/projects/some-gce-project/regions/us-central1/addresses/my-address",
&ResourceID{"some-gce-project", "addresses", meta.RegionalKey("my-address", "us-central1")},
},
{
"https://www.googleapis.com/compute/v1/projects/some-gce-project/zones/us-central1-c/instances/instance-1",
&ResourceID{"some-gce-project", "instances", meta.ZonalKey("instance-1", "us-central1-c")},
},
{
"http://localhost:3990/compute/beta/projects/some-gce-project/global/operations/operation-1513289952196-56054460af5a0-b1dae0c3-9bbf9dbf",
&ResourceID{"some-gce-project", "operations", meta.GlobalKey("operation-1513289952196-56054460af5a0-b1dae0c3-9bbf9dbf")},
},
{
"http://localhost:3990/compute/alpha/projects/some-gce-project/regions/dev-central1/addresses/my-address",
&ResourceID{"some-gce-project", "addresses", meta.RegionalKey("my-address", "dev-central1")},
},
{
"http://localhost:3990/compute/v1/projects/some-gce-project/zones/dev-central1-std/instances/instance-1",
&ResourceID{"some-gce-project", "instances", meta.ZonalKey("instance-1", "dev-central1-std")},
},
{
"projects/some-gce-project",
&ResourceID{"some-gce-project", "projects", nil},
},
{
"projects/some-gce-project/regions/us-central1",
&ResourceID{"some-gce-project", "regions", meta.GlobalKey("us-central1")},
},
{
"projects/some-gce-project/zones/us-central1-b",
&ResourceID{"some-gce-project", "zones", meta.GlobalKey("us-central1-b")},
},
{
"projects/some-gce-project/global/operations/operation-1513289952196-56054460af5a0-b1dae0c3-9bbf9dbf",
&ResourceID{"some-gce-project", "operations", meta.GlobalKey("operation-1513289952196-56054460af5a0-b1dae0c3-9bbf9dbf")},
},
{
"projects/some-gce-project/regions/us-central1/addresses/my-address",
&ResourceID{"some-gce-project", "addresses", meta.RegionalKey("my-address", "us-central1")},
},
{
"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 {
t.Errorf("ParseResourceURL(%q) = %+v, %v; want _, nil", tc.in, r, err)
continue
}
if !r.Equal(tc.r) {
t.Errorf("ParseResourceURL(%q) = %+v, nil; want %+v, nil", tc.in, r, tc.r)
}
}
// Malformed URLs.
for _, tc := range []string{
"",
"/",
"/a",
"/a/b",
"/a/b/c",
"/a/b/c/d",
"/a/b/c/d/e",
"/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",
} {
r, err := ParseResourceURL(tc)
if err == nil {
t.Errorf("ParseResourceURL(%q) = %+v, %v, want _, error", tc, r, err)
}
}
}
type A struct {
A, B, C string
}
type B struct {
A, B, D string
}
type E struct{}
func (*E) MarshalJSON() ([]byte, error) {
return nil, errors.New("injected error")
}
func TestCopyVisJSON(t *testing.T) {
t.Parallel()
var b B
srcA := &A{"aa", "bb", "cc"}
err := copyViaJSON(&b, srcA)
if err != nil {
t.Errorf(`copyViaJSON(&b, %+v) = %v, want nil`, srcA, err)
} else {
expectedB := B{"aa", "bb", ""}
if b != expectedB {
t.Errorf("b == %+v, want %+v", b, expectedB)
}
}
var a A
srcB := &B{"aaa", "bbb", "ccc"}
err = copyViaJSON(&a, srcB)
if err != nil {
t.Errorf(`copyViaJSON(&a, %+v) = %v, want nil`, srcB, err)
} else {
expectedA := A{"aaa", "bbb", ""}
if a != expectedA {
t.Errorf("a == %+v, want %+v", a, expectedA)
}
}
if err := copyViaJSON(&a, &E{}); err == nil {
t.Errorf("copyViaJSON(&a, &E{}) = nil, want error")
}
}
func TestSelfLink(t *testing.T) {
t.Parallel()
for _, tc := range []struct {
ver meta.Version
project string
resource string
key *meta.Key
want string
}{
{
meta.VersionAlpha,
"proj1",
"addresses",
meta.RegionalKey("key1", "us-central1"),
"https://www.googleapis.com/compute/alpha/projects/proj1/regions/us-central1/addresses/key1",
},
{
meta.VersionBeta,
"proj3",
"disks",
meta.ZonalKey("key2", "us-central1-b"),
"https://www.googleapis.com/compute/beta/projects/proj3/zones/us-central1-b/disks/key2",
},
{
meta.VersionGA,
"proj4",
"urlMaps",
meta.GlobalKey("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 {
t.Errorf("SelfLink(%v, %q, %q, %v) = %v, want %q", tc.ver, tc.project, tc.resource, tc.key, link, tc.want)
}
}
}