From ff099bd460e9a7b69da8e15c4034bf6d47ad942c Mon Sep 17 00:00:00 2001 From: Chao Xu Date: Thu, 19 Nov 2015 13:54:41 -0800 Subject: [PATCH] First step of adding clinet-gen: add the generator that generates client code for top-level types. --- .../client-gen/generators/client-generator.go | 97 +++++++++ .../generators/generator-for-type.go | 205 ++++++++++++++++++ cmd/libs/go2idl/client-gen/main.go | 49 +++++ 3 files changed, 351 insertions(+) create mode 100644 cmd/libs/go2idl/client-gen/generators/client-generator.go create mode 100644 cmd/libs/go2idl/client-gen/generators/generator-for-type.go create mode 100644 cmd/libs/go2idl/client-gen/main.go diff --git a/cmd/libs/go2idl/client-gen/generators/client-generator.go b/cmd/libs/go2idl/client-gen/generators/client-generator.go new file mode 100644 index 00000000000..5e2301d311b --- /dev/null +++ b/cmd/libs/go2idl/client-gen/generators/client-generator.go @@ -0,0 +1,97 @@ +/* +Copyright 2015 The Kubernetes Authors All rights reserved. + +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 generators has the generators for the client-gen utility. +package generators + +import ( + "os" + "strings" + + "k8s.io/kubernetes/cmd/libs/go2idl/args" + "k8s.io/kubernetes/cmd/libs/go2idl/generator" + "k8s.io/kubernetes/cmd/libs/go2idl/namer" + "k8s.io/kubernetes/cmd/libs/go2idl/types" + + "github.com/golang/glog" +) + +// NameSystems returns the name system used by the generators in this package. +func NameSystems() namer.NameSystems { + return namer.NameSystems{ + "public": namer.NewPublicNamer(0), + "private": namer.NewPrivateNamer(0), + "raw": namer.NewRawNamer("", nil), + } +} + +// DefaultNameSystem returns the default name system for ordering the types to be +// processed by the generators in this package. +func DefaultNameSystem() string { + return "public" +} + +// Packages makes the client package definition. +func Packages(context *generator.Context, arguments *args.GeneratorArgs) generator.Packages { + boilerplate, err := arguments.LoadGoBoilerplate() + if err != nil { + glog.Fatalf("Failed loading boilerplate: %v", err) + } + return generator.Packages{&generator.DefaultPackage{ + PackageName: "unversioned", + PackagePath: arguments.OutputPackagePath, + HeaderText: append(boilerplate, []byte( + ` +// This file was autogenerated by the command: +// $ `+strings.Join(os.Args, " ")+` +// Do not edit it manually! + +`)...), + PackageDocumentation: []byte( + `// Package unversioned has the automatically generated clients for unversioned resources. +`), + // GeneratorFunc returns a list of generators. Each generator makes a + // single file. + GeneratorFunc: func(c *generator.Context) (generators []generator.Generator) { + generators = []generator.Generator{ + // Always generate a "doc.go" file. + generator.DefaultGen{OptionalName: "doc"}, + } + // Since we want a file per type that we generate a client for, we + // have to provide a function for this. + for _, t := range c.Order { + generators = append(generators, &genClientForType{ + DefaultGen: generator.DefaultGen{ + // Use the privatized version of the + // type name as the file name. + // + // TODO: make a namer that converts + // camelCase to '-' separation for file + // names? + OptionalName: c.Namers["private"].Name(t), + }, + outputPackage: arguments.OutputPackagePath, + typeToMatch: t, + imports: generator.NewImportTracker(), + }) + } + return generators + }, + FilterFunc: func(c *generator.Context, t *types.Type) bool { + return types.ExtractCommentTags("+", t.CommentLines)["genclient"] == "true" + }, + }} +} diff --git a/cmd/libs/go2idl/client-gen/generators/generator-for-type.go b/cmd/libs/go2idl/client-gen/generators/generator-for-type.go new file mode 100644 index 00000000000..5e51a84515a --- /dev/null +++ b/cmd/libs/go2idl/client-gen/generators/generator-for-type.go @@ -0,0 +1,205 @@ +/* +Copyright 2015 The Kubernetes Authors All rights reserved. + +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 generators + +import ( + "io" + "path/filepath" + + "k8s.io/kubernetes/cmd/libs/go2idl/generator" + "k8s.io/kubernetes/cmd/libs/go2idl/namer" + "k8s.io/kubernetes/cmd/libs/go2idl/types" +) + +// genClientForType produces a file for each top-level type. +type genClientForType struct { + generator.DefaultGen + outputPackage string + typeToMatch *types.Type + imports *generator.ImportTracker +} + +// Filter ignores all but one type because we're making a single file per type. +func (g *genClientForType) Filter(c *generator.Context, t *types.Type) bool { return t == g.typeToMatch } + +func (g *genClientForType) Namers(c *generator.Context) namer.NameSystems { + return namer.NameSystems{ + "raw": namer.NewRawNamer(g.outputPackage, g.imports), + } +} + +func (g *genClientForType) Imports(c *generator.Context) (imports []string) { + return g.imports.ImportLines() +} + +// GenerateType makes the body of a file implementing a set for type t. +func (g *genClientForType) GenerateType(c *generator.Context, t *types.Type, w io.Writer) error { + sw := generator.NewSnippetWriter(w, c, "$", "$") + pkg := filepath.Base(t.Name.Package) + m := map[string]interface{}{ + "type": t, + "package": pkg, + "Package": namer.IC(pkg), + "fieldSelector": c.Universe.Get(types.Name{"k8s.io/kubernetes/pkg/fields", "Selector"}), + "labelSelector": c.Universe.Get(types.Name{"k8s.io/kubernetes/pkg/labels", "Selector"}), + "watchInterface": c.Universe.Get(types.Name{"k8s.io/kubernetes/pkg/watch", "Interface"}), + "apiDeleteOptions": c.Universe.Get(types.Name{"k8s.io/kubernetes/pkg/api", "DeleteOptions"}), + "apiListOptions": c.Universe.Get(types.Name{"k8s.io/kubernetes/pkg/api", "ListOptions"}), + } + sw.Do(namespacerTemplate, m) + sw.Do(interfaceTemplate, m) + sw.Do(structTemplate, m) + sw.Do(newStructTemplate, m) + sw.Do(createTemplate, m) + sw.Do(updateTemplate, m) + sw.Do(deleteTemplate, m) + sw.Do(getTemplate, m) + sw.Do(listTemplate, m) + sw.Do(watchTemplate, m) + + return sw.Error() +} + +// template for namespacer +var namespacerTemplate = ` +// $.type|public$Namespacer has methods to work with $.type|public$ resources in a namespace +type $.type|public$Namespacer interface { + $.type|public$s(namespace string) $.type|public$Interface +} +` + +// template for the Interface +var interfaceTemplate = ` +// $.type|public$Interface has methods to work with $.type|public$ resources. +type $.type|public$Interface interface { + Create(*$.type|raw$) (*$.type|raw$, error) + Update(*$.type|raw$) (*$.type|raw$, error) + Delete(name string, options *$.apiDeleteOptions|raw$) error + Get(name string) (*$.type|raw$, error) + List(label $.labelSelector|raw$, field $.fieldSelector|raw$) (*$.type|raw$List, error) + Watch(label $.labelSelector|raw$, field $.fieldSelector|raw$, opts $.apiListOptions|raw$) ($.watchInterface|raw$, error) +} +` + +// template for the struct that implements the interface +var structTemplate = ` +// $.type|private$s implements $.type|public$Interface +type $.type|private$s struct { + client *$.Package$Client + ns string +} +` +var newStructTemplate = ` +// new$.type|public$s returns a $.type|public$s +func new$.type|public$s(c *ExtensionsClient, namespace string) *$.type|private$s { + return &$.type|private$s{ + client: c, + ns: namespace, + } +} +` +var listTemplate = ` +// List takes label and field selectors, and returns the list of $.type|public$s that match those selectors. +func (c *$.type|private$s) List(label $.labelSelector|raw$, field $.fieldSelector|raw$) (result *$.type|raw$List, err error) { + result = &$.type|raw$List{} + err = c.client.Get(). + Namespace(c.ns). + Resource("$.type|private$s"). + LabelsSelectorParam(label). + FieldsSelectorParam(field). + Do(). + Into(result) + return +} +` +var getTemplate = ` +// Get takes name of the $.type|private$, and returns the corresponding $.type|private$ object, and an error if there is any. +func (c *$.type|private$s) Get(name string) (result *$.type|raw$, err error) { + result = &$.type|raw${} + err = c.client.Get(). + Namespace(c.ns). + Resource("$.type|private$s"). + Name(name). + Do(). + Into(result) + return +} +` + +var deleteTemplate = ` +// Delete takes name of the $.type|private$ and deletes it. Returns an error if one occurs. +func (c *$.type|private$s) Delete(name string, options *$.apiDeleteOptions|raw$) error { + if options == nil { + return c.client.Delete().Namespace(c.ns).Resource("$.type|private$s").Name(name).Do().Error() + } + body, err := api.Scheme.EncodeToVersion(options, c.client.APIVersion()) + if err != nil { + return err + } + return c.client.Delete(). + Namespace(c.ns). + Resource("$.type|private$s"). + Name(name). + Body(body). + Do(). + Error() +} +` + +var createTemplate = ` +// Create takes the representation of a $.type|private$ and creates it. Returns the server's representation of the $.type|private$, and an error, if there is any. +func (c *$.type|private$s) Create($.type|private$ *$.type|raw$) (result *$.type|raw$, err error) { + result = &$.type|raw${} + err = c.client.Post(). + Namespace(c.ns). + Resource("$.type|private$s"). + Body($.type|private$). + Do(). + Into(result) + return +} +` + +var updateTemplate = ` +// Update takes the representation of a $.type|private$ and updates it. Returns the server's representation of the $.type|private$, and an error, if there is any. +func (c *$.type|private$s) Update($.type|private$ *$.type|raw$) (result *$.type|raw$, err error) { + result = &$.type|raw${} + err = c.client.Put(). + Namespace(c.ns). + Resource("$.type|private$s"). + Name($.type|private$.Name). + Body($.type|private$). + Do(). + Into(result) + return +} +` + +var watchTemplate = ` +// Watch returns a $.watchInterface|raw$ that watches the requested $.type|private$s. +func (c *$.type|private$s) Watch(label $.labelSelector|raw$, field $.fieldSelector|raw$, opts $.apiListOptions|raw$) ($.watchInterface|raw$, error) { + return c.client.Get(). + Prefix("watch"). + Namespace(c.ns). + Resource("$.type|private$s"). + Param("resourceVersion", opts.ResourceVersion). + TimeoutSeconds(TimeoutFromListOptions(opts)). + LabelsSelectorParam(label). + FieldsSelectorParam(field). + Watch() +} +` diff --git a/cmd/libs/go2idl/client-gen/main.go b/cmd/libs/go2idl/client-gen/main.go new file mode 100644 index 00000000000..e970faf1be7 --- /dev/null +++ b/cmd/libs/go2idl/client-gen/main.go @@ -0,0 +1,49 @@ +/* +Copyright 2015 The Kubernetes Authors All rights reserved. + +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. +*/ + +// client-gen makes the individual typed clients using go2idl. +package main + +import ( + "k8s.io/kubernetes/cmd/libs/go2idl/args" + "k8s.io/kubernetes/cmd/libs/go2idl/client-gen/generators" + + "github.com/golang/glog" +) + +func main() { + arguments := args.Default() + + // Override defaults. These are Kubernetes specific input and output + // locations. + arguments.InputDirs = []string{ + "k8s.io/kubernetes/pkg/api", + "k8s.io/kubernetes/pkg/apis/extensions", + "k8s.io/kubernetes/pkg/fields", + "k8s.io/kubernetes/pkg/labels", + "k8s.io/kubernetes/pkg/watch", + } + // We may change the output path later. + arguments.OutputPackagePath = "k8s.io/kubernetes/pkg/client/clientset/unversioned" + + if err := arguments.Execute( + generators.NameSystems(), + generators.DefaultNameSystem(), + generators.Packages, + ); err != nil { + glog.Fatalf("Error: %v", err) + } +}