diff --git a/cmd/openapi-gen/args/args.go b/cmd/openapi-gen/args/args.go index 19783370e..d53282b7a 100644 --- a/cmd/openapi-gen/args/args.go +++ b/cmd/openapi-gen/args/args.go @@ -18,59 +18,61 @@ 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 +type Args struct { + OutputDir string // must be a directory path + OutputPkg string // must be a Go import-path + OutputFile string + + GoHeaderFile string + + // ReportFilename is added to Args 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 +// New 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 +func New() *Args { + args := &Args{} // Default value for report filename is "-", which stands for stdout - customArgs.ReportFilename = "-" - // Default value for output file base name - genericArgs.OutputFileBaseName = "openapi_generated" + args.ReportFilename = "-" - return genericArgs, customArgs + return args } // 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.") +func (args *Args) AddFlags(fs *pflag.FlagSet) { + fs.StringVar(&args.OutputDir, "output-dir", "", + "the base directory under which to generate results") + fs.StringVar(&args.OutputPkg, "output-pkg", "", + "the base Go import-path under which to generate results") + fs.StringVar(&args.OutputFile, "output-file", "generated.openapi.go", + "the name of the file to be generated") + fs.StringVar(&args.GoHeaderFile, "go-header-file", "", + "the path to a file containing boilerplate header text; the string \"YEAR\" will be replaced with the current 4-digit year") + fs.StringVarP(&args.ReportFilename, "report-filename", "r", "-", + "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") +func (args *Args) Validate() error { + if len(args.OutputDir) == 0 { + return fmt.Errorf("--output-dir must be specified") } - if len(c.ReportFilename) == 0 { - return fmt.Errorf("report filename cannot be empty. specify a valid filename or use \"-\" for stdout") + if len(args.OutputPkg) == 0 { + return fmt.Errorf("--output-pkg must be specified") } - if len(genericArgs.OutputFileBaseName) == 0 { - return fmt.Errorf("output file base name cannot be empty") + if len(args.OutputFile) == 0 { + return fmt.Errorf("--output-file must be specified") } - if len(genericArgs.OutputPackagePath) == 0 { - return fmt.Errorf("output package cannot be empty") + if len(args.ReportFilename) == 0 { + return fmt.Errorf("--report-filename must be specified (use \"-\" for stdout)") } return nil } diff --git a/cmd/openapi-gen/openapi-gen.go b/cmd/openapi-gen/openapi-gen.go index c446e80b8..b466019ad 100644 --- a/cmd/openapi-gen/openapi-gen.go +++ b/cmd/openapi-gen/openapi-gen.go @@ -24,33 +24,38 @@ import ( "flag" "log" - generatorargs "k8s.io/kube-openapi/cmd/openapi-gen/args" - "k8s.io/kube-openapi/pkg/generators" - "github.com/spf13/pflag" - + "k8s.io/gengo/v2" + "k8s.io/gengo/v2/generator" "k8s.io/klog/v2" + "k8s.io/kube-openapi/cmd/openapi-gen/args" + "k8s.io/kube-openapi/pkg/generators" ) func main() { klog.InitFlags(nil) - genericArgs, customArgs := generatorargs.NewDefaults() + args := args.New() - genericArgs.AddFlags(pflag.CommandLine) - customArgs.AddFlags(pflag.CommandLine) + args.AddFlags(pflag.CommandLine) flag.Set("logtostderr", "true") pflag.CommandLine.AddGoFlagSet(flag.CommandLine) pflag.Parse() - if err := generatorargs.Validate(genericArgs); err != nil { + if err := args.Validate(); err != nil { log.Fatalf("Arguments validation error: %v", err) } + myTargets := func(context *generator.Context) []generator.Target { + return generators.GetTargets(context, args) + } + // Generates the code for the OpenAPIDefinitions. - if err := genericArgs.Execute( + if err := gengo.Execute( generators.NameSystems(), generators.DefaultNameSystem(), - generators.Packages, + myTargets, + gengo.StdBuildTag, + pflag.Args(), ); err != nil { log.Fatalf("OpenAPI code generation error: %v", err) } diff --git a/go.mod b/go.mod index 1f7b6c933..ead437565 100644 --- a/go.mod +++ b/go.mod @@ -1,6 +1,6 @@ module k8s.io/kube-openapi -go 1.21 +go 1.21.3 require ( github.com/NYTimes/gziphandler v0.0.0-20170623195520-56545f4a5d46 @@ -18,10 +18,11 @@ require ( github.com/onsi/gomega v1.19.0 github.com/spf13/pflag v1.0.5 github.com/stretchr/testify v1.8.1 + golang.org/x/tools v0.18.0 google.golang.org/protobuf v1.27.1 gopkg.in/yaml.v2 v2.4.0 gopkg.in/yaml.v3 v3.0.1 - k8s.io/gengo v0.0.0-20230829151522-9cce18d56c01 + k8s.io/gengo/v2 v2.0.0-20240226174109-00c4be8627da k8s.io/klog/v2 v2.80.1 k8s.io/utils v0.0.0-20230726121419-3b25d923346b sigs.k8s.io/json v0.0.0-20221116044647-bc3834ca7abd @@ -39,10 +40,9 @@ require ( github.com/modern-go/concurrent v0.0.0-20180306012644-bacd9c7ef1dd // indirect github.com/modern-go/reflect2 v1.0.2 // indirect github.com/pmezard/go-difflib v1.0.0 // indirect - golang.org/x/mod v0.7.0 // indirect - golang.org/x/net v0.7.0 // indirect - golang.org/x/sys v0.5.0 // indirect - golang.org/x/text v0.7.0 // indirect - golang.org/x/tools v0.4.0 // indirect + golang.org/x/mod v0.15.0 // indirect + golang.org/x/net v0.21.0 // indirect + golang.org/x/sys v0.17.0 // indirect + golang.org/x/text v0.14.0 // indirect golang.org/x/xerrors v0.0.0-20220907171357-04be3eba64a2 // indirect ) diff --git a/go.sum b/go.sum index 03cbef686..295b62b0a 100644 --- a/go.sum +++ b/go.sum @@ -8,7 +8,6 @@ github.com/davecgh/go-spew v1.1.1 h1:vj9j/u1bqnvCEfJOwUhtlOARqs3+rkHYY13jYWTU97c github.com/davecgh/go-spew v1.1.1/go.mod h1:J7Y8YcW2NihsgmVo/mv3lAwl/skON4iLHjSsI+c5H38= github.com/emicklei/go-restful/v3 v3.8.0 h1:eCZ8ulSerjdAiaNpF7GxXIE7ZCMo1moN1qX+S609eVw= github.com/emicklei/go-restful/v3 v3.8.0/go.mod h1:6n3XBCmQQb25CM2LCACGz8ukIrRry+4bhvbpWn3mrbc= -github.com/go-logr/logr v0.2.0/go.mod h1:z6/tIYblkpsD+a4lm/fGIIU9mZ+XfAiaFtq7xTgseGU= github.com/go-logr/logr v1.2.0 h1:QK40JKJyMdUDz+h+xvCsru/bJhvG0UxvePV0ufL/AcE= github.com/go-logr/logr v1.2.0/go.mod h1:jdQByPbusPIv2/zmleS9BjJVeZ6kBagPoEUsqbVz/1A= github.com/go-openapi/jsonpointer v0.19.6 h1:eCs3fxoIi3Wh6vtgmLTOjdhSpiqphQ+DaPn38N2ZdrE= @@ -22,7 +21,6 @@ github.com/golang/protobuf v1.5.2 h1:ROPKBNFfQgOUMifHyP+KYbvpjbdoFNs+aK7DXlji0Tw github.com/golang/protobuf v1.5.2/go.mod h1:XVQd3VNwM+JqD3oG2Ue2ip4fOMUkwXdXDdiuN0vRsmY= github.com/google/gnostic-models v0.6.8 h1:yo/ABAfM5IMRsS1VnXjTBvUb61tFIHozhlYvRgGre9I= github.com/google/gnostic-models v0.6.8/go.mod h1:5n7qKqH0f5wFt+aWF8CW6pZLLNOfYuF5OpfBSENuI8U= -github.com/google/go-cmp v0.4.0/go.mod h1:v8dTdLbMG2kIc/vJvl+f65V22dbkXbowE6jgT/gNBxE= github.com/google/go-cmp v0.5.5 h1:Khx7svrCpmxxtHBq5j2mp/xVjsi8hQMfNLvJFAlrGgU= github.com/google/go-cmp v0.5.5/go.mod h1:v8dTdLbMG2kIc/vJvl+f65V22dbkXbowE6jgT/gNBxE= github.com/google/gofuzz v1.0.0/go.mod h1:dBl0BpW6vV/+mYPU4Po3pmUjxk6FQPldtuIdl/M65Eg= @@ -34,7 +32,6 @@ github.com/josharian/intern v1.0.0 h1:vlS4z54oSdjm0bgjRigI+G1HpF+tI+9rE5LLzOg8Hm github.com/josharian/intern v1.0.0/go.mod h1:5DoeVV0s6jJacbCEi61lwdGj/aVlrQvzHFFd8Hwg//Y= github.com/json-iterator/go v1.1.12 h1:PV8peI4a0ysnczrg+LtxykD8LfKY9ML6u2jnxaEnrnM= github.com/json-iterator/go v1.1.12/go.mod h1:e30LSqwooZae/UwlEbR2852Gd8hjQvJoHmT4TnhNGBo= -github.com/kr/pretty v0.2.0/go.mod h1:ipq/a2n7PKx3OHsz4KJII5eveXtPO4qwEXGdVfWzfnI= github.com/kr/pretty v0.2.1 h1:Fmg33tUaq4/8ym9TJN1x7sLJnHVwhP33CNkpYV/7rwI= github.com/kr/pretty v0.2.1/go.mod h1:ipq/a2n7PKx3OHsz4KJII5eveXtPO4qwEXGdVfWzfnI= github.com/kr/pty v1.1.1/go.mod h1:pFQYn66WHrOpPYNljwOMqo10TkYh1fy3cYio2l3bCsQ= @@ -66,32 +63,18 @@ github.com/stretchr/testify v1.7.1/go.mod h1:6Fq8oRcR53rry900zMqJjRRixrwX3KX962/ github.com/stretchr/testify v1.8.0/go.mod h1:yNjHg4UonilssWZ8iaSj1OCr/vHnekPRkoO+kdMU+MU= github.com/stretchr/testify v1.8.1 h1:w7B6lhMri9wdJUVmEZPGGhZzrYTPvgJArz7wNPgYKsk= github.com/stretchr/testify v1.8.1/go.mod h1:w2LPCIKwWwSfY2zedu0+kehJoqGctiVI29o6fzry7u4= -github.com/yuin/goldmark v1.1.27/go.mod h1:3hX8gzYuyVAZsxl0MRgGTJEmQBFcNTphYh9decYSb74= -golang.org/x/crypto v0.0.0-20190308221718-c2843e01d9a2/go.mod h1:djNgcEr1/C05ACkg1iLfiJU5Ep61QUkGW8qpdssI0+w= -golang.org/x/crypto v0.0.0-20191011191535-87dc89f01550/go.mod h1:yigFU9vqHzYiE8UmvKecakEJjdnWj3jj499lnFckfCI= -golang.org/x/mod v0.2.0/go.mod h1:s0Qsj1ACt9ePp/hMypM3fl4fZqREWJwdYDEqhRiZZUA= -golang.org/x/mod v0.7.0 h1:LapD9S96VoQRhi/GrNTqeBJFrUjs5UHCAtTlgwA5oZA= -golang.org/x/mod v0.7.0/go.mod h1:iBbtSCu2XBx23ZKBPSOrRkjjQPZFPuis4dIYUhu/chs= -golang.org/x/net v0.0.0-20190404232315-eb5bcb51f2a3/go.mod h1:t9HGtf8HONx5eT2rtn7q6eTqICYqUVnKs3thJo3Qplg= -golang.org/x/net v0.0.0-20190620200207-3b0461eec859/go.mod h1:z5CRVTTTmAJ677TzLLGU+0bjPO0LkuOLi4/5GtJWs/s= -golang.org/x/net v0.0.0-20200226121028-0de0cce0169b/go.mod h1:z5CRVTTTmAJ677TzLLGU+0bjPO0LkuOLi4/5GtJWs/s= -golang.org/x/net v0.7.0 h1:rJrUqqhjsgNp7KqAIc25s9pZnjU7TUcSY7HcVZjdn1g= -golang.org/x/net v0.7.0/go.mod h1:2Tu9+aMcznHK/AK1HMvgo6xiTLG5rD5rZLDS+rp2Bjs= -golang.org/x/sync v0.0.0-20190423024810-112230192c58/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM= -golang.org/x/sync v0.0.0-20190911185100-cd5d95a43a6e/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM= -golang.org/x/sys v0.0.0-20190215142949-d0b11bdaac8a/go.mod h1:STP8DvDyc/dI5b8T5hshtkjS+E42TnysNCUPdjciGhY= -golang.org/x/sys v0.0.0-20190412213103-97732733099d/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= -golang.org/x/sys v0.5.0 h1:MUK/U/4lj1t1oPg0HfuXDN/Z1wv31ZJ/YcPiGccS4DU= -golang.org/x/sys v0.5.0/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= -golang.org/x/text v0.3.0/go.mod h1:NqM8EUOU14njkJ3fqMW+pc6Ldnwhi/IjpwHt7yyuwOQ= -golang.org/x/text v0.7.0 h1:4BRB4x83lYWy72KwLD/qYDuTu7q9PjSagHvijDw7cLo= -golang.org/x/text v0.7.0/go.mod h1:mrYo+phRRbMaCq/xk9113O4dZlRixOauAjOtrjsXDZ8= -golang.org/x/tools v0.0.0-20191119224855-298f0cb1881e/go.mod h1:b+2E5dAYhXwXZwtnZ6UAqBI28+e2cm9otk0dWdXHAEo= -golang.org/x/tools v0.0.0-20200505023115-26f46d2f7ef8/go.mod h1:EkVYQZoAsY45+roYkvgYkIh4xh/qjgUK9TdY2XT94GE= -golang.org/x/tools v0.4.0 h1:7mTAgkunk3fr4GAloyyCasadO6h9zSsQZbwvcaIciV4= -golang.org/x/tools v0.4.0/go.mod h1:UE5sM2OK9E/d67R0ANs2xJizIymRP5gJU295PvKXxjQ= -golang.org/x/xerrors v0.0.0-20190717185122-a985d3407aa7/go.mod h1:I/5z698sn9Ka8TeJc9MKroUUfqBBauWjQqLJ2OPfmY0= -golang.org/x/xerrors v0.0.0-20191011141410-1b5146add898/go.mod h1:I/5z698sn9Ka8TeJc9MKroUUfqBBauWjQqLJ2OPfmY0= +golang.org/x/mod v0.15.0 h1:SernR4v+D55NyBH2QiEQrlBAnj1ECL6AGrA5+dPaMY8= +golang.org/x/mod v0.15.0/go.mod h1:hTbmBsO62+eylJbnUtE2MGJUyE7QWk4xUqPFrRgJ+7c= +golang.org/x/net v0.21.0 h1:AQyQV4dYCvJ7vGmJyKki9+PBdyvhkSd8EIx/qb0AYv4= +golang.org/x/net v0.21.0/go.mod h1:bIjVDfnllIU7BJ2DNgfnXvpSvtn8VRwhlsaeUTyUS44= +golang.org/x/sync v0.6.0 h1:5BMeUDZ7vkXGfEr1x9B4bRcTH4lpkTkpdh0T/J+qjbQ= +golang.org/x/sync v0.6.0/go.mod h1:Czt+wKu1gCyEFDUtn0jG5QVvpJ6rzVqr5aXyt9drQfk= +golang.org/x/sys v0.17.0 h1:25cE3gD+tdBA7lp7QfhuV+rJiE9YXTcS3VG1SqssI/Y= +golang.org/x/sys v0.17.0/go.mod h1:/VUhepiaJMQUp4+oa/7Zr1D23ma6VTLIYjOOTFZPUcA= +golang.org/x/text v0.14.0 h1:ScX5w1eTa3QqT8oi6+ziP7dTV1S2+ALU0bI+0zXKWiQ= +golang.org/x/text v0.14.0/go.mod h1:18ZOQIKpY8NJVqYksKHtTdi31H5itFRjB5/qKTNYzSU= +golang.org/x/tools v0.18.0 h1:k8NLag8AGHnn+PHbl7g43CtqZAwG60vZkLqgyZgIHgQ= +golang.org/x/tools v0.18.0/go.mod h1:GL7B4CwcLLeo59yx/9UWWuNOW1n3VZ4f5axWfML7Lcg= golang.org/x/xerrors v0.0.0-20191204190536-9bdfabe68543/go.mod h1:I/5z698sn9Ka8TeJc9MKroUUfqBBauWjQqLJ2OPfmY0= golang.org/x/xerrors v0.0.0-20220907171357-04be3eba64a2 h1:H2TDz8ibqkAF6YGhCdN3jS9O0/s90v0rJh3X/OLHEUk= golang.org/x/xerrors v0.0.0-20220907171357-04be3eba64a2/go.mod h1:K8+ghG5WaK9qNqU5K3HdILfMLy1f3aNYFI/wnl100a8= @@ -100,7 +83,6 @@ google.golang.org/protobuf v1.26.0/go.mod h1:9q0QmTI4eRPtz6boOQmLYwt+qCgq0jsYwAQ google.golang.org/protobuf v1.27.1 h1:SnqbnDw1V7RiZcXPx5MEeqPv2s79L9i7BJUlG/+RurQ= google.golang.org/protobuf v1.27.1/go.mod h1:9q0QmTI4eRPtz6boOQmLYwt+qCgq0jsYwAQnmE0givc= gopkg.in/check.v1 v0.0.0-20161208181325-20d25e280405/go.mod h1:Co6ibVJAznAaIkqp8huTwlJQCZ016jof/cbN4VW5Yz0= -gopkg.in/check.v1 v1.0.0-20190902080502-41f04d3bba15/go.mod h1:Co6ibVJAznAaIkqp8huTwlJQCZ016jof/cbN4VW5Yz0= gopkg.in/check.v1 v1.0.0-20201130134442-10cb98267c6c h1:Hei/4ADfdWqJk1ZMxUNpqntNwaWcugrBjAiHlqqRiVk= gopkg.in/check.v1 v1.0.0-20201130134442-10cb98267c6c/go.mod h1:JHkPIbrfpd72SG/EVd6muEfDQjcINNoR0C8j2r3qZ4Q= gopkg.in/yaml.v2 v2.2.8/go.mod h1:hI93XBmqTisBFMUTm0b8Fm+jr3Dg1NNxqwp+5A1VGuI= @@ -109,9 +91,8 @@ gopkg.in/yaml.v2 v2.4.0/go.mod h1:RDklbk79AGWmwhnvt/jBztapEOGDOx6ZbXqjP6csGnQ= gopkg.in/yaml.v3 v3.0.0-20200313102051-9f266ea9e77c/go.mod h1:K4uyk7z7BCEPqu6E+C64Yfv1cQ7kz7rIZviUmN+EgEM= gopkg.in/yaml.v3 v3.0.1 h1:fxVm/GzAzEWqLHuvctI91KS9hhNmmWOoWu0XTYJS7CA= gopkg.in/yaml.v3 v3.0.1/go.mod h1:K4uyk7z7BCEPqu6E+C64Yfv1cQ7kz7rIZviUmN+EgEM= -k8s.io/gengo v0.0.0-20230829151522-9cce18d56c01 h1:pWEwq4Asjm4vjW7vcsmijwBhOr1/shsbSYiWXmNGlks= -k8s.io/gengo v0.0.0-20230829151522-9cce18d56c01/go.mod h1:FiNAH4ZV3gBg2Kwh89tzAEV2be7d5xI0vBa/VySYy3E= -k8s.io/klog/v2 v2.2.0/go.mod h1:Od+F08eJP+W3HUb4pSrPpgp9DGU4GzlpG/TmITuYh/Y= +k8s.io/gengo/v2 v2.0.0-20240226174109-00c4be8627da h1:TthpcphFcSU/2R2/j67duxXOSRdUGP2ZOAG4TyGgz5c= +k8s.io/gengo/v2 v2.0.0-20240226174109-00c4be8627da/go.mod h1:5177teI4XU3cmd0ZSDKF3jxMubfjilA5FNOegKTCgK4= k8s.io/klog/v2 v2.80.1 h1:atnLQ121W371wYYFawwYx1aEY2eUfs4l3J72wtgAwV4= k8s.io/klog/v2 v2.80.1/go.mod h1:y1WjHnz7Dj687irZUWR/WLkLc5N1YHtjLdmgWjndZn0= k8s.io/utils v0.0.0-20230726121419-3b25d923346b h1:sgn3ZU783SCgtaSJjpcVVlRqd6GSnlTLKgpAAttJvpI= diff --git a/pkg/generators/api_linter.go b/pkg/generators/api_linter.go index 2763cf884..5deff4d5a 100644 --- a/pkg/generators/api_linter.go +++ b/pkg/generators/api_linter.go @@ -25,8 +25,8 @@ import ( "k8s.io/kube-openapi/pkg/generators/rules" - "k8s.io/gengo/generator" - "k8s.io/gengo/types" + "k8s.io/gengo/v2/generator" + "k8s.io/gengo/v2/types" "k8s.io/klog/v2" ) @@ -94,7 +94,7 @@ func newAPIViolationGen() *apiViolationGen { } type apiViolationGen struct { - generator.DefaultGen + generator.GoGenerator linter *apiLinter } diff --git a/pkg/generators/config.go b/pkg/generators/config.go index d728f2a32..098d400a6 100644 --- a/pkg/generators/config.go +++ b/pkg/generators/config.go @@ -17,16 +17,14 @@ 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/gengo/v2" + "k8s.io/gengo/v2/generator" + "k8s.io/gengo/v2/namer" + "k8s.io/gengo/v2/types" "k8s.io/klog/v2" - - generatorargs "k8s.io/kube-openapi/cmd/openapi-gen/args" + "k8s.io/kube-openapi/cmd/openapi-gen/args" ) type identityNamer struct{} @@ -51,36 +49,31 @@ func DefaultNameSystem() string { return "sorting_namer" } -func Packages(context *generator.Context, arguments *args.GeneratorArgs) generator.Packages { - boilerplate, err := arguments.LoadGoBoilerplate() +func GetTargets(context *generator.Context, args *args.Args) []generator.Target { + boilerplate, err := gengo.GoBoilerplate(args.GoHeaderFile, gengo.StdBuildTag, gengo.StdGeneratedBy) 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 + if args.ReportFilename != "" { + reportPath = args.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.Target{ + &generator.SimpleTarget{ + PkgName: filepath.Base(args.OutputPkg), + PkgPath: args.OutputPkg, + PkgDir: args.OutputDir, + HeaderComment: boilerplate, + GeneratorsFunc: func(c *generator.Context) (generators []generator.Generator) { return []generator.Generator{ newOpenAPIGen( - arguments.OutputFileBaseName, - arguments.OutputPackagePath, + args.OutputFile, + args.OutputPkg, ), newAPIViolationGen(), } diff --git a/pkg/generators/enum.go b/pkg/generators/enum.go index 933d61690..dfc30ebc8 100644 --- a/pkg/generators/enum.go +++ b/pkg/generators/enum.go @@ -22,8 +22,9 @@ import ( "sort" "strings" - "k8s.io/gengo/generator" - "k8s.io/gengo/types" + "k8s.io/gengo/v2" + "k8s.io/gengo/v2/generator" + "k8s.io/gengo/v2/types" ) const tagEnumType = "enum" @@ -163,7 +164,7 @@ func isEnumType(stringType *types.Type, t *types.Type) bool { } func hasEnumTag(t *types.Type) bool { - return types.ExtractCommentTags("+", t.CommentLines)[tagEnumType] != nil + return gengo.ExtractCommentTags("+", t.CommentLines)[tagEnumType] != nil } // whitespaceRegex is the regex for consecutive whitespaces. diff --git a/pkg/generators/enum_test.go b/pkg/generators/enum_test.go index 93c51a59e..b96ff4eb5 100644 --- a/pkg/generators/enum_test.go +++ b/pkg/generators/enum_test.go @@ -21,8 +21,8 @@ import ( "sort" "testing" - "k8s.io/gengo/generator" - "k8s.io/gengo/types" + "k8s.io/gengo/v2/generator" + "k8s.io/gengo/v2/types" ) func TestParseEnums(t *testing.T) { diff --git a/pkg/generators/extension.go b/pkg/generators/extension.go index e37d93ef7..42d385416 100644 --- a/pkg/generators/extension.go +++ b/pkg/generators/extension.go @@ -21,8 +21,9 @@ import ( "sort" "strings" - "k8s.io/gengo/examples/set-gen/sets" - "k8s.io/gengo/types" + "k8s.io/gengo/v2" + "k8s.io/gengo/v2/types" + "k8s.io/kube-openapi/pkg/util/sets" ) const extensionPrefix = "x-kubernetes-" @@ -171,7 +172,7 @@ func parseExtensions(comments []string) ([]extension, []error) { } } // Next, generate extensions from "idlTags" (e.g. +listType) - tagValues := types.ExtractCommentTags("+", comments) + tagValues := gengo.ExtractCommentTags("+", comments) for _, idlTag := range sortedMapKeys(tagValues) { xAttrs, exists := tagToExtension[idlTag] if !exists { diff --git a/pkg/generators/extension_test.go b/pkg/generators/extension_test.go index 7c2c0dff1..7964bdbc4 100644 --- a/pkg/generators/extension_test.go +++ b/pkg/generators/extension_test.go @@ -21,8 +21,8 @@ import ( "strings" "testing" - "k8s.io/gengo/examples/set-gen/sets" - "k8s.io/gengo/types" + "k8s.io/gengo/v2/types" + "k8s.io/kube-openapi/pkg/util/sets" ) func TestSingleTagExtension(t *testing.T) { diff --git a/pkg/generators/markers.go b/pkg/generators/markers.go index 7421f6c41..7f0fe985a 100644 --- a/pkg/generators/markers.go +++ b/pkg/generators/markers.go @@ -24,8 +24,7 @@ import ( "strconv" "strings" - defaultergen "k8s.io/gengo/examples/defaulter-gen/generators" - "k8s.io/gengo/types" + "k8s.io/gengo/v2/types" openapi "k8s.io/kube-openapi/pkg/common" "k8s.io/kube-openapi/pkg/validation/spec" ) @@ -456,7 +455,7 @@ func parseMarkers(markerComments []string, prefix string) (map[string]any, error if len(key) == 0 { return nil, fmt.Errorf("cannot have empty key for marker comment") - } else if _, ok := defaultergen.ParseSymbolReference(value, ""); ok { + } else if _, ok := parseSymbolReference(value, ""); ok { // Skip ref markers continue } else if len(value) == 0 { diff --git a/pkg/generators/markers_test.go b/pkg/generators/markers_test.go index 284996ca2..36f0ff10d 100644 --- a/pkg/generators/markers_test.go +++ b/pkg/generators/markers_test.go @@ -19,7 +19,7 @@ import ( "testing" "github.com/stretchr/testify/require" - "k8s.io/gengo/types" + "k8s.io/gengo/v2/types" "k8s.io/kube-openapi/pkg/generators" "k8s.io/kube-openapi/pkg/validation/spec" "k8s.io/utils/ptr" diff --git a/pkg/generators/openapi.go b/pkg/generators/openapi.go index b6fd741b5..f70b6748d 100644 --- a/pkg/generators/openapi.go +++ b/pkg/generators/openapi.go @@ -23,13 +23,14 @@ import ( "io" "path/filepath" "reflect" + "regexp" "sort" "strings" - defaultergen "k8s.io/gengo/examples/defaulter-gen/generators" - "k8s.io/gengo/generator" - "k8s.io/gengo/namer" - "k8s.io/gengo/types" + "k8s.io/gengo/v2" + "k8s.io/gengo/v2/generator" + "k8s.io/gengo/v2/namer" + "k8s.io/gengo/v2/types" openapi "k8s.io/kube-openapi/pkg/common" "k8s.io/kube-openapi/pkg/validation/spec" @@ -57,11 +58,11 @@ var tempPatchTags = [...]string{ } func getOpenAPITagValue(comments []string) []string { - return types.ExtractCommentTags("+", comments)[tagName] + return gengo.ExtractCommentTags("+", comments)[tagName] } func getSingleTagsValue(comments []string, tag string) (string, error) { - tags, ok := types.ExtractCommentTags("+", comments)[tag] + tags, ok := gengo.ExtractCommentTags("+", comments)[tag] if !ok || len(tags) == 0 { return "", nil } @@ -85,9 +86,9 @@ func hasOpenAPITagValue(comments []string, value string) bool { // its comments. If +optional is present it returns true. If +required is present // it returns false. Otherwise, it returns true if `omitempty` JSON tag is present func isOptional(m *types.Member) (bool, error) { - hasOptionalCommentTag := types.ExtractCommentTags( + hasOptionalCommentTag := gengo.ExtractCommentTags( "+", m.CommentLines)[tagOptional] != nil - hasRequiredCommentTag := types.ExtractCommentTags( + hasRequiredCommentTag := gengo.ExtractCommentTags( "+", m.CommentLines)[tagRequired] != nil if hasOptionalCommentTag && hasRequiredCommentTag { return false, fmt.Errorf("member %s cannot be both optional and required", m.Name) @@ -124,16 +125,16 @@ const ( // openApiGen produces a file with auto-generated OpenAPI functions. type openAPIGen struct { - generator.DefaultGen + generator.GoGenerator // 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 { +func newOpenAPIGen(outputFilename string, targetPackage string) generator.Generator { return &openAPIGen{ - DefaultGen: generator.DefaultGen{ - OptionalName: sanitizedName, + GoGenerator: generator.GoGenerator{ + OutputFilename: outputFilename, }, imports: generator.NewImportTrackerForPackage(targetPackage), targetPackage: targetPackage, @@ -155,16 +156,6 @@ func (g *openAPIGen) Namers(c *generator.Context) namer.NameSystems { } } -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() { @@ -711,7 +702,7 @@ func defaultFromComments(comments []string, commentPath string, t *types.Type) ( } var i interface{} - if id, ok := defaultergen.ParseSymbolReference(tag, commentPath); ok { + if id, ok := parseSymbolReference(tag, commentPath); ok { klog.Errorf("%v, %v", id, commentPath) return nil, &id, nil } else if err := json.Unmarshal([]byte(tag), &i); err != nil { @@ -720,6 +711,31 @@ func defaultFromComments(comments []string, commentPath string, t *types.Type) ( return i, nil, nil } +var refRE = regexp.MustCompile(`^ref\((?P[^"]+)\)$`) +var refREIdentIndex = refRE.SubexpIndex("reference") + +// parseSymbolReference looks for strings that match one of the following: +// - ref(Ident) +// - ref(pkgpath.Ident) +// If the input string matches either of these, it will return the (optional) +// pkgpath, the Ident, and true. Otherwise it will return empty strings and +// false. +// +// This is borrowed from k8s.io/code-generator. +func parseSymbolReference(s, sourcePackage string) (types.Name, bool) { + matches := refRE.FindStringSubmatch(s) + if len(matches) < refREIdentIndex || matches[refREIdentIndex] == "" { + return types.Name{}, false + } + + contents := matches[refREIdentIndex] + name := types.ParseFullyQualifiedName(contents) + if len(name.Package) == 0 { + name.Package = sourcePackage + } + return name, true +} + func implementsCustomUnmarshalling(t *types.Type) bool { switch t.Kind { case types.Pointer: diff --git a/pkg/generators/openapi_test.go b/pkg/generators/openapi_test.go index d29ba0830..82b321969 100644 --- a/pkg/generators/openapi_test.go +++ b/pkg/generators/openapi_test.go @@ -19,48 +19,38 @@ package generators import ( "bytes" "fmt" + "go/format" "path/filepath" "strings" "testing" - "go/format" - - "github.com/stretchr/testify/assert" - "k8s.io/gengo/generator" - "k8s.io/gengo/namer" - "k8s.io/gengo/parser" - "k8s.io/gengo/types" + "github.com/google/go-cmp/cmp" + "golang.org/x/tools/go/packages" + "golang.org/x/tools/go/packages/packagestest" + "k8s.io/gengo/v2/generator" + "k8s.io/gengo/v2/namer" + "k8s.io/gengo/v2/parser" + "k8s.io/gengo/v2/types" ) -func construct(t *testing.T, files map[string]string, testNamer namer.Namer) (*parser.Builder, types.Universe, []*types.Type) { - b := parser.New() - for name, src := range files { - if err := b.AddFileForTest(filepath.Dir(name), name, []byte(src)); err != nil { - t.Fatal(err) - } +func construct(t *testing.T, cfg *packages.Config, nameSystems namer.NameSystems, defaultSystem string, pkg string) *generator.Context { + p := parser.New() + if err := p.LoadPackagesWithConfigForTesting(cfg, pkg); err != nil { + t.Fatalf("failed to load package: %v", err) } - u, err := b.FindTypes() + c, err := generator.NewContext(p, nameSystems, defaultSystem) if err != nil { - t.Fatal(err) + t.Fatalf("failed to make a context: %v", err) } - orderer := namer.Orderer{Namer: testNamer} - o := orderer.OrderUniverse(u) - return b, u, o + return c } -func testOpenAPITypeWriter(t *testing.T, code string) (error, error, *assert.Assertions, *bytes.Buffer, *bytes.Buffer, []string) { - return testOpenAPITypeWriterWithFiles(t, code, nil) -} - -func testOpenAPITypeWriterWithFiles(t *testing.T, code string, testFiles map[string]string) (error, error, *assert.Assertions, *bytes.Buffer, *bytes.Buffer, []string) { - assert := assert.New(t) - if testFiles == nil { - testFiles = map[string]string{} - } - testFiles["base/foo/bar.go"] = code - outputPackage := "base/output" - imports := generator.NewImportTrackerForPackage(outputPackage) - rawNamer := namer.NewRawNamer(outputPackage, imports) +func testOpenAPITypeWriter(t *testing.T, cfg *packages.Config) (error, error, *bytes.Buffer, *bytes.Buffer, []string) { + pkgBase := "example.com/base" + inputPkg := filepath.Join(pkgBase, "foo") + outputPkg := filepath.Join(pkgBase, "output") + imports := generator.NewImportTrackerForPackage(outputPkg) + rawNamer := namer.NewRawNamer(outputPkg, imports) namers := namer.NameSystems{ "raw": rawNamer, "private": &namer.NameStrategy{ @@ -70,12 +60,9 @@ func testOpenAPITypeWriterWithFiles(t *testing.T, code string, testFiles map[str PrependPackageNames: 4, // enough to fully qualify from k8s.io/api/... }, } - builder, universe, _ := construct(t, testFiles, rawNamer) - context, err := generator.NewContext(builder, namers, "raw") - if err != nil { - t.Fatal(err) - } - blahT := universe.Type(types.Name{Package: "base/foo", Name: "Blah"}) + context := construct(t, cfg, namers, "raw", inputPkg) + universe := context.Universe + blahT := universe.Type(types.Name{Package: inputPkg, Name: "Blah"}) callBuffer := &bytes.Buffer{} callSW := generator.NewSnippetWriter(callBuffer, context, "$", "$") @@ -85,83 +72,106 @@ func testOpenAPITypeWriterWithFiles(t *testing.T, code string, testFiles map[str funcSW := generator.NewSnippetWriter(funcBuffer, context, "$", "$") funcError := newOpenAPITypeWriter(funcSW, context).generate(blahT) - return callError, funcError, assert, callBuffer, funcBuffer, imports.ImportLines() + return callError, funcError, callBuffer, funcBuffer, imports.ImportLines() } -func TestSimple(t *testing.T) { - callErr, funcErr, assert, callBuffer, funcBuffer, _ := testOpenAPITypeWriter(t, ` -package foo - -// Blah is a test. -// +k8s:openapi-gen=true -// +k8s:openapi-gen=x-kubernetes-type-tag:type_test -type Blah struct { - // A simple string - String string - // A simple int - Int int `+"`"+`json:",omitempty"`+"`"+` - // An int considered string simple int - IntString int `+"`"+`json:",string"`+"`"+` - // A simple int64 - Int64 int64 - // A simple int32 - Int32 int32 - // A simple int16 - Int16 int16 - // A simple int8 - Int8 int8 - // A simple int - Uint uint - // A simple int64 - Uint64 uint64 - // A simple int32 - Uint32 uint32 - // A simple int16 - Uint16 uint16 - // A simple int8 - Uint8 uint8 - // A simple byte - Byte byte - // A simple boolean - Bool bool - // A simple float64 - Float64 float64 - // A simple float32 - Float32 float32 - // a base64 encoded characters - ByteArray []byte - // a member with an extension - // +k8s:openapi-gen=x-kubernetes-member-tag:member_test - WithExtension string - // a member with struct tag as extension - // +patchStrategy=merge - // +patchMergeKey=pmk - WithStructTagExtension string `+"`"+`patchStrategy:"merge" patchMergeKey:"pmk"`+"`"+` - // a member with a list type - // +listType=atomic - // +default=["foo", "bar"] - WithListType []string - // a member with a map type - // +listType=atomic - // +default={"foo": "bar", "fizz": "buzz"} - Map map[string]string - // a member with a string pointer - // +default="foo" - StringPointer *string - // an int member with a default - // +default=1 - OmittedInt int `+"`"+`json:"omitted,omitempty"`+"`"+` -} - `) - if callErr != nil { - t.Fatal(callErr) - } - if funcErr != nil { - t.Fatal(funcErr) +// NOTE: the usual order of arguments for an assertion would be want, got, but +// this helper function flips that in favor of callsite readability. +func assertEqual(t *testing.T, got, want string) { + t.Helper() + want = strings.TrimSpace(want) + got = strings.TrimSpace(got) + if !cmp.Equal(want, got) { + t.Errorf("Wrong result:\n%s", cmp.Diff(want, got)) } - assert.Equal(`"base/foo.Blah": schema_base_foo_Blah(ref), -`, callBuffer.String()) - assert.Equal(`func schema_base_foo_Blah(ref common.ReferenceCallback) common.OpenAPIDefinition { +} + +func TestSimple(t *testing.T) { + inputFile := ` + package foo + + // Blah is a test. + // +k8s:openapi-gen=true + // +k8s:openapi-gen=x-kubernetes-type-tag:type_test + type Blah struct { + // A simple string + String string + // A simple int + Int int ` + "`" + `json:",omitempty"` + "`" + ` + // An int considered string simple int + IntString int ` + "`" + `json:",string"` + "`" + ` + // A simple int64 + Int64 int64 + // A simple int32 + Int32 int32 + // A simple int16 + Int16 int16 + // A simple int8 + Int8 int8 + // A simple int + Uint uint + // A simple int64 + Uint64 uint64 + // A simple int32 + Uint32 uint32 + // A simple int16 + Uint16 uint16 + // A simple int8 + Uint8 uint8 + // A simple byte + Byte byte + // A simple boolean + Bool bool + // A simple float64 + Float64 float64 + // A simple float32 + Float32 float32 + // a base64 encoded characters + ByteArray []byte + // a member with an extension + // +k8s:openapi-gen=x-kubernetes-member-tag:member_test + WithExtension string + // a member with struct tag as extension + // +patchStrategy=merge + // +patchMergeKey=pmk + WithStructTagExtension string ` + "`" + `patchStrategy:"merge" patchMergeKey:"pmk"` + "`" + ` + // a member with a list type + // +listType=atomic + // +default=["foo", "bar"] + WithListType []string + // a member with a map type + // +listType=atomic + // +default={"foo": "bar", "fizz": "buzz"} + Map map[string]string + // a member with a string pointer + // +default="foo" + StringPointer *string + // an int member with a default + // +default=1 + OmittedInt int ` + "`" + `json:"omitted,omitempty"` + "`" + ` + }` + + packagestest.TestAll(t, func(t *testing.T, x packagestest.Exporter) { + e := packagestest.Export(t, x, []packagestest.Module{{ + Name: "example.com/base/foo", + Files: map[string]interface{}{ + "foo.go": inputFile, + }, + }}) + defer e.Cleanup() + + callErr, funcErr, callBuffer, funcBuffer, _ := testOpenAPITypeWriter(t, e.Config) + if callErr != nil { + t.Fatal(callErr) + } + if funcErr != nil { + t.Fatal(funcErr) + } + assertEqual(t, callBuffer.String(), + `"example.com/base/foo.Blah": schema_examplecom_base_foo_Blah(ref),`) + + assertEqual(t, funcBuffer.String(), + `func schema_examplecom_base_foo_Blah(ref common.ReferenceCallback) common.OpenAPIDefinition { return common.OpenAPIDefinition{ Schema: spec.Schema{ SchemaProps: spec.SchemaProps{ @@ -383,28 +393,38 @@ Extensions: spec.Extensions{ }, }, } -} - -`, funcBuffer.String()) +}`) + }) } func TestEmptyProperties(t *testing.T) { - callErr, funcErr, assert, callBuffer, funcBuffer, _ := testOpenAPITypeWriter(t, ` -package foo + inputFile := ` + package foo -// Blah demonstrate a struct without fields. -type Blah struct { -} - `) - if callErr != nil { - t.Fatal(callErr) - } - if funcErr != nil { - t.Fatal(funcErr) - } - assert.Equal(`"base/foo.Blah": schema_base_foo_Blah(ref), -`, callBuffer.String()) - assert.Equal(`func schema_base_foo_Blah(ref common.ReferenceCallback) common.OpenAPIDefinition { + // Blah demonstrate a struct without fields. + type Blah struct { + }` + + packagestest.TestAll(t, func(t *testing.T, x packagestest.Exporter) { + e := packagestest.Export(t, x, []packagestest.Module{{ + Name: "example.com/base/foo", + Files: map[string]interface{}{ + "foo.go": inputFile, + }, + }}) + defer e.Cleanup() + + callErr, funcErr, callBuffer, funcBuffer, _ := testOpenAPITypeWriter(t, e.Config) + if callErr != nil { + t.Fatal(callErr) + } + if funcErr != nil { + t.Fatal(funcErr) + } + assertEqual(t, callBuffer.String(), + `"example.com/base/foo.Blah": schema_examplecom_base_foo_Blah(ref),`) + assertEqual(t, funcBuffer.String(), + `func schema_examplecom_base_foo_Blah(ref common.ReferenceCallback) common.OpenAPIDefinition { return common.OpenAPIDefinition{ Schema: spec.Schema{ SchemaProps: spec.SchemaProps{ @@ -413,36 +433,46 @@ Type: []string{"object"}, }, }, } -} - -`, funcBuffer.String()) +}`) + }) } func TestNestedStruct(t *testing.T) { - callErr, funcErr, assert, callBuffer, funcBuffer, _ := testOpenAPITypeWriter(t, ` -package foo + inputFile := ` + package foo -// Nested is used as struct field -type Nested struct { - // A simple string - String string -} + // Nested is used as struct field + type Nested struct { + // A simple string + String string + } -// Blah demonstrate a struct with struct field. -type Blah struct { - // A struct field - Field Nested -} - `) - if callErr != nil { - t.Fatal(callErr) - } - if funcErr != nil { - t.Fatal(funcErr) - } - assert.Equal(`"base/foo.Blah": schema_base_foo_Blah(ref), -`, callBuffer.String()) - assert.Equal(`func schema_base_foo_Blah(ref common.ReferenceCallback) common.OpenAPIDefinition { + // Blah demonstrate a struct with struct field. + type Blah struct { + // A struct field + Field Nested + }` + + packagestest.TestAll(t, func(t *testing.T, x packagestest.Exporter) { + e := packagestest.Export(t, x, []packagestest.Module{{ + Name: "example.com/base/foo", + Files: map[string]interface{}{ + "foo.go": inputFile, + }, + }}) + defer e.Cleanup() + + callErr, funcErr, callBuffer, funcBuffer, _ := testOpenAPITypeWriter(t, e.Config) + if callErr != nil { + t.Fatal(callErr) + } + if funcErr != nil { + t.Fatal(funcErr) + } + assertEqual(t, callBuffer.String(), + `"example.com/base/foo.Blah": schema_examplecom_base_foo_Blah(ref),`) + assertEqual(t, funcBuffer.String(), + `func schema_examplecom_base_foo_Blah(ref common.ReferenceCallback) common.OpenAPIDefinition { return common.OpenAPIDefinition{ Schema: spec.Schema{ SchemaProps: spec.SchemaProps{ @@ -453,7 +483,7 @@ Properties: map[string]spec.Schema{ SchemaProps: spec.SchemaProps{ Description: "A struct field", Default: map[string]interface {}{}, -Ref: ref("base/foo.Nested"), +Ref: ref("example.com/base/foo.Nested"), }, }, }, @@ -461,38 +491,49 @@ Required: []string{"Field"}, }, }, Dependencies: []string{ -"base/foo.Nested",}, +"example.com/base/foo.Nested",}, } -} - -`, funcBuffer.String()) +}`) + }) } func TestNestedStructPointer(t *testing.T) { - callErr, funcErr, assert, callBuffer, funcBuffer, _ := testOpenAPITypeWriter(t, ` -package foo + inputFile := ` + package foo -// Nested is used as struct pointer field -type Nested struct { - // A simple string - String string -} + // Nested is used as struct pointer field + type Nested struct { + // A simple string + String string + } -// Blah demonstrate a struct with struct pointer field. -type Blah struct { - // A struct pointer field - Field *Nested -} - `) - if callErr != nil { - t.Fatal(callErr) - } - if funcErr != nil { - t.Fatal(funcErr) - } - assert.Equal(`"base/foo.Blah": schema_base_foo_Blah(ref), -`, callBuffer.String()) - assert.Equal(`func schema_base_foo_Blah(ref common.ReferenceCallback) common.OpenAPIDefinition { + // Blah demonstrate a struct with struct pointer field. + type Blah struct { + // A struct pointer field + Field *Nested + }` + + packagestest.TestAll(t, func(t *testing.T, x packagestest.Exporter) { + e := packagestest.Export(t, x, []packagestest.Module{{ + Name: "example.com/base/foo", + Files: map[string]interface{}{ + "foo.go": inputFile, + }, + }}) + defer e.Cleanup() + + callErr, funcErr, callBuffer, funcBuffer, _ := testOpenAPITypeWriter(t, e.Config) + + if callErr != nil { + t.Fatal(callErr) + } + if funcErr != nil { + t.Fatal(funcErr) + } + assertEqual(t, callBuffer.String(), + `"example.com/base/foo.Blah": schema_examplecom_base_foo_Blah(ref),`) + assertEqual(t, funcBuffer.String(), + `func schema_examplecom_base_foo_Blah(ref common.ReferenceCallback) common.OpenAPIDefinition { return common.OpenAPIDefinition{ Schema: spec.Schema{ SchemaProps: spec.SchemaProps{ @@ -502,7 +543,7 @@ Properties: map[string]spec.Schema{ "Field": { SchemaProps: spec.SchemaProps{ Description: "A struct pointer field", -Ref: ref("base/foo.Nested"), +Ref: ref("example.com/base/foo.Nested"), }, }, }, @@ -510,38 +551,48 @@ Required: []string{"Field"}, }, }, Dependencies: []string{ -"base/foo.Nested",}, +"example.com/base/foo.Nested",}, } -} - -`, funcBuffer.String()) +}`) + }) } func TestEmbeddedStruct(t *testing.T) { - callErr, funcErr, assert, callBuffer, funcBuffer, _ := testOpenAPITypeWriter(t, ` -package foo + inputFile := ` + package foo -// Nested is used as embedded struct field -type Nested struct { - // A simple string - String string -} + // Nested is used as embedded struct field + type Nested struct { + // A simple string + String string + } -// Blah demonstrate a struct with embedded struct field. -type Blah struct { - // An embedded struct field - Nested -} - `) - if callErr != nil { - t.Fatal(callErr) - } - if funcErr != nil { - t.Fatal(funcErr) - } - assert.Equal(`"base/foo.Blah": schema_base_foo_Blah(ref), -`, callBuffer.String()) - assert.Equal(`func schema_base_foo_Blah(ref common.ReferenceCallback) common.OpenAPIDefinition { + // Blah demonstrate a struct with embedded struct field. + type Blah struct { + // An embedded struct field + Nested + }` + + packagestest.TestAll(t, func(t *testing.T, x packagestest.Exporter) { + e := packagestest.Export(t, x, []packagestest.Module{{ + Name: "example.com/base/foo", + Files: map[string]interface{}{ + "foo.go": inputFile, + }, + }}) + defer e.Cleanup() + + callErr, funcErr, callBuffer, funcBuffer, _ := testOpenAPITypeWriter(t, e.Config) + if callErr != nil { + t.Fatal(callErr) + } + if funcErr != nil { + t.Fatal(funcErr) + } + assertEqual(t, callBuffer.String(), + `"example.com/base/foo.Blah": schema_examplecom_base_foo_Blah(ref),`) + assertEqual(t, funcBuffer.String(), + `func schema_examplecom_base_foo_Blah(ref common.ReferenceCallback) common.OpenAPIDefinition { return common.OpenAPIDefinition{ Schema: spec.Schema{ SchemaProps: spec.SchemaProps{ @@ -552,7 +603,7 @@ Properties: map[string]spec.Schema{ SchemaProps: spec.SchemaProps{ Description: "An embedded struct field", Default: map[string]interface {}{}, -Ref: ref("base/foo.Nested"), +Ref: ref("example.com/base/foo.Nested"), }, }, }, @@ -560,41 +611,51 @@ Required: []string{"Nested"}, }, }, Dependencies: []string{ -"base/foo.Nested",}, +"example.com/base/foo.Nested",}, } -} - -`, funcBuffer.String()) +}`) + }) } func TestSingleEmbeddedStruct(t *testing.T) { - callErr, funcErr, assert, callBuffer, funcBuffer, _ := testOpenAPITypeWriter(t, ` -package foo + inputFile := ` + package foo -import "time" + import "time" -// Nested is used as embedded struct field -type Nested struct { - // A simple string - time.Duration -} + // Nested is used as embedded struct field + type Nested struct { + // A simple string + time.Duration + } -// Blah demonstrate a struct with embedded struct field. -type Blah struct { - // An embedded struct field - // +default="10ms" - Nested `+"`"+`json:"nested,omitempty" protobuf:"bytes,5,opt,name=nested"`+"`"+` -} - `) - if callErr != nil { - t.Fatal(callErr) - } - if funcErr != nil { - t.Fatal(funcErr) - } - assert.Equal(`"base/foo.Blah": schema_base_foo_Blah(ref), -`, callBuffer.String()) - assert.Equal(`func schema_base_foo_Blah(ref common.ReferenceCallback) common.OpenAPIDefinition { + // Blah demonstrate a struct with embedded struct field. + type Blah struct { + // An embedded struct field + // +default="10ms" + Nested ` + "`" + `json:"nested,omitempty" protobuf:"bytes,5,opt,name=nested"` + "`" + ` + }` + + packagestest.TestAll(t, func(t *testing.T, x packagestest.Exporter) { + e := packagestest.Export(t, x, []packagestest.Module{{ + Name: "example.com/base/foo", + Files: map[string]interface{}{ + "foo.go": inputFile, + }, + }}) + defer e.Cleanup() + + callErr, funcErr, callBuffer, funcBuffer, _ := testOpenAPITypeWriter(t, e.Config) + if callErr != nil { + t.Fatal(callErr) + } + if funcErr != nil { + t.Fatal(funcErr) + } + assertEqual(t, callBuffer.String(), + `"example.com/base/foo.Blah": schema_examplecom_base_foo_Blah(ref),`) + assertEqual(t, funcBuffer.String(), + `func schema_examplecom_base_foo_Blah(ref common.ReferenceCallback) common.OpenAPIDefinition { return common.OpenAPIDefinition{ Schema: spec.Schema{ SchemaProps: spec.SchemaProps{ @@ -605,45 +666,55 @@ Properties: map[string]spec.Schema{ SchemaProps: spec.SchemaProps{ Description: "An embedded struct field", Default: "10ms", -Ref: ref("base/foo.Nested"), +Ref: ref("example.com/base/foo.Nested"), }, }, }, }, }, Dependencies: []string{ -"base/foo.Nested",}, -} +"example.com/base/foo.Nested",}, } - -`, funcBuffer.String()) +}`) + }) } func TestEmbeddedInlineStruct(t *testing.T) { - callErr, funcErr, assert, callBuffer, funcBuffer, _ := testOpenAPITypeWriter(t, ` -package foo + inputFile := ` + package foo -// Nested is used as embedded inline struct field -type Nested struct { - // A simple string - String string -} + // Nested is used as embedded inline struct field + type Nested struct { + // A simple string + String string + } -// Blah demonstrate a struct with embedded inline struct field. -type Blah struct { - // An embedded inline struct field - Nested `+"`"+`json:",inline,omitempty"`+"`"+` -} - `) - if callErr != nil { - t.Fatal(callErr) - } - if funcErr != nil { - t.Fatal(funcErr) - } - assert.Equal(`"base/foo.Blah": schema_base_foo_Blah(ref), -`, callBuffer.String()) - assert.Equal(`func schema_base_foo_Blah(ref common.ReferenceCallback) common.OpenAPIDefinition { + // Blah demonstrate a struct with embedded inline struct field. + type Blah struct { + // An embedded inline struct field + Nested ` + "`" + `json:",inline,omitempty"` + "`" + ` + }` + + packagestest.TestAll(t, func(t *testing.T, x packagestest.Exporter) { + e := packagestest.Export(t, x, []packagestest.Module{{ + Name: "example.com/base/foo", + Files: map[string]interface{}{ + "foo.go": inputFile, + }, + }}) + defer e.Cleanup() + + callErr, funcErr, callBuffer, funcBuffer, _ := testOpenAPITypeWriter(t, e.Config) + if callErr != nil { + t.Fatal(callErr) + } + if funcErr != nil { + t.Fatal(funcErr) + } + assertEqual(t, callBuffer.String(), + `"example.com/base/foo.Blah": schema_examplecom_base_foo_Blah(ref),`) + assertEqual(t, funcBuffer.String(), + `func schema_examplecom_base_foo_Blah(ref common.ReferenceCallback) common.OpenAPIDefinition { return common.OpenAPIDefinition{ Schema: spec.Schema{ SchemaProps: spec.SchemaProps{ @@ -663,36 +734,46 @@ Required: []string{"String"}, }, }, } -} - -`, funcBuffer.String()) +}`) + }) } func TestEmbeddedInlineStructPointer(t *testing.T) { - callErr, funcErr, assert, callBuffer, funcBuffer, _ := testOpenAPITypeWriter(t, ` -package foo + inputFile := ` + package foo -// Nested is used as embedded inline struct pointer field. -type Nested struct { - // A simple string - String string -} + // Nested is used as embedded inline struct pointer field. + type Nested struct { + // A simple string + String string + } -// Blah demonstrate a struct with embedded inline struct pointer field. -type Blah struct { - // An embedded inline struct pointer field - *Nested `+"`"+`json:",inline,omitempty"`+"`"+` -} - `) - if callErr != nil { - t.Fatal(callErr) - } - if funcErr != nil { - t.Fatal(funcErr) - } - assert.Equal(`"base/foo.Blah": schema_base_foo_Blah(ref), -`, callBuffer.String()) - assert.Equal(`func schema_base_foo_Blah(ref common.ReferenceCallback) common.OpenAPIDefinition { + // Blah demonstrate a struct with embedded inline struct pointer field. + type Blah struct { + // An embedded inline struct pointer field + *Nested ` + "`" + `json:",inline,omitempty"` + "`" + ` + }` + + packagestest.TestAll(t, func(t *testing.T, x packagestest.Exporter) { + e := packagestest.Export(t, x, []packagestest.Module{{ + Name: "example.com/base/foo", + Files: map[string]interface{}{ + "foo.go": inputFile, + }, + }}) + defer e.Cleanup() + + callErr, funcErr, callBuffer, funcBuffer, _ := testOpenAPITypeWriter(t, e.Config) + if callErr != nil { + t.Fatal(callErr) + } + if funcErr != nil { + t.Fatal(funcErr) + } + assertEqual(t, callBuffer.String(), + `"example.com/base/foo.Blah": schema_examplecom_base_foo_Blah(ref),`) + assertEqual(t, funcBuffer.String(), + `func schema_examplecom_base_foo_Blah(ref common.ReferenceCallback) common.OpenAPIDefinition { return common.OpenAPIDefinition{ Schema: spec.Schema{ SchemaProps: spec.SchemaProps{ @@ -712,30 +793,40 @@ Required: []string{"String"}, }, }, } -} - -`, funcBuffer.String()) +}`) + }) } func TestNestedMapString(t *testing.T) { - callErr, funcErr, assert, callBuffer, funcBuffer, _ := testOpenAPITypeWriter(t, ` -package foo + inputFile := ` + package foo -// Map sample tests openAPIGen.generateMapProperty method. -type Blah struct { - // A sample String to String map - StringToArray map[string]map[string]string -} - `) - if callErr != nil { - t.Fatal(callErr) - } - if funcErr != nil { - t.Fatal(funcErr) - } - assert.Equal(`"base/foo.Blah": schema_base_foo_Blah(ref), -`, callBuffer.String()) - assert.Equal(`func schema_base_foo_Blah(ref common.ReferenceCallback) common.OpenAPIDefinition { + // Map sample tests openAPIGen.generateMapProperty method. + type Blah struct { + // A sample String to String map + StringToArray map[string]map[string]string + }` + + packagestest.TestAll(t, func(t *testing.T, x packagestest.Exporter) { + e := packagestest.Export(t, x, []packagestest.Module{{ + Name: "example.com/base/foo", + Files: map[string]interface{}{ + "foo.go": inputFile, + }, + }}) + defer e.Cleanup() + + callErr, funcErr, callBuffer, funcBuffer, _ := testOpenAPITypeWriter(t, e.Config) + if callErr != nil { + t.Fatal(callErr) + } + if funcErr != nil { + t.Fatal(funcErr) + } + assertEqual(t, callBuffer.String(), + `"example.com/base/foo.Blah": schema_examplecom_base_foo_Blah(ref),`) + assertEqual(t, funcBuffer.String(), + `func schema_examplecom_base_foo_Blah(ref common.ReferenceCallback) common.OpenAPIDefinition { return common.OpenAPIDefinition{ Schema: spec.Schema{ SchemaProps: spec.SchemaProps{ @@ -771,30 +862,40 @@ Required: []string{"StringToArray"}, }, }, } -} - -`, funcBuffer.String()) +}`) + }) } func TestNestedMapInt(t *testing.T) { - callErr, funcErr, assert, callBuffer, funcBuffer, _ := testOpenAPITypeWriter(t, ` -package foo + inputFile := ` + package foo -// Map sample tests openAPIGen.generateMapProperty method. -type Blah struct { - // A sample String to String map - StringToArray map[string]map[string]int -} - `) - if callErr != nil { - t.Fatal(callErr) - } - if funcErr != nil { - t.Fatal(funcErr) - } - assert.Equal(`"base/foo.Blah": schema_base_foo_Blah(ref), -`, callBuffer.String()) - assert.Equal(`func schema_base_foo_Blah(ref common.ReferenceCallback) common.OpenAPIDefinition { + // Map sample tests openAPIGen.generateMapProperty method. + type Blah struct { + // A sample String to String map + StringToArray map[string]map[string]int + }` + + packagestest.TestAll(t, func(t *testing.T, x packagestest.Exporter) { + e := packagestest.Export(t, x, []packagestest.Module{{ + Name: "example.com/base/foo", + Files: map[string]interface{}{ + "foo.go": inputFile, + }, + }}) + defer e.Cleanup() + + callErr, funcErr, callBuffer, funcBuffer, _ := testOpenAPITypeWriter(t, e.Config) + if callErr != nil { + t.Fatal(callErr) + } + if funcErr != nil { + t.Fatal(funcErr) + } + assertEqual(t, callBuffer.String(), + `"example.com/base/foo.Blah": schema_examplecom_base_foo_Blah(ref),`) + assertEqual(t, funcBuffer.String(), + `func schema_examplecom_base_foo_Blah(ref common.ReferenceCallback) common.OpenAPIDefinition { return common.OpenAPIDefinition{ Schema: spec.Schema{ SchemaProps: spec.SchemaProps{ @@ -830,30 +931,40 @@ Required: []string{"StringToArray"}, }, }, } -} - -`, funcBuffer.String()) +}`) + }) } func TestNestedMapBoolean(t *testing.T) { - callErr, funcErr, assert, callBuffer, funcBuffer, _ := testOpenAPITypeWriter(t, ` -package foo + inputFile := ` + package foo -// Map sample tests openAPIGen.generateMapProperty method. -type Blah struct { - // A sample String to String map - StringToArray map[string]map[string]bool -} - `) - if callErr != nil { - t.Fatal(callErr) - } - if funcErr != nil { - t.Fatal(funcErr) - } - assert.Equal(`"base/foo.Blah": schema_base_foo_Blah(ref), -`, callBuffer.String()) - assert.Equal(`func schema_base_foo_Blah(ref common.ReferenceCallback) common.OpenAPIDefinition { + // Map sample tests openAPIGen.generateMapProperty method. + type Blah struct { + // A sample String to String map + StringToArray map[string]map[string]bool + }` + + packagestest.TestAll(t, func(t *testing.T, x packagestest.Exporter) { + e := packagestest.Export(t, x, []packagestest.Module{{ + Name: "example.com/base/foo", + Files: map[string]interface{}{ + "foo.go": inputFile, + }, + }}) + defer e.Cleanup() + + callErr, funcErr, callBuffer, funcBuffer, _ := testOpenAPITypeWriter(t, e.Config) + if callErr != nil { + t.Fatal(callErr) + } + if funcErr != nil { + t.Fatal(funcErr) + } + assertEqual(t, callBuffer.String(), + `"example.com/base/foo.Blah": schema_examplecom_base_foo_Blah(ref),`) + assertEqual(t, funcBuffer.String(), + `func schema_examplecom_base_foo_Blah(ref common.ReferenceCallback) common.OpenAPIDefinition { return common.OpenAPIDefinition{ Schema: spec.Schema{ SchemaProps: spec.SchemaProps{ @@ -889,229 +1000,331 @@ Required: []string{"StringToArray"}, }, }, } -} - -`, funcBuffer.String()) +}`) + }) } func TestFailingSample1(t *testing.T) { - _, funcErr, assert, _, _, _ := testOpenAPITypeWriter(t, ` -package foo + inputFile := ` + package foo -// Map sample tests openAPIGen.generateMapProperty method. -type Blah struct { - // A sample String to String map - StringToArray map[string]map[string]map[int]string -} - `) - if assert.Error(funcErr, "An error was expected") { - assert.Equal(funcErr, fmt.Errorf("failed to generate map property in base/foo.Blah: StringToArray: map with non-string keys are not supported by OpenAPI in map[int]string")) - } + // Map sample tests openAPIGen.generateMapProperty method. + type Blah struct { + // A sample String to String map + StringToArray map[string]map[string]map[int]string + }` + + packagestest.TestAll(t, func(t *testing.T, x packagestest.Exporter) { + e := packagestest.Export(t, x, []packagestest.Module{{ + Name: "example.com/base/foo", + Files: map[string]interface{}{ + "foo.go": inputFile, + }, + }}) + defer e.Cleanup() + + _, funcErr, _, _, _ := testOpenAPITypeWriter(t, e.Config) + if funcErr == nil { + t.Fatalf("An error was expected") + } + assertEqual(t, + "failed to generate map property in example.com/base/foo.Blah: StringToArray: map with non-string keys are not supported by OpenAPI in map[int]string", + funcErr.Error()) + }) } func TestFailingSample2(t *testing.T) { - _, funcErr, assert, _, _, _ := testOpenAPITypeWriter(t, ` -package foo - -// Map sample tests openAPIGen.generateMapProperty method. -type Blah struct { - // A sample String to String map - StringToArray map[int]string -} `) - if assert.Error(funcErr, "An error was expected") { - assert.Equal(funcErr, fmt.Errorf("failed to generate map property in base/foo.Blah: StringToArray: map with non-string keys are not supported by OpenAPI in map[int]string")) - } + inputFile := ` + package foo + + // Map sample tests openAPIGen.generateMapProperty method. + type Blah struct { + // A sample String to String map + StringToArray map[int]string + }` + + packagestest.TestAll(t, func(t *testing.T, x packagestest.Exporter) { + e := packagestest.Export(t, x, []packagestest.Module{{ + Name: "example.com/base/foo", + Files: map[string]interface{}{ + "foo.go": inputFile, + }, + }}) + defer e.Cleanup() + + _, funcErr, _, _, _ := testOpenAPITypeWriter(t, e.Config) + if funcErr == nil { + t.Fatalf("An error was expected") + } + assertEqual(t, + "failed to generate map property in example.com/base/foo.Blah: StringToArray: map with non-string keys are not supported by OpenAPI in map[int]string", + funcErr.Error()) + }) } func TestFailingDefaultEnforced(t *testing.T) { tests := []struct { definition string - expectedError error - }{ - { - definition: ` -package foo - -type Blah struct { - // +default=5 - Int int -} `, - expectedError: fmt.Errorf("failed to generate default in base/foo.Blah: Int: invalid default value (5) for non-pointer/non-omitempty. If specified, must be: 0"), - }, - { - definition: ` -package foo - -type Blah struct { - // +default={"foo": 5} - Struct struct{ - foo int - } -} `, - expectedError: fmt.Errorf(`failed to generate default in base/foo.Blah: Struct: invalid default value (map[string]interface {}{"foo":5}) for non-pointer/non-omitempty. If specified, must be: {}`), - }, - { - definition: ` -package foo + expectedError string + }{{ + definition: ` + package foo + + type Blah struct { + // +default=5 + Int int + }`, + expectedError: "failed to generate default in example.com/base/foo.Blah: Int: invalid default value (5) for non-pointer/non-omitempty. If specified, must be: 0", + }, { + definition: ` + package foo + + type Blah struct { + // +default={"foo": 5} + Struct struct{ + foo int + } + }`, + expectedError: `failed to generate default in example.com/base/foo.Blah: Struct: invalid default value (map[string]interface {}{"foo":5}) for non-pointer/non-omitempty. If specified, must be: {}`, + }, { + definition: ` + package foo + + type Blah struct { + List []Item -type Blah struct { - List []Item + } -} + // +default="foo" + type Item string`, + expectedError: `failed to generate slice property in example.com/base/foo.Blah: List: invalid default value ("foo") for non-pointer/non-omitempty. If specified, must be: ""`, + }, { + definition: ` + package foo -// +default="foo" -type Item string `, - expectedError: fmt.Errorf(`failed to generate slice property in base/foo.Blah: List: invalid default value ("foo") for non-pointer/non-omitempty. If specified, must be: ""`), - }, - { - definition: ` -package foo + type Blah struct { + Map map[string]Item -type Blah struct { - Map map[string]Item + } -} - -// +default="foo" -type Item string `, - expectedError: fmt.Errorf(`failed to generate map property in base/foo.Blah: Map: invalid default value ("foo") for non-pointer/non-omitempty. If specified, must be: ""`), - }, - } + // +default="foo" + type Item string`, + expectedError: `failed to generate map property in example.com/base/foo.Blah: Map: invalid default value ("foo") for non-pointer/non-omitempty. If specified, must be: ""`, + }} for i, test := range tests { t.Run(fmt.Sprintf("%d", i), func(t *testing.T) { - _, funcErr, assert, _, _, _ := testOpenAPITypeWriter(t, test.definition) - if assert.Error(funcErr, "An error was expected") { - assert.Equal(funcErr, test.expectedError) - } + packagestest.TestAll(t, func(t *testing.T, x packagestest.Exporter) { + e := packagestest.Export(t, x, []packagestest.Module{{ + Name: "example.com/base/foo", + Files: map[string]interface{}{ + "foo.go": test.definition, + }, + }}) + defer e.Cleanup() + + _, funcErr, _, _, _ := testOpenAPITypeWriter(t, e.Config) + if funcErr == nil { + t.Fatalf("An error was expected") + } + assertEqual(t, test.expectedError, funcErr.Error()) + }) }) } } func TestCustomDef(t *testing.T) { - callErr, funcErr, assert, callBuffer, funcBuffer, _ := testOpenAPITypeWriter(t, ` -package foo + inputFile := ` + package foo -import openapi "k8s.io/kube-openapi/pkg/common" + import openapi "k8s.io/kube-openapi/pkg/common" -type Blah struct { -} + type Blah struct { + } -func (_ Blah) OpenAPIDefinition() openapi.OpenAPIDefinition { - return openapi.OpenAPIDefinition{ - Schema: spec.Schema{ - SchemaProps: spec.SchemaProps{ - Type: []string{"string"}, - Format: "date-time", + func (_ Blah) OpenAPIDefinition() openapi.OpenAPIDefinition { + return openapi.OpenAPIDefinition{ + Schema: spec.Schema{ + SchemaProps: spec.SchemaProps{ + Type: []string{"string"}, + Format: "date-time", + }, + }, + } + }` + commonFile := `package common + + type OpenAPIDefinition struct {}` + + packagestest.TestAll(t, func(t *testing.T, x packagestest.Exporter) { + e := packagestest.Export(t, x, []packagestest.Module{{ + Name: "example.com/base/foo", + Files: map[string]interface{}{ + "foo.go": inputFile, }, - }, - } -} -`) - if callErr != nil { - t.Fatal(callErr) - } - if funcErr != nil { - t.Fatal(funcErr) - } - assert.Equal(`"base/foo.Blah": foo.Blah{}.OpenAPIDefinition(), -`, callBuffer.String()) - assert.Equal(``, funcBuffer.String()) + }, { + Name: "k8s.io/kube-openapi/pkg/common", + Files: map[string]interface{}{ + "common.go": commonFile, + }, + }}) + defer e.Cleanup() + + callErr, funcErr, callBuffer, funcBuffer, _ := testOpenAPITypeWriter(t, e.Config) + if callErr != nil { + t.Fatal(callErr) + } + if funcErr != nil { + t.Fatal(funcErr) + } + assertEqual(t, callBuffer.String(), + `"example.com/base/foo.Blah": foo.Blah{}.OpenAPIDefinition(),`) + assertEqual(t, "", funcBuffer.String()) + }) } func TestCustomDefV3(t *testing.T) { - callErr, funcErr, assert, callBuffer, funcBuffer, _ := testOpenAPITypeWriter(t, ` -package foo + inputFile := ` + package foo -import openapi "k8s.io/kube-openapi/pkg/common" + import openapi "k8s.io/kube-openapi/pkg/common" -type Blah struct { -} + type Blah struct { + } -func (_ Blah) OpenAPIV3Definition() openapi.OpenAPIDefinition { - return openapi.OpenAPIDefinition{ - Schema: spec.Schema{ - SchemaProps: spec.SchemaProps{ - Type: []string{"string"}, - Format: "date-time", + func (_ Blah) OpenAPIV3Definition() openapi.OpenAPIDefinition { + return openapi.OpenAPIDefinition{ + Schema: spec.Schema{ + SchemaProps: spec.SchemaProps{ + Type: []string{"string"}, + Format: "date-time", + }, + }, + } + }` + commonFile := `package common + + type OpenAPIDefinition struct {}` + + packagestest.TestAll(t, func(t *testing.T, x packagestest.Exporter) { + e := packagestest.Export(t, x, []packagestest.Module{{ + Name: "example.com/base/foo", + Files: map[string]interface{}{ + "foo.go": inputFile, }, - }, - } -} -`) - if callErr != nil { - t.Fatal(callErr) - } - if funcErr != nil { - t.Fatal(funcErr) - } - assert.Equal(`"base/foo.Blah": foo.Blah{}.OpenAPIV3Definition(), -`, callBuffer.String()) - assert.Equal(``, funcBuffer.String()) + }, { + Name: "k8s.io/kube-openapi/pkg/common", + Files: map[string]interface{}{ + "common.go": commonFile, + }, + }}) + defer e.Cleanup() + + callErr, funcErr, callBuffer, funcBuffer, _ := testOpenAPITypeWriter(t, e.Config) + if callErr != nil { + t.Fatal(callErr) + } + if funcErr != nil { + t.Fatal(funcErr) + } + assertEqual(t, callBuffer.String(), + `"example.com/base/foo.Blah": foo.Blah{}.OpenAPIV3Definition(),`) + assertEqual(t, "", funcBuffer.String()) + }) } func TestCustomDefV2AndV3(t *testing.T) { - callErr, funcErr, assert, callBuffer, funcBuffer, _ := testOpenAPITypeWriter(t, ` -package foo + inputFile := ` + package foo -import openapi "k8s.io/kube-openapi/pkg/common" + import openapi "k8s.io/kube-openapi/pkg/common" -type Blah struct { -} + type Blah struct { + } -func (_ Blah) OpenAPIV3Definition() openapi.OpenAPIDefinition { - return openapi.OpenAPIDefinition{ - Schema: spec.Schema{ - SchemaProps: spec.SchemaProps{ - Type: []string{"string"}, - Format: "date-time", - }, - }, - } -} + func (_ Blah) OpenAPIV3Definition() openapi.OpenAPIDefinition { + return openapi.OpenAPIDefinition{ + Schema: spec.Schema{ + SchemaProps: spec.SchemaProps{ + Type: []string{"string"}, + Format: "date-time", + }, + }, + } + } -func (_ Blah) OpenAPIDefinition() openapi.OpenAPIDefinition { - return openapi.OpenAPIDefinition{ - Schema: spec.Schema{ - SchemaProps: spec.SchemaProps{ - Type: []string{"string"}, - Format: "date-time", + func (_ Blah) OpenAPIDefinition() openapi.OpenAPIDefinition { + return openapi.OpenAPIDefinition{ + Schema: spec.Schema{ + SchemaProps: spec.SchemaProps{ + Type: []string{"string"}, + Format: "date-time", + }, + }, + } + }` + commonFile := `package common + + type OpenAPIDefinition struct {}` + + packagestest.TestAll(t, func(t *testing.T, x packagestest.Exporter) { + e := packagestest.Export(t, x, []packagestest.Module{{ + Name: "example.com/base/foo", + Files: map[string]interface{}{ + "foo.go": inputFile, }, - }, - } -} -`) - if callErr != nil { - t.Fatal(callErr) - } - if funcErr != nil { - t.Fatal(funcErr) - } - assert.Equal(`"base/foo.Blah": common.EmbedOpenAPIDefinitionIntoV2Extension(foo.Blah{}.OpenAPIV3Definition(), foo.Blah{}.OpenAPIDefinition()), -`, callBuffer.String()) - assert.Equal(``, funcBuffer.String()) + }, { + Name: "k8s.io/kube-openapi/pkg/common", + Files: map[string]interface{}{ + "common.go": commonFile, + }, + }}) + defer e.Cleanup() + + callErr, funcErr, callBuffer, funcBuffer, _ := testOpenAPITypeWriter(t, e.Config) + if callErr != nil { + t.Fatal(callErr) + } + if funcErr != nil { + t.Fatal(funcErr) + } + assertEqual(t, callBuffer.String(), + `"example.com/base/foo.Blah": common.EmbedOpenAPIDefinitionIntoV2Extension(foo.Blah{}.OpenAPIV3Definition(), foo.Blah{}.OpenAPIDefinition()),`) + assertEqual(t, "", funcBuffer.String()) + }) } func TestCustomDefs(t *testing.T) { - callErr, funcErr, assert, callBuffer, funcBuffer, _ := testOpenAPITypeWriter(t, ` -package foo + inputFile := ` + package foo -// Blah is a custom type -type Blah struct { -} + // Blah is a custom type + type Blah struct { + } -func (_ Blah) OpenAPISchemaType() []string { return []string{"string"} } -func (_ Blah) OpenAPISchemaFormat() string { return "date-time" } -`) - if callErr != nil { - t.Fatal(callErr) - } - if funcErr != nil { - t.Fatal(funcErr) - } - assert.Equal(`"base/foo.Blah": schema_base_foo_Blah(ref), -`, callBuffer.String()) - assert.Equal(`func schema_base_foo_Blah(ref common.ReferenceCallback) common.OpenAPIDefinition { + func (_ Blah) OpenAPISchemaType() []string { return []string{"string"} } + func (_ Blah) OpenAPISchemaFormat() string { return "date-time" }` + + packagestest.TestAll(t, func(t *testing.T, x packagestest.Exporter) { + e := packagestest.Export(t, x, []packagestest.Module{{ + Name: "example.com/base/foo", + Files: map[string]interface{}{ + "foo.go": inputFile, + }, + }}) + defer e.Cleanup() + + callErr, funcErr, callBuffer, funcBuffer, _ := testOpenAPITypeWriter(t, e.Config) + if callErr != nil { + t.Fatal(callErr) + } + if funcErr != nil { + t.Fatal(funcErr) + } + assertEqual(t, callBuffer.String(), + `"example.com/base/foo.Blah": schema_examplecom_base_foo_Blah(ref),`) + assertEqual(t, funcBuffer.String(), + `func schema_examplecom_base_foo_Blah(ref common.ReferenceCallback) common.OpenAPIDefinition { return common.OpenAPIDefinition{ Schema: spec.Schema{ SchemaProps: spec.SchemaProps{ @@ -1121,44 +1334,62 @@ Format:foo.Blah{}.OpenAPISchemaFormat(), }, }, } -} - -`, funcBuffer.String()) +}`) + }) } func TestCustomDefsV3(t *testing.T) { - callErr, funcErr, assert, callBuffer, funcBuffer, _ := testOpenAPITypeWriter(t, ` -package foo + inputFile := ` + package foo -import openapi "k8s.io/kube-openapi/pkg/common" + import openapi "k8s.io/kube-openapi/pkg/common" -// Blah is a custom type -type Blah struct { -} + // Blah is a custom type + type Blah struct { + } -func (_ Blah) OpenAPIV3Definition() openapi.OpenAPIDefinition { - return openapi.OpenAPIDefinition{ - Schema: spec.Schema{ - SchemaProps: spec.SchemaProps{ - Type: []string{"string"}, - Format: "date-time", + func (_ Blah) OpenAPIV3Definition() openapi.OpenAPIDefinition { + return openapi.OpenAPIDefinition{ + Schema: spec.Schema{ + SchemaProps: spec.SchemaProps{ + Type: []string{"string"}, + Format: "date-time", + }, + }, + } + } + + func (_ Blah) OpenAPISchemaType() []string { return []string{"string"} } + func (_ Blah) OpenAPISchemaFormat() string { return "date-time" }` + commonFile := `package common + + type OpenAPIDefinition struct {}` + + packagestest.TestAll(t, func(t *testing.T, x packagestest.Exporter) { + e := packagestest.Export(t, x, []packagestest.Module{{ + Name: "example.com/base/foo", + Files: map[string]interface{}{ + "foo.go": inputFile, }, - }, - } -} + }, { + Name: "k8s.io/kube-openapi/pkg/common", + Files: map[string]interface{}{ + "common.go": commonFile, + }, + }}) + defer e.Cleanup() -func (_ Blah) OpenAPISchemaType() []string { return []string{"string"} } -func (_ Blah) OpenAPISchemaFormat() string { return "date-time" } -`) - if callErr != nil { - t.Fatal(callErr) - } - if funcErr != nil { - t.Fatal(funcErr) - } - assert.Equal(`"base/foo.Blah": schema_base_foo_Blah(ref), -`, callBuffer.String()) - assert.Equal(`func schema_base_foo_Blah(ref common.ReferenceCallback) common.OpenAPIDefinition { + callErr, funcErr, callBuffer, funcBuffer, _ := testOpenAPITypeWriter(t, e.Config) + if callErr != nil { + t.Fatal(callErr) + } + if funcErr != nil { + t.Fatal(funcErr) + } + assertEqual(t, callBuffer.String(), + `"example.com/base/foo.Blah": schema_examplecom_base_foo_Blah(ref),`) + assertEqual(t, funcBuffer.String(), + `func schema_examplecom_base_foo_Blah(ref common.ReferenceCallback) common.OpenAPIDefinition { return common.EmbedOpenAPIDefinitionIntoV2Extension(foo.Blah{}.OpenAPIV3Definition(), common.OpenAPIDefinition{ Schema: spec.Schema{ SchemaProps: spec.SchemaProps{ @@ -1168,33 +1399,42 @@ Format:foo.Blah{}.OpenAPISchemaFormat(), }, }, }) -} - -`, funcBuffer.String()) +}`) + }) } func TestV3OneOfTypes(t *testing.T) { - callErr, funcErr, assert, callBuffer, funcBuffer, _ := testOpenAPITypeWriter(t, ` -package foo + inputFile := ` + package foo -// Blah is a custom type -type Blah struct { -} + // Blah is a custom type + type Blah struct { + } -func (_ Blah) OpenAPISchemaType() []string { return []string{"string"} } -func (_ Blah) OpenAPISchemaFormat() string { return "date-time" } -func (_ Blah) OpenAPIV3OneOfTypes() []string { return []string{"string", "number"} } + func (_ Blah) OpenAPISchemaType() []string { return []string{"string"} } + func (_ Blah) OpenAPISchemaFormat() string { return "date-time" } + func (_ Blah) OpenAPIV3OneOfTypes() []string { return []string{"string", "number"} }` -`) - if callErr != nil { - t.Fatal(callErr) - } - if funcErr != nil { - t.Fatal(funcErr) - } - assert.Equal(`"base/foo.Blah": schema_base_foo_Blah(ref), -`, callBuffer.String()) - assert.Equal(`func schema_base_foo_Blah(ref common.ReferenceCallback) common.OpenAPIDefinition { + packagestest.TestAll(t, func(t *testing.T, x packagestest.Exporter) { + e := packagestest.Export(t, x, []packagestest.Module{{ + Name: "example.com/base/foo", + Files: map[string]interface{}{ + "foo.go": inputFile, + }, + }}) + defer e.Cleanup() + + callErr, funcErr, callBuffer, funcBuffer, _ := testOpenAPITypeWriter(t, e.Config) + if callErr != nil { + t.Fatal(callErr) + } + if funcErr != nil { + t.Fatal(funcErr) + } + assertEqual(t, callBuffer.String(), + `"example.com/base/foo.Blah": schema_examplecom_base_foo_Blah(ref),`) + assertEqual(t, funcBuffer.String(), + `func schema_examplecom_base_foo_Blah(ref common.ReferenceCallback) common.OpenAPIDefinition { return common.EmbedOpenAPIDefinitionIntoV2Extension(common.OpenAPIDefinition{ Schema: spec.Schema{ SchemaProps: spec.SchemaProps{ @@ -1212,36 +1452,45 @@ Format:foo.Blah{}.OpenAPISchemaFormat(), }, }, }) +}`) + }) } -`, funcBuffer.String()) -} +func TestPointer(t *testing.T) { + inputFile := ` + package foo + + // PointerSample demonstrate pointer's properties + type Blah struct { + // A string pointer + StringPointer *string + // A struct pointer + StructPointer *Blah + // A slice pointer + SlicePointer *[]string + // A map pointer + MapPointer *map[string]string + }` + packagestest.TestAll(t, func(t *testing.T, x packagestest.Exporter) { + e := packagestest.Export(t, x, []packagestest.Module{{ + Name: "example.com/base/foo", + Files: map[string]interface{}{ + "foo.go": inputFile, + }, + }}) + defer e.Cleanup() -func TestPointer(t *testing.T) { - callErr, funcErr, assert, callBuffer, funcBuffer, _ := testOpenAPITypeWriter(t, ` -package foo - -// PointerSample demonstrate pointer's properties -type Blah struct { - // A string pointer - StringPointer *string - // A struct pointer - StructPointer *Blah - // A slice pointer - SlicePointer *[]string - // A map pointer - MapPointer *map[string]string -} - `) - if callErr != nil { - t.Fatal(callErr) - } - if funcErr != nil { - t.Fatal(funcErr) - } - assert.Equal(`"base/foo.Blah": schema_base_foo_Blah(ref), -`, callBuffer.String()) - assert.Equal(`func schema_base_foo_Blah(ref common.ReferenceCallback) common.OpenAPIDefinition { + callErr, funcErr, callBuffer, funcBuffer, _ := testOpenAPITypeWriter(t, e.Config) + if callErr != nil { + t.Fatal(callErr) + } + if funcErr != nil { + t.Fatal(funcErr) + } + assertEqual(t, callBuffer.String(), + `"example.com/base/foo.Blah": schema_examplecom_base_foo_Blah(ref),`) + assertEqual(t, funcBuffer.String(), + `func schema_examplecom_base_foo_Blah(ref common.ReferenceCallback) common.OpenAPIDefinition { return common.OpenAPIDefinition{ Schema: spec.Schema{ SchemaProps: spec.SchemaProps{ @@ -1258,7 +1507,7 @@ Format: "", "StructPointer": { SchemaProps: spec.SchemaProps{ Description: "A struct pointer", -Ref: ref("base/foo.Blah"), +Ref: ref("example.com/base/foo.Blah"), }, }, "SlicePointer": { @@ -1297,34 +1546,43 @@ Required: []string{"StringPointer","StructPointer","SlicePointer","MapPointer"}, }, }, Dependencies: []string{ -"base/foo.Blah",}, +"example.com/base/foo.Blah",}, } -} - -`, funcBuffer.String()) +}`) + }) } func TestNestedLists(t *testing.T) { - callErr, funcErr, assert, callBuffer, funcBuffer, _ := testOpenAPITypeWriter(t, ` -package foo - -// Blah is a test. -// +k8s:openapi-gen=true -// +k8s:openapi-gen=x-kubernetes-type-tag:type_test -type Blah struct { - // Nested list - NestedList [][]int64 -} -`) - if callErr != nil { - t.Fatal(callErr) - } - if funcErr != nil { - t.Fatal(funcErr) - } - assert.Equal(`"base/foo.Blah": schema_base_foo_Blah(ref), -`, callBuffer.String()) - assert.Equal(`func schema_base_foo_Blah(ref common.ReferenceCallback) common.OpenAPIDefinition { + inputFile := ` + package foo + + // Blah is a test. + // +k8s:openapi-gen=true + // +k8s:openapi-gen=x-kubernetes-type-tag:type_test + type Blah struct { + // Nested list + NestedList [][]int64 + }` + packagestest.TestAll(t, func(t *testing.T, x packagestest.Exporter) { + e := packagestest.Export(t, x, []packagestest.Module{{ + Name: "example.com/base/foo", + Files: map[string]interface{}{ + "foo.go": inputFile, + }, + }}) + defer e.Cleanup() + + callErr, funcErr, callBuffer, funcBuffer, _ := testOpenAPITypeWriter(t, e.Config) + if callErr != nil { + t.Fatal(callErr) + } + if funcErr != nil { + t.Fatal(funcErr) + } + assertEqual(t, callBuffer.String(), + `"example.com/base/foo.Blah": schema_examplecom_base_foo_Blah(ref),`) + assertEqual(t, funcBuffer.String(), + `func schema_examplecom_base_foo_Blah(ref common.ReferenceCallback) common.OpenAPIDefinition { return common.OpenAPIDefinition{ Schema: spec.Schema{ SchemaProps: spec.SchemaProps{ @@ -1363,32 +1621,41 @@ Extensions: spec.Extensions{ }, }, } -} - -`, funcBuffer.String()) +}`) + }) } func TestNestListOfMaps(t *testing.T) { - callErr, funcErr, assert, callBuffer, funcBuffer, _ := testOpenAPITypeWriter(t, ` -package foo - -// Blah is a test. -// +k8s:openapi-gen=true -// +k8s:openapi-gen=x-kubernetes-type-tag:type_test -type Blah struct { - // Nested list of maps - NestedListOfMaps [][]map[string]string -} -`) - if callErr != nil { - t.Fatal(callErr) - } - if funcErr != nil { - t.Fatal(funcErr) - } - assert.Equal(`"base/foo.Blah": schema_base_foo_Blah(ref), -`, callBuffer.String()) - assert.Equal(`func schema_base_foo_Blah(ref common.ReferenceCallback) common.OpenAPIDefinition { + inputFile := ` + package foo + + // Blah is a test. + // +k8s:openapi-gen=true + // +k8s:openapi-gen=x-kubernetes-type-tag:type_test + type Blah struct { + // Nested list of maps + NestedListOfMaps [][]map[string]string + }` + packagestest.TestAll(t, func(t *testing.T, x packagestest.Exporter) { + e := packagestest.Export(t, x, []packagestest.Module{{ + Name: "example.com/base/foo", + Files: map[string]interface{}{ + "foo.go": inputFile, + }, + }}) + defer e.Cleanup() + + callErr, funcErr, callBuffer, funcBuffer, _ := testOpenAPITypeWriter(t, e.Config) + if callErr != nil { + t.Fatal(callErr) + } + if funcErr != nil { + t.Fatal(funcErr) + } + assertEqual(t, callBuffer.String(), + `"example.com/base/foo.Blah": schema_examplecom_base_foo_Blah(ref),`) + assertEqual(t, funcBuffer.String(), + `func schema_examplecom_base_foo_Blah(ref common.ReferenceCallback) common.OpenAPIDefinition { return common.OpenAPIDefinition{ Schema: spec.Schema{ SchemaProps: spec.SchemaProps{ @@ -1435,40 +1702,49 @@ Extensions: spec.Extensions{ }, }, } -} - -`, funcBuffer.String()) +}`) + }) } func TestExtensions(t *testing.T) { - callErr, funcErr, assert, callBuffer, funcBuffer, _ := testOpenAPITypeWriter(t, ` -package foo - -// Blah is a test. -// +k8s:openapi-gen=true -// +k8s:openapi-gen=x-kubernetes-type-tag:type_test -type Blah struct { - // a member with a list type with two map keys - // +listType=map - // +listMapKey=port - // +listMapKey=protocol - WithListField []string - - // another member with a list type with one map key - // +listType=map - // +listMapKey=port - WithListField2 []string -} - `) - if callErr != nil { - t.Fatal(callErr) - } - if funcErr != nil { - t.Fatal(funcErr) - } - assert.Equal(`"base/foo.Blah": schema_base_foo_Blah(ref), -`, callBuffer.String()) - assert.Equal(`func schema_base_foo_Blah(ref common.ReferenceCallback) common.OpenAPIDefinition { + inputFile := ` + package foo + + // Blah is a test. + // +k8s:openapi-gen=true + // +k8s:openapi-gen=x-kubernetes-type-tag:type_test + type Blah struct { + // a member with a list type with two map keys + // +listType=map + // +listMapKey=port + // +listMapKey=protocol + WithListField []string + + // another member with a list type with one map key + // +listType=map + // +listMapKey=port + WithListField2 []string + }` + packagestest.TestAll(t, func(t *testing.T, x packagestest.Exporter) { + e := packagestest.Export(t, x, []packagestest.Module{{ + Name: "example.com/base/foo", + Files: map[string]interface{}{ + "foo.go": inputFile, + }, + }}) + defer e.Cleanup() + + callErr, funcErr, callBuffer, funcBuffer, _ := testOpenAPITypeWriter(t, e.Config) + if callErr != nil { + t.Fatal(callErr) + } + if funcErr != nil { + t.Fatal(funcErr) + } + assertEqual(t, callBuffer.String(), + `"example.com/base/foo.Blah": schema_examplecom_base_foo_Blah(ref),`) + assertEqual(t, funcBuffer.String(), + `func schema_examplecom_base_foo_Blah(ref common.ReferenceCallback) common.OpenAPIDefinition { return common.OpenAPIDefinition{ Schema: spec.Schema{ SchemaProps: spec.SchemaProps{ @@ -1532,39 +1808,48 @@ Extensions: spec.Extensions{ }, }, } -} - -`, funcBuffer.String()) +}`) + }) } func TestUnion(t *testing.T) { - callErr, funcErr, assert, callBuffer, funcBuffer, _ := testOpenAPITypeWriter(t, ` -package foo - -// Blah is a test. -// +k8s:openapi-gen=true -// +k8s:openapi-gen=x-kubernetes-type-tag:type_test -// +union -type Blah struct { - // +unionDiscriminator - Discriminator *string `+"`"+`json:"discriminator"`+"`"+` - // +optional - Numeric int `+"`"+`json:"numeric"`+"`"+` - // +optional - String string `+"`"+`json:"string"`+"`"+` - // +optional - Float float64 `+"`"+`json:"float"`+"`"+` -} - `) - if callErr != nil { - t.Fatal(callErr) - } - if funcErr != nil { - t.Fatal(funcErr) - } - assert.Equal(`"base/foo.Blah": schema_base_foo_Blah(ref), -`, callBuffer.String()) - assert.Equal(`func schema_base_foo_Blah(ref common.ReferenceCallback) common.OpenAPIDefinition { + inputFile := ` + package foo + + // Blah is a test. + // +k8s:openapi-gen=true + // +k8s:openapi-gen=x-kubernetes-type-tag:type_test + // +union + type Blah struct { + // +unionDiscriminator + Discriminator *string ` + "`" + `json:"discriminator"` + "`" + ` + // +optional + Numeric int ` + "`" + `json:"numeric"` + "`" + ` + // +optional + String string ` + "`" + `json:"string"` + "`" + ` + // +optional + Float float64 ` + "`" + `json:"float"` + "`" + ` + }` + packagestest.TestAll(t, func(t *testing.T, x packagestest.Exporter) { + e := packagestest.Export(t, x, []packagestest.Module{{ + Name: "example.com/base/foo", + Files: map[string]interface{}{ + "foo.go": inputFile, + }, + }}) + defer e.Cleanup() + + callErr, funcErr, callBuffer, funcBuffer, _ := testOpenAPITypeWriter(t, e.Config) + if callErr != nil { + t.Fatal(callErr) + } + if funcErr != nil { + t.Fatal(funcErr) + } + assertEqual(t, callBuffer.String(), + `"example.com/base/foo.Blah": schema_examplecom_base_foo_Blah(ref),`) + assertEqual(t, funcBuffer.String(), + `func schema_examplecom_base_foo_Blah(ref common.ReferenceCallback) common.OpenAPIDefinition { return common.OpenAPIDefinition{ Schema: spec.Schema{ SchemaProps: spec.SchemaProps{ @@ -1618,54 +1903,66 @@ map[string]interface{}{ }, }, } -} - -`, funcBuffer.String()) +}`) + }) } func TestEnumAlias(t *testing.T) { - callErr, funcErr, assert, _, funcBuffer, _ := testOpenAPITypeWriterWithFiles(t, ` - package foo + inputFile := ` + package foo - import "base/bar" - - // EnumType is the enumType. - // +enum - type EnumType = bar.EnumType - - // EnumA is a. - const EnumA EnumType = bar.EnumA - // EnumB is b. - const EnumB EnumType = bar.EnumB - - // Blah is a test. - // +k8s:openapi-gen=true - type Blah struct { - // Value is the value. - Value EnumType - } + import "example.com/base/bar" - `, map[string]string{"base/bar/foo.go": ` - package bar - - // EnumType is the enumType. - // +enum - type EnumType string - - // EnumA is a. - const EnumA EnumType = "a" - // EnumB is b. - const EnumB EnumType = "b" - `}) - - if callErr != nil { - t.Fatal(callErr) - } - if funcErr != nil { - t.Fatal(funcErr) - } - _ = assert - assert.Equal(`func schema_base_foo_Blah(ref common.ReferenceCallback) common.OpenAPIDefinition { + // EnumType is the enumType. + // +enum + type EnumType = bar.EnumType + + // EnumA is a. + const EnumA EnumType = bar.EnumA + // EnumB is b. + const EnumB EnumType = bar.EnumB + + // Blah is a test. + // +k8s:openapi-gen=true + type Blah struct { + // Value is the value. + Value EnumType + }` + otherFile := ` + package bar + + // EnumType is the enumType. + // +enum + type EnumType string + + // EnumA is a. + const EnumA EnumType = "a" + // EnumB is b. + const EnumB EnumType = "b"` + + packagestest.TestAll(t, func(t *testing.T, x packagestest.Exporter) { + e := packagestest.Export(t, x, []packagestest.Module{{ + Name: "example.com/base/foo", + Files: map[string]interface{}{ + "foo.go": inputFile, + }, + }, { + Name: "example.com/base/bar", + Files: map[string]interface{}{ + "bar.go": otherFile, + }, + }}) + defer e.Cleanup() + + callErr, funcErr, _, funcBuffer, _ := testOpenAPITypeWriter(t, e.Config) + if callErr != nil { + t.Fatal(callErr) + } + if funcErr != nil { + t.Fatal(funcErr) + } + assertEqual(t, funcBuffer.String(), + `func schema_examplecom_base_foo_Blah(ref common.ReferenceCallback) common.OpenAPIDefinition { return common.OpenAPIDefinition{ Schema: spec.Schema{ SchemaProps: spec.SchemaProps{ @@ -1674,7 +1971,7 @@ Type: []string{"object"}, Properties: map[string]spec.Schema{ "Value": { SchemaProps: spec.SchemaProps{`+"\n"+ - "Description: \"Value is the value.\\n\\nPossible enum values:\\n - `\\\"a\\\"` is a.\\n - `\\\"b\\\"` is b.\","+` + "Description: \"Value is the value.\\n\\nPossible enum values:\\n - `\\\"a\\\"` is a.\\n - `\\\"b\\\"` is b.\","+` Default: "", Type: []string{"string"}, Format: "", @@ -1686,45 +1983,54 @@ Required: []string{"Value"}, }, }, } +}`) + }) } -`, funcBuffer.String()) +func TestEnum(t *testing.T) { + inputFile := ` + package foo -} + // EnumType is the enumType. + // +enum + type EnumType string -func TestEnum(t *testing.T) { - callErr, funcErr, assert, _, funcBuffer, _ := testOpenAPITypeWriter(t, ` -package foo - -// EnumType is the enumType. -// +enum -type EnumType string - -// EnumA is a. -const EnumA EnumType = "a" -// EnumB is b. -const EnumB EnumType = "b" - -// Blah is a test. -// +k8s:openapi-gen=true -// +k8s:openapi-gen=x-kubernetes-type-tag:type_test -type Blah struct { - // Value is the value. - Value EnumType - NoCommentEnum EnumType - // +optional - OptionalEnum *EnumType - List []EnumType - Map map[string]EnumType -}`) - if callErr != nil { - t.Fatal(callErr) - } - if funcErr != nil { - t.Fatal(funcErr) - } - _ = assert - assert.Equal(`func schema_base_foo_Blah(ref common.ReferenceCallback) common.OpenAPIDefinition { + // EnumA is a. + const EnumA EnumType = "a" + // EnumB is b. + const EnumB EnumType = "b" + + // Blah is a test. + // +k8s:openapi-gen=true + // +k8s:openapi-gen=x-kubernetes-type-tag:type_test + type Blah struct { + // Value is the value. + Value EnumType + NoCommentEnum EnumType + // +optional + OptionalEnum *EnumType + List []EnumType + Map map[string]EnumType + }` + + packagestest.TestAll(t, func(t *testing.T, x packagestest.Exporter) { + e := packagestest.Export(t, x, []packagestest.Module{{ + Name: "example.com/base/foo", + Files: map[string]interface{}{ + "foo.go": inputFile, + }, + }}) + defer e.Cleanup() + + callErr, funcErr, _, funcBuffer, _ := testOpenAPITypeWriter(t, e.Config) + if callErr != nil { + t.Fatal(callErr) + } + if funcErr != nil { + t.Fatal(funcErr) + } + assertEqual(t, funcBuffer.String(), + `func schema_examplecom_base_foo_Blah(ref common.ReferenceCallback) common.OpenAPIDefinition { return common.OpenAPIDefinition{ Schema: spec.Schema{ SchemaProps: spec.SchemaProps{ @@ -1733,7 +2039,7 @@ Type: []string{"object"}, Properties: map[string]spec.Schema{ "Value": { SchemaProps: spec.SchemaProps{`+"\n"+ - "Description: \"Value is the value.\\n\\nPossible enum values:\\n - `\\\"a\\\"` is a.\\n - `\\\"b\\\"` is b.\","+` + "Description: \"Value is the value.\\n\\nPossible enum values:\\n - `\\\"a\\\"` is a.\\n - `\\\"b\\\"` is b.\","+` Default: "", Type: []string{"string"}, Format: "", @@ -1742,7 +2048,7 @@ Enum: []interface{}{"a", "b"}, }, "NoCommentEnum": { SchemaProps: spec.SchemaProps{`+"\n"+ - "Description: \"Possible enum values:\\n - `\\\"a\\\"` is a.\\n - `\\\"b\\\"` is b.\","+` + "Description: \"Possible enum values:\\n - `\\\"a\\\"` is a.\\n - `\\\"b\\\"` is b.\","+` Default: "", Type: []string{"string"}, Format: "", @@ -1751,7 +2057,7 @@ Enum: []interface{}{"a", "b"}, }, "OptionalEnum": { SchemaProps: spec.SchemaProps{`+"\n"+ - "Description: \"Possible enum values:\\n - `\\\"a\\\"` is a.\\n - `\\\"b\\\"` is b.\","+` + "Description: \"Possible enum values:\\n - `\\\"a\\\"` is a.\\n - `\\\"b\\\"` is b.\","+` Type: []string{"string"}, Format: "", Enum: []interface{}{"a", "b"}, @@ -1798,48 +2104,69 @@ Extensions: spec.Extensions{ }, }, } -} - -`, funcBuffer.String()) +}`) + }) } func TestSymbolReference(t *testing.T) { - callErr, funcErr, assert, _, funcBuffer, imports := testOpenAPITypeWriter(t, ` -package foo - -// +k8s:openapi-gen=true -type Blah struct { - // +default="A Default Value" - // +optional - Value *string - - // User constant local to the output package fully qualified - // +default=ref(base/output.MyConst) - // +optional - FullyQualifiedOutputValue *string - - // Local to types but not to output - // +default=ref(MyConst) - // +optional - LocalValue *string - - // +default=ref(base/foo.MyConst) - // +optional - FullyQualifiedLocalValue *string - - // +default=ref(k8s.io/api/v1.TerminationPathDefault) - // +optional - FullyQualifiedExternalValue *string -} - `) - assert.NoError(funcErr) - assert.NoError(callErr) - assert.ElementsMatch(imports, []string{`foo "base/foo"`, `v1 "k8s.io/api/v1"`, `common "k8s.io/kube-openapi/pkg/common"`, `spec "k8s.io/kube-openapi/pkg/validation/spec"`}) - - if formatted, err := format.Source(funcBuffer.Bytes()); err != nil { - t.Fatal(err) - } else { - assert.Equal(string(formatted), `func schema_base_foo_Blah(ref common.ReferenceCallback) common.OpenAPIDefinition { + inputFile := ` + package foo + + // +k8s:openapi-gen=true + type Blah struct { + // +default="A Default Value" + // +optional + Value *string + + // User constant local to the output package fully qualified + // +default=ref(example.com/base/output.MyConst) + // +optional + FullyQualifiedOutputValue *string + + // Local to types but not to output + // +default=ref(MyConst) + // +optional + LocalValue *string + + // +default=ref(example.com/base/foo.MyConst) + // +optional + FullyQualifiedLocalValue *string + + // +default=ref(k8s.io/api/v1.TerminationPathDefault) + // +optional + FullyQualifiedExternalValue *string + }` + + packagestest.TestAll(t, func(t *testing.T, x packagestest.Exporter) { + e := packagestest.Export(t, x, []packagestest.Module{{ + Name: "example.com/base/foo", + Files: map[string]interface{}{ + "foo.go": inputFile, + }, + }}) + defer e.Cleanup() + + callErr, funcErr, _, funcBuffer, imports := testOpenAPITypeWriter(t, e.Config) + if funcErr != nil { + t.Fatalf("Unexpected funcErr: %v", funcErr) + } + if callErr != nil { + t.Fatalf("Unexpected callErr: %v", callErr) + } + expImports := []string{ + `foo "example.com/base/foo"`, + `v1 "k8s.io/api/v1"`, + `common "k8s.io/kube-openapi/pkg/common"`, + `spec "k8s.io/kube-openapi/pkg/validation/spec"`, + } + if !cmp.Equal(imports, expImports) { + t.Errorf("wrong imports:\n%s", cmp.Diff(expImports, imports)) + } + + if formatted, err := format.Source(funcBuffer.Bytes()); err != nil { + t.Fatal(err) + } else { + assertEqual(t, string(formatted), `func schema_examplecom_base_foo_Blah(ref common.ReferenceCallback) common.OpenAPIDefinition { return common.OpenAPIDefinition{ Schema: spec.Schema{ SchemaProps: spec.SchemaProps{ @@ -1886,81 +2213,100 @@ type Blah struct { }, }, } -} - -`) - } - +}`) + } + }) } // Show that types with unmarshalJSON in their hierarchy do not have struct // defaults enforced, and that aliases and embededd types are respected func TestMustEnforceDefaultStruct(t *testing.T) { - callErr, funcErr, assert, _, funcBuffer, imports := testOpenAPITypeWriter(t, ` -package foo + inputFile := ` + package foo -type Time struct { - value interface{} -} + type Time struct { + value interface{} + } -type TimeWithoutUnmarshal struct { - value interface{} -} + type TimeWithoutUnmarshal struct { + value interface{} + } -func (_ TimeWithoutUnmarshal) OpenAPISchemaType() []string { return []string{"string"} } -func (_ TimeWithoutUnmarshal) OpenAPISchemaFormat() string { return "date-time" } + func (_ TimeWithoutUnmarshal) OpenAPISchemaType() []string { return []string{"string"} } + func (_ TimeWithoutUnmarshal) OpenAPISchemaFormat() string { return "date-time" } -func (_ Time) UnmarshalJSON([]byte) error { - return nil -} + func (_ Time) UnmarshalJSON([]byte) error { + return nil + } -func (_ Time) OpenAPISchemaType() []string { return []string{"string"} } -func (_ Time) OpenAPISchemaFormat() string { return "date-time" } + func (_ Time) OpenAPISchemaType() []string { return []string{"string"} } + func (_ Time) OpenAPISchemaFormat() string { return "date-time" } -// Time with UnmarshalJSON defined on pointer instead of struct -type MicroTime struct { - value interface{} -} + // Time with UnmarshalJSON defined on pointer instead of struct + type MicroTime struct { + value interface{} + } -func (t *MicroTime) UnmarshalJSON([]byte) error { - return nil -} + func (t *MicroTime) UnmarshalJSON([]byte) error { + return nil + } -func (_ MicroTime) OpenAPISchemaType() []string { return []string{"string"} } -func (_ MicroTime) OpenAPISchemaFormat() string { return "date-time" } + func (_ MicroTime) OpenAPISchemaType() []string { return []string{"string"} } + func (_ MicroTime) OpenAPISchemaFormat() string { return "date-time" } -type Int64 int64 + type Int64 int64 -type Duration struct { - Int64 -} + type Duration struct { + Int64 + } -func (_ Duration) OpenAPISchemaType() []string { return []string{"string"} } -func (_ Duration) OpenAPISchemaFormat() string { return "" } + func (_ Duration) OpenAPISchemaType() []string { return []string{"string"} } + func (_ Duration) OpenAPISchemaFormat() string { return "" } -type NothingSpecial struct { - Field string -} + type NothingSpecial struct { + Field string + } -// +k8s:openapi-gen=true -type Blah struct { - Embedded Duration - PointerUnmarshal MicroTime - StructUnmarshal Time - NoUnmarshal TimeWithoutUnmarshal - Regular NothingSpecial -} - `) - assert.NoError(funcErr) - assert.NoError(callErr) - assert.ElementsMatch(imports, []string{`foo "base/foo"`, `common "k8s.io/kube-openapi/pkg/common"`, `spec "k8s.io/kube-openapi/pkg/validation/spec"`}) - - if formatted, err := format.Source(funcBuffer.Bytes()); err != nil { - t.Fatal(err) - } else { - assert.Equal(string(formatted), `func schema_base_foo_Blah(ref common.ReferenceCallback) common.OpenAPIDefinition { + // +k8s:openapi-gen=true + type Blah struct { + Embedded Duration + PointerUnmarshal MicroTime + StructUnmarshal Time + NoUnmarshal TimeWithoutUnmarshal + Regular NothingSpecial + }` + + packagestest.TestAll(t, func(t *testing.T, x packagestest.Exporter) { + e := packagestest.Export(t, x, []packagestest.Module{{ + Name: "example.com/base/foo", + Files: map[string]interface{}{ + "foo.go": inputFile, + }, + }}) + defer e.Cleanup() + + callErr, funcErr, _, funcBuffer, imports := testOpenAPITypeWriter(t, e.Config) + if funcErr != nil { + t.Fatalf("Unexpected funcErr: %v", funcErr) + } + if callErr != nil { + t.Fatalf("Unexpected callErr: %v", callErr) + } + expImports := []string{ + `foo "example.com/base/foo"`, + `common "k8s.io/kube-openapi/pkg/common"`, + `spec "k8s.io/kube-openapi/pkg/validation/spec"`, + } + if !cmp.Equal(imports, expImports) { + t.Errorf("wrong imports:\n%s", cmp.Diff(expImports, imports)) + } + + if formatted, err := format.Source(funcBuffer.Bytes()); err != nil { + t.Fatal(err) + } else { + assertEqual(t, string(formatted), `func schema_examplecom_base_foo_Blah(ref common.ReferenceCallback) common.OpenAPIDefinition { return common.OpenAPIDefinition{ Schema: spec.Schema{ SchemaProps: spec.SchemaProps{ @@ -1969,29 +2315,29 @@ type Blah struct { "Embedded": { SchemaProps: spec.SchemaProps{ Default: 0, - Ref: ref("base/foo.Duration"), + Ref: ref("example.com/base/foo.Duration"), }, }, "PointerUnmarshal": { SchemaProps: spec.SchemaProps{ - Ref: ref("base/foo.MicroTime"), + Ref: ref("example.com/base/foo.MicroTime"), }, }, "StructUnmarshal": { SchemaProps: spec.SchemaProps{ - Ref: ref("base/foo.Time"), + Ref: ref("example.com/base/foo.Time"), }, }, "NoUnmarshal": { SchemaProps: spec.SchemaProps{ Default: map[string]interface{}{}, - Ref: ref("base/foo.TimeWithoutUnmarshal"), + Ref: ref("example.com/base/foo.TimeWithoutUnmarshal"), }, }, "Regular": { SchemaProps: spec.SchemaProps{ Default: map[string]interface{}{}, - Ref: ref("base/foo.NothingSpecial"), + Ref: ref("example.com/base/foo.NothingSpecial"), }, }, }, @@ -1999,58 +2345,77 @@ type Blah struct { }, }, Dependencies: []string{ - "base/foo.Duration", "base/foo.MicroTime", "base/foo.NothingSpecial", "base/foo.Time", "base/foo.TimeWithoutUnmarshal"}, + "example.com/base/foo.Duration", "example.com/base/foo.MicroTime", "example.com/base/foo.NothingSpecial", "example.com/base/foo.Time", "example.com/base/foo.TimeWithoutUnmarshal"}, } +}`) + } + }) } -`) - } +func TestMarkerComments(t *testing.T) { + inputFile := ` + package foo -} + // +k8s:openapi-gen=true + // +k8s:validation:maxProperties=10 + // +k8s:validation:minProperties=1 + // +k8s:validation:exclusiveMinimum + // +k8s:validation:exclusiveMaximum + type Blah struct { -func TestMarkerComments(t *testing.T) { + // Integer with min and max values + // +k8s:validation:minimum=0 + // +k8s:validation:maximum=10 + // +k8s:validation:exclusiveMinimum + // +k8s:validation:exclusiveMaximum + IntValue int + + // String with min and max lengths + // +k8s:validation:minLength=1 + // +k8s:validation:maxLength=10 + // +k8s:validation:pattern="^foo$[0-9]+" + StringValue string + + // +k8s:validation:maxitems=10 + // +k8s:validation:minItems=1 + // +k8s:validation:uniqueItems + ArrayValue []string + + // +k8s:validation:maxProperties=10 + // +k8s:validation:minProperties=1 + ObjValue map[string]interface{} + }` + + packagestest.TestAll(t, func(t *testing.T, x packagestest.Exporter) { + e := packagestest.Export(t, x, []packagestest.Module{{ + Name: "example.com/base/foo", + Files: map[string]interface{}{ + "foo.go": inputFile, + }, + }}) + defer e.Cleanup() - callErr, funcErr, assert, _, funcBuffer, imports := testOpenAPITypeWriter(t, ` -package foo - -// +k8s:openapi-gen=true -// +k8s:validation:maxProperties=10 -// +k8s:validation:minProperties=1 -// +k8s:validation:exclusiveMinimum -// +k8s:validation:exclusiveMaximum -type Blah struct { - - // Integer with min and max values - // +k8s:validation:minimum=0 - // +k8s:validation:maximum=10 - // +k8s:validation:exclusiveMinimum - // +k8s:validation:exclusiveMaximum - IntValue int - - // String with min and max lengths - // +k8s:validation:minLength=1 - // +k8s:validation:maxLength=10 - // +k8s:validation:pattern="^foo$[0-9]+" - StringValue string - - // +k8s:validation:maxitems=10 - // +k8s:validation:minItems=1 - // +k8s:validation:uniqueItems - ArrayValue []string - - // +k8s:validation:maxProperties=10 - // +k8s:validation:minProperties=1 - ObjValue map[string]interface{} -} - `) - assert.NoError(funcErr) - assert.NoError(callErr) - assert.ElementsMatch(imports, []string{`foo "base/foo"`, `common "k8s.io/kube-openapi/pkg/common"`, `spec "k8s.io/kube-openapi/pkg/validation/spec"`, `ptr "k8s.io/utils/ptr"`}) - - if formatted, err := format.Source(funcBuffer.Bytes()); err != nil { - t.Fatalf("%v\n%v", err, string(funcBuffer.Bytes())) - } else { - formatted_expected, ree := format.Source([]byte(`func schema_base_foo_Blah(ref common.ReferenceCallback) common.OpenAPIDefinition { + callErr, funcErr, _, funcBuffer, imports := testOpenAPITypeWriter(t, e.Config) + if funcErr != nil { + t.Fatalf("Unexpected funcErr: %v", funcErr) + } + if callErr != nil { + t.Fatalf("Unexpected callErr: %v", callErr) + } + expImports := []string{ + `foo "example.com/base/foo"`, + `common "k8s.io/kube-openapi/pkg/common"`, + `spec "k8s.io/kube-openapi/pkg/validation/spec"`, + `ptr "k8s.io/utils/ptr"`, + } + if !cmp.Equal(imports, expImports) { + t.Errorf("wrong imports:\n%s", cmp.Diff(expImports, imports)) + } + + if formatted, err := format.Source(funcBuffer.Bytes()); err != nil { + t.Fatalf("%v\n%v", err, string(funcBuffer.Bytes())) + } else { + formatted_expected, ree := format.Source([]byte(`func schema_examplecom_base_foo_Blah(ref common.ReferenceCallback) common.OpenAPIDefinition { return common.OpenAPIDefinition{ Schema: spec.Schema{ SchemaProps: spec.SchemaProps{ @@ -2121,19 +2486,17 @@ type Blah struct { }, }, } - } - -`)) - if ree != nil { - t.Fatal(ree) + }`)) + if ree != nil { + t.Fatal(ree) + } + assertEqual(t, string(formatted), string(formatted_expected)) } - assert.Equal(string(formatted), string(formatted_expected)) - } + }) } func TestCELMarkerComments(t *testing.T) { - - callErr, funcErr, assert, _, funcBuffer, imports := testOpenAPITypeWriter(t, ` + inputFile := ` package foo // +k8s:openapi-gen=true @@ -2147,17 +2510,37 @@ func TestCELMarkerComments(t *testing.T) { // +k8s:validation:cel[1]:optionalOldSelf // +optional Field string - } - `) + }` - assert.NoError(funcErr) - assert.NoError(callErr) - assert.ElementsMatch(imports, []string{`foo "base/foo"`, `common "k8s.io/kube-openapi/pkg/common"`, `spec "k8s.io/kube-openapi/pkg/validation/spec"`}) + packagestest.TestAll(t, func(t *testing.T, x packagestest.Exporter) { + e := packagestest.Export(t, x, []packagestest.Module{{ + Name: "example.com/base/foo", + Files: map[string]interface{}{ + "foo.go": inputFile, + }, + }}) + defer e.Cleanup() + + callErr, funcErr, _, funcBuffer, imports := testOpenAPITypeWriter(t, e.Config) + if funcErr != nil { + t.Fatalf("Unexpected funcErr: %v", funcErr) + } + if callErr != nil { + t.Fatalf("Unexpected callErr: %v", callErr) + } + expImports := []string{ + `foo "example.com/base/foo"`, + `common "k8s.io/kube-openapi/pkg/common"`, + `spec "k8s.io/kube-openapi/pkg/validation/spec"`, + } + if !cmp.Equal(imports, expImports) { + t.Errorf("wrong imports:\n%s", cmp.Diff(expImports, imports)) + } - if formatted, err := format.Source(funcBuffer.Bytes()); err != nil { - t.Fatalf("%v\n%v", err, string(funcBuffer.Bytes())) - } else { - formatted_expected, ree := format.Source([]byte(`func schema_base_foo_Blah(ref common.ReferenceCallback) common.OpenAPIDefinition { + if formatted, err := format.Source(funcBuffer.Bytes()); err != nil { + t.Fatalf("%v\n%v", err, string(funcBuffer.Bytes())) + } else { + formatted_expected, ree := format.Source([]byte(`func schema_examplecom_base_foo_Blah(ref common.ReferenceCallback) common.OpenAPIDefinition { return common.OpenAPIDefinition{ Schema: spec.Schema{ SchemaProps: spec.SchemaProps{ @@ -2184,19 +2567,17 @@ func TestCELMarkerComments(t *testing.T) { }, }, } - } - -`)) - if ree != nil { - t.Fatal(ree) + }`)) + if ree != nil { + t.Fatal(ree) + } + assertEqual(t, string(formatted_expected), string(formatted)) } - assert.Equal(string(formatted_expected), string(formatted)) - } + }) } func TestMultilineCELMarkerComments(t *testing.T) { - - callErr, funcErr, assert, _, funcBuffer, imports := testOpenAPITypeWriter(t, ` + inputFile := ` package foo // +k8s:openapi-gen=true @@ -2214,17 +2595,37 @@ func TestMultilineCELMarkerComments(t *testing.T) { // +k8s:validation:cel[1]:optionalOldSelf // +optional Field string - } - `) + }` - assert.NoError(funcErr) - assert.NoError(callErr) - assert.ElementsMatch(imports, []string{`foo "base/foo"`, `common "k8s.io/kube-openapi/pkg/common"`, `spec "k8s.io/kube-openapi/pkg/validation/spec"`}) + packagestest.TestAll(t, func(t *testing.T, x packagestest.Exporter) { + e := packagestest.Export(t, x, []packagestest.Module{{ + Name: "example.com/base/foo", + Files: map[string]interface{}{ + "foo.go": inputFile, + }, + }}) + defer e.Cleanup() + + callErr, funcErr, _, funcBuffer, imports := testOpenAPITypeWriter(t, e.Config) + if funcErr != nil { + t.Fatalf("Unexpected funcErr: %v", funcErr) + } + if callErr != nil { + t.Fatalf("Unexpected callErr: %v", callErr) + } + expImports := []string{ + `foo "example.com/base/foo"`, + `common "k8s.io/kube-openapi/pkg/common"`, + `spec "k8s.io/kube-openapi/pkg/validation/spec"`, + } + if !cmp.Equal(imports, expImports) { + t.Errorf("wrong imports:\n%s", cmp.Diff(expImports, imports)) + } - if formatted, err := format.Source(funcBuffer.Bytes()); err != nil { - t.Fatalf("%v\n%v", err, string(funcBuffer.Bytes())) - } else { - formatted_expected, ree := format.Source([]byte(`func schema_base_foo_Blah(ref common.ReferenceCallback) common.OpenAPIDefinition { + if formatted, err := format.Source(funcBuffer.Bytes()); err != nil { + t.Fatalf("%v\n%v", err, string(funcBuffer.Bytes())) + } else { + formatted_expected, ree := format.Source([]byte(`func schema_examplecom_base_foo_Blah(ref common.ReferenceCallback) common.OpenAPIDefinition { return common.OpenAPIDefinition{ Schema: spec.Schema{ SchemaProps: spec.SchemaProps{ @@ -2251,18 +2652,17 @@ func TestMultilineCELMarkerComments(t *testing.T) { }, }, } - } - -`)) - if ree != nil { - t.Fatal(ree) + }`)) + if ree != nil { + t.Fatal(ree) + } + assertEqual(t, string(formatted_expected), string(formatted)) } - assert.Equal(string(formatted_expected), string(formatted)) - } + }) } func TestRequired(t *testing.T) { - callErr, funcErr, assert, _, funcBuffer, imports := testOpenAPITypeWriter(t, ` + inputFile := ` package foo // +k8s:openapi-gen=true @@ -2274,24 +2674,44 @@ func TestRequired(t *testing.T) { RequiredField string // +required - RequiredPointerField *string `+"`json:\"requiredPointerField,omitempty\"`"+` + RequiredPointerField *string ` + "`json:\"requiredPointerField,omitempty\"`" + ` // +optional - OptionalPointerField *string `+"`json:\"optionalPointerField,omitempty\"`"+` + OptionalPointerField *string ` + "`json:\"optionalPointerField,omitempty\"`" + ` ImplicitlyRequiredField string - ImplicitlyOptionalField string `+"`json:\"implicitlyOptionalField,omitempty\"`"+` - } - `) + ImplicitlyOptionalField string ` + "`json:\"implicitlyOptionalField,omitempty\"`" + ` + }` + + packagestest.TestAll(t, func(t *testing.T, x packagestest.Exporter) { + e := packagestest.Export(t, x, []packagestest.Module{{ + Name: "example.com/base/foo", + Files: map[string]interface{}{ + "foo.go": inputFile, + }, + }}) + defer e.Cleanup() - assert.NoError(funcErr) - assert.NoError(callErr) - assert.ElementsMatch(imports, []string{`foo "base/foo"`, `common "k8s.io/kube-openapi/pkg/common"`, `spec "k8s.io/kube-openapi/pkg/validation/spec"`}) + callErr, funcErr, _, funcBuffer, imports := testOpenAPITypeWriter(t, e.Config) + if funcErr != nil { + t.Fatalf("Unexpected funcErr: %v", funcErr) + } + if callErr != nil { + t.Fatalf("Unexpected callErr: %v", callErr) + } + expImports := []string{ + `foo "example.com/base/foo"`, + `common "k8s.io/kube-openapi/pkg/common"`, + `spec "k8s.io/kube-openapi/pkg/validation/spec"`, + } + if !cmp.Equal(imports, expImports) { + t.Errorf("wrong imports:\n%s", cmp.Diff(expImports, imports)) + } - if formatted, err := format.Source(funcBuffer.Bytes()); err != nil { - t.Fatalf("%v\n%v", err, string(funcBuffer.Bytes())) - } else { - formatted_expected, ree := format.Source([]byte(`func schema_base_foo_Blah(ref common.ReferenceCallback) common.OpenAPIDefinition { + if formatted, err := format.Source(funcBuffer.Bytes()); err != nil { + t.Fatalf("%v\n%v", err, string(funcBuffer.Bytes())) + } else { + formatted_expected, ree := format.Source([]byte(`func schema_examplecom_base_foo_Blah(ref common.ReferenceCallback) common.OpenAPIDefinition { return common.OpenAPIDefinition{ Schema: spec.Schema{ SchemaProps: spec.SchemaProps{ @@ -2341,64 +2761,99 @@ func TestRequired(t *testing.T) { }, }, } - } - -`)) - if ree != nil { - t.Fatal(ree) + }`)) + if ree != nil { + t.Fatal(ree) + } + assertEqual(t, string(formatted_expected), string(formatted)) } - assert.Equal(string(formatted_expected), string(formatted)) - } + }) // Show specifying both is an error - callErr, funcErr, assert, _, _, _ = testOpenAPITypeWriter(t, ` - package foo + badFile := ` + package foo - // +k8s:openapi-gen=true - type Blah struct { - // +optional - // +required - ConfusingField string - } -`) - assert.NoError(callErr) - assert.ErrorContains(funcErr, "cannot be both optional and required") + // +k8s:openapi-gen=true + type Blah struct { + // +optional + // +required + ConfusingField string + }` + packagestest.TestAll(t, func(t *testing.T, x packagestest.Exporter) { + e := packagestest.Export(t, x, []packagestest.Module{{ + Name: "example.com/base/foo", + Files: map[string]interface{}{ + "foo.go": badFile, + }, + }}) + defer e.Cleanup() + + callErr, funcErr, _, _, _ := testOpenAPITypeWriter(t, e.Config) + if callErr != nil { + t.Errorf("Unexpected callErr: %v", callErr) + } + if funcErr == nil { + t.Fatalf("Expected funcErr") + } + if !strings.Contains(funcErr.Error(), "cannot be both optional and required") { + t.Errorf("Unexpected error: %v", funcErr) + } + }) } func TestMarkerCommentsCustomDefsV3(t *testing.T) { - callErr, funcErr, assert, callBuffer, funcBuffer, _ := testOpenAPITypeWriter(t, ` -package foo + inputFile := ` + package foo -import openapi "k8s.io/kube-openapi/pkg/common" + import openapi "k8s.io/kube-openapi/pkg/common" -// +k8s:validation:maxProperties=10 -type Blah struct { -} + // +k8s:validation:maxProperties=10 + type Blah struct { + } -func (_ Blah) OpenAPIV3Definition() openapi.OpenAPIDefinition { - return openapi.OpenAPIDefinition{ - Schema: spec.Schema{ - SchemaProps: spec.SchemaProps{ - Type: []string{"object"}, - MaxProperties: ptr.To[int64](10), - Format: "ipv4", + func (_ Blah) OpenAPIV3Definition() openapi.OpenAPIDefinition { + return openapi.OpenAPIDefinition{ + Schema: spec.Schema{ + SchemaProps: spec.SchemaProps{ + Type: []string{"object"}, + MaxProperties: ptr.To[int64](10), + Format: "ipv4", + }, + }, + } + } + + func (_ Blah) OpenAPISchemaType() []string { return []string{"object"} } + func (_ Blah) OpenAPISchemaFormat() string { return "ipv4" }` + commonFile := `package common + + type OpenAPIDefinition struct {}` + + packagestest.TestAll(t, func(t *testing.T, x packagestest.Exporter) { + e := packagestest.Export(t, x, []packagestest.Module{{ + Name: "example.com/base/foo", + Files: map[string]interface{}{ + "foo.go": inputFile, }, - }, - } -} + }, { + Name: "k8s.io/kube-openapi/pkg/common", + Files: map[string]interface{}{ + "common.go": commonFile, + }, + }}) + defer e.Cleanup() -func (_ Blah) OpenAPISchemaType() []string { return []string{"object"} } -func (_ Blah) OpenAPISchemaFormat() string { return "ipv4" } -`) - if callErr != nil { - t.Fatal(callErr) - } - if funcErr != nil { - t.Fatal(funcErr) - } - assert.Equal(`"base/foo.Blah": schema_base_foo_Blah(ref), -`, callBuffer.String()) - assert.Equal(`func schema_base_foo_Blah(ref common.ReferenceCallback) common.OpenAPIDefinition { + callErr, funcErr, callBuffer, funcBuffer, _ := testOpenAPITypeWriter(t, e.Config) + if callErr != nil { + t.Fatal(callErr) + } + if funcErr != nil { + t.Fatal(funcErr) + } + assertEqual(t, callBuffer.String(), + `"example.com/base/foo.Blah": schema_examplecom_base_foo_Blah(ref),`) + assertEqual(t, funcBuffer.String(), + `func schema_examplecom_base_foo_Blah(ref common.ReferenceCallback) common.OpenAPIDefinition { return common.EmbedOpenAPIDefinitionIntoV2Extension(foo.Blah{}.OpenAPIV3Definition(), common.OpenAPIDefinition{ Schema: spec.Schema{ SchemaProps: spec.SchemaProps{ @@ -2408,33 +2863,41 @@ MaxProperties: ptr.To[int64](10), }, }, }) -} - -`, funcBuffer.String()) +}`) + }) } func TestMarkerCommentsV3OneOfTypes(t *testing.T) { - callErr, funcErr, assert, callBuffer, funcBuffer, _ := testOpenAPITypeWriter(t, ` -package foo + inputFile := ` + package foo -// +k8s:validation:maxLength=10 -type Blah struct { -} + // +k8s:validation:maxLength=10 + type Blah struct { + } -func (_ Blah) OpenAPISchemaType() []string { return []string{"string"} } -func (_ Blah) OpenAPIV3OneOfTypes() []string { return []string{"string", "array"} } -func (_ Blah) OpenAPISchemaFormat() string { return "ipv4" } + func (_ Blah) OpenAPISchemaType() []string { return []string{"string"} } + func (_ Blah) OpenAPIV3OneOfTypes() []string { return []string{"string", "array"} } + func (_ Blah) OpenAPISchemaFormat() string { return "ipv4" }` + packagestest.TestAll(t, func(t *testing.T, x packagestest.Exporter) { + e := packagestest.Export(t, x, []packagestest.Module{{ + Name: "example.com/base/foo", + Files: map[string]interface{}{ + "foo.go": inputFile, + }, + }}) + defer e.Cleanup() -`) - if callErr != nil { - t.Fatal(callErr) - } - if funcErr != nil { - t.Fatal(funcErr) - } - assert.Equal(`"base/foo.Blah": schema_base_foo_Blah(ref), -`, callBuffer.String()) - assert.Equal(`func schema_base_foo_Blah(ref common.ReferenceCallback) common.OpenAPIDefinition { + callErr, funcErr, callBuffer, funcBuffer, _ := testOpenAPITypeWriter(t, e.Config) + if callErr != nil { + t.Fatal(callErr) + } + if funcErr != nil { + t.Fatal(funcErr) + } + assertEqual(t, callBuffer.String(), + `"example.com/base/foo.Blah": schema_examplecom_base_foo_Blah(ref),`) + assertEqual(t, funcBuffer.String(), + `func schema_examplecom_base_foo_Blah(ref common.ReferenceCallback) common.OpenAPIDefinition { return common.EmbedOpenAPIDefinitionIntoV2Extension(common.OpenAPIDefinition{ Schema: spec.Schema{ SchemaProps: spec.SchemaProps{ @@ -2452,7 +2915,6 @@ MaxLength: ptr.To[int64](10), }, }, }) -} - -`, funcBuffer.String()) +}`) + }) } diff --git a/pkg/generators/rules/idl_tag.go b/pkg/generators/rules/idl_tag.go index 0abe0aa07..e4b0f7cd3 100644 --- a/pkg/generators/rules/idl_tag.go +++ b/pkg/generators/rules/idl_tag.go @@ -1,7 +1,8 @@ package rules import ( - "k8s.io/gengo/types" + "k8s.io/gengo/v2" + "k8s.io/gengo/v2/types" ) const ListTypeIDLTag = "listType" @@ -24,7 +25,7 @@ func (l *ListTypeMissing) Validate(t *types.Type) ([]string, error) { switch t.Kind { case types.Struct: for _, m := range t.Members { - hasListType := types.ExtractCommentTags("+", m.CommentLines)[ListTypeIDLTag] != nil + hasListType := gengo.ExtractCommentTags("+", m.CommentLines)[ListTypeIDLTag] != nil if m.Name == "Items" && m.Type.Kind == types.Slice && hasNamedMember(t, "ListMeta") { if hasListType { diff --git a/pkg/generators/rules/idl_tag_test.go b/pkg/generators/rules/idl_tag_test.go index d3c41e3d5..e334f3269 100644 --- a/pkg/generators/rules/idl_tag_test.go +++ b/pkg/generators/rules/idl_tag_test.go @@ -4,7 +4,7 @@ import ( "reflect" "testing" - "k8s.io/gengo/types" + "k8s.io/gengo/v2/types" ) func TestListTypeMissing(t *testing.T) { diff --git a/pkg/generators/rules/names_match.go b/pkg/generators/rules/names_match.go index 53e870c1a..af30edc5e 100644 --- a/pkg/generators/rules/names_match.go +++ b/pkg/generators/rules/names_match.go @@ -22,7 +22,7 @@ import ( "k8s.io/kube-openapi/pkg/util/sets" - "k8s.io/gengo/types" + "k8s.io/gengo/v2/types" ) var ( diff --git a/pkg/generators/rules/names_match_test.go b/pkg/generators/rules/names_match_test.go index 05c78116c..e45e6d1bb 100644 --- a/pkg/generators/rules/names_match_test.go +++ b/pkg/generators/rules/names_match_test.go @@ -20,7 +20,7 @@ import ( "reflect" "testing" - "k8s.io/gengo/types" + "k8s.io/gengo/v2/types" ) func TestNamesMatch(t *testing.T) { diff --git a/pkg/generators/rules/omitempty_match_case.go b/pkg/generators/rules/omitempty_match_case.go index dd37ad8a5..d83875964 100644 --- a/pkg/generators/rules/omitempty_match_case.go +++ b/pkg/generators/rules/omitempty_match_case.go @@ -20,7 +20,7 @@ import ( "reflect" "strings" - "k8s.io/gengo/types" + "k8s.io/gengo/v2/types" ) // OmitEmptyMatchCase implements APIRule interface. diff --git a/pkg/generators/rules/omitempty_match_case_test.go b/pkg/generators/rules/omitempty_match_case_test.go index 07276ff32..0234773a1 100644 --- a/pkg/generators/rules/omitempty_match_case_test.go +++ b/pkg/generators/rules/omitempty_match_case_test.go @@ -20,7 +20,7 @@ import ( "reflect" "testing" - "k8s.io/gengo/types" + "k8s.io/gengo/v2/types" ) func TestOmitEmptyMatchCase(t *testing.T) { diff --git a/pkg/generators/union.go b/pkg/generators/union.go index 89f564e43..bfcba1ad7 100644 --- a/pkg/generators/union.go +++ b/pkg/generators/union.go @@ -20,7 +20,8 @@ import ( "fmt" "sort" - "k8s.io/gengo/types" + "k8s.io/gengo/v2" + "k8s.io/gengo/v2/types" ) const tagUnionMember = "union" @@ -141,7 +142,7 @@ func parseEmbeddedUnion(t *types.Type) ([]union, []error) { // embedded types. func parseUnionStruct(t *types.Type) (*union, []error) { errors := []error{} - if types.ExtractCommentTags("+", t.CommentLines)[tagUnionMember] == nil { + if gengo.ExtractCommentTags("+", t.CommentLines)[tagUnionMember] == nil { return nil, nil } @@ -156,11 +157,11 @@ func parseUnionStruct(t *types.Type) (*union, []error) { 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 { + if gengo.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 { + if gengo.ExtractCommentTags("+", m.CommentLines)[tagUnionDiscriminator] != nil { errors = append(errors, u.setDiscriminator(jsonName)...) } else { if optional, err := isOptional(&m); !optional || err != nil { @@ -186,14 +187,14 @@ func parseUnionMembers(t *types.Type) (*union, []error) { if shouldInlineMembers(&m) { continue } - if types.ExtractCommentTags("+", m.CommentLines)[tagUnionDiscriminator] != nil { + if gengo.ExtractCommentTags("+", m.CommentLines)[tagUnionDiscriminator] != nil { errors = append(errors, u.setDiscriminator(jsonName)...) } - if types.ExtractCommentTags("+", m.CommentLines)[tagUnionMember] != nil { + if gengo.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 gengo.ExtractCommentTags("+", m.CommentLines)[tagUnionDeprecated] != nil { if optional, err := isOptional(&m); !optional || err != nil { errors = append(errors, fmt.Errorf("union members must be optional: %v.%v", t.Name, m.Name)) } diff --git a/test/integration/go.mod b/test/integration/go.mod index bc30f6e81..155d68db5 100644 --- a/test/integration/go.mod +++ b/test/integration/go.mod @@ -1,6 +1,6 @@ module k8s.io/kube-openapi/test/integration -go 1.21 +go 1.21.3 require ( github.com/emicklei/go-restful/v3 v3.8.0 @@ -26,15 +26,15 @@ require ( github.com/modern-go/concurrent v0.0.0-20180306012644-bacd9c7ef1dd // indirect github.com/modern-go/reflect2 v1.0.2 // indirect github.com/spf13/pflag v1.0.5 // indirect - golang.org/x/mod v0.7.0 // indirect - golang.org/x/net v0.7.0 // indirect - golang.org/x/sys v0.5.0 // indirect - golang.org/x/text v0.7.0 // indirect - golang.org/x/tools v0.4.0 // indirect + golang.org/x/mod v0.15.0 // indirect + golang.org/x/net v0.21.0 // indirect + golang.org/x/sys v0.17.0 // indirect + golang.org/x/text v0.14.0 // indirect + golang.org/x/tools v0.18.0 // indirect google.golang.org/protobuf v1.27.1 // indirect gopkg.in/yaml.v2 v2.4.0 // indirect gopkg.in/yaml.v3 v3.0.1 // indirect - k8s.io/gengo v0.0.0-20230829151522-9cce18d56c01 // indirect + k8s.io/gengo/v2 v2.0.0-20240226174109-00c4be8627da // indirect k8s.io/klog/v2 v2.80.1 // indirect ) diff --git a/test/integration/go.sum b/test/integration/go.sum index 37bc83f63..e244b0643 100644 --- a/test/integration/go.sum +++ b/test/integration/go.sum @@ -8,7 +8,6 @@ github.com/getkin/kin-openapi v0.76.0 h1:j77zg3Ec+k+r+GA3d8hBoXpAc6KX9TbBPrwQGBI github.com/getkin/kin-openapi v0.76.0/go.mod h1:660oXbgy5JFMKreazJaQTw7o+X00qeSyhcnluiMv+Xg= github.com/ghodss/yaml v1.0.0 h1:wQHKEahhL6wmXdzwWG11gIVCkOv05bNOh+Rxn0yngAk= github.com/ghodss/yaml v1.0.0/go.mod h1:4dBDuWmgqj2HViK6kFavaiC9ZROes6MMH2rRYeMEF04= -github.com/go-logr/logr v0.2.0/go.mod h1:z6/tIYblkpsD+a4lm/fGIIU9mZ+XfAiaFtq7xTgseGU= github.com/go-logr/logr v1.2.0 h1:QK40JKJyMdUDz+h+xvCsru/bJhvG0UxvePV0ufL/AcE= github.com/go-logr/logr v1.2.0/go.mod h1:jdQByPbusPIv2/zmleS9BjJVeZ6kBagPoEUsqbVz/1A= github.com/go-openapi/jsonpointer v0.19.5/go.mod h1:Pl9vOtqEWErmShwVjC8pYs9cog34VGT37dQOVbmoatg= @@ -24,7 +23,6 @@ github.com/golang/protobuf v1.5.2 h1:ROPKBNFfQgOUMifHyP+KYbvpjbdoFNs+aK7DXlji0Tw github.com/golang/protobuf v1.5.2/go.mod h1:XVQd3VNwM+JqD3oG2Ue2ip4fOMUkwXdXDdiuN0vRsmY= github.com/google/gnostic-models v0.6.8 h1:yo/ABAfM5IMRsS1VnXjTBvUb61tFIHozhlYvRgGre9I= github.com/google/gnostic-models v0.6.8/go.mod h1:5n7qKqH0f5wFt+aWF8CW6pZLLNOfYuF5OpfBSENuI8U= -github.com/google/go-cmp v0.4.0/go.mod h1:v8dTdLbMG2kIc/vJvl+f65V22dbkXbowE6jgT/gNBxE= github.com/google/go-cmp v0.5.5 h1:Khx7svrCpmxxtHBq5j2mp/xVjsi8hQMfNLvJFAlrGgU= github.com/google/go-cmp v0.5.5/go.mod h1:v8dTdLbMG2kIc/vJvl+f65V22dbkXbowE6jgT/gNBxE= github.com/google/gofuzz v1.0.0/go.mod h1:dBl0BpW6vV/+mYPU4Po3pmUjxk6FQPldtuIdl/M65Eg= @@ -36,7 +34,6 @@ github.com/josharian/intern v1.0.0/go.mod h1:5DoeVV0s6jJacbCEi61lwdGj/aVlrQvzHFF github.com/json-iterator/go v1.1.12 h1:PV8peI4a0ysnczrg+LtxykD8LfKY9ML6u2jnxaEnrnM= github.com/json-iterator/go v1.1.12/go.mod h1:e30LSqwooZae/UwlEbR2852Gd8hjQvJoHmT4TnhNGBo= github.com/kr/pretty v0.1.0/go.mod h1:dAy3ld7l9f0ibDNOQOHHMYYIIbhfbHSm3C4ZsoJORNo= -github.com/kr/pretty v0.2.0/go.mod h1:ipq/a2n7PKx3OHsz4KJII5eveXtPO4qwEXGdVfWzfnI= github.com/kr/pretty v0.2.1 h1:Fmg33tUaq4/8ym9TJN1x7sLJnHVwhP33CNkpYV/7rwI= github.com/kr/pretty v0.2.1/go.mod h1:ipq/a2n7PKx3OHsz4KJII5eveXtPO4qwEXGdVfWzfnI= github.com/kr/pty v1.1.1/go.mod h1:pFQYn66WHrOpPYNljwOMqo10TkYh1fy3cYio2l3bCsQ= @@ -69,32 +66,18 @@ github.com/stretchr/testify v1.7.1/go.mod h1:6Fq8oRcR53rry900zMqJjRRixrwX3KX962/ github.com/stretchr/testify v1.8.0/go.mod h1:yNjHg4UonilssWZ8iaSj1OCr/vHnekPRkoO+kdMU+MU= github.com/stretchr/testify v1.8.1 h1:w7B6lhMri9wdJUVmEZPGGhZzrYTPvgJArz7wNPgYKsk= github.com/stretchr/testify v1.8.1/go.mod h1:w2LPCIKwWwSfY2zedu0+kehJoqGctiVI29o6fzry7u4= -github.com/yuin/goldmark v1.1.27/go.mod h1:3hX8gzYuyVAZsxl0MRgGTJEmQBFcNTphYh9decYSb74= -golang.org/x/crypto v0.0.0-20190308221718-c2843e01d9a2/go.mod h1:djNgcEr1/C05ACkg1iLfiJU5Ep61QUkGW8qpdssI0+w= -golang.org/x/crypto v0.0.0-20191011191535-87dc89f01550/go.mod h1:yigFU9vqHzYiE8UmvKecakEJjdnWj3jj499lnFckfCI= -golang.org/x/mod v0.2.0/go.mod h1:s0Qsj1ACt9ePp/hMypM3fl4fZqREWJwdYDEqhRiZZUA= -golang.org/x/mod v0.7.0 h1:LapD9S96VoQRhi/GrNTqeBJFrUjs5UHCAtTlgwA5oZA= -golang.org/x/mod v0.7.0/go.mod h1:iBbtSCu2XBx23ZKBPSOrRkjjQPZFPuis4dIYUhu/chs= -golang.org/x/net v0.0.0-20190404232315-eb5bcb51f2a3/go.mod h1:t9HGtf8HONx5eT2rtn7q6eTqICYqUVnKs3thJo3Qplg= -golang.org/x/net v0.0.0-20190620200207-3b0461eec859/go.mod h1:z5CRVTTTmAJ677TzLLGU+0bjPO0LkuOLi4/5GtJWs/s= -golang.org/x/net v0.0.0-20200226121028-0de0cce0169b/go.mod h1:z5CRVTTTmAJ677TzLLGU+0bjPO0LkuOLi4/5GtJWs/s= -golang.org/x/net v0.7.0 h1:rJrUqqhjsgNp7KqAIc25s9pZnjU7TUcSY7HcVZjdn1g= -golang.org/x/net v0.7.0/go.mod h1:2Tu9+aMcznHK/AK1HMvgo6xiTLG5rD5rZLDS+rp2Bjs= -golang.org/x/sync v0.0.0-20190423024810-112230192c58/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM= -golang.org/x/sync v0.0.0-20190911185100-cd5d95a43a6e/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM= -golang.org/x/sys v0.0.0-20190215142949-d0b11bdaac8a/go.mod h1:STP8DvDyc/dI5b8T5hshtkjS+E42TnysNCUPdjciGhY= -golang.org/x/sys v0.0.0-20190412213103-97732733099d/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= -golang.org/x/sys v0.5.0 h1:MUK/U/4lj1t1oPg0HfuXDN/Z1wv31ZJ/YcPiGccS4DU= -golang.org/x/sys v0.5.0/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= -golang.org/x/text v0.3.0/go.mod h1:NqM8EUOU14njkJ3fqMW+pc6Ldnwhi/IjpwHt7yyuwOQ= -golang.org/x/text v0.7.0 h1:4BRB4x83lYWy72KwLD/qYDuTu7q9PjSagHvijDw7cLo= -golang.org/x/text v0.7.0/go.mod h1:mrYo+phRRbMaCq/xk9113O4dZlRixOauAjOtrjsXDZ8= -golang.org/x/tools v0.0.0-20191119224855-298f0cb1881e/go.mod h1:b+2E5dAYhXwXZwtnZ6UAqBI28+e2cm9otk0dWdXHAEo= -golang.org/x/tools v0.0.0-20200505023115-26f46d2f7ef8/go.mod h1:EkVYQZoAsY45+roYkvgYkIh4xh/qjgUK9TdY2XT94GE= -golang.org/x/tools v0.4.0 h1:7mTAgkunk3fr4GAloyyCasadO6h9zSsQZbwvcaIciV4= -golang.org/x/tools v0.4.0/go.mod h1:UE5sM2OK9E/d67R0ANs2xJizIymRP5gJU295PvKXxjQ= -golang.org/x/xerrors v0.0.0-20190717185122-a985d3407aa7/go.mod h1:I/5z698sn9Ka8TeJc9MKroUUfqBBauWjQqLJ2OPfmY0= -golang.org/x/xerrors v0.0.0-20191011141410-1b5146add898/go.mod h1:I/5z698sn9Ka8TeJc9MKroUUfqBBauWjQqLJ2OPfmY0= +golang.org/x/mod v0.15.0 h1:SernR4v+D55NyBH2QiEQrlBAnj1ECL6AGrA5+dPaMY8= +golang.org/x/mod v0.15.0/go.mod h1:hTbmBsO62+eylJbnUtE2MGJUyE7QWk4xUqPFrRgJ+7c= +golang.org/x/net v0.21.0 h1:AQyQV4dYCvJ7vGmJyKki9+PBdyvhkSd8EIx/qb0AYv4= +golang.org/x/net v0.21.0/go.mod h1:bIjVDfnllIU7BJ2DNgfnXvpSvtn8VRwhlsaeUTyUS44= +golang.org/x/sync v0.6.0 h1:5BMeUDZ7vkXGfEr1x9B4bRcTH4lpkTkpdh0T/J+qjbQ= +golang.org/x/sync v0.6.0/go.mod h1:Czt+wKu1gCyEFDUtn0jG5QVvpJ6rzVqr5aXyt9drQfk= +golang.org/x/sys v0.17.0 h1:25cE3gD+tdBA7lp7QfhuV+rJiE9YXTcS3VG1SqssI/Y= +golang.org/x/sys v0.17.0/go.mod h1:/VUhepiaJMQUp4+oa/7Zr1D23ma6VTLIYjOOTFZPUcA= +golang.org/x/text v0.14.0 h1:ScX5w1eTa3QqT8oi6+ziP7dTV1S2+ALU0bI+0zXKWiQ= +golang.org/x/text v0.14.0/go.mod h1:18ZOQIKpY8NJVqYksKHtTdi31H5itFRjB5/qKTNYzSU= +golang.org/x/tools v0.18.0 h1:k8NLag8AGHnn+PHbl7g43CtqZAwG60vZkLqgyZgIHgQ= +golang.org/x/tools v0.18.0/go.mod h1:GL7B4CwcLLeo59yx/9UWWuNOW1n3VZ4f5axWfML7Lcg= golang.org/x/xerrors v0.0.0-20191204190536-9bdfabe68543/go.mod h1:I/5z698sn9Ka8TeJc9MKroUUfqBBauWjQqLJ2OPfmY0= golang.org/x/xerrors v0.0.0-20220907171357-04be3eba64a2 h1:H2TDz8ibqkAF6YGhCdN3jS9O0/s90v0rJh3X/OLHEUk= golang.org/x/xerrors v0.0.0-20220907171357-04be3eba64a2/go.mod h1:K8+ghG5WaK9qNqU5K3HdILfMLy1f3aNYFI/wnl100a8= @@ -104,24 +87,20 @@ google.golang.org/protobuf v1.27.1 h1:SnqbnDw1V7RiZcXPx5MEeqPv2s79L9i7BJUlG/+Rur google.golang.org/protobuf v1.27.1/go.mod h1:9q0QmTI4eRPtz6boOQmLYwt+qCgq0jsYwAQnmE0givc= gopkg.in/check.v1 v0.0.0-20161208181325-20d25e280405/go.mod h1:Co6ibVJAznAaIkqp8huTwlJQCZ016jof/cbN4VW5Yz0= gopkg.in/check.v1 v1.0.0-20180628173108-788fd7840127/go.mod h1:Co6ibVJAznAaIkqp8huTwlJQCZ016jof/cbN4VW5Yz0= -gopkg.in/check.v1 v1.0.0-20190902080502-41f04d3bba15/go.mod h1:Co6ibVJAznAaIkqp8huTwlJQCZ016jof/cbN4VW5Yz0= gopkg.in/check.v1 v1.0.0-20201130134442-10cb98267c6c h1:Hei/4ADfdWqJk1ZMxUNpqntNwaWcugrBjAiHlqqRiVk= gopkg.in/check.v1 v1.0.0-20201130134442-10cb98267c6c/go.mod h1:JHkPIbrfpd72SG/EVd6muEfDQjcINNoR0C8j2r3qZ4Q= gopkg.in/yaml.v2 v2.2.2/go.mod h1:hI93XBmqTisBFMUTm0b8Fm+jr3Dg1NNxqwp+5A1VGuI= -gopkg.in/yaml.v2 v2.2.8/go.mod h1:hI93XBmqTisBFMUTm0b8Fm+jr3Dg1NNxqwp+5A1VGuI= gopkg.in/yaml.v2 v2.3.0/go.mod h1:hI93XBmqTisBFMUTm0b8Fm+jr3Dg1NNxqwp+5A1VGuI= gopkg.in/yaml.v2 v2.4.0 h1:D8xgwECY7CYvx+Y2n4sBz93Jn9JRvxdiyyo8CTfuKaY= gopkg.in/yaml.v2 v2.4.0/go.mod h1:RDklbk79AGWmwhnvt/jBztapEOGDOx6ZbXqjP6csGnQ= gopkg.in/yaml.v3 v3.0.0-20200313102051-9f266ea9e77c/go.mod h1:K4uyk7z7BCEPqu6E+C64Yfv1cQ7kz7rIZviUmN+EgEM= gopkg.in/yaml.v3 v3.0.1 h1:fxVm/GzAzEWqLHuvctI91KS9hhNmmWOoWu0XTYJS7CA= gopkg.in/yaml.v3 v3.0.1/go.mod h1:K4uyk7z7BCEPqu6E+C64Yfv1cQ7kz7rIZviUmN+EgEM= -k8s.io/gengo v0.0.0-20230829151522-9cce18d56c01 h1:pWEwq4Asjm4vjW7vcsmijwBhOr1/shsbSYiWXmNGlks= -k8s.io/gengo v0.0.0-20230829151522-9cce18d56c01/go.mod h1:FiNAH4ZV3gBg2Kwh89tzAEV2be7d5xI0vBa/VySYy3E= -k8s.io/klog/v2 v2.2.0/go.mod h1:Od+F08eJP+W3HUb4pSrPpgp9DGU4GzlpG/TmITuYh/Y= +k8s.io/gengo/v2 v2.0.0-20240226174109-00c4be8627da h1:TthpcphFcSU/2R2/j67duxXOSRdUGP2ZOAG4TyGgz5c= +k8s.io/gengo/v2 v2.0.0-20240226174109-00c4be8627da/go.mod h1:5177teI4XU3cmd0ZSDKF3jxMubfjilA5FNOegKTCgK4= k8s.io/klog/v2 v2.80.1 h1:atnLQ121W371wYYFawwYx1aEY2eUfs4l3J72wtgAwV4= k8s.io/klog/v2 v2.80.1/go.mod h1:y1WjHnz7Dj687irZUWR/WLkLc5N1YHtjLdmgWjndZn0= k8s.io/utils v0.0.0-20230726121419-3b25d923346b h1:sgn3ZU783SCgtaSJjpcVVlRqd6GSnlTLKgpAAttJvpI= k8s.io/utils v0.0.0-20230726121419-3b25d923346b/go.mod h1:OLgZIPagt7ERELqWJFomSt595RzquPNLL48iOWgYOg0= sigs.k8s.io/json v0.0.0-20221116044647-bc3834ca7abd h1:EDPBXCAspyGV4jQlpZSudPeMmr1bNJefnuqLsRAsHZo= sigs.k8s.io/json v0.0.0-20221116044647-bc3834ca7abd/go.mod h1:B8JuhiUyNFVKdsE8h686QcCxMaH6HrOAZj4vswFpcB0= -sigs.k8s.io/yaml v1.2.0/go.mod h1:yfXDCHCao9+ENCvLSE62v9VSji2MKu5jeNfTrofGhJc= diff --git a/test/integration/integration_suite_test.go b/test/integration/integration_suite_test.go index 35e895820..a49b0765a 100644 --- a/test/integration/integration_suite_test.go +++ b/test/integration/integration_suite_test.go @@ -28,24 +28,15 @@ import ( ) const ( - headerFilePath = "../../boilerplate/boilerplate.go.txt" - testdataDir = "./testdata" - testPkgDir = "k8s.io/kube-openapi/test/integration/testdata" - inputDir = testPkgDir + "/listtype" + - "," + testPkgDir + "/maptype" + - "," + testPkgDir + "/structtype" + - "," + testPkgDir + "/dummytype" + - "," + testPkgDir + "/uniontype" + - "," + testPkgDir + "/enumtype" + - "," + testPkgDir + "/custom" + - "," + testPkgDir + "/valuevalidation" + - "," + testPkgDir + "/defaults" - outputBase = "pkg" - outputPackage = "generated" - outputBaseFileName = "openapi_generated" + headerFilePath = "../../boilerplate/boilerplate.go.txt" + testdataDir = "./testdata" + testPkgRoot = "k8s.io/kube-openapi/test/integration/testdata" + outputPkg = testPkgRoot + "/pkg/generated" + generatedCodeFileName = "openapi_generated.go" + goldenCodeFilePath = "pkg/generated/" + generatedCodeFileName generatedSwaggerFileName = "generated.v2.json" - generatedReportFileName = "generated.v2.report" goldenSwaggerFileName = "golden.v2.json" + generatedReportFileName = "generated.v2.report" goldenReportFileName = "golden.v2.report" generatedOpenAPIv3FileName = "generated.v3.json" goldenOpenAPIv3Filename = "golden.v3.json" @@ -58,6 +49,18 @@ var ( tempDir string terr error openAPIGenPath string + + inputDirs = []string{ + filepath.Join(testPkgRoot, "listtype"), + filepath.Join(testPkgRoot, "maptype"), + filepath.Join(testPkgRoot, "structtype"), + filepath.Join(testPkgRoot, "dummytype"), + filepath.Join(testPkgRoot, "uniontype"), + filepath.Join(testPkgRoot, "enumtype"), + filepath.Join(testPkgRoot, "custom"), + filepath.Join(testPkgRoot, "valuevalidation"), + filepath.Join(testPkgRoot, "defaults"), + } ) func generatedFile(filename string) string { return filepath.Join(tempDir, filename) } @@ -84,18 +87,16 @@ var _ = BeforeSuite(func() { Expect(berr).ShouldNot(HaveOccurred()) openAPIGenPath = binaryPath - // Run the OpenAPI code generator, creating OpenAPIDefinition code - // to be compiled into builder. - By("processing go idl with openapi-gen") - gr := generatedFile(generatedReportFileName) - command := exec.Command(openAPIGenPath, - "-i", inputDir, - "-o", outputBase, - "-p", outputPackage, - "-O", outputBaseFileName, - "-r", gr, - "-h", headerFilePath, - ) + // Run the OpenAPI code generator. + By("running openapi-gen") + args := append([]string{ + "--output-dir", tempDir, + "--output-pkg", outputPkg, + "--output-file", generatedCodeFileName, + "--report-filename", generatedFile(generatedReportFileName), + "--go-header-file", headerFilePath, + }, inputDirs...) + command := exec.Command(openAPIGenPath, args...) command.Dir = workingDirectory session, err := gexec.Start(command, GinkgoWriter, GinkgoWriter) Expect(err).ShouldNot(HaveOccurred()) @@ -136,16 +137,13 @@ var _ = AfterSuite(func() { }) var _ = Describe("Open API Definitions Generation", func() { - Describe("openapi-gen --verify", func() { - It("Verifies that the existing files are correct", func() { - command := exec.Command(openAPIGenPath, - "-i", inputDir, - "-o", outputBase, - "-p", outputPackage, - "-O", outputBaseFileName, - "-r", testdataFile(goldenReportFileName), - "-h", headerFilePath, - "--verify-only", + Describe("Validating generated code", func() { + It("Generated code should match golden files", func() { + // Diff the generated code against the golden code. Exit code should be zero. + command := exec.Command( + "diff", "-u", + goldenCodeFilePath, + generatedFile(generatedCodeFileName), ) command.Dir = workingDirectory session, err := gexec.Start(command, GinkgoWriter, GinkgoWriter) @@ -158,7 +156,7 @@ var _ = Describe("Open API Definitions Generation", func() { It("Generated OpenAPI swagger definitions should match golden files", func() { // Diff the generated swagger against the golden swagger. Exit code should be zero. command := exec.Command( - "diff", + "diff", "-u", testdataFile(goldenSwaggerFileName), generatedFile(generatedSwaggerFileName), ) @@ -173,7 +171,7 @@ var _ = Describe("Open API Definitions Generation", func() { It("Generated OpenAPI swagger definitions should match golden files", func() { // Diff the generated swagger against the golden swagger. Exit code should be zero. command := exec.Command( - "diff", + "diff", "-u", testdataFile(goldenOpenAPIv3Filename), generatedFile(generatedOpenAPIv3FileName), ) @@ -188,7 +186,7 @@ var _ = Describe("Open API Definitions Generation", func() { It("Generated API rule violations should match golden report files", func() { // Diff the generated report against the golden report. Exit code should be zero. command := exec.Command( - "diff", + "diff", "-u", testdataFile(goldenReportFileName), generatedFile(generatedReportFileName), ) diff --git a/test/integration/pkg/generated/openapi_generated.go b/test/integration/pkg/generated/openapi_generated.go index 9d6b6d84d..993ef5815 100644 --- a/test/integration/pkg/generated/openapi_generated.go +++ b/test/integration/pkg/generated/openapi_generated.go @@ -17,9 +17,7 @@ See the License for the specific language governing permissions and limitations under the License. */ -// Code generated by openapi-gen.go. DO NOT EDIT. - -// This file was autogenerated by openapi-gen. Do not edit it manually! +// Code generated by openapi-gen. DO NOT EDIT. package generated @@ -1080,16 +1078,7 @@ func schema_test_integration_testdata_valuevalidation_Foo(ref common.ReferenceCa "celField": { VendorExtensible: spec.VendorExtensible{ Extensions: spec.Extensions{ - "x-kubernetes-validations": []interface{}{ - map[string]interface{}{ - "rule": "self.length() > 0", - "message": "string message", - }, - map[string]interface{}{ - "rule": "self.length() % 2 == 0", - "messageExpression": "self + ' hello'", - }, - }, + "x-kubernetes-validations": []interface{}{map[string]interface{}{"message": "string message", "rule": "self.length() > 0"}, map[string]interface{}{"messageExpression": "self + ' hello'", "rule": "self.length() % 2 == 0"}}, }, }, SchemaProps: spec.SchemaProps{ @@ -1103,12 +1092,7 @@ func schema_test_integration_testdata_valuevalidation_Foo(ref common.ReferenceCa }, VendorExtensible: spec.VendorExtensible{ Extensions: spec.Extensions{ - "x-kubernetes-validations": []interface{}{ - map[string]interface{}{ - "rule": "self == oldSelf", - "message": "foo", - }, - }, + "x-kubernetes-validations": []interface{}{map[string]interface{}{"message": "foo", "rule": "self == oldSelf"}}, }, }, }, @@ -1126,12 +1110,7 @@ func schema_test_integration_testdata_valuevalidation_Foo2(ref common.ReferenceC }, VendorExtensible: spec.VendorExtensible{ Extensions: spec.Extensions{ - "x-kubernetes-validations": []interface{}{ - map[string]interface{}{ - "rule": "self == oldSelf", - "message": "foo2", - }, - }, + "x-kubernetes-validations": []interface{}{map[string]interface{}{"message": "foo2", "rule": "self == oldSelf"}}, }, }, }, @@ -1149,12 +1128,7 @@ func schema_test_integration_testdata_valuevalidation_Foo3(ref common.ReferenceC }, VendorExtensible: spec.VendorExtensible{ Extensions: spec.Extensions{ - "x-kubernetes-validations": []interface{}{ - map[string]interface{}{ - "rule": "self == oldSelf", - "message": "foo3", - }, - }, + "x-kubernetes-validations": []interface{}{map[string]interface{}{"message": "foo3", "rule": "self == oldSelf"}}, }, }, }, @@ -1168,12 +1142,7 @@ func schema_test_integration_testdata_valuevalidation_Foo3(ref common.ReferenceC }, VendorExtensible: spec.VendorExtensible{ Extensions: spec.Extensions{ - "x-kubernetes-validations": []interface{}{ - map[string]interface{}{ - "rule": "self == oldSelf", - "message": "foo3", - }, - }, + "x-kubernetes-validations": []interface{}{map[string]interface{}{"message": "foo3", "rule": "self == oldSelf"}}, }, }, }, @@ -1191,12 +1160,7 @@ func schema_test_integration_testdata_valuevalidation_Foo5(ref common.ReferenceC }, VendorExtensible: spec.VendorExtensible{ Extensions: spec.Extensions{ - "x-kubernetes-validations": []interface{}{ - map[string]interface{}{ - "rule": "self == oldSelf", - "message": "foo5", - }, - }, + "x-kubernetes-validations": []interface{}{map[string]interface{}{"message": "foo5", "rule": "self == oldSelf"}}, }, }, },