diff --git a/pkg/cloudprovider/providers/gce/cloud/gen/main.go b/pkg/cloudprovider/providers/gce/cloud/gen/main.go new file mode 100644 index 00000000000..f8dcd730b9f --- /dev/null +++ b/pkg/cloudprovider/providers/gce/cloud/gen/main.go @@ -0,0 +1,1140 @@ +/* +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. +*/ + +// Generator for GCE compute wrapper code. You must regenerate the code after +// modifying this file: +// +// $ go run gen/main.go > gen.go +package main + +import ( + "bytes" + "flag" + "fmt" + "io" + "os" + "os/exec" + "text/template" + "time" + + "k8s.io/kubernetes/pkg/cloudprovider/providers/gce/cloud/meta" + "github.com/golang/glog" +) + +const ( + gofmt = "gofmt" + packageRoot = "k8s.io/kubernetes/pkg/cloudprovider/providers/gce/cloud" + + // readOnly specifies that the given resource is read-only and should not + // have insert() or delete() methods generated for the wrapper. + readOnly = iota +) + +var flags = struct { + gofmt bool + mode string +}{} + +func init() { + flag.BoolVar(&flags.gofmt, "gofmt", true, "run output through gofmt") + flag.StringVar(&flags.mode, "mode", "src", "content to generate: src, test, dummy") +} + +// gofmtContent runs "gofmt" on the given contents. +func gofmtContent(r io.Reader) string { + cmd := exec.Command(gofmt, "-s") + out := &bytes.Buffer{} + cmd.Stdin = r + cmd.Stdout = out + cmdErr := &bytes.Buffer{} + cmd.Stderr = cmdErr + + if err := cmd.Run(); err != nil { + fmt.Fprintf(os.Stderr, cmdErr.String()) + panic(err) + } + return out.String() +} + +// genHeader generate the header for the file. +func genHeader(wr io.Writer) { + const text = `/* +Copyright {{.Year}} The Kubernetes Authors. + +Licensed under the Apache License, Version 2.0 (the "License"); +you may not use this file except in compliance with the License. +You may obtain a copy of the License at + + http://www.apache.org/licenses/LICENSE-2.0 + +Unless required by applicable law or agreed to in writing, software +distributed under the License is distributed on an "AS IS" BASIS, +WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +See the License for the specific language governing permissions and +limitations under the License. +*/ + +// This file was generated by "go run gen/main.go > gen.go". Do not edit +// directly. + +package cloud + +import ( + "context" + "fmt" + "net/http" + "sync" + + "google.golang.org/api/googleapi" + "github.com/golang/glog" + + "{{.PackageRoot}}/filter" + "{{.PackageRoot}}/meta" + +` + tmpl := template.Must(template.New("header").Parse(text)) + values := map[string]string{ + "Year": fmt.Sprintf("%v", time.Now().Year()), + "PackageRoot": packageRoot, + } + if err := tmpl.Execute(wr, values); err != nil { + panic(err) + } + + var hasGA, hasAlpha, hasBeta bool + for _, s := range meta.AllServices { + switch s.Version() { + case meta.VersionGA: + hasGA = true + case meta.VersionAlpha: + hasAlpha = true + case meta.VersionBeta: + hasBeta = true + } + } + if hasAlpha { + fmt.Fprintln(wr, ` alpha "google.golang.org/api/compute/v0.alpha"`) + } + if hasBeta { + fmt.Fprintln(wr, ` beta "google.golang.org/api/compute/v0.beta"`) + } + if hasGA { + fmt.Fprintln(wr, ` ga "google.golang.org/api/compute/v1"`) + } + fmt.Fprintf(wr, ")\n\n") +} + +// genStubs generates the interface and wrapper stubs. +func genStubs(wr io.Writer) { + const text = `// Cloud is an interface for the GCE compute API. +type Cloud interface { +{{- range .All}} + {{.WrapType}}() {{.WrapType}} +{{- end}} +} + +// NewGCE returns a GCE. +func NewGCE(s *Service) *GCE { + g := &GCE{ + {{- range .All}} + {{.Field}}: &{{.GCEWrapType}}{s}, + {{- end}} + } + return g +} + +// GCE implements Cloud. +var _ Cloud = (*GCE)(nil) + +// GCE is the golang adapter for the compute APIs. +type GCE struct { +{{- range .All}} + {{.Field}} *{{.GCEWrapType}} +{{- end}} +} + +{{range .All}} +func (gce *GCE) {{.WrapType}}() {{.WrapType}} { + return gce.{{.Field}} +} +{{- end}} + +// NewMockGCE returns a new mock for GCE. +func NewMockGCE() *MockGCE { + {{- range .Groups}} + mock{{.Service}}Objs := map[meta.Key]*Mock{{.Service}}Obj{} + {{- end}} + + mock := &MockGCE{ + {{- range .All}} + {{.MockField}}: New{{.MockWrapType}}(mock{{.Service}}Objs), + {{- end}} + } + return mock +} + +// MockGCE implements Cloud. +var _ Cloud = (*MockGCE)(nil) + +// MockGCE is the mock for the compute API. +type MockGCE struct { +{{- range .All}} + {{.MockField}} *{{.MockWrapType}} +{{- end}} +} +{{range .All}} +func (mock *MockGCE) {{.WrapType}}() {{.WrapType}} { + return mock.{{.MockField}} +} +{{end}} + +{{range .Groups}} +// Mock{{.Service}}Obj is used to store the various object versions in the shared +// map of mocked objects. This allows for multiple API versions to co-exist and +// share the same "view" of the objects in the backend. +type Mock{{.Service}}Obj struct { + Obj interface{} +} +{{- if .HasAlpha}} +// ToAlpha retrieves the given version of the object. +func (m *Mock{{.Service}}Obj) ToAlpha() *{{.Alpha.FQObjectType}} { + if ret, ok := m.Obj.(*{{.Alpha.FQObjectType}}); ok { + return ret + } + // Convert the object via JSON copying to the type that was requested. + ret := &{{.Alpha.FQObjectType}}{} + if err := copyViaJSON(ret, m.Obj); err != nil { + glog.Errorf("Could not convert %T to *{{.Alpha.FQObjectType}} via JSON: %v", m.Obj, err) + } + return ret +} +{{- end}} +{{- if .HasBeta}} +// ToBeta retrieves the given version of the object. +func (m *Mock{{.Service}}Obj) ToBeta() *{{.Beta.FQObjectType}} { + if ret, ok := m.Obj.(*{{.Beta.FQObjectType}}); ok { + return ret + } + // Convert the object via JSON copying to the type that was requested. + ret := &{{.Beta.FQObjectType}}{} + if err := copyViaJSON(ret, m.Obj); err != nil { + glog.Errorf("Could not convert %T to *{{.Beta.FQObjectType}} via JSON: %v", m.Obj, err) + } + return ret +} +{{- end}} +{{- if .HasGA}} +// ToGA retrieves the given version of the object. +func (m *Mock{{.Service}}Obj) ToGA() *{{.GA.FQObjectType}} { + if ret, ok := m.Obj.(*{{.GA.FQObjectType}}); ok { + return ret + } + // Convert the object via JSON copying to the type that was requested. + ret := &{{.GA.FQObjectType}}{} + if err := copyViaJSON(ret, m.Obj); err != nil { + glog.Errorf("Could not convert %T to *{{.GA.FQObjectType}} via JSON: %v", m.Obj, err) + } + return ret +} +{{- end}} +{{- end}} +` + data := struct { + All []*meta.ServiceInfo + Groups map[string]*meta.ServiceGroup + }{meta.AllServices, meta.AllServicesByGroup} + + tmpl := template.Must(template.New("interface").Parse(text)) + if err := tmpl.Execute(wr, data); err != nil { + panic(err) + } +} + +// genTypes generates the type wrappers. +func genTypes(wr io.Writer) { + const text = `// {{.WrapType}} is an interface that allows for mocking of {{.Service}}. +type {{.WrapType}} interface { +{{- if .GenerateCustomOps}} + // {{.WrapTypeOps}} is an interface with additional non-CRUD type methods. + // This interface is expected to be implemented by hand (non-autogenerated). + {{.WrapTypeOps}} +{{- end}} +{{- if .GenerateGet}} + Get(ctx context.Context, key meta.Key) (*{{.FQObjectType}}, error) +{{- end -}} +{{- if .GenerateList}} +{{- if .KeyIsGlobal}} + List(ctx context.Context, fl *filter.F) ([]*{{.FQObjectType}}, error) +{{- end -}} +{{- if .KeyIsRegional}} + List(ctx context.Context, region string, fl *filter.F) ([]*{{.FQObjectType}}, error) +{{- end -}} +{{- if .KeyIsZonal}} + List(ctx context.Context, zone string, fl *filter.F) ([]*{{.FQObjectType}}, error) +{{- end -}} +{{- end -}} +{{- if .GenerateInsert}} + Insert(ctx context.Context, key meta.Key, obj *{{.FQObjectType}}) error +{{- end -}} +{{- if .GenerateDelete}} + Delete(ctx context.Context, key meta.Key) error +{{- end -}} +{{- if .AggregatedList}} + AggregatedList(ctx context.Context, fl *filter.F) (map[string][]*{{.FQObjectType}}, error) +{{- end}} +{{- with .Methods -}} +{{- range .}} + {{.InterfaceFunc}} +{{- end -}} +{{- end}} +} + +// New{{.MockWrapType}} returns a new mock for {{.Service}}. +func New{{.MockWrapType}}(objs map[meta.Key]*Mock{{.Service}}Obj) *{{.MockWrapType}} { + mock := &{{.MockWrapType}}{ + Objects: objs, + {{- if .GenerateGet}} + GetError: map[meta.Key]error{}, + {{- end -}} + {{- if .GenerateInsert}} + InsertError: map[meta.Key]error{}, + {{- end -}} + {{- if .GenerateDelete}} + DeleteError: map[meta.Key]error{}, + {{- end}} + } + return mock +} + +// {{.MockWrapType}} is the mock for {{.Service}}. +type {{.MockWrapType}} struct { + Lock sync.Mutex + + // Objects maintained by the mock. + Objects map[meta.Key]*Mock{{.Service}}Obj + + // If an entry exists for the given key and operation, then the error + // will be returned instead of the operation. + {{- if .GenerateGet}} + GetError map[meta.Key]error + {{- end -}} + {{- if .GenerateList}} + ListError *error + {{- end -}} + {{- if .GenerateInsert}} + InsertError map[meta.Key]error + {{- end -}} + {{- if .GenerateDelete}} + DeleteError map[meta.Key]error + {{- end -}} + {{- if .AggregatedList}} + AggregatedListError *error + {{- end}} + + // xxxHook allow you to intercept the standard processing of the mock in + // order to add your own logic. Return (true, _, _) to prevent the normal + // 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) + {{- end -}} + {{- if .GenerateList}} + {{- if .KeyIsGlobal}} + ListHook func(m *{{.MockWrapType}}, ctx context.Context, fl *filter.F) (bool, []*{{.FQObjectType}}, error) + {{- end -}} + {{- if .KeyIsRegional}} + ListHook func(m *{{.MockWrapType}}, ctx context.Context, region string, fl *filter.F) (bool, []*{{.FQObjectType}}, error) + {{- end -}} + {{- if .KeyIsZonal}} + ListHook func(m *{{.MockWrapType}}, ctx context.Context, zone string, fl *filter.F) (bool, []*{{.FQObjectType}}, error) + {{- end}} + {{- end -}} + {{- if .GenerateInsert}} + 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) + {{- end -}} + {{- if .AggregatedList}} + AggregatedListHook func(m *{{.MockWrapType}}, ctx context.Context, fl *filter.F) (bool, map[string][]*{{.FQObjectType}}, error) + {{- end}} + +{{- with .Methods -}} +{{- range .}} + {{.MockHook}} +{{- end -}} +{{- end}} + + // X is extra state that can be used as part of the mock. Generated code + // will not use this field. + X interface{} +} + +{{- if .GenerateGet}} +// Get returns the object from the mock. +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) + return obj, err + } + } + + m.Lock.Lock() + defer m.Lock.Unlock() + + 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 { + typedObj := obj.To{{.VersionTitle}}() + glog.V(5).Infof("{{.MockWrapType}}.Get(%v, %s) = %+v, nil", ctx, key, typedObj) + return typedObj, nil + } + + err := &googleapi.Error{ + Code: http.StatusNotFound, + Message: fmt.Sprintf("{{.MockWrapType}} %v not found", key), + } + glog.V(5).Infof("{{.MockWrapType}}.Get(%v, %s) = nil, %v", ctx, key, err) + return nil, err +} +{{- end}} + +{{- if .GenerateList}} +{{if .KeyIsGlobal -}} +// List all of the objects in the mock. +func (m *{{.MockWrapType}}) List(ctx context.Context, fl *filter.F) ([]*{{.FQObjectType}}, error) { +{{- end -}} +{{- if .KeyIsRegional -}} +// List all of the objects in the mock in the given region. +func (m *{{.MockWrapType}}) List(ctx context.Context, region string, fl *filter.F) ([]*{{.FQObjectType}}, error) { +{{- end -}} +{{- if .KeyIsZonal -}} +// List all of the objects in the mock in the given zone. +func (m *{{.MockWrapType}}) List(ctx context.Context, zone string, fl *filter.F) ([]*{{.FQObjectType}}, error) { +{{- end}} + if m.ListHook != nil { + {{if .KeyIsGlobal -}} + if intercept, objs, err := m.ListHook(m, ctx, fl); intercept { + glog.V(5).Infof("{{.MockWrapType}}.List(%v, %v) = [%v items], %v", ctx, fl, len(objs), err) + {{- end -}} + {{- if .KeyIsRegional -}} + if intercept, objs, err := m.ListHook(m, ctx, region, fl); intercept { + glog.V(5).Infof("{{.MockWrapType}}.List(%v, %q, %v) = [%v items], %v", ctx, region, fl, len(objs), err) + {{- end -}} + {{- if .KeyIsZonal -}} + if intercept, objs, err := m.ListHook(m, ctx, zone, fl); intercept { + glog.V(5).Infof("{{.MockWrapType}}.List(%v, %q, %v) = [%v items], %v", ctx, zone, fl, len(objs), err) + {{- end}} + return objs, err + } + } + + m.Lock.Lock() + defer m.Lock.Unlock() + + if m.ListError != nil { + err := *m.ListError + {{if .KeyIsGlobal -}} + glog.V(5).Infof("{{.MockWrapType}}.List(%v, %v) = nil, %v", ctx, fl, err) + {{- end -}} + {{- if .KeyIsRegional -}} + glog.V(5).Infof("{{.MockWrapType}}.List(%v, %q, %v) = nil, %v", ctx, region, fl, err) + {{- end -}} + {{- if .KeyIsZonal -}} + glog.V(5).Infof("{{.MockWrapType}}.List(%v, %q, %v) = nil, %v", ctx, zone, fl, err) + {{- end}} + + return nil, *m.ListError + } + + var objs []*{{.FQObjectType}} +{{- if .KeyIsGlobal}} + for _, obj := range m.Objects { +{{- else}} + for key, obj := range m.Objects { +{{- end -}} +{{- if .KeyIsRegional}} + if key.Region != region { + continue + } +{{- end -}} +{{- if .KeyIsZonal}} + if key.Zone != zone { + continue + } +{{- end}} + if ! fl.Match(obj.To{{.VersionTitle}}()) { + continue + } + objs = append(objs, obj.To{{.VersionTitle}}()) + } + + {{if .KeyIsGlobal -}} + glog.V(5).Infof("{{.MockWrapType}}.List(%v, %v) = [%v items], nil", ctx, fl, len(objs)) + {{- end -}} + {{- if .KeyIsRegional -}} + glog.V(5).Infof("{{.MockWrapType}}.List(%v, %q, %v) = [%v items], nil", ctx, region, fl, len(objs)) + {{- end -}} + {{- if .KeyIsZonal -}} + glog.V(5).Infof("{{.MockWrapType}}.List(%v, %q, %v) = [%v items], nil", ctx, zone, fl, len(objs)) + {{- end}} + return objs, nil +} +{{- end}} + +{{- 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 { + 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) + return err + } + } + + m.Lock.Lock() + defer m.Lock.Unlock() + + 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 { + err := &googleapi.Error{ + Code: http.StatusConflict, + Message: fmt.Sprintf("{{.MockWrapType}} %v exists", key), + } + glog.V(5).Infof("{{.MockWrapType}}.Insert(%v, %v, %+v) = %v", ctx, key, obj, err) + return err + } + + obj.Name = key.Name + if obj.SelfLink == "" { + obj.SelfLink = SelfLink(meta.Version{{.VersionTitle}}, "mock-project", "{{.Resource}}", key) + } + + m.Objects[key] = &Mock{{.Service}}Obj{obj} + glog.V(5).Infof("{{.MockWrapType}}.Insert(%v, %v, %+v) = nil", ctx, key, obj) + return nil +} +{{- end}} + +{{- if .GenerateDelete}} +// Delete is a mock for deleting the object. +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) + return err + } + } + + m.Lock.Lock() + defer m.Lock.Unlock() + + 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 { + err := &googleapi.Error{ + Code: http.StatusNotFound, + Message: fmt.Sprintf("{{.MockWrapType}} %v not found", key), + } + glog.V(5).Infof("{{.MockWrapType}}.Delete(%v, %v) = %v", ctx, key, err) + return err + } + + delete(m.Objects, key) + glog.V(5).Infof("{{.MockWrapType}}.Delete(%v, %v) = nil", ctx, key) + return nil +} +{{- end}} + +{{- if .AggregatedList}} +// AggregatedList is a mock for AggregatedList. +func (m *{{.MockWrapType}}) AggregatedList(ctx context.Context, fl *filter.F) (map[string][]*{{.FQObjectType}}, error) { + if m.AggregatedListHook != nil { + if intercept, objs, err := m.AggregatedListHook(m, ctx, fl); intercept { + glog.V(5).Infof("{{.MockWrapType}}.AggregatedList(%v, %v) = [%v items], %v", ctx, fl, len(objs), err) + return objs, err + } + } + + m.Lock.Lock() + defer m.Lock.Unlock() + + if m.AggregatedListError != nil { + err := *m.AggregatedListError + glog.V(5).Infof("{{.MockWrapType}}.AggregatedList(%v, %v) = nil, %v", ctx, fl, err) + return nil, err + } + + objs := map[string][]*{{.FQObjectType}}{} + for _, obj := range m.Objects { + res, err := ParseResourceURL(obj.To{{.VersionTitle}}().SelfLink) + {{- if .KeyIsRegional}} + location := res.Key.Region + {{- end -}} + {{- if .KeyIsZonal}} + location := res.Key.Zone + {{- end}} + if err != nil { + glog.V(5).Infof("{{.MockWrapType}}.AggregatedList(%v, %v) = nil, %v", ctx, fl, err) + return nil, err + } + if ! fl.Match(obj.To{{.VersionTitle}}()) { + continue + } + objs[location] = append(objs[location], obj.To{{.VersionTitle}}()) + } + glog.V(5).Infof("{{.MockWrapType}}.AggregatedList(%v, %v) = [%v items], nil", ctx, fl, len(objs)) + return objs, nil +} +{{- end}} + +// Obj wraps the object for use in the mock. +func (m *{{.MockWrapType}}) Obj(o *{{.FQObjectType}}) *Mock{{.Service}}Obj { + return &Mock{{.Service}}Obj{o} +} + +{{with .Methods -}} +{{- range .}} +// {{.Name}} is a mock for the corresponding method. +func (m *{{.MockWrapType}}) {{.FcnArgs}} { +{{- if eq .ReturnType "Operation"}} + if m.{{.MockHookName}} != nil { + return m.{{.MockHookName}}(m, ctx, key {{.CallArgs}}) + } + return nil +{{- else}} + if m.{{.MockHookName}} != nil { + return m.{{.MockHookName}}(m, ctx, key {{.CallArgs}}) + } + return nil, fmt.Errorf("{{.MockHookName}} must be set") +{{- end}} +} +{{end -}} +{{- end}} +// {{.GCEWrapType}} is a simplifying adapter for the GCE {{.Service}}. +type {{.GCEWrapType}} struct { + s *Service +} + +{{- if .GenerateGet}} +// Get the {{.Object}} named by key. +func (g *{{.GCEWrapType}}) Get(ctx context.Context, key meta.Key) (*{{.FQObjectType}}, error) { + projectID := g.s.ProjectRouter.ProjectID(ctx, "{{.Version}}", "{{.Service}}") + rk := &RateLimitKey{ + ProjectID: projectID, + Operation: "Get", + Version: meta.Version("{{.Version}}"), + Service: "{{.Service}}", + } + if err := g.s.RateLimiter.Accept(ctx, rk); err != nil { + return nil, err + } +{{- if .KeyIsGlobal}} + call := g.s.{{.VersionTitle}}.{{.Service}}.Get(projectID, key.Name) +{{- end -}} +{{- if .KeyIsRegional}} + call := g.s.{{.VersionTitle}}.{{.Service}}.Get(projectID, key.Region, key.Name) +{{- end -}} +{{- if .KeyIsZonal}} + call := g.s.{{.VersionTitle}}.{{.Service}}.Get(projectID, key.Zone, key.Name) +{{- end}} + call.Context(ctx) + return call.Do() +} +{{- end}} + +{{- if .GenerateList}} +// List all {{.Object}} objects. +{{- if .KeyIsGlobal}} +func (g *{{.GCEWrapType}}) List(ctx context.Context, fl *filter.F) ([]*{{.FQObjectType}}, error) { +{{- end -}} +{{- if .KeyIsRegional}} +func (g *{{.GCEWrapType}}) List(ctx context.Context, region string, fl *filter.F) ([]*{{.FQObjectType}}, error) { +{{- end -}} +{{- if .KeyIsZonal}} +func (g *{{.GCEWrapType}}) List(ctx context.Context, zone string, fl *filter.F) ([]*{{.FQObjectType}}, error) { +{{- end}} +projectID := g.s.ProjectRouter.ProjectID(ctx, "{{.Version}}", "{{.Service}}") +rk := &RateLimitKey{ + ProjectID: projectID, + Operation: "List", + Version: meta.Version("{{.Version}}"), + Service: "{{.Service}}", + } + if err := g.s.RateLimiter.Accept(ctx, rk); err != nil { + return nil, err + } +{{- if .KeyIsGlobal}} + call := g.s.{{.VersionTitle}}.{{.Service}}.List(projectID) +{{- end -}} +{{- if .KeyIsRegional}} + call := g.s.{{.VersionTitle}}.{{.Service}}.List(projectID, region) +{{- end -}} +{{- if .KeyIsZonal}} + call := g.s.{{.VersionTitle}}.{{.Service}}.List(projectID, zone) +{{- end}} + if fl != filter.None { + call.Filter(fl.String()) + } + var all []*{{.FQObjectType}} + f := func(l *{{.ObjectListType}}) error { + all = append(all, l.Items...) + return nil + } + if err := call.Pages(ctx, f); err != nil { + return nil, err + } + return all, nil +} +{{- end}} + +{{- if .GenerateInsert}} +// Insert {{.Object}} with key of value obj. +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, + Operation: "Insert", + Version: meta.Version("{{.Version}}"), + Service: "{{.Service}}", + } + if err := g.s.RateLimiter.Accept(ctx, rk); err != nil { + return err + } + obj.Name = key.Name +{{- if .KeyIsGlobal}} + call := g.s.{{.VersionTitle}}.{{.Service}}.Insert(projectID, obj) +{{- end -}} +{{- if .KeyIsRegional}} + call := g.s.{{.VersionTitle}}.{{.Service}}.Insert(projectID, key.Region, obj) +{{- end -}} +{{- if .KeyIsZonal}} + call := g.s.{{.VersionTitle}}.{{.Service}}.Insert(projectID, key.Zone, obj) +{{- end}} + call.Context(ctx) + + op, err := call.Do() + if err != nil { + return err + } + return g.s.WaitForCompletion(ctx, op) +} +{{- end}} + +{{- if .GenerateDelete}} +// Delete the {{.Object}} referenced by key. +func (g *{{.GCEWrapType}}) Delete(ctx context.Context, key meta.Key) error { + projectID := g.s.ProjectRouter.ProjectID(ctx, "{{.Version}}", "{{.Service}}") + rk := &RateLimitKey{ + ProjectID: projectID, + Operation: "Delete", + Version: meta.Version("{{.Version}}"), + Service: "{{.Service}}", + } + if err := g.s.RateLimiter.Accept(ctx, rk); err != nil { + return err + } +{{- if .KeyIsGlobal}} + call := g.s.{{.VersionTitle}}.{{.Service}}.Delete(projectID, key.Name) +{{end -}} +{{- if .KeyIsRegional}} + call := g.s.{{.VersionTitle}}.{{.Service}}.Delete(projectID, key.Region, key.Name) +{{- end -}} +{{- if .KeyIsZonal}} + call := g.s.{{.VersionTitle}}.{{.Service}}.Delete(projectID, key.Zone, key.Name) +{{- end}} + call.Context(ctx) + + op, err := call.Do() + if err != nil { + return err + } + return g.s.WaitForCompletion(ctx, op) +} +{{end -}} + +{{- if .AggregatedList}} +// AggregatedList lists all resources of the given type across all locations. +func (g *{{.GCEWrapType}}) AggregatedList(ctx context.Context, fl *filter.F) (map[string][]*{{.FQObjectType}}, error) { + projectID := g.s.ProjectRouter.ProjectID(ctx, "{{.Version}}", "{{.Service}}") + rk := &RateLimitKey{ + ProjectID: projectID, + Operation: "AggregatedList", + Version: meta.Version("{{.Version}}"), + Service: "{{.Service}}", + } + if err := g.s.RateLimiter.Accept(ctx, rk); err != nil { + return nil, err + } + + call := g.s.{{.VersionTitle}}.{{.Service}}.AggregatedList(projectID) + call.Context(ctx) + if fl != filter.None { + call.Filter(fl.String()) + } + + all := map[string][]*{{.FQObjectType}}{} + f := func(l *{{.ObjectAggregatedListType}}) error { + for k, v := range l.Items { + all[k] = append(all[k], v.{{.AggregatedListField}}...) + } + return nil + } + if err := call.Pages(ctx, f); err != nil { + return nil, err + } + return all, nil +} +{{- end}} + +{{- with .Methods -}} +{{- range .}} +// {{.Name}} is a method on {{.GCEWrapType}}. +func (g *{{.GCEWrapType}}) {{.FcnArgs}} { + projectID := g.s.ProjectRouter.ProjectID(ctx, "{{.Version}}", "{{.Service}}") + rk := &RateLimitKey{ + ProjectID: projectID, + Operation: "{{.Name}}", + Version: meta.Version("{{.Version}}"), + Service: "{{.Service}}", + } + if err := g.s.RateLimiter.Accept(ctx, rk); err != nil { + {{- if eq .ReturnType "Operation"}} + return err + {{- else}} + return nil, err + {{- end}} + } +{{- if .KeyIsGlobal}} + call := g.s.{{.VersionTitle}}.{{.Service}}.{{.Name}}(projectID, key.Name {{.CallArgs}}) +{{- end -}} +{{- if .KeyIsRegional}} + call := g.s.{{.VersionTitle}}.{{.Service}}.{{.Name}}(projectID, key.Region, key.Name {{.CallArgs}}) +{{- end -}} +{{- if .KeyIsZonal}} + call := g.s.{{.VersionTitle}}.{{.Service}}.{{.Name}}(projectID, key.Zone, key.Name {{.CallArgs}}) +{{- end}} + call.Context(ctx) +{{- if eq .ReturnType "Operation"}} + op, err := call.Do() + if err != nil { + return err + } + return g.s.WaitForCompletion(ctx, op) +{{- else}} + return call.Do() +{{- end}} +} +{{end -}} +{{- end}} +` + tmpl := template.Must(template.New("interface").Parse(text)) + for _, s := range meta.AllServices { + if err := tmpl.Execute(wr, s); err != nil { + panic(err) + } + } +} + +func genUnitTestHeader(wr io.Writer) { + const text = `/* +Copyright {{.Year}} The Kubernetes Authors. + +Licensed under the Apache License, Version 2.0 (the "License"); +you may not use this file except in compliance with the License. +You may obtain a copy of the License at + + http://www.apache.org/licenses/LICENSE-2.0 + +Unless required by applicable law or agreed to in writing, software +distributed under the License is distributed on an "AS IS" BASIS, +WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +See the License for the specific language governing permissions and +limitations under the License. +*/ + +// This file was generated by "go run gen/main.go -mode test > gen_test.go". Do not edit +// directly. + +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" + + "{{.PackageRoot}}/filter" + "{{.PackageRoot}}/meta" +) + +const location = "location" +` + tmpl := template.Must(template.New("header").Parse(text)) + values := map[string]string{ + "Year": fmt.Sprintf("%v", time.Now().Year()), + "PackageRoot": packageRoot, + } + if err := tmpl.Execute(wr, values); err != nil { + panic(err) + } +} + +func genUnitTestServices(wr io.Writer) { + const text = ` +func Test{{.Service}}Group(t *testing.T) { + t.Parallel() + + ctx := context.Background() + mock := NewMockGCE() + + var key *meta.Key +{{- if .HasAlpha}} + keyAlpha := meta.{{.Alpha.MakeKey "key-alpha" "location"}} + key = keyAlpha +{{- end}} +{{- if .HasBeta}} + keyBeta := meta.{{.Beta.MakeKey "key-beta" "location"}} + key = keyBeta +{{- end}} +{{- if .HasGA}} + keyGA := meta.{{.GA.MakeKey "key-ga" "location"}} + key = keyGA +{{- end}} + // Ignore unused variables. + _, _, _ = ctx, mock, key + + // Get not found. +{{- if .HasAlpha}}{{- if .Alpha.GenerateGet}} + 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 { + 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 { + t.Errorf("{{.Service}}().Get(%v, %v) = _, nil; want error", ctx, key) + } +{{- end}}{{- end}} + + // Insert. +{{- 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) + } + } +{{- 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) + } + } +{{- 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) + } + } +{{- end}}{{- end}} + + // Get across versions. +{{- if .HasAlpha}}{{- if .Alpha.GenerateInsert}} + 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 { + 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 { + t.Errorf("{{.Service}}().Get(%v, %v) = %v, %v; want nil", ctx, key, obj, err) + } +{{- end}}{{- end}} + + // List. +{{- if .HasAlpha}} + mock.MockAlpha{{.Service}}.Objects[*keyAlpha] = mock.MockAlpha{{.Service}}.Obj(&alpha.{{.Alpha.Object}}{Name: keyAlpha.Name}) +{{- end}} +{{- if .HasBeta}} + mock.MockBeta{{.Service}}.Objects[*keyBeta] = mock.MockBeta{{.Service}}.Obj(&beta.{{.Beta.Object}}{Name: keyBeta.Name}) +{{- end}} +{{- if .HasGA}} + mock.Mock{{.Service}}.Objects[*keyGA] = mock.Mock{{.Service}}.Obj(&ga.{{.GA.Object}}{Name: keyGA.Name}) +{{- end}} + want := map[string]bool{ +{{- if .HasAlpha}} + "key-alpha": true, +{{- end}} +{{- if .HasBeta}} + "key-beta": true, +{{- end}} +{{- if .HasGA}} + "key-ga": true, +{{- end}} + } + _ = want // ignore unused variables. + +{{- if .HasAlpha}}{{- if .Alpha.GenerateList}} + { + {{- if .Alpha.KeyIsGlobal }} + objs, err := mock.Alpha{{.Service}}().List(ctx, filter.None) + {{- else}} + objs, err := mock.Alpha{{.Service}}().List(ctx, location, filter.None) + {{- end}} + if err != nil { + t.Errorf("Alpha{{.Service}}().List(%v, %v, %v) = %v, %v; want _, nil", ctx, location, filter.None, objs, err) + } else { + got := map[string]bool{} + for _, obj := range objs { + got[obj.Name] = true + } + if !reflect.DeepEqual(got, want) { + t.Errorf("Alpha{{.Service}}().List(); got %+v, want %+v", got, want) + } + } + } +{{- end}}{{- end}} +{{- if .HasBeta}}{{- if .Beta.GenerateList}} + { + {{- if .Beta.KeyIsGlobal }} + objs, err := mock.Beta{{.Service}}().List(ctx, filter.None) + {{- else}} + objs, err := mock.Beta{{.Service}}().List(ctx, location, filter.None) + {{- end}} + if err != nil { + t.Errorf("Beta{{.Service}}().List(%v, %v, %v) = %v, %v; want _, nil", ctx, location, filter.None, objs, err) + } else { + got := map[string]bool{} + for _, obj := range objs { + got[obj.Name] = true + } + if !reflect.DeepEqual(got, want) { + t.Errorf("Alpha{{.Service}}().List(); got %+v, want %+v", got, want) + } + } + } +{{- end}}{{- end}} +{{- if .HasGA}}{{- if .GA.GenerateList}} + { + {{- if .GA.KeyIsGlobal }} + objs, err := mock.{{.Service}}().List(ctx, filter.None) + {{- else}} + objs, err := mock.{{.Service}}().List(ctx, location, filter.None) + {{- end}} + if err != nil { + t.Errorf("{{.Service}}().List(%v, %v, %v) = %v, %v; want _, nil", ctx, location, filter.None, objs, err) + } else { + got := map[string]bool{} + for _, obj := range objs { + got[obj.Name] = true + } + if !reflect.DeepEqual(got, want) { + t.Errorf("Alpha{{.Service}}().List(); got %+v, want %+v", got, want) + } + } + } +{{- end}}{{- end}} + + // 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) + } +{{- 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) + } +{{- 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) + } +{{- 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) + } +{{- 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) + } +{{- 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) + } +{{- end}}{{- end}} +} +` + tmpl := template.Must(template.New("unittest").Parse(text)) + for _, s := range meta.AllServicesByGroup { + if err := tmpl.Execute(wr, s); err != nil { + panic(err) + } + } +} + +func main() { + flag.Parse() + + out := &bytes.Buffer{} + + switch flags.mode { + case "src": + genHeader(out) + genStubs(out) + genTypes(out) + case "test": + genUnitTestHeader(out) + genUnitTestServices(out) + default: + glog.Fatalf("Invalid -mode: %q", flags.mode) + } + + if flags.gofmt { + fmt.Print(gofmtContent(out)) + } else { + fmt.Print(out.String()) + } +}