123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241 |
- /*
- Copyright 2019 The Kubernetes Authors.
- Licensed under the Apache License, Version 2.0 (the "License");
- you may not use this file except in compliance with the License.
- You may obtain a copy of the License at
- http://www.apache.org/licenses/LICENSE-2.0
- Unless required by applicable law or agreed to in writing, software
- distributed under the License is distributed on an "AS IS" BASIS,
- WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
- See the License for the specific language governing permissions and
- limitations under the License.
- */
- package main
- import (
- "flag"
- "fmt"
- "os"
- "sort"
- "strings"
- "github.com/go-openapi/analysis"
- "github.com/go-openapi/loads"
- "github.com/go-openapi/spec"
- "gopkg.in/yaml.v2"
- "k8s.io/kubernetes/test/conformance/behaviors"
- )
- type options struct {
- schemaPath string
- resource string
- area string
- behaviorsDir string
- }
- func parseFlags() *options {
- o := &options{}
- flag.StringVar(&o.schemaPath, "schema", "", "Path to the OpenAPI schema")
- flag.StringVar(&o.resource, "resource", ".*", "Resource name")
- flag.StringVar(&o.area, "area", "default", "Area name to use")
- flag.StringVar(&o.behaviorsDir, "dir", "../behaviors/", "Path to the behaviors directory")
- flag.Parse()
- return o
- }
- var defMap map[string]analysis.SchemaRef
- func main() {
- defMap = make(map[string]analysis.SchemaRef)
- o := parseFlags()
- d, err := loads.JSONSpec(o.schemaPath)
- if err != nil {
- fmt.Printf("ERROR: %s\n", err.Error())
- os.Exit(1)
- }
- defs := d.Analyzer.AllDefinitions()
- sort.Slice(defs, func(i, j int) bool { return defs[i].Name < defs[j].Name })
- for _, d := range defs {
- if !d.TopLevel {
- continue
- }
- defMap[d.Ref.String()] = d
- }
- var suites []behaviors.Suite
- var suiteMapping = make(map[string]*behaviors.Suite)
- for _, v := range defs {
- if !v.TopLevel || o.resource != v.Name {
- continue
- }
- name := trimObjectName(v.Name)
- defaultsuite := behaviors.Suite{
- Suite: o.area + "/spec",
- Description: "Base suite for " + o.area,
- Behaviors: []behaviors.Behavior{},
- }
- _ = defaultsuite
- for p, propSchema := range v.Schema.Properties {
- id := o.area + p + "/"
- if propSchema.Ref.String() != "" || propSchema.Type[0] == "array" {
- if _, ok := suiteMapping[id]; !ok {
- newsuite := behaviors.Suite{
- Suite: o.area + "/" + p,
- Description: "Suite for " + o.area + "/" + p,
- Behaviors: []behaviors.Behavior{},
- }
- suiteMapping[id] = &newsuite
- }
- behaviors := suiteMapping[id].Behaviors
- behaviors = append(behaviors, schemaBehavior(o.area, name, p, propSchema)...)
- suiteMapping[id].Behaviors = behaviors
- } else {
- if _, ok := suiteMapping["default"]; !ok {
- newsuite := behaviors.Suite{
- Suite: o.area + "/spec",
- Description: "Base suite for " + o.area,
- Behaviors: []behaviors.Behavior{},
- }
- suiteMapping["default"] = &newsuite
- }
- behaviors := suiteMapping["default"].Behaviors
- behaviors = append(behaviors, schemaBehavior(o.area, name, p, propSchema)...)
- suiteMapping["default"].Behaviors = behaviors
- }
- }
- for _, v := range suiteMapping {
- suites = append(suites, *v)
- }
- break
- }
- var area behaviors.Area = behaviors.Area{Area: o.area, Suites: suites}
- countFields(suites)
- printYAML(o.behaviorsDir+o.area, area)
- }
- func printYAML(fileName string, areaO behaviors.Area) {
- f, err := os.Create(fileName + ".yaml")
- if err != nil {
- fmt.Printf("ERROR: %s\n", err.Error())
- os.Exit(1)
- }
- defer f.Close()
- y, err := yaml.Marshal(areaO)
- if err != nil {
- fmt.Printf("ERROR: %s\n", err.Error())
- os.Exit(1)
- }
- _, err = f.WriteString(string(y))
- if err != nil {
- fmt.Printf("ERROR: %s\n", err.Error())
- os.Exit(1)
- }
- }
- func countFields(suites []behaviors.Suite) {
- var fieldsMapping map[string]int
- fieldsMapping = make(map[string]int)
- for _, suite := range suites {
- for _, behavior := range suite.Behaviors {
- if _, exists := fieldsMapping[behavior.APIType]; exists {
- fieldsMapping[behavior.APIType]++
- } else {
- fieldsMapping[behavior.APIType] = 1
- }
- }
- }
- for k, v := range fieldsMapping {
- fmt.Printf("Type %v, Count %v\n", k, v)
- }
- }
- func trimObjectName(name string) string {
- if strings.Index(name, "#/definitions/") == 0 {
- name = name[len("#/definitions/"):]
- }
- if strings.Index(name, "io.k8s.api.") == 0 {
- return name[len("io.k8s.api."):]
- }
- return name
- }
- func objectBehaviors(id string, s *spec.Schema) []behaviors.Behavior {
- if strings.Contains(id, "openAPIV3Schema") || strings.Contains(id, "JSONSchema") || strings.Contains(s.Ref.String(), "JSONSchema") {
- return []behaviors.Behavior{}
- }
- ref, ok := defMap[s.Ref.String()]
- if !ok {
- return []behaviors.Behavior{}
- }
- return schemaBehaviors(id, trimObjectName(ref.Name), ref.Schema)
- }
- func schemaBehaviors(base, apiObject string, s *spec.Schema) []behaviors.Behavior {
- var behaviors []behaviors.Behavior
- for p, propSchema := range s.Properties {
- b := schemaBehavior(base, apiObject, p, propSchema)
- behaviors = append(behaviors, b...)
- }
- return behaviors
- }
- func schemaBehavior(base, apiObject, p string, propSchema spec.Schema) []behaviors.Behavior {
- id := strings.Join([]string{base, p}, "/")
- if propSchema.Ref.String() != "" {
- if apiObject == trimObjectName(propSchema.Ref.String()) {
- return []behaviors.Behavior{}
- }
- return objectBehaviors(id, &propSchema)
- }
- var b []behaviors.Behavior
- switch propSchema.Type[0] {
- case "array":
- b = objectBehaviors(id, propSchema.Items.Schema)
- case "boolean":
- b = []behaviors.Behavior{
- {
- ID: id,
- APIObject: apiObject,
- APIField: p,
- APIType: propSchema.Type[0],
- Description: "Boolean set to true. " + propSchema.Description,
- },
- {
- ID: id,
- APIObject: apiObject,
- APIField: p,
- APIType: propSchema.Type[0],
- Description: "Boolean set to false. " + propSchema.Description,
- },
- }
- default:
- b = []behaviors.Behavior{{
- ID: id,
- APIObject: apiObject,
- APIField: p,
- APIType: propSchema.Type[0],
- Description: propSchema.Description,
- }}
- }
- return b
- }
|