From 3aeb03760533700d5d2c0a62cf0e85ed24d5f986 Mon Sep 17 00:00:00 2001 From: Joe Betz Date: Mon, 9 Nov 2020 14:57:48 -0800 Subject: [PATCH 1/2] Stub out generator --- cmd/controller-gen/main.go | 2 + pkg/applyconfigurations/doc.go | 18 ++ pkg/applyconfigurations/gen.go | 270 ++++++++++++++++++ pkg/applyconfigurations/jsontagutil.go | 97 +++++++ pkg/applyconfigurations/traverse.go | 257 +++++++++++++++++ .../zz_generated.markerhelp.go | 45 +++ 6 files changed, 689 insertions(+) create mode 100644 pkg/applyconfigurations/doc.go create mode 100644 pkg/applyconfigurations/gen.go create mode 100644 pkg/applyconfigurations/jsontagutil.go create mode 100644 pkg/applyconfigurations/traverse.go create mode 100644 pkg/applyconfigurations/zz_generated.markerhelp.go diff --git a/cmd/controller-gen/main.go b/cmd/controller-gen/main.go index c27bcda42..700df2156 100644 --- a/cmd/controller-gen/main.go +++ b/cmd/controller-gen/main.go @@ -24,6 +24,7 @@ import ( "github.com/spf13/cobra" + "sigs.k8s.io/controller-tools/pkg/applyconfigurations" "sigs.k8s.io/controller-tools/pkg/crd" "sigs.k8s.io/controller-tools/pkg/deepcopy" "sigs.k8s.io/controller-tools/pkg/genall" @@ -51,6 +52,7 @@ var ( "crd": crd.Generator{}, "rbac": rbac.Generator{}, "object": deepcopy.Generator{}, + "apply": applyconfigurations.Generator{}, "webhook": webhook.Generator{}, "schemapatch": schemapatcher.Generator{}, } diff --git a/pkg/applyconfigurations/doc.go b/pkg/applyconfigurations/doc.go new file mode 100644 index 000000000..82c2c10f5 --- /dev/null +++ b/pkg/applyconfigurations/doc.go @@ -0,0 +1,18 @@ +/* +Copyright 2019 The Kubernetes Authors. + +Licensed under the Apache License, Version 2.0 (the "License"); +you may not use this file except in compliance with the License. +You may obtain a copy of the License at + + http://www.apache.org/licenses/LICENSE-2.0 + +Unless required by applicable law or agreed to in writing, software +distributed under the License is distributed on an "AS IS" BASIS, +WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +See the License for the specific language governing permissions and +limitations under the License. +*/ + +// Package applyconfigurations generates types for constructing declarative apply configurations. +package applyconfigurations diff --git a/pkg/applyconfigurations/gen.go b/pkg/applyconfigurations/gen.go new file mode 100644 index 000000000..70e3c2119 --- /dev/null +++ b/pkg/applyconfigurations/gen.go @@ -0,0 +1,270 @@ +/* +Copyright 2019 The Kubernetes Authors. + +Licensed under the Apache License, Version 2.0 (the "License"); +you may not use this file except in compliance with the License. +You may obtain a copy of the License at + + http://www.apache.org/licenses/LICENSE-2.0 + +Unless required by applicable law or agreed to in writing, software +distributed under the License is distributed on an "AS IS" BASIS, +WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +See the License for the specific language governing permissions and +limitations under the License. +*/ + +package applyconfigurations + +import ( + "bytes" + "fmt" + "go/ast" + "go/format" + "io" + "sort" + "strings" + + "sigs.k8s.io/controller-tools/pkg/genall" + "sigs.k8s.io/controller-tools/pkg/loader" + "sigs.k8s.io/controller-tools/pkg/markers" +) + +// Based on deepcopy gen but with legacy marker support removed. + +var ( + enablePkgMarker = markers.Must(markers.MakeDefinition("kubebuilder:object:generate", markers.DescribesPackage, false)) + enableTypeMarker = markers.Must(markers.MakeDefinition("kubebuilder:object:generate", markers.DescribesType, false)) + isObjectMarker = markers.Must(markers.MakeDefinition("kubebuilder:object:root", markers.DescribesType, false)) +) + +// +controllertools:marker:generateHelp + +// Generator generates code containing apply configuration type implementations. +type Generator struct { + // HeaderFile specifies the header text (e.g. license) to prepend to generated files. + HeaderFile string `marker:",optional"` + // Year specifies the year to substitute for " YEAR" in the header file. + Year string `marker:",optional"` +} + +func (Generator) CheckFilter() loader.NodeFilter { + return func(node ast.Node) bool { + // ignore interfaces + _, isIface := node.(*ast.InterfaceType) + return !isIface + } +} + +func (Generator) RegisterMarkers(into *markers.Registry) error { + if err := markers.RegisterAll(into, + enablePkgMarker, enableTypeMarker, isObjectMarker); err != nil { + return err + } + into.AddHelp(enablePkgMarker, + markers.SimpleHelp("apply", "enables or disables object apply configuration generation for this package")) + into.AddHelp( + enableTypeMarker, markers.SimpleHelp("apply", "overrides enabling or disabling apply configuration generation for this type")) + into.AddHelp(isObjectMarker, + markers.SimpleHelp("apply", "enables apply configuration generation for this type")) + return nil +} + +func enabledOnPackage(col *markers.Collector, pkg *loader.Package) (bool, error) { + pkgMarkers, err := markers.PackageMarkers(col, pkg) + if err != nil { + return false, err + } + pkgMarker := pkgMarkers.Get(enablePkgMarker.Name) + if pkgMarker != nil { + return pkgMarker.(bool), nil + } + + return false, nil +} + +func enabledOnType(allTypes bool, info *markers.TypeInfo) bool { + if typeMarker := info.Markers.Get(enableTypeMarker.Name); typeMarker != nil { + return typeMarker.(bool) + } + return allTypes || genObjectInterface(info) +} + +func genObjectInterface(info *markers.TypeInfo) bool { + objectEnabled := info.Markers.Get(isObjectMarker.Name) + if objectEnabled != nil { + return objectEnabled.(bool) + } + return false +} + +func (d Generator) Generate(ctx *genall.GenerationContext) error { + var headerText string + + if d.HeaderFile != "" { + headerBytes, err := ctx.ReadFile(d.HeaderFile) + if err != nil { + return err + } + headerText = string(headerBytes) + } + headerText = strings.ReplaceAll(headerText, " YEAR", " "+d.Year) + + objGenCtx := ObjectGenCtx{ + Collector: ctx.Collector, + Checker: ctx.Checker, + HeaderText: headerText, + } + + for _, root := range ctx.Roots { + outContents := objGenCtx.generateForPackage(root) + if outContents == nil { + continue + } + + writeOut(ctx, root, outContents) + } + + return nil +} + +// ObjectGenCtx contains the common info for generating apply configuration implementations. +// It mostly exists so that generating for a package can be easily tested without +// requiring a full set of output rules, etc. +type ObjectGenCtx struct { + Collector *markers.Collector + Checker *loader.TypeChecker + HeaderText string +} + +// writeHeader writes out the build tag, package declaration, and imports +func writeHeader(pkg *loader.Package, out io.Writer, packageName string, imports *importsList, headerText string) { + // NB(directxman12): blank line after build tags to distinguish them from comments + _, err := fmt.Fprintf(out, `// +build !ignore_autogenerated + +%[3]s + +// Code generated by controller-gen. DO NOT EDIT. + +package %[1]s + +import ( +%[2]s +) + +`, packageName, strings.Join(imports.ImportSpecs(), "\n"), headerText) + if err != nil { + pkg.AddError(err) + } + +} + +// generateForPackage generates apply configuration implementations for +// types in the given package, writing the formatted result to given writer. +// May return nil if source could not be generated. +func (ctx *ObjectGenCtx) generateForPackage(root *loader.Package) []byte { + allTypes, err := enabledOnPackage(ctx.Collector, root) + if err != nil { + root.AddError(err) + return nil + } + + ctx.Checker.Check(root) + + root.NeedTypesInfo() + + byType := make(map[string][]byte) + imports := &importsList{ + byPath: make(map[string]string), + byAlias: make(map[string]string), + pkg: root, + } + // avoid confusing aliases by "reserving" the root package's name as an alias + imports.byAlias[root.Name] = "" + + if err := markers.EachType(ctx.Collector, root, func(info *markers.TypeInfo) { + outContent := new(bytes.Buffer) + + if !enabledOnType(allTypes, info) { + //root.AddError(fmt.Errorf("skipping type: %v", info.Name)) // TODO(jpbetz): Remove + return + } + + // not all types required a generate apply configuration. For example, no apply configuration + // type is needed for Quantity, IntOrString, RawExtension or Unknown. + if !shouldBeApplyConfiguration(root, info) { + //root.AddError(fmt.Errorf("skipping type: %v", info.Name)) // TODO(jpbetz): Remove + return + } + + copyCtx := &applyConfigurationMaker{ + pkg: root, + importsList: imports, + codeWriter: &codeWriter{out: outContent}, + } + + copyCtx.GenerateTypesFor(root, info) + + outBytes := outContent.Bytes() + if len(outBytes) > 0 { + byType[info.Name] = outBytes + } + }); err != nil { + root.AddError(err) + return nil + } + + if len(byType) == 0 { + return nil + } + + outContent := new(bytes.Buffer) + writeHeader(root, outContent, root.Name, imports, ctx.HeaderText) + writeTypes(root, outContent, byType) + + outBytes := outContent.Bytes() + formattedBytes, err := format.Source(outBytes) + if err != nil { + root.AddError(err) + // we still write the invalid source to disk to figure out what went wrong + } else { + outBytes = formattedBytes + } + + return outBytes +} + +// writeTypes writes each method to the file, sorted by type name. +func writeTypes(pkg *loader.Package, out io.Writer, byType map[string][]byte) { + sortedNames := make([]string, 0, len(byType)) + for name := range byType { + sortedNames = append(sortedNames, name) + } + sort.Strings(sortedNames) + + for _, name := range sortedNames { + _, err := out.Write(byType[name]) + if err != nil { + pkg.AddError(err) + } + } +} + +// writeFormatted outputs the given code, after gofmt-ing it. If we couldn't gofmt, +// we write the unformatted code for debugging purposes. +func writeOut(ctx *genall.GenerationContext, root *loader.Package, outBytes []byte) { + outputFile, err := ctx.Open(root, "zz_generated.applyconfigurations.go") + if err != nil { + root.AddError(err) + return + } + defer outputFile.Close() + n, err := outputFile.Write(outBytes) + if err != nil { + root.AddError(err) + return + } + if n < len(outBytes) { + root.AddError(io.ErrShortWrite) + } +} diff --git a/pkg/applyconfigurations/jsontagutil.go b/pkg/applyconfigurations/jsontagutil.go new file mode 100644 index 000000000..01df470b2 --- /dev/null +++ b/pkg/applyconfigurations/jsontagutil.go @@ -0,0 +1,97 @@ +/* +Copyright 2019 The Kubernetes Authors. + +Licensed under the Apache License, Version 2.0 (the "License"); +you may not use this file except in compliance with the License. +You may obtain a copy of the License at + + http://www.apache.org/licenses/LICENSE-2.0 + +Unless required by applicable law or agreed to in writing, software +distributed under the License is distributed on an "AS IS" BASIS, +WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +See the License for the specific language governing permissions and +limitations under the License. +*/ + +package applyconfigurations + +import ( + "strings" + + "sigs.k8s.io/controller-tools/pkg/markers" +) + +// TODO: This implements the same functionality as https://github.com/kubernetes/kubernetes/blob/master/staging/src/k8s.io/apimachinery/pkg/runtime/converter.go#L236 +// but is based on the more efficient approach from https://golang.org/src/encoding/json/encode.go + +type jsonTags struct { + name string + omit bool + inline bool + omitempty bool +} + +func (t jsonTags) String() string { + var tag string + if !t.inline { + tag += t.name + } + if t.omitempty { + tag += ",omitempty" + } + if t.inline { + tag += ",inline" + } + return tag +} + +func lookupJsonTags(field markers.FieldInfo) (jsonTags, bool) { + tag := field.Tag.Get("json") + if tag == "" || tag == "-" { + return jsonTags{}, false + } + name, opts := parseTag(tag) + if name == "" { + name = field.Name + } + return jsonTags{ + name: name, + omit: false, + inline: opts.Contains("inline"), + omitempty: opts.Contains("omitempty"), + }, true +} + +type tagOptions string + +// parseTag splits a struct field's json tag into its name and +// comma-separated options. +func parseTag(tag string) (string, tagOptions) { + if idx := strings.Index(tag, ","); idx != -1 { + return tag[:idx], tagOptions(tag[idx+1:]) + } + return tag, "" +} + +// Contains reports whether a comma-separated listAlias of options +// contains a particular substr flag. substr must be surrounded by a +// string boundary or commas. +func (o tagOptions) Contains(optionName string) bool { + if len(o) == 0 { + return false + } + s := string(o) + for s != "" { + var next string + i := strings.Index(s, ",") + if i >= 0 { + s, next = s[:i], s[i+1:] + } + if s == optionName { + return true + } + s = next + } + return false +} diff --git a/pkg/applyconfigurations/traverse.go b/pkg/applyconfigurations/traverse.go new file mode 100644 index 000000000..3c8bb1bb9 --- /dev/null +++ b/pkg/applyconfigurations/traverse.go @@ -0,0 +1,257 @@ +/* +Copyright 2019 The Kubernetes Authors. + +Licensed under the Apache License, Version 2.0 (the "License"); +you may not use this file except in compliance with the License. +You may obtain a copy of the License at + + http://www.apache.org/licenses/LICENSE-2.0 + +Unless required by applicable law or agreed to in writing, software +distributed under the License is distributed on an "AS IS" BASIS, +WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +See the License for the specific language governing permissions and +limitations under the License. +*/ + +package applyconfigurations + +import ( + "fmt" + "go/ast" + "go/types" + "io" + "path" + "strings" + "unicode" + "unicode/utf8" + + "sigs.k8s.io/controller-tools/pkg/loader" + "sigs.k8s.io/controller-tools/pkg/markers" +) + +// TODO(jpbetz): This was copied from deepcopy-gen + +// codeWriter assists in writing out Go code lines and blocks to a writer. +type codeWriter struct { + out io.Writer +} + +// Line writes a single line. +func (c *codeWriter) Line(line string) { + if _, err := fmt.Fprintln(c.out, line); err != nil { + panic(err) + } +} + +// Linef writes a single line with formatting (as per fmt.Sprintf). +func (c *codeWriter) Linef(line string, args ...interface{}) { + if _, err := fmt.Fprintf(c.out, line+"\n", args...); err != nil { + panic(err) + } +} + +// importsList keeps track of required imports, automatically assigning aliases +// to import statement. +type importsList struct { + byPath map[string]string + byAlias map[string]string + + pkg *loader.Package +} + +// NeedImport marks that the given package is needed in the list of imports, +// returning the ident (import alias) that should be used to reference the package. +func (l *importsList) NeedImport(importPath string) string { + // we get an actual path from Package, which might include venddored + // packages if running on a package in vendor. + if ind := strings.LastIndex(importPath, "/vendor/"); ind != -1 { + importPath = importPath[ind+8: /* len("/vendor/") */] + } + + // check to see if we've already assigned an alias, and just return that. + alias, exists := l.byPath[importPath] + if exists { + return alias + } + + // otherwise, calculate an import alias by joining path parts till we get something unique + restPath, nextWord := path.Split(importPath) + + for otherPath, exists := "", true; exists && otherPath != importPath; otherPath, exists = l.byAlias[alias] { + if restPath == "" { + // do something else to disambiguate if we're run out of parts and + // still have duplicates, somehow + alias += "x" + } + + // can't have a first digit, per Go identifier rules, so just skip them + for firstRune, runeLen := utf8.DecodeRuneInString(nextWord); unicode.IsDigit(firstRune); firstRune, runeLen = utf8.DecodeRuneInString(nextWord) { + nextWord = nextWord[runeLen:] + } + + // make a valid identifier by replacing "bad" characters with underscores + nextWord = strings.Map(func(r rune) rune { + if unicode.IsLetter(r) || unicode.IsDigit(r) || r == '_' { + return r + } + return '_' + }, nextWord) + + alias = nextWord + alias + if len(restPath) > 0 { + restPath, nextWord = path.Split(restPath[:len(restPath)-1] /* chop off final slash */) + } + } + + l.byPath[importPath] = alias + l.byAlias[alias] = importPath + return alias +} + +// ImportSpecs returns a string form of each import spec +// (i.e. `alias "path/to/import"). Aliases are only present +// when they don't match the package name. +func (l *importsList) ImportSpecs() []string { + res := make([]string, 0, len(l.byPath)) + for importPath, alias := range l.byPath { + pkg := l.pkg.Imports()[importPath] + if pkg != nil && pkg.Name == alias { + // don't print if alias is the same as package name + // (we've already taken care of duplicates). + res = append(res, fmt.Sprintf("%q", importPath)) + } else { + res = append(res, fmt.Sprintf("%s %q", alias, importPath)) + } + } + return res +} + +// namingInfo holds package and syntax for referencing a field, type, +// etc. It's used to allow lazily marking import usage. +// You should generally retrieve the syntax using Syntax. +type namingInfo struct { + // typeInfo is the type being named. + typeInfo types.Type + nameOverride string +} + +// Syntax calculates the code representation of the given type or name, +// and marks that is used (potentially marking an import as used). +func (n *namingInfo) Syntax(basePkg *loader.Package, imports *importsList) string { + if n.nameOverride != "" { + return n.nameOverride + } + + // NB(directxman12): typeInfo.String gets us most of the way there, + // but fails (for us) on named imports, since it uses the full package path. + switch typeInfo := n.typeInfo.(type) { + case *types.Named: + // register that we need an import for this type, + // so we can get the appropriate alias to use. + typeName := typeInfo.Obj() + otherPkg := typeName.Pkg() + if otherPkg == basePkg.Types { + // local import + return typeName.Name() + } + alias := imports.NeedImport(loader.NonVendorPath(otherPkg.Path())) + return alias + "." + typeName.Name() + case *types.Basic: + return typeInfo.String() + case *types.Pointer: + return "*" + (&namingInfo{typeInfo: typeInfo.Elem()}).Syntax(basePkg, imports) + case *types.Slice: + return "[]" + (&namingInfo{typeInfo: typeInfo.Elem()}).Syntax(basePkg, imports) + case *types.Map: + return fmt.Sprintf( + "map[%s]%s", + (&namingInfo{typeInfo: typeInfo.Key()}).Syntax(basePkg, imports), + (&namingInfo{typeInfo: typeInfo.Elem()}).Syntax(basePkg, imports)) + default: + basePkg.AddError(fmt.Errorf("name requested for invalid type: %s", typeInfo)) + return typeInfo.String() + } +} + +// copyMethodMakers makes apply configurations for Go types, +// writing them to its codeWriter. +type applyConfigurationMaker struct { + pkg *loader.Package + *importsList + *codeWriter +} + +// GenerateTypesFor makes makes apply configuration types for the given type, when appropriate +func (c *applyConfigurationMaker) GenerateTypesFor(root *loader.Package, info *markers.TypeInfo) { + typeInfo := root.TypesInfo.TypeOf(info.RawSpec.Name) + if typeInfo == types.Typ[types.Invalid] { + root.AddError(loader.ErrFromNode(fmt.Errorf("unknown type: %s", info.Name), info.RawSpec)) + } + + // TODO(jpbetz): Generate output here + + c.Linef("type %sApplyConfiguration struct {}", info.Name) +} + +// shouldBeApplyConfiguration checks if we're supposed to make apply configurations for the given type. +// +// TODO(jpbetz): Copy over logic for inclusion from requiresApplyConfiguration +func shouldBeApplyConfiguration(pkg *loader.Package, info *markers.TypeInfo) bool { + if !ast.IsExported(info.Name) { + return false + } + + typeInfo := pkg.TypesInfo.TypeOf(info.RawSpec.Name) + if typeInfo == types.Typ[types.Invalid] { + pkg.AddError(loader.ErrFromNode(fmt.Errorf("unknown type: %s", info.Name), info.RawSpec)) + return false + } + + // according to gengo, everything named is an alias, except for an alias to a pointer, + // which is just a pointer, afaict. Just roll with it. + if asPtr, isPtr := typeInfo.(*types.Named).Underlying().(*types.Pointer); isPtr { + typeInfo = asPtr + } + + lastType := typeInfo + if _, isNamed := typeInfo.(*types.Named); isNamed { + for underlyingType := typeInfo.Underlying(); underlyingType != lastType; lastType, underlyingType = underlyingType, underlyingType.Underlying() { + // aliases to other things besides basics need copy methods + // (basics can be straight-up shallow-copied) + if _, isBasic := underlyingType.(*types.Basic); !isBasic { + return true + } + } + } + + // structs are the only thing that's not a basic that apply configurations are generated for + _, isStruct := lastType.(*types.Struct) + if !isStruct { + return false + } + if _, ok := excludeTypes[info.Name]; ok { // TODO(jpbetz): What to do here? + return false + } + var hasJsonTaggedMembers bool + for _, field := range info.Fields { + if _, ok := lookupJsonTags(field); ok { + hasJsonTaggedMembers = true + } + } + return hasJsonTaggedMembers +} + +var ( + rawExtension = "k8s.io/apimachinery/pkg/runtime/RawExtension" + unknown = "k8s.io/apimachinery/pkg/runtime/Unknown" +) +// excludeTypes contains well known types that we do not generate apply configurations for. +// Hard coding because we only have two, very specific types that serve a special purpose +// in the type system here. +var excludeTypes = map[string]struct{}{ + rawExtension: {}, + unknown: {}, + // DO NOT ADD TO THIS LIST. If we need to exclude other types, we should consider allowing the + // go type declarations to be annotated as excluded from this generator. +} diff --git a/pkg/applyconfigurations/zz_generated.markerhelp.go b/pkg/applyconfigurations/zz_generated.markerhelp.go new file mode 100644 index 000000000..84ef16fc2 --- /dev/null +++ b/pkg/applyconfigurations/zz_generated.markerhelp.go @@ -0,0 +1,45 @@ +// +build !ignore_autogenerated + +/* +Copyright2020 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. +*/ + +// Code generated by helpgen. DO NOT EDIT. + +package applyconfigurations + +import ( + "sigs.k8s.io/controller-tools/pkg/markers" +) + +func (Generator) Help() *markers.DefinitionHelp { + return &markers.DefinitionHelp{ + Category: "", + DetailedHelp: markers.DetailedHelp{ + Summary: "generates code containing apply configuration type implementations.", + Details: "", + }, + FieldHelp: map[string]markers.DetailedHelp{ + "HeaderFile": markers.DetailedHelp{ + Summary: "specifies the header text (e.g. license) to prepend to generated files.", + Details: "", + }, + "Year": markers.DetailedHelp{ + Summary: "specifies the year to substitute for \" YEAR\" in the header file.", + Details: "", + }, + }, + } +} From 2332681dad5841376befba418e83adf353d5c2e5 Mon Sep 17 00:00:00 2001 From: Jefftree Date: Tue, 19 Jan 2021 10:59:18 -0800 Subject: [PATCH 2/2] Add apply gen to controller-tools --- cmd/controller-gen/main.go | 7 +- pkg/applyconfigurations/codewriter.go | 41 ++ pkg/applyconfigurations/gen.go | 304 +++++++++-- pkg/applyconfigurations/jsontagutil.go | 5 +- .../ac/zz_generated.applyconfigurations.go | 331 ++++++++++++ .../testdata/cronjob/cronjob_types.go | 182 +++++++ .../testdata/cronjob/go.mod | 51 ++ .../testdata/cronjob/go.sum | 484 ++++++++++++++++++ .../testdata/cronjob/groupversion_info.go | 20 + pkg/applyconfigurations/traverse.go | 402 ++++++++++----- .../zz_generated.markerhelp.go | 7 +- pkg/crd/testdata/go.sum | 1 + pkg/deepcopy/testdata/go.sum | 1 + pkg/genall/output.go | 6 + pkg/util/util.go | 139 +++++ 15 files changed, 1786 insertions(+), 195 deletions(-) create mode 100644 pkg/applyconfigurations/codewriter.go create mode 100644 pkg/applyconfigurations/testdata/cronjob/ac/zz_generated.applyconfigurations.go create mode 100644 pkg/applyconfigurations/testdata/cronjob/cronjob_types.go create mode 100644 pkg/applyconfigurations/testdata/cronjob/go.mod create mode 100644 pkg/applyconfigurations/testdata/cronjob/go.sum create mode 100644 pkg/applyconfigurations/testdata/cronjob/groupversion_info.go create mode 100644 pkg/util/util.go diff --git a/cmd/controller-gen/main.go b/cmd/controller-gen/main.go index 700df2156..46b1f40fb 100644 --- a/cmd/controller-gen/main.go +++ b/cmd/controller-gen/main.go @@ -141,13 +141,18 @@ func main() { controller-gen object paths=./apis/v1beta1/some_types.go # Generate OpenAPI v3 schemas for API packages and merge them into existing CRD manifests - controller-gen schemapatch:manifests=./manifests output:dir=./manifests paths=./pkg/apis/... + controller-gen schemapatch:manifests=./manifests output:dir=./manifests paths=./pkg/apis/... # Run all the generators for a given project controller-gen paths=./apis/... # Explain the markers for generating CRDs, and their arguments controller-gen crd -ww + + # Generate applyconfigurations for CRDs for use with Server Side Apply. They will be placed + # into a "ac/" subdirectory + + controller-gen apply paths=./apis/... `, RunE: func(c *cobra.Command, rawOpts []string) error { // print version if asked for it diff --git a/pkg/applyconfigurations/codewriter.go b/pkg/applyconfigurations/codewriter.go new file mode 100644 index 000000000..431de8081 --- /dev/null +++ b/pkg/applyconfigurations/codewriter.go @@ -0,0 +1,41 @@ +/* +Copyright 2021 The Kubernetes Authors. + +Licensed under the Apache License, Version 2.0 (the "License"); +you may not use this file except in compliance with the License. +You may obtain a copy of the License at + + http://www.apache.org/licenses/LICENSE-2.0 + +Unless required by applicable law or agreed to in writing, software +distributed under the License is distributed on an "AS IS" BASIS, +WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +See the License for the specific language governing permissions and +limitations under the License. +*/ + +package applyconfigurations + +import ( + "fmt" + "io" +) + +// codeWriter assists in writing out Go code lines and blocks to a writer. +type codeWriter struct { + out io.Writer +} + +// Line writes a single line. +func (c *codeWriter) Line(line string) { + if _, err := fmt.Fprintln(c.out, line); err != nil { + panic(err) + } +} + +// Linef writes a single line with formatting (as per fmt.Sprintf). +func (c *codeWriter) Linef(line string, args ...interface{}) { + if _, err := fmt.Fprintf(c.out, line+"\n", args...); err != nil { + panic(err) + } +} diff --git a/pkg/applyconfigurations/gen.go b/pkg/applyconfigurations/gen.go index 70e3c2119..282715fe4 100644 --- a/pkg/applyconfigurations/gen.go +++ b/pkg/applyconfigurations/gen.go @@ -1,5 +1,5 @@ /* -Copyright 2019 The Kubernetes Authors. +Copyright 2021 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. @@ -18,26 +18,40 @@ package applyconfigurations import ( "bytes" - "fmt" "go/ast" "go/format" + "go/types" "io" + "path/filepath" "sort" "strings" + "golang.org/x/tools/go/packages" + crdmarkers "sigs.k8s.io/controller-tools/pkg/crd/markers" "sigs.k8s.io/controller-tools/pkg/genall" "sigs.k8s.io/controller-tools/pkg/loader" "sigs.k8s.io/controller-tools/pkg/markers" + "sigs.k8s.io/controller-tools/pkg/util" ) // Based on deepcopy gen but with legacy marker support removed. var ( - enablePkgMarker = markers.Must(markers.MakeDefinition("kubebuilder:object:generate", markers.DescribesPackage, false)) - enableTypeMarker = markers.Must(markers.MakeDefinition("kubebuilder:object:generate", markers.DescribesType, false)) - isObjectMarker = markers.Must(markers.MakeDefinition("kubebuilder:object:root", markers.DescribesType, false)) + groupNameMarker = markers.Must(markers.MakeDefinition("groupName", markers.DescribesPackage, "")) + versionNameMarker = markers.Must(markers.MakeDefinition("versionName", markers.DescribesPackage, "")) + isCRDMarker = markers.Must(markers.MakeDefinition("kubebuilder:resource", markers.DescribesType, crdmarkers.Resource{})) + enablePkgMarker = markers.Must(markers.MakeDefinition("kubebuilder:ac:generate", markers.DescribesPackage, false)) + enableTypeMarker = markers.Must(markers.MakeDefinition("kubebuilder:ac:generate", markers.DescribesType, false)) ) +var importMapping = map[string]string{ + "k8s.io/apimachinery/pkg/apis/": "k8s.io/client-go/applyconfigurations/", + "k8s.io/api/": "k8s.io/client-go/applyconfigurations/", +} + +const importPathSuffix = "ac" +const packageFileName = "zz_generated.applyconfigurations.go" + // +controllertools:marker:generateHelp // Generator generates code containing apply configuration type implementations. @@ -58,16 +72,23 @@ func (Generator) CheckFilter() loader.NodeFilter { func (Generator) RegisterMarkers(into *markers.Registry) error { if err := markers.RegisterAll(into, - enablePkgMarker, enableTypeMarker, isObjectMarker); err != nil { + groupNameMarker, versionNameMarker, isCRDMarker, enablePkgMarker, enableTypeMarker); err != nil { return err } - into.AddHelp(enablePkgMarker, - markers.SimpleHelp("apply", "enables or disables object apply configuration generation for this package")) - into.AddHelp( - enableTypeMarker, markers.SimpleHelp("apply", "overrides enabling or disabling apply configuration generation for this type")) - into.AddHelp(isObjectMarker, + into.AddHelp(groupNameMarker, + markers.SimpleHelp("apply", "specifies the API group name for this package.")) + + into.AddHelp(versionNameMarker, + markers.SimpleHelp("apply", "overrides the API group version for this package (defaults to the package name).")) + into.AddHelp(isCRDMarker, markers.SimpleHelp("apply", "enables apply configuration generation for this type")) + into.AddHelp( + enableTypeMarker, markers.SimpleHelp("apply", "overrides enabling or disabling applyconfigurations generation for the type")) + + into.AddHelp( + enablePkgMarker, markers.SimpleHelp("apply", "overrides enabling or disabling applyconfigurations generation for the package")) return nil + } func enabledOnPackage(col *markers.Collector, pkg *loader.Package) (bool, error) { @@ -79,25 +100,41 @@ func enabledOnPackage(col *markers.Collector, pkg *loader.Package) (bool, error) if pkgMarker != nil { return pkgMarker.(bool), nil } - return false, nil } -func enabledOnType(allTypes bool, info *markers.TypeInfo) bool { +// enableOnType marks whether applyconfiguration generation is enabled for the type. +func enabledOnType(info *markers.TypeInfo) bool { if typeMarker := info.Markers.Get(enableTypeMarker.Name); typeMarker != nil { return typeMarker.(bool) } - return allTypes || genObjectInterface(info) + return isCRD(info) } -func genObjectInterface(info *markers.TypeInfo) bool { - objectEnabled := info.Markers.Get(isObjectMarker.Name) +// isCRD marks whether the type is a CRD based on the +kubebuilder:resource marker. +func isCRD(info *markers.TypeInfo) bool { + objectEnabled := info.Markers.Get(isCRDMarker.Name) if objectEnabled != nil { - return objectEnabled.(bool) + return true } return false } +func isCRDClusterScope(info *markers.TypeInfo) bool { + if o := info.Markers.Get(isCRDMarker.Name); o != nil { + crd := o.(crdmarkers.Resource) + return crd.Scope == "Cluster" + } + return false +} + +func createApplyConfigPackage(pkg *loader.Package) *loader.Package { + newPkg := &loader.Package{Package: &packages.Package{}} + dir := filepath.Dir(pkg.CompiledGoFiles[0]) + newPkg.CompiledGoFiles = append(newPkg.CompiledGoFiles, dir+"/"+importPathSuffix+"/") + return newPkg +} + func (d Generator) Generate(ctx *genall.GenerationContext) error { var headerText string @@ -116,15 +153,45 @@ func (d Generator) Generate(ctx *genall.GenerationContext) error { HeaderText: headerText, } + var pkgList []*loader.Package + visited := make(map[string]*loader.Package) + for _, root := range ctx.Roots { - outContents := objGenCtx.generateForPackage(root) - if outContents == nil { - continue + visited[root.PkgPath] = root + pkgList = append(pkgList, root) + } + + for _, pkg := range pkgList { + for _, imp := range pkg.Imports() { + if _, ok := visited[imp.PkgPath]; ok { + continue + } + visited[imp.PkgPath] = imp } + } + + universe := &Universe{typeMetadata: make(map[types.Type]*typeMetadata)} - writeOut(ctx, root, outContents) + // Multiple traverses are required so that cross package imports are able + // to be resolved. + // generateEligibleTypes creates the universe for generateUsedTypes to perform a + // breadth first search across all CRDs that need ac generation. + // generateForPackage is final step and performs the code generation. + for _, pkg := range visited { + objGenCtx.generateEligibleTypes(pkg, universe) + } + for _, pkg := range visited { + objGenCtx.generateUsedTypes(pkg, universe) } + for _, pkg := range pkgList { + outContents := objGenCtx.generateForPackage(universe, pkg) + if outContents == nil { + continue + } + newPkg := createApplyConfigPackage(pkg) + writeOut(ctx, newPkg, outContents) + } return nil } @@ -137,73 +204,198 @@ type ObjectGenCtx struct { HeaderText string } -// writeHeader writes out the build tag, package declaration, and imports -func writeHeader(pkg *loader.Package, out io.Writer, packageName string, imports *importsList, headerText string) { - // NB(directxman12): blank line after build tags to distinguish them from comments - _, err := fmt.Fprintf(out, `// +build !ignore_autogenerated +// generateEligibleTypes generates a universe of all possible ApplyConfiguration types. +// The function also scans all imported packages for types that are eligible to be ApplyConfigurations. +// This first pass is necessary because the loader package is not able to follow references between packages +// and this universe constructs the necessary references. +func (ctx *ObjectGenCtx) generateEligibleTypes(root *loader.Package, universe *Universe) { + ctx.Checker.Check(root) + root.NeedTypesInfo() -%[3]s + if err := markers.EachType(ctx.Collector, root, func(info *markers.TypeInfo) { + // not all types required a generate apply configuration. For example, no apply configuration + // type is needed for Quantity, IntOrString, RawExtension or Unknown. -// Code generated by controller-gen. DO NOT EDIT. + if shouldBeApplyConfiguration(root, info) { + typeInfo := root.TypesInfo.TypeOf(info.RawSpec.Name) + universe.typeMetadata[typeInfo] = &typeMetadata{ + info: info, + root: root, + eligible: true, + used: false, + } + } -package %[1]s + }); err != nil { + root.AddError(err) + return + } + return +} -import ( -%[2]s -) +// generateUsedTypes does a breadth first search from each top level root object +// to find all ApplyConfiguration types that must be generated based on the fields +// that the object references. +func (ctx *ObjectGenCtx) generateUsedTypes(root *loader.Package, universe *Universe) { + ctx.Checker.Check(root) + root.NeedTypesInfo() -`, packageName, strings.Join(imports.ImportSpecs(), "\n"), headerText) - if err != nil { - pkg.AddError(err) + if err := markers.EachType(ctx.Collector, root, func(info *markers.TypeInfo) { + if !enabledOnType(info) { + return + } + + var q []types.Type + q = append(q, root.TypesInfo.TypeOf(info.RawSpec.Name)) + + for len(q) > 0 { + node := universe.typeMetadata[q[0]] + q = q[1:] + if node.used { + continue + } + node.used = true + if len(node.info.Fields) > 0 { + for _, field := range node.info.Fields { + fieldType := node.root.TypesInfo.TypeOf(field.RawField.Type) + resolved := false + // TODO: Are these all the types that need to be resolved? + for !resolved { + resolved = true + switch typeInfo := fieldType.(type) { + case *types.Pointer: + fieldType = typeInfo.Elem() + resolved = false + case *types.Slice: + fieldType = typeInfo.Elem() + resolved = false + } + } + + if _, ok := universe.typeMetadata[fieldType]; ok { + q = append(q, fieldType) + } + } + } + } + }); err != nil { + root.AddError(err) + return + } + return +} + +type Universe struct { + typeMetadata map[types.Type]*typeMetadata +} + +type typeMetadata struct { + info *markers.TypeInfo + root *loader.Package + eligible bool + used bool +} + +func (u *Universe) existingApplyConfigPath(_ *types.Named, pkgPath string) (string, bool) { + for prefix, replacePath := range importMapping { + if strings.HasPrefix(pkgPath, prefix) { + path := replacePath + strings.TrimPrefix(pkgPath, prefix) + return path, true + } + } + return "", false +} + +func (u *Universe) IsApplyConfigGenerated(typeInfo *types.Named, pkgPath string) bool { + if t, ok := u.typeMetadata[typeInfo]; ok { + return t.used } + return false +} +func (u *Universe) GetApplyConfigPath(typeInfo *types.Named, pkgPath string) (string, bool) { + isApplyConfigGenerated := u.IsApplyConfigGenerated(typeInfo, pkgPath) + if path, ok := u.existingApplyConfigPath(typeInfo, pkgPath); ok { + if isApplyConfigGenerated { + return path, true + } + return pkgPath, false + } + // ApplyConfig is necessary but location is not explicitly specified. Assume the ApplyConfig exists at the below directory + if isApplyConfigGenerated { + return pkgPath + "/" + importPathSuffix, true + } + return pkgPath, false } // generateForPackage generates apply configuration implementations for // types in the given package, writing the formatted result to given writer. // May return nil if source could not be generated. -func (ctx *ObjectGenCtx) generateForPackage(root *loader.Package) []byte { - allTypes, err := enabledOnPackage(ctx.Collector, root) +func (ctx *ObjectGenCtx) generateForPackage(universe *Universe, root *loader.Package) []byte { + pkgMarkers, err := markers.PackageMarkers(ctx.Collector, root) if err != nil { root.AddError(err) - return nil + } + group := "" + if val := pkgMarkers.Get("groupName"); val != nil { + group = val.(string) } - ctx.Checker.Check(root) - - root.NeedTypesInfo() + version := root.Name + if val := pkgMarkers.Get("versionName"); val != nil { + version = val.(string) + } byType := make(map[string][]byte) - imports := &importsList{ - byPath: make(map[string]string), - byAlias: make(map[string]string), - pkg: root, + imports := util.NewImportsList(root) + + enabled, _ := enabledOnPackage(ctx.Collector, root) + if !enabled { + return nil } - // avoid confusing aliases by "reserving" the root package's name as an alias - imports.byAlias[root.Name] = "" if err := markers.EachType(ctx.Collector, root, func(info *markers.TypeInfo) { outContent := new(bytes.Buffer) - if !enabledOnType(allTypes, info) { - //root.AddError(fmt.Errorf("skipping type: %v", info.Name)) // TODO(jpbetz): Remove - return + if t, ok := universe.typeMetadata[root.TypesInfo.TypeOf(info.RawSpec.Name)]; ok { + if !t.used { + return + } } - // not all types required a generate apply configuration. For example, no apply configuration - // type is needed for Quantity, IntOrString, RawExtension or Unknown. if !shouldBeApplyConfiguration(root, info) { - //root.AddError(fmt.Errorf("skipping type: %v", info.Name)) // TODO(jpbetz): Remove return } copyCtx := &applyConfigurationMaker{ pkg: root, - importsList: imports, + ImportsList: imports, codeWriter: &codeWriter{out: outContent}, } - copyCtx.GenerateTypesFor(root, info) + copyCtx.GenerateTypesFor(universe, root, info) + for _, field := range info.Fields { + if field.Name != "" { + switch root.TypesInfo.TypeOf(field.RawField.Type).(type) { + case *types.Slice: + copyCtx.GenerateMemberSetForSlice(universe, field, root, info) + case *types.Map: + copyCtx.GenerateMemberSetForMap(universe, field, root, info) + default: + copyCtx.GenerateMemberSet(universe, field, root, info) + } + } + } + + if enabledOnType(info) { + copyCtx.GenerateRootStructConstructor(root, info, isCRDClusterScope(info), group, version) + } else { + copyCtx.GenerateStructConstructor(root, info) + } + + if enabledOnType(info) { + copyCtx.GenerateRootFunctions(universe, root, info) + } outBytes := outContent.Bytes() if len(outBytes) > 0 { @@ -219,7 +411,7 @@ func (ctx *ObjectGenCtx) generateForPackage(root *loader.Package) []byte { } outContent := new(bytes.Buffer) - writeHeader(root, outContent, root.Name, imports, ctx.HeaderText) + util.WriteHeader(root, outContent, root.Name, imports, ctx.HeaderText) writeTypes(root, outContent, byType) outBytes := outContent.Bytes() @@ -253,7 +445,7 @@ func writeTypes(pkg *loader.Package, out io.Writer, byType map[string][]byte) { // writeFormatted outputs the given code, after gofmt-ing it. If we couldn't gofmt, // we write the unformatted code for debugging purposes. func writeOut(ctx *genall.GenerationContext, root *loader.Package, outBytes []byte) { - outputFile, err := ctx.Open(root, "zz_generated.applyconfigurations.go") + outputFile, err := ctx.Open(root, packageFileName) if err != nil { root.AddError(err) return diff --git a/pkg/applyconfigurations/jsontagutil.go b/pkg/applyconfigurations/jsontagutil.go index 01df470b2..fa37243aa 100644 --- a/pkg/applyconfigurations/jsontagutil.go +++ b/pkg/applyconfigurations/jsontagutil.go @@ -22,8 +22,7 @@ import ( "sigs.k8s.io/controller-tools/pkg/markers" ) -// TODO: This implements the same functionality as https://github.com/kubernetes/kubernetes/blob/master/staging/src/k8s.io/apimachinery/pkg/runtime/converter.go#L236 -// but is based on the more efficient approach from https://golang.org/src/encoding/json/encode.go +// TODO(jefftree): Import from k8s.io/code-generator/cmd/applyconfiguration-gen/generators/jsontagutil.go instead. type jsonTags struct { name string @@ -46,7 +45,7 @@ func (t jsonTags) String() string { return tag } -func lookupJsonTags(field markers.FieldInfo) (jsonTags, bool) { +func lookupJSONTags(field markers.FieldInfo) (jsonTags, bool) { tag := field.Tag.Get("json") if tag == "" || tag == "-" { return jsonTags{}, false diff --git a/pkg/applyconfigurations/testdata/cronjob/ac/zz_generated.applyconfigurations.go b/pkg/applyconfigurations/testdata/cronjob/ac/zz_generated.applyconfigurations.go new file mode 100644 index 000000000..1d6133a79 --- /dev/null +++ b/pkg/applyconfigurations/testdata/cronjob/ac/zz_generated.applyconfigurations.go @@ -0,0 +1,331 @@ +//go:build !ignore_autogenerated +// +build !ignore_autogenerated + +/* +Copyright 2022 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. +*/ + +// Code generated by controller-gen. DO NOT EDIT. + +package testdata + +import ( + metav1 "k8s.io/apimachinery/pkg/apis/meta/v1" + v1beta1 "k8s.io/client-go/applyconfigurations/batch/v1beta1" + v1 "k8s.io/client-go/applyconfigurations/core/v1" + applyconfigurationsmetav1 "k8s.io/client-go/applyconfigurations/meta/v1" + cronjob "sigs.k8s.io/controller-tools/pkg/applyconfigurations/testdata/cronjob" +) + +// AssociativeTypeApplyConfiguration represents a declarative configuration of the AssociativeType type for use +// with apply. +type AssociativeTypeApplyConfiguration struct { + Name *string `json:"name,omitempty"` + Secondary *int `json:"secondary,omitempty"` + Foo *string `json:"foo,omitempty"` +} + +// WithName sets the Name field in the declarative configuration to the given value +func (b *AssociativeTypeApplyConfiguration) WithName(value string) *AssociativeTypeApplyConfiguration { + b.Name = &value + return b +} + +// WithSecondary sets the Secondary field in the declarative configuration to the given value +func (b *AssociativeTypeApplyConfiguration) WithSecondary(value int) *AssociativeTypeApplyConfiguration { + b.Secondary = &value + return b +} + +// WithFoo sets the Foo field in the declarative configuration to the given value +func (b *AssociativeTypeApplyConfiguration) WithFoo(value string) *AssociativeTypeApplyConfiguration { + b.Foo = &value + return b +} + +// AssociativeTypeApplyConfiguration represents a declarative configuration of the AssociativeType type for use +// with apply. +func AssociativeType() *AssociativeTypeApplyConfiguration { + return &AssociativeTypeApplyConfiguration{} +} + +// CronJobApplyConfiguration represents a declarative configuration of the CronJob type for use +// with apply. +type CronJobApplyConfiguration struct { + applyconfigurationsmetav1.TypeMetaApplyConfiguration `json:",inline"` + *applyconfigurationsmetav1.ObjectMetaApplyConfiguration `json:"metadata,omitempty"` + Spec *CronJobSpecApplyConfiguration `json:"spec,omitempty"` + Status *CronJobStatusApplyConfiguration `json:"status,omitempty"` +} + +// WithSpec sets the Spec field in the declarative configuration to the given value +func (b *CronJobApplyConfiguration) WithSpec(value *CronJobSpecApplyConfiguration) *CronJobApplyConfiguration { + b.Spec = value + return b +} + +// WithStatus sets the Status field in the declarative configuration to the given value +func (b *CronJobApplyConfiguration) WithStatus(value *CronJobStatusApplyConfiguration) *CronJobApplyConfiguration { + b.Status = value + return b +} + +// CronJobApplyConfiguration represents a declarative configuration of the CronJob type for use +// with apply. +func CronJob(name, namespace string) *CronJobApplyConfiguration { + ac := &CronJobApplyConfiguration{} + ac.WithName(name) + ac.WithNamespace(namespace) + ac.WithKind("CronJob") + ac.WithAPIVersion("testdata.kubebuilder.io/v1") + return ac +} +func (ac *CronJobApplyConfiguration) WithKind(value string) *CronJobApplyConfiguration { + ac.Kind = &value + return ac +} +func (ac *CronJobApplyConfiguration) WithAPIVersion(value string) *CronJobApplyConfiguration { + ac.APIVersion = &value + return ac +} +func (ac *CronJobApplyConfiguration) ensureObjectMetaApplyConfigurationExists() { + if ac.ObjectMetaApplyConfiguration == nil { + ac.ObjectMetaApplyConfiguration = &applyconfigurationsmetav1.ObjectMetaApplyConfiguration{} + } +} +func (ac *CronJobApplyConfiguration) WithName(value string) *CronJobApplyConfiguration { + ac.ensureObjectMetaApplyConfigurationExists() + ac.Name = &value + return ac +} +func (ac *CronJobApplyConfiguration) WithNamespace(value string) *CronJobApplyConfiguration { + ac.Namespace = &value + return ac +} +func (ac *CronJobApplyConfiguration) GetName() string { + ac.ensureObjectMetaApplyConfigurationExists() + return *ac.Name +} +func (ac *CronJobApplyConfiguration) GetNamespace() string { + ac.ensureObjectMetaApplyConfigurationExists() + return *ac.Namespace +} + +// CronJobSpecApplyConfiguration represents a declarative configuration of the CronJobSpec type for use +// with apply. +type CronJobSpecApplyConfiguration struct { + Schedule *string `json:"schedule,omitempty"` + StartingDeadlineSeconds *int64 `json:"startingDeadlineSeconds,omitempty"` + ConcurrencyPolicy *cronjob.ConcurrencyPolicy `json:"concurrencyPolicy,omitempty"` + Suspend *bool `json:"suspend,omitempty"` + BinaryName []byte `json:"binaryName,omitempty"` + CanBeNull *string `json:"canBeNull,omitempty"` + JobTemplate *v1beta1.JobTemplateSpecApplyConfiguration `json:"jobTemplate,omitempty"` + SuccessfulJobsHistoryLimit *int32 `json:"successfulJobsHistoryLimit,omitempty"` + FailedJobsHistoryLimit *int32 `json:"failedJobsHistoryLimit,omitempty"` + StringSliceData map[string][]string `json:"stringSliceData,omitempty"` + PtrData map[string]*string `json:"ptrData,omitempty"` + Slice []string `json:"slice,omitempty"` + SlicePtr []*string `json:"slicePtr,omitempty"` + SliceStruct []*ExampleStructApplyConfiguration `json:"sliceStruct,omitempty"` + BuiltInReference *v1.PodSpecApplyConfiguration `json:"builtInReference,omitempty"` + Int *int `json:"int,omitempty"` + AssociativeList []AssociativeTypeApplyConfiguration `json:"associativeList,omitempty"` +} + +// WithSchedule sets the Schedule field in the declarative configuration to the given value +func (b *CronJobSpecApplyConfiguration) WithSchedule(value string) *CronJobSpecApplyConfiguration { + b.Schedule = &value + return b +} + +// WithStartingDeadlineSeconds sets the StartingDeadlineSeconds field in the declarative configuration to the given value +func (b *CronJobSpecApplyConfiguration) WithStartingDeadlineSeconds(value *int64) *CronJobSpecApplyConfiguration { + b.StartingDeadlineSeconds = value + return b +} + +// WithConcurrencyPolicy sets the ConcurrencyPolicy field in the declarative configuration to the given value +func (b *CronJobSpecApplyConfiguration) WithConcurrencyPolicy(value cronjob.ConcurrencyPolicy) *CronJobSpecApplyConfiguration { + b.ConcurrencyPolicy = &value + return b +} + +// WithSuspend sets the Suspend field in the declarative configuration to the given value +func (b *CronJobSpecApplyConfiguration) WithSuspend(value *bool) *CronJobSpecApplyConfiguration { + b.Suspend = value + return b +} + +// WithInternalData sets the InternalData field in the declarative configuration to the given value +func (b *CronJobSpecApplyConfiguration) WithInternalData(value string) *CronJobSpecApplyConfiguration { + return b +} + +// WithBinaryName sets the BinaryName field in the declarative configuration to the given value +func (b *CronJobSpecApplyConfiguration) WithBinaryName(values ...byte) *CronJobSpecApplyConfiguration { + for i := range values { + b.BinaryName = append(b.BinaryName, values[i]) + } + return b +} + +// WithCanBeNull sets the CanBeNull field in the declarative configuration to the given value +func (b *CronJobSpecApplyConfiguration) WithCanBeNull(value string) *CronJobSpecApplyConfiguration { + b.CanBeNull = &value + return b +} + +// WithJobTemplate sets the JobTemplate field in the declarative configuration to the given value +func (b *CronJobSpecApplyConfiguration) WithJobTemplate(value *v1beta1.JobTemplateSpecApplyConfiguration) *CronJobSpecApplyConfiguration { + b.JobTemplate = value + return b +} + +// WithSuccessfulJobsHistoryLimit sets the SuccessfulJobsHistoryLimit field in the declarative configuration to the given value +func (b *CronJobSpecApplyConfiguration) WithSuccessfulJobsHistoryLimit(value *int32) *CronJobSpecApplyConfiguration { + b.SuccessfulJobsHistoryLimit = value + return b +} + +// WithFailedJobsHistoryLimit sets the FailedJobsHistoryLimit field in the declarative configuration to the given value +func (b *CronJobSpecApplyConfiguration) WithFailedJobsHistoryLimit(value *int32) *CronJobSpecApplyConfiguration { + b.FailedJobsHistoryLimit = value + return b +} + +// WithStringSliceData sets the StringSliceData field in the declarative configuration to the given value +func (b *CronJobSpecApplyConfiguration) WithStringSliceData(entries map[string][]string) *CronJobSpecApplyConfiguration { + if b.StringSliceData == nil && len(entries) > 0 { + b.StringSliceData = make(map[string][]string, len(entries)) + } + for k, v := range entries { + b.StringSliceData[k] = v + } + return b +} + +// WithPtrData sets the PtrData field in the declarative configuration to the given value +func (b *CronJobSpecApplyConfiguration) WithPtrData(entries map[string]*string) *CronJobSpecApplyConfiguration { + if b.PtrData == nil && len(entries) > 0 { + b.PtrData = make(map[string]*string, len(entries)) + } + for k, v := range entries { + b.PtrData[k] = v + } + return b +} + +// WithSlice sets the Slice field in the declarative configuration to the given value +func (b *CronJobSpecApplyConfiguration) WithSlice(values ...string) *CronJobSpecApplyConfiguration { + for i := range values { + b.Slice = append(b.Slice, values[i]) + } + return b +} + +// WithSlicePtr sets the SlicePtr field in the declarative configuration to the given value +func (b *CronJobSpecApplyConfiguration) WithSlicePtr(values ...*string) *CronJobSpecApplyConfiguration { + for i := range values { + b.SlicePtr = append(b.SlicePtr, values[i]) + } + return b +} + +// WithSliceStruct sets the SliceStruct field in the declarative configuration to the given value +func (b *CronJobSpecApplyConfiguration) WithSliceStruct(values ...*ExampleStructApplyConfiguration) *CronJobSpecApplyConfiguration { + for i := range values { + b.SliceStruct = append(b.SliceStruct, values[i]) + } + return b +} + +// WithBuiltInReference sets the BuiltInReference field in the declarative configuration to the given value +func (b *CronJobSpecApplyConfiguration) WithBuiltInReference(value *v1.PodSpecApplyConfiguration) *CronJobSpecApplyConfiguration { + b.BuiltInReference = value + return b +} + +// WithInt sets the Int field in the declarative configuration to the given value +func (b *CronJobSpecApplyConfiguration) WithInt(value int) *CronJobSpecApplyConfiguration { + b.Int = &value + return b +} + +// WithAssociativeList sets the AssociativeList field in the declarative configuration to the given value +func (b *CronJobSpecApplyConfiguration) WithAssociativeList(values ...AssociativeTypeApplyConfiguration) *CronJobSpecApplyConfiguration { + for i := range values { + b.AssociativeList = append(b.AssociativeList, values[i]) + } + return b +} + +// CronJobSpecApplyConfiguration represents a declarative configuration of the CronJobSpec type for use +// with apply. +func CronJobSpec() *CronJobSpecApplyConfiguration { + return &CronJobSpecApplyConfiguration{} +} + +// CronJobStatusApplyConfiguration represents a declarative configuration of the CronJobStatus type for use +// with apply. +type CronJobStatusApplyConfiguration struct { + Active []v1.ObjectReferenceApplyConfiguration `json:"active,omitempty"` + LastScheduleTime *metav1.Time `json:"lastScheduleTime,omitempty"` + LastScheduleMicroTime *metav1.MicroTime `json:"lastScheduleMicroTime,omitempty"` +} + +// WithActive sets the Active field in the declarative configuration to the given value +func (b *CronJobStatusApplyConfiguration) WithActive(values ...v1.ObjectReferenceApplyConfiguration) *CronJobStatusApplyConfiguration { + for i := range values { + b.Active = append(b.Active, values[i]) + } + return b +} + +// WithLastScheduleTime sets the LastScheduleTime field in the declarative configuration to the given value +func (b *CronJobStatusApplyConfiguration) WithLastScheduleTime(value *metav1.Time) *CronJobStatusApplyConfiguration { + b.LastScheduleTime = value + return b +} + +// WithLastScheduleMicroTime sets the LastScheduleMicroTime field in the declarative configuration to the given value +func (b *CronJobStatusApplyConfiguration) WithLastScheduleMicroTime(value *metav1.MicroTime) *CronJobStatusApplyConfiguration { + b.LastScheduleMicroTime = value + return b +} + +// CronJobStatusApplyConfiguration represents a declarative configuration of the CronJobStatus type for use +// with apply. +func CronJobStatus() *CronJobStatusApplyConfiguration { + return &CronJobStatusApplyConfiguration{} +} + +// ExampleStructApplyConfiguration represents a declarative configuration of the ExampleStruct type for use +// with apply. +type ExampleStructApplyConfiguration struct { + ExampleField *string `json:"string,omitempty"` +} + +// WithExampleField sets the ExampleField field in the declarative configuration to the given value +func (b *ExampleStructApplyConfiguration) WithExampleField(value string) *ExampleStructApplyConfiguration { + b.ExampleField = &value + return b +} + +// ExampleStructApplyConfiguration represents a declarative configuration of the ExampleStruct type for use +// with apply. +func ExampleStruct() *ExampleStructApplyConfiguration { + return &ExampleStructApplyConfiguration{} +} diff --git a/pkg/applyconfigurations/testdata/cronjob/cronjob_types.go b/pkg/applyconfigurations/testdata/cronjob/cronjob_types.go new file mode 100644 index 000000000..9fca5df08 --- /dev/null +++ b/pkg/applyconfigurations/testdata/cronjob/cronjob_types.go @@ -0,0 +1,182 @@ +/* + +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. +*/ + +//go:generate ../../../.run-controller-gen.sh paths=. output:dir=. + +package testdata + +import ( + "encoding/json" + "fmt" + + batchv1beta1 "k8s.io/api/batch/v1beta1" + corev1 "k8s.io/api/core/v1" + metav1 "k8s.io/apimachinery/pkg/apis/meta/v1" + "k8s.io/apimachinery/pkg/runtime" +) + +// EDIT THIS FILE! THIS IS SCAFFOLDING FOR YOU TO OWN! +// NOTE: json tags are required. Any new fields you add must have json tags for the fields to be serialized. + +// CronJobSpec defines the desired state of CronJob +type CronJobSpec struct { + // The schedule in Cron format, see https://en.wikipedia.org/wiki/Cron. + Schedule string `json:"schedule"` + + // Optional deadline in seconds for starting the job if it misses scheduled + // time for any reason. Missed jobs executions will be counted as failed ones. + // +optional + StartingDeadlineSeconds *int64 `json:"startingDeadlineSeconds,omitempty"` + + // Specifies how to treat concurrent executions of a Job. + // Valid values are: + // - "Allow" (default): allows CronJobs to run concurrently; + // - "Forbid": forbids concurrent runs, skipping next run if previous run hasn't finished yet; + // - "Replace": cancels currently running job and replaces it with a new one + // +optional + ConcurrencyPolicy ConcurrencyPolicy `json:"concurrencyPolicy,omitempty"` + + // This flag tells the controller to suspend subsequent executions, it does + // not apply to already started executions. Defaults to false. + // +optional + Suspend *bool `json:"suspend,omitempty"` + + // This tests that non-serialized fields aren't included in the schema. + InternalData string `json:"-"` + + // This tests byte slice schema generation. + BinaryName []byte `json:"binaryName"` + + // This tests that nullable works correctly + // +nullable + CanBeNull string `json:"canBeNull"` + + // Specifies the job that will be created when executing a CronJob. + JobTemplate batchv1beta1.JobTemplateSpec `json:"jobTemplate"` + + // The number of successful finished jobs to retain. + // This is a pointer to distinguish between explicit zero and not specified. + // +optional + SuccessfulJobsHistoryLimit *int32 `json:"successfulJobsHistoryLimit,omitempty"` + + // The number of failed finished jobs to retain. + // This is a pointer to distinguish between explicit zero and not specified. + // +optional + FailedJobsHistoryLimit *int32 `json:"failedJobsHistoryLimit,omitempty"` + + // This tests string slices are allowed as map values. + StringSliceData map[string][]string `json:"stringSliceData,omitempty"` + + // This tests pointers are allowed as map values. + PtrData map[string]*string `json:"ptrData,omitempty"` + + // This tests that slice has the proper ac generation + // +kubebuilder:default={a,b} + Slice []string `json:"slice"` + + // This tests that slice with pointers has the proper ac generation + // +kubebuilder:default={a,b} + SlicePtr []*string `json:"slicePtr"` + + // This tests that slice with structs has the proper ac generation + // +kubebuilder:default={a,b} + SliceStruct []*ExampleStruct `json:"sliceStruct"` + + // This tests that references to built in types have the proper ac generation + // Built in types are generated under "k8s.io/client-go/applyconfigurations" + BuiltInReference *corev1.PodSpec `json:"builtInReference"` + + // This tests that non-pointer ints have the proper ac generation + Int int `json:"int"` + + // This tests that associative lists work. + // +listType=map + // +listMapKey=name + // +listMapKey=secondary + AssociativeList []AssociativeType `json:"associativeList"` +} + +type AssociativeType struct { + Name string `json:"name"` + Secondary int `json:"secondary"` + Foo string `json:"foo"` +} + +// ConcurrencyPolicy describes how the job will be handled. +// Only one of the following concurrent policies may be specified. +// If none of the following policies is specified, the default one +// is AllowConcurrent. +// +kubebuilder:validation:Enum=Allow;Forbid;Replace +type ConcurrencyPolicy string + +const ( + // AllowConcurrent allows CronJobs to run concurrently. + AllowConcurrent ConcurrencyPolicy = "Allow" + + // ForbidConcurrent forbids concurrent runs, skipping next run if previous + // hasn't finished yet. + ForbidConcurrent ConcurrencyPolicy = "Forbid" + + // ReplaceConcurrent cancels currently running job and replaces it with a new one. + ReplaceConcurrent ConcurrencyPolicy = "Replace" +) + +// CronJobStatus defines the observed state of CronJob +type CronJobStatus struct { + // INSERT ADDITIONAL STATUS FIELD - define observed state of cluster + // Important: Run "make" to regenerate code after modifying this file + + // A list of pointers to currently running jobs. + // +optional + Active []corev1.ObjectReference `json:"active,omitempty"` + + // Information when was the last time the job was successfully scheduled. + // +optional + LastScheduleTime *metav1.Time `json:"lastScheduleTime,omitempty"` + + // Information about the last time the job was successfully scheduled, + // with microsecond precision. + // +optional + LastScheduleMicroTime *metav1.MicroTime `json:"lastScheduleMicroTime,omitempty"` +} + +type ExampleStruct struct { + ExampleField string `json:"string"` +} + +// +kubebuilder:subresource:status +// +kubebuilder:resource:singular=mycronjob + +// CronJob is the Schema for the cronjobs API +type CronJob struct { + /* + */ + metav1.TypeMeta `json:",inline"` + metav1.ObjectMeta `json:"metadata,omitempty"` + + Spec CronJobSpec `json:"spec,omitempty"` + Status CronJobStatus `json:"status,omitempty"` +} + +// CronJobList contains a list of CronJob +type CronJobList struct { + metav1.TypeMeta `json:",inline"` + metav1.ListMeta `json:"metadata,omitempty"` + Items []CronJob `json:"items"` +} + +func init() { + SchemeBuilder.Register(&CronJob{}, &CronJobList{}) +} diff --git a/pkg/applyconfigurations/testdata/cronjob/go.mod b/pkg/applyconfigurations/testdata/cronjob/go.mod new file mode 100644 index 000000000..cc2c17945 --- /dev/null +++ b/pkg/applyconfigurations/testdata/cronjob/go.mod @@ -0,0 +1,51 @@ +module sigs.k8s.io/controller-tools/pkg/applyconfigurations/testdata/cronjob + +go 1.19 + +require ( + k8s.io/api v0.25.0 + k8s.io/apimachinery v0.25.0 + k8s.io/client-go v0.25.0 +) + +require ( + github.com/PuerkitoBio/purell v1.1.1 // indirect + github.com/PuerkitoBio/urlesc v0.0.0-20170810143723-de5bf2ad4578 // indirect + github.com/davecgh/go-spew v1.1.1 // indirect + github.com/emicklei/go-restful/v3 v3.8.0 // indirect + github.com/go-logr/logr v1.2.3 // indirect + github.com/go-openapi/jsonpointer v0.19.5 // indirect + github.com/go-openapi/jsonreference v0.19.5 // indirect + github.com/go-openapi/swag v0.19.15 // indirect + github.com/gogo/protobuf v1.3.2 // indirect + github.com/golang/protobuf v1.5.2 // indirect + github.com/google/gnostic v0.5.7-v3refs // indirect + github.com/google/go-cmp v0.5.9 // indirect + github.com/google/gofuzz v1.2.0 // indirect + github.com/josharian/intern v1.0.0 // indirect + github.com/json-iterator/go v1.1.12 // indirect + github.com/mailru/easyjson v0.7.7 // indirect + github.com/modern-go/concurrent v0.0.0-20180306012644-bacd9c7ef1dd // indirect + github.com/modern-go/reflect2 v1.0.2 // indirect + github.com/munnerz/goautoneg v0.0.0-20191010083416-a7dc8b61c822 // indirect + github.com/stretchr/testify v1.8.0 // indirect + golang.org/x/net v0.0.0-20220722155237-a158d28d115b // indirect + golang.org/x/oauth2 v0.0.0-20220411215720-9780585627b5 // indirect + golang.org/x/sys v0.0.0-20220722155257-8c9f86f7a55f // indirect + golang.org/x/term v0.0.0-20210927222741-03fcf44c2211 // indirect + golang.org/x/text v0.3.7 // indirect + golang.org/x/time v0.0.0-20220224211638-0e9765cccd65 // indirect + google.golang.org/appengine v1.6.7 // indirect + google.golang.org/protobuf v1.28.0 // indirect + gopkg.in/inf.v0 v0.9.1 // indirect + gopkg.in/yaml.v2 v2.4.0 // indirect + gopkg.in/yaml.v3 v3.0.1 // indirect + k8s.io/klog/v2 v2.70.2-0.20220707122935-0990e81f1a8f // indirect + k8s.io/kube-openapi v0.0.0-20220803162953-67bda5d908f1 // indirect + k8s.io/utils v0.0.0-20220728103510-ee6ede2d64ed // indirect + sigs.k8s.io/json v0.0.0-20220713155537-f223a00ba0e2 // indirect + sigs.k8s.io/structured-merge-diff/v4 v4.2.3 // indirect + sigs.k8s.io/yaml v1.3.0 // indirect +) + +replace sigs.k8s.io/controller-tools => ../../../../ diff --git a/pkg/applyconfigurations/testdata/cronjob/go.sum b/pkg/applyconfigurations/testdata/cronjob/go.sum new file mode 100644 index 000000000..2251073c2 --- /dev/null +++ b/pkg/applyconfigurations/testdata/cronjob/go.sum @@ -0,0 +1,484 @@ +cloud.google.com/go v0.26.0/go.mod h1:aQUYkXzVsufM+DwF1aE+0xfcU+56JwCaLick0ClmMTw= +cloud.google.com/go v0.34.0/go.mod h1:aQUYkXzVsufM+DwF1aE+0xfcU+56JwCaLick0ClmMTw= +cloud.google.com/go v0.38.0/go.mod h1:990N+gfupTy94rShfmMCWGDn0LpTmnzTp2qbd1dvSRU= +cloud.google.com/go v0.44.1/go.mod h1:iSa0KzasP4Uvy3f1mN/7PiObzGgflwredwwASm/v6AU= +cloud.google.com/go v0.44.2/go.mod h1:60680Gw3Yr4ikxnPRS/oxxkBccT6SA1yMk63TGekxKY= +cloud.google.com/go v0.45.1/go.mod h1:RpBamKRgapWJb87xiFSdk4g1CME7QZg3uwTez+TSTjc= +cloud.google.com/go v0.46.3/go.mod h1:a6bKKbmY7er1mI7TEI4lsAkts/mkhTSZK8w33B4RAg0= +cloud.google.com/go v0.50.0/go.mod h1:r9sluTvynVuxRIOHXQEHMFffphuXHOMZMycpNR5e6To= +cloud.google.com/go v0.52.0/go.mod h1:pXajvRH/6o3+F9jDHZWQ5PbGhn+o8w9qiu/CffaVdO4= +cloud.google.com/go v0.53.0/go.mod h1:fp/UouUEsRkN6ryDKNW/Upv/JBKnv6WDthjR6+vze6M= +cloud.google.com/go v0.54.0/go.mod h1:1rq2OEkV3YMf6n/9ZvGWI3GWw0VoqH/1x2nd8Is/bPc= +cloud.google.com/go v0.56.0/go.mod h1:jr7tqZxxKOVYizybht9+26Z/gUq7tiRzu+ACVAMbKVk= +cloud.google.com/go v0.57.0/go.mod h1:oXiQ6Rzq3RAkkY7N6t3TcE6jE+CIBBbA36lwQ1JyzZs= +cloud.google.com/go v0.62.0/go.mod h1:jmCYTdRCQuc1PHIIJ/maLInMho30T/Y0M4hTdTShOYc= +cloud.google.com/go v0.65.0/go.mod h1:O5N8zS7uWy9vkA9vayVHs65eM1ubvY4h553ofrNHObY= +cloud.google.com/go/bigquery v1.0.1/go.mod h1:i/xbL2UlR5RvWAURpBYZTtm/cXjCha9lbfbpx4poX+o= +cloud.google.com/go/bigquery v1.3.0/go.mod h1:PjpwJnslEMmckchkHFfq+HTD2DmtT67aNFKH1/VBDHE= +cloud.google.com/go/bigquery v1.4.0/go.mod h1:S8dzgnTigyfTmLBfrtrhyYhwRxG72rYxvftPBK2Dvzc= +cloud.google.com/go/bigquery v1.5.0/go.mod h1:snEHRnqQbz117VIFhE8bmtwIDY80NLUZUMb4Nv6dBIg= +cloud.google.com/go/bigquery v1.7.0/go.mod h1://okPTzCYNXSlb24MZs83e2Do+h+VXtc4gLoIoXIAPc= +cloud.google.com/go/bigquery v1.8.0/go.mod h1:J5hqkt3O0uAFnINi6JXValWIb1v0goeZM77hZzJN/fQ= +cloud.google.com/go/datastore v1.0.0/go.mod h1:LXYbyblFSglQ5pkeyhO+Qmw7ukd3C+pD7TKLgZqpHYE= +cloud.google.com/go/datastore v1.1.0/go.mod h1:umbIZjpQpHh4hmRpGhH4tLFup+FVzqBi1b3c64qFpCk= +cloud.google.com/go/pubsub v1.0.1/go.mod h1:R0Gpsv3s54REJCy4fxDixWD93lHJMoZTyQ2kNxGRt3I= +cloud.google.com/go/pubsub v1.1.0/go.mod h1:EwwdRX2sKPjnvnqCa270oGRyludottCI76h+R3AArQw= +cloud.google.com/go/pubsub v1.2.0/go.mod h1:jhfEVHT8odbXTkndysNHCcx0awwzvfOlguIAii9o8iA= +cloud.google.com/go/pubsub v1.3.1/go.mod h1:i+ucay31+CNRpDW4Lu78I4xXG+O1r/MAHgjpRVR+TSU= +cloud.google.com/go/storage v1.0.0/go.mod h1:IhtSnM/ZTZV8YYJWCY8RULGVqBDmpoyjwiyrjsg+URw= +cloud.google.com/go/storage v1.5.0/go.mod h1:tpKbwo567HUNpVclU5sGELwQWBDZ8gh0ZeosJ0Rtdos= +cloud.google.com/go/storage v1.6.0/go.mod h1:N7U0C8pVQ/+NIKOBQyamJIeKQKkZ+mxpohlUTyfDhBk= +cloud.google.com/go/storage v1.8.0/go.mod h1:Wv1Oy7z6Yz3DshWRJFhqM/UCfaWIRTdp0RXyy7KQOVs= +cloud.google.com/go/storage v1.10.0/go.mod h1:FLPqc6j+Ki4BU591ie1oL6qBQGu2Bl/tZ9ullr3+Kg0= +dmitri.shuralyov.com/gpu/mtl v0.0.0-20190408044501-666a987793e9/go.mod h1:H6x//7gZCb22OMCxBHrMx7a5I7Hp++hsVxbQ4BYO7hU= +github.com/BurntSushi/toml v0.3.1/go.mod h1:xHWCNGjB5oqiDr8zfno3MHue2Ht5sIBksp03qcyfWMU= +github.com/BurntSushi/xgb v0.0.0-20160522181843-27f122750802/go.mod h1:IVnqGOEym/WlBOVXweHU+Q+/VP0lqqI8lqeDx9IjBqo= +github.com/PuerkitoBio/purell v1.1.1 h1:WEQqlqaGbrPkxLJWfBwQmfEAE1Z7ONdDLqrN38tNFfI= +github.com/PuerkitoBio/purell v1.1.1/go.mod h1:c11w/QuzBsJSee3cPx9rAFu61PvFxuPbtSwDGJws/X0= +github.com/PuerkitoBio/urlesc v0.0.0-20170810143723-de5bf2ad4578 h1:d+Bc7a5rLufV/sSk/8dngufqelfh6jnri85riMAaF/M= +github.com/PuerkitoBio/urlesc v0.0.0-20170810143723-de5bf2ad4578/go.mod h1:uGdkoq3SwY9Y+13GIhn11/XLaGBb4BfwItxLd5jeuXE= +github.com/census-instrumentation/opencensus-proto v0.2.1/go.mod h1:f6KPmirojxKA12rnyqOA5BBL4O983OfeGPqjHWSTneU= +github.com/chzyer/logex v1.1.10/go.mod h1:+Ywpsq7O8HXn0nuIou7OrIPyXbp3wmkHB+jjWRnGsAI= +github.com/chzyer/readline v0.0.0-20180603132655-2972be24d48e/go.mod h1:nSuG5e5PlCu98SY8svDHJxuZscDgtXS6KTTbou5AhLI= +github.com/chzyer/test v0.0.0-20180213035817-a1ea475d72b1/go.mod h1:Q3SI9o4m/ZMnBNeIyt5eFwwo7qiLfzFZmjNmxjkiQlU= +github.com/client9/misspell v0.3.4/go.mod h1:qj6jICC3Q7zFZvVWo7KLAzC3yx5G7kyvSDkc90ppPyw= +github.com/cncf/udpa/go v0.0.0-20191209042840-269d4d468f6f/go.mod h1:M8M6+tZqaGXZJjfX53e64911xZQV5JYwmTeXPW+k8Sc= +github.com/creack/pty v1.1.9/go.mod h1:oKZEueFk5CKHvIhNR5MUki03XCEU+Q6VDXinZuGJ33E= +github.com/davecgh/go-spew v1.1.0/go.mod h1:J7Y8YcW2NihsgmVo/mv3lAwl/skON4iLHjSsI+c5H38= +github.com/davecgh/go-spew v1.1.1 h1:vj9j/u1bqnvCEfJOwUhtlOARqs3+rkHYY13jYWTU97c= +github.com/davecgh/go-spew v1.1.1/go.mod h1:J7Y8YcW2NihsgmVo/mv3lAwl/skON4iLHjSsI+c5H38= +github.com/docopt/docopt-go v0.0.0-20180111231733-ee0de3bc6815/go.mod h1:WwZ+bS3ebgob9U8Nd0kOddGdZWjyMGR8Wziv+TBNwSE= +github.com/emicklei/go-restful/v3 v3.8.0 h1:eCZ8ulSerjdAiaNpF7GxXIE7ZCMo1moN1qX+S609eVw= +github.com/emicklei/go-restful/v3 v3.8.0/go.mod h1:6n3XBCmQQb25CM2LCACGz8ukIrRry+4bhvbpWn3mrbc= +github.com/envoyproxy/go-control-plane v0.9.0/go.mod h1:YTl/9mNaCwkRvm6d1a2C3ymFceY/DCBVvsKhRF0iEA4= +github.com/envoyproxy/go-control-plane v0.9.1-0.20191026205805-5f8ba28d4473/go.mod h1:YTl/9mNaCwkRvm6d1a2C3ymFceY/DCBVvsKhRF0iEA4= +github.com/envoyproxy/go-control-plane v0.9.4/go.mod h1:6rpuAdCZL397s3pYoYcLgu1mIlRU8Am5FuJP05cCM98= +github.com/envoyproxy/protoc-gen-validate v0.1.0/go.mod h1:iSmxcyjqTsJpI2R4NaDN7+kN2VEUnK/pcBlmesArF7c= +github.com/go-gl/glfw v0.0.0-20190409004039-e6da0acd62b1/go.mod h1:vR7hzQXu2zJy9AVAgeJqvqgH9Q5CA+iKCZ2gyEVpxRU= +github.com/go-gl/glfw/v3.3/glfw v0.0.0-20191125211704-12ad95a8df72/go.mod h1:tQ2UAYgL5IevRw8kRxooKSPJfGvJ9fJQFa0TUsXzTg8= +github.com/go-gl/glfw/v3.3/glfw v0.0.0-20200222043503-6f7a984d4dc4/go.mod h1:tQ2UAYgL5IevRw8kRxooKSPJfGvJ9fJQFa0TUsXzTg8= +github.com/go-logr/logr v0.1.0/go.mod h1:ixOQHD9gLJUVQQ2ZOR7zLEifBX6tGkNJF4QyIY7sIas= +github.com/go-logr/logr v1.2.0/go.mod h1:jdQByPbusPIv2/zmleS9BjJVeZ6kBagPoEUsqbVz/1A= +github.com/go-logr/logr v1.2.3 h1:2DntVwHkVopvECVRSlL5PSo9eG+cAkDCuckLubN+rq0= +github.com/go-logr/logr v1.2.3/go.mod h1:jdQByPbusPIv2/zmleS9BjJVeZ6kBagPoEUsqbVz/1A= +github.com/go-openapi/jsonpointer v0.19.3/go.mod h1:Pl9vOtqEWErmShwVjC8pYs9cog34VGT37dQOVbmoatg= +github.com/go-openapi/jsonpointer v0.19.5 h1:gZr+CIYByUqjcgeLXnQu2gHYQC9o73G2XUeOFYEICuY= +github.com/go-openapi/jsonpointer v0.19.5/go.mod h1:Pl9vOtqEWErmShwVjC8pYs9cog34VGT37dQOVbmoatg= +github.com/go-openapi/jsonreference v0.19.5 h1:1WJP/wi4OjB4iV8KVbH73rQaoialJrqv8gitZLxGLtM= +github.com/go-openapi/jsonreference v0.19.5/go.mod h1:RdybgQwPxbL4UEjuAruzK1x3nE69AqPYEJeo/TWfEeg= +github.com/go-openapi/swag v0.19.5/go.mod h1:POnQmlKehdgb5mhVOsnJFsivZCEZ/vjK9gh66Z9tfKk= +github.com/go-openapi/swag v0.19.15 h1:D2NRCBzS9/pEY3gP9Nl8aDqGUcPFrwG2p+CNFrLyrCM= +github.com/go-openapi/swag v0.19.15/go.mod h1:QYRuS/SOXUCsnplDa677K7+DxSOj6IPNl/eQntq43wQ= +github.com/gogo/protobuf v1.3.2 h1:Ov1cvc58UF3b5XjBnZv7+opcTcQFZebYjWzi34vdm4Q= +github.com/gogo/protobuf v1.3.2/go.mod h1:P1XiOD3dCwIKUDQYPy72D8LYyHL2YPYrpS2s69NZV8Q= +github.com/golang/glog v0.0.0-20160126235308-23def4e6c14b/go.mod h1:SBH7ygxi8pfUlaOkMMuAQtPIUF8ecWP5IEl/CR7VP2Q= +github.com/golang/groupcache v0.0.0-20190702054246-869f871628b6/go.mod h1:cIg4eruTrX1D+g88fzRXU5OdNfaM+9IcxsU14FzY7Hc= +github.com/golang/groupcache v0.0.0-20191227052852-215e87163ea7/go.mod h1:cIg4eruTrX1D+g88fzRXU5OdNfaM+9IcxsU14FzY7Hc= +github.com/golang/groupcache v0.0.0-20200121045136-8c9f03a8e57e/go.mod h1:cIg4eruTrX1D+g88fzRXU5OdNfaM+9IcxsU14FzY7Hc= +github.com/golang/mock v1.1.1/go.mod h1:oTYuIxOrZwtPieC+H1uAHpcLFnEyAGVDL/k47Jfbm0A= +github.com/golang/mock v1.2.0/go.mod h1:oTYuIxOrZwtPieC+H1uAHpcLFnEyAGVDL/k47Jfbm0A= +github.com/golang/mock v1.3.1/go.mod h1:sBzyDLLjw3U8JLTeZvSv8jJB+tU5PVekmnlKIyFUx0Y= +github.com/golang/mock v1.4.0/go.mod h1:UOMv5ysSaYNkG+OFQykRIcU/QvvxJf3p21QfJ2Bt3cw= +github.com/golang/mock v1.4.1/go.mod h1:UOMv5ysSaYNkG+OFQykRIcU/QvvxJf3p21QfJ2Bt3cw= +github.com/golang/mock v1.4.3/go.mod h1:UOMv5ysSaYNkG+OFQykRIcU/QvvxJf3p21QfJ2Bt3cw= +github.com/golang/mock v1.4.4/go.mod h1:l3mdAwkq5BuhzHwde/uurv3sEJeZMXNpwsxVWU71h+4= +github.com/golang/protobuf v1.2.0/go.mod h1:6lQm79b+lXiMfvg/cZm0SGofjICqVBUtrP5yJMmIC1U= +github.com/golang/protobuf v1.3.1/go.mod h1:6lQm79b+lXiMfvg/cZm0SGofjICqVBUtrP5yJMmIC1U= +github.com/golang/protobuf v1.3.2/go.mod h1:6lQm79b+lXiMfvg/cZm0SGofjICqVBUtrP5yJMmIC1U= +github.com/golang/protobuf v1.3.3/go.mod h1:vzj43D7+SQXF/4pzW/hwtAqwc6iTitCiVSaWz5lYuqw= +github.com/golang/protobuf v1.3.4/go.mod h1:vzj43D7+SQXF/4pzW/hwtAqwc6iTitCiVSaWz5lYuqw= +github.com/golang/protobuf v1.3.5/go.mod h1:6O5/vntMXwX2lRkT1hjjk0nAC1IDOTvTlVgjlRvqsdk= +github.com/golang/protobuf v1.4.0-rc.1/go.mod h1:ceaxUfeHdC40wWswd/P6IGgMaK3YpKi5j83Wpe3EHw8= +github.com/golang/protobuf v1.4.0-rc.1.0.20200221234624-67d41d38c208/go.mod h1:xKAWHe0F5eneWXFV3EuXVDTCmh+JuBKY0li0aMyXATA= +github.com/golang/protobuf v1.4.0-rc.2/go.mod h1:LlEzMj4AhA7rCAGe4KMBDvJI+AwstrUpVNzEA03Pprs= +github.com/golang/protobuf v1.4.0-rc.4.0.20200313231945-b860323f09d0/go.mod h1:WU3c8KckQ9AFe+yFwt9sWVRKCVIyN9cPHBJSNnbL67w= +github.com/golang/protobuf v1.4.0/go.mod h1:jodUvKwWbYaEsadDk5Fwe5c77LiNKVO9IDvqG2KuDX0= +github.com/golang/protobuf v1.4.1/go.mod h1:U8fpvMrcmy5pZrNK1lt4xCsGvpyWQ/VVv6QDs8UjoX8= +github.com/golang/protobuf v1.4.2/go.mod h1:oDoupMAO8OvCJWAcko0GGGIgR6R6ocIYbsSw735rRwI= +github.com/golang/protobuf v1.5.0/go.mod h1:FsONVRAS9T7sI+LIUmWTfcYkHO4aIWwzhcaSAoJOfIk= +github.com/golang/protobuf v1.5.2 h1:ROPKBNFfQgOUMifHyP+KYbvpjbdoFNs+aK7DXlji0Tw= +github.com/golang/protobuf v1.5.2/go.mod h1:XVQd3VNwM+JqD3oG2Ue2ip4fOMUkwXdXDdiuN0vRsmY= +github.com/google/btree v0.0.0-20180813153112-4030bb1f1f0c/go.mod h1:lNA+9X1NB3Zf8V7Ke586lFgjr2dZNuvo3lPJSGZ5JPQ= +github.com/google/btree v1.0.0/go.mod h1:lNA+9X1NB3Zf8V7Ke586lFgjr2dZNuvo3lPJSGZ5JPQ= +github.com/google/gnostic v0.5.7-v3refs h1:FhTMOKj2VhjpouxvWJAV1TL304uMlb9zcDqkl6cEI54= +github.com/google/gnostic v0.5.7-v3refs/go.mod h1:73MKFl6jIHelAJNaBGFzt3SPtZULs9dYrGFt8OiIsHQ= +github.com/google/go-cmp v0.2.0/go.mod h1:oXzfMopK8JAjlY9xF4vHSVASa0yLyX7SntLO5aqRK0M= +github.com/google/go-cmp v0.3.0/go.mod h1:8QqcDgzrUqlUb/G2PQTWiueGozuR1884gddMywk6iLU= +github.com/google/go-cmp v0.3.1/go.mod h1:8QqcDgzrUqlUb/G2PQTWiueGozuR1884gddMywk6iLU= +github.com/google/go-cmp v0.4.0/go.mod h1:v8dTdLbMG2kIc/vJvl+f65V22dbkXbowE6jgT/gNBxE= +github.com/google/go-cmp v0.4.1/go.mod h1:v8dTdLbMG2kIc/vJvl+f65V22dbkXbowE6jgT/gNBxE= +github.com/google/go-cmp v0.5.0/go.mod h1:v8dTdLbMG2kIc/vJvl+f65V22dbkXbowE6jgT/gNBxE= +github.com/google/go-cmp v0.5.1/go.mod h1:v8dTdLbMG2kIc/vJvl+f65V22dbkXbowE6jgT/gNBxE= +github.com/google/go-cmp v0.5.5/go.mod h1:v8dTdLbMG2kIc/vJvl+f65V22dbkXbowE6jgT/gNBxE= +github.com/google/go-cmp v0.5.9 h1:O2Tfq5qg4qc4AmwVlvv0oLiVAGB7enBSJ2x2DqQFi38= +github.com/google/go-cmp v0.5.9/go.mod h1:17dUlkBOakJ0+DkrSSNjCkIjxS6bF9zb3elmeNGIjoY= +github.com/google/gofuzz v1.0.0/go.mod h1:dBl0BpW6vV/+mYPU4Po3pmUjxk6FQPldtuIdl/M65Eg= +github.com/google/gofuzz v1.2.0 h1:xRy4A+RhZaiKjJ1bPfwQ8sedCA+YS2YcCHW6ec7JMi0= +github.com/google/gofuzz v1.2.0/go.mod h1:dBl0BpW6vV/+mYPU4Po3pmUjxk6FQPldtuIdl/M65Eg= +github.com/google/martian v2.1.0+incompatible/go.mod h1:9I4somxYTbIHy5NJKHRl3wXiIaQGbYVAs8BPL6v8lEs= +github.com/google/martian/v3 v3.0.0/go.mod h1:y5Zk1BBys9G+gd6Jrk0W3cC1+ELVxBWuIGO+w/tUAp0= +github.com/google/pprof v0.0.0-20181206194817-3ea8567a2e57/go.mod h1:zfwlbNMJ+OItoe0UupaVj+oy1omPYYDuagoSzA8v9mc= +github.com/google/pprof v0.0.0-20190515194954-54271f7e092f/go.mod h1:zfwlbNMJ+OItoe0UupaVj+oy1omPYYDuagoSzA8v9mc= +github.com/google/pprof v0.0.0-20191218002539-d4f498aebedc/go.mod h1:ZgVRPoUq/hfqzAqh7sHMqb3I9Rq5C59dIz2SbBwJ4eM= +github.com/google/pprof v0.0.0-20200212024743-f11f1df84d12/go.mod h1:ZgVRPoUq/hfqzAqh7sHMqb3I9Rq5C59dIz2SbBwJ4eM= +github.com/google/pprof v0.0.0-20200229191704-1ebb73c60ed3/go.mod h1:ZgVRPoUq/hfqzAqh7sHMqb3I9Rq5C59dIz2SbBwJ4eM= +github.com/google/pprof v0.0.0-20200430221834-fc25d7d30c6d/go.mod h1:ZgVRPoUq/hfqzAqh7sHMqb3I9Rq5C59dIz2SbBwJ4eM= +github.com/google/pprof v0.0.0-20200708004538-1a94d8640e99/go.mod h1:ZgVRPoUq/hfqzAqh7sHMqb3I9Rq5C59dIz2SbBwJ4eM= +github.com/google/renameio v0.1.0/go.mod h1:KWCgfxg9yswjAJkECMjeO8J8rahYeXnNhOm40UhjYkI= +github.com/googleapis/gax-go/v2 v2.0.4/go.mod h1:0Wqv26UfaUD9n4G6kQubkQ+KchISgw+vpHVxEJEs9eg= +github.com/googleapis/gax-go/v2 v2.0.5/go.mod h1:DWXyrwAJ9X0FpwwEdw+IPEYBICEFu5mhpdKc/us6bOk= +github.com/hashicorp/golang-lru v0.5.0/go.mod h1:/m3WP610KZHVQ1SGc6re/UDhFvYD7pJ4Ao+sR/qLZy8= +github.com/hashicorp/golang-lru v0.5.1/go.mod h1:/m3WP610KZHVQ1SGc6re/UDhFvYD7pJ4Ao+sR/qLZy8= +github.com/ianlancetaylor/demangle v0.0.0-20181102032728-5e5cf60278f6/go.mod h1:aSSvb/t6k1mPoxDqO4vJh6VOCGPwU4O0C2/Eqndh1Sc= +github.com/josharian/intern v1.0.0 h1:vlS4z54oSdjm0bgjRigI+G1HpF+tI+9rE5LLzOg8HmY= +github.com/josharian/intern v1.0.0/go.mod h1:5DoeVV0s6jJacbCEi61lwdGj/aVlrQvzHFFd8Hwg//Y= +github.com/json-iterator/go v1.1.12 h1:PV8peI4a0ysnczrg+LtxykD8LfKY9ML6u2jnxaEnrnM= +github.com/json-iterator/go v1.1.12/go.mod h1:e30LSqwooZae/UwlEbR2852Gd8hjQvJoHmT4TnhNGBo= +github.com/jstemmer/go-junit-report v0.0.0-20190106144839-af01ea7f8024/go.mod h1:6v2b51hI/fHJwM22ozAgKL4VKDeJcHhJFhtBdhmNjmU= +github.com/jstemmer/go-junit-report v0.9.1/go.mod h1:Brl9GWCQeLvo8nXZwPNNblvFj/XSXhF0NWZEnDohbsk= +github.com/kisielk/errcheck v1.5.0/go.mod h1:pFxgyoBC7bSaBwPgfKdkLd5X25qrDl4LWUI2bnpBCr8= +github.com/kisielk/gotool v1.0.0/go.mod h1:XhKaO+MFFWcvkIS/tQcRk01m1F5IRFswLeQ+oQHNcck= +github.com/kr/pretty v0.1.0/go.mod h1:dAy3ld7l9f0ibDNOQOHHMYYIIbhfbHSm3C4ZsoJORNo= +github.com/kr/pretty v0.2.0/go.mod h1:ipq/a2n7PKx3OHsz4KJII5eveXtPO4qwEXGdVfWzfnI= +github.com/kr/pty v1.1.1/go.mod h1:pFQYn66WHrOpPYNljwOMqo10TkYh1fy3cYio2l3bCsQ= +github.com/kr/text v0.1.0/go.mod h1:4Jbv+DJW3UT/LiOwJeYQe1efqtUx/iVham/4vfdArNI= +github.com/kr/text v0.2.0 h1:5Nx0Ya0ZqY2ygV366QzturHI13Jq95ApcVaJBhpS+AY= +github.com/kr/text v0.2.0/go.mod h1:eLer722TekiGuMkidMxC/pM04lWEeraHUUmBw8l2grE= +github.com/mailru/easyjson v0.0.0-20190614124828-94de47d64c63/go.mod h1:C1wdFJiN94OJF2b5HbByQZoLdCWB1Yqtg26g4irojpc= +github.com/mailru/easyjson v0.0.0-20190626092158-b2ccc519800e/go.mod h1:C1wdFJiN94OJF2b5HbByQZoLdCWB1Yqtg26g4irojpc= +github.com/mailru/easyjson v0.7.6/go.mod h1:xzfreul335JAWq5oZzymOObrkdz5UnU4kGfJJLY9Nlc= +github.com/mailru/easyjson v0.7.7 h1:UGYAvKxe3sBsEDzO8ZeWOSlIQfWFlxbzLZe7hwFURr0= +github.com/mailru/easyjson v0.7.7/go.mod h1:xzfreul335JAWq5oZzymOObrkdz5UnU4kGfJJLY9Nlc= +github.com/modern-go/concurrent v0.0.0-20180228061459-e0a39a4cb421/go.mod h1:6dJC0mAP4ikYIbvyc7fijjWJddQyLn8Ig3JB5CqoB9Q= +github.com/modern-go/concurrent v0.0.0-20180306012644-bacd9c7ef1dd h1:TRLaZ9cD/w8PVh93nsPXa1VrQ6jlwL5oN8l14QlcNfg= +github.com/modern-go/concurrent v0.0.0-20180306012644-bacd9c7ef1dd/go.mod h1:6dJC0mAP4ikYIbvyc7fijjWJddQyLn8Ig3JB5CqoB9Q= +github.com/modern-go/reflect2 v1.0.2 h1:xBagoLtFs94CBntxluKeaWgTMpvLxC4ur3nMaC9Gz0M= +github.com/modern-go/reflect2 v1.0.2/go.mod h1:yWuevngMOJpCy52FWWMvUC8ws7m/LJsjYzDa0/r8luk= +github.com/munnerz/goautoneg v0.0.0-20191010083416-a7dc8b61c822 h1:C3w9PqII01/Oq1c1nUAm88MOHcQC9l5mIlSMApZMrHA= +github.com/munnerz/goautoneg v0.0.0-20191010083416-a7dc8b61c822/go.mod h1:+n7T8mK8HuQTcFwEeznm/DIxMOiR9yIdICNftLE1DvQ= +github.com/niemeyer/pretty v0.0.0-20200227124842-a10e7caefd8e h1:fD57ERR4JtEqsWbfPhv4DMiApHyliiK5xCTNVSPiaAs= +github.com/niemeyer/pretty v0.0.0-20200227124842-a10e7caefd8e/go.mod h1:zD1mROLANZcx1PVRCS0qkT7pwLkGfwJo4zjcN/Tysno= +github.com/onsi/ginkgo/v2 v2.1.4 h1:GNapqRSid3zijZ9H77KrgVG4/8KqiyRsxcSxe+7ApXY= +github.com/onsi/gomega v1.19.0 h1:4ieX6qQjPP/BfC3mpsAtIGGlxTWPeA3Inl/7DtXw1tw= +github.com/pmezard/go-difflib v1.0.0 h1:4DBwDE0NGyQoBHbLQYPwSUPoCMWR5BEzIk/f1lZbAQM= +github.com/pmezard/go-difflib v1.0.0/go.mod h1:iKH77koFhYxTK1pcRnkKkqfTogsbg7gZNVY4sRDYZ/4= +github.com/prometheus/client_model v0.0.0-20190812154241-14fe0d1b01d4/go.mod h1:xMI15A0UPsDsEKsMN9yxemIoYk6Tm2C1GtYGdfGttqA= +github.com/rogpeppe/go-internal v1.3.0/go.mod h1:M8bDsm7K2OlrFYOpmOWEs/qY81heoFRclV5y23lUDJ4= +github.com/spf13/afero v1.2.2/go.mod h1:9ZxEEn6pIJ8Rxe320qSDBk6AsU0r9pR7Q4OcevTdifk= +github.com/spf13/pflag v1.0.5 h1:iy+VFUOCP1a+8yFto/drg2CJ5u0yRoB7fZw3DKv/JXA= +github.com/stoewer/go-strcase v1.2.0/go.mod h1:IBiWB2sKIp3wVVQ3Y035++gc+knqhUQag1KpM8ahLw8= +github.com/stretchr/objx v0.1.0/go.mod h1:HFkY916IF+rwdDfMAkV7OtwuqBVzrE8GR6GFx+wExME= +github.com/stretchr/objx v0.4.0/go.mod h1:YvHI0jy2hoMjB+UWwv71VJQ9isScKT/TqJzVSSt89Yw= +github.com/stretchr/testify v1.3.0/go.mod h1:M5WIy9Dh21IEIfnGCwXGc5bZfKNJtfHm1UVUgZn+9EI= +github.com/stretchr/testify v1.4.0/go.mod h1:j7eGeouHqKxXV5pUuKE4zz7dFj8WfuZ+81PSLYec5m4= +github.com/stretchr/testify v1.5.1/go.mod h1:5W2xD1RspED5o8YsWQXVCued0rvSQ+mT+I5cxcmMvtA= +github.com/stretchr/testify v1.6.1/go.mod h1:6Fq8oRcR53rry900zMqJjRRixrwX3KX962/h/Wwjteg= +github.com/stretchr/testify v1.7.1/go.mod h1:6Fq8oRcR53rry900zMqJjRRixrwX3KX962/h/Wwjteg= +github.com/stretchr/testify v1.8.0 h1:pSgiaMZlXftHpm5L7V1+rVB+AZJydKsMxsQBIJw4PKk= +github.com/stretchr/testify v1.8.0/go.mod h1:yNjHg4UonilssWZ8iaSj1OCr/vHnekPRkoO+kdMU+MU= +github.com/yuin/goldmark v1.1.25/go.mod h1:3hX8gzYuyVAZsxl0MRgGTJEmQBFcNTphYh9decYSb74= +github.com/yuin/goldmark v1.1.27/go.mod h1:3hX8gzYuyVAZsxl0MRgGTJEmQBFcNTphYh9decYSb74= +github.com/yuin/goldmark v1.1.32/go.mod h1:3hX8gzYuyVAZsxl0MRgGTJEmQBFcNTphYh9decYSb74= +github.com/yuin/goldmark v1.2.1/go.mod h1:3hX8gzYuyVAZsxl0MRgGTJEmQBFcNTphYh9decYSb74= +go.opencensus.io v0.21.0/go.mod h1:mSImk1erAIZhrmZN+AvHh14ztQfjbGwt4TtuofqLduU= +go.opencensus.io v0.22.0/go.mod h1:+kGneAE2xo2IficOXnaByMWTGM9T73dGwxeWcUqIpI8= +go.opencensus.io v0.22.2/go.mod h1:yxeiOL68Rb0Xd1ddK5vPZ/oVn4vY4Ynel7k9FzqtOIw= +go.opencensus.io v0.22.3/go.mod h1:yxeiOL68Rb0Xd1ddK5vPZ/oVn4vY4Ynel7k9FzqtOIw= +go.opencensus.io v0.22.4/go.mod h1:yxeiOL68Rb0Xd1ddK5vPZ/oVn4vY4Ynel7k9FzqtOIw= +golang.org/x/crypto v0.0.0-20190308221718-c2843e01d9a2/go.mod h1:djNgcEr1/C05ACkg1iLfiJU5Ep61QUkGW8qpdssI0+w= +golang.org/x/crypto v0.0.0-20190510104115-cbcb75029529/go.mod h1:yigFU9vqHzYiE8UmvKecakEJjdnWj3jj499lnFckfCI= +golang.org/x/crypto v0.0.0-20190605123033-f99c8df09eb5/go.mod h1:yigFU9vqHzYiE8UmvKecakEJjdnWj3jj499lnFckfCI= +golang.org/x/crypto v0.0.0-20191011191535-87dc89f01550/go.mod h1:yigFU9vqHzYiE8UmvKecakEJjdnWj3jj499lnFckfCI= +golang.org/x/crypto v0.0.0-20200622213623-75b288015ac9/go.mod h1:LzIPMQfyMNhhGPhUkYOs5KpL4U8rLKemX1yGLhDgUto= +golang.org/x/exp v0.0.0-20190121172915-509febef88a4/go.mod h1:CJ0aWSM057203Lf6IL+f9T1iT9GByDxfZKAQTCR3kQA= +golang.org/x/exp v0.0.0-20190306152737-a1d7652674e8/go.mod h1:CJ0aWSM057203Lf6IL+f9T1iT9GByDxfZKAQTCR3kQA= +golang.org/x/exp v0.0.0-20190510132918-efd6b22b2522/go.mod h1:ZjyILWgesfNpC6sMxTJOJm9Kp84zZh5NQWvqDGG3Qr8= +golang.org/x/exp v0.0.0-20190829153037-c13cbed26979/go.mod h1:86+5VVa7VpoJ4kLfm080zCjGlMRFzhUhsZKEZO7MGek= +golang.org/x/exp v0.0.0-20191030013958-a1ab85dbe136/go.mod h1:JXzH8nQsPlswgeRAPE3MuO9GYsAcnJvJ4vnMwN/5qkY= +golang.org/x/exp v0.0.0-20191129062945-2f5052295587/go.mod h1:2RIsYlXP63K8oxa1u096TMicItID8zy7Y6sNkU49FU4= +golang.org/x/exp v0.0.0-20191227195350-da58074b4299/go.mod h1:2RIsYlXP63K8oxa1u096TMicItID8zy7Y6sNkU49FU4= +golang.org/x/exp v0.0.0-20200119233911-0405dc783f0a/go.mod h1:2RIsYlXP63K8oxa1u096TMicItID8zy7Y6sNkU49FU4= +golang.org/x/exp v0.0.0-20200207192155-f17229e696bd/go.mod h1:J/WKrq2StrnmMY6+EHIKF9dgMWnmCNThgcyBT1FY9mM= +golang.org/x/exp v0.0.0-20200224162631-6cc2880d07d6/go.mod h1:3jZMyOhIsHpP37uCMkUooju7aAi5cS1Q23tOzKc+0MU= +golang.org/x/image v0.0.0-20190227222117-0694c2d4d067/go.mod h1:kZ7UVZpmo3dzQBMxlp+ypCbDeSB+sBbTgSJuh5dn5js= +golang.org/x/image v0.0.0-20190802002840-cff245a6509b/go.mod h1:FeLwcggjj3mMvU+oOTbSwawSJRM1uh48EjtB4UJZlP0= +golang.org/x/lint v0.0.0-20181026193005-c67002cb31c3/go.mod h1:UVdnD1Gm6xHRNCYTkRU2/jEulfH38KcIWyp/GAMgvoE= +golang.org/x/lint v0.0.0-20190227174305-5b3e6a55c961/go.mod h1:wehouNa3lNwaWXcvxsM5YxQ5yQlVC4a0KAMCusXpPoU= +golang.org/x/lint v0.0.0-20190301231843-5614ed5bae6f/go.mod h1:UVdnD1Gm6xHRNCYTkRU2/jEulfH38KcIWyp/GAMgvoE= +golang.org/x/lint v0.0.0-20190313153728-d0100b6bd8b3/go.mod h1:6SW0HCj/g11FgYtHlgUYUwCkIfeOF89ocIRzGO/8vkc= +golang.org/x/lint v0.0.0-20190409202823-959b441ac422/go.mod h1:6SW0HCj/g11FgYtHlgUYUwCkIfeOF89ocIRzGO/8vkc= +golang.org/x/lint v0.0.0-20190909230951-414d861bb4ac/go.mod h1:6SW0HCj/g11FgYtHlgUYUwCkIfeOF89ocIRzGO/8vkc= +golang.org/x/lint v0.0.0-20190930215403-16217165b5de/go.mod h1:6SW0HCj/g11FgYtHlgUYUwCkIfeOF89ocIRzGO/8vkc= +golang.org/x/lint v0.0.0-20191125180803-fdd1cda4f05f/go.mod h1:5qLYkcX4OjUUV8bRuDixDT3tpyyb+LUpUlRWLxfhWrs= +golang.org/x/lint v0.0.0-20200130185559-910be7a94367/go.mod h1:3xt1FjdF8hUf6vQPIChWIBhFzV8gjjsPE/fR3IyQdNY= +golang.org/x/lint v0.0.0-20200302205851-738671d3881b/go.mod h1:3xt1FjdF8hUf6vQPIChWIBhFzV8gjjsPE/fR3IyQdNY= +golang.org/x/mobile v0.0.0-20190312151609-d3739f865fa6/go.mod h1:z+o9i4GpDbdi3rU15maQ/Ox0txvL9dWGYEHz965HBQE= +golang.org/x/mobile v0.0.0-20190719004257-d2bd2a29d028/go.mod h1:E/iHnbuqvinMTCcRqshq8CkpyQDoeVncDDYHnLhea+o= +golang.org/x/mod v0.0.0-20190513183733-4bf6d317e70e/go.mod h1:mXi4GBBbnImb6dmsKGUJ2LatrhH/nqhxcFungHvyanc= +golang.org/x/mod v0.1.0/go.mod h1:0QHyrYULN0/3qlju5TqG8bIK38QM8yzMo5ekMj3DlcY= +golang.org/x/mod v0.1.1-0.20191105210325-c90efee705ee/go.mod h1:QqPTAvyqsEbceGzBzNggFXnrqF1CaUcvgkdR5Ot7KZg= +golang.org/x/mod v0.1.1-0.20191107180719-034126e5016b/go.mod h1:QqPTAvyqsEbceGzBzNggFXnrqF1CaUcvgkdR5Ot7KZg= +golang.org/x/mod v0.2.0/go.mod h1:s0Qsj1ACt9ePp/hMypM3fl4fZqREWJwdYDEqhRiZZUA= +golang.org/x/mod v0.3.0/go.mod h1:s0Qsj1ACt9ePp/hMypM3fl4fZqREWJwdYDEqhRiZZUA= +golang.org/x/net v0.0.0-20180724234803-3673e40ba225/go.mod h1:mL1N/T3taQHkDXs73rZJwtUhF3w3ftmwwsq0BUmARs4= +golang.org/x/net v0.0.0-20180826012351-8a410e7b638d/go.mod h1:mL1N/T3taQHkDXs73rZJwtUhF3w3ftmwwsq0BUmARs4= +golang.org/x/net v0.0.0-20190108225652-1e06a53dbb7e/go.mod h1:mL1N/T3taQHkDXs73rZJwtUhF3w3ftmwwsq0BUmARs4= +golang.org/x/net v0.0.0-20190213061140-3a22650c66bd/go.mod h1:mL1N/T3taQHkDXs73rZJwtUhF3w3ftmwwsq0BUmARs4= +golang.org/x/net v0.0.0-20190311183353-d8887717615a/go.mod h1:t9HGtf8HONx5eT2rtn7q6eTqICYqUVnKs3thJo3Qplg= +golang.org/x/net v0.0.0-20190404232315-eb5bcb51f2a3/go.mod h1:t9HGtf8HONx5eT2rtn7q6eTqICYqUVnKs3thJo3Qplg= +golang.org/x/net v0.0.0-20190501004415-9ce7a6920f09/go.mod h1:t9HGtf8HONx5eT2rtn7q6eTqICYqUVnKs3thJo3Qplg= +golang.org/x/net v0.0.0-20190503192946-f4e77d36d62c/go.mod h1:t9HGtf8HONx5eT2rtn7q6eTqICYqUVnKs3thJo3Qplg= +golang.org/x/net v0.0.0-20190603091049-60506f45cf65/go.mod h1:HSz+uSET+XFnRR8LxR5pz3Of3rY3CfYBVs4xY44aLks= +golang.org/x/net v0.0.0-20190620200207-3b0461eec859/go.mod h1:z5CRVTTTmAJ677TzLLGU+0bjPO0LkuOLi4/5GtJWs/s= +golang.org/x/net v0.0.0-20190628185345-da137c7871d7/go.mod h1:z5CRVTTTmAJ677TzLLGU+0bjPO0LkuOLi4/5GtJWs/s= +golang.org/x/net v0.0.0-20190724013045-ca1201d0de80/go.mod h1:z5CRVTTTmAJ677TzLLGU+0bjPO0LkuOLi4/5GtJWs/s= +golang.org/x/net v0.0.0-20190827160401-ba9fcec4b297/go.mod h1:z5CRVTTTmAJ677TzLLGU+0bjPO0LkuOLi4/5GtJWs/s= +golang.org/x/net v0.0.0-20191209160850-c0dbc17a3553/go.mod h1:z5CRVTTTmAJ677TzLLGU+0bjPO0LkuOLi4/5GtJWs/s= +golang.org/x/net v0.0.0-20200114155413-6afb5195e5aa/go.mod h1:z5CRVTTTmAJ677TzLLGU+0bjPO0LkuOLi4/5GtJWs/s= +golang.org/x/net v0.0.0-20200202094626-16171245cfb2/go.mod h1:z5CRVTTTmAJ677TzLLGU+0bjPO0LkuOLi4/5GtJWs/s= +golang.org/x/net v0.0.0-20200222125558-5a598a2470a0/go.mod h1:z5CRVTTTmAJ677TzLLGU+0bjPO0LkuOLi4/5GtJWs/s= +golang.org/x/net v0.0.0-20200226121028-0de0cce0169b/go.mod h1:z5CRVTTTmAJ677TzLLGU+0bjPO0LkuOLi4/5GtJWs/s= +golang.org/x/net v0.0.0-20200301022130-244492dfa37a/go.mod h1:z5CRVTTTmAJ677TzLLGU+0bjPO0LkuOLi4/5GtJWs/s= +golang.org/x/net v0.0.0-20200324143707-d3edc9973b7e/go.mod h1:qpuaurCH72eLCgpAm/N6yyVIVM9cpaDIP3A8BGJEC5A= +golang.org/x/net v0.0.0-20200501053045-e0ff5e5a1de5/go.mod h1:qpuaurCH72eLCgpAm/N6yyVIVM9cpaDIP3A8BGJEC5A= +golang.org/x/net v0.0.0-20200506145744-7e3656a0809f/go.mod h1:qpuaurCH72eLCgpAm/N6yyVIVM9cpaDIP3A8BGJEC5A= +golang.org/x/net v0.0.0-20200513185701-a91f0712d120/go.mod h1:qpuaurCH72eLCgpAm/N6yyVIVM9cpaDIP3A8BGJEC5A= +golang.org/x/net v0.0.0-20200520182314-0ba52f642ac2/go.mod h1:qpuaurCH72eLCgpAm/N6yyVIVM9cpaDIP3A8BGJEC5A= +golang.org/x/net v0.0.0-20200625001655-4c5254603344/go.mod h1:/O7V0waA8r7cgGh81Ro3o1hOxt32SMVPicZroKQ2sZA= +golang.org/x/net v0.0.0-20200707034311-ab3426394381/go.mod h1:/O7V0waA8r7cgGh81Ro3o1hOxt32SMVPicZroKQ2sZA= +golang.org/x/net v0.0.0-20200822124328-c89045814202/go.mod h1:/O7V0waA8r7cgGh81Ro3o1hOxt32SMVPicZroKQ2sZA= +golang.org/x/net v0.0.0-20201021035429-f5854403a974/go.mod h1:sp8m0HH+o8qH0wwXwYZr8TS3Oi6o0r6Gce1SSxlDquU= +golang.org/x/net v0.0.0-20220127200216-cd36cc0744dd/go.mod h1:CfG3xpIq0wQ8r1q4Su4UZFWDARRcnwPjda9FqA0JpMk= +golang.org/x/net v0.0.0-20220722155237-a158d28d115b h1:PxfKdU9lEEDYjdIzOtC4qFWgkU2rGHdKlKowJSMN9h0= +golang.org/x/net v0.0.0-20220722155237-a158d28d115b/go.mod h1:XRhObCWvk6IyKnWLug+ECip1KBveYUHfp+8e9klMJ9c= +golang.org/x/oauth2 v0.0.0-20180821212333-d2e6202438be/go.mod h1:N/0e6XlmueqKjAGxoOufVs8QHGRruUQn6yWY3a++T0U= +golang.org/x/oauth2 v0.0.0-20190226205417-e64efc72b421/go.mod h1:gOpvHmFTYa4IltrdGE7lF6nIHvwfUNPOp7c8zoXwtLw= +golang.org/x/oauth2 v0.0.0-20190604053449-0f29369cfe45/go.mod h1:gOpvHmFTYa4IltrdGE7lF6nIHvwfUNPOp7c8zoXwtLw= +golang.org/x/oauth2 v0.0.0-20191202225959-858c2ad4c8b6/go.mod h1:gOpvHmFTYa4IltrdGE7lF6nIHvwfUNPOp7c8zoXwtLw= +golang.org/x/oauth2 v0.0.0-20200107190931-bf48bf16ab8d/go.mod h1:gOpvHmFTYa4IltrdGE7lF6nIHvwfUNPOp7c8zoXwtLw= +golang.org/x/oauth2 v0.0.0-20220411215720-9780585627b5 h1:OSnWWcOd/CtWQC2cYSBgbTSJv3ciqd8r54ySIW2y3RE= +golang.org/x/oauth2 v0.0.0-20220411215720-9780585627b5/go.mod h1:DAh4E804XQdzx2j+YRIaUnCqCV2RuMz24cGBJ5QYIrc= +golang.org/x/sync v0.0.0-20180314180146-1d60e4601c6f/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM= +golang.org/x/sync v0.0.0-20181108010431-42b317875d0f/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM= +golang.org/x/sync v0.0.0-20181221193216-37e7f081c4d4/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM= +golang.org/x/sync v0.0.0-20190227155943-e225da77a7e6/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM= +golang.org/x/sync v0.0.0-20190423024810-112230192c58/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM= +golang.org/x/sync v0.0.0-20190911185100-cd5d95a43a6e/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM= +golang.org/x/sync v0.0.0-20200317015054-43a5402ce75a/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM= +golang.org/x/sync v0.0.0-20200625203802-6e8e738ad208/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM= +golang.org/x/sync v0.0.0-20201020160332-67f06af15bc9/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM= +golang.org/x/sys v0.0.0-20180830151530-49385e6e1522/go.mod h1:STP8DvDyc/dI5b8T5hshtkjS+E42TnysNCUPdjciGhY= +golang.org/x/sys v0.0.0-20190215142949-d0b11bdaac8a/go.mod h1:STP8DvDyc/dI5b8T5hshtkjS+E42TnysNCUPdjciGhY= +golang.org/x/sys v0.0.0-20190312061237-fead79001313/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= +golang.org/x/sys v0.0.0-20190412213103-97732733099d/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= +golang.org/x/sys v0.0.0-20190502145724-3ef323f4f1fd/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= +golang.org/x/sys v0.0.0-20190507160741-ecd444e8653b/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= +golang.org/x/sys v0.0.0-20190606165138-5da285871e9c/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= +golang.org/x/sys v0.0.0-20190624142023-c5567b49c5d0/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= +golang.org/x/sys v0.0.0-20190726091711-fc99dfbffb4e/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= +golang.org/x/sys v0.0.0-20191001151750-bb3f8db39f24/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= +golang.org/x/sys v0.0.0-20191204072324-ce4227a45e2e/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= +golang.org/x/sys v0.0.0-20191228213918-04cbcbbfeed8/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= +golang.org/x/sys v0.0.0-20200113162924-86b910548bc1/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= +golang.org/x/sys v0.0.0-20200122134326-e047566fdf82/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= +golang.org/x/sys v0.0.0-20200202164722-d101bd2416d5/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= +golang.org/x/sys v0.0.0-20200212091648-12a6c2dcc1e4/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= +golang.org/x/sys v0.0.0-20200223170610-d5e6a3e2c0ae/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= +golang.org/x/sys v0.0.0-20200302150141-5c8b2ff67527/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= +golang.org/x/sys v0.0.0-20200323222414-85ca7c5b95cd/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= +golang.org/x/sys v0.0.0-20200331124033-c3d80250170d/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= +golang.org/x/sys v0.0.0-20200501052902-10377860bb8e/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= +golang.org/x/sys v0.0.0-20200511232937-7e40ca221e25/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= +golang.org/x/sys v0.0.0-20200515095857-1151b9dac4a9/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= +golang.org/x/sys v0.0.0-20200523222454-059865788121/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= +golang.org/x/sys v0.0.0-20200803210538-64077c9b5642/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= +golang.org/x/sys v0.0.0-20200930185726-fdedc70b468f/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= +golang.org/x/sys v0.0.0-20210615035016-665e8c7367d1/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= +golang.org/x/sys v0.0.0-20211216021012-1d35b9e2eb4e/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= +golang.org/x/sys v0.0.0-20220722155257-8c9f86f7a55f h1:v4INt8xihDGvnrfjMDVXGxw9wrfxYyCjk0KbXjhR55s= +golang.org/x/sys v0.0.0-20220722155257-8c9f86f7a55f/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= +golang.org/x/term v0.0.0-20210927222741-03fcf44c2211 h1:JGgROgKl9N8DuW20oFS5gxc+lE67/N3FcwmBPMe7ArY= +golang.org/x/term v0.0.0-20210927222741-03fcf44c2211/go.mod h1:jbD1KX2456YbFQfuXm/mYQcufACuNUgVhRMnK/tPxf8= +golang.org/x/text v0.0.0-20170915032832-14c0d48ead0c/go.mod h1:NqM8EUOU14njkJ3fqMW+pc6Ldnwhi/IjpwHt7yyuwOQ= +golang.org/x/text v0.3.0/go.mod h1:NqM8EUOU14njkJ3fqMW+pc6Ldnwhi/IjpwHt7yyuwOQ= +golang.org/x/text v0.3.1-0.20180807135948-17ff2d5776d2/go.mod h1:NqM8EUOU14njkJ3fqMW+pc6Ldnwhi/IjpwHt7yyuwOQ= +golang.org/x/text v0.3.2/go.mod h1:bEr9sfX3Q8Zfm5fL9x+3itogRgK3+ptLWKqgva+5dAk= +golang.org/x/text v0.3.3/go.mod h1:5Zoc/QRtKVWzQhOtBMvqHzDpF6irO9z98xDceosuGiQ= +golang.org/x/text v0.3.7 h1:olpwvP2KacW1ZWvsR7uQhoyTYvKAupfQrRGBFM352Gk= +golang.org/x/text v0.3.7/go.mod h1:u+2+/6zg+i71rQMx5EYifcz6MCKuco9NR6JIITiCfzQ= +golang.org/x/time v0.0.0-20181108054448-85acf8d2951c/go.mod h1:tRJNPiyCQ0inRvYxbN9jk5I+vvW/OXSQhTDSoE431IQ= +golang.org/x/time v0.0.0-20190308202827-9d24e82272b4/go.mod h1:tRJNPiyCQ0inRvYxbN9jk5I+vvW/OXSQhTDSoE431IQ= +golang.org/x/time v0.0.0-20191024005414-555d28b269f0/go.mod h1:tRJNPiyCQ0inRvYxbN9jk5I+vvW/OXSQhTDSoE431IQ= +golang.org/x/time v0.0.0-20220224211638-0e9765cccd65 h1:M73Iuj3xbbb9Uk1DYhzydthsj6oOd6l9bpuFcNoUvTs= +golang.org/x/time v0.0.0-20220224211638-0e9765cccd65/go.mod h1:tRJNPiyCQ0inRvYxbN9jk5I+vvW/OXSQhTDSoE431IQ= +golang.org/x/tools v0.0.0-20180917221912-90fa682c2a6e/go.mod h1:n7NCudcB/nEzxVGmLbDWY5pfWTLqBcC2KZ6jyYvM4mQ= +golang.org/x/tools v0.0.0-20190114222345-bf090417da8b/go.mod h1:n7NCudcB/nEzxVGmLbDWY5pfWTLqBcC2KZ6jyYvM4mQ= +golang.org/x/tools v0.0.0-20190226205152-f727befe758c/go.mod h1:9Yl7xja0Znq3iFh3HoIrodX9oNMXvdceNzlUR8zjMvY= +golang.org/x/tools v0.0.0-20190311212946-11955173bddd/go.mod h1:LCzVGOaR6xXOjkQ3onu1FJEFr0SW1gC7cKk1uF8kGRs= +golang.org/x/tools v0.0.0-20190312151545-0bb0c0a6e846/go.mod h1:LCzVGOaR6xXOjkQ3onu1FJEFr0SW1gC7cKk1uF8kGRs= +golang.org/x/tools v0.0.0-20190312170243-e65039ee4138/go.mod h1:LCzVGOaR6xXOjkQ3onu1FJEFr0SW1gC7cKk1uF8kGRs= +golang.org/x/tools v0.0.0-20190425150028-36563e24a262/go.mod h1:RgjU9mgBXZiqYHBnxXauZ1Gv1EHHAz9KjViQ78xBX0Q= +golang.org/x/tools v0.0.0-20190506145303-2d16b83fe98c/go.mod h1:RgjU9mgBXZiqYHBnxXauZ1Gv1EHHAz9KjViQ78xBX0Q= +golang.org/x/tools v0.0.0-20190524140312-2c0ae7006135/go.mod h1:RgjU9mgBXZiqYHBnxXauZ1Gv1EHHAz9KjViQ78xBX0Q= +golang.org/x/tools v0.0.0-20190606124116-d0a3d012864b/go.mod h1:/rFqwRUd4F7ZHNgwSSTFct+R/Kf4OFW1sUzUTQQTgfc= +golang.org/x/tools v0.0.0-20190621195816-6e04913cbbac/go.mod h1:/rFqwRUd4F7ZHNgwSSTFct+R/Kf4OFW1sUzUTQQTgfc= +golang.org/x/tools v0.0.0-20190628153133-6cdbf07be9d0/go.mod h1:/rFqwRUd4F7ZHNgwSSTFct+R/Kf4OFW1sUzUTQQTgfc= +golang.org/x/tools v0.0.0-20190816200558-6889da9d5479/go.mod h1:b+2E5dAYhXwXZwtnZ6UAqBI28+e2cm9otk0dWdXHAEo= +golang.org/x/tools v0.0.0-20190911174233-4f2ddba30aff/go.mod h1:b+2E5dAYhXwXZwtnZ6UAqBI28+e2cm9otk0dWdXHAEo= +golang.org/x/tools v0.0.0-20191012152004-8de300cfc20a/go.mod h1:b+2E5dAYhXwXZwtnZ6UAqBI28+e2cm9otk0dWdXHAEo= +golang.org/x/tools v0.0.0-20191113191852-77e3bb0ad9e7/go.mod h1:b+2E5dAYhXwXZwtnZ6UAqBI28+e2cm9otk0dWdXHAEo= +golang.org/x/tools v0.0.0-20191115202509-3a792d9c32b2/go.mod h1:b+2E5dAYhXwXZwtnZ6UAqBI28+e2cm9otk0dWdXHAEo= +golang.org/x/tools v0.0.0-20191119224855-298f0cb1881e/go.mod h1:b+2E5dAYhXwXZwtnZ6UAqBI28+e2cm9otk0dWdXHAEo= +golang.org/x/tools v0.0.0-20191125144606-a911d9008d1f/go.mod h1:b+2E5dAYhXwXZwtnZ6UAqBI28+e2cm9otk0dWdXHAEo= +golang.org/x/tools v0.0.0-20191130070609-6e064ea0cf2d/go.mod h1:b+2E5dAYhXwXZwtnZ6UAqBI28+e2cm9otk0dWdXHAEo= +golang.org/x/tools v0.0.0-20191216173652-a0e659d51361/go.mod h1:TB2adYChydJhpapKDTa4BR/hXlZSLoq2Wpct/0txZ28= +golang.org/x/tools v0.0.0-20191227053925-7b8e75db28f4/go.mod h1:TB2adYChydJhpapKDTa4BR/hXlZSLoq2Wpct/0txZ28= +golang.org/x/tools v0.0.0-20200117161641-43d50277825c/go.mod h1:TB2adYChydJhpapKDTa4BR/hXlZSLoq2Wpct/0txZ28= +golang.org/x/tools v0.0.0-20200122220014-bf1340f18c4a/go.mod h1:TB2adYChydJhpapKDTa4BR/hXlZSLoq2Wpct/0txZ28= +golang.org/x/tools v0.0.0-20200130002326-2f3ba24bd6e7/go.mod h1:TB2adYChydJhpapKDTa4BR/hXlZSLoq2Wpct/0txZ28= +golang.org/x/tools v0.0.0-20200204074204-1cc6d1ef6c74/go.mod h1:TB2adYChydJhpapKDTa4BR/hXlZSLoq2Wpct/0txZ28= +golang.org/x/tools v0.0.0-20200207183749-b753a1ba74fa/go.mod h1:TB2adYChydJhpapKDTa4BR/hXlZSLoq2Wpct/0txZ28= +golang.org/x/tools v0.0.0-20200212150539-ea181f53ac56/go.mod h1:TB2adYChydJhpapKDTa4BR/hXlZSLoq2Wpct/0txZ28= +golang.org/x/tools v0.0.0-20200224181240-023911ca70b2/go.mod h1:TB2adYChydJhpapKDTa4BR/hXlZSLoq2Wpct/0txZ28= +golang.org/x/tools v0.0.0-20200227222343-706bc42d1f0d/go.mod h1:TB2adYChydJhpapKDTa4BR/hXlZSLoq2Wpct/0txZ28= +golang.org/x/tools v0.0.0-20200304193943-95d2e580d8eb/go.mod h1:o4KQGtdN14AW+yjsvvwRTJJuXz8XRtIHtEnmAXLyFUw= +golang.org/x/tools v0.0.0-20200312045724-11d5b4c81c7d/go.mod h1:o4KQGtdN14AW+yjsvvwRTJJuXz8XRtIHtEnmAXLyFUw= +golang.org/x/tools v0.0.0-20200331025713-a30bf2db82d4/go.mod h1:Sl4aGygMT6LrqrWclx+PTx3U+LnKx/seiNR+3G19Ar8= +golang.org/x/tools v0.0.0-20200501065659-ab2804fb9c9d/go.mod h1:EkVYQZoAsY45+roYkvgYkIh4xh/qjgUK9TdY2XT94GE= +golang.org/x/tools v0.0.0-20200512131952-2bc93b1c0c88/go.mod h1:EkVYQZoAsY45+roYkvgYkIh4xh/qjgUK9TdY2XT94GE= +golang.org/x/tools v0.0.0-20200515010526-7d3b6ebf133d/go.mod h1:EkVYQZoAsY45+roYkvgYkIh4xh/qjgUK9TdY2XT94GE= +golang.org/x/tools v0.0.0-20200618134242-20370b0cb4b2/go.mod h1:EkVYQZoAsY45+roYkvgYkIh4xh/qjgUK9TdY2XT94GE= +golang.org/x/tools v0.0.0-20200619180055-7c47624df98f/go.mod h1:EkVYQZoAsY45+roYkvgYkIh4xh/qjgUK9TdY2XT94GE= +golang.org/x/tools v0.0.0-20200729194436-6467de6f59a7/go.mod h1:njjCfa9FT2d7l9Bc6FUM5FLjQPp3cFF28FI3qnDFljA= +golang.org/x/tools v0.0.0-20200804011535-6c149bb5ef0d/go.mod h1:njjCfa9FT2d7l9Bc6FUM5FLjQPp3cFF28FI3qnDFljA= +golang.org/x/tools v0.0.0-20200825202427-b303f430e36d/go.mod h1:njjCfa9FT2d7l9Bc6FUM5FLjQPp3cFF28FI3qnDFljA= +golang.org/x/tools v0.0.0-20210106214847-113979e3529a/go.mod h1:emZCQorbCU4vsT4fOWvOPXz4eW1wZW4PmDk9uLelYpA= +golang.org/x/xerrors v0.0.0-20190717185122-a985d3407aa7/go.mod h1:I/5z698sn9Ka8TeJc9MKroUUfqBBauWjQqLJ2OPfmY0= +golang.org/x/xerrors v0.0.0-20191011141410-1b5146add898/go.mod h1:I/5z698sn9Ka8TeJc9MKroUUfqBBauWjQqLJ2OPfmY0= +golang.org/x/xerrors v0.0.0-20191204190536-9bdfabe68543/go.mod h1:I/5z698sn9Ka8TeJc9MKroUUfqBBauWjQqLJ2OPfmY0= +golang.org/x/xerrors v0.0.0-20200804184101-5ec99f83aff1/go.mod h1:I/5z698sn9Ka8TeJc9MKroUUfqBBauWjQqLJ2OPfmY0= +google.golang.org/api v0.4.0/go.mod h1:8k5glujaEP+g9n7WNsDg8QP6cUVNI86fCNMcbazEtwE= +google.golang.org/api v0.7.0/go.mod h1:WtwebWUNSVBH/HAw79HIFXZNqEvBhG+Ra+ax0hx3E3M= +google.golang.org/api v0.8.0/go.mod h1:o4eAsZoiT+ibD93RtjEohWalFOjRDx6CVaqeizhEnKg= +google.golang.org/api v0.9.0/go.mod h1:o4eAsZoiT+ibD93RtjEohWalFOjRDx6CVaqeizhEnKg= +google.golang.org/api v0.13.0/go.mod h1:iLdEw5Ide6rF15KTC1Kkl0iskquN2gFfn9o9XIsbkAI= +google.golang.org/api v0.14.0/go.mod h1:iLdEw5Ide6rF15KTC1Kkl0iskquN2gFfn9o9XIsbkAI= +google.golang.org/api v0.15.0/go.mod h1:iLdEw5Ide6rF15KTC1Kkl0iskquN2gFfn9o9XIsbkAI= +google.golang.org/api v0.17.0/go.mod h1:BwFmGc8tA3vsd7r/7kR8DY7iEEGSU04BFxCo5jP/sfE= +google.golang.org/api v0.18.0/go.mod h1:BwFmGc8tA3vsd7r/7kR8DY7iEEGSU04BFxCo5jP/sfE= +google.golang.org/api v0.19.0/go.mod h1:BwFmGc8tA3vsd7r/7kR8DY7iEEGSU04BFxCo5jP/sfE= +google.golang.org/api v0.20.0/go.mod h1:BwFmGc8tA3vsd7r/7kR8DY7iEEGSU04BFxCo5jP/sfE= +google.golang.org/api v0.22.0/go.mod h1:BwFmGc8tA3vsd7r/7kR8DY7iEEGSU04BFxCo5jP/sfE= +google.golang.org/api v0.24.0/go.mod h1:lIXQywCXRcnZPGlsd8NbLnOjtAoL6em04bJ9+z0MncE= +google.golang.org/api v0.28.0/go.mod h1:lIXQywCXRcnZPGlsd8NbLnOjtAoL6em04bJ9+z0MncE= +google.golang.org/api v0.29.0/go.mod h1:Lcubydp8VUV7KeIHD9z2Bys/sm/vGKnG1UHuDBSrHWM= +google.golang.org/api v0.30.0/go.mod h1:QGmEvQ87FHZNiUVJkT14jQNYJ4ZJjdRF23ZXz5138Fc= +google.golang.org/appengine v1.1.0/go.mod h1:EbEs0AVv82hx2wNQdGPgUI5lhzA/G0D9YwlJXL52JkM= +google.golang.org/appengine v1.4.0/go.mod h1:xpcJRLb0r/rnEns0DIKYYv+WjYCduHsrkT7/EB5XEv4= +google.golang.org/appengine v1.5.0/go.mod h1:xpcJRLb0r/rnEns0DIKYYv+WjYCduHsrkT7/EB5XEv4= +google.golang.org/appengine v1.6.1/go.mod h1:i06prIuMbXzDqacNJfV5OdTW448YApPu5ww/cMBSeb0= +google.golang.org/appengine v1.6.5/go.mod h1:8WjMMxjGQR8xUklV/ARdw2HLXBOI7O7uCIDZVag1xfc= +google.golang.org/appengine v1.6.6/go.mod h1:8WjMMxjGQR8xUklV/ARdw2HLXBOI7O7uCIDZVag1xfc= +google.golang.org/appengine v1.6.7 h1:FZR1q0exgwxzPzp/aF+VccGrSfxfPpkBqjIIEq3ru6c= +google.golang.org/appengine v1.6.7/go.mod h1:8WjMMxjGQR8xUklV/ARdw2HLXBOI7O7uCIDZVag1xfc= +google.golang.org/genproto v0.0.0-20180817151627-c66870c02cf8/go.mod h1:JiN7NxoALGmiZfu7CAH4rXhgtRTLTxftemlI0sWmxmc= +google.golang.org/genproto v0.0.0-20190307195333-5fe7a883aa19/go.mod h1:VzzqZJRnGkLBvHegQrXjBqPurQTc5/KpmUdxsrq26oE= +google.golang.org/genproto v0.0.0-20190418145605-e7d98fc518a7/go.mod h1:VzzqZJRnGkLBvHegQrXjBqPurQTc5/KpmUdxsrq26oE= +google.golang.org/genproto v0.0.0-20190425155659-357c62f0e4bb/go.mod h1:VzzqZJRnGkLBvHegQrXjBqPurQTc5/KpmUdxsrq26oE= +google.golang.org/genproto v0.0.0-20190502173448-54afdca5d873/go.mod h1:VzzqZJRnGkLBvHegQrXjBqPurQTc5/KpmUdxsrq26oE= +google.golang.org/genproto v0.0.0-20190801165951-fa694d86fc64/go.mod h1:DMBHOl98Agz4BDEuKkezgsaosCRResVns1a3J2ZsMNc= +google.golang.org/genproto v0.0.0-20190819201941-24fa4b261c55/go.mod h1:DMBHOl98Agz4BDEuKkezgsaosCRResVns1a3J2ZsMNc= +google.golang.org/genproto v0.0.0-20190911173649-1774047e7e51/go.mod h1:IbNlFCBrqXvoKpeg0TB2l7cyZUmoaFKYIwrEpbDKLA8= +google.golang.org/genproto v0.0.0-20191108220845-16a3f7862a1a/go.mod h1:n3cpQtvxv34hfy77yVDNjmbRyujviMdxYliBSkLhpCc= +google.golang.org/genproto v0.0.0-20191115194625-c23dd37a84c9/go.mod h1:n3cpQtvxv34hfy77yVDNjmbRyujviMdxYliBSkLhpCc= +google.golang.org/genproto v0.0.0-20191216164720-4f79533eabd1/go.mod h1:n3cpQtvxv34hfy77yVDNjmbRyujviMdxYliBSkLhpCc= +google.golang.org/genproto v0.0.0-20191230161307-f3c370f40bfb/go.mod h1:n3cpQtvxv34hfy77yVDNjmbRyujviMdxYliBSkLhpCc= +google.golang.org/genproto v0.0.0-20200115191322-ca5a22157cba/go.mod h1:n3cpQtvxv34hfy77yVDNjmbRyujviMdxYliBSkLhpCc= +google.golang.org/genproto v0.0.0-20200122232147-0452cf42e150/go.mod h1:n3cpQtvxv34hfy77yVDNjmbRyujviMdxYliBSkLhpCc= +google.golang.org/genproto v0.0.0-20200204135345-fa8e72b47b90/go.mod h1:GmwEX6Z4W5gMy59cAlVYjN9JhxgbQH6Gn+gFDQe2lzA= +google.golang.org/genproto v0.0.0-20200212174721-66ed5ce911ce/go.mod h1:55QSHmfGQM9UVYDPBsyGGes0y52j32PQ3BqQfXhyH3c= +google.golang.org/genproto v0.0.0-20200224152610-e50cd9704f63/go.mod h1:55QSHmfGQM9UVYDPBsyGGes0y52j32PQ3BqQfXhyH3c= +google.golang.org/genproto v0.0.0-20200228133532-8c2c7df3a383/go.mod h1:55QSHmfGQM9UVYDPBsyGGes0y52j32PQ3BqQfXhyH3c= +google.golang.org/genproto v0.0.0-20200305110556-506484158171/go.mod h1:55QSHmfGQM9UVYDPBsyGGes0y52j32PQ3BqQfXhyH3c= +google.golang.org/genproto v0.0.0-20200312145019-da6875a35672/go.mod h1:55QSHmfGQM9UVYDPBsyGGes0y52j32PQ3BqQfXhyH3c= +google.golang.org/genproto v0.0.0-20200331122359-1ee6d9798940/go.mod h1:55QSHmfGQM9UVYDPBsyGGes0y52j32PQ3BqQfXhyH3c= +google.golang.org/genproto v0.0.0-20200430143042-b979b6f78d84/go.mod h1:55QSHmfGQM9UVYDPBsyGGes0y52j32PQ3BqQfXhyH3c= +google.golang.org/genproto v0.0.0-20200511104702-f5ebc3bea380/go.mod h1:55QSHmfGQM9UVYDPBsyGGes0y52j32PQ3BqQfXhyH3c= +google.golang.org/genproto v0.0.0-20200515170657-fc4c6c6a6587/go.mod h1:YsZOwe1myG/8QRHRsmBRE1LrgQY60beZKjly0O1fX9U= +google.golang.org/genproto v0.0.0-20200526211855-cb27e3aa2013/go.mod h1:NbSheEEYHJ7i3ixzK3sjbqSGDJWnxyFXZblF3eUsNvo= +google.golang.org/genproto v0.0.0-20200618031413-b414f8b61790/go.mod h1:jDfRM7FcilCzHH/e9qn6dsT145K34l5v+OpcnNgKAAA= +google.golang.org/genproto v0.0.0-20200729003335-053ba62fc06f/go.mod h1:FWY/as6DDZQgahTzZj3fqbO1CbirC29ZNUFHwi0/+no= +google.golang.org/genproto v0.0.0-20200804131852-c06518451d9c/go.mod h1:FWY/as6DDZQgahTzZj3fqbO1CbirC29ZNUFHwi0/+no= +google.golang.org/genproto v0.0.0-20200825200019-8632dd797987/go.mod h1:FWY/as6DDZQgahTzZj3fqbO1CbirC29ZNUFHwi0/+no= +google.golang.org/genproto v0.0.0-20201019141844-1ed22bb0c154/go.mod h1:FWY/as6DDZQgahTzZj3fqbO1CbirC29ZNUFHwi0/+no= +google.golang.org/grpc v1.19.0/go.mod h1:mqu4LbDTu4XGKhr4mRzUsmM4RtVoemTSY81AxZiDr8c= +google.golang.org/grpc v1.20.1/go.mod h1:10oTOabMzJvdu6/UiuZezV6QK5dSlG84ov/aaiqXj38= +google.golang.org/grpc v1.21.1/go.mod h1:oYelfM1adQP15Ek0mdvEgi9Df8B9CZIaU1084ijfRaM= +google.golang.org/grpc v1.23.0/go.mod h1:Y5yQAOtifL1yxbo5wqy6BxZv8vAUGQwXBOALyacEbxg= +google.golang.org/grpc v1.25.1/go.mod h1:c3i+UQWmh7LiEpx4sFZnkU36qjEYZ0imhYfXVyQciAY= +google.golang.org/grpc v1.26.0/go.mod h1:qbnxyOmOxrQa7FizSgH+ReBfzJrCY1pSN7KXBS8abTk= +google.golang.org/grpc v1.27.0/go.mod h1:qbnxyOmOxrQa7FizSgH+ReBfzJrCY1pSN7KXBS8abTk= +google.golang.org/grpc v1.27.1/go.mod h1:qbnxyOmOxrQa7FizSgH+ReBfzJrCY1pSN7KXBS8abTk= +google.golang.org/grpc v1.28.0/go.mod h1:rpkK4SK4GF4Ach/+MFLZUBavHOvF2JJB5uozKKal+60= +google.golang.org/grpc v1.29.1/go.mod h1:itym6AZVZYACWQqET3MqgPpjcuV5QH3BxFS3IjizoKk= +google.golang.org/grpc v1.30.0/go.mod h1:N36X2cJ7JwdamYAgDz+s+rVMFjt3numwzf/HckM8pak= +google.golang.org/grpc v1.31.0/go.mod h1:N36X2cJ7JwdamYAgDz+s+rVMFjt3numwzf/HckM8pak= +google.golang.org/protobuf v0.0.0-20200109180630-ec00e32a8dfd/go.mod h1:DFci5gLYBciE7Vtevhsrf46CRTquxDuWsQurQQe4oz8= +google.golang.org/protobuf v0.0.0-20200221191635-4d8936d0db64/go.mod h1:kwYJMbMJ01Woi6D6+Kah6886xMZcty6N08ah7+eCXa0= +google.golang.org/protobuf v0.0.0-20200228230310-ab0ca4ff8a60/go.mod h1:cfTl7dwQJ+fmap5saPgwCLgHXTUD7jkjRqWcaiX5VyM= +google.golang.org/protobuf v1.20.1-0.20200309200217-e05f789c0967/go.mod h1:A+miEFZTKqfCUM6K7xSMQL9OKL/b6hQv+e19PK+JZNE= +google.golang.org/protobuf v1.21.0/go.mod h1:47Nbq4nVaFHyn7ilMalzfO3qCViNmqZ2kzikPIcrTAo= +google.golang.org/protobuf v1.22.0/go.mod h1:EGpADcykh3NcUnDUJcl1+ZksZNG86OlYog2l/sGQquU= +google.golang.org/protobuf v1.23.0/go.mod h1:EGpADcykh3NcUnDUJcl1+ZksZNG86OlYog2l/sGQquU= +google.golang.org/protobuf v1.23.1-0.20200526195155-81db48ad09cc/go.mod h1:EGpADcykh3NcUnDUJcl1+ZksZNG86OlYog2l/sGQquU= +google.golang.org/protobuf v1.24.0/go.mod h1:r/3tXBNzIEhYS9I1OUVjXDlt8tc493IdKGjtUeSXeh4= +google.golang.org/protobuf v1.25.0/go.mod h1:9JNX74DMeImyA3h4bdi1ymwjUzf21/xIlbajtzgsN7c= +google.golang.org/protobuf v1.26.0-rc.1/go.mod h1:jlhhOSvTdKEhbULTjvd4ARK9grFBp09yW+WbY/TyQbw= +google.golang.org/protobuf v1.26.0/go.mod h1:9q0QmTI4eRPtz6boOQmLYwt+qCgq0jsYwAQnmE0givc= +google.golang.org/protobuf v1.28.0 h1:w43yiav+6bVFTBQFZX0r7ipe9JQ1QsbMgHwbBziscLw= +google.golang.org/protobuf v1.28.0/go.mod h1:HV8QOd/L58Z+nl8r43ehVNZIU/HEI6OcFqwMG9pJV4I= +gopkg.in/check.v1 v0.0.0-20161208181325-20d25e280405/go.mod h1:Co6ibVJAznAaIkqp8huTwlJQCZ016jof/cbN4VW5Yz0= +gopkg.in/check.v1 v1.0.0-20180628173108-788fd7840127/go.mod h1:Co6ibVJAznAaIkqp8huTwlJQCZ016jof/cbN4VW5Yz0= +gopkg.in/check.v1 v1.0.0-20190902080502-41f04d3bba15/go.mod h1:Co6ibVJAznAaIkqp8huTwlJQCZ016jof/cbN4VW5Yz0= +gopkg.in/check.v1 v1.0.0-20200227125254-8fa46927fb4f h1:BLraFXnmrev5lT+xlilqcH8XK9/i0At2xKjWk4p6zsU= +gopkg.in/check.v1 v1.0.0-20200227125254-8fa46927fb4f/go.mod h1:Co6ibVJAznAaIkqp8huTwlJQCZ016jof/cbN4VW5Yz0= +gopkg.in/errgo.v2 v2.1.0/go.mod h1:hNsd1EY+bozCKY1Ytp96fpM3vjJbqLJn88ws8XvfDNI= +gopkg.in/inf.v0 v0.9.1 h1:73M5CoZyi3ZLMOyDlQh031Cx6N9NDJ2Vvfl76EDAgDc= +gopkg.in/inf.v0 v0.9.1/go.mod h1:cWUDdTG/fYaXco+Dcufb5Vnc6Gp2YChqWtbxRZE0mXw= +gopkg.in/yaml.v2 v2.2.2/go.mod h1:hI93XBmqTisBFMUTm0b8Fm+jr3Dg1NNxqwp+5A1VGuI= +gopkg.in/yaml.v2 v2.2.8/go.mod h1:hI93XBmqTisBFMUTm0b8Fm+jr3Dg1NNxqwp+5A1VGuI= +gopkg.in/yaml.v2 v2.4.0 h1:D8xgwECY7CYvx+Y2n4sBz93Jn9JRvxdiyyo8CTfuKaY= +gopkg.in/yaml.v2 v2.4.0/go.mod h1:RDklbk79AGWmwhnvt/jBztapEOGDOx6ZbXqjP6csGnQ= +gopkg.in/yaml.v3 v3.0.0-20200313102051-9f266ea9e77c/go.mod h1:K4uyk7z7BCEPqu6E+C64Yfv1cQ7kz7rIZviUmN+EgEM= +gopkg.in/yaml.v3 v3.0.0-20200615113413-eeeca48fe776/go.mod h1:K4uyk7z7BCEPqu6E+C64Yfv1cQ7kz7rIZviUmN+EgEM= +gopkg.in/yaml.v3 v3.0.1 h1:fxVm/GzAzEWqLHuvctI91KS9hhNmmWOoWu0XTYJS7CA= +gopkg.in/yaml.v3 v3.0.1/go.mod h1:K4uyk7z7BCEPqu6E+C64Yfv1cQ7kz7rIZviUmN+EgEM= +honnef.co/go/tools v0.0.0-20190102054323-c2f93a96b099/go.mod h1:rf3lG4BRIbNafJWhAfAdb/ePZxsR/4RtNHQocxwk9r4= +honnef.co/go/tools v0.0.0-20190106161140-3f1c8253044a/go.mod h1:rf3lG4BRIbNafJWhAfAdb/ePZxsR/4RtNHQocxwk9r4= +honnef.co/go/tools v0.0.0-20190418001031-e561f6794a2a/go.mod h1:rf3lG4BRIbNafJWhAfAdb/ePZxsR/4RtNHQocxwk9r4= +honnef.co/go/tools v0.0.0-20190523083050-ea95bdfd59fc/go.mod h1:rf3lG4BRIbNafJWhAfAdb/ePZxsR/4RtNHQocxwk9r4= +honnef.co/go/tools v0.0.1-2019.2.3/go.mod h1:a3bituU0lyd329TUQxRnasdCoJDkEUEAqEt0JzvZhAg= +honnef.co/go/tools v0.0.1-2020.1.3/go.mod h1:X/FiERA/W4tHapMX5mGpAtMSVEeEUOyHaw9vFzvIQ3k= +honnef.co/go/tools v0.0.1-2020.1.4/go.mod h1:X/FiERA/W4tHapMX5mGpAtMSVEeEUOyHaw9vFzvIQ3k= +k8s.io/api v0.25.0 h1:H+Q4ma2U/ww0iGB78ijZx6DRByPz6/733jIuFpX70e0= +k8s.io/api v0.25.0/go.mod h1:ttceV1GyV1i1rnmvzT3BST08N6nGt+dudGrquzVQWPk= +k8s.io/apimachinery v0.25.0 h1:MlP0r6+3XbkUG2itd6vp3oxbtdQLQI94fD5gCS+gnoU= +k8s.io/apimachinery v0.25.0/go.mod h1:qMx9eAk0sZQGsXGu86fab8tZdffHbwUfsvzqKn4mfB0= +k8s.io/client-go v0.25.0 h1:CVWIaCETLMBNiTUta3d5nzRbXvY5Hy9Dpl+VvREpu5E= +k8s.io/client-go v0.25.0/go.mod h1:lxykvypVfKilxhTklov0wz1FoaUZ8X4EwbhS6rpRfN8= +k8s.io/klog/v2 v2.0.0/go.mod h1:PBfzABfn139FHAV07az/IF9Wp1bkk3vpT2XSJ76fSDE= +k8s.io/klog/v2 v2.70.2-0.20220707122935-0990e81f1a8f h1:dltw7bAn8bCrQ2CmzzhgoieUZEbWqrvIGVdHGioP5nY= +k8s.io/klog/v2 v2.70.2-0.20220707122935-0990e81f1a8f/go.mod h1:y1WjHnz7Dj687irZUWR/WLkLc5N1YHtjLdmgWjndZn0= +k8s.io/kube-openapi v0.0.0-20220803162953-67bda5d908f1 h1:MQ8BAZPZlWk3S9K4a9NCkIFQtZShWqoha7snGixVgEA= +k8s.io/kube-openapi v0.0.0-20220803162953-67bda5d908f1/go.mod h1:C/N6wCaBHeBHkHUesQOQy2/MZqGgMAFPqGsGQLdbZBU= +k8s.io/utils v0.0.0-20220728103510-ee6ede2d64ed h1:jAne/RjBTyawwAy0utX5eqigAwz/lQhTmy+Hr/Cpue4= +k8s.io/utils v0.0.0-20220728103510-ee6ede2d64ed/go.mod h1:jPW/WVKK9YHAvNhRxK0md/EJ228hCsBRufyofKtW8HA= +rsc.io/binaryregexp v0.2.0/go.mod h1:qTv7/COck+e2FymRvadv62gMdZztPaShugOCi3I+8D8= +rsc.io/quote/v3 v3.1.0/go.mod h1:yEA65RcK8LyAZtP9Kv3t0HmxON59tX3rD+tICJqUlj0= +rsc.io/sampler v1.3.0/go.mod h1:T1hPZKmBbMNahiBKFy5HrXp6adAjACjK9JXDnKaTXpA= +sigs.k8s.io/json v0.0.0-20220713155537-f223a00ba0e2 h1:iXTIw73aPyC+oRdyqqvVJuloN1p0AC/kzH07hu3NE+k= +sigs.k8s.io/json v0.0.0-20220713155537-f223a00ba0e2/go.mod h1:B8JuhiUyNFVKdsE8h686QcCxMaH6HrOAZj4vswFpcB0= +sigs.k8s.io/structured-merge-diff/v4 v4.2.3 h1:PRbqxJClWWYMNV1dhaG4NsibJbArud9kFxnAMREiWFE= +sigs.k8s.io/structured-merge-diff/v4 v4.2.3/go.mod h1:qjx8mGObPmV2aSZepjQjbmb2ihdVs8cGKBraizNC69E= +sigs.k8s.io/yaml v1.3.0 h1:a2VclLzOGrwOHDiV8EfBGhvjHvP46CtW5j6POvhYGGo= +sigs.k8s.io/yaml v1.3.0/go.mod h1:GeOyir5tyXNByN85N/dRIT9es5UQNerPYEKK56eTBm8= diff --git a/pkg/applyconfigurations/testdata/cronjob/groupversion_info.go b/pkg/applyconfigurations/testdata/cronjob/groupversion_info.go new file mode 100644 index 000000000..c1f850329 --- /dev/null +++ b/pkg/applyconfigurations/testdata/cronjob/groupversion_info.go @@ -0,0 +1,20 @@ +/* +Copyright 2022 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. +*/ + +// +groupName=testdata.kubebuilder.io +// +versionName=v1 +// +kubebuilder:ac:generate=true +package testdata diff --git a/pkg/applyconfigurations/traverse.go b/pkg/applyconfigurations/traverse.go index 3c8bb1bb9..6c5364936 100644 --- a/pkg/applyconfigurations/traverse.go +++ b/pkg/applyconfigurations/traverse.go @@ -1,5 +1,5 @@ /* -Copyright 2019 The Kubernetes Authors. +Copyright 2021 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. @@ -20,112 +20,15 @@ import ( "fmt" "go/ast" "go/types" - "io" - "path" - "strings" - "unicode" - "unicode/utf8" "sigs.k8s.io/controller-tools/pkg/loader" "sigs.k8s.io/controller-tools/pkg/markers" + "sigs.k8s.io/controller-tools/pkg/util" ) -// TODO(jpbetz): This was copied from deepcopy-gen - -// codeWriter assists in writing out Go code lines and blocks to a writer. -type codeWriter struct { - out io.Writer -} - -// Line writes a single line. -func (c *codeWriter) Line(line string) { - if _, err := fmt.Fprintln(c.out, line); err != nil { - panic(err) - } -} - -// Linef writes a single line with formatting (as per fmt.Sprintf). -func (c *codeWriter) Linef(line string, args ...interface{}) { - if _, err := fmt.Fprintf(c.out, line+"\n", args...); err != nil { - panic(err) - } -} - -// importsList keeps track of required imports, automatically assigning aliases -// to import statement. -type importsList struct { - byPath map[string]string - byAlias map[string]string - - pkg *loader.Package -} - -// NeedImport marks that the given package is needed in the list of imports, -// returning the ident (import alias) that should be used to reference the package. -func (l *importsList) NeedImport(importPath string) string { - // we get an actual path from Package, which might include venddored - // packages if running on a package in vendor. - if ind := strings.LastIndex(importPath, "/vendor/"); ind != -1 { - importPath = importPath[ind+8: /* len("/vendor/") */] - } - - // check to see if we've already assigned an alias, and just return that. - alias, exists := l.byPath[importPath] - if exists { - return alias - } - - // otherwise, calculate an import alias by joining path parts till we get something unique - restPath, nextWord := path.Split(importPath) - - for otherPath, exists := "", true; exists && otherPath != importPath; otherPath, exists = l.byAlias[alias] { - if restPath == "" { - // do something else to disambiguate if we're run out of parts and - // still have duplicates, somehow - alias += "x" - } - - // can't have a first digit, per Go identifier rules, so just skip them - for firstRune, runeLen := utf8.DecodeRuneInString(nextWord); unicode.IsDigit(firstRune); firstRune, runeLen = utf8.DecodeRuneInString(nextWord) { - nextWord = nextWord[runeLen:] - } - - // make a valid identifier by replacing "bad" characters with underscores - nextWord = strings.Map(func(r rune) rune { - if unicode.IsLetter(r) || unicode.IsDigit(r) || r == '_' { - return r - } - return '_' - }, nextWord) - - alias = nextWord + alias - if len(restPath) > 0 { - restPath, nextWord = path.Split(restPath[:len(restPath)-1] /* chop off final slash */) - } - } - - l.byPath[importPath] = alias - l.byAlias[alias] = importPath - return alias -} - -// ImportSpecs returns a string form of each import spec -// (i.e. `alias "path/to/import"). Aliases are only present -// when they don't match the package name. -func (l *importsList) ImportSpecs() []string { - res := make([]string, 0, len(l.byPath)) - for importPath, alias := range l.byPath { - pkg := l.pkg.Imports()[importPath] - if pkg != nil && pkg.Name == alias { - // don't print if alias is the same as package name - // (we've already taken care of duplicates). - res = append(res, fmt.Sprintf("%q", importPath)) - } else { - res = append(res, fmt.Sprintf("%s %q", alias, importPath)) - } - } - return res -} +var ( + applyConfigAppendString = "ApplyConfiguration" +) // namingInfo holds package and syntax for referencing a field, type, // etc. It's used to allow lazily marking import usage. @@ -137,39 +40,56 @@ type namingInfo struct { } // Syntax calculates the code representation of the given type or name, -// and marks that is used (potentially marking an import as used). -func (n *namingInfo) Syntax(basePkg *loader.Package, imports *importsList) string { +// and returns the apply representation +func (n *namingInfo) Syntax(universe *Universe, basePkg *loader.Package, imports *util.ImportsList) string { if n.nameOverride != "" { return n.nameOverride } - - // NB(directxman12): typeInfo.String gets us most of the way there, - // but fails (for us) on named imports, since it uses the full package path. switch typeInfo := n.typeInfo.(type) { case *types.Named: // register that we need an import for this type, // so we can get the appropriate alias to use. + + var lastType types.Type + appendString := applyConfigAppendString + for underlyingType := typeInfo.Underlying(); underlyingType != lastType; lastType, underlyingType = underlyingType, underlyingType.Underlying() { + // ApplyConfigurations are not necessary for basic types + if _, ok := underlyingType.(*types.Basic); ok { + appendString = "" + } + } + typeName := typeInfo.Obj() otherPkg := typeName.Pkg() - if otherPkg == basePkg.Types { - // local import - return typeName.Name() + + // ApplyConfiguration is in the same package + if otherPkg == basePkg.Types && universe.IsApplyConfigGenerated(typeInfo, otherPkg.Path()) { + return typeName.Name() + appendString + } + + // ApplyConfiguration is in different package + path, isAc := universe.GetApplyConfigPath(typeInfo, otherPkg.Path()) + alias := imports.NeedImport(path) + if !isAc { + appendString = "" } - alias := imports.NeedImport(loader.NonVendorPath(otherPkg.Path())) - return alias + "." + typeName.Name() + return alias + "." + typeName.Name() + appendString case *types.Basic: return typeInfo.String() case *types.Pointer: - return "*" + (&namingInfo{typeInfo: typeInfo.Elem()}).Syntax(basePkg, imports) + return "*" + (&namingInfo{typeInfo: typeInfo.Elem()}).Syntax(universe, basePkg, imports) case *types.Slice: - return "[]" + (&namingInfo{typeInfo: typeInfo.Elem()}).Syntax(basePkg, imports) + return "[]" + (&namingInfo{typeInfo: typeInfo.Elem()}).Syntax(universe, basePkg, imports) case *types.Map: return fmt.Sprintf( "map[%s]%s", - (&namingInfo{typeInfo: typeInfo.Key()}).Syntax(basePkg, imports), - (&namingInfo{typeInfo: typeInfo.Elem()}).Syntax(basePkg, imports)) + (&namingInfo{typeInfo: typeInfo.Key()}).Syntax(universe, basePkg, imports), + (&namingInfo{typeInfo: typeInfo.Elem()}).Syntax(universe, basePkg, imports)) + case *types.Interface: + return "interface{}" + case *types.Signature: + return typeInfo.String() default: - basePkg.AddError(fmt.Errorf("name requested for invalid type: %s", typeInfo)) return typeInfo.String() } } @@ -178,20 +98,242 @@ func (n *namingInfo) Syntax(basePkg *loader.Package, imports *importsList) strin // writing them to its codeWriter. type applyConfigurationMaker struct { pkg *loader.Package - *importsList + *util.ImportsList *codeWriter } // GenerateTypesFor makes makes apply configuration types for the given type, when appropriate -func (c *applyConfigurationMaker) GenerateTypesFor(root *loader.Package, info *markers.TypeInfo) { +func (c *applyConfigurationMaker) GenerateTypesFor(universe *Universe, root *loader.Package, info *markers.TypeInfo) { typeInfo := root.TypesInfo.TypeOf(info.RawSpec.Name) if typeInfo == types.Typ[types.Invalid] { root.AddError(loader.ErrFromNode(fmt.Errorf("unknown type: %s", info.Name), info.RawSpec)) } + if len(info.Fields) == 0 { + return + } - // TODO(jpbetz): Generate output here + c.Linef("// %sApplyConfiguration represents a declarative configuration of the %s type for use", info.Name, info.Name) + c.Linef("// with apply.") + c.Linef("type %sApplyConfiguration struct {", info.Name) + for _, field := range info.Fields { + fieldName := field.Name + fieldType := root.TypesInfo.TypeOf(field.RawField.Type) + fieldNamingInfo := namingInfo{typeInfo: fieldType} + fieldTypeString := fieldNamingInfo.Syntax(universe, root, c.ImportsList) + + if tags, ok := lookupJSONTags(field); ok { + if tags.inline { + c.Linef("%s %s `json:\"%s\"`", fieldName, fieldTypeString, tags.String()) + } else if isPointer(fieldNamingInfo.typeInfo) || isMap(fieldNamingInfo.typeInfo) || isList(fieldNamingInfo.typeInfo) { + tags.omitempty = true + c.Linef("%s %s `json:\"%s\"`", fieldName, fieldTypeString, tags.String()) + } else { + tags.omitempty = true + c.Linef("%s *%s `json:\"%s\"`", fieldName, fieldTypeString, tags.String()) + } + } + } + c.Linef("}") +} + +// These functions implement a specific interface as required by controller-runtime +func (c *applyConfigurationMaker) GenerateRootFunctions(universe *Universe, root *loader.Package, info *markers.TypeInfo) { + // For TypeMeta + c.Linef("func (ac * %[1]sApplyConfiguration) WithKind (value string) *%[1]sApplyConfiguration {", info.Name) + c.Linef("ac.Kind = &value") + c.Linef("return ac") + c.Linef("}") + + c.Linef("func (ac * %[1]sApplyConfiguration) WithAPIVersion (value string) *%[1]sApplyConfiguration {", info.Name) + c.Linef("ac.APIVersion = &value") + c.Linef("return ac") + c.Linef("}") + + metav1ac := c.NeedImport("k8s.io/client-go/applyconfigurations/meta/v1") + c.Linef("func (ac * %[1]sApplyConfiguration) ensureObjectMetaApplyConfigurationExists () {", info.Name) + c.Linef("if ac.ObjectMetaApplyConfiguration == nil {") + c.Linef("ac.ObjectMetaApplyConfiguration = &%s.ObjectMetaApplyConfiguration{}", metav1ac) + c.Linef("}") + c.Linef("}") + + // For ObjectMeta + c.Linef("func (ac * %[1]sApplyConfiguration) WithName(value string) *%[1]sApplyConfiguration {", info.Name) + c.Linef("ac.ensureObjectMetaApplyConfigurationExists()") + c.Linef("ac.Name = &value") + c.Linef("return ac") + c.Linef("}") + + c.Linef("func (ac * %[1]sApplyConfiguration) WithNamespace(value string) *%[1]sApplyConfiguration {", info.Name) + c.Linef("ac.Namespace = &value") + c.Linef("return ac") + c.Linef("}") + + c.Linef("func (ac * %[1]sApplyConfiguration) GetName() string {", info.Name) + c.Linef("ac.ensureObjectMetaApplyConfigurationExists()") + c.Linef("return *ac.Name") + c.Linef("}") + + c.Linef("func (ac * %[1]sApplyConfiguration) GetNamespace() string {", info.Name) + c.Linef("ac.ensureObjectMetaApplyConfigurationExists()") + c.Linef("return *ac.Namespace") + c.Linef("}") - c.Linef("type %sApplyConfiguration struct {}", info.Name) +} + +func (c *applyConfigurationMaker) GenerateStructConstructor(root *loader.Package, info *markers.TypeInfo) { + c.Linef("// %sApplyConfiguration represents a declarative configuration of the %s type for use", info.Name, info.Name) + c.Linef("// with apply.") + c.Linef("func %s() *%sApplyConfiguration {", info.Name, info.Name) + c.Linef("return &%sApplyConfiguration{}", info.Name) + c.Linef("}") +} + +func (c *applyConfigurationMaker) GenerateRootStructConstructor(root *loader.Package, info *markers.TypeInfo, clusterScope bool, group, version string) { + c.Linef("// %sApplyConfiguration represents a declarative configuration of the %s type for use", info.Name, info.Name) + c.Linef("// with apply.") + if clusterScope { + c.Linef("func %s(name string) *%sApplyConfiguration {", info.Name, info.Name) + } else { + c.Linef("func %s(name, namespace string) *%sApplyConfiguration {", info.Name, info.Name) + } + c.Linef("ac := &%sApplyConfiguration{}", info.Name) + c.Linef("ac.WithName(name)") + if !clusterScope { + c.Linef("ac.WithNamespace(namespace)") + } + c.Linef("ac.WithKind(\"%s\")", info.Name) + c.Linef("ac.WithAPIVersion(\"%s/%s\")", group, version) + c.Linef("return ac") + c.Linef("}") +} + +func isApplyConfig(t types.Type) bool { + switch typeInfo := t.(type) { + case *types.Named: + return isApplyConfig(typeInfo.Underlying()) + case *types.Struct: + return true + case *types.Pointer: + return isApplyConfig(typeInfo.Elem()) + default: + return false + } +} + +func isList(t types.Type) bool { + switch t.(type) { + case *types.Slice: + return true + default: + return false + } +} + +func isMap(t types.Type) bool { + switch t.(type) { + case *types.Map: + return true + default: + return false + } +} + +func isPointer(t types.Type) bool { + switch t.(type) { + case *types.Pointer: + return true + default: + return false + } +} + +func (c *applyConfigurationMaker) GenerateMemberSet(universe *Universe, field markers.FieldInfo, root *loader.Package, info *markers.TypeInfo) { + fieldType := root.TypesInfo.TypeOf(field.RawField.Type) + fieldNamingInfo := namingInfo{typeInfo: fieldType} + fieldTypeString := fieldNamingInfo.Syntax(universe, root, c.ImportsList) + + c.Linef("// With%s sets the %s field in the declarative configuration to the given value", field.Name, field.Name) + if isApplyConfig(fieldNamingInfo.typeInfo) && !isPointer(fieldNamingInfo.typeInfo) { + fieldTypeString = "*" + fieldTypeString + } + c.Linef("func (b *%sApplyConfiguration) With%s(value %s) *%sApplyConfiguration {", info.Name, field.Name, fieldTypeString, info.Name) + + if tags, ok := lookupJSONTags(field); ok { + if tags.inline { + c.Linef("if value != nil {") + c.Linef("b.%s = *value", field.Name) + c.Linef("}") + } else if isApplyConfig(fieldNamingInfo.typeInfo) { + c.Linef("b.%s = value", field.Name) + } else if isPointer(fieldNamingInfo.typeInfo) { + c.Linef("b.%s = value", field.Name) + } else { + c.Linef("b.%s = &value", field.Name) + } + } + c.Linef("return b") + c.Linef("}") +} + +func (c *applyConfigurationMaker) GenerateMemberSetForSlice(universe *Universe, field markers.FieldInfo, root *loader.Package, info *markers.TypeInfo) { + fieldType := root.TypesInfo.TypeOf(field.RawField.Type) + fieldNamingInfo := namingInfo{typeInfo: fieldType} + + sliceType := fieldType.(*types.Slice) + listVal := (&namingInfo{typeInfo: sliceType.Elem()}).Syntax(universe, root, c.ImportsList) + + c.Linef("// With%s sets the %s field in the declarative configuration to the given value", field.Name, field.Name) + c.Linef("func (b *%sApplyConfiguration) With%s(values ...%s) *%sApplyConfiguration {", info.Name, field.Name, listVal, info.Name) + + c.Linef("for i := range values {") + if isApplyConfig(fieldNamingInfo.typeInfo) || isPointer(fieldNamingInfo.typeInfo) { + c.Linef("if values[i] == nil {") + c.Linef("panic(\"nil value passed to With%s\")", field.Name) + c.Linef("}") + c.Linef("b.%s[1] = append(b.%s[1], values[i]", field.Name) + } + c.Linef("b.%[1]s = append(b.%[1]s, values[i])", field.Name) + c.Linef("}") + c.Linef("return b") + c.Linef("}") +} + +func (c *applyConfigurationMaker) GenerateMemberSetForMap(universe *Universe, field markers.FieldInfo, root *loader.Package, info *markers.TypeInfo) { + fieldType := root.TypesInfo.TypeOf(field.RawField.Type) + fieldNamingInfo := namingInfo{typeInfo: fieldType} + fieldTypeString := fieldNamingInfo.Syntax(universe, root, c.ImportsList) + + mapType := fieldType.(*types.Map) + k := (&namingInfo{typeInfo: mapType.Key()}).Syntax(universe, root, c.ImportsList) + v := (&namingInfo{typeInfo: mapType.Elem()}).Syntax(universe, root, c.ImportsList) + + c.Linef("// With%s sets the %s field in the declarative configuration to the given value", field.Name, field.Name) + if isApplyConfig(fieldNamingInfo.typeInfo) && !isPointer(fieldNamingInfo.typeInfo) { + fieldTypeString = "*" + fieldTypeString + } + c.Linef("func (b *%sApplyConfiguration) With%s(entries %s) *%sApplyConfiguration {", info.Name, field.Name, fieldTypeString, info.Name) + c.Linef("if b.%s == nil && len(entries) > 0 {", field.Name) + c.Linef("b.%s = make(map[%s]%s, len(entries))", field.Name, k, v) + c.Linef("}") + c.Linef("for k, v := range entries {") + c.Linef("b.%s[k] = v", field.Name) + c.Linef("}") + c.Linef("return b") + c.Linef("}") +} + +func (c *applyConfigurationMaker) GenerateListMapAlias(root *loader.Package, info *markers.TypeInfo) { + var listAlias = ` +// %[1]sList represents a listAlias of %[1]sApplyConfiguration. +type %[1]sList []*%[1]sApplyConfiguration +` + var mapAlias = ` +// %[1]sMap represents a map of %[1]sApplyConfiguration. +type %[1]sMap map[string]%[1]sApplyConfiguration +` + + c.Linef(listAlias, info.Name) + c.Linef(mapAlias, info.Name) } // shouldBeApplyConfiguration checks if we're supposed to make apply configurations for the given type. @@ -217,11 +359,6 @@ func shouldBeApplyConfiguration(pkg *loader.Package, info *markers.TypeInfo) boo lastType := typeInfo if _, isNamed := typeInfo.(*types.Named); isNamed { for underlyingType := typeInfo.Underlying(); underlyingType != lastType; lastType, underlyingType = underlyingType, underlyingType.Underlying() { - // aliases to other things besides basics need copy methods - // (basics can be straight-up shallow-copied) - if _, isBasic := underlyingType.(*types.Basic); !isBasic { - return true - } } } @@ -233,19 +370,20 @@ func shouldBeApplyConfiguration(pkg *loader.Package, info *markers.TypeInfo) boo if _, ok := excludeTypes[info.Name]; ok { // TODO(jpbetz): What to do here? return false } - var hasJsonTaggedMembers bool + var hasJSONTaggedMembers bool for _, field := range info.Fields { - if _, ok := lookupJsonTags(field); ok { - hasJsonTaggedMembers = true + if _, ok := lookupJSONTags(field); ok { + hasJSONTaggedMembers = true } } - return hasJsonTaggedMembers + return hasJSONTaggedMembers } var ( - rawExtension = "k8s.io/apimachinery/pkg/runtime/RawExtension" - unknown = "k8s.io/apimachinery/pkg/runtime/Unknown" + rawExtension = "k8s.io/apimachinery/pkg/runtime/RawExtension" + unknown = "k8s.io/apimachinery/pkg/runtime/Unknown" ) + // excludeTypes contains well known types that we do not generate apply configurations for. // Hard coding because we only have two, very specific types that serve a special purpose // in the type system here. diff --git a/pkg/applyconfigurations/zz_generated.markerhelp.go b/pkg/applyconfigurations/zz_generated.markerhelp.go index 84ef16fc2..9b76ea32f 100644 --- a/pkg/applyconfigurations/zz_generated.markerhelp.go +++ b/pkg/applyconfigurations/zz_generated.markerhelp.go @@ -1,7 +1,8 @@ +//go:build !ignore_autogenerated // +build !ignore_autogenerated /* -Copyright2020 The Kubernetes Authors. +Copyright2019 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. @@ -32,11 +33,11 @@ func (Generator) Help() *markers.DefinitionHelp { Details: "", }, FieldHelp: map[string]markers.DetailedHelp{ - "HeaderFile": markers.DetailedHelp{ + "HeaderFile": { Summary: "specifies the header text (e.g. license) to prepend to generated files.", Details: "", }, - "Year": markers.DetailedHelp{ + "Year": { Summary: "specifies the year to substitute for \" YEAR\" in the header file.", Details: "", }, diff --git a/pkg/crd/testdata/go.sum b/pkg/crd/testdata/go.sum index 576c77956..a600d92d5 100644 --- a/pkg/crd/testdata/go.sum +++ b/pkg/crd/testdata/go.sum @@ -149,6 +149,7 @@ gopkg.in/fsnotify.v1 v1.4.7/go.mod h1:Tz8NjZHkW78fSQdbUxIjBTcgA1z1m8ZHf0WmKUhAMy gopkg.in/inf.v0 v0.9.1 h1:73M5CoZyi3ZLMOyDlQh031Cx6N9NDJ2Vvfl76EDAgDc= gopkg.in/inf.v0 v0.9.1/go.mod h1:cWUDdTG/fYaXco+Dcufb5Vnc6Gp2YChqWtbxRZE0mXw= gopkg.in/tomb.v1 v1.0.0-20141024135613-dd632973f1e7/go.mod h1:dt/ZhP58zS4L8KSrWDmTeBkI65Dw0HsyUHuEVlX15mw= +gopkg.in/yaml.v2 v2.2.1 h1:mUhvW9EsL+naU5Q3cakzfE91YhliOondGd6ZrsDBHQE= gopkg.in/yaml.v2 v2.2.1/go.mod h1:hI93XBmqTisBFMUTm0b8Fm+jr3Dg1NNxqwp+5A1VGuI= gopkg.in/yaml.v2 v2.2.2/go.mod h1:hI93XBmqTisBFMUTm0b8Fm+jr3Dg1NNxqwp+5A1VGuI= gopkg.in/yaml.v2 v2.2.8 h1:obN1ZagJSUGI0Ek/LBmuj4SNLPfIny3KsKFopxRdj10= diff --git a/pkg/deepcopy/testdata/go.sum b/pkg/deepcopy/testdata/go.sum index 965a7973c..eb76c6bed 100644 --- a/pkg/deepcopy/testdata/go.sum +++ b/pkg/deepcopy/testdata/go.sum @@ -151,6 +151,7 @@ gopkg.in/inf.v0 v0.9.1 h1:73M5CoZyi3ZLMOyDlQh031Cx6N9NDJ2Vvfl76EDAgDc= gopkg.in/inf.v0 v0.9.1/go.mod h1:cWUDdTG/fYaXco+Dcufb5Vnc6Gp2YChqWtbxRZE0mXw= gopkg.in/tomb.v1 v1.0.0-20141024135613-dd632973f1e7/go.mod h1:dt/ZhP58zS4L8KSrWDmTeBkI65Dw0HsyUHuEVlX15mw= gopkg.in/yaml.v2 v2.2.1/go.mod h1:hI93XBmqTisBFMUTm0b8Fm+jr3Dg1NNxqwp+5A1VGuI= +gopkg.in/yaml.v2 v2.2.2 h1:ZCJp+EgiOT7lHqUV2J862kp8Qj64Jo6az82+3Td9dZw= gopkg.in/yaml.v2 v2.2.2/go.mod h1:hI93XBmqTisBFMUTm0b8Fm+jr3Dg1NNxqwp+5A1VGuI= gopkg.in/yaml.v2 v2.2.8 h1:obN1ZagJSUGI0Ek/LBmuj4SNLPfIny3KsKFopxRdj10= gopkg.in/yaml.v2 v2.2.8/go.mod h1:hI93XBmqTisBFMUTm0b8Fm+jr3Dg1NNxqwp+5A1VGuI= diff --git a/pkg/genall/output.go b/pkg/genall/output.go index b5a09e169..5c835b72f 100644 --- a/pkg/genall/output.go +++ b/pkg/genall/output.go @@ -155,6 +155,12 @@ func (o OutputArtifacts) Open(pkg *loader.Package, itemPath string) (io.WriteClo return nil, fmt.Errorf("cannot output to a package with no path on disk") } outDir := filepath.Dir(pkg.CompiledGoFiles[0]) + if _, err := os.Stat(outDir); os.IsNotExist(err) { + if err := os.MkdirAll(outDir, os.ModePerm); err != nil { + return nil, err + } + } + outPath := filepath.Join(outDir, itemPath) return os.Create(outPath) } diff --git a/pkg/util/util.go b/pkg/util/util.go new file mode 100644 index 000000000..5a3e694ef --- /dev/null +++ b/pkg/util/util.go @@ -0,0 +1,139 @@ +/* +Copyright 2022 The Kubernetes Authors. + +Licensed under the Apache License, Version 2.0 (the "License"); +you may not use this file except in compliance with the License. +You may obtain a copy of the License at + + http://www.apache.org/licenses/LICENSE-2.0 + +Unless required by applicable law or agreed to in writing, software +distributed under the License is distributed on an "AS IS" BASIS, +WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +See the License for the specific language governing permissions and +limitations under the License. +*/ + +package util + +import ( + "fmt" + "io" + "path" + "strings" + "unicode" + "unicode/utf8" + + "sigs.k8s.io/controller-tools/pkg/loader" +) + +// importsList keeps track of required imports, automatically assigning aliases +// to import statement. +type ImportsList struct { + byPath map[string]string + byAlias map[string]string + + pkg *loader.Package +} + +func NewImportsList(root *loader.Package) *ImportsList { + importsList := &ImportsList{ + byPath: make(map[string]string), + byAlias: make(map[string]string), + pkg: root, + } + + // avoid confusing aliases by "reserving" the root package's name as an alias + importsList.byAlias[root.Name] = "" + + return importsList +} + +// NeedImport marks that the given package is needed in the list of imports, +// returning the ident (import alias) that should be used to reference the package. +func (l *ImportsList) NeedImport(importPath string) string { + // we get an actual path from Package, which might include vendored + // packages if running on a package in vendor. + if ind := strings.LastIndex(importPath, "/vendor/"); ind != -1 { + importPath = importPath[ind+8: /* len("/vendor/") */] + } + + // check to see if we've already assigned an alias, and just return that. + alias, exists := l.byPath[importPath] + if exists { + return alias + } + + // otherwise, calculate an import alias by joining path parts till we get something unique + restPath, nextWord := path.Split(importPath) + + for otherPath, exists := "", true; exists && otherPath != importPath; otherPath, exists = l.byAlias[alias] { + if restPath == "" { + // do something else to disambiguate if we're run out of parts and + // still have duplicates, somehow + alias += "x" + } + + // can't have a first digit, per Go identifier rules, so just skip them + for firstRune, runeLen := utf8.DecodeRuneInString(nextWord); unicode.IsDigit(firstRune); firstRune, runeLen = utf8.DecodeRuneInString(nextWord) { + nextWord = nextWord[runeLen:] + } + + // make a valid identifier by replacing "bad" characters with underscores + nextWord = strings.Map(func(r rune) rune { + if unicode.IsLetter(r) || unicode.IsDigit(r) || r == '_' { + return r + } + return '_' + }, nextWord) + + alias = nextWord + alias + if len(restPath) > 0 { + restPath, nextWord = path.Split(restPath[:len(restPath)-1] /* chop off final slash */) + } + } + + l.byPath[importPath] = alias + l.byAlias[alias] = importPath + return alias +} + +// ImportSpecs returns a string form of each import spec +// (i.e. `alias "path/to/import"). Aliases are only present +// when they don't match the package name. +func (l *ImportsList) ImportSpecs() []string { + res := make([]string, 0, len(l.byPath)) + for importPath, alias := range l.byPath { + pkg := l.pkg.Imports()[importPath] + if pkg != nil && pkg.Name == alias { + // don't print if alias is the same as package name + // (we've already taken care of duplicates). + res = append(res, fmt.Sprintf("%q", importPath)) + } else { + res = append(res, fmt.Sprintf("%s %q", alias, importPath)) + } + } + return res +} + +// writeHeader writes out the build tag, package declaration, and imports +func WriteHeader(pkg *loader.Package, out io.Writer, packageName string, imports *ImportsList, headerText string) { + // NB(directxman12): blank line after build tags to distinguish them from comments + _, err := fmt.Fprintf(out, `// +build !ignore_autogenerated + +%[3]s + +// Code generated by controller-gen. DO NOT EDIT. + +package %[1]s + +import ( +%[2]s +) + +`, packageName, strings.Join(imports.ImportSpecs(), "\n"), headerText) + if err != nil { + pkg.AddError(err) + } + +}