mirror of
https://github.com/k3s-io/kubernetes.git
synced 2025-07-21 02:41:25 +00:00
Merge pull request #95016 from Jefftree/remove-kubeconform
Remove kubeconform
This commit is contained in:
commit
7a5b058f27
1
go.mod
1
go.mod
@ -44,7 +44,6 @@ require (
|
||||
github.com/evanphx/json-patch v4.9.0+incompatible
|
||||
github.com/fsnotify/fsnotify v1.4.9
|
||||
github.com/go-bindata/go-bindata v3.1.1+incompatible
|
||||
github.com/go-openapi/analysis v0.19.5
|
||||
github.com/go-openapi/loads v0.19.4
|
||||
github.com/go-openapi/spec v0.19.3
|
||||
github.com/go-openapi/strfmt v0.19.3
|
||||
|
@ -32,7 +32,6 @@ filegroup(
|
||||
srcs = [
|
||||
":package-srcs",
|
||||
"//test/conformance/behaviors:all-srcs",
|
||||
"//test/conformance/kubeconform:all-srcs",
|
||||
"//test/conformance/testdata:all-srcs",
|
||||
],
|
||||
tags = ["automanaged"],
|
||||
|
1
test/conformance/kubeconform/.gitignore
vendored
1
test/conformance/kubeconform/.gitignore
vendored
@ -1 +0,0 @@
|
||||
kubetestgen
|
@ -1,40 +0,0 @@
|
||||
load("@io_bazel_rules_go//go:def.bzl", "go_binary", "go_library")
|
||||
|
||||
go_library(
|
||||
name = "go_default_library",
|
||||
srcs = [
|
||||
"gen.go",
|
||||
"kubeconform.go",
|
||||
"link.go",
|
||||
],
|
||||
importpath = "k8s.io/kubernetes/test/conformance/kubeconform",
|
||||
visibility = ["//visibility:private"],
|
||||
deps = [
|
||||
"//test/conformance/behaviors:go_default_library",
|
||||
"//vendor/github.com/go-openapi/analysis:go_default_library",
|
||||
"//vendor/github.com/go-openapi/loads:go_default_library",
|
||||
"//vendor/github.com/go-openapi/spec:go_default_library",
|
||||
"//vendor/gopkg.in/yaml.v2:go_default_library",
|
||||
],
|
||||
)
|
||||
|
||||
filegroup(
|
||||
name = "package-srcs",
|
||||
srcs = glob(["**"]),
|
||||
tags = ["automanaged"],
|
||||
visibility = ["//visibility:private"],
|
||||
)
|
||||
|
||||
filegroup(
|
||||
name = "all-srcs",
|
||||
srcs = [":package-srcs"],
|
||||
tags = ["automanaged"],
|
||||
visibility = ["//visibility:public"],
|
||||
)
|
||||
|
||||
go_binary(
|
||||
name = "kubeconform",
|
||||
data = ["//api/openapi-spec"],
|
||||
embed = [":go_default_library"],
|
||||
visibility = ["//visibility:public"],
|
||||
)
|
@ -1,32 +0,0 @@
|
||||
# kubeconform
|
||||
|
||||
`kubeconform` is used to manage the creation and coverage analysis of conformance behaviors and tests. Currently it performs two functions:
|
||||
|
||||
* `gen`. This command generates a list of behaviors for a resource based on the OpenAPI schema. The purpose is to bootstrap a list of behaviors, and not to produce the final list of behaviors. We expect that the resulting files will be curated to identify a meaningful set of behaviors for the conformance requirements of the targeted resource. This may include addition, modification, and removal of behaviors from the generated list.
|
||||
* `link`. This command prints the defined behaviors not covered by any test.
|
||||
|
||||
## gen
|
||||
**Example usage for PodSpec:**
|
||||
|
||||
From the root directory of the k/k repo, will produce `pod.yaml` in
|
||||
`test/conformance/behaviors`. The `pwd` is needed because of how bazel handles
|
||||
working directories with `run`.
|
||||
|
||||
```
|
||||
$ bazel run //test/conformance/kubeconform:kubeconform -- --resource io.k8s.api.core.v1.PodSpec --area pod --schema api/openapi-spec/swagger.json --dir `pwd`/test/conformance/behaviors/ gen
|
||||
```
|
||||
|
||||
**Flags:**
|
||||
|
||||
- `schema` - a URL or local file name pointing to the JSON OpenAPI schema
|
||||
- `resource` - the specific OpenAPI definition for which to generate behaviors
|
||||
- `area` - the name to use for the area
|
||||
- `dir` - the path to the behaviors directory (default current directory)
|
||||
|
||||
**Note**: The tool automatically generates suites based on the object type for a field. All primitive data types are grouped into a default suite, while object data types are grouped into their own suite, one per object.
|
||||
|
||||
## link
|
||||
|
||||
```
|
||||
$ bazel run //test/conformance/kubeconform:kubeconform -- -dir `pwd`/test/conformance/behaviors/sig-node -testdata `pwd`/test/conformance/testdata/conformance.yaml link
|
||||
```
|
@ -1,218 +0,0 @@
|
||||
/*
|
||||
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 (
|
||||
"fmt"
|
||||
"os"
|
||||
"path/filepath"
|
||||
"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"
|
||||
)
|
||||
|
||||
var defMap map[string]analysis.SchemaRef
|
||||
|
||||
func gen(o *options) error {
|
||||
defMap = make(map[string]analysis.SchemaRef)
|
||||
d, err := loads.JSONSpec(o.schemaPath)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
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)
|
||||
return printYAML(filepath.Join(o.behaviorsDir, o.area), area)
|
||||
}
|
||||
|
||||
func printYAML(fileName string, areaO behaviors.Area) error {
|
||||
f, err := os.Create(fileName + ".yaml")
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
defer f.Close()
|
||||
y, err := yaml.Marshal(areaO)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
_, err = f.WriteString(string(y))
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
return nil
|
||||
}
|
||||
|
||||
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
|
||||
}
|
@ -1,119 +0,0 @@
|
||||
/*
|
||||
Copyright 2020 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"
|
||||
)
|
||||
|
||||
// homegrown command structures now but if this grows we may
|
||||
// want to adopt whatever kubectl uses
|
||||
type options struct {
|
||||
// Flags only used for generating behaviors
|
||||
schemaPath string
|
||||
resource string
|
||||
area string
|
||||
|
||||
// Flags only used for linking behaviors
|
||||
testdata string
|
||||
listAll bool
|
||||
|
||||
// Flags shared between CLI tools
|
||||
behaviorsDir string
|
||||
}
|
||||
|
||||
type actionFunc func(*options) error
|
||||
|
||||
func parseFlags() (actionFunc, *options) {
|
||||
o := &options{}
|
||||
|
||||
f := flag.NewFlagSet(os.Args[0], flag.ExitOnError)
|
||||
f.StringVar(&o.schemaPath, "schema", "", "Path to the OpenAPI schema")
|
||||
f.StringVar(&o.resource, "resource", "", "Resource name")
|
||||
f.StringVar(&o.area, "area", "", "Area name to use")
|
||||
|
||||
f.StringVar(&o.testdata, "testdata", "test/conformance/testdata/conformance.yaml", "YAML file containing test linkage data")
|
||||
f.BoolVar(&o.listAll, "all", false, "List all behaviors, not just those missing tests")
|
||||
|
||||
f.StringVar(&o.behaviorsDir, "dir", "test/conformance/behaviors/", "Path to the behaviors directory")
|
||||
|
||||
f.Usage = func() {
|
||||
fmt.Fprintf(os.Stderr,
|
||||
"USAGE\n-----\n%s [ options ] { link | gen }\n",
|
||||
os.Args[0])
|
||||
fmt.Fprintf(os.Stderr, "\nOPTIONS\n-------\n")
|
||||
flag.PrintDefaults()
|
||||
fmt.Fprintf(os.Stderr, "\nACTIONS\n------------")
|
||||
fmt.Fprintf(os.Stderr, `
|
||||
'link' lists behaviors associated with tests
|
||||
'gen' generates behaviors based on the API schema
|
||||
`)
|
||||
}
|
||||
|
||||
flag.CommandLine = f
|
||||
flag.Parse()
|
||||
if len(flag.Args()) != 1 {
|
||||
flag.CommandLine.Usage()
|
||||
os.Exit(2)
|
||||
}
|
||||
|
||||
var action actionFunc
|
||||
switch flag.Args()[0] {
|
||||
case "gen":
|
||||
action = gen
|
||||
if o.schemaPath == "" {
|
||||
action = nil
|
||||
fmt.Fprintf(os.Stderr, "-schema is required for 'gen'\n")
|
||||
}
|
||||
if o.resource == "" {
|
||||
action = nil
|
||||
fmt.Fprintf(os.Stderr, "-resource is required for 'gen'\n")
|
||||
}
|
||||
if o.area == "" {
|
||||
action = nil
|
||||
fmt.Fprintf(os.Stderr, "-area is required for 'gen'\n")
|
||||
}
|
||||
case "link":
|
||||
action = link
|
||||
if o.testdata == "" {
|
||||
action = nil
|
||||
fmt.Fprintf(os.Stderr, "-testdata is required for 'link'\n")
|
||||
}
|
||||
}
|
||||
|
||||
if o.behaviorsDir == "" {
|
||||
action = nil
|
||||
fmt.Fprintf(os.Stderr, "-dir is required\n")
|
||||
}
|
||||
|
||||
if action == nil {
|
||||
flag.CommandLine.Usage()
|
||||
os.Exit(2)
|
||||
}
|
||||
return action, o
|
||||
}
|
||||
|
||||
func main() {
|
||||
action, o := parseFlags()
|
||||
err := action(o)
|
||||
if err != nil {
|
||||
fmt.Printf("Error: %s\n", err)
|
||||
os.Exit(1)
|
||||
}
|
||||
}
|
@ -1,92 +0,0 @@
|
||||
/*
|
||||
Copyright 2020 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 (
|
||||
"fmt"
|
||||
"io/ioutil"
|
||||
|
||||
"gopkg.in/yaml.v2"
|
||||
|
||||
"k8s.io/kubernetes/test/conformance/behaviors"
|
||||
)
|
||||
|
||||
func link(o *options) error {
|
||||
behaviorsMapping := make(map[string][]string)
|
||||
var conformanceDataList []behaviors.ConformanceData
|
||||
|
||||
behaviorFiles, err := behaviors.BehaviorFileList(o.behaviorsDir)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
fmt.Println()
|
||||
fmt.Printf("Using behaviors from these %d files:\n", len(behaviorFiles))
|
||||
for _, f := range behaviorFiles {
|
||||
fmt.Println(" ", f)
|
||||
}
|
||||
fmt.Println()
|
||||
|
||||
if o.listAll {
|
||||
fmt.Println("All behaviors:")
|
||||
} else {
|
||||
fmt.Println("Behaviors not covered by any conformance test:")
|
||||
}
|
||||
|
||||
for _, behaviorFile := range behaviorFiles {
|
||||
suite, err := behaviors.LoadSuite(behaviorFile)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
err = behaviors.ValidateSuite(suite)
|
||||
if err != nil {
|
||||
return fmt.Errorf("error validating %s: %q", behaviorFile, err.Error())
|
||||
}
|
||||
for _, behavior := range suite.Behaviors {
|
||||
behaviorsMapping[behavior.ID] = nil
|
||||
}
|
||||
}
|
||||
|
||||
conformanceYaml, err := ioutil.ReadFile(o.testdata)
|
||||
if err != nil {
|
||||
return fmt.Errorf("%s: %v", o.testdata, err)
|
||||
}
|
||||
|
||||
err = yaml.Unmarshal(conformanceYaml, &conformanceDataList)
|
||||
if err != nil {
|
||||
return fmt.Errorf("%s: %v", o.testdata, err)
|
||||
}
|
||||
|
||||
for _, data := range conformanceDataList {
|
||||
for _, behaviorID := range data.Behaviors {
|
||||
if _, ok := behaviorsMapping[behaviorID]; !ok {
|
||||
return fmt.Errorf("cannot find behavior %q", behaviorID)
|
||||
}
|
||||
behaviorsMapping[behaviorID] = append(behaviorsMapping[behaviorID], data.CodeName)
|
||||
}
|
||||
}
|
||||
printBehaviorsMapping(behaviorsMapping, o)
|
||||
return nil
|
||||
}
|
||||
|
||||
func printBehaviorsMapping(behaviorsMapping map[string][]string, o *options) {
|
||||
for behaviorID, tests := range behaviorsMapping {
|
||||
if o.listAll || tests == nil {
|
||||
fmt.Println(" ", behaviorID)
|
||||
}
|
||||
}
|
||||
}
|
1
vendor/modules.txt
vendored
1
vendor/modules.txt
vendored
@ -408,7 +408,6 @@ github.com/go-bindata/go-bindata/go-bindata
|
||||
github.com/go-logr/logr
|
||||
# github.com/go-logr/logr => github.com/go-logr/logr v0.2.0
|
||||
# github.com/go-openapi/analysis v0.19.5 => github.com/go-openapi/analysis v0.19.5
|
||||
## explicit
|
||||
github.com/go-openapi/analysis
|
||||
# github.com/go-openapi/analysis => github.com/go-openapi/analysis v0.19.5
|
||||
github.com/go-openapi/analysis/internal
|
||||
|
Loading…
Reference in New Issue
Block a user