move hack to client
This commit is contained in:
76
vendor/k8s.io/kube-openapi/cmd/openapi-gen/args/args.go
generated
vendored
76
vendor/k8s.io/kube-openapi/cmd/openapi-gen/args/args.go
generated
vendored
@@ -1,76 +0,0 @@
|
||||
/*
|
||||
Copyright 2018 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 args
|
||||
|
||||
import (
|
||||
"fmt"
|
||||
"path/filepath"
|
||||
|
||||
"github.com/spf13/pflag"
|
||||
"k8s.io/gengo/args"
|
||||
)
|
||||
|
||||
// CustomArgs is used by the gengo framework to pass args specific to this generator.
|
||||
type CustomArgs struct {
|
||||
// ReportFilename is added to CustomArgs for specifying name of report file used
|
||||
// by API linter. If specified, API rule violations will be printed to report file.
|
||||
// Otherwise default value "-" will be used which indicates stdout.
|
||||
ReportFilename string
|
||||
}
|
||||
|
||||
// NewDefaults returns default arguments for the generator. Returning the arguments instead
|
||||
// of using default flag parsing allows registering custom arguments afterwards
|
||||
func NewDefaults() (*args.GeneratorArgs, *CustomArgs) {
|
||||
// Default() sets a couple of flag default values for example the boilerplate.
|
||||
// WithoutDefaultFlagParsing() disables implicit addition of command line flags and parsing,
|
||||
// which allows registering custom arguments afterwards
|
||||
genericArgs := args.Default().WithoutDefaultFlagParsing()
|
||||
genericArgs.GoHeaderFilePath = filepath.Join(args.DefaultSourceTree(), "k8s.io/kube-openapi/boilerplate/boilerplate.go.txt")
|
||||
|
||||
customArgs := &CustomArgs{}
|
||||
genericArgs.CustomArgs = customArgs
|
||||
|
||||
// Default value for report filename is "-", which stands for stdout
|
||||
customArgs.ReportFilename = "-"
|
||||
// Default value for output file base name
|
||||
genericArgs.OutputFileBaseName = "openapi_generated"
|
||||
|
||||
return genericArgs, customArgs
|
||||
}
|
||||
|
||||
// AddFlags add the generator flags to the flag set.
|
||||
func (c *CustomArgs) AddFlags(fs *pflag.FlagSet) {
|
||||
fs.StringVarP(&c.ReportFilename, "report-filename", "r", c.ReportFilename, "Name of report file used by API linter to print API violations. Default \"-\" stands for standard output. NOTE that if valid filename other than \"-\" is specified, API linter won't return error on detected API violations. This allows further check of existing API violations without stopping the OpenAPI generation toolchain.")
|
||||
}
|
||||
|
||||
// Validate checks the given arguments.
|
||||
func Validate(genericArgs *args.GeneratorArgs) error {
|
||||
c, ok := genericArgs.CustomArgs.(*CustomArgs)
|
||||
if !ok {
|
||||
return fmt.Errorf("input arguments don't contain valid custom arguments")
|
||||
}
|
||||
if len(c.ReportFilename) == 0 {
|
||||
return fmt.Errorf("report filename cannot be empty. specify a valid filename or use \"-\" for stdout")
|
||||
}
|
||||
if len(genericArgs.OutputFileBaseName) == 0 {
|
||||
return fmt.Errorf("output file base name cannot be empty")
|
||||
}
|
||||
if len(genericArgs.OutputPackagePath) == 0 {
|
||||
return fmt.Errorf("output package cannot be empty")
|
||||
}
|
||||
return nil
|
||||
}
|
192
vendor/k8s.io/kube-openapi/pkg/common/common.go
generated
vendored
192
vendor/k8s.io/kube-openapi/pkg/common/common.go
generated
vendored
@@ -1,192 +0,0 @@
|
||||
/*
|
||||
Copyright 2016 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 common
|
||||
|
||||
import (
|
||||
"net/http"
|
||||
"strings"
|
||||
|
||||
"github.com/emicklei/go-restful"
|
||||
"github.com/go-openapi/spec"
|
||||
)
|
||||
|
||||
const (
|
||||
// TODO: Make this configurable.
|
||||
ExtensionPrefix = "x-kubernetes-"
|
||||
ExtensionV2Schema = ExtensionPrefix + "v2-schema"
|
||||
)
|
||||
|
||||
// OpenAPIDefinition describes single type. Normally these definitions are auto-generated using gen-openapi.
|
||||
type OpenAPIDefinition struct {
|
||||
Schema spec.Schema
|
||||
Dependencies []string
|
||||
}
|
||||
|
||||
type ReferenceCallback func(path string) spec.Ref
|
||||
|
||||
// GetOpenAPIDefinitions is collection of all definitions.
|
||||
type GetOpenAPIDefinitions func(ReferenceCallback) map[string]OpenAPIDefinition
|
||||
|
||||
// OpenAPIDefinitionGetter gets openAPI definitions for a given type. If a type implements this interface,
|
||||
// the definition returned by it will be used, otherwise the auto-generated definitions will be used. See
|
||||
// GetOpenAPITypeFormat for more information about trade-offs of using this interface or GetOpenAPITypeFormat method when
|
||||
// possible.
|
||||
type OpenAPIDefinitionGetter interface {
|
||||
OpenAPIDefinition() *OpenAPIDefinition
|
||||
}
|
||||
|
||||
type OpenAPIV3DefinitionGetter interface {
|
||||
OpenAPIV3Definition() *OpenAPIDefinition
|
||||
}
|
||||
|
||||
type PathHandler interface {
|
||||
Handle(path string, handler http.Handler)
|
||||
}
|
||||
|
||||
// Config is set of configuration for openAPI spec generation.
|
||||
type Config struct {
|
||||
// List of supported protocols such as https, http, etc.
|
||||
ProtocolList []string
|
||||
|
||||
// Info is general information about the API.
|
||||
Info *spec.Info
|
||||
|
||||
// DefaultResponse will be used if an operation does not have any responses listed. It
|
||||
// will show up as ... "responses" : {"default" : $DefaultResponse} in the spec.
|
||||
DefaultResponse *spec.Response
|
||||
|
||||
// ResponseDefinitions will be added to "responses" under the top-level swagger object. This is an object
|
||||
// that holds responses definitions that can be used across operations. This property does not define
|
||||
// global responses for all operations. For more info please refer:
|
||||
// https://github.com/OAI/OpenAPI-Specification/blob/master/versions/2.0.md#fixed-fields
|
||||
ResponseDefinitions map[string]spec.Response
|
||||
|
||||
// CommonResponses will be added as a response to all operation specs. This is a good place to add common
|
||||
// responses such as authorization failed.
|
||||
CommonResponses map[int]spec.Response
|
||||
|
||||
// List of webservice's path prefixes to ignore
|
||||
IgnorePrefixes []string
|
||||
|
||||
// OpenAPIDefinitions should provide definition for all models used by routes. Failure to provide this map
|
||||
// or any of the models will result in spec generation failure.
|
||||
GetDefinitions GetOpenAPIDefinitions
|
||||
|
||||
// GetOperationIDAndTags returns operation id and tags for a restful route. It is an optional function to customize operation IDs.
|
||||
GetOperationIDAndTags func(r *restful.Route) (string, []string, error)
|
||||
|
||||
// GetDefinitionName returns a friendly name for a definition base on the serving path. parameter `name` is the full name of the definition.
|
||||
// It is an optional function to customize model names.
|
||||
GetDefinitionName func(name string) (string, spec.Extensions)
|
||||
|
||||
// PostProcessSpec runs after the spec is ready to serve. It allows a final modification to the spec before serving.
|
||||
PostProcessSpec func(*spec.Swagger) (*spec.Swagger, error)
|
||||
|
||||
// SecurityDefinitions is list of all security definitions for OpenAPI service. If this is not nil, the user of config
|
||||
// is responsible to provide DefaultSecurity and (maybe) add unauthorized response to CommonResponses.
|
||||
SecurityDefinitions *spec.SecurityDefinitions
|
||||
|
||||
// DefaultSecurity for all operations. This will pass as spec.SwaggerProps.Security to OpenAPI.
|
||||
// For most cases, this will be list of acceptable definitions in SecurityDefinitions.
|
||||
DefaultSecurity []map[string][]string
|
||||
}
|
||||
|
||||
var schemaTypeFormatMap = map[string][]string{
|
||||
"uint": {"integer", "int32"},
|
||||
"uint8": {"integer", "byte"},
|
||||
"uint16": {"integer", "int32"},
|
||||
"uint32": {"integer", "int64"},
|
||||
"uint64": {"integer", "int64"},
|
||||
"int": {"integer", "int32"},
|
||||
"int8": {"integer", "byte"},
|
||||
"int16": {"integer", "int32"},
|
||||
"int32": {"integer", "int32"},
|
||||
"int64": {"integer", "int64"},
|
||||
"byte": {"integer", "byte"},
|
||||
"float64": {"number", "double"},
|
||||
"float32": {"number", "float"},
|
||||
"bool": {"boolean", ""},
|
||||
"time.Time": {"string", "date-time"},
|
||||
"string": {"string", ""},
|
||||
"integer": {"integer", ""},
|
||||
"number": {"number", ""},
|
||||
"boolean": {"boolean", ""},
|
||||
"[]byte": {"string", "byte"}, // base64 encoded characters
|
||||
"interface{}": {"object", ""},
|
||||
}
|
||||
|
||||
// This function is a reference for converting go (or any custom type) to a simple open API type,format pair. There are
|
||||
// two ways to customize spec for a type. If you add it here, a type will be converted to a simple type and the type
|
||||
// comment (the comment that is added before type definition) will be lost. The spec will still have the property
|
||||
// comment. The second way is to implement OpenAPIDefinitionGetter interface. That function can customize the spec (so
|
||||
// the spec does not need to be simple type,format) or can even return a simple type,format (e.g. IntOrString). For simple
|
||||
// type formats, the benefit of adding OpenAPIDefinitionGetter interface is to keep both type and property documentation.
|
||||
// Example:
|
||||
// type Sample struct {
|
||||
// ...
|
||||
// // port of the server
|
||||
// port IntOrString
|
||||
// ...
|
||||
// }
|
||||
// // IntOrString documentation...
|
||||
// type IntOrString { ... }
|
||||
//
|
||||
// Adding IntOrString to this function:
|
||||
// "port" : {
|
||||
// format: "string",
|
||||
// type: "int-or-string",
|
||||
// Description: "port of the server"
|
||||
// }
|
||||
//
|
||||
// Implement OpenAPIDefinitionGetter for IntOrString:
|
||||
//
|
||||
// "port" : {
|
||||
// $Ref: "#/definitions/IntOrString"
|
||||
// Description: "port of the server"
|
||||
// }
|
||||
// ...
|
||||
// definitions:
|
||||
// {
|
||||
// "IntOrString": {
|
||||
// format: "string",
|
||||
// type: "int-or-string",
|
||||
// Description: "IntOrString documentation..." // new
|
||||
// }
|
||||
// }
|
||||
//
|
||||
func GetOpenAPITypeFormat(typeName string) (string, string) {
|
||||
mapped, ok := schemaTypeFormatMap[typeName]
|
||||
if !ok {
|
||||
return "", ""
|
||||
}
|
||||
return mapped[0], mapped[1]
|
||||
}
|
||||
|
||||
func EscapeJsonPointer(p string) string {
|
||||
// Escaping reference name using rfc6901
|
||||
p = strings.Replace(p, "~", "~0", -1)
|
||||
p = strings.Replace(p, "/", "~1", -1)
|
||||
return p
|
||||
}
|
||||
|
||||
func EmbedOpenAPIDefinitionIntoV2Extension(main OpenAPIDefinition, embedded OpenAPIDefinition) OpenAPIDefinition {
|
||||
if main.Schema.Extensions == nil {
|
||||
main.Schema.Extensions = make(map[string]interface{})
|
||||
}
|
||||
main.Schema.Extensions[ExtensionV2Schema] = embedded.Schema
|
||||
return main
|
||||
}
|
19
vendor/k8s.io/kube-openapi/pkg/common/doc.go
generated
vendored
19
vendor/k8s.io/kube-openapi/pkg/common/doc.go
generated
vendored
@@ -1,19 +0,0 @@
|
||||
/*
|
||||
Copyright 2016 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 common holds shared code and types between open API code
|
||||
// generator and spec generator.
|
||||
package common
|
49
vendor/k8s.io/kube-openapi/pkg/generators/README.md
generated
vendored
49
vendor/k8s.io/kube-openapi/pkg/generators/README.md
generated
vendored
@@ -1,49 +0,0 @@
|
||||
# Generate OpenAPI definitions
|
||||
|
||||
- To generate definition for a specific type or package add "+k8s:openapi-gen=true" tag to the type/package comment lines.
|
||||
- To exclude a type or a member from a tagged package/type, add "+k8s:openapi-gen=false" tag to the comment lines.
|
||||
|
||||
# OpenAPI Extensions
|
||||
|
||||
OpenAPI spec can have extensions on types. To define one or more extensions on a type or its member
|
||||
add `+k8s:openapi-gen=x-kubernetes-$NAME:$VALUE` to the comment lines before type/member. A type/member can
|
||||
have multiple extensions. The rest of the line in the comment will be used as $VALUE so there is no need to
|
||||
escape or quote the value string. Extensions can be used to pass more information to client generators or
|
||||
documentation generators. For example a type might have a friendly name to be displayed in documentation or
|
||||
being used in a client's fluent interface.
|
||||
|
||||
# Custom OpenAPI type definitions
|
||||
|
||||
Custom types which otherwise don't map directly to OpenAPI can override their
|
||||
OpenAPI definition by implementing a function named "OpenAPIDefinition" with
|
||||
the following signature:
|
||||
|
||||
```go
|
||||
import openapi "k8s.io/kube-openapi/pkg/common"
|
||||
|
||||
// ...
|
||||
|
||||
type Time struct {
|
||||
time.Time
|
||||
}
|
||||
|
||||
func (_ Time) OpenAPIDefinition() openapi.OpenAPIDefinition {
|
||||
return openapi.OpenAPIDefinition{
|
||||
Schema: spec.Schema{
|
||||
SchemaProps: spec.SchemaProps{
|
||||
Type: []string{"string"},
|
||||
Format: "date-time",
|
||||
},
|
||||
},
|
||||
}
|
||||
}
|
||||
```
|
||||
|
||||
Alternatively, the type can avoid the "openapi" import by defining the following
|
||||
methods. The following example produces the same OpenAPI definition as the
|
||||
example above:
|
||||
|
||||
```go
|
||||
func (_ Time) OpenAPISchemaType() []string { return []string{"string"} }
|
||||
func (_ Time) OpenAPISchemaFormat() string { return "date-time" }
|
||||
```
|
220
vendor/k8s.io/kube-openapi/pkg/generators/api_linter.go
generated
vendored
220
vendor/k8s.io/kube-openapi/pkg/generators/api_linter.go
generated
vendored
@@ -1,220 +0,0 @@
|
||||
/*
|
||||
Copyright 2018 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 generators
|
||||
|
||||
import (
|
||||
"bytes"
|
||||
"fmt"
|
||||
"io"
|
||||
"io/ioutil"
|
||||
"os"
|
||||
"sort"
|
||||
|
||||
"k8s.io/kube-openapi/pkg/generators/rules"
|
||||
|
||||
"k8s.io/gengo/generator"
|
||||
"k8s.io/gengo/types"
|
||||
"k8s.io/klog/v2"
|
||||
)
|
||||
|
||||
const apiViolationFileType = "api-violation"
|
||||
|
||||
type apiViolationFile struct {
|
||||
// Since our file actually is unrelated to the package structure, use a
|
||||
// path that hasn't been mangled by the framework.
|
||||
unmangledPath string
|
||||
}
|
||||
|
||||
func (a apiViolationFile) AssembleFile(f *generator.File, path string) error {
|
||||
path = a.unmangledPath
|
||||
klog.V(2).Infof("Assembling file %q", path)
|
||||
if path == "-" {
|
||||
_, err := io.Copy(os.Stdout, &f.Body)
|
||||
return err
|
||||
}
|
||||
|
||||
output, err := os.Create(path)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
defer output.Close()
|
||||
_, err = io.Copy(output, &f.Body)
|
||||
return err
|
||||
}
|
||||
|
||||
func (a apiViolationFile) VerifyFile(f *generator.File, path string) error {
|
||||
if path == "-" {
|
||||
// Nothing to verify against.
|
||||
return nil
|
||||
}
|
||||
path = a.unmangledPath
|
||||
|
||||
formatted := f.Body.Bytes()
|
||||
existing, err := ioutil.ReadFile(path)
|
||||
if err != nil {
|
||||
return fmt.Errorf("unable to read file %q for comparison: %v", path, err)
|
||||
}
|
||||
if bytes.Compare(formatted, existing) == 0 {
|
||||
return nil
|
||||
}
|
||||
|
||||
// Be nice and find the first place where they differ
|
||||
// (Copied from gengo's default file type)
|
||||
i := 0
|
||||
for i < len(formatted) && i < len(existing) && formatted[i] == existing[i] {
|
||||
i++
|
||||
}
|
||||
eDiff, fDiff := existing[i:], formatted[i:]
|
||||
if len(eDiff) > 100 {
|
||||
eDiff = eDiff[:100]
|
||||
}
|
||||
if len(fDiff) > 100 {
|
||||
fDiff = fDiff[:100]
|
||||
}
|
||||
return fmt.Errorf("output for %q differs; first existing/expected diff: \n %q\n %q", path, string(eDiff), string(fDiff))
|
||||
}
|
||||
|
||||
func newAPIViolationGen() *apiViolationGen {
|
||||
return &apiViolationGen{
|
||||
linter: newAPILinter(),
|
||||
}
|
||||
}
|
||||
|
||||
type apiViolationGen struct {
|
||||
generator.DefaultGen
|
||||
|
||||
linter *apiLinter
|
||||
}
|
||||
|
||||
func (v *apiViolationGen) FileType() string { return apiViolationFileType }
|
||||
func (v *apiViolationGen) Filename() string {
|
||||
return "this file is ignored by the file assembler"
|
||||
}
|
||||
|
||||
func (v *apiViolationGen) GenerateType(c *generator.Context, t *types.Type, w io.Writer) error {
|
||||
klog.V(5).Infof("validating API rules for type %v", t)
|
||||
if err := v.linter.validate(t); err != nil {
|
||||
return err
|
||||
}
|
||||
return nil
|
||||
}
|
||||
|
||||
// Finalize prints the API rule violations to report file (if specified from
|
||||
// arguments) or stdout (default)
|
||||
func (v *apiViolationGen) Finalize(c *generator.Context, w io.Writer) error {
|
||||
// NOTE: we don't return error here because we assume that the report file will
|
||||
// get evaluated afterwards to determine if error should be raised. For example,
|
||||
// you can have make rules that compare the report file with existing known
|
||||
// violations (whitelist) and determine no error if no change is detected.
|
||||
v.linter.report(w)
|
||||
return nil
|
||||
}
|
||||
|
||||
// apiLinter is the framework hosting multiple API rules and recording API rule
|
||||
// violations
|
||||
type apiLinter struct {
|
||||
// API rules that implement APIRule interface and output API rule violations
|
||||
rules []APIRule
|
||||
violations []apiViolation
|
||||
}
|
||||
|
||||
// newAPILinter creates an apiLinter object with API rules in package rules. Please
|
||||
// add APIRule here when new API rule is implemented.
|
||||
func newAPILinter() *apiLinter {
|
||||
return &apiLinter{
|
||||
rules: []APIRule{
|
||||
&rules.NamesMatch{},
|
||||
&rules.OmitEmptyMatchCase{},
|
||||
&rules.ListTypeMissing{},
|
||||
},
|
||||
}
|
||||
}
|
||||
|
||||
// apiViolation uniquely identifies single API rule violation
|
||||
type apiViolation struct {
|
||||
// Name of rule from APIRule.Name()
|
||||
rule string
|
||||
|
||||
packageName string
|
||||
typeName string
|
||||
|
||||
// Optional: name of field that violates API rule. Empty fieldName implies that
|
||||
// the entire type violates the rule.
|
||||
field string
|
||||
}
|
||||
|
||||
// apiViolations implements sort.Interface for []apiViolation based on the fields: rule,
|
||||
// packageName, typeName and field.
|
||||
type apiViolations []apiViolation
|
||||
|
||||
func (a apiViolations) Len() int { return len(a) }
|
||||
func (a apiViolations) Swap(i, j int) { a[i], a[j] = a[j], a[i] }
|
||||
func (a apiViolations) Less(i, j int) bool {
|
||||
if a[i].rule != a[j].rule {
|
||||
return a[i].rule < a[j].rule
|
||||
}
|
||||
if a[i].packageName != a[j].packageName {
|
||||
return a[i].packageName < a[j].packageName
|
||||
}
|
||||
if a[i].typeName != a[j].typeName {
|
||||
return a[i].typeName < a[j].typeName
|
||||
}
|
||||
return a[i].field < a[j].field
|
||||
}
|
||||
|
||||
// APIRule is the interface for validating API rule on Go types
|
||||
type APIRule interface {
|
||||
// Validate evaluates API rule on type t and returns a list of field names in
|
||||
// the type that violate the rule. Empty field name [""] implies the entire
|
||||
// type violates the rule.
|
||||
Validate(t *types.Type) ([]string, error)
|
||||
|
||||
// Name returns the name of APIRule
|
||||
Name() string
|
||||
}
|
||||
|
||||
// validate runs all API rules on type t and records any API rule violation
|
||||
func (l *apiLinter) validate(t *types.Type) error {
|
||||
for _, r := range l.rules {
|
||||
klog.V(5).Infof("validating API rule %v for type %v", r.Name(), t)
|
||||
fields, err := r.Validate(t)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
for _, field := range fields {
|
||||
l.violations = append(l.violations, apiViolation{
|
||||
rule: r.Name(),
|
||||
packageName: t.Name.Package,
|
||||
typeName: t.Name.Name,
|
||||
field: field,
|
||||
})
|
||||
}
|
||||
}
|
||||
return nil
|
||||
}
|
||||
|
||||
// report prints any API rule violation to writer w and returns error if violation exists
|
||||
func (l *apiLinter) report(w io.Writer) error {
|
||||
sort.Sort(apiViolations(l.violations))
|
||||
for _, v := range l.violations {
|
||||
fmt.Fprintf(w, "API rule violation: %s,%s,%s,%s\n", v.rule, v.packageName, v.typeName, v.field)
|
||||
}
|
||||
if len(l.violations) > 0 {
|
||||
return fmt.Errorf("API rule violations exist")
|
||||
}
|
||||
return nil
|
||||
}
|
91
vendor/k8s.io/kube-openapi/pkg/generators/config.go
generated
vendored
91
vendor/k8s.io/kube-openapi/pkg/generators/config.go
generated
vendored
@@ -1,91 +0,0 @@
|
||||
/*
|
||||
Copyright 2018 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 generators
|
||||
|
||||
import (
|
||||
"fmt"
|
||||
"path/filepath"
|
||||
|
||||
"k8s.io/gengo/args"
|
||||
"k8s.io/gengo/generator"
|
||||
"k8s.io/gengo/namer"
|
||||
"k8s.io/gengo/types"
|
||||
"k8s.io/klog/v2"
|
||||
|
||||
generatorargs "k8s.io/kube-openapi/cmd/openapi-gen/args"
|
||||
)
|
||||
|
||||
type identityNamer struct{}
|
||||
|
||||
func (_ identityNamer) Name(t *types.Type) string {
|
||||
return t.Name.String()
|
||||
}
|
||||
|
||||
var _ namer.Namer = identityNamer{}
|
||||
|
||||
// NameSystems returns the name system used by the generators in this package.
|
||||
func NameSystems() namer.NameSystems {
|
||||
return namer.NameSystems{
|
||||
"raw": namer.NewRawNamer("", nil),
|
||||
"sorting_namer": identityNamer{},
|
||||
}
|
||||
}
|
||||
|
||||
// DefaultNameSystem returns the default name system for ordering the types to be
|
||||
// processed by the generators in this package.
|
||||
func DefaultNameSystem() string {
|
||||
return "sorting_namer"
|
||||
}
|
||||
|
||||
func Packages(context *generator.Context, arguments *args.GeneratorArgs) generator.Packages {
|
||||
boilerplate, err := arguments.LoadGoBoilerplate()
|
||||
if err != nil {
|
||||
klog.Fatalf("Failed loading boilerplate: %v", err)
|
||||
}
|
||||
header := append([]byte(fmt.Sprintf("// +build !%s\n\n", arguments.GeneratedBuildTag)), boilerplate...)
|
||||
header = append(header, []byte(
|
||||
`
|
||||
// This file was autogenerated by openapi-gen. Do not edit it manually!
|
||||
|
||||
`)...)
|
||||
|
||||
reportPath := "-"
|
||||
if customArgs, ok := arguments.CustomArgs.(*generatorargs.CustomArgs); ok {
|
||||
reportPath = customArgs.ReportFilename
|
||||
}
|
||||
context.FileTypes[apiViolationFileType] = apiViolationFile{
|
||||
unmangledPath: reportPath,
|
||||
}
|
||||
|
||||
return generator.Packages{
|
||||
&generator.DefaultPackage{
|
||||
PackageName: filepath.Base(arguments.OutputPackagePath),
|
||||
PackagePath: arguments.OutputPackagePath,
|
||||
HeaderText: header,
|
||||
GeneratorFunc: func(c *generator.Context) (generators []generator.Generator) {
|
||||
return []generator.Generator{
|
||||
newOpenAPIGen(
|
||||
arguments.OutputFileBaseName,
|
||||
arguments.OutputPackagePath,
|
||||
),
|
||||
newAPIViolationGen(),
|
||||
}
|
||||
},
|
||||
FilterFunc: apiTypeFilterFunc,
|
||||
},
|
||||
}
|
||||
}
|
198
vendor/k8s.io/kube-openapi/pkg/generators/extension.go
generated
vendored
198
vendor/k8s.io/kube-openapi/pkg/generators/extension.go
generated
vendored
@@ -1,198 +0,0 @@
|
||||
/*
|
||||
Copyright 2018 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 generators
|
||||
|
||||
import (
|
||||
"fmt"
|
||||
"sort"
|
||||
"strings"
|
||||
|
||||
"k8s.io/gengo/examples/set-gen/sets"
|
||||
"k8s.io/gengo/types"
|
||||
)
|
||||
|
||||
const extensionPrefix = "x-kubernetes-"
|
||||
|
||||
// extensionAttributes encapsulates common traits for particular extensions.
|
||||
type extensionAttributes struct {
|
||||
xName string
|
||||
kind types.Kind
|
||||
allowedValues sets.String
|
||||
enforceArray bool
|
||||
}
|
||||
|
||||
// Extension tag to openapi extension attributes
|
||||
var tagToExtension = map[string]extensionAttributes{
|
||||
"patchMergeKey": {
|
||||
xName: "x-kubernetes-patch-merge-key",
|
||||
kind: types.Slice,
|
||||
},
|
||||
"patchStrategy": {
|
||||
xName: "x-kubernetes-patch-strategy",
|
||||
kind: types.Slice,
|
||||
allowedValues: sets.NewString("merge", "retainKeys"),
|
||||
},
|
||||
"listMapKey": {
|
||||
xName: "x-kubernetes-list-map-keys",
|
||||
kind: types.Slice,
|
||||
enforceArray: true,
|
||||
},
|
||||
"listType": {
|
||||
xName: "x-kubernetes-list-type",
|
||||
kind: types.Slice,
|
||||
allowedValues: sets.NewString("atomic", "set", "map"),
|
||||
},
|
||||
"mapType": {
|
||||
xName: "x-kubernetes-map-type",
|
||||
kind: types.Map,
|
||||
allowedValues: sets.NewString("atomic", "granular"),
|
||||
},
|
||||
"structType": {
|
||||
xName: "x-kubernetes-map-type",
|
||||
kind: types.Struct,
|
||||
allowedValues: sets.NewString("atomic", "granular"),
|
||||
},
|
||||
}
|
||||
|
||||
// Extension encapsulates information necessary to generate an OpenAPI extension.
|
||||
type extension struct {
|
||||
idlTag string // Example: listType
|
||||
xName string // Example: x-kubernetes-list-type
|
||||
values []string // Example: [atomic]
|
||||
}
|
||||
|
||||
func (e extension) hasAllowedValues() bool {
|
||||
return tagToExtension[e.idlTag].allowedValues.Len() > 0
|
||||
}
|
||||
|
||||
func (e extension) allowedValues() sets.String {
|
||||
return tagToExtension[e.idlTag].allowedValues
|
||||
}
|
||||
|
||||
func (e extension) hasKind() bool {
|
||||
return len(tagToExtension[e.idlTag].kind) > 0
|
||||
}
|
||||
|
||||
func (e extension) kind() types.Kind {
|
||||
return tagToExtension[e.idlTag].kind
|
||||
}
|
||||
|
||||
func (e extension) validateAllowedValues() error {
|
||||
// allowedValues not set means no restrictions on values.
|
||||
if !e.hasAllowedValues() {
|
||||
return nil
|
||||
}
|
||||
// Check for missing value.
|
||||
if len(e.values) == 0 {
|
||||
return fmt.Errorf("%s needs a value, none given.", e.idlTag)
|
||||
}
|
||||
// For each extension value, validate that it is allowed.
|
||||
allowedValues := e.allowedValues()
|
||||
if !allowedValues.HasAll(e.values...) {
|
||||
return fmt.Errorf("%v not allowed for %s. Allowed values: %v",
|
||||
e.values, e.idlTag, allowedValues.List())
|
||||
}
|
||||
return nil
|
||||
}
|
||||
|
||||
func (e extension) validateType(kind types.Kind) error {
|
||||
// If this extension class has no kind, then don't validate the type.
|
||||
if !e.hasKind() {
|
||||
return nil
|
||||
}
|
||||
if kind != e.kind() {
|
||||
return fmt.Errorf("tag %s on type %v; only allowed on type %v",
|
||||
e.idlTag, kind, e.kind())
|
||||
}
|
||||
return nil
|
||||
}
|
||||
|
||||
func (e extension) hasMultipleValues() bool {
|
||||
return len(e.values) > 1
|
||||
}
|
||||
|
||||
func (e extension) isAlwaysArrayFormat() bool {
|
||||
return tagToExtension[e.idlTag].enforceArray
|
||||
}
|
||||
|
||||
// Returns sorted list of map keys. Needed for deterministic testing.
|
||||
func sortedMapKeys(m map[string][]string) []string {
|
||||
keys := make([]string, len(m))
|
||||
i := 0
|
||||
for k := range m {
|
||||
keys[i] = k
|
||||
i++
|
||||
}
|
||||
sort.Strings(keys)
|
||||
return keys
|
||||
}
|
||||
|
||||
// Parses comments to return openapi extensions. Returns a list of
|
||||
// extensions which parsed correctly, as well as a list of the
|
||||
// parse errors. Validating extensions is performed separately.
|
||||
// NOTE: Non-empty errors does not mean extensions is empty.
|
||||
func parseExtensions(comments []string) ([]extension, []error) {
|
||||
extensions := []extension{}
|
||||
errors := []error{}
|
||||
// First, generate extensions from "+k8s:openapi-gen=x-kubernetes-*" annotations.
|
||||
values := getOpenAPITagValue(comments)
|
||||
for _, val := range values {
|
||||
// Example: x-kubernetes-member-tag:member_test
|
||||
if strings.HasPrefix(val, extensionPrefix) {
|
||||
parts := strings.SplitN(val, ":", 2)
|
||||
if len(parts) != 2 {
|
||||
errors = append(errors, fmt.Errorf("invalid extension value: %v", val))
|
||||
continue
|
||||
}
|
||||
e := extension{
|
||||
idlTag: tagName, // Example: k8s:openapi-gen
|
||||
xName: parts[0], // Example: x-kubernetes-member-tag
|
||||
values: []string{parts[1]}, // Example: member_test
|
||||
}
|
||||
extensions = append(extensions, e)
|
||||
}
|
||||
}
|
||||
// Next, generate extensions from "idlTags" (e.g. +listType)
|
||||
tagValues := types.ExtractCommentTags("+", comments)
|
||||
for _, idlTag := range sortedMapKeys(tagValues) {
|
||||
xAttrs, exists := tagToExtension[idlTag]
|
||||
if !exists {
|
||||
continue
|
||||
}
|
||||
values := tagValues[idlTag]
|
||||
e := extension{
|
||||
idlTag: idlTag, // listType
|
||||
xName: xAttrs.xName, // x-kubernetes-list-type
|
||||
values: values, // [atomic]
|
||||
}
|
||||
extensions = append(extensions, e)
|
||||
}
|
||||
return extensions, errors
|
||||
}
|
||||
|
||||
func validateMemberExtensions(extensions []extension, m *types.Member) []error {
|
||||
errors := []error{}
|
||||
for _, e := range extensions {
|
||||
if err := e.validateAllowedValues(); err != nil {
|
||||
errors = append(errors, err)
|
||||
}
|
||||
if err := e.validateType(m.Type.Kind); err != nil {
|
||||
errors = append(errors, err)
|
||||
}
|
||||
}
|
||||
return errors
|
||||
}
|
692
vendor/k8s.io/kube-openapi/pkg/generators/openapi.go
generated
vendored
692
vendor/k8s.io/kube-openapi/pkg/generators/openapi.go
generated
vendored
@@ -1,692 +0,0 @@
|
||||
/*
|
||||
Copyright 2016 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 generators
|
||||
|
||||
import (
|
||||
"bytes"
|
||||
"fmt"
|
||||
"io"
|
||||
"path/filepath"
|
||||
"reflect"
|
||||
"sort"
|
||||
"strings"
|
||||
|
||||
"k8s.io/gengo/generator"
|
||||
"k8s.io/gengo/namer"
|
||||
"k8s.io/gengo/types"
|
||||
openapi "k8s.io/kube-openapi/pkg/common"
|
||||
|
||||
"k8s.io/klog/v2"
|
||||
)
|
||||
|
||||
// This is the comment tag that carries parameters for open API generation.
|
||||
const tagName = "k8s:openapi-gen"
|
||||
const tagOptional = "optional"
|
||||
|
||||
// Known values for the tag.
|
||||
const (
|
||||
tagValueTrue = "true"
|
||||
tagValueFalse = "false"
|
||||
)
|
||||
|
||||
// Used for temporary validation of patch struct tags.
|
||||
// TODO: Remove patch struct tag validation because they we are now consuming OpenAPI on server.
|
||||
var tempPatchTags = [...]string{
|
||||
"patchMergeKey",
|
||||
"patchStrategy",
|
||||
}
|
||||
|
||||
func getOpenAPITagValue(comments []string) []string {
|
||||
return types.ExtractCommentTags("+", comments)[tagName]
|
||||
}
|
||||
|
||||
func getSingleTagsValue(comments []string, tag string) (string, error) {
|
||||
tags, ok := types.ExtractCommentTags("+", comments)[tag]
|
||||
if !ok || len(tags) == 0 {
|
||||
return "", nil
|
||||
}
|
||||
if len(tags) > 1 {
|
||||
return "", fmt.Errorf("multiple values are not allowed for tag %s", tag)
|
||||
}
|
||||
return tags[0], nil
|
||||
}
|
||||
|
||||
func hasOpenAPITagValue(comments []string, value string) bool {
|
||||
tagValues := getOpenAPITagValue(comments)
|
||||
for _, val := range tagValues {
|
||||
if val == value {
|
||||
return true
|
||||
}
|
||||
}
|
||||
return false
|
||||
}
|
||||
|
||||
// hasOptionalTag returns true if the member has +optional in its comments or
|
||||
// omitempty in its json tags.
|
||||
func hasOptionalTag(m *types.Member) bool {
|
||||
hasOptionalCommentTag := types.ExtractCommentTags(
|
||||
"+", m.CommentLines)[tagOptional] != nil
|
||||
hasOptionalJsonTag := strings.Contains(
|
||||
reflect.StructTag(m.Tags).Get("json"), "omitempty")
|
||||
return hasOptionalCommentTag || hasOptionalJsonTag
|
||||
}
|
||||
|
||||
func apiTypeFilterFunc(c *generator.Context, t *types.Type) bool {
|
||||
// There is a conflict between this codegen and codecgen, we should avoid types generated for codecgen
|
||||
if strings.HasPrefix(t.Name.Name, "codecSelfer") {
|
||||
return false
|
||||
}
|
||||
pkg := c.Universe.Package(t.Name.Package)
|
||||
if hasOpenAPITagValue(pkg.Comments, tagValueTrue) {
|
||||
return !hasOpenAPITagValue(t.CommentLines, tagValueFalse)
|
||||
}
|
||||
if hasOpenAPITagValue(t.CommentLines, tagValueTrue) {
|
||||
return true
|
||||
}
|
||||
return false
|
||||
}
|
||||
|
||||
const (
|
||||
specPackagePath = "github.com/go-openapi/spec"
|
||||
openAPICommonPackagePath = "k8s.io/kube-openapi/pkg/common"
|
||||
)
|
||||
|
||||
// openApiGen produces a file with auto-generated OpenAPI functions.
|
||||
type openAPIGen struct {
|
||||
generator.DefaultGen
|
||||
// TargetPackage is the package that will get GetOpenAPIDefinitions function returns all open API definitions.
|
||||
targetPackage string
|
||||
imports namer.ImportTracker
|
||||
}
|
||||
|
||||
func newOpenAPIGen(sanitizedName string, targetPackage string) generator.Generator {
|
||||
return &openAPIGen{
|
||||
DefaultGen: generator.DefaultGen{
|
||||
OptionalName: sanitizedName,
|
||||
},
|
||||
imports: generator.NewImportTracker(),
|
||||
targetPackage: targetPackage,
|
||||
}
|
||||
}
|
||||
|
||||
const nameTmpl = "schema_$.type|private$"
|
||||
|
||||
func (g *openAPIGen) Namers(c *generator.Context) namer.NameSystems {
|
||||
// Have the raw namer for this file track what it imports.
|
||||
return namer.NameSystems{
|
||||
"raw": namer.NewRawNamer(g.targetPackage, g.imports),
|
||||
"private": &namer.NameStrategy{
|
||||
Join: func(pre string, in []string, post string) string {
|
||||
return strings.Join(in, "_")
|
||||
},
|
||||
PrependPackageNames: 4, // enough to fully qualify from k8s.io/api/...
|
||||
},
|
||||
}
|
||||
}
|
||||
|
||||
func (g *openAPIGen) isOtherPackage(pkg string) bool {
|
||||
if pkg == g.targetPackage {
|
||||
return false
|
||||
}
|
||||
if strings.HasSuffix(pkg, "\""+g.targetPackage+"\"") {
|
||||
return false
|
||||
}
|
||||
return true
|
||||
}
|
||||
|
||||
func (g *openAPIGen) Imports(c *generator.Context) []string {
|
||||
importLines := []string{}
|
||||
for _, singleImport := range g.imports.ImportLines() {
|
||||
importLines = append(importLines, singleImport)
|
||||
}
|
||||
return importLines
|
||||
}
|
||||
|
||||
func argsFromType(t *types.Type) generator.Args {
|
||||
return generator.Args{
|
||||
"type": t,
|
||||
"ReferenceCallback": types.Ref(openAPICommonPackagePath, "ReferenceCallback"),
|
||||
"OpenAPIDefinition": types.Ref(openAPICommonPackagePath, "OpenAPIDefinition"),
|
||||
"SpecSchemaType": types.Ref(specPackagePath, "Schema"),
|
||||
}
|
||||
}
|
||||
|
||||
func (g *openAPIGen) Init(c *generator.Context, w io.Writer) error {
|
||||
sw := generator.NewSnippetWriter(w, c, "$", "$")
|
||||
sw.Do("func GetOpenAPIDefinitions(ref $.ReferenceCallback|raw$) map[string]$.OpenAPIDefinition|raw$ {\n", argsFromType(nil))
|
||||
sw.Do("return map[string]$.OpenAPIDefinition|raw${\n", argsFromType(nil))
|
||||
|
||||
for _, t := range c.Order {
|
||||
err := newOpenAPITypeWriter(sw, c).generateCall(t)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
}
|
||||
|
||||
sw.Do("}\n", nil)
|
||||
sw.Do("}\n\n", nil)
|
||||
|
||||
return sw.Error()
|
||||
}
|
||||
|
||||
func (g *openAPIGen) GenerateType(c *generator.Context, t *types.Type, w io.Writer) error {
|
||||
klog.V(5).Infof("generating for type %v", t)
|
||||
sw := generator.NewSnippetWriter(w, c, "$", "$")
|
||||
err := newOpenAPITypeWriter(sw, c).generate(t)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
return sw.Error()
|
||||
}
|
||||
|
||||
func getJsonTags(m *types.Member) []string {
|
||||
jsonTag := reflect.StructTag(m.Tags).Get("json")
|
||||
if jsonTag == "" {
|
||||
return []string{}
|
||||
}
|
||||
return strings.Split(jsonTag, ",")
|
||||
}
|
||||
|
||||
func getReferableName(m *types.Member) string {
|
||||
jsonTags := getJsonTags(m)
|
||||
if len(jsonTags) > 0 {
|
||||
if jsonTags[0] == "-" {
|
||||
return ""
|
||||
} else {
|
||||
return jsonTags[0]
|
||||
}
|
||||
} else {
|
||||
return m.Name
|
||||
}
|
||||
}
|
||||
|
||||
func shouldInlineMembers(m *types.Member) bool {
|
||||
jsonTags := getJsonTags(m)
|
||||
return len(jsonTags) > 1 && jsonTags[1] == "inline"
|
||||
}
|
||||
|
||||
type openAPITypeWriter struct {
|
||||
*generator.SnippetWriter
|
||||
context *generator.Context
|
||||
refTypes map[string]*types.Type
|
||||
GetDefinitionInterface *types.Type
|
||||
}
|
||||
|
||||
func newOpenAPITypeWriter(sw *generator.SnippetWriter, c *generator.Context) openAPITypeWriter {
|
||||
return openAPITypeWriter{
|
||||
SnippetWriter: sw,
|
||||
context: c,
|
||||
refTypes: map[string]*types.Type{},
|
||||
}
|
||||
}
|
||||
|
||||
func methodReturnsValue(mt *types.Type, pkg, name string) bool {
|
||||
if len(mt.Signature.Parameters) != 0 || len(mt.Signature.Results) != 1 {
|
||||
return false
|
||||
}
|
||||
r := mt.Signature.Results[0]
|
||||
return r.Name.Name == name && r.Name.Package == pkg
|
||||
}
|
||||
|
||||
func hasOpenAPIV3DefinitionMethod(t *types.Type) bool {
|
||||
for mn, mt := range t.Methods {
|
||||
if mn != "OpenAPIV3Definition" {
|
||||
continue
|
||||
}
|
||||
return methodReturnsValue(mt, openAPICommonPackagePath, "OpenAPIDefinition")
|
||||
}
|
||||
return false
|
||||
}
|
||||
|
||||
func hasOpenAPIDefinitionMethod(t *types.Type) bool {
|
||||
for mn, mt := range t.Methods {
|
||||
if mn != "OpenAPIDefinition" {
|
||||
continue
|
||||
}
|
||||
return methodReturnsValue(mt, openAPICommonPackagePath, "OpenAPIDefinition")
|
||||
}
|
||||
return false
|
||||
}
|
||||
|
||||
func hasOpenAPIDefinitionMethods(t *types.Type) bool {
|
||||
var hasSchemaTypeMethod, hasOpenAPISchemaFormat bool
|
||||
for mn, mt := range t.Methods {
|
||||
switch mn {
|
||||
case "OpenAPISchemaType":
|
||||
hasSchemaTypeMethod = methodReturnsValue(mt, "", "[]string")
|
||||
case "OpenAPISchemaFormat":
|
||||
hasOpenAPISchemaFormat = methodReturnsValue(mt, "", "string")
|
||||
}
|
||||
}
|
||||
return hasSchemaTypeMethod && hasOpenAPISchemaFormat
|
||||
}
|
||||
|
||||
// typeShortName returns short package name (e.g. the name x appears in package x definition) dot type name.
|
||||
func typeShortName(t *types.Type) string {
|
||||
return filepath.Base(t.Name.Package) + "." + t.Name.Name
|
||||
}
|
||||
|
||||
func (g openAPITypeWriter) generateMembers(t *types.Type, required []string) ([]string, error) {
|
||||
var err error
|
||||
for _, m := range t.Members {
|
||||
if hasOpenAPITagValue(m.CommentLines, tagValueFalse) {
|
||||
continue
|
||||
}
|
||||
if shouldInlineMembers(&m) {
|
||||
required, err = g.generateMembers(m.Type, required)
|
||||
if err != nil {
|
||||
return required, err
|
||||
}
|
||||
continue
|
||||
}
|
||||
name := getReferableName(&m)
|
||||
if name == "" {
|
||||
continue
|
||||
}
|
||||
if !hasOptionalTag(&m) {
|
||||
required = append(required, name)
|
||||
}
|
||||
if err = g.generateProperty(&m, t); err != nil {
|
||||
klog.Errorf("Error when generating: %v, %v\n", name, m)
|
||||
return required, err
|
||||
}
|
||||
}
|
||||
return required, nil
|
||||
}
|
||||
|
||||
func (g openAPITypeWriter) generateCall(t *types.Type) error {
|
||||
// Only generate for struct type and ignore the rest
|
||||
switch t.Kind {
|
||||
case types.Struct:
|
||||
args := argsFromType(t)
|
||||
g.Do("\"$.$\": ", t.Name)
|
||||
|
||||
hasV2Definition := hasOpenAPIDefinitionMethod(t)
|
||||
hasV2DefinitionTypeAndFormat := hasOpenAPIDefinitionMethods(t)
|
||||
hasV3Definition := hasOpenAPIV3DefinitionMethod(t)
|
||||
|
||||
switch {
|
||||
case hasV2DefinitionTypeAndFormat:
|
||||
g.Do(nameTmpl+"(ref),\n", args)
|
||||
case hasV2Definition && hasV3Definition:
|
||||
g.Do("common.EmbedOpenAPIDefinitionIntoV2Extension($.type|raw${}.OpenAPIV3Definition(), $.type|raw${}.OpenAPIDefinition()),\n", args)
|
||||
case hasV2Definition:
|
||||
g.Do("$.type|raw${}.OpenAPIDefinition(),\n", args)
|
||||
case hasV3Definition:
|
||||
g.Do("$.type|raw${}.OpenAPIV3Definition(),\n", args)
|
||||
default:
|
||||
g.Do(nameTmpl+"(ref),\n", args)
|
||||
}
|
||||
}
|
||||
return g.Error()
|
||||
}
|
||||
|
||||
func (g openAPITypeWriter) generate(t *types.Type) error {
|
||||
// Only generate for struct type and ignore the rest
|
||||
switch t.Kind {
|
||||
case types.Struct:
|
||||
hasV2Definition := hasOpenAPIDefinitionMethod(t)
|
||||
hasV2DefinitionTypeAndFormat := hasOpenAPIDefinitionMethods(t)
|
||||
hasV3Definition := hasOpenAPIV3DefinitionMethod(t)
|
||||
|
||||
if hasV2Definition || (hasV3Definition && !hasV2DefinitionTypeAndFormat) {
|
||||
// already invoked directly
|
||||
return nil
|
||||
}
|
||||
|
||||
args := argsFromType(t)
|
||||
g.Do("func "+nameTmpl+"(ref $.ReferenceCallback|raw$) $.OpenAPIDefinition|raw$ {\n", args)
|
||||
switch {
|
||||
case hasV2DefinitionTypeAndFormat && hasV3Definition:
|
||||
g.Do("return common.EmbedOpenAPIDefinitionIntoV2Extension($.type|raw${}.OpenAPIV3Definition(), $.OpenAPIDefinition|raw${\n"+
|
||||
"Schema: spec.Schema{\n"+
|
||||
"SchemaProps: spec.SchemaProps{\n", args)
|
||||
g.generateDescription(t.CommentLines)
|
||||
g.Do("Type:$.type|raw${}.OpenAPISchemaType(),\n"+
|
||||
"Format:$.type|raw${}.OpenAPISchemaFormat(),\n"+
|
||||
"},\n"+
|
||||
"},\n"+
|
||||
"})\n}\n\n", args)
|
||||
return nil
|
||||
case hasV2DefinitionTypeAndFormat:
|
||||
g.Do("return $.OpenAPIDefinition|raw${\n"+
|
||||
"Schema: spec.Schema{\n"+
|
||||
"SchemaProps: spec.SchemaProps{\n", args)
|
||||
g.generateDescription(t.CommentLines)
|
||||
g.Do("Type:$.type|raw${}.OpenAPISchemaType(),\n"+
|
||||
"Format:$.type|raw${}.OpenAPISchemaFormat(),\n"+
|
||||
"},\n"+
|
||||
"},\n"+
|
||||
"}\n}\n\n", args)
|
||||
return nil
|
||||
}
|
||||
g.Do("return $.OpenAPIDefinition|raw${\nSchema: spec.Schema{\nSchemaProps: spec.SchemaProps{\n", args)
|
||||
g.generateDescription(t.CommentLines)
|
||||
g.Do("Type: []string{\"object\"},\n", nil)
|
||||
|
||||
// write members into a temporary buffer, in order to postpone writing out the Properties field. We only do
|
||||
// that if it is not empty.
|
||||
propertiesBuf := bytes.Buffer{}
|
||||
bsw := g
|
||||
bsw.SnippetWriter = generator.NewSnippetWriter(&propertiesBuf, g.context, "$", "$")
|
||||
required, err := bsw.generateMembers(t, []string{})
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
if propertiesBuf.Len() > 0 {
|
||||
g.Do("Properties: map[string]$.SpecSchemaType|raw${\n", args)
|
||||
g.Do(strings.Replace(propertiesBuf.String(), "$", "$\"$\"$", -1), nil) // escape $ (used as delimiter of the templates)
|
||||
g.Do("},\n", nil)
|
||||
}
|
||||
|
||||
if len(required) > 0 {
|
||||
g.Do("Required: []string{\"$.$\"},\n", strings.Join(required, "\",\""))
|
||||
}
|
||||
g.Do("},\n", nil)
|
||||
if err := g.generateStructExtensions(t); err != nil {
|
||||
return err
|
||||
}
|
||||
g.Do("},\n", nil)
|
||||
|
||||
// Map order is undefined, sort them or we may get a different file generated each time.
|
||||
keys := []string{}
|
||||
for k := range g.refTypes {
|
||||
keys = append(keys, k)
|
||||
}
|
||||
sort.Strings(keys)
|
||||
deps := []string{}
|
||||
for _, k := range keys {
|
||||
v := g.refTypes[k]
|
||||
if t, _ := openapi.GetOpenAPITypeFormat(v.String()); t != "" {
|
||||
// This is a known type, we do not need a reference to it
|
||||
// Will eliminate special case of time.Time
|
||||
continue
|
||||
}
|
||||
deps = append(deps, k)
|
||||
}
|
||||
if len(deps) > 0 {
|
||||
g.Do("Dependencies: []string{\n", args)
|
||||
for _, k := range deps {
|
||||
g.Do("\"$.$\",", k)
|
||||
}
|
||||
g.Do("},\n", nil)
|
||||
}
|
||||
g.Do("}\n}\n\n", nil)
|
||||
}
|
||||
return nil
|
||||
}
|
||||
|
||||
func (g openAPITypeWriter) generateStructExtensions(t *types.Type) error {
|
||||
extensions, errors := parseExtensions(t.CommentLines)
|
||||
// Initially, we will only log struct extension errors.
|
||||
if len(errors) > 0 {
|
||||
for _, e := range errors {
|
||||
klog.Errorf("[%s]: %s\n", t.String(), e)
|
||||
}
|
||||
}
|
||||
unions, errors := parseUnions(t)
|
||||
if len(errors) > 0 {
|
||||
for _, e := range errors {
|
||||
klog.Errorf("[%s]: %s\n", t.String(), e)
|
||||
}
|
||||
}
|
||||
|
||||
// TODO(seans3): Validate struct extensions here.
|
||||
g.emitExtensions(extensions, unions)
|
||||
return nil
|
||||
}
|
||||
|
||||
func (g openAPITypeWriter) generateMemberExtensions(m *types.Member, parent *types.Type) error {
|
||||
extensions, parseErrors := parseExtensions(m.CommentLines)
|
||||
validationErrors := validateMemberExtensions(extensions, m)
|
||||
errors := append(parseErrors, validationErrors...)
|
||||
// Initially, we will only log member extension errors.
|
||||
if len(errors) > 0 {
|
||||
errorPrefix := fmt.Sprintf("[%s] %s:", parent.String(), m.String())
|
||||
for _, e := range errors {
|
||||
klog.V(2).Infof("%s %s\n", errorPrefix, e)
|
||||
}
|
||||
}
|
||||
g.emitExtensions(extensions, nil)
|
||||
return nil
|
||||
}
|
||||
|
||||
func (g openAPITypeWriter) emitExtensions(extensions []extension, unions []union) {
|
||||
// If any extensions exist, then emit code to create them.
|
||||
if len(extensions) == 0 && len(unions) == 0 {
|
||||
return
|
||||
}
|
||||
g.Do("VendorExtensible: spec.VendorExtensible{\nExtensions: spec.Extensions{\n", nil)
|
||||
for _, extension := range extensions {
|
||||
g.Do("\"$.$\": ", extension.xName)
|
||||
if extension.hasMultipleValues() || extension.isAlwaysArrayFormat() {
|
||||
g.Do("[]interface{}{\n", nil)
|
||||
}
|
||||
for _, value := range extension.values {
|
||||
g.Do("\"$.$\",\n", value)
|
||||
}
|
||||
if extension.hasMultipleValues() || extension.isAlwaysArrayFormat() {
|
||||
g.Do("},\n", nil)
|
||||
}
|
||||
}
|
||||
if len(unions) > 0 {
|
||||
g.Do("\"x-kubernetes-unions\": []interface{}{\n", nil)
|
||||
for _, u := range unions {
|
||||
u.emit(g)
|
||||
}
|
||||
g.Do("},\n", nil)
|
||||
}
|
||||
g.Do("},\n},\n", nil)
|
||||
}
|
||||
|
||||
// TODO(#44005): Move this validation outside of this generator (probably to policy verifier)
|
||||
func (g openAPITypeWriter) validatePatchTags(m *types.Member, parent *types.Type) error {
|
||||
// TODO: Remove patch struct tag validation because they we are now consuming OpenAPI on server.
|
||||
for _, tagKey := range tempPatchTags {
|
||||
structTagValue := reflect.StructTag(m.Tags).Get(tagKey)
|
||||
commentTagValue, err := getSingleTagsValue(m.CommentLines, tagKey)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
if structTagValue != commentTagValue {
|
||||
return fmt.Errorf("Tags in comment and struct should match for member (%s) of (%s)",
|
||||
m.Name, parent.Name.String())
|
||||
}
|
||||
}
|
||||
return nil
|
||||
}
|
||||
|
||||
func (g openAPITypeWriter) generateDescription(CommentLines []string) {
|
||||
var buffer bytes.Buffer
|
||||
delPrevChar := func() {
|
||||
if buffer.Len() > 0 {
|
||||
buffer.Truncate(buffer.Len() - 1) // Delete the last " " or "\n"
|
||||
}
|
||||
}
|
||||
|
||||
for _, line := range CommentLines {
|
||||
// Ignore all lines after ---
|
||||
if line == "---" {
|
||||
break
|
||||
}
|
||||
line = strings.TrimRight(line, " ")
|
||||
leading := strings.TrimLeft(line, " ")
|
||||
switch {
|
||||
case len(line) == 0: // Keep paragraphs
|
||||
delPrevChar()
|
||||
buffer.WriteString("\n\n")
|
||||
case strings.HasPrefix(leading, "TODO"): // Ignore one line TODOs
|
||||
case strings.HasPrefix(leading, "+"): // Ignore instructions to go2idl
|
||||
default:
|
||||
if strings.HasPrefix(line, " ") || strings.HasPrefix(line, "\t") {
|
||||
delPrevChar()
|
||||
line = "\n" + line + "\n" // Replace it with newline. This is useful when we have a line with: "Example:\n\tJSON-someting..."
|
||||
} else {
|
||||
line += " "
|
||||
}
|
||||
buffer.WriteString(line)
|
||||
}
|
||||
}
|
||||
|
||||
postDoc := strings.TrimRight(buffer.String(), "\n")
|
||||
postDoc = strings.Replace(postDoc, "\\\"", "\"", -1) // replace user's \" to "
|
||||
postDoc = strings.Replace(postDoc, "\"", "\\\"", -1) // Escape "
|
||||
postDoc = strings.Replace(postDoc, "\n", "\\n", -1)
|
||||
postDoc = strings.Replace(postDoc, "\t", "\\t", -1)
|
||||
postDoc = strings.Trim(postDoc, " ")
|
||||
if postDoc != "" {
|
||||
g.Do("Description: \"$.$\",\n", postDoc)
|
||||
}
|
||||
}
|
||||
|
||||
func (g openAPITypeWriter) generateProperty(m *types.Member, parent *types.Type) error {
|
||||
name := getReferableName(m)
|
||||
if name == "" {
|
||||
return nil
|
||||
}
|
||||
if err := g.validatePatchTags(m, parent); err != nil {
|
||||
return err
|
||||
}
|
||||
g.Do("\"$.$\": {\n", name)
|
||||
if err := g.generateMemberExtensions(m, parent); err != nil {
|
||||
return err
|
||||
}
|
||||
g.Do("SchemaProps: spec.SchemaProps{\n", nil)
|
||||
g.generateDescription(m.CommentLines)
|
||||
jsonTags := getJsonTags(m)
|
||||
if len(jsonTags) > 1 && jsonTags[1] == "string" {
|
||||
g.generateSimpleProperty("string", "")
|
||||
g.Do("},\n},\n", nil)
|
||||
return nil
|
||||
}
|
||||
t := resolveAliasAndPtrType(m.Type)
|
||||
// If we can get a openAPI type and format for this type, we consider it to be simple property
|
||||
typeString, format := openapi.GetOpenAPITypeFormat(t.String())
|
||||
if typeString != "" {
|
||||
g.generateSimpleProperty(typeString, format)
|
||||
g.Do("},\n},\n", nil)
|
||||
return nil
|
||||
}
|
||||
switch t.Kind {
|
||||
case types.Builtin:
|
||||
return fmt.Errorf("please add type %v to getOpenAPITypeFormat function", t)
|
||||
case types.Map:
|
||||
if err := g.generateMapProperty(t); err != nil {
|
||||
return err
|
||||
}
|
||||
case types.Slice, types.Array:
|
||||
if err := g.generateSliceProperty(t); err != nil {
|
||||
return err
|
||||
}
|
||||
case types.Struct, types.Interface:
|
||||
g.generateReferenceProperty(t)
|
||||
default:
|
||||
return fmt.Errorf("cannot generate spec for type %v", t)
|
||||
}
|
||||
g.Do("},\n},\n", nil)
|
||||
return g.Error()
|
||||
}
|
||||
|
||||
func (g openAPITypeWriter) generateSimpleProperty(typeString, format string) {
|
||||
g.Do("Type: []string{\"$.$\"},\n", typeString)
|
||||
g.Do("Format: \"$.$\",\n", format)
|
||||
}
|
||||
|
||||
func (g openAPITypeWriter) generateReferenceProperty(t *types.Type) {
|
||||
g.refTypes[t.Name.String()] = t
|
||||
g.Do("Ref: ref(\"$.$\"),\n", t.Name.String())
|
||||
}
|
||||
|
||||
func resolveAliasAndPtrType(t *types.Type) *types.Type {
|
||||
var prev *types.Type
|
||||
for prev != t {
|
||||
prev = t
|
||||
if t.Kind == types.Alias {
|
||||
t = t.Underlying
|
||||
}
|
||||
if t.Kind == types.Pointer {
|
||||
t = t.Elem
|
||||
}
|
||||
}
|
||||
return t
|
||||
}
|
||||
|
||||
func (g openAPITypeWriter) generateMapProperty(t *types.Type) error {
|
||||
keyType := resolveAliasAndPtrType(t.Key)
|
||||
elemType := resolveAliasAndPtrType(t.Elem)
|
||||
|
||||
// According to OpenAPI examples, only map from string is supported
|
||||
if keyType.Name.Name != "string" {
|
||||
return fmt.Errorf("map with non-string keys are not supported by OpenAPI in %v", t)
|
||||
}
|
||||
g.Do("Type: []string{\"object\"},\n", nil)
|
||||
g.Do("AdditionalProperties: &spec.SchemaOrBool{\nAllows: true,\nSchema: &spec.Schema{\nSchemaProps: spec.SchemaProps{\n", nil)
|
||||
typeString, format := openapi.GetOpenAPITypeFormat(elemType.String())
|
||||
if typeString != "" {
|
||||
g.generateSimpleProperty(typeString, format)
|
||||
g.Do("},\n},\n},\n", nil)
|
||||
return nil
|
||||
}
|
||||
switch elemType.Kind {
|
||||
case types.Builtin:
|
||||
return fmt.Errorf("please add type %v to getOpenAPITypeFormat function", elemType)
|
||||
case types.Struct:
|
||||
g.generateReferenceProperty(elemType)
|
||||
case types.Slice, types.Array:
|
||||
if err := g.generateSliceProperty(elemType); err != nil {
|
||||
return err
|
||||
}
|
||||
case types.Map:
|
||||
if err := g.generateMapProperty(elemType); err != nil {
|
||||
return err
|
||||
}
|
||||
default:
|
||||
return fmt.Errorf("map Element kind %v is not supported in %v", elemType.Kind, t.Name)
|
||||
}
|
||||
g.Do("},\n},\n},\n", nil)
|
||||
return nil
|
||||
}
|
||||
|
||||
func (g openAPITypeWriter) generateSliceProperty(t *types.Type) error {
|
||||
elemType := resolveAliasAndPtrType(t.Elem)
|
||||
g.Do("Type: []string{\"array\"},\n", nil)
|
||||
g.Do("Items: &spec.SchemaOrArray{\nSchema: &spec.Schema{\nSchemaProps: spec.SchemaProps{\n", nil)
|
||||
typeString, format := openapi.GetOpenAPITypeFormat(elemType.String())
|
||||
if typeString != "" {
|
||||
g.generateSimpleProperty(typeString, format)
|
||||
g.Do("},\n},\n},\n", nil)
|
||||
return nil
|
||||
}
|
||||
switch elemType.Kind {
|
||||
case types.Builtin:
|
||||
return fmt.Errorf("please add type %v to getOpenAPITypeFormat function", elemType)
|
||||
case types.Struct:
|
||||
g.generateReferenceProperty(elemType)
|
||||
case types.Slice, types.Array:
|
||||
if err := g.generateSliceProperty(elemType); err != nil {
|
||||
return err
|
||||
}
|
||||
case types.Map:
|
||||
if err := g.generateMapProperty(elemType); err != nil {
|
||||
return err
|
||||
}
|
||||
default:
|
||||
return fmt.Errorf("slice Element kind %v is not supported in %v", elemType.Kind, t)
|
||||
}
|
||||
g.Do("},\n},\n},\n", nil)
|
||||
return nil
|
||||
}
|
4
vendor/k8s.io/kube-openapi/pkg/generators/rules/OWNERS
generated
vendored
4
vendor/k8s.io/kube-openapi/pkg/generators/rules/OWNERS
generated
vendored
@@ -1,4 +0,0 @@
|
||||
reviewers:
|
||||
- roycaihw
|
||||
approvers:
|
||||
- roycaihw
|
23
vendor/k8s.io/kube-openapi/pkg/generators/rules/doc.go
generated
vendored
23
vendor/k8s.io/kube-openapi/pkg/generators/rules/doc.go
generated
vendored
@@ -1,23 +0,0 @@
|
||||
/*
|
||||
Copyright 2018 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 rules contains API rules that are enforced in OpenAPI spec generation
|
||||
// as part of the machinery. Files under this package implement APIRule interface
|
||||
// which evaluates Go type and produces list of API rule violations.
|
||||
//
|
||||
// Implementations of APIRule should be added to API linter under openAPIGen code-
|
||||
// generator to get integrated in the generation process.
|
||||
package rules
|
53
vendor/k8s.io/kube-openapi/pkg/generators/rules/idl_tag.go
generated
vendored
53
vendor/k8s.io/kube-openapi/pkg/generators/rules/idl_tag.go
generated
vendored
@@ -1,53 +0,0 @@
|
||||
package rules
|
||||
|
||||
import (
|
||||
"k8s.io/gengo/types"
|
||||
)
|
||||
|
||||
const ListTypeIDLTag = "listType"
|
||||
|
||||
// ListTypeMissing implements APIRule interface.
|
||||
// A list type is required for inlined list.
|
||||
type ListTypeMissing struct{}
|
||||
|
||||
// Name returns the name of APIRule
|
||||
func (l *ListTypeMissing) Name() string {
|
||||
return "list_type_missing"
|
||||
}
|
||||
|
||||
// Validate evaluates API rule on type t and returns a list of field names in
|
||||
// the type that violate the rule. Empty field name [""] implies the entire
|
||||
// type violates the rule.
|
||||
func (l *ListTypeMissing) Validate(t *types.Type) ([]string, error) {
|
||||
fields := make([]string, 0)
|
||||
|
||||
switch t.Kind {
|
||||
case types.Struct:
|
||||
for _, m := range t.Members {
|
||||
hasListType := types.ExtractCommentTags("+", m.CommentLines)[ListTypeIDLTag] != nil
|
||||
|
||||
if m.Name == "Items" && m.Type.Kind == types.Slice && hasNamedMember(t, "ListMeta") {
|
||||
if hasListType {
|
||||
fields = append(fields, m.Name)
|
||||
}
|
||||
continue
|
||||
}
|
||||
|
||||
if m.Type.Kind == types.Slice && !hasListType {
|
||||
fields = append(fields, m.Name)
|
||||
continue
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
return fields, nil
|
||||
}
|
||||
|
||||
func hasNamedMember(t *types.Type, name string) bool {
|
||||
for _, m := range t.Members {
|
||||
if m.Name == name {
|
||||
return true
|
||||
}
|
||||
}
|
||||
return false
|
||||
}
|
172
vendor/k8s.io/kube-openapi/pkg/generators/rules/names_match.go
generated
vendored
172
vendor/k8s.io/kube-openapi/pkg/generators/rules/names_match.go
generated
vendored
@@ -1,172 +0,0 @@
|
||||
/*
|
||||
Copyright 2018 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 rules
|
||||
|
||||
import (
|
||||
"reflect"
|
||||
"strings"
|
||||
|
||||
"k8s.io/kube-openapi/pkg/util/sets"
|
||||
|
||||
"k8s.io/gengo/types"
|
||||
)
|
||||
|
||||
var (
|
||||
// Blacklist of JSON tags that should skip match evaluation
|
||||
jsonTagBlacklist = sets.NewString(
|
||||
// Omitted field is ignored by the package
|
||||
"-",
|
||||
)
|
||||
|
||||
// Blacklist of JSON names that should skip match evaluation
|
||||
jsonNameBlacklist = sets.NewString(
|
||||
// Empty name is used for inline struct field (e.g. metav1.TypeMeta)
|
||||
"",
|
||||
// Special case for object and list meta
|
||||
"metadata",
|
||||
)
|
||||
|
||||
// List of substrings that aren't allowed in Go name and JSON name
|
||||
disallowedNameSubstrings = sets.NewString(
|
||||
// Underscore is not allowed in either name
|
||||
"_",
|
||||
// Dash is not allowed in either name. Note that since dash is a valid JSON tag, this should be checked
|
||||
// after JSON tag blacklist check.
|
||||
"-",
|
||||
)
|
||||
)
|
||||
|
||||
/*
|
||||
NamesMatch implements APIRule interface.
|
||||
Go field names must be CamelCase. JSON field names must be camelCase. Other than capitalization of the
|
||||
initial letter, the two should almost always match. No underscores nor dashes in either.
|
||||
This rule verifies the convention "Other than capitalization of the initial letter, the two should almost always match."
|
||||
Examples (also in unit test):
|
||||
Go name | JSON name | match
|
||||
podSpec false
|
||||
PodSpec podSpec true
|
||||
PodSpec PodSpec false
|
||||
podSpec podSpec false
|
||||
PodSpec spec false
|
||||
Spec podSpec false
|
||||
JSONSpec jsonSpec true
|
||||
JSONSpec jsonspec false
|
||||
HTTPJSONSpec httpJSONSpec true
|
||||
NOTE: this validator cannot tell two sequential all-capital words from one word, therefore the case below
|
||||
is also considered matched.
|
||||
HTTPJSONSpec httpjsonSpec true
|
||||
NOTE: JSON names in jsonNameBlacklist should skip evaluation
|
||||
true
|
||||
podSpec true
|
||||
podSpec - true
|
||||
podSpec metadata true
|
||||
*/
|
||||
type NamesMatch struct{}
|
||||
|
||||
// Name returns the name of APIRule
|
||||
func (n *NamesMatch) Name() string {
|
||||
return "names_match"
|
||||
}
|
||||
|
||||
// Validate evaluates API rule on type t and returns a list of field names in
|
||||
// the type that violate the rule. Empty field name [""] implies the entire
|
||||
// type violates the rule.
|
||||
func (n *NamesMatch) Validate(t *types.Type) ([]string, error) {
|
||||
fields := make([]string, 0)
|
||||
|
||||
// Only validate struct type and ignore the rest
|
||||
switch t.Kind {
|
||||
case types.Struct:
|
||||
for _, m := range t.Members {
|
||||
goName := m.Name
|
||||
jsonTag, ok := reflect.StructTag(m.Tags).Lookup("json")
|
||||
// Distinguish empty JSON tag and missing JSON tag. Empty JSON tag / name is
|
||||
// allowed (in JSON name blacklist) but missing JSON tag is invalid.
|
||||
if !ok {
|
||||
fields = append(fields, goName)
|
||||
continue
|
||||
}
|
||||
if jsonTagBlacklist.Has(jsonTag) {
|
||||
continue
|
||||
}
|
||||
jsonName := strings.Split(jsonTag, ",")[0]
|
||||
if !namesMatch(goName, jsonName) {
|
||||
fields = append(fields, goName)
|
||||
}
|
||||
}
|
||||
}
|
||||
return fields, nil
|
||||
}
|
||||
|
||||
// namesMatch evaluates if goName and jsonName match the API rule
|
||||
// TODO: Use an off-the-shelf CamelCase solution instead of implementing this logic. The following existing
|
||||
// packages have been tried out:
|
||||
// github.com/markbates/inflect
|
||||
// github.com/segmentio/go-camelcase
|
||||
// github.com/iancoleman/strcase
|
||||
// github.com/fatih/camelcase
|
||||
// Please see https://github.com/kubernetes/kube-openapi/pull/83#issuecomment-400842314 for more details
|
||||
// about why they don't satisfy our need. What we need can be a function that detects an acronym at the
|
||||
// beginning of a string.
|
||||
func namesMatch(goName, jsonName string) bool {
|
||||
if jsonNameBlacklist.Has(jsonName) {
|
||||
return true
|
||||
}
|
||||
if !isAllowedName(goName) || !isAllowedName(jsonName) {
|
||||
return false
|
||||
}
|
||||
if strings.ToLower(goName) != strings.ToLower(jsonName) {
|
||||
return false
|
||||
}
|
||||
// Go field names must be CamelCase. JSON field names must be camelCase.
|
||||
if !isCapital(goName[0]) || isCapital(jsonName[0]) {
|
||||
return false
|
||||
}
|
||||
for i := 0; i < len(goName); i++ {
|
||||
if goName[i] == jsonName[i] {
|
||||
// goName[0:i-1] is uppercase and jsonName[0:i-1] is lowercase, goName[i:]
|
||||
// and jsonName[i:] should match;
|
||||
// goName[i] should be lowercase if i is equal to 1, e.g.:
|
||||
// goName | jsonName
|
||||
// PodSpec podSpec
|
||||
// or uppercase if i is greater than 1, e.g.:
|
||||
// goname | jsonName
|
||||
// JSONSpec jsonSpec
|
||||
// This is to rule out cases like:
|
||||
// goname | jsonName
|
||||
// JSONSpec jsonspec
|
||||
return goName[i:] == jsonName[i:] && (i == 1 || isCapital(goName[i]))
|
||||
}
|
||||
}
|
||||
return true
|
||||
}
|
||||
|
||||
// isCaptical returns true if one character is capital
|
||||
func isCapital(b byte) bool {
|
||||
return b >= 'A' && b <= 'Z'
|
||||
}
|
||||
|
||||
// isAllowedName checks the list of disallowedNameSubstrings and returns true if name doesn't contain
|
||||
// any disallowed substring.
|
||||
func isAllowedName(name string) bool {
|
||||
for _, substr := range disallowedNameSubstrings.UnsortedList() {
|
||||
if strings.Contains(name, substr) {
|
||||
return false
|
||||
}
|
||||
}
|
||||
return true
|
||||
}
|
64
vendor/k8s.io/kube-openapi/pkg/generators/rules/omitempty_match_case.go
generated
vendored
64
vendor/k8s.io/kube-openapi/pkg/generators/rules/omitempty_match_case.go
generated
vendored
@@ -1,64 +0,0 @@
|
||||
/*
|
||||
Copyright 2018 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 rules
|
||||
|
||||
import (
|
||||
"reflect"
|
||||
"strings"
|
||||
|
||||
"k8s.io/gengo/types"
|
||||
)
|
||||
|
||||
// OmitEmptyMatchCase implements APIRule interface.
|
||||
// "omitempty" must appear verbatim (no case variants).
|
||||
type OmitEmptyMatchCase struct{}
|
||||
|
||||
func (n *OmitEmptyMatchCase) Name() string {
|
||||
return "omitempty_match_case"
|
||||
}
|
||||
|
||||
func (n *OmitEmptyMatchCase) Validate(t *types.Type) ([]string, error) {
|
||||
fields := make([]string, 0)
|
||||
|
||||
// Only validate struct type and ignore the rest
|
||||
switch t.Kind {
|
||||
case types.Struct:
|
||||
for _, m := range t.Members {
|
||||
goName := m.Name
|
||||
jsonTag, ok := reflect.StructTag(m.Tags).Lookup("json")
|
||||
if !ok {
|
||||
continue
|
||||
}
|
||||
|
||||
parts := strings.Split(jsonTag, ",")
|
||||
if len(parts) < 2 {
|
||||
// no tags other than name
|
||||
continue
|
||||
}
|
||||
if parts[0] == "-" {
|
||||
// not serialized
|
||||
continue
|
||||
}
|
||||
for _, part := range parts[1:] {
|
||||
if strings.EqualFold(part, "omitempty") && part != "omitempty" {
|
||||
fields = append(fields, goName)
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
return fields, nil
|
||||
}
|
207
vendor/k8s.io/kube-openapi/pkg/generators/union.go
generated
vendored
207
vendor/k8s.io/kube-openapi/pkg/generators/union.go
generated
vendored
@@ -1,207 +0,0 @@
|
||||
/*
|
||||
Copyright 2016 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 generators
|
||||
|
||||
import (
|
||||
"fmt"
|
||||
"sort"
|
||||
|
||||
"k8s.io/gengo/types"
|
||||
)
|
||||
|
||||
const tagUnionMember = "union"
|
||||
const tagUnionDeprecated = "unionDeprecated"
|
||||
const tagUnionDiscriminator = "unionDiscriminator"
|
||||
|
||||
type union struct {
|
||||
discriminator string
|
||||
fieldsToDiscriminated map[string]string
|
||||
}
|
||||
|
||||
// emit prints the union, can be called on a nil union (emits nothing)
|
||||
func (u *union) emit(g openAPITypeWriter) {
|
||||
if u == nil {
|
||||
return
|
||||
}
|
||||
g.Do("map[string]interface{}{\n", nil)
|
||||
if u.discriminator != "" {
|
||||
g.Do("\"discriminator\": \"$.$\",\n", u.discriminator)
|
||||
}
|
||||
g.Do("\"fields-to-discriminateBy\": map[string]interface{}{\n", nil)
|
||||
keys := []string{}
|
||||
for field := range u.fieldsToDiscriminated {
|
||||
keys = append(keys, field)
|
||||
}
|
||||
sort.Strings(keys)
|
||||
for _, field := range keys {
|
||||
g.Do("\"$.$\": ", field)
|
||||
g.Do("\"$.$\",\n", u.fieldsToDiscriminated[field])
|
||||
}
|
||||
g.Do("},\n", nil)
|
||||
g.Do("},\n", nil)
|
||||
}
|
||||
|
||||
// Sets the discriminator if it's not set yet, otherwise return an error
|
||||
func (u *union) setDiscriminator(value string) []error {
|
||||
errors := []error{}
|
||||
if u.discriminator != "" {
|
||||
errors = append(errors, fmt.Errorf("at least two discriminators found: %v and %v", value, u.discriminator))
|
||||
}
|
||||
u.discriminator = value
|
||||
return errors
|
||||
}
|
||||
|
||||
// Add a new member to the union
|
||||
func (u *union) addMember(jsonName, variableName string) {
|
||||
if _, ok := u.fieldsToDiscriminated[jsonName]; ok {
|
||||
panic(fmt.Errorf("same field (%v) found multiple times", jsonName))
|
||||
}
|
||||
u.fieldsToDiscriminated[jsonName] = variableName
|
||||
}
|
||||
|
||||
// Makes sure that the union is valid, specifically looking for re-used discriminated
|
||||
func (u *union) isValid() []error {
|
||||
errors := []error{}
|
||||
// Case 1: discriminator but no fields
|
||||
if u.discriminator != "" && len(u.fieldsToDiscriminated) == 0 {
|
||||
errors = append(errors, fmt.Errorf("discriminator set with no fields in union"))
|
||||
}
|
||||
// Case 2: two fields have the same discriminated value
|
||||
discriminated := map[string]struct{}{}
|
||||
for _, d := range u.fieldsToDiscriminated {
|
||||
if _, ok := discriminated[d]; ok {
|
||||
errors = append(errors, fmt.Errorf("discriminated value is used twice: %v", d))
|
||||
}
|
||||
discriminated[d] = struct{}{}
|
||||
}
|
||||
// Case 3: a field is both discriminator AND part of the union
|
||||
if u.discriminator != "" {
|
||||
if _, ok := u.fieldsToDiscriminated[u.discriminator]; ok {
|
||||
errors = append(errors, fmt.Errorf("%v can't be both discriminator and part of the union", u.discriminator))
|
||||
}
|
||||
}
|
||||
return errors
|
||||
}
|
||||
|
||||
// Find unions either directly on the members (or inlined members, not
|
||||
// going across types) or on the type itself, or on embedded types.
|
||||
func parseUnions(t *types.Type) ([]union, []error) {
|
||||
errors := []error{}
|
||||
unions := []union{}
|
||||
su, err := parseUnionStruct(t)
|
||||
if su != nil {
|
||||
unions = append(unions, *su)
|
||||
}
|
||||
errors = append(errors, err...)
|
||||
eu, err := parseEmbeddedUnion(t)
|
||||
unions = append(unions, eu...)
|
||||
errors = append(errors, err...)
|
||||
mu, err := parseUnionMembers(t)
|
||||
if mu != nil {
|
||||
unions = append(unions, *mu)
|
||||
}
|
||||
errors = append(errors, err...)
|
||||
return unions, errors
|
||||
}
|
||||
|
||||
// Find unions in embedded types, unions shouldn't go across types.
|
||||
func parseEmbeddedUnion(t *types.Type) ([]union, []error) {
|
||||
errors := []error{}
|
||||
unions := []union{}
|
||||
for _, m := range t.Members {
|
||||
if hasOpenAPITagValue(m.CommentLines, tagValueFalse) {
|
||||
continue
|
||||
}
|
||||
if !shouldInlineMembers(&m) {
|
||||
continue
|
||||
}
|
||||
u, err := parseUnions(m.Type)
|
||||
unions = append(unions, u...)
|
||||
errors = append(errors, err...)
|
||||
}
|
||||
return unions, errors
|
||||
}
|
||||
|
||||
// Look for union tag on a struct, and then include all the fields
|
||||
// (except the discriminator if there is one). The struct shouldn't have
|
||||
// embedded types.
|
||||
func parseUnionStruct(t *types.Type) (*union, []error) {
|
||||
errors := []error{}
|
||||
if types.ExtractCommentTags("+", t.CommentLines)[tagUnionMember] == nil {
|
||||
return nil, nil
|
||||
}
|
||||
|
||||
u := &union{fieldsToDiscriminated: map[string]string{}}
|
||||
|
||||
for _, m := range t.Members {
|
||||
jsonName := getReferableName(&m)
|
||||
if jsonName == "" {
|
||||
continue
|
||||
}
|
||||
if shouldInlineMembers(&m) {
|
||||
errors = append(errors, fmt.Errorf("union structures can't have embedded fields: %v.%v", t.Name, m.Name))
|
||||
continue
|
||||
}
|
||||
if types.ExtractCommentTags("+", m.CommentLines)[tagUnionDeprecated] != nil {
|
||||
errors = append(errors, fmt.Errorf("union struct can't have unionDeprecated members: %v.%v", t.Name, m.Name))
|
||||
continue
|
||||
}
|
||||
if types.ExtractCommentTags("+", m.CommentLines)[tagUnionDiscriminator] != nil {
|
||||
errors = append(errors, u.setDiscriminator(jsonName)...)
|
||||
} else {
|
||||
if !hasOptionalTag(&m) {
|
||||
errors = append(errors, fmt.Errorf("union members must be optional: %v.%v", t.Name, m.Name))
|
||||
}
|
||||
u.addMember(jsonName, m.Name)
|
||||
}
|
||||
}
|
||||
|
||||
return u, errors
|
||||
}
|
||||
|
||||
// Find unions specifically on members.
|
||||
func parseUnionMembers(t *types.Type) (*union, []error) {
|
||||
errors := []error{}
|
||||
u := &union{fieldsToDiscriminated: map[string]string{}}
|
||||
|
||||
for _, m := range t.Members {
|
||||
jsonName := getReferableName(&m)
|
||||
if jsonName == "" {
|
||||
continue
|
||||
}
|
||||
if shouldInlineMembers(&m) {
|
||||
continue
|
||||
}
|
||||
if types.ExtractCommentTags("+", m.CommentLines)[tagUnionDiscriminator] != nil {
|
||||
errors = append(errors, u.setDiscriminator(jsonName)...)
|
||||
}
|
||||
if types.ExtractCommentTags("+", m.CommentLines)[tagUnionMember] != nil {
|
||||
errors = append(errors, fmt.Errorf("union tag is not accepted on struct members: %v.%v", t.Name, m.Name))
|
||||
continue
|
||||
}
|
||||
if types.ExtractCommentTags("+", m.CommentLines)[tagUnionDeprecated] != nil {
|
||||
if !hasOptionalTag(&m) {
|
||||
errors = append(errors, fmt.Errorf("union members must be optional: %v.%v", t.Name, m.Name))
|
||||
}
|
||||
u.addMember(jsonName, m.Name)
|
||||
}
|
||||
}
|
||||
if len(u.fieldsToDiscriminated) == 0 {
|
||||
return nil, nil
|
||||
}
|
||||
return u, append(errors, u.isValid()...)
|
||||
}
|
27
vendor/k8s.io/kube-openapi/pkg/util/sets/empty.go
generated
vendored
27
vendor/k8s.io/kube-openapi/pkg/util/sets/empty.go
generated
vendored
@@ -1,27 +0,0 @@
|
||||
/*
|
||||
Copyright 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 set-gen. DO NOT EDIT.
|
||||
|
||||
// NOTE: This file is copied from k8s.io/kubernetes/vendor/k8s.io/apimachinery/pkg/util/sets/empty.go
|
||||
// because in Kubernetes we don't allowed vendor code to import staging code. See
|
||||
// https://github.com/kubernetes/kube-openapi/pull/90 for more details.
|
||||
|
||||
package sets
|
||||
|
||||
// Empty is public since it is used by some internal API objects for conversions between external
|
||||
// string arrays and internal sets, and conversion logic requires public types today.
|
||||
type Empty struct{}
|
207
vendor/k8s.io/kube-openapi/pkg/util/sets/string.go
generated
vendored
207
vendor/k8s.io/kube-openapi/pkg/util/sets/string.go
generated
vendored
@@ -1,207 +0,0 @@
|
||||
/*
|
||||
Copyright 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 set-gen. DO NOT EDIT.
|
||||
|
||||
// NOTE: This file is copied from k8s.io/kubernetes/vendor/k8s.io/apimachinery/pkg/util/sets/string.go
|
||||
// because in Kubernetes we don't allowed vendor code to import staging code. See
|
||||
// https://github.com/kubernetes/kube-openapi/pull/90 for more details.
|
||||
|
||||
package sets
|
||||
|
||||
import (
|
||||
"reflect"
|
||||
"sort"
|
||||
)
|
||||
|
||||
// sets.String is a set of strings, implemented via map[string]struct{} for minimal memory consumption.
|
||||
type String map[string]Empty
|
||||
|
||||
// NewString creates a String from a list of values.
|
||||
func NewString(items ...string) String {
|
||||
ss := String{}
|
||||
ss.Insert(items...)
|
||||
return ss
|
||||
}
|
||||
|
||||
// StringKeySet creates a String from a keys of a map[string](? extends interface{}).
|
||||
// If the value passed in is not actually a map, this will panic.
|
||||
func StringKeySet(theMap interface{}) String {
|
||||
v := reflect.ValueOf(theMap)
|
||||
ret := String{}
|
||||
|
||||
for _, keyValue := range v.MapKeys() {
|
||||
ret.Insert(keyValue.Interface().(string))
|
||||
}
|
||||
return ret
|
||||
}
|
||||
|
||||
// Insert adds items to the set.
|
||||
func (s String) Insert(items ...string) {
|
||||
for _, item := range items {
|
||||
s[item] = Empty{}
|
||||
}
|
||||
}
|
||||
|
||||
// Delete removes all items from the set.
|
||||
func (s String) Delete(items ...string) {
|
||||
for _, item := range items {
|
||||
delete(s, item)
|
||||
}
|
||||
}
|
||||
|
||||
// Has returns true if and only if item is contained in the set.
|
||||
func (s String) Has(item string) bool {
|
||||
_, contained := s[item]
|
||||
return contained
|
||||
}
|
||||
|
||||
// HasAll returns true if and only if all items are contained in the set.
|
||||
func (s String) HasAll(items ...string) bool {
|
||||
for _, item := range items {
|
||||
if !s.Has(item) {
|
||||
return false
|
||||
}
|
||||
}
|
||||
return true
|
||||
}
|
||||
|
||||
// HasAny returns true if any items are contained in the set.
|
||||
func (s String) HasAny(items ...string) bool {
|
||||
for _, item := range items {
|
||||
if s.Has(item) {
|
||||
return true
|
||||
}
|
||||
}
|
||||
return false
|
||||
}
|
||||
|
||||
// Difference returns a set of objects that are not in s2
|
||||
// For example:
|
||||
// s1 = {a1, a2, a3}
|
||||
// s2 = {a1, a2, a4, a5}
|
||||
// s1.Difference(s2) = {a3}
|
||||
// s2.Difference(s1) = {a4, a5}
|
||||
func (s String) Difference(s2 String) String {
|
||||
result := NewString()
|
||||
for key := range s {
|
||||
if !s2.Has(key) {
|
||||
result.Insert(key)
|
||||
}
|
||||
}
|
||||
return result
|
||||
}
|
||||
|
||||
// Union returns a new set which includes items in either s1 or s2.
|
||||
// For example:
|
||||
// s1 = {a1, a2}
|
||||
// s2 = {a3, a4}
|
||||
// s1.Union(s2) = {a1, a2, a3, a4}
|
||||
// s2.Union(s1) = {a1, a2, a3, a4}
|
||||
func (s1 String) Union(s2 String) String {
|
||||
result := NewString()
|
||||
for key := range s1 {
|
||||
result.Insert(key)
|
||||
}
|
||||
for key := range s2 {
|
||||
result.Insert(key)
|
||||
}
|
||||
return result
|
||||
}
|
||||
|
||||
// Intersection returns a new set which includes the item in BOTH s1 and s2
|
||||
// For example:
|
||||
// s1 = {a1, a2}
|
||||
// s2 = {a2, a3}
|
||||
// s1.Intersection(s2) = {a2}
|
||||
func (s1 String) Intersection(s2 String) String {
|
||||
var walk, other String
|
||||
result := NewString()
|
||||
if s1.Len() < s2.Len() {
|
||||
walk = s1
|
||||
other = s2
|
||||
} else {
|
||||
walk = s2
|
||||
other = s1
|
||||
}
|
||||
for key := range walk {
|
||||
if other.Has(key) {
|
||||
result.Insert(key)
|
||||
}
|
||||
}
|
||||
return result
|
||||
}
|
||||
|
||||
// IsSuperset returns true if and only if s1 is a superset of s2.
|
||||
func (s1 String) IsSuperset(s2 String) bool {
|
||||
for item := range s2 {
|
||||
if !s1.Has(item) {
|
||||
return false
|
||||
}
|
||||
}
|
||||
return true
|
||||
}
|
||||
|
||||
// Equal returns true if and only if s1 is equal (as a set) to s2.
|
||||
// Two sets are equal if their membership is identical.
|
||||
// (In practice, this means same elements, order doesn't matter)
|
||||
func (s1 String) Equal(s2 String) bool {
|
||||
return len(s1) == len(s2) && s1.IsSuperset(s2)
|
||||
}
|
||||
|
||||
type sortableSliceOfString []string
|
||||
|
||||
func (s sortableSliceOfString) Len() int { return len(s) }
|
||||
func (s sortableSliceOfString) Less(i, j int) bool { return lessString(s[i], s[j]) }
|
||||
func (s sortableSliceOfString) Swap(i, j int) { s[i], s[j] = s[j], s[i] }
|
||||
|
||||
// List returns the contents as a sorted string slice.
|
||||
func (s String) List() []string {
|
||||
res := make(sortableSliceOfString, 0, len(s))
|
||||
for key := range s {
|
||||
res = append(res, key)
|
||||
}
|
||||
sort.Sort(res)
|
||||
return []string(res)
|
||||
}
|
||||
|
||||
// UnsortedList returns the slice with contents in random order.
|
||||
func (s String) UnsortedList() []string {
|
||||
res := make([]string, 0, len(s))
|
||||
for key := range s {
|
||||
res = append(res, key)
|
||||
}
|
||||
return res
|
||||
}
|
||||
|
||||
// Returns a single element from the set.
|
||||
func (s String) PopAny() (string, bool) {
|
||||
for key := range s {
|
||||
s.Delete(key)
|
||||
return key, true
|
||||
}
|
||||
var zeroValue string
|
||||
return zeroValue, false
|
||||
}
|
||||
|
||||
// Len returns the size of the set.
|
||||
func (s String) Len() int {
|
||||
return len(s)
|
||||
}
|
||||
|
||||
func lessString(lhs, rhs string) bool {
|
||||
return lhs < rhs
|
||||
}
|
Reference in New Issue
Block a user