diff --git a/pkg/kubectl/cmd/util/factory.go b/pkg/kubectl/cmd/util/factory.go index 9be1580854d..06f90b4551c 100644 --- a/pkg/kubectl/cmd/util/factory.go +++ b/pkg/kubectl/cmd/util/factory.go @@ -18,6 +18,7 @@ package util import ( "bytes" + "errors" "flag" "fmt" "io" @@ -236,8 +237,8 @@ func NewFactory(optionalClientConfig clientcmd.ClientConfig) *Factory { } return &clientSwaggerSchema{ c: client, - ec: client.ExperimentalClient, cacheDir: dir, + mapper: api.RESTMapper, }, nil } return validation.NullSchema{}, nil @@ -298,8 +299,8 @@ func getServicePorts(spec api.ServiceSpec) []string { type clientSwaggerSchema struct { c *client.Client - ec *client.ExperimentalClient cacheDir string + mapper meta.RESTMapper } const schemaFileName = "schema.json" @@ -349,29 +350,29 @@ func getSchemaAndValidate(c schemaClient, data []byte, group, version, cacheDir } func (c *clientSwaggerSchema) ValidateBytes(data []byte) error { - version, _, err := runtime.UnstructuredJSONScheme.DataVersionAndKind(data) + version, kind, err := runtime.UnstructuredJSONScheme.DataVersionAndKind(data) if err != nil { return err } if ok := registered.IsRegisteredAPIVersion(version); !ok { return fmt.Errorf("API version %q isn't supported, only supports API versions %q", version, registered.RegisteredVersions) } - // First try stable api, if we can't validate using that, try experimental. - // If experimental fails, return error from stable api. - // TODO: Figure out which group to try once multiple group support is merged - // instead of trying everything. - err = getSchemaAndValidate(c.c.RESTClient, data, "api", version, c.cacheDir) - if err != nil && c.ec != nil { - g, err := latest.Group("experimental") + resource, _ := meta.KindToResource(kind, false) + group, err := c.mapper.GroupForResource(resource) + if err != nil { + return fmt.Errorf("could not find api group for %s: %v", kind, err) + } + if group == "experimental" { + g, err := latest.Group(group) if err != nil { return err } - errExp := getSchemaAndValidate(c.ec.RESTClient, data, "apis"+"/"+g.Group, version, c.cacheDir) - if errExp == nil { - return nil + if c.c.ExperimentalClient == nil { + return errors.New("unable to validate: no experimental client") } + return getSchemaAndValidate(c.c.ExperimentalClient.RESTClient, data, "apis/"+g.Group, version, c.cacheDir) } - return err + return getSchemaAndValidate(c.c.RESTClient, data, "api", version, c.cacheDir) } // DefaultClientConfig creates a clientcmd.ClientConfig with the following hierarchy: diff --git a/test/integration/framework/master_utils.go b/test/integration/framework/master_utils.go index 968e053923b..ad07615307f 100644 --- a/test/integration/framework/master_utils.go +++ b/test/integration/framework/master_utils.go @@ -140,16 +140,18 @@ func startMasterOrDie(masterConfig *master.Config) (*master.Master, *httptest.Se } masterConfig = &master.Config{ - DatabaseStorage: etcdStorage, - ExpDatabaseStorage: expEtcdStorage, - KubeletClient: client.FakeKubeletClient{}, - EnableLogsSupport: false, - EnableProfiling: true, - EnableUISupport: false, - APIPrefix: "/api", - APIGroupPrefix: "/apis", - Authorizer: apiserver.NewAlwaysAllowAuthorizer(), - AdmissionControl: admit.NewAlwaysAdmit(), + DatabaseStorage: etcdStorage, + ExpDatabaseStorage: expEtcdStorage, + KubeletClient: client.FakeKubeletClient{}, + EnableExp: true, + EnableLogsSupport: false, + EnableProfiling: true, + EnableSwaggerSupport: true, + EnableUISupport: false, + APIPrefix: "/api", + APIGroupPrefix: "/apis", + Authorizer: apiserver.NewAlwaysAllowAuthorizer(), + AdmissionControl: admit.NewAlwaysAdmit(), } } else { etcdStorage = masterConfig.DatabaseStorage diff --git a/test/integration/kubectl_test.go b/test/integration/kubectl_test.go new file mode 100644 index 00000000000..4738d78eaf1 --- /dev/null +++ b/test/integration/kubectl_test.go @@ -0,0 +1,76 @@ +// +build integration,!no-etcd + +/* +Copyright 2015 The Kubernetes Authors All rights reserved. + +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 integration + +import ( + "testing" + + "k8s.io/kubernetes/pkg/client/unversioned/clientcmd" + clientcmdapi "k8s.io/kubernetes/pkg/client/unversioned/clientcmd/api" + "k8s.io/kubernetes/pkg/kubectl/cmd/util" + "k8s.io/kubernetes/test/integration/framework" +) + +func TestKubectlValidation(t *testing.T) { + testCases := []struct { + data string + err bool + }{ + {`{"apiVersion": "v1", "kind": "thisObjectShouldNotExistInAnyGroup"}`, true}, + {`{"apiVersion": "invalidVersion", "kind": "Pod"}`, true}, + {`{"apiVersion": "v1", "kind": "Pod"}`, false}, + + // The following test the experimental api. + // TOOD: Replace with something more robust. These may move. + {`{"apiVersion": "v1", "kind": "DaemonSet"}`, false}, + {`{"apiVersion": "v1", "kind": "Job"}`, false}, + {`{"apiVersion": "vNotAVersion", "kind": "Job"}`, true}, + } + components := framework.NewMasterComponents(&framework.Config{}) + defer components.Stop(true, true) + ctx := clientcmdapi.NewContext() + cfg := clientcmdapi.NewConfig() + cluster := clientcmdapi.NewCluster() + cluster.Server = components.ApiServer.URL + cluster.InsecureSkipTLSVerify = true + overrides := clientcmd.ConfigOverrides{ + ClusterInfo: *cluster, + Context: *ctx, + CurrentContext: "test", + } + cmdConfig := clientcmd.NewNonInteractiveClientConfig(*cfg, "test", &overrides) + factory := util.NewFactory(cmdConfig) + schema, err := factory.Validator(true, "") + if err != nil { + t.Errorf("failed to get validator: %v", err) + return + } + for i, test := range testCases { + err := schema.ValidateBytes([]byte(test.data)) + if err == nil { + if test.err { + t.Errorf("case %d: expected error", i) + } + } else { + if !test.err { + t.Errorf("case %d: unexpected error: %v", i, err) + } + } + } +}