Merge pull request #70260 from lavalamp/schema-fix

bump kube-openapi dep

Kubernetes-commit: 6f897af2daffd8cd3539f1ef279b9f9bc280d98f
This commit is contained in:
Kubernetes Publisher 2018-10-31 23:04:56 -07:00
commit 8c6eeb11d7
27 changed files with 589 additions and 387 deletions

490
Godeps/Godeps.json generated

File diff suppressed because it is too large Load Diff

View File

@ -223,7 +223,7 @@ message Webhook {
// connection with the webhook
message WebhookClientConfig {
// `url` gives the location of the webhook, in standard URL form
// (`[scheme://]host:port/path`). Exactly one of `url` or `service`
// (`scheme://host:port/path`). Exactly one of `url` or `service`
// must be specified.
//
// The `host` should not refer to a service running in the cluster; use

View File

@ -246,7 +246,7 @@ const (
// connection with the webhook
type WebhookClientConfig struct {
// `url` gives the location of the webhook, in standard URL form
// (`[scheme://]host:port/path`). Exactly one of `url` or `service`
// (`scheme://host:port/path`). Exactly one of `url` or `service`
// must be specified.
//
// The `host` should not refer to a service running in the cluster; use

View File

@ -114,7 +114,7 @@ func (Webhook) SwaggerDoc() map[string]string {
var map_WebhookClientConfig = map[string]string{
"": "WebhookClientConfig contains the information to make a TLS connection with the webhook",
"url": "`url` gives the location of the webhook, in standard URL form (`[scheme://]host:port/path`). Exactly one of `url` or `service` must be specified.\n\nThe `host` should not refer to a service running in the cluster; use the `service` field instead. The host might be resolved via external DNS in some apiservers (e.g., `kube-apiserver` cannot resolve in-cluster DNS as that would be a layering violation). `host` may also be an IP address.\n\nPlease note that using `localhost` or `127.0.0.1` as a `host` is risky unless you take great care to run this webhook on all hosts which run an apiserver which might need to make calls to this webhook. Such installs are likely to be non-portable, i.e., not easy to turn up in a new cluster.\n\nThe scheme must be \"https\"; the URL must begin with \"https://\".\n\nA path is optional, and if present may be any string permissible in a URL. You may use the path to pass an arbitrary string to the webhook, for example, a cluster identifier.\n\nAttempting to use a user or basic auth e.g. \"user:password@\" is not allowed. Fragments (\"#...\") and query parameters (\"?...\") are not allowed, either.",
"url": "`url` gives the location of the webhook, in standard URL form (`scheme://host:port/path`). Exactly one of `url` or `service` must be specified.\n\nThe `host` should not refer to a service running in the cluster; use the `service` field instead. The host might be resolved via external DNS in some apiservers (e.g., `kube-apiserver` cannot resolve in-cluster DNS as that would be a layering violation). `host` may also be an IP address.\n\nPlease note that using `localhost` or `127.0.0.1` as a `host` is risky unless you take great care to run this webhook on all hosts which run an apiserver which might need to make calls to this webhook. Such installs are likely to be non-portable, i.e., not easy to turn up in a new cluster.\n\nThe scheme must be \"https\"; the URL must begin with \"https://\".\n\nA path is optional, and if present may be any string permissible in a URL. You may use the path to pass an arbitrary string to the webhook, for example, a cluster identifier.\n\nAttempting to use a user or basic auth e.g. \"user:password@\" is not allowed. Fragments (\"#...\") and query parameters (\"?...\") are not allowed, either.",
"service": "`service` is a reference to the service for this webhook. Either `service` or `url` must be specified.\n\nIf the webhook is running within the cluster, then you should use `service`.\n\nPort 443 will be used if it is open, otherwise it is an error.",
"caBundle": "`caBundle` is a PEM encoded CA bundle which will be used to validate the webhook's server certificate. If unspecified, system trust roots on the apiserver are used.",
}

View File

@ -280,6 +280,7 @@ message DeploymentSpec {
// The deployment strategy to use to replace existing pods with new ones.
// +optional
// +patchStrategy=retainKeys
optional DeploymentStrategy strategy = 4;
// Minimum number of seconds for which a newly created pod should be ready

3
vendor/k8s.io/api/apps/v1/types.go generated vendored
View File

@ -279,7 +279,8 @@ type DeploymentSpec struct {
// The deployment strategy to use to replace existing pods with new ones.
// +optional
Strategy DeploymentStrategy `json:"strategy,omitempty" protobuf:"bytes,4,opt,name=strategy"`
// +patchStrategy=retainKeys
Strategy DeploymentStrategy `json:"strategy,omitempty" patchStrategy:"retainKeys" protobuf:"bytes,4,opt,name=strategy"`
// Minimum number of seconds for which a newly created pod should be ready
// without any of its container crashing, for it to be considered available.

View File

@ -143,6 +143,7 @@ message DeploymentSpec {
// The deployment strategy to use to replace existing pods with new ones.
// +optional
// +patchStrategy=retainKeys
optional DeploymentStrategy strategy = 4;
// Minimum number of seconds for which a newly created pod should be ready

View File

@ -323,7 +323,8 @@ type DeploymentSpec struct {
// The deployment strategy to use to replace existing pods with new ones.
// +optional
Strategy DeploymentStrategy `json:"strategy,omitempty" protobuf:"bytes,4,opt,name=strategy"`
// +patchStrategy=retainKeys
Strategy DeploymentStrategy `json:"strategy,omitempty" patchStrategy:"retainKeys" protobuf:"bytes,4,opt,name=strategy"`
// Minimum number of seconds for which a newly created pod should be ready
// without any of its container crashing, for it to be considered available.

View File

@ -286,6 +286,7 @@ message DeploymentSpec {
// The deployment strategy to use to replace existing pods with new ones.
// +optional
// +patchStrategy=retainKeys
optional DeploymentStrategy strategy = 4;
// Minimum number of seconds for which a newly created pod should be ready

View File

@ -331,7 +331,8 @@ type DeploymentSpec struct {
// The deployment strategy to use to replace existing pods with new ones.
// +optional
Strategy DeploymentStrategy `json:"strategy,omitempty" protobuf:"bytes,4,opt,name=strategy"`
// +patchStrategy=retainKeys
Strategy DeploymentStrategy `json:"strategy,omitempty" patchStrategy:"retainKeys" protobuf:"bytes,4,opt,name=strategy"`
// Minimum number of seconds for which a newly created pod should be ready
// without any of its container crashing, for it to be considered available.

View File

@ -99,7 +99,7 @@ message Webhook {
// WebhookClientConfig contains the information to make a connection with the webhook
message WebhookClientConfig {
// `url` gives the location of the webhook, in standard URL form
// (`[scheme://]host:port/path`). Exactly one of `url` or `service`
// (`scheme://host:port/path`). Exactly one of `url` or `service`
// must be specified.
//
// The `host` should not refer to a service running in the cluster; use

View File

@ -133,7 +133,7 @@ type WebhookThrottleConfig struct {
// WebhookClientConfig contains the information to make a connection with the webhook
type WebhookClientConfig struct {
// `url` gives the location of the webhook, in standard URL form
// (`[scheme://]host:port/path`). Exactly one of `url` or `service`
// (`scheme://host:port/path`). Exactly one of `url` or `service`
// must be specified.
//
// The `host` should not refer to a service running in the cluster; use

View File

@ -88,7 +88,7 @@ func (Webhook) SwaggerDoc() map[string]string {
var map_WebhookClientConfig = map[string]string{
"": "WebhookClientConfig contains the information to make a connection with the webhook",
"url": "`url` gives the location of the webhook, in standard URL form (`[scheme://]host:port/path`). Exactly one of `url` or `service` must be specified.\n\nThe `host` should not refer to a service running in the cluster; use the `service` field instead. The host might be resolved via external DNS in some apiservers (e.g., `kube-apiserver` cannot resolve in-cluster DNS as that would be a layering violation). `host` may also be an IP address.\n\nPlease note that using `localhost` or `127.0.0.1` as a `host` is risky unless you take great care to run this webhook on all hosts which run an apiserver which might need to make calls to this webhook. Such installs are likely to be non-portable, i.e., not easy to turn up in a new cluster.\n\nThe scheme must be \"https\"; the URL must begin with \"https://\".\n\nA path is optional, and if present may be any string permissible in a URL. You may use the path to pass an arbitrary string to the webhook, for example, a cluster identifier.\n\nAttempting to use a user or basic auth e.g. \"user:password@\" is not allowed. Fragments (\"#...\") and query parameters (\"?...\") are not allowed, either.",
"url": "`url` gives the location of the webhook, in standard URL form (`scheme://host:port/path`). Exactly one of `url` or `service` must be specified.\n\nThe `host` should not refer to a service running in the cluster; use the `service` field instead. The host might be resolved via external DNS in some apiservers (e.g., `kube-apiserver` cannot resolve in-cluster DNS as that would be a layering violation). `host` may also be an IP address.\n\nPlease note that using `localhost` or `127.0.0.1` as a `host` is risky unless you take great care to run this webhook on all hosts which run an apiserver which might need to make calls to this webhook. Such installs are likely to be non-portable, i.e., not easy to turn up in a new cluster.\n\nThe scheme must be \"https\"; the URL must begin with \"https://\".\n\nA path is optional, and if present may be any string permissible in a URL. You may use the path to pass an arbitrary string to the webhook, for example, a cluster identifier.\n\nAttempting to use a user or basic auth e.g. \"user:password@\" is not allowed. Fragments (\"#...\") and query parameters (\"?...\") are not allowed, either.",
"service": "`service` is a reference to the service for this webhook. Either `service` or `url` must be specified.\n\nIf the webhook is running within the cluster, then you should use `service`.\n\nPort 443 will be used if it is open, otherwise it is an error.",
"caBundle": "`caBundle` is a PEM encoded CA bundle which will be used to validate the webhook's server certificate. If unspecified, system trust roots on the apiserver are used.",
}

View File

@ -620,8 +620,8 @@ message ObjectMeta {
}
// OwnerReference contains enough information to let you identify an owning
// object. Currently, an owning object must be in the same namespace, so there
// is no namespace field.
// object. An owning object must be in the same namespace as the dependent, or
// be cluster-scoped, so there is no namespace field.
message OwnerReference {
// API version of the referent.
optional string apiVersion = 5;

View File

@ -286,8 +286,8 @@ const (
)
// OwnerReference contains enough information to let you identify an owning
// object. Currently, an owning object must be in the same namespace, so there
// is no namespace field.
// object. An owning object must be in the same namespace as the dependent, or
// be cluster-scoped, so there is no namespace field.
type OwnerReference struct {
// API version of the referent.
APIVersion string `json:"apiVersion" protobuf:"bytes,5,opt,name=apiVersion"`

View File

@ -240,7 +240,7 @@ func (ObjectMeta) SwaggerDoc() map[string]string {
}
var map_OwnerReference = map[string]string{
"": "OwnerReference contains enough information to let you identify an owning object. Currently, an owning object must be in the same namespace, so there is no namespace field.",
"": "OwnerReference contains enough information to let you identify an owning object. An owning object must be in the same namespace as the dependent, or be cluster-scoped, so there is no namespace field.",
"apiVersion": "API version of the referent.",
"kind": "Kind of the referent. More info: https://git.k8s.io/community/contributors/devel/api-conventions.md#types-kinds",
"name": "Name of the referent. More info: http://kubernetes.io/docs/user-guide/identifiers#names",

View File

@ -260,23 +260,23 @@
},
{
"ImportPath": "k8s.io/kube-openapi/cmd/openapi-gen/args",
"Rev": "0cf8f7e6ed1d2e3d47d02e3b6e559369af24d803"
"Rev": "72693cb1fadd73ae2742f6fe29af77d1aecdd8cd"
},
{
"ImportPath": "k8s.io/kube-openapi/pkg/common",
"Rev": "0cf8f7e6ed1d2e3d47d02e3b6e559369af24d803"
"Rev": "72693cb1fadd73ae2742f6fe29af77d1aecdd8cd"
},
{
"ImportPath": "k8s.io/kube-openapi/pkg/generators",
"Rev": "0cf8f7e6ed1d2e3d47d02e3b6e559369af24d803"
"Rev": "72693cb1fadd73ae2742f6fe29af77d1aecdd8cd"
},
{
"ImportPath": "k8s.io/kube-openapi/pkg/generators/rules",
"Rev": "0cf8f7e6ed1d2e3d47d02e3b6e559369af24d803"
"Rev": "72693cb1fadd73ae2742f6fe29af77d1aecdd8cd"
},
{
"ImportPath": "k8s.io/kube-openapi/pkg/util/sets",
"Rev": "0cf8f7e6ed1d2e3d47d02e3b6e559369af24d803"
"Rev": "72693cb1fadd73ae2742f6fe29af77d1aecdd8cd"
}
]
}

View File

@ -59,6 +59,12 @@ type Config struct {
// 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

View File

@ -4,8 +4,9 @@
- 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
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
@ -17,6 +18,7 @@ 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"
// ...
@ -35,12 +37,13 @@ the following signature:
},
}
}
```
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" }
TODO(mehdy): Make k8s:openapi-gen a parameter to the generator now that OpenAPI has its own repo.
```

View File

@ -17,16 +17,114 @@ limitations under the License.
package generators
import (
"bytes"
"fmt"
"io"
"io/ioutil"
"os"
"sort"
"k8s.io/kube-openapi/pkg/generators/rules"
"github.com/golang/glog"
"k8s.io/gengo/generator"
"k8s.io/gengo/types"
)
// apiLinter is the framework hosting mutliple API rules and recording API rule
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
glog.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 {
glog.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
@ -40,6 +138,7 @@ func newAPILinter() *apiLinter {
return &apiLinter{
rules: []APIRule{
&rules.NamesMatch{},
&rules.OmitEmptyMatchCase{},
},
}
}
@ -57,6 +156,25 @@ type apiViolation struct {
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
@ -90,6 +208,7 @@ func (l *apiLinter) validate(t *types.Type) error {
// 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)
}

View File

@ -0,0 +1,91 @@
/*
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"
"github.com/golang/glog"
"k8s.io/gengo/args"
"k8s.io/gengo/generator"
"k8s.io/gengo/namer"
"k8s.io/gengo/types"
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 {
glog.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,
},
}
}

View File

@ -36,20 +36,20 @@ type extensionAttributes struct {
// Extension tag to openapi extension attributes
var tagToExtension = map[string]extensionAttributes{
"patchMergeKey": extensionAttributes{
"patchMergeKey": {
xName: "x-kubernetes-patch-merge-key",
kind: types.Slice,
},
"patchStrategy": extensionAttributes{
"patchStrategy": {
xName: "x-kubernetes-patch-strategy",
kind: types.Slice,
allowedValues: sets.NewString("merge", "retainKeys"),
},
"listMapKey": extensionAttributes{
"listMapKey": {
xName: "x-kubernetes-list-map-keys",
kind: types.Slice,
},
"listType": extensionAttributes{
"listType": {
xName: "x-kubernetes-list-type",
kind: types.Slice,
allowedValues: sets.NewString("atomic", "set", "map"),

View File

@ -20,17 +20,14 @@ import (
"bytes"
"fmt"
"io"
"os"
"path/filepath"
"reflect"
"sort"
"strings"
"k8s.io/gengo/args"
"k8s.io/gengo/generator"
"k8s.io/gengo/namer"
"k8s.io/gengo/types"
generatorargs "k8s.io/kube-openapi/cmd/openapi-gen/args"
openapi "k8s.io/kube-openapi/pkg/common"
"github.com/golang/glog"
@ -88,69 +85,19 @@ func hasOptionalTag(m *types.Member) bool {
return hasOptionalCommentTag || hasOptionalJsonTag
}
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{},
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
}
}
// 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 {
glog.Fatalf("Failed loading boilerplate: %v", err)
pkg := c.Universe.Package(t.Name.Package)
if hasOpenAPITagValue(pkg.Comments, tagValueTrue) {
return !hasOpenAPITagValue(t.CommentLines, tagValueFalse)
}
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!
`)...)
reportFilename := "-"
if customArgs, ok := arguments.CustomArgs.(*generatorargs.CustomArgs); ok {
reportFilename = customArgs.ReportFilename
}
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, context, newAPILinter(), reportFilename)}
},
FilterFunc: func(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 := context.Universe.Package(t.Name.Package)
if hasOpenAPITagValue(pkg.Comments, tagValueTrue) {
return !hasOpenAPITagValue(t.CommentLines, tagValueFalse)
}
if hasOpenAPITagValue(t.CommentLines, tagValueTrue) {
return true
}
return false
},
},
if hasOpenAPITagValue(t.CommentLines, tagValueTrue) {
return true
}
return false
}
const (
@ -162,24 +109,17 @@ const (
type openAPIGen struct {
generator.DefaultGen
// TargetPackage is the package that will get GetOpenAPIDefinitions function returns all open API definitions.
targetPackage string
imports namer.ImportTracker
types []*types.Type
context *generator.Context
linter *apiLinter
reportFilename string
targetPackage string
imports namer.ImportTracker
}
func NewOpenAPIGen(sanitizedName string, targetPackage string, context *generator.Context, linter *apiLinter, reportFilename string) generator.Generator {
func newOpenAPIGen(sanitizedName string, targetPackage string) generator.Generator {
return &openAPIGen{
DefaultGen: generator.DefaultGen{
OptionalName: sanitizedName,
},
imports: generator.NewImportTracker(),
targetPackage: targetPackage,
context: context,
linter: linter,
reportFilename: reportFilename,
imports: generator.NewImportTracker(),
targetPackage: targetPackage,
}
}
@ -198,15 +138,6 @@ func (g *openAPIGen) Namers(c *generator.Context) namer.NameSystems {
}
}
func (g *openAPIGen) Filter(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
}
g.types = append(g.types, t)
return true
}
func (g *openAPIGen) isOtherPackage(pkg string) bool {
if pkg == g.targetPackage {
return false
@ -239,7 +170,7 @@ func (g *openAPIGen) Init(c *generator.Context, w io.Writer) error {
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 g.types {
for _, t := range c.Order {
err := newOpenAPITypeWriter(sw).generateCall(t)
if err != nil {
return err
@ -253,10 +184,6 @@ func (g *openAPIGen) Init(c *generator.Context, w io.Writer) error {
}
func (g *openAPIGen) GenerateType(c *generator.Context, t *types.Type, w io.Writer) error {
glog.V(5).Infof("validating API rules for type %v", t)
if err := g.linter.validate(t); err != nil {
return err
}
glog.V(5).Infof("generating for type %v", t)
sw := generator.NewSnippetWriter(w, c, "$", "$")
err := newOpenAPITypeWriter(sw).generate(t)
@ -678,27 +605,3 @@ func (g openAPITypeWriter) generateSliceProperty(t *types.Type) error {
g.Do("},\n},\n},\n", nil)
return nil
}
// Finalize prints the API rule violations to report file (if specified from arguments) or stdout (default)
func (g *openAPIGen) Finalize(c *generator.Context, w io.Writer) error {
// If report file isn't specified, return error to force user to choose either stdout ("-") or a file name
if len(g.reportFilename) == 0 {
return fmt.Errorf("empty report file name: please provide a valid file name or use the default \"-\" (stdout)")
}
// If stdout is specified, print violations and return error
if g.reportFilename == "-" {
return g.linter.report(os.Stdout)
}
// Otherwise, print violations to report file and return nil
f, err := os.Create(g.reportFilename)
if err != nil {
return err
}
defer f.Close()
g.linter.report(f)
// 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.
return nil
}

View File

@ -0,0 +1,4 @@
reviewers:
- roycaihw
approvers:
- roycaihw

View File

@ -0,0 +1,64 @@
/*
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
}

View File

@ -196,20 +196,24 @@ func (d *Definitions) parseKind(s *openapi_v2.Schema, path *Path) (Schema, error
}
fields := map[string]Schema{}
fieldOrder := []string{}
for _, namedSchema := range s.GetProperties().GetAdditionalProperties() {
var err error
path := path.FieldPath(namedSchema.GetName())
fields[namedSchema.GetName()], err = d.ParseSchema(namedSchema.GetValue(), &path)
name := namedSchema.GetName()
path := path.FieldPath(name)
fields[name], err = d.ParseSchema(namedSchema.GetValue(), &path)
if err != nil {
return nil, err
}
fieldOrder = append(fieldOrder, name)
}
return &Kind{
BaseSchema: d.parseBaseSchema(s, path),
RequiredFields: s.GetRequired(),
Fields: fields,
FieldOrder: fieldOrder,
}, nil
}

View File

@ -173,6 +173,8 @@ type Kind struct {
RequiredFields []string
// Maps field names to types.
Fields map[string]Schema
// FieldOrder reports the canonical order for the fields.
FieldOrder []string
}
var _ Schema = &Kind{}