mirror of
				https://github.com/optim-enterprises-bv/kubernetes.git
				synced 2025-11-04 04:08:16 +00:00 
			
		
		
		
	Merge pull request #22420 from AdoHe/kubectl_swagger_cache
Auto commit by PR queue bot
This commit is contained in:
		@@ -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 {
 | 
						schema, err := validation.NewSwaggerSchemaFromBytes(schemaData)
 | 
				
			||||||
				return err
 | 
						if err != nil {
 | 
				
			||||||
			}
 | 
							return err
 | 
				
			||||||
		}
 | 
						}
 | 
				
			||||||
 | 
						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)
 | 
							schema, err := validation.NewSwaggerSchemaFromBytes(schemaData)
 | 
				
			||||||
		if err != nil {
 | 
							if err != nil {
 | 
				
			||||||
			return err
 | 
								return err
 | 
				
			||||||
		}
 | 
							}
 | 
				
			||||||
		return schema.ValidateBytes(data)
 | 
							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"])
 | 
				
			||||||
	}
 | 
						}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 
 | 
				
			|||||||
		Reference in New Issue
	
	Block a user