Skip to content

Commit

Permalink
Support different levels validation for JSON reader stream
Browse files Browse the repository at this point in the history
This establishes function list, to allow consumer to select items for
validation. A based validation level is JSON schema, and a optional item
is the media-type of their referenced fields.

Signed-off-by: xiekeyang <xiekeyang@huawei.com>
  • Loading branch information
xiekeyang committed Feb 20, 2017
1 parent 0ecc46b commit 3ad5d1b
Show file tree
Hide file tree
Showing 7 changed files with 84 additions and 27 deletions.
6 changes: 3 additions & 3 deletions schema/backwards_compatibility_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -116,7 +116,7 @@ func TestBackwardsCompatibilityImageIndex(t *testing.T) {

imageIndex := convertFormats(tt.imageIndex)
r := strings.NewReader(imageIndex)
err := schema.ValidatorMediaTypeImageIndex.Validate(r)
err := schema.ValidatorMediaTypeImageIndex.Validate(r, []schema.ValidateFunc{schema.ValidateSchema})

if got := err != nil; tt.fail != got {
t.Errorf("test %d: expected validation failure %t but got %t, err %v", i, tt.fail, got, err)
Expand Down Expand Up @@ -178,7 +178,7 @@ func TestBackwardsCompatibilityManifest(t *testing.T) {

manifest := convertFormats(tt.manifest)
r := strings.NewReader(manifest)
err := schema.ValidatorMediaTypeManifest.Validate(r)
err := schema.ValidatorMediaTypeManifest.Validate(r, []schema.ValidateFunc{schema.ValidateSchema})

if got := err != nil; tt.fail != got {
t.Errorf("test %d: expected validation failure %t but got %t, err %v", i, tt.fail, got, err)
Expand Down Expand Up @@ -217,7 +217,7 @@ func TestBackwardsCompatibilityConfig(t *testing.T) {

config := convertFormats(tt.config)
r := strings.NewReader(config)
err := schema.ValidatorMediaTypeImageConfig.Validate(r)
err := schema.ValidatorMediaTypeImageConfig.Validate(r, []schema.ValidateFunc{schema.ValidateSchema})

if got := err != nil; tt.fail != got {
t.Errorf("test %d: expected validation failure %t but got %t, err %v", i, tt.fail, got, err)
Expand Down
2 changes: 1 addition & 1 deletion schema/config_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -211,7 +211,7 @@ func TestConfig(t *testing.T) {
},
} {
r := strings.NewReader(tt.config)
err := schema.ValidatorMediaTypeImageConfig.Validate(r)
err := schema.ValidatorMediaTypeImageConfig.Validate(r, []schema.ValidateFunc{schema.ValidateSchema})

if got := err != nil; tt.fail != got {
t.Errorf("test %d: expected validation failure %t but got %t, err %v", i, tt.fail, got, err)
Expand Down
2 changes: 1 addition & 1 deletion schema/descriptor_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -204,7 +204,7 @@ func TestDescriptor(t *testing.T) {
},
} {
r := strings.NewReader(tt.descriptor)
err := schema.ValidatorMediaTypeDescriptor.Validate(r)
err := schema.ValidatorMediaTypeDescriptor.Validate(r, []schema.ValidateFunc{schema.ValidateSchema})

if got := err != nil; tt.fail != got {
t.Errorf("test %d: expected validation failure %t but got %t, err %v", i, tt.fail, got, err)
Expand Down
2 changes: 1 addition & 1 deletion schema/imageindex_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -239,7 +239,7 @@ func TestImageIndex(t *testing.T) {
},
} {
r := strings.NewReader(tt.imageIndex)
err := schema.ValidatorMediaTypeImageIndex.Validate(r)
err := schema.ValidatorMediaTypeImageIndex.Validate(r, []schema.ValidateFunc{schema.ValidateSchema})

if got := err != nil; tt.fail != got {
t.Errorf("test %d: expected validation failure %t but got %t, err %v", i, tt.fail, got, err)
Expand Down
2 changes: 1 addition & 1 deletion schema/manifest_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -193,7 +193,7 @@ func TestManifest(t *testing.T) {
},
} {
r := strings.NewReader(tt.manifest)
err := schema.ValidatorMediaTypeManifest.Validate(r)
err := schema.ValidatorMediaTypeManifest.Validate(r, []schema.ValidateFunc{schema.ValidateSchema})

if got := err != nil; tt.fail != got {
t.Errorf("test %d: expected validation failure %t but got %t, err %v", i, tt.fail, got, err)
Expand Down
3 changes: 2 additions & 1 deletion schema/spec_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -77,7 +77,8 @@ func validate(t *testing.T, name string) {
continue
}

err = schema.Validator(example.Mediatype).Validate(strings.NewReader(example.Body))
err = schema.Validator(example.Mediatype).Validate(strings.NewReader(example.Body),
[]schema.ValidateFunc{schema.ValidateSchema, schema.ValidateRefMedia})
if err == nil {
printFields(t, "ok", example.Mediatype, example.Title)
t.Log(example.Body, "---")
Expand Down
94 changes: 75 additions & 19 deletions schema/validator.go
Original file line number Diff line number Diff line change
Expand Up @@ -26,14 +26,42 @@ import (
"github.com/xeipuuv/gojsonschema"
)

type validateRefMediaFunc func(r io.Reader) error

var vlidateRefMediaMap = map[Validator]validateRefMediaFunc{
ValidatorMediaTypeImageIndex: validateImageIndexRefObject,
ValidatorMediaTypeManifest: validateManifestRefObject,
}

// ValidateFunc provides the type of function to validate specific object.
type ValidateFunc func(r io.Reader, v Validator) error

// Validator wraps a media type string identifier
// and implements validation against a JSON schema.
type Validator string

type validateDescendantsFunc func(r io.Reader) error
// Validate validates the given reader against the schema and OCI format defined in spec.
// r: the given reader.
// funcList: the optional functions to validate the wrapped media type.
func (v Validator) Validate(r io.Reader, funcList []ValidateFunc) error {
buf, err := ioutil.ReadAll(r)
if err != nil {
return errors.Wrap(err, "unable to read the document file")
}

for _, f := range funcList {
err := f(bytes.NewReader(buf), v)
if err != nil {
return err
}
}
return nil
}

type unimplemented string

var mapValidateDescendants = map[Validator]validateDescendantsFunc{
ValidatorMediaTypeManifest: validateManifestDescendants,
func (v unimplemented) Validate(src io.Reader) error {
return fmt.Errorf("%s: unimplemented", v)
}

// ValidationError contains all the errors that happened during validation.
Expand All @@ -45,23 +73,15 @@ func (e ValidationError) Error() string {
return fmt.Sprintf("%v", e.Errs)
}

// Validate validates the given reader against the schema of the wrapped media type.
func (v Validator) Validate(src io.Reader) error {
// ValidateSchema validates the given reader against the schema of the wrapped media type.
// src: the given reader.
// v: the expected media type.
func ValidateSchema(src io.Reader, v Validator) error {
buf, err := ioutil.ReadAll(src)
if err != nil {
return errors.Wrap(err, "unable to read the document file")
}

if f, ok := mapValidateDescendants[v]; ok {
if f == nil {
return fmt.Errorf("internal error: mapValidateDescendents[%q] is nil", v)
}
err = f(bytes.NewReader(buf))
if err != nil {
return err
}
}

sl := gojsonschema.NewReferenceLoaderFileSystem("file:///"+specs[v], fs)
ml := gojsonschema.NewStringLoader(string(buf))

Expand All @@ -86,13 +106,28 @@ func (v Validator) Validate(src io.Reader) error {
}
}

type unimplemented string
// ValidateRefMedia validates the referenced objects against OCI media type defined in spec.
// r: the given reader.
// v: the expected media type.
func ValidateRefMedia(r io.Reader, v Validator) error {
f, ok := vlidateRefMediaMap[v]
if ok {
if f == nil {
return fmt.Errorf("referenced media type validation %q unimplemented", v)
}

func (v unimplemented) Validate(src io.Reader) error {
return fmt.Errorf("%s: unimplemented", v)
buf, err := ioutil.ReadAll(r)
if err != nil {
return errors.Wrap(err, "unable to read the document file")
}

return f(bytes.NewReader(buf))
}

return nil
}

func validateManifestDescendants(r io.Reader) error {
func validateManifestRefObject(r io.Reader) error {
header := v1.Manifest{}

buf, err := ioutil.ReadAll(r)
Expand All @@ -117,3 +152,24 @@ func validateManifestDescendants(r io.Reader) error {
}
return nil
}

func validateImageIndexRefObject(r io.Reader) error {
header := v1.ImageIndex{}

buf, err := ioutil.ReadAll(r)
if err != nil {
return errors.Wrapf(err, "error reading the io stream")
}

err = json.Unmarshal(buf, &header)
if err != nil {
return errors.Wrap(err, "manifest list format mismatch")
}

for _, manifest := range header.Manifests {
if manifest.MediaType != string(v1.MediaTypeImageManifest) {
return fmt.Errorf("manifest %s has an unknown media type: %s", manifest.Digest, manifest.MediaType)
}
}
return nil
}

0 comments on commit 3ad5d1b

Please sign in to comment.