// GenerateType makes the body of a file implementing the individual typed client 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) namespaced := !(types.ExtractCommentTags("+", t.SecondClosestCommentLines)["nonNamespaced"] == "true") m := map[string]interface{}{ "type": t, "package": pkg, "Package": namer.IC(pkg), "Group": namer.IC(g.group), "watchInterface": c.Universe.Type(types.Name{Package: "k8s.io/kubernetes/pkg/watch", Name: "Interface"}), "apiDeleteOptions": c.Universe.Type(types.Name{Package: "k8s.io/kubernetes/pkg/api", Name: "DeleteOptions"}), "apiListOptions": c.Universe.Type(types.Name{Package: "k8s.io/kubernetes/pkg/api", Name: "ListOptions"}), "apiParameterCodec": c.Universe.Type(types.Name{Package: "k8s.io/kubernetes/pkg/api", Name: "ParameterCodec"}), "namespaced": namespaced, } sw.Do(getterComment, m) if namespaced { sw.Do(getterNamesapced, m) } else { sw.Do(getterNonNamesapced, m) } noMethods := types.ExtractCommentTags("+", t.SecondClosestCommentLines)["noMethods"] == "true" sw.Do(interfaceTemplate1, m) if !noMethods { sw.Do(interfaceTemplate2, m) // Include the UpdateStatus method if the type has a status if hasStatus(t) { sw.Do(interfaceUpdateStatusTemplate, m) } sw.Do(interfaceTemplate3, m) } sw.Do(interfaceTemplate4, m) if namespaced { sw.Do(structNamespaced, m) sw.Do(newStructNamespaced, m) } else { sw.Do(structNonNamespaced, m) sw.Do(newStructNonNamespaced, m) } if !noMethods { sw.Do(createTemplate, m) sw.Do(updateTemplate, m) // Generate the UpdateStatus method if the type has a status if hasStatus(t) { sw.Do(updateStatusTemplate, m) } sw.Do(deleteTemplate, m) sw.Do(deleteCollectionTemplate, m) sw.Do(getTemplate, m) sw.Do(listTemplate, m) sw.Do(watchTemplate, m) } return sw.Error() }
func isDirectlyConvertible(in, out *types.Type, preexisting conversions) bool { // If one of the types is Alias, resolve it. if in.Kind == types.Alias { return isConvertible(in.Underlying, out, preexisting) } if out.Kind == types.Alias { return isConvertible(in, out.Underlying, preexisting) } if in.Kind != out.Kind { return false } switch in.Kind { case types.Builtin, types.Struct, types.Map, types.Slice, types.Pointer: default: // We don't support conversion of other types yet. return false } switch out.Kind { case types.Builtin, types.Struct, types.Map, types.Slice, types.Pointer: default: // We don't support conversion of other types yet. return false } switch in.Kind { case types.Builtin: if in == out { return true } // TODO: Support more conversion types. return types.IsInteger(in) && types.IsInteger(out) case types.Struct: convertible := true for _, inMember := range in.Members { // Check if there is an out member with that name. outMember, found := findMember(out, inMember.Name) if !found { // Check if the member doesn't have comment: // "+ genconversion=false" // comment to ignore this field for conversion. // TODO: Switch to SecondClosestCommentLines. if types.ExtractCommentTags("+", inMember.CommentLines)["genconversion"] == "false" { continue } return false } convertible = convertible && isConvertible(inMember.Type, outMember.Type, preexisting) } return convertible case types.Map: return isConvertible(in.Key, out.Key, preexisting) && isConvertible(in.Elem, out.Elem, preexisting) case types.Slice: return isConvertible(in.Elem, out.Elem, preexisting) case types.Pointer: return isConvertible(in.Elem, out.Elem, preexisting) } glog.Fatalf("All other types should be filtered before") return false }
func (g *genConversion) convertibleOnlyWithinPackage(inType, outType *types.Type) bool { var t *types.Type var other *types.Type if inType.Name.Package == g.targetPackage { t, other = inType, outType } else { t, other = outType, inType } if t.Name.Package != g.targetPackage { return false } if types.ExtractCommentTags("+", t.CommentLines)["genconversion"] == "false" { return false } // TODO: Consider generating functions for other kinds too. if t.Kind != types.Struct { return false } // Also, filter out private types. if namer.IsPrivateGoName(other.Name.Name) { return false } return true }
func packageForGroup(gv unversioned.GroupVersion, typeList []*types.Type, packageBasePath string, srcTreePath string, boilerplate []byte, generatedBy string) generator.Package { outputPackagePath := filepath.Join(packageBasePath, gv.Group, gv.Version) return &generator.DefaultPackage{ PackageName: gv.Version, PackagePath: outputPackagePath, HeaderText: boilerplate, PackageDocumentation: []byte( generatedBy + `// This package has the automatically generated typed clients. `), // 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 typeList { generators = append(generators, &genClientForType{ DefaultGen: generator.DefaultGen{ OptionalName: strings.ToLower(c.Namers["private"].Name(t)), }, outputPackage: outputPackagePath, group: normalization.BeforeFirstDot(gv.Group), typeToMatch: t, imports: generator.NewImportTracker(), }) } generators = append(generators, &genGroup{ DefaultGen: generator.DefaultGen{ OptionalName: normalization.BeforeFirstDot(gv.Group) + "_client", }, outputPackage: outputPackagePath, group: gv.Group, version: gv.Version, types: typeList, imports: generator.NewImportTracker(), }) expansionFileName := "generated_expansion" // To avoid overriding user's manual modification, only generate the expansion file if it doesn't exist. if _, err := os.Stat(filepath.Join(srcTreePath, outputPackagePath, expansionFileName+".go")); os.IsNotExist(err) { generators = append(generators, &genExpansion{ DefaultGen: generator.DefaultGen{ OptionalName: expansionFileName, }, types: typeList, }) } return generators }, FilterFunc: func(c *generator.Context, t *types.Type) bool { return types.ExtractCommentTags("+", t.SecondClosestCommentLines)["genclient"] == "true" }, } }
// 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) } groupToTypes := map[string][]*types.Type{} for _, inputDir := range arguments.InputDirs { p := context.Universe.Package(inputDir) for _, t := range p.Types { if types.ExtractCommentTags("+", t.SecondClosestCommentLines)["genclient"] != "true" { continue } group := filepath.Base(t.Name.Package) // Special case for the legacy API. if group == "api" { group = "legacy" } if _, found := groupToTypes[group]; !found { groupToTypes[group] = []*types.Type{} } groupToTypes[group] = append(groupToTypes[group], t) } } var packageList []generator.Package orderer := namer.Orderer{namer.NewPrivateNamer(0)} for group, types := range groupToTypes { packageList = append(packageList, packageForGroup(group, "unversioned", orderer.OrderTypes(types), arguments.OutputPackagePath, boilerplate)) } return generator.Packages(packageList) }
func (g *genFakeForGroup) GenerateType(c *generator.Context, t *types.Type, w io.Writer) error { sw := generator.NewSnippetWriter(w, c, "$", "$") const pkgTestingCore = "k8s.io/kubernetes/pkg/client/testing/core" m := map[string]interface{}{ "group": g.group, "Group": namer.IC(g.group), "Fake": c.Universe.Type(types.Name{Package: pkgTestingCore, Name: "Fake"}), } sw.Do(groupClientTemplate, m) for _, t := range g.types { wrapper := map[string]interface{}{ "type": t, "Group": namer.IC(g.group), "realClientPackage": filepath.Base(g.realClientPath), } namespaced := !(types.ExtractCommentTags("+", t.SecondClosestCommentLines)["nonNamespaced"] == "true") if namespaced { sw.Do(getterImplNamespaced, wrapper) } else { sw.Do(getterImplNonNamespaced, wrapper) } } return sw.Error() }
// isOptionalAlias should return true if the specified type has an underlying type // (is an alias) of a map or slice and has the comment tag protobuf.nullable=true, // indicating that the type should be nullable in protobuf. func isOptionalAlias(t *types.Type) bool { if t.Underlying == nil || (t.Underlying.Kind != types.Map && t.Underlying.Kind != types.Slice) { return false } if types.ExtractCommentTags("+", t.CommentLines)["protobuf.nullable"] != "true" { return false } return true }
func (g *genGroup) GenerateType(c *generator.Context, t *types.Type, w io.Writer) error { sw := generator.NewSnippetWriter(w, c, "$", "$") const pkgUnversioned = "k8s.io/kubernetes/pkg/client/unversioned" const pkgRegistered = "k8s.io/kubernetes/pkg/apimachinery/registered" const pkgAPI = "k8s.io/kubernetes/pkg/api" apiPath := func(group string) string { if group == "legacy" { return `"/api"` } return `"/apis"` } canonize := func(group string) string { if group == "legacy" { return "" } return group } m := map[string]interface{}{ "group": g.group, "Group": namer.IC(g.group), "canonicalGroup": canonize(g.group), "types": g.types, "Config": c.Universe.Type(types.Name{Package: pkgUnversioned, Name: "Config"}), "DefaultKubernetesUserAgent": c.Universe.Function(types.Name{Package: pkgUnversioned, Name: "DefaultKubernetesUserAgent"}), "RESTClient": c.Universe.Type(types.Name{Package: pkgUnversioned, Name: "RESTClient"}), "RESTClientFor": c.Universe.Function(types.Name{Package: pkgUnversioned, Name: "RESTClientFor"}), "latestGroup": c.Universe.Variable(types.Name{Package: pkgRegistered, Name: "Group"}), "GroupOrDie": c.Universe.Variable(types.Name{Package: pkgRegistered, Name: "GroupOrDie"}), "apiPath": apiPath(g.group), "codecs": c.Universe.Variable(types.Name{Package: pkgAPI, Name: "Codecs"}), } sw.Do(groupInterfaceTemplate, m) sw.Do(groupClientTemplate, m) for _, t := range g.types { wrapper := map[string]interface{}{ "type": t, "Group": namer.IC(g.group), } namespaced := !(types.ExtractCommentTags("+", t.SecondClosestCommentLines)["nonNamespaced"] == "true") if namespaced { sw.Do(getterImplNamespaced, wrapper) } else { sw.Do(getterImplNonNamespaced, wrapper) } } sw.Do(newClientForConfigTemplate, m) sw.Do(newClientForConfigOrDieTemplate, m) sw.Do(newClientForRESTClientTemplate, m) sw.Do(setClientDefaultsTemplate, m) return sw.Error() }
// 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) } customArgs, ok := arguments.CustomArgs.(clientgenargs.Args) if !ok { glog.Fatalf("cannot convert arguments.CustomArgs to clientgenargs.Args") } if len(customArgs.CmdArgs) != 0 { boilerplate = append(boilerplate, []byte(fmt.Sprintf("\n// This file is generated by client-gen with arguments: %s\n\n", customArgs.CmdArgs))...) } else { boilerplate = append(boilerplate, []byte(fmt.Sprintf("\n// This file is generated by client-gen with the default arguments.\n\n"))...) } gvToTypes := map[unversioned.GroupVersion][]*types.Type{} for gv, inputDir := range customArgs.GroupVersionToInputPath { p := context.Universe.Package(inputDir) for _, t := range p.Types { if types.ExtractCommentTags("+", t.SecondClosestCommentLines)["genclient"] != "true" { continue } if _, found := gvToTypes[gv]; !found { gvToTypes[gv] = []*types.Type{} } gvToTypes[gv] = append(gvToTypes[gv], t) } } var packageList []generator.Package typedClientBasePath := filepath.Join(customArgs.ClientsetOutputPath, customArgs.ClientsetName, "typed") packageList = append(packageList, packageForClientset(customArgs, typedClientBasePath, boilerplate)) if customArgs.FakeClient { packageList = append(packageList, fake.PackageForClientset(customArgs, typedClientBasePath, boilerplate)) } // If --clientset-only=true, we don't regenerate the individual typed clients. if customArgs.ClientsetOnly { return generator.Packages(packageList) } orderer := namer.Orderer{namer.NewPrivateNamer(0)} for _, gv := range customArgs.GroupVersions { types := gvToTypes[gv] packageList = append(packageList, packageForGroup(normalization.GroupVersion(gv), orderer.OrderTypes(types), typedClientBasePath, arguments.OutputBase, boilerplate)) if customArgs.FakeClient { packageList = append(packageList, fake.PackageForGroup(normalization.GroupVersion(gv), orderer.OrderTypes(types), typedClientBasePath, arguments.OutputBase, boilerplate)) } } return generator.Packages(packageList) }
func hasOpenAPITagValue(comments []string, value string) bool { tagValues := types.ExtractCommentTags("+", comments)[tagName] if tagValues == nil { return false } for _, val := range tagValues { if val == value { return true } } return false }
func packageForGroup(group string, version string, typeList []*types.Type, basePath string, boilerplate []byte) generator.Package { outputPackagePath := filepath.Join(basePath, group, version) return &generator.DefaultPackage{ PackageName: version, PackagePath: outputPackagePath, HeaderText: boilerplate, 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 typeList { 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: outputPackagePath, group: group, typeToMatch: t, imports: generator.NewImportTracker(), }) } generators = append(generators, &genGroup{ DefaultGen: generator.DefaultGen{ OptionalName: group + "_client", }, outputPackage: outputPackagePath, group: group, types: typeList, imports: generator.NewImportTracker(), }) return generators }, FilterFunc: func(c *generator.Context, t *types.Type) bool { return types.ExtractCommentTags("+", t.SecondClosestCommentLines)["genclient"] == "true" }, } }
// 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) } groupToTypes := map[string][]*types.Type{} for _, inputDir := range arguments.InputDirs { p := context.Universe.Package(inputDir) for _, t := range p.Types { if types.ExtractCommentTags("+", t.SecondClosestCommentLines)["genclient"] != "true" { continue } group := filepath.Base(t.Name.Package) // Special case for the core API. if group == "api" { group = "core" } if _, found := groupToTypes[group]; !found { groupToTypes[group] = []*types.Type{} } groupToTypes[group] = append(groupToTypes[group], t) } } customArgs, ok := arguments.CustomArgs.(ClientGenArgs) if !ok { glog.Fatalf("cannot convert arguments.CustomArgs to ClientGenArgs") } var packageList []generator.Package packageList = append(packageList, packageForClientset(customArgs, arguments.OutputPackagePath, boilerplate)) if customArgs.FakeClient { packageList = append(packageList, fake.PackageForClientset(arguments.OutputPackagePath, customArgs.GroupVersions, boilerplate)) } // If --clientset-only=true, we don't regenerate the individual typed clients. if customArgs.ClientsetOnly { return generator.Packages(packageList) } orderer := namer.Orderer{namer.NewPrivateNamer(0)} for group, types := range groupToTypes { packageList = append(packageList, packageForGroup(group, "unversioned", orderer.OrderTypes(types), arguments.OutputPackagePath, arguments.OutputBase, boilerplate)) if customArgs.FakeClient { packageList = append(packageList, fake.PackageForGroup(group, "unversioned", orderer.OrderTypes(types), arguments.OutputPackagePath, arguments.OutputBase, boilerplate)) } } return generator.Packages(packageList) }
func PackageForGroup(gv unversioned.GroupVersion, typeList []*types.Type, packageBasePath string, srcTreePath string, inputPath string, boilerplate []byte, generatedBy string) generator.Package { outputPackagePath := filepath.Join(packageBasePath, gv.Group, gv.Version, "fake") // TODO: should make this a function, called by here and in client-generator.go realClientPath := filepath.Join(packageBasePath, gv.Group, gv.Version) return &generator.DefaultPackage{ PackageName: "fake", PackagePath: outputPackagePath, HeaderText: boilerplate, PackageDocumentation: []byte( generatedBy + `// Package fake has the automatically generated clients. `), // 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 typeList { generators = append(generators, &genFakeForType{ DefaultGen: generator.DefaultGen{ OptionalName: "fake_" + strings.ToLower(c.Namers["private"].Name(t)), }, outputPackage: outputPackagePath, group: normalization.BeforeFirstDot(gv.Group), inputPackage: inputPath, version: gv.Version, typeToMatch: t, imports: generator.NewImportTracker(), }) } generators = append(generators, &genFakeForGroup{ DefaultGen: generator.DefaultGen{ OptionalName: "fake_" + normalization.BeforeFirstDot(gv.Group) + "_client", }, outputPackage: outputPackagePath, realClientPath: realClientPath, group: normalization.BeforeFirstDot(gv.Group), types: typeList, imports: generator.NewImportTracker(), }) return generators }, FilterFunc: func(c *generator.Context, t *types.Type) bool { return types.ExtractCommentTags("+", t.SecondClosestCommentLines)["genclient"] == "true" }, } }
// 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" }, }} }
func copyableWithinPackage(t *types.Type) bool { if types.ExtractCommentTags("+", t.CommentLines)["gencopy"] == "false" { return false } // TODO: Consider generating functions for other kinds too. if t.Kind != types.Struct { return false } // Also, filter out private types. if namer.IsPrivateGoName(t.Name.Name) { return false } return true }
// Filter ignores types that are identified as not exportable. func (g *genProtoIDL) Filter(c *generator.Context, t *types.Type) bool { flags := types.ExtractCommentTags("+", t.CommentLines) switch { case flags["protobuf"] == "false": return false case flags["protobuf"] == "true": return true case !g.generateAll: return false } seen := map[*types.Type]bool{} ok := isProtoable(seen, t) return ok }
// 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) } customArgs, ok := arguments.CustomArgs.(ClientGenArgs) if !ok { glog.Fatalf("cannot convert arguments.CustomArgs to ClientGenArgs") } gvToTypes := map[unversioned.GroupVersion][]*types.Type{} for gv, inputDir := range customArgs.GroupVersionToInputPath { p := context.Universe.Package(inputDir) for _, t := range p.Types { if types.ExtractCommentTags("+", t.SecondClosestCommentLines)["genclient"] != "true" { continue } if _, found := gvToTypes[gv]; !found { gvToTypes[gv] = []*types.Type{} } gvToTypes[gv] = append(gvToTypes[gv], t) } } var packageList []generator.Package packageList = append(packageList, packageForClientset(customArgs, arguments.OutputPackagePath, boilerplate)) if customArgs.FakeClient { packageList = append(packageList, fake.PackageForClientset(arguments.OutputPackagePath, customArgs.GroupVersions, boilerplate)) } // If --clientset-only=true, we don't regenerate the individual typed clients. if customArgs.ClientsetOnly { return generator.Packages(packageList) } orderer := namer.Orderer{namer.NewPrivateNamer(0)} for _, gv := range customArgs.GroupVersions { types := gvToTypes[gv] packageList = append(packageList, packageForGroup(normalization.GroupVersion(gv), orderer.OrderTypes(types), arguments.OutputPackagePath, arguments.OutputBase, boilerplate)) if customArgs.FakeClient { packageList = append(packageList, fake.PackageForGroup(normalization.GroupVersion(gv), orderer.OrderTypes(types), arguments.OutputPackagePath, arguments.OutputBase, boilerplate)) } } return generator.Packages(packageList) }
func (g *genDeepCopy) copyableWithinPackage(t *types.Type) bool { // TODO: We should generate public DeepCopy functions per directory, instead // of generating everything everywhere. // This is done that way only to minimize number of changes per PR. // Once this is done, we should replace HasPrefix with: //if t.Name.Package != g.targetPackage { // return false //} if !strings.HasPrefix(t.Name.Package, "k8s.io/kubernetes/") { return false } if types.ExtractCommentTags("+", t.CommentLines)["gencopy"] == "false" { return false } // TODO: Consider generating functions for other kinds too. return t.Kind == types.Struct }
func copyableWithinPackage(t *types.Type, explicitCopyRequired bool) bool { tag := types.ExtractCommentTags("+", t.CommentLines)["gencopy"] if tag == "false" { return false } if explicitCopyRequired && tag != "true" { return false } // TODO: Consider generating functions for other kinds too. if t.Kind != types.Struct { return false } // Also, filter out private types. if namer.IsPrivateGoName(t.Name.Name) { return false } return true }
// Filter ignores types that are identified as not exportable. func (g *genProtoIDL) Filter(c *generator.Context, t *types.Type) bool { tagVals := types.ExtractCommentTags("+", t.CommentLines)["protobuf"] if tagVals != nil { if tagVals[0] == "false" { // Type specified "false". return false } if tagVals[0] == "true" { // Type specified "true". return true } glog.Fatalf(`Comment tag "protobuf" must be true or false, found: %q`, tagVals[0]) } if !g.generateAll { // We're not generating everything. return false } seen := map[*types.Type]bool{} ok := isProtoable(seen, t) return ok }
func extractTag(comments []string) *tagValue { tagVals := types.ExtractCommentTags("+", comments)[tagName] if tagVals == nil { // No match for the tag. return nil } // If there are multiple values, abort. if len(tagVals) > 1 { glog.Fatalf("Found %d %s tags: %q", len(tagVals), tagName, tagVals) } // If we got here we are returning something. tag := &tagValue{} // Get the primary value. parts := strings.Split(tagVals[0], ",") if len(parts) >= 1 { tag.value = parts[0] } // Parse extra arguments. parts = parts[1:] for i := range parts { kv := strings.SplitN(parts[i], "=", 2) k := kv[0] v := "" if len(kv) == 2 { v = kv[1] } switch k { case "register": if v != "false" { tag.register = true } default: glog.Fatalf("Unsupported %s param: %q", tagName, parts[i]) } } return tag }
func (g *genDeepCopy) copyableWithinPackage(t *types.Type) bool { // TODO: We should generate public DeepCopy functions per directory, instead // of generating everything everywhere. // This is done that way only to minimize number of changes per PR. // Once this is done, we should replace HasPrefix with: //if t.Name.Package != g.targetPackage { // return false //} if !strings.HasPrefix(t.Name.Package, "k8s.io/kubernetes/") { return false } if types.ExtractCommentTags("+", t.CommentLines)["gencopy"] == "false" { return false } // TODO: Consider generating functions for other kinds too. if t.Kind != types.Struct { return false } // TODO: This should be removed once we start generating public DeepCopy // functions per directory. if t.Name.Package != g.targetPackage { // We won't be able to access private fields. // Thus, this type cannot have private fields. for _, member := range t.Members { if strings.ToLower(member.Name[:1]) == member.Name[:1] { return false } // TODO: This is a temporary hack, to make avoid generating function // for conversion.Equalities. We should get rid of it. if member.Embedded { return false } } } return true }
func extractTag(comments []string) []string { return types.ExtractCommentTags("+", comments)[tagName] }
// 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) } groupToTypes := map[string][]*types.Type{} for _, inputDir := range arguments.InputDirs { p := context.Universe.Package(inputDir) for _, t := range p.Types { if types.ExtractCommentTags("+", t.CommentLines)["genclient"] != "true" { continue } group := filepath.Base(t.Name.Package) // Special case for the legacy API. if group == "api" { group = "" } if _, found := groupToTypes[group]; !found { groupToTypes[group] = []*types.Type{} } groupToTypes[group] = append(groupToTypes[group], t) } } return generator.Packages{&generator.DefaultPackage{ PackageName: filepath.Base(arguments.OutputPackagePath), PackagePath: arguments.OutputPackagePath, HeaderText: boilerplate, 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(), }) } for group, types := range groupToTypes { generators = append(generators, &genGroup{ DefaultGen: generator.DefaultGen{ OptionalName: group, }, outputPackage: arguments.OutputPackagePath, group: group, types: types, imports: generator.NewImportTracker(), }) } return generators }, FilterFunc: func(c *generator.Context, t *types.Type) bool { return types.ExtractCommentTags("+", t.CommentLines)["genclient"] == "true" }, }} }
// 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) } customArgs, ok := arguments.CustomArgs.(clientgenargs.Args) if !ok { glog.Fatalf("cannot convert arguments.CustomArgs to clientgenargs.Args") } includedTypesOverrides := customArgs.IncludedTypesOverrides generatedBy := generatedBy(customArgs) gvToTypes := map[unversioned.GroupVersion][]*types.Type{} for gv, inputDir := range customArgs.GroupVersionToInputPath { p := context.Universe.Package(inputDir) for n, t := range p.Types { // filter out types which are not included in user specified overrides. typesOverride, ok := includedTypesOverrides[gv] if ok { found := false for _, typeStr := range typesOverride { if typeStr == n { found = true } } if !found { continue } } else { // User has not specified any override for this group version. // filter out types which dont have genclient=true. if types.ExtractCommentTags("+", t.SecondClosestCommentLines)["genclient"] != "true" { continue } } if _, found := gvToTypes[gv]; !found { gvToTypes[gv] = []*types.Type{} } gvToTypes[gv] = append(gvToTypes[gv], t) } } var packageList []generator.Package typedClientBasePath := filepath.Join(customArgs.ClientsetOutputPath, customArgs.ClientsetName, "typed") packageList = append(packageList, packageForClientset(customArgs, typedClientBasePath, boilerplate, generatedBy)) if customArgs.FakeClient { packageList = append(packageList, fake.PackageForClientset(customArgs, typedClientBasePath, boilerplate, generatedBy)) } // If --clientset-only=true, we don't regenerate the individual typed clients. if customArgs.ClientsetOnly { return generator.Packages(packageList) } orderer := namer.Orderer{Namer: namer.NewPrivateNamer(0)} for _, gv := range customArgs.GroupVersions { types := gvToTypes[gv] inputPath := customArgs.GroupVersionToInputPath[gv] packageList = append(packageList, packageForGroup(normalization.GroupVersion(gv), orderer.OrderTypes(types), typedClientBasePath, arguments.OutputBase, inputPath, boilerplate, generatedBy)) if customArgs.FakeClient { packageList = append(packageList, fake.PackageForGroup(normalization.GroupVersion(gv), orderer.OrderTypes(types), typedClientBasePath, arguments.OutputBase, inputPath, boilerplate, generatedBy)) } } return generator.Packages(packageList) }
// Packages makes the sets package definition. func Packages(_ *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: "sets", PackagePath: arguments.OutputPackagePath, HeaderText: append(boilerplate, []byte( ` // This file was autogenerated by set-gen. Do not edit it manually! `)...), PackageDocumentation: []byte( `// Package sets has auto-generated set types. `), // 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"}, // Make a separate file for the Empty type, since it's shared by every type. generator.DefaultGen{ OptionalName: "empty", OptionalBody: []byte(emptyTypeDecl), }, } // Since we want a file per type that we generate a set for, we // have to provide a function for this. for _, t := range c.Order { generators = append(generators, &genSet{ 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 { // It would be reasonable to filter by the type's package here. // It might be necessary if your input directory has a big // import graph. switch t.Kind { case types.Map, types.Slice, types.Pointer: // These types can't be keys in a map. return false case types.Builtin: return true case types.Struct: // Only some structs can be keys in a map. This is triggered by the line // // +genset // or // // +genset=true return types.ExtractCommentTags("+", t.CommentLines)["genset"] == "true" } return false }, }} }
func (b bodyGen) doStruct(sw *generator.SnippetWriter) error { if len(b.t.Name.Name) == 0 { return nil } if namer.IsPrivateGoName(b.t.Name.Name) { return nil } var alias *types.Type var fields []protoField options := []string{} allOptions := types.ExtractCommentTags("+", b.t.CommentLines) for k, v := range allOptions { switch { case strings.HasPrefix(k, "protobuf.options."): key := strings.TrimPrefix(k, "protobuf.options.") switch key { case "marshal": if v == "false" { if !b.omitGogo { options = append(options, "(gogoproto.marshaler) = false", "(gogoproto.unmarshaler) = false", "(gogoproto.sizer) = false", ) } } default: if !b.omitGogo || !strings.HasPrefix(key, "(gogoproto.") { options = append(options, fmt.Sprintf("%s = %s", key, v)) } } // protobuf.as allows a type to have the same message contents as another Go type case k == "protobuf.as": fields = nil if alias = b.locator.GoTypeForName(types.Name{Name: v}); alias == nil { return fmt.Errorf("type %v references alias %q which does not exist", b.t, v) } // protobuf.embed instructs the generator to use the named type in this package // as an embedded message. case k == "protobuf.embed": fields = []protoField{ { Tag: 1, Name: v, Type: &types.Type{ Name: types.Name{ Name: v, Package: b.localPackage.Package, Path: b.localPackage.Path, }, }, }, } } } if alias == nil { alias = b.t } // If we don't explicitly embed anything, generate fields by traversing fields. if fields == nil { memberFields, err := membersToFields(b.locator, alias, b.localPackage, b.omitFieldTypes) if err != nil { return fmt.Errorf("type %v cannot be converted to protobuf: %v", b.t, err) } fields = memberFields } out := sw.Out() genComment(out, b.t.CommentLines, "") sw.Do(`message $.Name.Name$ { `, b.t) if len(options) > 0 { sort.Sort(sort.StringSlice(options)) for _, s := range options { fmt.Fprintf(out, " option %s;\n", s) } fmt.Fprintln(out) } for i, field := range fields { genComment(out, field.CommentLines, " ") fmt.Fprintf(out, " ") switch { case field.Map: case field.Repeated: fmt.Fprintf(out, "repeated ") case field.Required: fmt.Fprintf(out, "required ") default: fmt.Fprintf(out, "optional ") } sw.Do(`$.Type|local$ $.Name$ = $.Tag$`, field) if len(field.Extras) > 0 { extras := []string{} for k, v := range field.Extras { if b.omitGogo && strings.HasPrefix(k, "(gogoproto.") { continue } extras = append(extras, fmt.Sprintf("%s = %s", k, v)) } sort.Sort(sort.StringSlice(extras)) if len(extras) > 0 { fmt.Fprintf(out, " [") fmt.Fprint(out, strings.Join(extras, ", ")) fmt.Fprintf(out, "]") } } fmt.Fprintf(out, ";\n") if i != len(fields)-1 { fmt.Fprintf(out, "\n") } } fmt.Fprintf(out, "}\n\n") return nil }
func Packages(context *generator.Context, arguments *args.GeneratorArgs) generator.Packages { boilerplate, err := arguments.LoadGoBoilerplate() if err != nil { glog.Fatalf("Failed loading boilerplate: %v", err) } inputs := sets.NewString(arguments.InputDirs...) packages := generator.Packages{} header := append([]byte( `// +build !ignore_autogenerated `), boilerplate...) header = append(header, []byte( ` // This file was autogenerated by conversion-gen. Do not edit it manually! `)...) // Compute all pre-existing conversion functions. preexisting := existingConversionFunctions(context) preexistingDefaults := existingDefaultingFunctions(context) // We are generating conversions only for packages that are explicitly // passed as InputDir, and only for those that have a corresponding type // (in the directory one above) and can be automatically converted to. for _, p := range context.Universe { path := p.Path if !inputs.Has(path) { continue } // Only generate conversions for package which explicitly requested it // byt setting "+genversion=true" in their doc.go file. filtered := false for _, comment := range p.DocComments { comment := strings.Trim(comment, "//") if types.ExtractCommentTags("+", comment)["genconversion"] == "true" { filtered = true } } if !filtered { continue } convertibleType := false for _, t := range p.Types { // Check whether this type can be auto-converted to the internal // version. internalType, exists := getInternalTypeFor(context, t) if !exists { // There is no corresponding type in the internal package. continue } // We won't be able to convert to private type. if namer.IsPrivateGoName(internalType.Name.Name) { continue } // If we can generate conversion in any direction, we should // generate this package. if isConvertible(t, internalType, preexisting) || isConvertible(internalType, t, preexisting) { convertibleType = true } } if convertibleType { packages = append(packages, &generator.DefaultPackage{ PackageName: filepath.Base(path), PackagePath: path, HeaderText: header, GeneratorFunc: func(c *generator.Context) (generators []generator.Generator) { generators = []generator.Generator{} generators = append( generators, NewGenConversion("conversion_generated", path, preexisting, preexistingDefaults)) return generators }, FilterFunc: func(c *generator.Context, t *types.Type) bool { return t.Name.Package == path }, }) } } return packages }
// GenerateType makes the body of a file implementing the individual typed client for type t. func (g *genFakeForType) GenerateType(c *generator.Context, t *types.Type, w io.Writer) error { sw := generator.NewSnippetWriter(w, c, "$", "$") pkg := filepath.Base(t.Name.Package) const pkgTestingCore = "k8s.io/kubernetes/pkg/client/testing/core" namespaced := !(types.ExtractCommentTags("+", t.SecondClosestCommentLines)["nonNamespaced"] == "true") canonicalGroup := g.group if canonicalGroup == "core" { canonicalGroup = "" } canonicalVersion := g.version if canonicalVersion == "unversioned" { canonicalVersion = "" } groupName := g.group if g.group == "core" { groupName = "" } // allow user to define a group name that's different from the one parsed from the directory. for _, comment := range c.Universe.Package(g.inputPackage).DocComments { comment = strings.TrimLeft(comment, "//") if override, ok := types.ExtractCommentTags("+", comment)["groupName"]; ok { groupName = override } } m := map[string]interface{}{ "type": t, "package": pkg, "Package": namer.IC(pkg), "namespaced": namespaced, "Group": namer.IC(g.group), "group": canonicalGroup, "groupName": groupName, "version": canonicalVersion, "watchInterface": c.Universe.Type(types.Name{Package: "k8s.io/kubernetes/pkg/watch", Name: "Interface"}), "apiDeleteOptions": c.Universe.Type(types.Name{Package: "k8s.io/kubernetes/pkg/api", Name: "DeleteOptions"}), "apiListOptions": c.Universe.Type(types.Name{Package: "k8s.io/kubernetes/pkg/api", Name: "ListOptions"}), "GroupVersionResource": c.Universe.Type(types.Name{Package: "k8s.io/kubernetes/pkg/api/unversioned", Name: "GroupVersionResource"}), "PatchType": c.Universe.Type(types.Name{Package: "k8s.io/kubernetes/pkg/api", Name: "PatchType"}), "Everything": c.Universe.Function(types.Name{Package: "k8s.io/kubernetes/pkg/labels", Name: "Everything"}), "NewRootListAction": c.Universe.Function(types.Name{Package: pkgTestingCore, Name: "NewRootListAction"}), "NewListAction": c.Universe.Function(types.Name{Package: pkgTestingCore, Name: "NewListAction"}), "NewRootGetAction": c.Universe.Function(types.Name{Package: pkgTestingCore, Name: "NewRootGetAction"}), "NewGetAction": c.Universe.Function(types.Name{Package: pkgTestingCore, Name: "NewGetAction"}), "NewRootDeleteAction": c.Universe.Function(types.Name{Package: pkgTestingCore, Name: "NewRootDeleteAction"}), "NewDeleteAction": c.Universe.Function(types.Name{Package: pkgTestingCore, Name: "NewDeleteAction"}), "NewRootDeleteCollectionAction": c.Universe.Function(types.Name{Package: pkgTestingCore, Name: "NewRootDeleteCollectionAction"}), "NewDeleteCollectionAction": c.Universe.Function(types.Name{Package: pkgTestingCore, Name: "NewDeleteCollectionAction"}), "NewRootUpdateAction": c.Universe.Function(types.Name{Package: pkgTestingCore, Name: "NewRootUpdateAction"}), "NewUpdateAction": c.Universe.Function(types.Name{Package: pkgTestingCore, Name: "NewUpdateAction"}), "NewRootCreateAction": c.Universe.Function(types.Name{Package: pkgTestingCore, Name: "NewRootCreateAction"}), "NewCreateAction": c.Universe.Function(types.Name{Package: pkgTestingCore, Name: "NewCreateAction"}), "NewRootWatchAction": c.Universe.Function(types.Name{Package: pkgTestingCore, Name: "NewRootWatchAction"}), "NewWatchAction": c.Universe.Function(types.Name{Package: pkgTestingCore, Name: "NewWatchAction"}), "NewUpdateSubresourceAction": c.Universe.Function(types.Name{Package: pkgTestingCore, Name: "NewUpdateSubresourceAction"}), "NewRootUpdateSubresourceAction": c.Universe.Function(types.Name{Package: pkgTestingCore, Name: "NewRootUpdateSubresourceAction"}), "NewRootPatchAction": c.Universe.Function(types.Name{Package: pkgTestingCore, Name: "NewRootPatchAction"}), "NewPatchAction": c.Universe.Function(types.Name{Package: pkgTestingCore, Name: "NewPatchAction"}), } noMethods := types.ExtractCommentTags("+", t.SecondClosestCommentLines)["noMethods"] == "true" if namespaced { sw.Do(structNamespaced, m) } else { sw.Do(structNonNamespaced, m) } if !noMethods { sw.Do(resource, m) sw.Do(createTemplate, m) sw.Do(updateTemplate, m) // Generate the UpdateStatus method if the type has a status if hasStatus(t) { sw.Do(updateStatusTemplate, m) } sw.Do(deleteTemplate, m) sw.Do(deleteCollectionTemplate, m) sw.Do(getTemplate, m) if hasObjectMeta(t) { sw.Do(listUsingOptionsTemplate, m) } else { sw.Do(listTemplate, m) } sw.Do(watchTemplate, m) sw.Do(patchTemplate, m) } return sw.Error() }
func (g *genGroup) GenerateType(c *generator.Context, t *types.Type, w io.Writer) error { sw := generator.NewSnippetWriter(w, c, "$", "$") const pkgRESTClient = "k8s.io/kubernetes/pkg/client/restclient" const pkgRegistered = "k8s.io/kubernetes/pkg/apimachinery/registered" const pkgAPI = "k8s.io/kubernetes/pkg/api" const pkgSerializer = "k8s.io/kubernetes/pkg/runtime/serializer" apiPath := func(group string) string { if group == "core" { return `"/api"` } return `"/apis"` } groupName := g.group if g.group == "core" { groupName = "" } // allow user to define a group name that's different from the one parsed from the directory. for _, comment := range c.Universe.Package(g.inputPacakge).DocComments { comment = strings.TrimLeft(comment, "//") if override, ok := types.ExtractCommentTags("+", comment)["groupName"]; ok && override != "" { groupName = override } } m := map[string]interface{}{ "group": normalization.BeforeFirstDot(g.group), "Group": namer.IC(normalization.BeforeFirstDot(g.group)), "groupName": groupName, "types": g.types, "Config": c.Universe.Type(types.Name{Package: pkgRESTClient, Name: "Config"}), "DefaultKubernetesUserAgent": c.Universe.Function(types.Name{Package: pkgRESTClient, Name: "DefaultKubernetesUserAgent"}), "RESTClient": c.Universe.Type(types.Name{Package: pkgRESTClient, Name: "RESTClient"}), "RESTClientFor": c.Universe.Function(types.Name{Package: pkgRESTClient, Name: "RESTClientFor"}), "latestGroup": c.Universe.Variable(types.Name{Package: pkgRegistered, Name: "Group"}), "GroupOrDie": c.Universe.Variable(types.Name{Package: pkgRegistered, Name: "GroupOrDie"}), "apiPath": apiPath(g.group), "codecs": c.Universe.Variable(types.Name{Package: pkgAPI, Name: "Codecs"}), "directCodecFactory": c.Universe.Variable(types.Name{Package: pkgSerializer, Name: "DirectCodecFactory"}), "Errorf": c.Universe.Variable(types.Name{Package: "fmt", Name: "Errorf"}), } sw.Do(groupInterfaceTemplate, m) sw.Do(groupClientTemplate, m) for _, t := range g.types { wrapper := map[string]interface{}{ "type": t, "Group": namer.IC(normalization.BeforeFirstDot(g.group)), } namespaced := !(types.ExtractCommentTags("+", t.SecondClosestCommentLines)["nonNamespaced"] == "true") if namespaced { sw.Do(getterImplNamespaced, wrapper) } else { sw.Do(getterImplNonNamespaced, wrapper) } } sw.Do(newClientForConfigTemplate, m) sw.Do(newClientForConfigOrDieTemplate, m) sw.Do(newClientForRESTClientTemplate, m) if g.version == "unversioned" { sw.Do(setInternalVersionClientDefaultsTemplate, m) } else { sw.Do(setClientDefaultsTemplate, m) } sw.Do(getRESTClient, m) return sw.Error() }