From 447823b4a1858be44cc64894a7e25972fb47a38d Mon Sep 17 00:00:00 2001 From: Bowei Du Date: Mon, 15 Jan 2018 15:02:01 -0800 Subject: [PATCH] Add handling for method that use Pages() to retrieve results Make functions take in *Key rather than value type. --- .../providers/gce/cloud/gen/main.go | 114 ++++++----- .../providers/gce/cloud/meta/BUILD | 1 - .../providers/gce/cloud/meta/meta.go | 13 ++ .../providers/gce/cloud/meta/method.go | 192 +++++++++++++----- .../providers/gce/cloud/mock_test.go | 30 +-- .../providers/gce/cloud/utils.go | 2 +- .../providers/gce/cloud/utils_test.go | 8 +- 7 files changed, 237 insertions(+), 123 deletions(-) diff --git a/pkg/cloudprovider/providers/gce/cloud/gen/main.go b/pkg/cloudprovider/providers/gce/cloud/gen/main.go index d6e16f16ec4..b7a2551a814 100644 --- a/pkg/cloudprovider/providers/gce/cloud/gen/main.go +++ b/pkg/cloudprovider/providers/gce/cloud/gen/main.go @@ -276,7 +276,7 @@ type {{.WrapType}} interface { {{.WrapTypeOps}} {{- end}} {{- if .GenerateGet}} - Get(ctx context.Context, key meta.Key) (*{{.FQObjectType}}, error) + Get(ctx context.Context, key *meta.Key) (*{{.FQObjectType}}, error) {{- end -}} {{- if .GenerateList}} {{- if .KeyIsGlobal}} @@ -290,10 +290,10 @@ type {{.WrapType}} interface { {{- end -}} {{- end -}} {{- if .GenerateInsert}} - Insert(ctx context.Context, key meta.Key, obj *{{.FQObjectType}}) error + Insert(ctx context.Context, key *meta.Key, obj *{{.FQObjectType}}) error {{- end -}} {{- if .GenerateDelete}} - Delete(ctx context.Context, key meta.Key) error + Delete(ctx context.Context, key *meta.Key) error {{- end -}} {{- if .AggregatedList}} AggregatedList(ctx context.Context, fl *filter.F) (map[string][]*{{.FQObjectType}}, error) @@ -352,7 +352,7 @@ type {{.MockWrapType}} struct { // execution flow of the mock. Return (false, nil, nil) to continue with // normal mock behavior/ after the hook function executes. {{- if .GenerateGet}} - GetHook func(m *{{.MockWrapType}}, ctx context.Context, key meta.Key) (bool, *{{.FQObjectType}}, error) + GetHook func(m *{{.MockWrapType}}, ctx context.Context, key *meta.Key) (bool, *{{.FQObjectType}}, error) {{- end -}} {{- if .GenerateList}} {{- if .KeyIsGlobal}} @@ -366,10 +366,10 @@ type {{.MockWrapType}} struct { {{- end}} {{- end -}} {{- if .GenerateInsert}} - InsertHook func(m *{{.MockWrapType}}, ctx context.Context, key meta.Key, obj *{{.FQObjectType}}) (bool, error) + InsertHook func(m *{{.MockWrapType}}, ctx context.Context, key *meta.Key, obj *{{.FQObjectType}}) (bool, error) {{- end -}} {{- if .GenerateDelete}} - DeleteHook func(m *{{.MockWrapType}}, ctx context.Context, key meta.Key) (bool, error) + DeleteHook func(m *{{.MockWrapType}}, ctx context.Context, key *meta.Key) (bool, error) {{- end -}} {{- if .AggregatedList}} AggregatedListHook func(m *{{.MockWrapType}}, ctx context.Context, fl *filter.F) (bool, map[string][]*{{.FQObjectType}}, error) @@ -388,7 +388,7 @@ type {{.MockWrapType}} struct { {{- if .GenerateGet}} // Get returns the object from the mock. -func (m *{{.MockWrapType}}) Get(ctx context.Context, key meta.Key) (*{{.FQObjectType}}, error) { +func (m *{{.MockWrapType}}) Get(ctx context.Context, key *meta.Key) (*{{.FQObjectType}}, error) { if m.GetHook != nil { if intercept, obj, err := m.GetHook(m, ctx, key); intercept { glog.V(5).Infof("{{.MockWrapType}}.Get(%v, %s) = %+v, %v", ctx, key, obj ,err) @@ -399,11 +399,11 @@ func (m *{{.MockWrapType}}) Get(ctx context.Context, key meta.Key) (*{{.FQObject m.Lock.Lock() defer m.Lock.Unlock() - if err, ok := m.GetError[key]; ok { + if err, ok := m.GetError[*key]; ok { glog.V(5).Infof("{{.MockWrapType}}.Get(%v, %s) = nil, %v", ctx, key, err) return nil, err } - if obj, ok := m.Objects[key]; ok { + if obj, ok := m.Objects[*key]; ok { typedObj := obj.To{{.VersionTitle}}() glog.V(5).Infof("{{.MockWrapType}}.Get(%v, %s) = %+v, nil", ctx, key, typedObj) return typedObj, nil @@ -503,7 +503,7 @@ func (m *{{.MockWrapType}}) List(ctx context.Context, zone string, fl *filter.F) {{- if .GenerateInsert}} // Insert is a mock for inserting/creating a new object. -func (m *{{.MockWrapType}}) Insert(ctx context.Context, key meta.Key, obj *{{.FQObjectType}}) error { +func (m *{{.MockWrapType}}) Insert(ctx context.Context, key *meta.Key, obj *{{.FQObjectType}}) error { if m.InsertHook != nil { if intercept, err := m.InsertHook(m, ctx, key, obj); intercept { glog.V(5).Infof("{{.MockWrapType}}.Insert(%v, %v, %+v) = %v", ctx, key, obj, err) @@ -514,11 +514,11 @@ func (m *{{.MockWrapType}}) Insert(ctx context.Context, key meta.Key, obj *{{.FQ m.Lock.Lock() defer m.Lock.Unlock() - if err, ok := m.InsertError[key]; ok { + if err, ok := m.InsertError[*key]; ok { glog.V(5).Infof("{{.MockWrapType}}.Insert(%v, %v, %+v) = %v", ctx, key, obj, err) return err } - if _, ok := m.Objects[key]; ok { + if _, ok := m.Objects[*key]; ok { err := &googleapi.Error{ Code: http.StatusConflict, Message: fmt.Sprintf("{{.MockWrapType}} %v exists", key), @@ -532,7 +532,7 @@ func (m *{{.MockWrapType}}) Insert(ctx context.Context, key meta.Key, obj *{{.FQ obj.SelfLink = SelfLink(meta.Version{{.VersionTitle}}, "mock-project", "{{.Resource}}", key) } - m.Objects[key] = &Mock{{.Service}}Obj{obj} + m.Objects[*key] = &Mock{{.Service}}Obj{obj} glog.V(5).Infof("{{.MockWrapType}}.Insert(%v, %v, %+v) = nil", ctx, key, obj) return nil } @@ -540,7 +540,7 @@ func (m *{{.MockWrapType}}) Insert(ctx context.Context, key meta.Key, obj *{{.FQ {{- if .GenerateDelete}} // Delete is a mock for deleting the object. -func (m *{{.MockWrapType}}) Delete(ctx context.Context, key meta.Key) error { +func (m *{{.MockWrapType}}) Delete(ctx context.Context, key *meta.Key) error { if m.DeleteHook != nil { if intercept, err := m.DeleteHook(m, ctx, key); intercept { glog.V(5).Infof("{{.MockWrapType}}.Delete(%v, %v) = %v", ctx, key, err) @@ -551,11 +551,11 @@ func (m *{{.MockWrapType}}) Delete(ctx context.Context, key meta.Key) error { m.Lock.Lock() defer m.Lock.Unlock() - if err, ok := m.DeleteError[key]; ok { + if err, ok := m.DeleteError[*key]; ok { glog.V(5).Infof("{{.MockWrapType}}.Delete(%v, %v) = %v", ctx, key, err) return err } - if _, ok := m.Objects[key]; !ok { + if _, ok := m.Objects[*key]; !ok { err := &googleapi.Error{ Code: http.StatusNotFound, Message: fmt.Sprintf("{{.MockWrapType}} %v not found", key), @@ -564,7 +564,7 @@ func (m *{{.MockWrapType}}) Delete(ctx context.Context, key meta.Key) error { return err } - delete(m.Objects, key) + delete(m.Objects, *key) glog.V(5).Infof("{{.MockWrapType}}.Delete(%v, %v) = nil", ctx, key) return nil } @@ -621,16 +621,21 @@ func (m *{{.MockWrapType}}) Obj(o *{{.FQObjectType}}) *Mock{{.Service}}Obj { {{- range .}} // {{.Name}} is a mock for the corresponding method. func (m *{{.MockWrapType}}) {{.FcnArgs}} { -{{- if eq .ReturnType "Operation"}} +{{- if .IsOperation }} if m.{{.MockHookName}} != nil { return m.{{.MockHookName}}(m, ctx, key {{.CallArgs}}) } return nil -{{- else}} +{{- else if .IsGet}} if m.{{.MockHookName}} != nil { return m.{{.MockHookName}}(m, ctx, key {{.CallArgs}}) } return nil, fmt.Errorf("{{.MockHookName}} must be set") +{{- else if .IsPaged}} + if m.{{.MockHookName}} != nil { + return m.{{.MockHookName}}(m, ctx, key {{.CallArgs}}, fl) + } + return nil, nil {{- end}} } {{end -}} @@ -642,7 +647,7 @@ type {{.GCEWrapType}} struct { {{- if .GenerateGet}} // Get the {{.Object}} named by key. -func (g *{{.GCEWrapType}}) Get(ctx context.Context, key meta.Key) (*{{.FQObjectType}}, error) { +func (g *{{.GCEWrapType}}) Get(ctx context.Context, key *meta.Key) (*{{.FQObjectType}}, error) { projectID := g.s.ProjectRouter.ProjectID(ctx, "{{.Version}}", "{{.Service}}") rk := &RateLimitKey{ ProjectID: projectID, @@ -714,7 +719,7 @@ rk := &RateLimitKey{ {{- if .GenerateInsert}} // Insert {{.Object}} with key of value obj. -func (g *{{.GCEWrapType}}) Insert(ctx context.Context, key meta.Key, obj *{{.FQObjectType}}) error { +func (g *{{.GCEWrapType}}) Insert(ctx context.Context, key *meta.Key, obj *{{.FQObjectType}}) error { projectID := g.s.ProjectRouter.ProjectID(ctx, "{{.Version}}", "{{.Service}}") rk := &RateLimitKey{ ProjectID: projectID, @@ -747,7 +752,7 @@ func (g *{{.GCEWrapType}}) Insert(ctx context.Context, key meta.Key, obj *{{.FQO {{- if .GenerateDelete}} // Delete the {{.Object}} referenced by key. -func (g *{{.GCEWrapType}}) Delete(ctx context.Context, key meta.Key) error { +func (g *{{.GCEWrapType}}) Delete(ctx context.Context, key *meta.Key) error { projectID := g.s.ProjectRouter.ProjectID(ctx, "{{.Version}}", "{{.Service}}") rk := &RateLimitKey{ ProjectID: projectID, @@ -823,7 +828,7 @@ func (g *{{.GCEWrapType}}) {{.FcnArgs}} { Service: "{{.Service}}", } if err := g.s.RateLimiter.Accept(ctx, rk); err != nil { - {{- if eq .ReturnType "Operation"}} + {{- if .IsOperation}} return err {{- else}} return nil, err @@ -838,15 +843,26 @@ func (g *{{.GCEWrapType}}) {{.FcnArgs}} { {{- if .KeyIsZonal}} call := g.s.{{.VersionTitle}}.{{.Service}}.{{.Name}}(projectID, key.Zone, key.Name {{.CallArgs}}) {{- end}} +{{- if .IsOperation}} call.Context(ctx) -{{- if eq .ReturnType "Operation"}} op, err := call.Do() if err != nil { return err } return g.s.WaitForCompletion(ctx, op) -{{- else}} +{{- else if .IsGet}} + call.Context(ctx) return call.Do() +{{- else if .IsPaged}} + var all []*{{.Version}}.{{.ItemType}} + f := func(l *{{.Version}}.{{.ReturnType}}) error { + all = append(all, l.Items...) + return nil + } + if err := call.Pages(ctx, f); err != nil { + return nil, err + } + return all, nil {{- end}} } {{end -}} @@ -933,17 +949,17 @@ func Test{{.Service}}Group(t *testing.T) { // Get not found. {{- if .HasAlpha}}{{- if .Alpha.GenerateGet}} - if _, err := mock.Alpha{{.Service}}().Get(ctx, *key); err == nil { + if _, err := mock.Alpha{{.Service}}().Get(ctx, key); err == nil { t.Errorf("Alpha{{.Service}}().Get(%v, %v) = _, nil; want error", ctx, key) } {{- end}}{{- end}} {{- if .HasBeta}}{{- if .Beta.GenerateGet}} - if _, err := mock.Beta{{.Service}}().Get(ctx, *key); err == nil { + if _, err := mock.Beta{{.Service}}().Get(ctx, key); err == nil { t.Errorf("Beta{{.Service}}().Get(%v, %v) = _, nil; want error", ctx, key) } {{- end}}{{- end}} {{- if .HasGA}}{{- if .GA.GenerateGet}} - if _, err := mock.{{.Service}}().Get(ctx, *key); err == nil { + if _, err := mock.{{.Service}}().Get(ctx, key); err == nil { t.Errorf("{{.Service}}().Get(%v, %v) = _, nil; want error", ctx, key) } {{- end}}{{- end}} @@ -952,41 +968,41 @@ func Test{{.Service}}Group(t *testing.T) { {{- if .HasAlpha}}{{- if .Alpha.GenerateInsert}} { obj := &alpha.{{.Alpha.Object}}{} - if err := mock.Alpha{{.Service}}().Insert(ctx, *keyAlpha, obj); err != nil { - t.Errorf("Alpha{{.Service}}().Insert(%v, %v, %v) = %v; want nil", ctx, key, obj, err) + if err := mock.Alpha{{.Service}}().Insert(ctx, keyAlpha, obj); err != nil { + t.Errorf("Alpha{{.Service}}().Insert(%v, %v, %v) = %v; want nil", ctx, keyAlpha, obj, err) } } {{- end}}{{- end}} {{- if .HasBeta}}{{- if .Beta.GenerateInsert}} { obj := &beta.{{.Beta.Object}}{} - if err := mock.Beta{{.Service}}().Insert(ctx, *keyBeta, obj); err != nil { - t.Errorf("Beta{{.Service}}().Insert(%v, %v, %v) = %v; want nil", ctx, key, obj, err) + if err := mock.Beta{{.Service}}().Insert(ctx, keyBeta, obj); err != nil { + t.Errorf("Beta{{.Service}}().Insert(%v, %v, %v) = %v; want nil", ctx, keyBeta, obj, err) } } {{- end}}{{- end}} {{- if .HasGA}}{{- if .GA.GenerateInsert}} { obj := &ga.{{.GA.Object}}{} - if err := mock.{{.Service}}().Insert(ctx, *keyGA, obj); err != nil { - t.Errorf("{{.Service}}().Insert(%v, %v, %v) = %v; want nil", ctx, key, obj, err) + if err := mock.{{.Service}}().Insert(ctx, keyGA, obj); err != nil { + t.Errorf("{{.Service}}().Insert(%v, %v, %v) = %v; want nil", ctx, keyGA, obj, err) } } {{- end}}{{- end}} // Get across versions. {{- if .HasAlpha}}{{- if .Alpha.GenerateInsert}} - if obj, err := mock.Alpha{{.Service}}().Get(ctx, *key); err != nil { + if obj, err := mock.Alpha{{.Service}}().Get(ctx, key); err != nil { t.Errorf("Alpha{{.Service}}().Get(%v, %v) = %v, %v; want nil", ctx, key, obj, err) } {{- end}}{{- end}} {{- if .HasBeta}}{{- if .Beta.GenerateInsert}} - if obj, err := mock.Beta{{.Service}}().Get(ctx, *key); err != nil { + if obj, err := mock.Beta{{.Service}}().Get(ctx, key); err != nil { t.Errorf("Beta{{.Service}}().Get(%v, %v) = %v, %v; want nil", ctx, key, obj, err) } {{- end}}{{- end}} {{- if .HasGA}}{{- if .GA.GenerateInsert}} - if obj, err := mock.{{.Service}}().Get(ctx, *key); err != nil { + if obj, err := mock.{{.Service}}().Get(ctx, key); err != nil { t.Errorf("{{.Service}}().Get(%v, %v) = %v, %v; want nil", ctx, key, obj, err) } {{- end}}{{- end}} @@ -1077,35 +1093,35 @@ func Test{{.Service}}Group(t *testing.T) { // Delete across versions. {{- if .HasAlpha}}{{- if .Alpha.GenerateDelete}} - if err := mock.Alpha{{.Service}}().Delete(ctx, *keyAlpha); err != nil { - t.Errorf("Alpha{{.Service}}().Delete(%v, %v) = %v; want nil", ctx, key, err) + if err := mock.Alpha{{.Service}}().Delete(ctx, keyAlpha); err != nil { + t.Errorf("Alpha{{.Service}}().Delete(%v, %v) = %v; want nil", ctx, keyAlpha, err) } {{- end}}{{- end}} {{- if .HasBeta}}{{- if .Beta.GenerateDelete}} - if err := mock.Beta{{.Service}}().Delete(ctx, *keyBeta); err != nil { - t.Errorf("Beta{{.Service}}().Delete(%v, %v) = %v; want nil", ctx, key, err) + if err := mock.Beta{{.Service}}().Delete(ctx, keyBeta); err != nil { + t.Errorf("Beta{{.Service}}().Delete(%v, %v) = %v; want nil", ctx, keyBeta, err) } {{- end}}{{- end}} {{- if .HasGA}}{{- if .GA.GenerateDelete}} - if err := mock.{{.Service}}().Delete(ctx, *keyGA); err != nil { - t.Errorf("{{.Service}}().Delete(%v, %v) = %v; want nil", ctx, key, err) + if err := mock.{{.Service}}().Delete(ctx, keyGA); err != nil { + t.Errorf("{{.Service}}().Delete(%v, %v) = %v; want nil", ctx, keyGA, err) } {{- end}}{{- end}} // Delete not found. {{- if .HasAlpha}}{{- if .Alpha.GenerateDelete}} - if err := mock.Alpha{{.Service}}().Delete(ctx, *keyAlpha); err == nil { - t.Errorf("Alpha{{.Service}}().Delete(%v, %v) = nil; want error", ctx, key) + if err := mock.Alpha{{.Service}}().Delete(ctx, keyAlpha); err == nil { + t.Errorf("Alpha{{.Service}}().Delete(%v, %v) = nil; want error", ctx, keyAlpha) } {{- end}}{{- end}} {{- if .HasBeta}}{{- if .Beta.GenerateDelete}} - if err := mock.Beta{{.Service}}().Delete(ctx, *keyBeta); err == nil { - t.Errorf("Beta{{.Service}}().Delete(%v, %v) = nil; want error", ctx, key) + if err := mock.Beta{{.Service}}().Delete(ctx, keyBeta); err == nil { + t.Errorf("Beta{{.Service}}().Delete(%v, %v) = nil; want error", ctx, keyBeta) } {{- end}}{{- end}} {{- if .HasGA}}{{- if .GA.GenerateDelete}} - if err := mock.{{.Service}}().Delete(ctx, *keyGA); err == nil { - t.Errorf("{{.Service}}().Delete(%v, %v) = nil; want error", ctx, key) + if err := mock.{{.Service}}().Delete(ctx, keyGA); err == nil { + t.Errorf("{{.Service}}().Delete(%v, %v) = nil; want error", ctx, keyGA) } {{- end}}{{- end}} } diff --git a/pkg/cloudprovider/providers/gce/cloud/meta/BUILD b/pkg/cloudprovider/providers/gce/cloud/meta/BUILD index 4bcf3b5f5ba..9962edeb74f 100644 --- a/pkg/cloudprovider/providers/gce/cloud/meta/BUILD +++ b/pkg/cloudprovider/providers/gce/cloud/meta/BUILD @@ -12,7 +12,6 @@ go_library( importpath = "k8s.io/kubernetes/pkg/cloudprovider/providers/gce/cloud/meta", visibility = ["//visibility:public"], deps = [ - "//vendor/github.com/golang/glog: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", diff --git a/pkg/cloudprovider/providers/gce/cloud/meta/meta.go b/pkg/cloudprovider/providers/gce/cloud/meta/meta.go index e1f36904d01..a636b5a1feb 100644 --- a/pkg/cloudprovider/providers/gce/cloud/meta/meta.go +++ b/pkg/cloudprovider/providers/gce/cloud/meta/meta.go @@ -115,6 +115,18 @@ var AllServices = []*ServiceInfo{ serviceType: reflect.TypeOf(&alpha.BackendServicesService{}), additionalMethods: []string{"Update"}, }, + { + Object: "BackendService", + Service: "RegionBackendServices", + Resource: "backendServices", + version: VersionGA, + keyType: Regional, + serviceType: reflect.TypeOf(&ga.RegionBackendServicesService{}), + additionalMethods: []string{ + "GetHealth", + "Update", + }, + }, { Object: "BackendService", Service: "RegionBackendServices", @@ -285,6 +297,7 @@ var AllServices = []*ServiceInfo{ additionalMethods: []string{ "AttachNetworkEndpoints", "DetachNetworkEndpoints", + "ListNetworkEndpoints", }, options: AggregatedList, }, diff --git a/pkg/cloudprovider/providers/gce/cloud/meta/method.go b/pkg/cloudprovider/providers/gce/cloud/meta/method.go index c3a33d801d3..db3710fa333 100644 --- a/pkg/cloudprovider/providers/gce/cloud/meta/method.go +++ b/pkg/cloudprovider/providers/gce/cloud/meta/method.go @@ -20,8 +20,6 @@ import ( "fmt" "reflect" "strings" - - "github.com/golang/glog" ) func newArg(t reflect.Type) *arg { @@ -86,23 +84,60 @@ func (a *arg) String() string { // newMethod returns a newly initialized method. func newMethod(s *ServiceInfo, m reflect.Method) *Method { - ret := &Method{s, m, ""} + 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 (mr *Method) argsSkip() int { - switch mr.keyType { +func (m *Method) argsSkip() int { + switch m.keyType { case Zonal: return 4 case Regional: @@ -110,15 +145,15 @@ func (mr *Method) argsSkip() int { case Global: return 3 } - panic(fmt.Errorf("invalid KeyType %v", mr.keyType)) + 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). prefix will be added to the parameters. -func (mr *Method) args(skip int, nameArgs bool, prefix []string) []string { +func (m *Method) args(skip int, nameArgs bool, prefix []string) []string { var args []*arg - fType := mr.m.Func.Type() + fType := m.m.Func.Type() for i := 0; i < fType.NumIn(); i++ { t := fType.In(i) args = append(args, newArg(t)) @@ -135,24 +170,26 @@ func (mr *Method) args(skip int, nameArgs bool, prefix []string) []string { return append(prefix, a...) } -// init the method, preforming some rudimentary static checking. -func (mr *Method) init() { - fType := mr.m.Func.Type() - if fType.NumIn() < mr.argsSkip() { +// 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)", - mr.Service, mr.Name(), fType.NumIn(), mr.argsSkip()) + 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 < mr.argsSkip(); i++ { + 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", mr.Service, mr.Name())) + 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", - mr.Service, mr.Name())) + m.Service, m.Name())) } returnType := fType.Out(0) returnTypeName := fType.Out(0).Elem().Name() @@ -160,48 +197,64 @@ func (mr *Method) init() { doMethod, ok := returnType.MethodByName("Do") if !ok { panic(fmt.Errorf("method %q.%q: return type %q does not have a Do() method", - mr.Service, mr.Name(), returnTypeName)) + m.Service, m.Name(), returnTypeName)) } + _, hasPages := returnType.MethodByName("Pages") // Do() method must return (*T, error). switch doMethod.Func.Type().NumOut() { case 2: - glog.Infof("Method %q.%q: return type %q of Do() = %v, %v", - mr.Service, mr.Name(), returnTypeName, doMethod.Func.Type().Out(0), doMethod.Func.Type().Out(1)) 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)", - mr.Service, mr.Name(), returnTypeName, out0)) + m.Service, m.Name(), returnTypeName, out0)) } - mr.ReturnType = out0.Elem().Name() - if out0.Elem().Name() == "Operation" { - glog.Infof("Method %q.%q is an *Operation", mr.Service, mr.Name()) - } else { - glog.Infof("Method %q.%q returns %v", mr.Service, mr.Name(), 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'", - mr.Service, mr.Name(), returnTypeName)) + m.Service, m.Name(), returnTypeName)) } break default: panic(fmt.Errorf("method %q.%q: %q Do() return type is not handled by the generator", - mr.Service, mr.Name(), returnTypeName)) + m.Service, m.Name(), returnTypeName)) } } // Name is the name of the method. -func (mr *Method) Name() string { - return mr.m.Name +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 (mr *Method) CallArgs() string { +func (m *Method) CallArgs() string { var args []string - for i := mr.argsSkip(); i < mr.m.Func.Type().NumIn(); i++ { - args = append(args, fmt.Sprintf("arg%d", i-mr.argsSkip())) + 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 "" @@ -210,41 +263,74 @@ func (mr *Method) CallArgs() string { } // MockHookName is the name of the hook function in the mock. -func (mr *Method) MockHookName() string { - return mr.m.Name + "Hook" +func (m *Method) MockHookName() string { + return m.m.Name + "Hook" } // MockHook is the definition of the hook function. -func (mr *Method) MockHook() string { - args := mr.args(mr.argsSkip(), false, []string{ - fmt.Sprintf("*%s", mr.MockWrapType()), +func (m *Method) MockHook() string { + args := m.args(m.argsSkip(), false, []string{ + fmt.Sprintf("*%s", m.MockWrapType()), "context.Context", - "meta.Key", + "*meta.Key", }) - if mr.ReturnType == "Operation" { - return fmt.Sprintf("%v func(%v) error", mr.MockHookName(), strings.Join(args, ", ")) + if m.kind == MethodPaged { + args = append(args, "*filter.F") + } + + 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)) } - return fmt.Sprintf("%v func(%v) (*%v.%v, error)", mr.MockHookName(), strings.Join(args, ", "), mr.Version(), mr.ReturnType) } // FcnArgs is the function signature for the definition of the method. -func (mr *Method) FcnArgs() string { - args := mr.args(mr.argsSkip(), true, []string{ +func (m *Method) FcnArgs() string { + args := m.args(m.argsSkip(), true, []string{ "ctx context.Context", - "key meta.Key", + "key *meta.Key", }) - - if mr.ReturnType == "Operation" { - return fmt.Sprintf("%v(%v) error", mr.m.Name, strings.Join(args, ", ")) + 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)) } - return fmt.Sprintf("%v(%v) (*%v.%v, error)", mr.m.Name, strings.Join(args, ", "), mr.Version(), mr.ReturnType) } // InterfaceFunc is the function declaration of the method in the interface. -func (mr *Method) InterfaceFunc() string { - args := mr.args(mr.argsSkip(), false, []string{"context.Context", "meta.Key"}) - if mr.ReturnType == "Operation" { - return fmt.Sprintf("%v(%v) error", mr.m.Name, strings.Join(args, ", ")) +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)) } - return fmt.Sprintf("%v(%v) (*%v.%v, error)", mr.m.Name, strings.Join(args, ", "), mr.Version(), mr.ReturnType) } diff --git a/pkg/cloudprovider/providers/gce/cloud/mock_test.go b/pkg/cloudprovider/providers/gce/cloud/mock_test.go index 3d0fb160cc0..0b43cabeb79 100644 --- a/pkg/cloudprovider/providers/gce/cloud/mock_test.go +++ b/pkg/cloudprovider/providers/gce/cloud/mock_test.go @@ -45,42 +45,42 @@ func TestMocks(t *testing.T) { key := keyAlpha // Get not found. - if _, err := mock.AlphaAddresses().Get(ctx, *key); err == nil { + 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 { + 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 { + 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 { + 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 { + 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 { + 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 { + 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 { + 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 { + 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. @@ -128,23 +128,23 @@ func TestMocks(t *testing.T) { } } // Delete across versions. - if err := mock.AlphaAddresses().Delete(ctx, *keyAlpha); err != nil { + 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 { + 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 { + 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 { + 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 { + 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 { + if err := mock.Addresses().Delete(ctx, keyGA); err == nil { t.Errorf("Addresses().Delete(%v, %v) = nil; want error", ctx, key) } } diff --git a/pkg/cloudprovider/providers/gce/cloud/utils.go b/pkg/cloudprovider/providers/gce/cloud/utils.go index dd4a07cfd05..cbba6862e0e 100644 --- a/pkg/cloudprovider/providers/gce/cloud/utils.go +++ b/pkg/cloudprovider/providers/gce/cloud/utils.go @@ -142,7 +142,7 @@ func copyViaJSON(dest, src interface{}) error { } // SelfLink returns the self link URL for the given object. -func SelfLink(ver meta.Version, project, resource string, key meta.Key) string { +func SelfLink(ver meta.Version, project, resource string, key *meta.Key) string { var prefix string switch ver { case meta.VersionAlpha: diff --git a/pkg/cloudprovider/providers/gce/cloud/utils_test.go b/pkg/cloudprovider/providers/gce/cloud/utils_test.go index 562d0f35ba7..4879ebbe33a 100644 --- a/pkg/cloudprovider/providers/gce/cloud/utils_test.go +++ b/pkg/cloudprovider/providers/gce/cloud/utils_test.go @@ -165,28 +165,28 @@ func TestSelfLink(t *testing.T) { ver meta.Version project string resource string - key meta.Key + key *meta.Key want string }{ { meta.VersionAlpha, "proj1", "addresses", - *meta.RegionalKey("key1", "us-central1"), + 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"), + 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"), + meta.GlobalKey("key3"), "https://www.googleapis.com/compute/v1/projects/proj4/urlMaps/key3", }, } {