From d1ce36371e637f159256f1f43e3590926ff906d5 Mon Sep 17 00:00:00 2001 From: Antoine Pelisse Date: Thu, 28 Sep 2017 15:58:53 -0700 Subject: [PATCH] kubectl: Remove swagger 1.2 entirely. --- pkg/kubectl/cmd/testing/BUILD | 1 - pkg/kubectl/cmd/testing/fake.go | 9 - pkg/kubectl/cmd/util/BUILD | 4 - pkg/kubectl/cmd/util/cached_discovery.go | 6 - pkg/kubectl/cmd/util/cached_discovery_test.go | 7 - pkg/kubectl/cmd/util/factory.go | 152 -------- .../cmd/util/factory_object_mapping.go | 12 - pkg/kubectl/cmd/util/factory_test.go | 210 ----------- pkg/kubectl/resource/BUILD | 1 - pkg/kubectl/resource/categories_test.go | 6 - pkg/kubectl/validation/BUILD | 30 -- pkg/kubectl/validation/schema.go | 343 ------------------ pkg/kubectl/validation/schema_test.go | 287 --------------- .../src/k8s.io/client-go/Godeps/Godeps.json | 4 - staging/src/k8s.io/client-go/discovery/BUILD | 3 - .../k8s.io/client-go/discovery/cached/BUILD | 2 - .../client-go/discovery/cached/memcache.go | 6 - .../client-go/discovery/discovery_client.go | 44 --- .../discovery/discovery_client_test.go | 64 ---- .../src/k8s.io/client-go/discovery/fake/BUILD | 2 - .../client-go/discovery/fake/discovery.go | 15 - .../client-go/discovery/restmapper_test.go | 5 - staging/src/k8s.io/metrics/Godeps/Godeps.json | 4 - 23 files changed, 1217 deletions(-) diff --git a/pkg/kubectl/cmd/testing/BUILD b/pkg/kubectl/cmd/testing/BUILD index 345a0a49100..e566090267d 100644 --- a/pkg/kubectl/cmd/testing/BUILD +++ b/pkg/kubectl/cmd/testing/BUILD @@ -24,7 +24,6 @@ go_library( "//pkg/kubectl/resource:go_default_library", "//pkg/kubectl/validation:go_default_library", "//pkg/printers:go_default_library", - "//vendor/github.com/emicklei/go-restful-swagger12:go_default_library", "//vendor/github.com/spf13/cobra:go_default_library", "//vendor/github.com/spf13/pflag:go_default_library", "//vendor/k8s.io/apimachinery/pkg/api/meta:go_default_library", diff --git a/pkg/kubectl/cmd/testing/fake.go b/pkg/kubectl/cmd/testing/fake.go index cb46de93789..8bf4cb78e61 100644 --- a/pkg/kubectl/cmd/testing/fake.go +++ b/pkg/kubectl/cmd/testing/fake.go @@ -23,7 +23,6 @@ import ( "path/filepath" "time" - swagger "github.com/emicklei/go-restful-swagger12" "github.com/spf13/cobra" "github.com/spf13/pflag" @@ -419,10 +418,6 @@ func (f *FakeFactory) Validator(validate bool) (validation.Schema, error) { return f.tf.Validator, f.tf.Err } -func (f *FakeFactory) SwaggerSchema(schema.GroupVersionKind) (*swagger.ApiDeclaration, error) { - return nil, nil -} - func (f *FakeFactory) OpenAPISchema() (openapi.Resources, error) { return nil, nil } @@ -765,10 +760,6 @@ func (f *fakeAPIFactory) SuggestedPodTemplateResources() []schema.GroupResource return []schema.GroupResource{} } -func (f *fakeAPIFactory) SwaggerSchema(schema.GroupVersionKind) (*swagger.ApiDeclaration, error) { - return nil, nil -} - func (f *fakeAPIFactory) OpenAPISchema() (openapi.Resources, error) { if f.tf.OpenAPISchemaFunc != nil { return f.tf.OpenAPISchemaFunc() diff --git a/pkg/kubectl/cmd/util/BUILD b/pkg/kubectl/cmd/util/BUILD index c94acee13d0..3db8cf5f788 100644 --- a/pkg/kubectl/cmd/util/BUILD +++ b/pkg/kubectl/cmd/util/BUILD @@ -41,7 +41,6 @@ go_library( "//pkg/printers:go_default_library", "//pkg/printers/internalversion:go_default_library", "//pkg/version:go_default_library", - "//vendor/github.com/emicklei/go-restful-swagger12:go_default_library", "//vendor/github.com/evanphx/json-patch:go_default_library", "//vendor/github.com/golang/glog:go_default_library", "//vendor/github.com/googleapis/gnostic/OpenAPIv2:go_default_library", @@ -57,7 +56,6 @@ go_library( "//vendor/k8s.io/apimachinery/pkg/labels:go_default_library", "//vendor/k8s.io/apimachinery/pkg/runtime:go_default_library", "//vendor/k8s.io/apimachinery/pkg/runtime/schema:go_default_library", - "//vendor/k8s.io/apimachinery/pkg/runtime/serializer/json:go_default_library", "//vendor/k8s.io/apimachinery/pkg/types:go_default_library", "//vendor/k8s.io/apimachinery/pkg/util/errors:go_default_library", "//vendor/k8s.io/apimachinery/pkg/util/json:go_default_library", @@ -105,8 +103,6 @@ go_test( "//pkg/controller:go_default_library", "//pkg/kubectl:go_default_library", "//pkg/kubectl/resource:go_default_library", - "//pkg/kubectl/validation:go_default_library", - "//vendor/github.com/emicklei/go-restful-swagger12:go_default_library", "//vendor/github.com/googleapis/gnostic/OpenAPIv2:go_default_library", "//vendor/github.com/stretchr/testify/assert:go_default_library", "//vendor/k8s.io/api/core/v1:go_default_library", diff --git a/pkg/kubectl/cmd/util/cached_discovery.go b/pkg/kubectl/cmd/util/cached_discovery.go index e48e479eaf0..4fc1a0ac0cb 100644 --- a/pkg/kubectl/cmd/util/cached_discovery.go +++ b/pkg/kubectl/cmd/util/cached_discovery.go @@ -24,13 +24,11 @@ import ( "sync" "time" - "github.com/emicklei/go-restful-swagger12" "github.com/golang/glog" "github.com/googleapis/gnostic/OpenAPIv2" metav1 "k8s.io/apimachinery/pkg/apis/meta/v1" "k8s.io/apimachinery/pkg/runtime" - "k8s.io/apimachinery/pkg/runtime/schema" "k8s.io/apimachinery/pkg/version" "k8s.io/client-go/discovery" restclient "k8s.io/client-go/rest" @@ -233,10 +231,6 @@ func (d *CachedDiscoveryClient) ServerVersion() (*version.Info, error) { return d.delegate.ServerVersion() } -func (d *CachedDiscoveryClient) SwaggerSchema(version schema.GroupVersion) (*swagger.ApiDeclaration, error) { - return d.delegate.SwaggerSchema(version) -} - func (d *CachedDiscoveryClient) OpenAPISchema() (*openapi_v2.Document, error) { return d.delegate.OpenAPISchema() } diff --git a/pkg/kubectl/cmd/util/cached_discovery_test.go b/pkg/kubectl/cmd/util/cached_discovery_test.go index ac8799cf5da..46cbc9c3872 100644 --- a/pkg/kubectl/cmd/util/cached_discovery_test.go +++ b/pkg/kubectl/cmd/util/cached_discovery_test.go @@ -22,7 +22,6 @@ import ( "testing" "time" - "github.com/emicklei/go-restful-swagger12" "github.com/googleapis/gnostic/OpenAPIv2" "github.com/stretchr/testify/assert" @@ -101,7 +100,6 @@ type fakeDiscoveryClient struct { groupCalls int resourceCalls int versionCalls int - swaggerCalls int openAPICalls int serverResourcesHandler func() ([]*metav1.APIResourceList, error) @@ -166,11 +164,6 @@ func (c *fakeDiscoveryClient) ServerVersion() (*version.Info, error) { return &version.Info{}, nil } -func (c *fakeDiscoveryClient) SwaggerSchema(version schema.GroupVersion) (*swagger.ApiDeclaration, error) { - c.swaggerCalls = c.swaggerCalls + 1 - return &swagger.ApiDeclaration{}, nil -} - func (c *fakeDiscoveryClient) OpenAPISchema() (*openapi_v2.Document, error) { c.openAPICalls = c.openAPICalls + 1 return &openapi_v2.Document{}, nil diff --git a/pkg/kubectl/cmd/util/factory.go b/pkg/kubectl/cmd/util/factory.go index 774fef1bad7..64259284cc2 100644 --- a/pkg/kubectl/cmd/util/factory.go +++ b/pkg/kubectl/cmd/util/factory.go @@ -17,20 +17,13 @@ limitations under the License. package util import ( - "bytes" "fmt" "io" - "io/ioutil" - "os" - "os/user" - "path" "sort" "strconv" "strings" "time" - swagger "github.com/emicklei/go-restful-swagger12" - "github.com/golang/glog" "github.com/spf13/cobra" "github.com/spf13/pflag" @@ -39,7 +32,6 @@ import ( metav1 "k8s.io/apimachinery/pkg/apis/meta/v1" "k8s.io/apimachinery/pkg/runtime" "k8s.io/apimachinery/pkg/runtime/schema" - "k8s.io/apimachinery/pkg/runtime/serializer/json" "k8s.io/apimachinery/pkg/watch" "k8s.io/client-go/discovery" "k8s.io/client-go/kubernetes" @@ -233,8 +225,6 @@ type ObjectMappingFactory interface { // Returns a schema that can validate objects stored on disk. Validator(validate bool) (validation.Schema, error) - // SwaggerSchema returns the schema declaration for the provided group version kind. - SwaggerSchema(schema.GroupVersionKind) (*swagger.ApiDeclaration, error) // OpenAPISchema returns the schema openapi schema definiton OpenAPISchema() (openapi.Resources, error) } @@ -389,145 +379,3 @@ func getServiceProtocols(spec api.ServiceSpec) map[string]string { } return result } - -type clientSwaggerSchema struct { - c restclient.Interface - cacheDir string -} - -const schemaFileName = "schema.json" - -type schemaClient interface { - Get() *restclient.Request -} - -func recursiveSplit(dir string) []string { - parent, file := path.Split(dir) - if len(parent) == 0 { - return []string{file} - } - return append(recursiveSplit(parent[:len(parent)-1]), file) -} - -func substituteUserHome(dir string) (string, error) { - if len(dir) == 0 || dir[0] != '~' { - return dir, nil - } - parts := recursiveSplit(dir) - if len(parts[0]) == 1 { - parts[0] = os.Getenv("HOME") - } else { - usr, err := user.Lookup(parts[0][1:]) - if err != nil { - return "", err - } - parts[0] = usr.HomeDir - } - return path.Join(parts...), nil -} - -func writeSchemaFile(schemaData []byte, cacheDir, cacheFile, prefix, groupVersion string) error { - if err := os.MkdirAll(path.Join(cacheDir, prefix, groupVersion), 0755); err != nil { - return err - } - tmpFile, err := ioutil.TempFile(cacheDir, "schema") - if err != nil { - // If we can't write, keep going. - if os.IsPermission(err) { - return nil - } - return err - } - if _, err := io.Copy(tmpFile, bytes.NewBuffer(schemaData)); err != nil { - return err - } - glog.V(4).Infof("Writing swagger cache (dir %v) file %v (from %v)", cacheDir, cacheFile, tmpFile.Name()) - if err := os.Link(tmpFile.Name(), cacheFile); err != nil { - // If we can't write due to file existing, or permission problems, keep going. - if os.IsExist(err) || os.IsPermission(err) { - return nil - } - return err - } - return nil -} - -func getSchemaAndValidate(c schemaClient, data []byte, prefix, groupVersion, cacheDir string, delegate validation.Schema) (err error) { - var schemaData []byte - var firstSeen bool - fullDir, err := substituteUserHome(cacheDir) - if err != nil { - return err - } - cacheFile := path.Join(fullDir, prefix, groupVersion, schemaFileName) - - if len(cacheDir) != 0 { - if schemaData, err = ioutil.ReadFile(cacheFile); err != nil && !os.IsNotExist(err) { - return err - } - } - if schemaData == nil { - firstSeen = true - schemaData, err = downloadSchemaAndStore(c, cacheDir, fullDir, cacheFile, prefix, groupVersion) - if err != nil { - return err - } - } - schema, err := validation.NewSwaggerSchemaFromBytes(schemaData, delegate) - if err != nil { - return err - } - err = schema.ValidateBytes(data) - if _, ok := err.(validation.TypeNotFoundError); ok && !firstSeen { - // As a temporary hack, kubectl would re-get the schema if validation - // fails for type not found reason. - // TODO: runtime-config settings needs to make into the file's name - schemaData, err = downloadSchemaAndStore(c, cacheDir, fullDir, cacheFile, prefix, groupVersion) - if err != nil { - return err - } - schema, err := validation.NewSwaggerSchemaFromBytes(schemaData, delegate) - if err != nil { - return err - } - return schema.ValidateBytes(data) - } - - return err -} - -// Download swagger schema from apiserver and store it to file. -func downloadSchemaAndStore(c schemaClient, cacheDir, fullDir, cacheFile, prefix, groupVersion string) (schemaData []byte, err error) { - schemaData, err = c.Get(). - AbsPath("/swaggerapi", prefix, groupVersion). - Do(). - Raw() - if err != nil { - return - } - if len(cacheDir) != 0 { - if err = writeSchemaFile(schemaData, fullDir, cacheFile, prefix, groupVersion); err != nil { - return - } - } - return -} - -func (c *clientSwaggerSchema) ValidateBytes(data []byte) error { - gvk, err := json.DefaultMetaFactory.Interpret(data) - if err != nil { - return err - } - if ok := api.Registry.IsEnabledVersion(gvk.GroupVersion()); !ok { - // if we don't have this in our scheme, just skip validation because its an object we don't recognize - return nil - } - - switch gvk.Group { - case api.GroupName: - return getSchemaAndValidate(c.c, data, "api", gvk.GroupVersion().String(), c.cacheDir, c) - - default: - return getSchemaAndValidate(c.c, data, "apis/", gvk.GroupVersion().String(), c.cacheDir, c) - } -} diff --git a/pkg/kubectl/cmd/util/factory_object_mapping.go b/pkg/kubectl/cmd/util/factory_object_mapping.go index 20226f4c1ca..111177d77c7 100644 --- a/pkg/kubectl/cmd/util/factory_object_mapping.go +++ b/pkg/kubectl/cmd/util/factory_object_mapping.go @@ -26,14 +26,11 @@ import ( "sync" "time" - swagger "github.com/emicklei/go-restful-swagger12" - "k8s.io/api/core/v1" "k8s.io/apimachinery/pkg/api/meta" metav1 "k8s.io/apimachinery/pkg/apis/meta/v1" "k8s.io/apimachinery/pkg/labels" "k8s.io/apimachinery/pkg/runtime" - "k8s.io/apimachinery/pkg/runtime/schema" "k8s.io/client-go/discovery" "k8s.io/client-go/dynamic" restclient "k8s.io/client-go/rest" @@ -423,15 +420,6 @@ func (f *ring1Factory) Validator(validate bool) (validation.Schema, error) { }, nil } -func (f *ring1Factory) SwaggerSchema(gvk schema.GroupVersionKind) (*swagger.ApiDeclaration, error) { - version := gvk.GroupVersion() - discovery, err := f.clientAccessFactory.DiscoveryClient() - if err != nil { - return nil, err - } - return discovery.SwaggerSchema(version) -} - // OpenAPISchema returns metadata and structural information about Kubernetes object definitions. func (f *ring1Factory) OpenAPISchema() (openapi.Resources, error) { discovery, err := f.clientAccessFactory.DiscoveryClient() diff --git a/pkg/kubectl/cmd/util/factory_test.go b/pkg/kubectl/cmd/util/factory_test.go index d10897a1f37..37bc6ee1a18 100644 --- a/pkg/kubectl/cmd/util/factory_test.go +++ b/pkg/kubectl/cmd/util/factory_test.go @@ -18,13 +18,7 @@ package util import ( "bytes" - "encoding/json" "fmt" - "io/ioutil" - "net/http" - "os" - "os/user" - "path" "sort" "strings" "testing" @@ -46,12 +40,10 @@ import ( clientcmdapi "k8s.io/client-go/tools/clientcmd/api" "k8s.io/kubernetes/pkg/api" "k8s.io/kubernetes/pkg/api/testapi" - "k8s.io/kubernetes/pkg/apis/extensions" "k8s.io/kubernetes/pkg/client/clientset_generated/internalclientset/fake" "k8s.io/kubernetes/pkg/controller" "k8s.io/kubernetes/pkg/kubectl" "k8s.io/kubernetes/pkg/kubectl/resource" - "k8s.io/kubernetes/pkg/kubectl/validation" ) func TestNewFactoryDefaultFlagBindings(t *testing.T) { @@ -230,208 +222,6 @@ func TestFlagUnderscoreRenaming(t *testing.T) { } } -func loadSchemaForTest() (validation.Schema, error) { - pathToSwaggerSpec := "../../../../api/swagger-spec/" + api.Registry.GroupOrDie(api.GroupName).GroupVersion.Version + ".json" - data, err := ioutil.ReadFile(pathToSwaggerSpec) - if err != nil { - return nil, err - } - return validation.NewSwaggerSchemaFromBytes(data, nil) -} - -func header() http.Header { - header := http.Header{} - header.Set("Content-Type", runtime.ContentTypeJSON) - return header -} - -func TestRefetchSchemaWhenValidationFails(t *testing.T) { - schema, err := loadSchemaForTest() - if err != nil { - t.Errorf("Error loading schema: %v", err) - t.FailNow() - } - output, err := json.Marshal(schema) - if err != nil { - t.Errorf("Error serializing schema: %v", err) - t.FailNow() - } - requests := map[string]int{} - - c := &manualfake.RESTClient{ - APIRegistry: api.Registry, - NegotiatedSerializer: testapi.Default.NegotiatedSerializer(), - Client: manualfake.CreateHTTPClient(func(req *http.Request) (*http.Response, error) { - switch p, m := req.URL.Path, req.Method; { - case strings.HasPrefix(p, "/swaggerapi") && m == "GET": - requests[p] = requests[p] + 1 - return &http.Response{StatusCode: 200, Header: header(), Body: ioutil.NopCloser(bytes.NewBuffer(output))}, nil - default: - t.Fatalf("unexpected request: %#v\n%#v", req.URL, req) - return nil, nil - } - }), - } - dir, err := ioutil.TempDir("", "schemaCache") - if err != nil { - t.Fatalf("Error getting tempDir: %v", err) - } - defer os.RemoveAll(dir) - - fullDir, err := substituteUserHome(dir) - if err != nil { - t.Errorf("Error getting fullDir: %v", err) - t.FailNow() - } - cacheFile := path.Join(fullDir, "foo", "bar", schemaFileName) - err = writeSchemaFile(output, fullDir, cacheFile, "foo", "bar") - if err != nil { - t.Errorf("Error building old cache schema: %v", err) - t.FailNow() - } - - obj := &extensions.Deployment{} - data, err := runtime.Encode(testapi.Extensions.Codec(), obj) - if err != nil { - t.Errorf("unexpected error: %v", err) - t.FailNow() - } - - // Re-get request, should use HTTP and write - if getSchemaAndValidate(c, data, "foo", "bar", dir, nil); err != nil { - t.Errorf("unexpected error validating: %v", err) - } - if requests["/swaggerapi/foo/bar"] != 1 { - t.Errorf("expected 1 schema request, saw: %d", requests["/swaggerapi/foo/bar"]) - } -} - -func TestValidateCachesSchema(t *testing.T) { - schema, err := loadSchemaForTest() - if err != nil { - t.Errorf("Error loading schema: %v", err) - t.FailNow() - } - output, err := json.Marshal(schema) - if err != nil { - t.Errorf("Error serializing schema: %v", err) - t.FailNow() - } - requests := map[string]int{} - - c := &manualfake.RESTClient{ - APIRegistry: api.Registry, - NegotiatedSerializer: testapi.Default.NegotiatedSerializer(), - Client: manualfake.CreateHTTPClient(func(req *http.Request) (*http.Response, error) { - switch p, m := req.URL.Path, req.Method; { - case strings.HasPrefix(p, "/swaggerapi") && m == "GET": - requests[p] = requests[p] + 1 - return &http.Response{StatusCode: 200, Header: header(), Body: ioutil.NopCloser(bytes.NewBuffer(output))}, nil - default: - t.Fatalf("unexpected request: %#v\n%#v", req.URL, req) - return nil, nil - } - }), - } - dir, err := ioutil.TempDir("", "schemaCache") - if err != nil { - t.Fatalf("Error getting tempDir: %v", err) - } - defer os.RemoveAll(dir) - - obj := &api.Pod{} - data, err := runtime.Encode(testapi.Default.Codec(), obj) - if err != nil { - t.Errorf("unexpected error: %v", err) - t.FailNow() - } - - // Initial request, should use HTTP and write - if getSchemaAndValidate(c, data, "foo", "bar", dir, nil); err != nil { - t.Errorf("unexpected error validating: %v", err) - } - if _, err := os.Stat(path.Join(dir, "foo", "bar", schemaFileName)); err != nil { - t.Errorf("unexpected missing cache file: %v", err) - } - if requests["/swaggerapi/foo/bar"] != 1 { - t.Errorf("expected 1 schema request, saw: %d", requests["/swaggerapi/foo/bar"]) - } - - // Same version and group, should skip HTTP - if getSchemaAndValidate(c, data, "foo", "bar", dir, nil); err != nil { - t.Errorf("unexpected error validating: %v", err) - } - if requests["/swaggerapi/foo/bar"] != 2 { - t.Errorf("expected 1 schema request, saw: %d", requests["/swaggerapi/foo/bar"]) - } - - // Different API group, should go to HTTP and write - if getSchemaAndValidate(c, data, "foo", "baz", dir, nil); err != nil { - t.Errorf("unexpected error validating: %v", err) - } - if _, err := os.Stat(path.Join(dir, "foo", "baz", schemaFileName)); err != nil { - t.Errorf("unexpected missing cache file: %v", err) - } - if requests["/swaggerapi/foo/baz"] != 1 { - t.Errorf("expected 1 schema request, saw: %d", requests["/swaggerapi/foo/baz"]) - } - - // Different version, should go to HTTP and write - if getSchemaAndValidate(c, data, "foo2", "bar", dir, nil); err != nil { - t.Errorf("unexpected error validating: %v", err) - } - if _, err := os.Stat(path.Join(dir, "foo2", "bar", schemaFileName)); err != nil { - t.Errorf("unexpected missing cache file: %v", err) - } - if requests["/swaggerapi/foo2/bar"] != 1 { - t.Errorf("expected 1 schema request, saw: %d", requests["/swaggerapi/foo2/bar"]) - } - - // No cache dir, should go straight to HTTP and not write - if getSchemaAndValidate(c, data, "foo", "blah", "", nil); err != nil { - t.Errorf("unexpected error validating: %v", err) - } - if requests["/swaggerapi/foo/blah"] != 1 { - t.Errorf("expected 1 schema request, saw: %d", requests["/swaggerapi/foo/blah"]) - } - if _, err := os.Stat(path.Join(dir, "foo", "blah", schemaFileName)); err == nil || !os.IsNotExist(err) { - t.Errorf("unexpected cache file error: %v", err) - } -} - -func TestSubstitueUser(t *testing.T) { - usr, err := user.Current() - if err != nil { - t.Logf("SKIPPING TEST: unexpected error: %v", err) - return - } - tests := []struct { - input string - expected string - expectErr bool - }{ - {input: "~/foo", expected: path.Join(os.Getenv("HOME"), "foo")}, - {input: "~" + usr.Username + "/bar", expected: usr.HomeDir + "/bar"}, - {input: "/foo/bar", expected: "/foo/bar"}, - {input: "~doesntexit/bar", expectErr: true}, - } - for _, test := range tests { - output, err := substituteUserHome(test.input) - if test.expectErr { - if err == nil { - t.Error("unexpected non-error") - } - continue - } - if err != nil { - t.Errorf("unexpected error: %v", err) - } - if output != test.expected { - t.Errorf("expected: %s, saw: %s", test.expected, output) - } - } -} - func newPodList(count, isUnready, isUnhealthy int, labels map[string]string) *api.PodList { pods := []api.Pod{} for i := 0; i < count; i++ { diff --git a/pkg/kubectl/resource/BUILD b/pkg/kubectl/resource/BUILD index b7232f385ab..856b866ba86 100644 --- a/pkg/kubectl/resource/BUILD +++ b/pkg/kubectl/resource/BUILD @@ -61,7 +61,6 @@ go_test( "//pkg/api:go_default_library", "//pkg/api/testapi:go_default_library", "//pkg/api/testing:go_default_library", - "//vendor/github.com/emicklei/go-restful-swagger12:go_default_library", "//vendor/github.com/ghodss/yaml:go_default_library", "//vendor/github.com/googleapis/gnostic/OpenAPIv2:go_default_library", "//vendor/github.com/stretchr/testify/assert:go_default_library", diff --git a/pkg/kubectl/resource/categories_test.go b/pkg/kubectl/resource/categories_test.go index 5c5a9d44f79..7e5a6c7aed6 100644 --- a/pkg/kubectl/resource/categories_test.go +++ b/pkg/kubectl/resource/categories_test.go @@ -20,8 +20,6 @@ import ( "reflect" "testing" - swagger "github.com/emicklei/go-restful-swagger12" - "github.com/googleapis/gnostic/OpenAPIv2" metav1 "k8s.io/apimachinery/pkg/apis/meta/v1" "k8s.io/apimachinery/pkg/runtime/schema" @@ -183,10 +181,6 @@ func (c *fakeDiscoveryClient) ServerVersion() (*version.Info, error) { return &version.Info{}, nil } -func (c *fakeDiscoveryClient) SwaggerSchema(version schema.GroupVersion) (*swagger.ApiDeclaration, error) { - return &swagger.ApiDeclaration{}, nil -} - func (c *fakeDiscoveryClient) OpenAPISchema() (*openapi_v2.Document, error) { return &openapi_v2.Document{}, nil } diff --git a/pkg/kubectl/validation/BUILD b/pkg/kubectl/validation/BUILD index 4366dca98b7..c9f6d2a9975 100644 --- a/pkg/kubectl/validation/BUILD +++ b/pkg/kubectl/validation/BUILD @@ -11,38 +11,13 @@ exports_files( visibility = ["//build/visible_to:COMMON_testing"], ) -filegroup( - name = "testdata", - srcs = [ - "testdata/v1/invalidPod.yaml", - "testdata/v1/invalidPod1.json", - "testdata/v1/invalidPod2.json", - "testdata/v1/invalidPod3.json", - "testdata/v1/invalidPod4.yaml", - "testdata/v1/validPod.yaml", - ], - visibility = ["//build/visible_to:COMMON_testing"], -) - go_test( name = "go_default_test", srcs = ["schema_test.go"], data = [ ":testdata", - "//api/swagger-spec", ], library = ":go_default_library", - deps = [ - "//pkg/api:go_default_library", - "//pkg/api/testapi:go_default_library", - "//pkg/api/testing:go_default_library", - "//vendor/github.com/ghodss/yaml:go_default_library", - "//vendor/k8s.io/api/core/v1:go_default_library", - "//vendor/k8s.io/api/extensions/v1beta1:go_default_library", - "//vendor/k8s.io/apimachinery/pkg/api/testing/fuzzer:go_default_library", - "//vendor/k8s.io/apimachinery/pkg/runtime:go_default_library", - "//vendor/k8s.io/apimachinery/pkg/util/yaml:go_default_library", - ], ) go_library( @@ -50,13 +25,8 @@ go_library( srcs = ["schema.go"], visibility = ["//build/visible_to:pkg_kubectl_validation_CONSUMERS"], deps = [ - "//pkg/api/util:go_default_library", - "//vendor/github.com/emicklei/go-restful-swagger12:go_default_library", "//vendor/github.com/exponent-io/jsonpath:go_default_library", - "//vendor/github.com/golang/glog:go_default_library", - "//vendor/k8s.io/apimachinery/pkg/apis/meta/v1/unstructured:go_default_library", "//vendor/k8s.io/apimachinery/pkg/util/errors:go_default_library", - "//vendor/k8s.io/apimachinery/pkg/util/yaml:go_default_library", ], ) diff --git a/pkg/kubectl/validation/schema.go b/pkg/kubectl/validation/schema.go index b75907aefbe..6eef619390d 100644 --- a/pkg/kubectl/validation/schema.go +++ b/pkg/kubectl/validation/schema.go @@ -20,43 +20,11 @@ import ( "bytes" "encoding/json" "fmt" - "reflect" - "regexp" - "strings" - swagger "github.com/emicklei/go-restful-swagger12" ejson "github.com/exponent-io/jsonpath" - "github.com/golang/glog" - "k8s.io/apimachinery/pkg/apis/meta/v1/unstructured" utilerrors "k8s.io/apimachinery/pkg/util/errors" - "k8s.io/apimachinery/pkg/util/yaml" - apiutil "k8s.io/kubernetes/pkg/api/util" ) -// InvalidTypeError records information about an invalid type. -type InvalidTypeError struct { - ExpectedKind reflect.Kind - ObservedKind reflect.Kind - FieldName string -} - -func (i *InvalidTypeError) Error() string { - return fmt.Sprintf("expected type %s, for field %s, got %s", i.ExpectedKind.String(), i.FieldName, i.ObservedKind.String()) -} - -// NewInvalidTypeError returns an instance of NewInvalidTypeError. -func NewInvalidTypeError(expected reflect.Kind, observed reflect.Kind, fieldName string) error { - return &InvalidTypeError{expected, observed, fieldName} -} - -// TypeNotFoundError is returned when specified type -// can not found in schema -type TypeNotFoundError string - -func (tnfe TypeNotFoundError) Error() string { - return fmt.Sprintf("couldn't find type: %s", string(tnfe)) -} - // Schema is an interface that knows how to validate an API object serialized to a byte array. type Schema interface { ValidateBytes(data []byte) error @@ -133,314 +101,3 @@ func (c ConjunctiveSchema) ValidateBytes(data []byte) error { } return utilerrors.NewAggregate(list) } - -// SwaggerSchema is a schema based on an OpenAPI spec. -type SwaggerSchema struct { - api swagger.ApiDeclaration - delegate Schema // For delegating to other api groups -} - -// NewSwaggerSchemaFromBytes creates an instance of SwaggerSchema from bytes. -func NewSwaggerSchemaFromBytes(data []byte, factory Schema) (Schema, error) { - schema := &SwaggerSchema{} - err := json.Unmarshal(data, &schema.api) - if err != nil { - return nil, err - } - schema.delegate = factory - return schema, nil -} - -// validateList unpacks a list and validate every item in the list. -// It return nil if every item is ok. -// Otherwise it return an error list contain errors of every item. -func (s *SwaggerSchema) validateList(obj map[string]interface{}) []error { - items, exists := obj["items"] - if !exists { - return []error{fmt.Errorf("no items field in %#v", obj)} - } - return s.validateItems(items) -} - -func (s *SwaggerSchema) validateItems(items interface{}) []error { - allErrs := []error{} - itemList, ok := items.([]interface{}) - if !ok { - return append(allErrs, fmt.Errorf("items isn't a slice")) - } - for i, item := range itemList { - fields, ok := item.(map[string]interface{}) - if !ok { - allErrs = append(allErrs, fmt.Errorf("items[%d] isn't a map[string]interface{}", i)) - continue - } - groupVersion := fields["apiVersion"] - if groupVersion == nil { - allErrs = append(allErrs, fmt.Errorf("items[%d].apiVersion not set", i)) - continue - } - itemVersion, ok := groupVersion.(string) - if !ok { - allErrs = append(allErrs, fmt.Errorf("items[%d].apiVersion isn't string type", i)) - continue - } - if len(itemVersion) == 0 { - allErrs = append(allErrs, fmt.Errorf("items[%d].apiVersion is empty", i)) - } - kind := fields["kind"] - if kind == nil { - allErrs = append(allErrs, fmt.Errorf("items[%d].kind not set", i)) - continue - } - itemKind, ok := kind.(string) - if !ok { - allErrs = append(allErrs, fmt.Errorf("items[%d].kind isn't string type", i)) - continue - } - if len(itemKind) == 0 { - allErrs = append(allErrs, fmt.Errorf("items[%d].kind is empty", i)) - } - version := apiutil.GetVersion(itemVersion) - errs := s.ValidateObject(item, "", version+"."+itemKind) - if len(errs) >= 1 { - allErrs = append(allErrs, errs...) - } - } - - return allErrs -} - -// ValidateBytes validates bytes in a SwaggerSchema. -func (s *SwaggerSchema) ValidateBytes(data []byte) error { - var obj interface{} - out, err := yaml.ToJSON(data) - if err != nil { - return err - } - data = out - if err := json.Unmarshal(data, &obj); err != nil { - return err - } - fields, ok := obj.(map[string]interface{}) - if !ok { - return fmt.Errorf("error in unmarshaling data %s", string(data)) - } - groupVersion := fields["apiVersion"] - if groupVersion == nil { - return fmt.Errorf("apiVersion not set") - } - if _, ok := groupVersion.(string); !ok { - return fmt.Errorf("apiVersion isn't string type") - } - kind := fields["kind"] - if kind == nil { - return fmt.Errorf("kind not set") - } - if _, ok := kind.(string); !ok { - return fmt.Errorf("kind isn't string type") - } - if strings.HasSuffix(kind.(string), "List") { - return utilerrors.NewAggregate(s.validateList(fields)) - } - version := apiutil.GetVersion(groupVersion.(string)) - allErrs := s.ValidateObject(obj, "", version+"."+kind.(string)) - if len(allErrs) == 1 { - return allErrs[0] - } - return utilerrors.NewAggregate(allErrs) -} - -// ValidateObject returns no errors for a valid object. -func (s *SwaggerSchema) ValidateObject(obj interface{}, fieldName, typeName string) []error { - allErrs := []error{} - models := s.api.Models - model, ok := models.At(typeName) - - // Verify the api version matches. This is required for nested types with differing api versions because - // s.api only has schema for 1 api version (the parent object type's version). - // e.g. an extensions/v1beta1 Template embedding a /v1 Service requires the schema for the extensions/v1beta1 - // api to delegate to the schema for the /v1 api. - // Only do this for !ok objects so that cross APIVersion vendored types take precedence. - if !ok && s.delegate != nil { - fields, mapOk := obj.(map[string]interface{}) - if !mapOk { - return append(allErrs, fmt.Errorf("field %s for %s: expected object of type map[string]interface{}, but the actual type is %T", fieldName, typeName, obj)) - } - if delegated, err := s.delegateIfDifferentAPIVersion(&unstructured.Unstructured{Object: fields}); delegated { - if err != nil { - allErrs = append(allErrs, err) - } - return allErrs - } - } - - if !ok { - return append(allErrs, TypeNotFoundError(typeName)) - } - properties := model.Properties - if len(properties.List) == 0 { - // The object does not have any sub-fields. - return nil - } - fields, ok := obj.(map[string]interface{}) - if !ok { - return append(allErrs, fmt.Errorf("field %s for %s: expected object of type map[string]interface{}, but the actual type is %T", fieldName, typeName, obj)) - } - if len(fieldName) > 0 { - fieldName = fieldName + "." - } - // handle required fields - for _, requiredKey := range model.Required { - if _, ok := fields[requiredKey]; !ok { - allErrs = append(allErrs, fmt.Errorf("field %s%s for %s is required", fieldName, requiredKey, typeName)) - } - } - for key, value := range fields { - details, ok := properties.At(key) - - // Special case for runtime.RawExtension and runtime.Objects because they always fail to validate - // This is because the actual values will be of some sub-type (e.g. Deployment) not the expected - // super-type (RawExtension) - if s.isGenericArray(details) { - errs := s.validateItems(value) - if len(errs) > 0 { - allErrs = append(allErrs, errs...) - } - continue - } - if !ok { - allErrs = append(allErrs, fmt.Errorf("found invalid field %s for %s", key, typeName)) - continue - } - if details.Type == nil && details.Ref == nil { - allErrs = append(allErrs, fmt.Errorf("could not find the type of %s%s from object %v", fieldName, key, details)) - } - var fieldType string - if details.Type != nil { - fieldType = *details.Type - } else { - fieldType = *details.Ref - } - if value == nil { - glog.V(2).Infof("Skipping nil field: %s%s", fieldName, key) - continue - } - errs := s.validateField(value, fieldName+key, fieldType, &details) - if len(errs) > 0 { - allErrs = append(allErrs, errs...) - } - } - return allErrs -} - -// delegateIfDifferentAPIVersion delegates the validation of an object if its ApiGroup does not match the -// current SwaggerSchema. -// First return value is true if the validation was delegated (by a different ApiGroup SwaggerSchema) -// Second return value is the result of the delegated validation if performed. -func (s *SwaggerSchema) delegateIfDifferentAPIVersion(obj *unstructured.Unstructured) (bool, error) { - // Never delegate objects in the same APIVersion or we will get infinite recursion - if !s.isDifferentAPIVersion(obj) { - return false, nil - } - - // Convert the object back into bytes so that we can pass it to the ValidateBytes function - m, err := json.Marshal(obj.Object) - if err != nil { - return true, err - } - - // Delegate validation of this object to the correct SwaggerSchema for its ApiGroup - return true, s.delegate.ValidateBytes(m) -} - -// isDifferentAPIVersion Returns true if obj lives in a different APIVersion than the SwaggerSchema does. -// The SwaggerSchema will not be able to process objects in different APIVersions unless they are vendored. -func (s *SwaggerSchema) isDifferentAPIVersion(obj *unstructured.Unstructured) bool { - groupVersion := obj.GetAPIVersion() - return len(groupVersion) > 0 && s.api.ApiVersion != groupVersion -} - -// isGenericArray Returns true if p is an array of generic Objects - either RawExtension or Object. -func (s *SwaggerSchema) isGenericArray(p swagger.ModelProperty) bool { - return p.DataTypeFields.Type != nil && - *p.DataTypeFields.Type == "array" && - p.Items != nil && - p.Items.Ref != nil && - (*p.Items.Ref == "runtime.RawExtension" || *p.Items.Ref == "runtime.Object") -} - -// This matches type name in the swagger spec, such as "v1.Binding". -var versionRegexp = regexp.MustCompile(`^(v.+|unversioned|types)\..*`) - -func (s *SwaggerSchema) validateField(value interface{}, fieldName, fieldType string, fieldDetails *swagger.ModelProperty) []error { - allErrs := []error{} - if reflect.TypeOf(value) == nil { - return append(allErrs, fmt.Errorf("unexpected nil value for field %v", fieldName)) - } - // TODO: caesarxuchao: because we have multiple group/versions and objects - // may reference objects in other group, the commented out way of checking - // if a filedType is a type defined by us is outdated. We use a hacky way - // for now. - // TODO: the type name in the swagger spec is something like "v1.Binding", - // and the "v1" is generated from the package name, not the groupVersion of - // the type. We need to fix go-restful to embed the group name in the type - // name, otherwise we couldn't handle identically named types in different - // groups correctly. - if versionRegexp.MatchString(fieldType) { - // if strings.HasPrefix(fieldType, apiVersion) { - return s.ValidateObject(value, fieldName, fieldType) - } - switch fieldType { - case "string": - // Be loose about what we accept for 'string' since we use IntOrString in a couple of places - _, isString := value.(string) - _, isNumber := value.(float64) - _, isInteger := value.(int) - if !isString && !isNumber && !isInteger { - return append(allErrs, NewInvalidTypeError(reflect.String, reflect.TypeOf(value).Kind(), fieldName)) - } - case "array": - arr, ok := value.([]interface{}) - if !ok { - return append(allErrs, NewInvalidTypeError(reflect.Array, reflect.TypeOf(value).Kind(), fieldName)) - } - var arrType string - if fieldDetails.Items.Ref == nil && fieldDetails.Items.Type == nil { - return append(allErrs, NewInvalidTypeError(reflect.Array, reflect.TypeOf(value).Kind(), fieldName)) - } - if fieldDetails.Items.Ref != nil { - arrType = *fieldDetails.Items.Ref - } else { - arrType = *fieldDetails.Items.Type - } - for ix := range arr { - errs := s.validateField(arr[ix], fmt.Sprintf("%s[%d]", fieldName, ix), arrType, nil) - if len(errs) > 0 { - allErrs = append(allErrs, errs...) - } - } - case "uint64": - case "int64": - case "integer": - _, isNumber := value.(float64) - _, isInteger := value.(int) - if !isNumber && !isInteger { - return append(allErrs, NewInvalidTypeError(reflect.Int, reflect.TypeOf(value).Kind(), fieldName)) - } - case "float64": - if _, ok := value.(float64); !ok { - return append(allErrs, NewInvalidTypeError(reflect.Float64, reflect.TypeOf(value).Kind(), fieldName)) - } - case "boolean": - if _, ok := value.(bool); !ok { - return append(allErrs, NewInvalidTypeError(reflect.Bool, reflect.TypeOf(value).Kind(), fieldName)) - } - // API servers before release 1.3 produce swagger spec with `type: "any"` as the fallback type, while newer servers produce spec with `type: "object"`. - // We have both here so that kubectl can work with both old and new api servers. - case "object": - case "any": - default: - return append(allErrs, fmt.Errorf("unexpected type: %v", fieldType)) - } - return allErrs -} diff --git a/pkg/kubectl/validation/schema_test.go b/pkg/kubectl/validation/schema_test.go index 74a9cd38f73..c43eb785dca 100644 --- a/pkg/kubectl/validation/schema_test.go +++ b/pkg/kubectl/validation/schema_test.go @@ -17,297 +17,10 @@ limitations under the License. package validation import ( - "encoding/json" "fmt" - "io/ioutil" - "math/rand" - "strings" "testing" - - "github.com/ghodss/yaml" - - "k8s.io/api/core/v1" - "k8s.io/api/extensions/v1beta1" - "k8s.io/apimachinery/pkg/api/testing/fuzzer" - "k8s.io/apimachinery/pkg/runtime" - k8syaml "k8s.io/apimachinery/pkg/util/yaml" - "k8s.io/kubernetes/pkg/api" - "k8s.io/kubernetes/pkg/api/testapi" - kapitesting "k8s.io/kubernetes/pkg/api/testing" ) -func readPod(filename string) ([]byte, error) { - data, err := ioutil.ReadFile("testdata/" + api.Registry.GroupOrDie(api.GroupName).GroupVersion.Version + "/" + filename) - return data, err -} - -func readSwaggerFile() ([]byte, error) { - return readSwaggerAPIFile(testapi.Default) -} - -func readSwaggerAPIFile(group testapi.TestGroup) ([]byte, error) { - // TODO: Figure out a better way of finding these files - var pathToSwaggerSpec string - if group.GroupVersion().Group == "" { - pathToSwaggerSpec = "../../../api/swagger-spec/" + group.GroupVersion().Version + ".json" - } else { - pathToSwaggerSpec = "../../../api/swagger-spec/" + group.GroupVersion().Group + "_" + group.GroupVersion().Version + ".json" - } - - return ioutil.ReadFile(pathToSwaggerSpec) -} - -// Mock delegating Schema. Not a full fake impl. -type Factory struct { - defaultSchema Schema - extensionsSchema Schema -} - -var _ Schema = &Factory{} - -// TODO: Consider using a mocking library instead or fully fleshing this out into a fake impl and putting it in some -// generally available location -func (f *Factory) ValidateBytes(data []byte) error { - var obj interface{} - out, err := k8syaml.ToJSON(data) - if err != nil { - return err - } - data = out - if err := json.Unmarshal(data, &obj); err != nil { - return err - } - fields, ok := obj.(map[string]interface{}) - if !ok { - return fmt.Errorf("error in unmarshaling data %s", string(data)) - } - // Note: This only supports the 2 api versions we expect from the test it is currently supporting. - groupVersion := fields["apiVersion"] - switch groupVersion { - case "v1": - return f.defaultSchema.ValidateBytes(data) - case "extensions/v1beta1": - return f.extensionsSchema.ValidateBytes(data) - default: - return fmt.Errorf("Unsupported API version %s", groupVersion) - } -} - -func loadSchemaForTest() (Schema, error) { - data, err := readSwaggerFile() - if err != nil { - return nil, err - } - return NewSwaggerSchemaFromBytes(data, nil) -} - -func loadSchemaForTestWithFactory(group testapi.TestGroup, factory Schema) (Schema, error) { - data, err := readSwaggerAPIFile(group) - if err != nil { - return nil, err - } - return NewSwaggerSchemaFromBytes(data, factory) -} - -func NewFactory() (*Factory, error) { - f := &Factory{} - defaultSchema, err := loadSchemaForTestWithFactory(testapi.Default, f) - if err != nil { - return nil, err - } - f.defaultSchema = defaultSchema - extensionSchema, err := loadSchemaForTestWithFactory(testapi.Extensions, f) - if err != nil { - return nil, err - } - f.extensionsSchema = extensionSchema - return f, nil -} - -func TestLoad(t *testing.T) { - _, err := loadSchemaForTest() - if err != nil { - t.Errorf("Failed to load: %v", err) - } -} - -func TestValidateOk(t *testing.T) { - schema, err := loadSchemaForTest() - if err != nil { - t.Fatalf("Failed to load: %v", err) - } - tests := []struct { - obj runtime.Object - typeName string - }{ - {obj: &api.Pod{}}, - {obj: &api.Service{}}, - {obj: &api.ReplicationController{}}, - } - - seed := rand.Int63() - apiObjectFuzzer := fuzzer.FuzzerFor(kapitesting.FuzzerFuncs, rand.NewSource(seed), api.Codecs) - for i := 0; i < 5; i++ { - for _, test := range tests { - testObj := test.obj - apiObjectFuzzer.Fuzz(testObj) - data, err := runtime.Encode(testapi.Default.Codec(), testObj) - if err != nil { - t.Errorf("unexpected error: %v", err) - } - err = schema.ValidateBytes(data) - if err != nil { - t.Errorf("unexpected error: %v", err) - } - } - } -} - -func TestValidateDifferentApiVersions(t *testing.T) { - schema, err := loadSchemaForTest() - if err != nil { - t.Fatalf("Failed to load: %v", err) - } - - pod := &v1.Pod{} - pod.APIVersion = "v1" - pod.Kind = "Pod" - - deployment := &v1beta1.Deployment{} - deployment.APIVersion = "extensions/v1beta1" - deployment.Kind = "Deployment" - - list := &v1.List{} - list.APIVersion = "v1" - list.Kind = "List" - list.Items = []runtime.RawExtension{{Object: pod}, {Object: deployment}} - bytes, err := json.Marshal(list) - if err != nil { - t.Error(err) - } - err = schema.ValidateBytes(bytes) - if err == nil { - t.Error(fmt.Errorf("expected error when validating different api version and no delegate exists")) - } - f, err := NewFactory() - if err != nil { - t.Error(fmt.Errorf("failed to create Schema factory %v", err)) - } - err = f.ValidateBytes(bytes) - if err != nil { - t.Error(fmt.Errorf("failed to validate object with multiple ApiGroups: %v", err)) - } -} - -func TestInvalid(t *testing.T) { - schema, err := loadSchemaForTest() - if err != nil { - t.Fatalf("Failed to load: %v", err) - } - tests := []string{ - "invalidPod1.json", // command is a string, instead of []string. - "invalidPod2.json", // hostPort if of type string, instead of int. - "invalidPod3.json", // volumes is not an array of objects. - "invalidPod4.yaml", // string list with empty string. - "invalidPod.yaml", // command is a string, instead of []string. - } - for _, test := range tests { - pod, err := readPod(test) - if err != nil { - t.Errorf("could not read file: %s, err: %v", test, err) - } - err = schema.ValidateBytes(pod) - if err == nil { - t.Errorf("unexpected non-error, err: %s for pod: %s", err, pod) - } - } -} - -func TestValid(t *testing.T) { - schema, err := loadSchemaForTest() - if err != nil { - t.Fatalf("Failed to load: %v", err) - } - tests := []string{ - "validPod.yaml", - } - for _, test := range tests { - pod, err := readPod(test) - if err != nil { - t.Errorf("could not read file: %s, err: %v", test, err) - } - err = schema.ValidateBytes(pod) - if err != nil { - t.Errorf("unexpected error: %s, for pod %s", err, pod) - } - } -} - -func TestVersionRegex(t *testing.T) { - testCases := []struct { - typeName string - match bool - }{ - { - typeName: "v1.Binding", - match: true, - }, - { - typeName: "v1beta1.Binding", - match: true, - }, - { - typeName: "Binding", - match: false, - }, - } - for _, test := range testCases { - if versionRegexp.MatchString(test.typeName) && !test.match { - t.Errorf("unexpected error: expect %s not to match the regular expression", test.typeName) - } - if !versionRegexp.MatchString(test.typeName) && test.match { - t.Errorf("unexpected error: expect %s to match the regular expression", test.typeName) - } - } -} - -// Tests that validation works fine when spec contains "type": "any" instead of "type": "object" -// Ref: https://github.com/kubernetes/kubernetes/issues/24309 -func TestTypeAny(t *testing.T) { - data, err := readSwaggerFile() - if err != nil { - t.Errorf("failed to read swagger file: %v", err) - } - // Replace type: "any" in the spec by type: "object" and verify that the validation still passes. - newData := strings.Replace(string(data), `"type": "object"`, `"type": "any"`, -1) - schema, err := NewSwaggerSchemaFromBytes([]byte(newData), nil) - if err != nil { - t.Fatalf("Failed to load: %v", err) - } - tests := []string{ - "validPod.yaml", - } - for _, test := range tests { - podBytes, err := readPod(test) - if err != nil { - t.Errorf("could not read file: %s, err: %v", test, err) - } - // Verify that pod has at least one label (labels are type "any") - var pod v1.Pod - err = yaml.Unmarshal(podBytes, &pod) - if err != nil { - t.Errorf("error in unmarshalling pod: %v", err) - } - if len(pod.Labels) == 0 { - t.Errorf("invalid test input: the pod should have at least one label") - } - err = schema.ValidateBytes(podBytes) - if err != nil { - t.Errorf("unexpected error: %s, for pod %s", err, string(podBytes)) - } - } -} - func TestValidateDuplicateLabelsFailCases(t *testing.T) { strs := []string{ `{ diff --git a/staging/src/k8s.io/client-go/Godeps/Godeps.json b/staging/src/k8s.io/client-go/Godeps/Godeps.json index dcd0c5de29f..8216e65bdd7 100644 --- a/staging/src/k8s.io/client-go/Godeps/Godeps.json +++ b/staging/src/k8s.io/client-go/Godeps/Godeps.json @@ -90,10 +90,6 @@ "ImportPath": "github.com/emicklei/go-restful", "Rev": "ff4f55a206334ef123e4f79bbf348980da81ca46" }, - { - "ImportPath": "github.com/emicklei/go-restful-swagger12", - "Rev": "dcef7f55730566d41eae5db10e7d6981829720f6" - }, { "ImportPath": "github.com/emicklei/go-restful/log", "Rev": "ff4f55a206334ef123e4f79bbf348980da81ca46" diff --git a/staging/src/k8s.io/client-go/discovery/BUILD b/staging/src/k8s.io/client-go/discovery/BUILD index 81135dded85..10928269686 100644 --- a/staging/src/k8s.io/client-go/discovery/BUILD +++ b/staging/src/k8s.io/client-go/discovery/BUILD @@ -15,11 +15,9 @@ go_library( "unstructured.go", ], deps = [ - "//vendor/github.com/emicklei/go-restful-swagger12:go_default_library", "//vendor/github.com/golang/glog:go_default_library", "//vendor/github.com/golang/protobuf/proto:go_default_library", "//vendor/github.com/googleapis/gnostic/OpenAPIv2:go_default_library", - "//vendor/k8s.io/api/core/v1:go_default_library", "//vendor/k8s.io/apimachinery/pkg/api/errors:go_default_library", "//vendor/k8s.io/apimachinery/pkg/api/meta:go_default_library", "//vendor/k8s.io/apimachinery/pkg/apis/meta/v1:go_default_library", @@ -41,7 +39,6 @@ go_test( "restmapper_test.go", ], deps = [ - "//vendor/github.com/emicklei/go-restful-swagger12:go_default_library", "//vendor/github.com/gogo/protobuf/proto:go_default_library", "//vendor/github.com/googleapis/gnostic/OpenAPIv2:go_default_library", "//vendor/github.com/stretchr/testify/assert:go_default_library", diff --git a/staging/src/k8s.io/client-go/discovery/cached/BUILD b/staging/src/k8s.io/client-go/discovery/cached/BUILD index a1d030d4de4..0e35f4223bd 100644 --- a/staging/src/k8s.io/client-go/discovery/cached/BUILD +++ b/staging/src/k8s.io/client-go/discovery/cached/BUILD @@ -20,10 +20,8 @@ go_library( name = "go_default_library", srcs = ["memcache.go"], deps = [ - "//vendor/github.com/emicklei/go-restful-swagger12:go_default_library", "//vendor/github.com/googleapis/gnostic/OpenAPIv2:go_default_library", "//vendor/k8s.io/apimachinery/pkg/apis/meta/v1:go_default_library", - "//vendor/k8s.io/apimachinery/pkg/runtime/schema:go_default_library", "//vendor/k8s.io/apimachinery/pkg/util/runtime:go_default_library", "//vendor/k8s.io/apimachinery/pkg/version:go_default_library", "//vendor/k8s.io/client-go/discovery:go_default_library", diff --git a/staging/src/k8s.io/client-go/discovery/cached/memcache.go b/staging/src/k8s.io/client-go/discovery/cached/memcache.go index 7489a5bbf17..585b4c80da8 100644 --- a/staging/src/k8s.io/client-go/discovery/cached/memcache.go +++ b/staging/src/k8s.io/client-go/discovery/cached/memcache.go @@ -21,11 +21,9 @@ import ( "fmt" "sync" - "github.com/emicklei/go-restful-swagger12" "github.com/googleapis/gnostic/OpenAPIv2" metav1 "k8s.io/apimachinery/pkg/apis/meta/v1" - "k8s.io/apimachinery/pkg/runtime/schema" utilruntime "k8s.io/apimachinery/pkg/util/runtime" "k8s.io/apimachinery/pkg/version" "k8s.io/client-go/discovery" @@ -116,10 +114,6 @@ func (d *memCacheClient) ServerVersion() (*version.Info, error) { return d.delegate.ServerVersion() } -func (d *memCacheClient) SwaggerSchema(version schema.GroupVersion) (*swagger.ApiDeclaration, error) { - return d.delegate.SwaggerSchema(version) -} - func (d *memCacheClient) OpenAPISchema() (*openapi_v2.Document, error) { return d.delegate.OpenAPISchema() } diff --git a/staging/src/k8s.io/client-go/discovery/discovery_client.go b/staging/src/k8s.io/client-go/discovery/discovery_client.go index 59c69680980..26319f49468 100644 --- a/staging/src/k8s.io/client-go/discovery/discovery_client.go +++ b/staging/src/k8s.io/client-go/discovery/discovery_client.go @@ -23,11 +23,9 @@ import ( "sort" "strings" - "github.com/emicklei/go-restful-swagger12" "github.com/golang/protobuf/proto" "github.com/googleapis/gnostic/OpenAPIv2" - "k8s.io/api/core/v1" "k8s.io/apimachinery/pkg/api/errors" metav1 "k8s.io/apimachinery/pkg/apis/meta/v1" "k8s.io/apimachinery/pkg/runtime" @@ -48,7 +46,6 @@ type DiscoveryInterface interface { ServerGroupsInterface ServerResourcesInterface ServerVersionInterface - SwaggerSchemaInterface OpenAPISchemaInterface } @@ -92,12 +89,6 @@ type ServerVersionInterface interface { ServerVersion() (*version.Info, error) } -// SwaggerSchemaInterface has a method to retrieve the swagger schema. -type SwaggerSchemaInterface interface { - // SwaggerSchema retrieves and parses the swagger API schema the server supports. - SwaggerSchema(version schema.GroupVersion) (*swagger.ApiDeclaration, error) -} - // OpenAPISchemaInterface has a method to retrieve the open API schema. type OpenAPISchemaInterface interface { // OpenAPISchema retrieves and parses the swagger API schema the server supports. @@ -336,41 +327,6 @@ func (d *DiscoveryClient) ServerVersion() (*version.Info, error) { return &info, nil } -// SwaggerSchema retrieves and parses the swagger API schema the server supports. -// TODO: Replace usages with Open API. Tracked in https://github.com/kubernetes/kubernetes/issues/44589 -func (d *DiscoveryClient) SwaggerSchema(version schema.GroupVersion) (*swagger.ApiDeclaration, error) { - if version.Empty() { - return nil, fmt.Errorf("groupVersion cannot be empty") - } - - groupList, err := d.ServerGroups() - if err != nil { - return nil, err - } - groupVersions := metav1.ExtractGroupVersions(groupList) - // This check also takes care the case that kubectl is newer than the running endpoint - if stringDoesntExistIn(version.String(), groupVersions) { - return nil, fmt.Errorf("API version: %v is not supported by the server. Use one of: %v", version, groupVersions) - } - var path string - if len(d.LegacyPrefix) > 0 && version == v1.SchemeGroupVersion { - path = "/swaggerapi" + d.LegacyPrefix + "/" + version.Version - } else { - path = "/swaggerapi/apis/" + version.Group + "/" + version.Version - } - - body, err := d.restClient.Get().AbsPath(path).Do().Raw() - if err != nil { - return nil, err - } - var schema swagger.ApiDeclaration - err = json.Unmarshal(body, &schema) - if err != nil { - return nil, fmt.Errorf("got '%s': %v", string(body), err) - } - return &schema, nil -} - // OpenAPISchema fetches the open api schema using a rest client and parses the proto. func (d *DiscoveryClient) OpenAPISchema() (*openapi_v2.Document, error) { data, err := d.restClient.Get().AbsPath("/swagger-2.0.0.pb-v1").Do().Raw() diff --git a/staging/src/k8s.io/client-go/discovery/discovery_client_test.go b/staging/src/k8s.io/client-go/discovery/discovery_client_test.go index e0c908ee39d..409add2bf7f 100644 --- a/staging/src/k8s.io/client-go/discovery/discovery_client_test.go +++ b/staging/src/k8s.io/client-go/discovery/discovery_client_test.go @@ -25,11 +25,9 @@ import ( "reflect" "testing" - "github.com/emicklei/go-restful-swagger12" "github.com/gogo/protobuf/proto" "github.com/googleapis/gnostic/OpenAPIv2" - "k8s.io/api/core/v1" metav1 "k8s.io/apimachinery/pkg/apis/meta/v1" "k8s.io/apimachinery/pkg/runtime/schema" "k8s.io/apimachinery/pkg/util/sets" @@ -267,68 +265,6 @@ func TestGetServerResources(t *testing.T) { } } -func swaggerSchemaFakeServer() (*httptest.Server, error) { - request := 1 - var sErr error - - server := httptest.NewServer(http.HandlerFunc(func(w http.ResponseWriter, req *http.Request) { - var resp interface{} - if request == 1 { - resp = metav1.APIVersions{Versions: []string{"v1", "v2", "v3"}} - request++ - } else { - resp = swagger.ApiDeclaration{} - } - output, err := json.Marshal(resp) - if err != nil { - sErr = err - return - } - w.Header().Set("Content-Type", "application/json") - w.WriteHeader(http.StatusOK) - w.Write(output) - })) - return server, sErr -} - -func TestGetSwaggerSchema(t *testing.T) { - expect := swagger.ApiDeclaration{} - - server, err := swaggerSchemaFakeServer() - if err != nil { - t.Errorf("unexpected encoding error: %v", err) - } - defer server.Close() - - client := NewDiscoveryClientForConfigOrDie(&restclient.Config{Host: server.URL}) - got, err := client.SwaggerSchema(v1.SchemeGroupVersion) - if err != nil { - t.Fatalf("unexpected encoding error: %v", err) - } - if e, a := expect, *got; !reflect.DeepEqual(e, a) { - t.Errorf("expected %v, got %v", e, a) - } -} - -func TestGetSwaggerSchemaFail(t *testing.T) { - expErr := "API version: api.group/v4 is not supported by the server. Use one of: [v1 v2 v3]" - - server, err := swaggerSchemaFakeServer() - if err != nil { - t.Errorf("unexpected encoding error: %v", err) - } - defer server.Close() - - client := NewDiscoveryClientForConfigOrDie(&restclient.Config{Host: server.URL}) - got, err := client.SwaggerSchema(schema.GroupVersion{Group: "api.group", Version: "v4"}) - if got != nil { - t.Fatalf("unexpected response: %v", got) - } - if err.Error() != expErr { - t.Errorf("expected an error, got %v", err) - } -} - var returnedOpenAPI = openapi_v2.Document{ Definitions: &openapi_v2.Definitions{ AdditionalProperties: []*openapi_v2.NamedSchema{ diff --git a/staging/src/k8s.io/client-go/discovery/fake/BUILD b/staging/src/k8s.io/client-go/discovery/fake/BUILD index b467202e845..8c190f86f0b 100644 --- a/staging/src/k8s.io/client-go/discovery/fake/BUILD +++ b/staging/src/k8s.io/client-go/discovery/fake/BUILD @@ -10,9 +10,7 @@ go_library( name = "go_default_library", srcs = ["discovery.go"], deps = [ - "//vendor/github.com/emicklei/go-restful-swagger12:go_default_library", "//vendor/github.com/googleapis/gnostic/OpenAPIv2:go_default_library", - "//vendor/k8s.io/api/core/v1:go_default_library", "//vendor/k8s.io/apimachinery/pkg/apis/meta/v1:go_default_library", "//vendor/k8s.io/apimachinery/pkg/runtime/schema:go_default_library", "//vendor/k8s.io/apimachinery/pkg/version:go_default_library", diff --git a/staging/src/k8s.io/client-go/discovery/fake/discovery.go b/staging/src/k8s.io/client-go/discovery/fake/discovery.go index 0b757ad4eef..13ccebfa74f 100644 --- a/staging/src/k8s.io/client-go/discovery/fake/discovery.go +++ b/staging/src/k8s.io/client-go/discovery/fake/discovery.go @@ -19,10 +19,8 @@ package fake import ( "fmt" - "github.com/emicklei/go-restful-swagger12" "github.com/googleapis/gnostic/OpenAPIv2" - "k8s.io/api/core/v1" metav1 "k8s.io/apimachinery/pkg/apis/meta/v1" "k8s.io/apimachinery/pkg/runtime/schema" "k8s.io/apimachinery/pkg/version" @@ -87,19 +85,6 @@ func (c *FakeDiscovery) ServerVersion() (*version.Info, error) { return &versionInfo, nil } -func (c *FakeDiscovery) SwaggerSchema(version schema.GroupVersion) (*swagger.ApiDeclaration, error) { - action := testing.ActionImpl{} - action.Verb = "get" - if version == v1.SchemeGroupVersion { - action.Resource = schema.GroupVersionResource{Resource: "/swaggerapi/api/" + version.Version} - } else { - action.Resource = schema.GroupVersionResource{Resource: "/swaggerapi/apis/" + version.Group + "/" + version.Version} - } - - c.Invokes(action, nil) - return &swagger.ApiDeclaration{}, nil -} - func (c *FakeDiscovery) OpenAPISchema() (*openapi_v2.Document, error) { return &openapi_v2.Document{}, nil } diff --git a/staging/src/k8s.io/client-go/discovery/restmapper_test.go b/staging/src/k8s.io/client-go/discovery/restmapper_test.go index f2956be3bc6..69ee7e68640 100644 --- a/staging/src/k8s.io/client-go/discovery/restmapper_test.go +++ b/staging/src/k8s.io/client-go/discovery/restmapper_test.go @@ -28,7 +28,6 @@ import ( restclient "k8s.io/client-go/rest" "k8s.io/client-go/rest/fake" - "github.com/emicklei/go-restful-swagger12" "github.com/googleapis/gnostic/OpenAPIv2" "github.com/stretchr/testify/assert" ) @@ -380,10 +379,6 @@ func (c *fakeCachedDiscoveryInterface) ServerVersion() (*version.Info, error) { return &version.Info{}, nil } -func (c *fakeCachedDiscoveryInterface) SwaggerSchema(version schema.GroupVersion) (*swagger.ApiDeclaration, error) { - return &swagger.ApiDeclaration{}, nil -} - func (c *fakeCachedDiscoveryInterface) OpenAPISchema() (*openapi_v2.Document, error) { return &openapi_v2.Document{}, nil } diff --git a/staging/src/k8s.io/metrics/Godeps/Godeps.json b/staging/src/k8s.io/metrics/Godeps/Godeps.json index 0e144afcac3..5d52a3aed57 100644 --- a/staging/src/k8s.io/metrics/Godeps/Godeps.json +++ b/staging/src/k8s.io/metrics/Godeps/Godeps.json @@ -22,10 +22,6 @@ "ImportPath": "github.com/emicklei/go-restful", "Rev": "ff4f55a206334ef123e4f79bbf348980da81ca46" }, - { - "ImportPath": "github.com/emicklei/go-restful-swagger12", - "Rev": "dcef7f55730566d41eae5db10e7d6981829720f6" - }, { "ImportPath": "github.com/emicklei/go-restful/log", "Rev": "ff4f55a206334ef123e4f79bbf348980da81ca46"