Skip to content

Commit

Permalink
support different levels validation of JSON reader stream
Browse files Browse the repository at this point in the history
Sometimes consumer want to strictly validate specific fields in OCI JSON
file.
So it support different levels validation to reader stream. Validator
organizes the level functions to array, and provide the optional
argument.

Signed-off-by: xiekeyang <xiekeyang@huawei.com>
  • Loading branch information
xiekeyang committed Nov 29, 2016
1 parent 734f5c4 commit 21ef4e8
Show file tree
Hide file tree
Showing 5 changed files with 79 additions and 23 deletions.
2 changes: 1 addition & 1 deletion schema/config_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -156,7 +156,7 @@ func TestConfig(t *testing.T) {
},
} {
r := strings.NewReader(tt.config)
err := schema.MediaTypeImageConfig.Validate(r)
err := schema.MediaTypeImageConfig.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
6 changes: 3 additions & 3 deletions schema/manifest_backwards_compatibility_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -118,7 +118,7 @@ func TestBackwardsCompatibilityManifestList(t *testing.T) {

manifest := convertFormats(tt.manifest)
r := strings.NewReader(manifest)
err := schema.MediaTypeManifestList.Validate(r)
err := schema.MediaTypeManifestList.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 @@ -181,7 +181,7 @@ func TestBackwardsCompatibilityManifest(t *testing.T) {

manifest := convertFormats(tt.manifest)
r := strings.NewReader(manifest)
err := schema.MediaTypeManifest.Validate(r)
err := schema.MediaTypeManifest.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 @@ -221,7 +221,7 @@ func TestBackwardsCompatibilityConfig(t *testing.T) {

config := convertFormats(tt.config)
r := strings.NewReader(config)
err := schema.MediaTypeImageConfig.Validate(r)
err := schema.MediaTypeImageConfig.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 @@ -144,7 +144,7 @@ func TestManifest(t *testing.T) {
},
} {
r := strings.NewReader(tt.manifest)
err := schema.MediaTypeManifest.Validate(r)
err := schema.MediaTypeManifest.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/spec_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -73,7 +73,7 @@ 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
90 changes: 73 additions & 17 deletions schema/validator.go
Original file line number Diff line number Diff line change
Expand Up @@ -30,12 +30,6 @@ import (
// and implements validation against a JSON schema.
type Validator string

type validateDescendantsFunc func(r io.Reader) error

var mapValidateDescendants = map[Validator]validateDescendantsFunc{
MediaTypeManifest: validateManifestDescendants,
}

// ValidationError contains all the errors that happened during validation.
type ValidationError struct {
Errs []error
Expand All @@ -45,22 +39,35 @@ 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 {
buf, err := ioutil.ReadAll(src)
// ValidateFunc provides the type of function to validate specific object.
type ValidateFunc func(r io.Reader, v Validator) 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")
}

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

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

sl := gojsonschema.NewReferenceLoaderFileSystem("file:///"+specs[v], fs)
ml := gojsonschema.NewStringLoader(string(buf))
Expand Down Expand Up @@ -92,7 +99,56 @@ func (v unimplemented) Validate(src io.Reader) error {
return fmt.Errorf("%s: unimplemented", v)
}

func validateManifestDescendants(r io.Reader) error {
type validateRefMediaFunc func(r io.Reader) error

var vlidateRefMediaMap = map[Validator]validateRefMediaFunc{
MediaTypeManifestList: validateManifestListRefObject,
MediaTypeManifest: validateManifestRefObject,
}

// 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)
}

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 validateManifestListRefObject(r io.Reader) error {
header := v1.ManifestList{}

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
}

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

buf, err := ioutil.ReadAll(r)
Expand All @@ -106,13 +162,13 @@ func validateManifestDescendants(r io.Reader) error {
}

if header.Config.MediaType != string(v1.MediaTypeImageConfig) {
fmt.Printf("warning: config %s has an unknown media type: %s\n", header.Config.Digest, header.Config.MediaType)
return fmt.Errorf("config %s has an unknown media type: %s", header.Config.Digest, header.Config.MediaType)
}

for _, layer := range header.Layers {
if layer.MediaType != string(v1.MediaTypeImageLayer) &&
layer.MediaType != string(v1.MediaTypeImageLayerNonDistributable) {
fmt.Printf("warning: layer %s has an unknown media type: %s\n", layer.Digest, layer.MediaType)
return fmt.Errorf("layer %s has an unknown media type: %s", layer.Digest, layer.MediaType)
}
}
return nil
Expand Down

0 comments on commit 21ef4e8

Please sign in to comment.