mirror of
https://github.com/k3s-io/kubernetes.git
synced 2025-07-28 14:07:14 +00:00
Merge pull request #22420 from AdoHe/kubectl_swagger_cache
Auto commit by PR queue bot
This commit is contained in:
commit
f046d6c83e
@ -44,6 +44,14 @@ func NewInvalidTypeError(expected reflect.Kind, observed reflect.Kind, fieldName
|
|||||||
return &InvalidTypeError{expected, observed, fieldName}
|
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.
|
// Schema is an interface that knows how to validate an API object serialized to a byte array.
|
||||||
type Schema interface {
|
type Schema interface {
|
||||||
ValidateBytes(data []byte) error
|
ValidateBytes(data []byte) error
|
||||||
@ -164,7 +172,7 @@ func (s *SwaggerSchema) ValidateObject(obj interface{}, fieldName, typeName stri
|
|||||||
models := s.api.Models
|
models := s.api.Models
|
||||||
model, ok := models.At(typeName)
|
model, ok := models.At(typeName)
|
||||||
if !ok {
|
if !ok {
|
||||||
return append(allErrs, fmt.Errorf("couldn't find type: %s", typeName))
|
return append(allErrs, TypeNotFoundError(typeName))
|
||||||
}
|
}
|
||||||
properties := model.Properties
|
properties := model.Properties
|
||||||
if len(properties.List) == 0 {
|
if len(properties.List) == 0 {
|
||||||
|
@ -729,6 +729,7 @@ func writeSchemaFile(schemaData []byte, cacheDir, cacheFile, prefix, groupVersio
|
|||||||
|
|
||||||
func getSchemaAndValidate(c schemaClient, data []byte, prefix, groupVersion, cacheDir string) (err error) {
|
func getSchemaAndValidate(c schemaClient, data []byte, prefix, groupVersion, cacheDir string) (err error) {
|
||||||
var schemaData []byte
|
var schemaData []byte
|
||||||
|
var firstSeen bool
|
||||||
fullDir, err := substituteUserHome(cacheDir)
|
fullDir, err := substituteUserHome(cacheDir)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return err
|
return err
|
||||||
@ -741,24 +742,50 @@ func getSchemaAndValidate(c schemaClient, data []byte, prefix, groupVersion, cac
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
if schemaData == nil {
|
if schemaData == nil {
|
||||||
schemaData, err = c.Get().
|
firstSeen = true
|
||||||
AbsPath("/swaggerapi", prefix, groupVersion).
|
schemaData, err = downloadSchemaAndStore(c, cacheDir, fullDir, cacheFile, prefix, groupVersion)
|
||||||
Do().
|
|
||||||
Raw()
|
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return err
|
return err
|
||||||
}
|
}
|
||||||
if len(cacheDir) != 0 {
|
|
||||||
if err := writeSchemaFile(schemaData, fullDir, cacheFile, prefix, groupVersion); err != nil {
|
|
||||||
return err
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
schema, err := validation.NewSwaggerSchemaFromBytes(schemaData)
|
schema, err := validation.NewSwaggerSchemaFromBytes(schemaData)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return err
|
return err
|
||||||
}
|
}
|
||||||
return schema.ValidateBytes(data)
|
err = schema.ValidateBytes(data)
|
||||||
|
if _, ok := err.(validation.TypeNotFoundError); ok && !firstSeen {
|
||||||
|
// As a temporay 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)
|
||||||
|
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 {
|
func (c *clientSwaggerSchema) ValidateBytes(data []byte) error {
|
||||||
|
@ -32,6 +32,7 @@ import (
|
|||||||
"k8s.io/kubernetes/pkg/api/testapi"
|
"k8s.io/kubernetes/pkg/api/testapi"
|
||||||
"k8s.io/kubernetes/pkg/api/unversioned"
|
"k8s.io/kubernetes/pkg/api/unversioned"
|
||||||
"k8s.io/kubernetes/pkg/api/validation"
|
"k8s.io/kubernetes/pkg/api/validation"
|
||||||
|
"k8s.io/kubernetes/pkg/apis/extensions"
|
||||||
"k8s.io/kubernetes/pkg/client/unversioned/clientcmd"
|
"k8s.io/kubernetes/pkg/client/unversioned/clientcmd"
|
||||||
clientcmdapi "k8s.io/kubernetes/pkg/client/unversioned/clientcmd/api"
|
clientcmdapi "k8s.io/kubernetes/pkg/client/unversioned/clientcmd/api"
|
||||||
"k8s.io/kubernetes/pkg/client/unversioned/fake"
|
"k8s.io/kubernetes/pkg/client/unversioned/fake"
|
||||||
@ -215,6 +216,63 @@ func loadSchemaForTest() (validation.Schema, error) {
|
|||||||
return validation.NewSwaggerSchemaFromBytes(data)
|
return validation.NewSwaggerSchemaFromBytes(data)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
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 := &fake.RESTClient{
|
||||||
|
Codec: testapi.Default.Codec(),
|
||||||
|
Client: fake.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, Body: ioutil.NopCloser(bytes.NewBuffer(output))}, nil
|
||||||
|
default:
|
||||||
|
t.Fatalf("unexpected request: %#v\n%#v", req.URL, req)
|
||||||
|
return nil, nil
|
||||||
|
}
|
||||||
|
}),
|
||||||
|
}
|
||||||
|
dir := os.TempDir() + "/schemaCache"
|
||||||
|
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); 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) {
|
func TestValidateCachesSchema(t *testing.T) {
|
||||||
schema, err := loadSchemaForTest()
|
schema, err := loadSchemaForTest()
|
||||||
if err != nil {
|
if err != nil {
|
||||||
@ -266,7 +324,7 @@ func TestValidateCachesSchema(t *testing.T) {
|
|||||||
if getSchemaAndValidate(c, data, "foo", "bar", dir); err != nil {
|
if getSchemaAndValidate(c, data, "foo", "bar", dir); err != nil {
|
||||||
t.Errorf("unexpected error validating: %v", err)
|
t.Errorf("unexpected error validating: %v", err)
|
||||||
}
|
}
|
||||||
if requests["/swaggerapi/foo/bar"] != 1 {
|
if requests["/swaggerapi/foo/bar"] != 2 {
|
||||||
t.Errorf("expected 1 schema request, saw: %d", requests["/swaggerapi/foo/bar"])
|
t.Errorf("expected 1 schema request, saw: %d", requests["/swaggerapi/foo/bar"])
|
||||||
}
|
}
|
||||||
|
|
||||||
|
Loading…
Reference in New Issue
Block a user