mirror of
https://github.com/opencontainers/image-spec.git
synced 2025-04-18 03:24:01 +03:00
Switch jsonschema validation libraries
Signed-off-by: Brandon Mitchell <git@bmitch.net>
This commit is contained in:
parent
dd33f727e2
commit
4bbdd7f035
10
go.mod
10
go.mod
@ -5,13 +5,5 @@ go 1.18
|
||||
require (
|
||||
github.com/opencontainers/go-digest v1.0.0
|
||||
github.com/russross/blackfriday v1.6.0
|
||||
github.com/xeipuuv/gojsonreference v0.0.0-20180127040603-bd5ef7bd5415
|
||||
github.com/xeipuuv/gojsonschema v1.2.0
|
||||
)
|
||||
|
||||
require (
|
||||
github.com/davecgh/go-spew v1.1.1 // indirect
|
||||
github.com/stretchr/testify v1.7.0 // indirect
|
||||
github.com/xeipuuv/gojsonpointer v0.0.0-20180127040702-4e3ac2762d5f // indirect
|
||||
gopkg.in/yaml.v3 v3.0.0-20210107192922-496545a6307b // indirect
|
||||
github.com/santhosh-tekuri/jsonschema/v5 v5.3.1
|
||||
)
|
||||
|
21
go.sum
21
go.sum
@ -1,23 +1,6 @@
|
||||
github.com/davecgh/go-spew v1.1.0/go.mod h1:J7Y8YcW2NihsgmVo/mv3lAwl/skON4iLHjSsI+c5H38=
|
||||
github.com/davecgh/go-spew v1.1.1 h1:vj9j/u1bqnvCEfJOwUhtlOARqs3+rkHYY13jYWTU97c=
|
||||
github.com/davecgh/go-spew v1.1.1/go.mod h1:J7Y8YcW2NihsgmVo/mv3lAwl/skON4iLHjSsI+c5H38=
|
||||
github.com/opencontainers/go-digest v1.0.0 h1:apOUWs51W5PlhuyGyz9FCeeBIOUDA/6nW8Oi/yOhh5U=
|
||||
github.com/opencontainers/go-digest v1.0.0/go.mod h1:0JzlMkj0TRzQZfJkVvzbP0HBR3IKzErnv2BNG4W4MAM=
|
||||
github.com/pmezard/go-difflib v1.0.0 h1:4DBwDE0NGyQoBHbLQYPwSUPoCMWR5BEzIk/f1lZbAQM=
|
||||
github.com/pmezard/go-difflib v1.0.0/go.mod h1:iKH77koFhYxTK1pcRnkKkqfTogsbg7gZNVY4sRDYZ/4=
|
||||
github.com/russross/blackfriday v1.6.0 h1:KqfZb0pUVN2lYqZUYRddxF4OR8ZMURnJIG5Y3VRLtww=
|
||||
github.com/russross/blackfriday v1.6.0/go.mod h1:ti0ldHuxg49ri4ksnFxlkCfN+hvslNlmVHqNRXXJNAY=
|
||||
github.com/stretchr/objx v0.1.0/go.mod h1:HFkY916IF+rwdDfMAkV7OtwuqBVzrE8GR6GFx+wExME=
|
||||
github.com/stretchr/testify v1.3.0/go.mod h1:M5WIy9Dh21IEIfnGCwXGc5bZfKNJtfHm1UVUgZn+9EI=
|
||||
github.com/stretchr/testify v1.7.0 h1:nwc3DEeHmmLAfoZucVR881uASk0Mfjw8xYJ99tb5CcY=
|
||||
github.com/stretchr/testify v1.7.0/go.mod h1:6Fq8oRcR53rry900zMqJjRRixrwX3KX962/h/Wwjteg=
|
||||
github.com/xeipuuv/gojsonpointer v0.0.0-20180127040702-4e3ac2762d5f h1:J9EGpcZtP0E/raorCMxlFGSTBrsSlaDGf3jU/qvAE2c=
|
||||
github.com/xeipuuv/gojsonpointer v0.0.0-20180127040702-4e3ac2762d5f/go.mod h1:N2zxlSyiKSe5eX1tZViRH5QA0qijqEDrYZiPEAiq3wU=
|
||||
github.com/xeipuuv/gojsonreference v0.0.0-20180127040603-bd5ef7bd5415 h1:EzJWgHovont7NscjpAxXsDA8S8BMYve8Y5+7cuRE7R0=
|
||||
github.com/xeipuuv/gojsonreference v0.0.0-20180127040603-bd5ef7bd5415/go.mod h1:GwrjFmJcFw6At/Gs6z4yjiIwzuJ1/+UwLxMQDVQXShQ=
|
||||
github.com/xeipuuv/gojsonschema v1.2.0 h1:LhYJRs+L4fBtjZUfuSZIKGeVu0QRy8e5Xi7D17UxZ74=
|
||||
github.com/xeipuuv/gojsonschema v1.2.0/go.mod h1:anYRn/JVcOK2ZgGU+IjEV4nwlhoK5sQluxsYJ78Id3Y=
|
||||
gopkg.in/check.v1 v0.0.0-20161208181325-20d25e280405/go.mod h1:Co6ibVJAznAaIkqp8huTwlJQCZ016jof/cbN4VW5Yz0=
|
||||
gopkg.in/yaml.v3 v3.0.0-20200313102051-9f266ea9e77c/go.mod h1:K4uyk7z7BCEPqu6E+C64Yfv1cQ7kz7rIZviUmN+EgEM=
|
||||
gopkg.in/yaml.v3 v3.0.0-20210107192922-496545a6307b h1:h8qDotaEPuJATrMmW04NCwg7v22aHH28wwpauUhK9Oo=
|
||||
gopkg.in/yaml.v3 v3.0.0-20210107192922-496545a6307b/go.mod h1:K4uyk7z7BCEPqu6E+C64Yfv1cQ7kz7rIZviUmN+EgEM=
|
||||
github.com/santhosh-tekuri/jsonschema/v5 v5.3.1 h1:lZUw3E0/J3roVtGQ+SCrUrg3ON6NgVqpn3+iol9aGu4=
|
||||
github.com/santhosh-tekuri/jsonschema/v5 v5.3.1/go.mod h1:uToXkOrWAZ6/Oc07xWQrPOhJotwFIyu2bBVN41fcDUY=
|
||||
|
@ -4,7 +4,7 @@
|
||||
"mediaType": {
|
||||
"id": "https://opencontainers.org/schema/image/descriptor/mediaType",
|
||||
"type": "string",
|
||||
"pattern": "^[A-Za-z0-9][A-Za-z0-9!#$&-^_.+]{0,126}/[A-Za-z0-9][A-Za-z0-9!#$&-^_.+]{0,126}$"
|
||||
"pattern": "^[A-Za-z0-9][A-Za-z0-9!#$&^_.+-]{0,126}/[A-Za-z0-9][A-Za-z0-9!#$&^_.+-]{0,126}$"
|
||||
},
|
||||
"digest": {
|
||||
"description": "the cryptographic checksum digest of the object, in the pattern '<algorithm>:<encoded>'",
|
||||
|
@ -23,6 +23,8 @@ import (
|
||||
|
||||
// A SyntaxError is a description of a JSON syntax error
|
||||
// including line, column and offset in the JSON file.
|
||||
//
|
||||
// Deprecated: SyntaxError is no longer returned from Validator.
|
||||
type SyntaxError struct {
|
||||
msg string
|
||||
Line, Col int
|
||||
@ -34,6 +36,8 @@ func (e *SyntaxError) Error() string { return e.msg }
|
||||
// WrapSyntaxError checks whether the given error is a *json.SyntaxError
|
||||
// and converts it into a *schema.SyntaxError containing line/col information using the given reader.
|
||||
// If the given error is not a *json.SyntaxError it is returned unchanged.
|
||||
//
|
||||
// Deprecated: WrapSyntaxError is no longer returned by Validator.
|
||||
func WrapSyntaxError(r io.Reader, err error) error {
|
||||
var serr *json.SyntaxError
|
||||
if errors.As(err, &serr) {
|
||||
|
125
schema/loader.go
125
schema/loader.go
@ -1,125 +0,0 @@
|
||||
// Copyright 2018 The Linux Foundation
|
||||
//
|
||||
// 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 schema
|
||||
|
||||
import (
|
||||
"bytes"
|
||||
"encoding/json"
|
||||
"fmt"
|
||||
"io"
|
||||
"net/http"
|
||||
"strings"
|
||||
|
||||
"github.com/xeipuuv/gojsonreference"
|
||||
"github.com/xeipuuv/gojsonschema"
|
||||
)
|
||||
|
||||
// fsLoaderFactory implements gojsonschema.JSONLoaderFactory by reading files under the specified namespaces from the root of fs.
|
||||
type fsLoaderFactory struct {
|
||||
namespaces []string
|
||||
fs http.FileSystem
|
||||
}
|
||||
|
||||
// newFSLoaderFactory returns a fsLoaderFactory reading files under the specified namespaces from the root of fs.
|
||||
func newFSLoaderFactory(namespaces []string, fs http.FileSystem) *fsLoaderFactory {
|
||||
return &fsLoaderFactory{
|
||||
namespaces: namespaces,
|
||||
fs: fs,
|
||||
}
|
||||
}
|
||||
|
||||
func (factory *fsLoaderFactory) New(source string) gojsonschema.JSONLoader {
|
||||
return &fsLoader{
|
||||
factory: factory,
|
||||
source: source,
|
||||
}
|
||||
}
|
||||
|
||||
// refContents returns the contents of ref, if available in fsLoaderFactory.
|
||||
func (factory *fsLoaderFactory) refContents(ref gojsonreference.JsonReference) ([]byte, error) {
|
||||
refStr := ref.String()
|
||||
path := ""
|
||||
for _, ns := range factory.namespaces {
|
||||
if strings.HasPrefix(refStr, ns) {
|
||||
path = "/" + strings.TrimPrefix(refStr, ns)
|
||||
break
|
||||
}
|
||||
}
|
||||
if path == "" {
|
||||
return nil, fmt.Errorf("schema reference %#v unexpectedly not available in fsLoaderFactory with namespaces %#v", path, factory.namespaces)
|
||||
}
|
||||
|
||||
f, err := factory.fs.Open(path)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
defer f.Close()
|
||||
|
||||
return io.ReadAll(f)
|
||||
}
|
||||
|
||||
// fsLoader implements gojsonschema.JSONLoader by reading the document named by source from a fsLoaderFactory.
|
||||
type fsLoader struct {
|
||||
factory *fsLoaderFactory
|
||||
source string
|
||||
}
|
||||
|
||||
// JsonSource implements gojsonschema.JSONLoader.JsonSource. The "Json" capitalization needs to be maintained to conform to the interface.
|
||||
func (l *fsLoader) JsonSource() interface{} { // revive:disable-line:var-naming
|
||||
return l.source
|
||||
}
|
||||
|
||||
func (l *fsLoader) LoadJSON() (interface{}, error) {
|
||||
// Based on gojsonschema.jsonReferenceLoader.LoadJSON.
|
||||
reference, err := gojsonreference.NewJsonReference(l.source)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
refToURL := reference
|
||||
refToURL.GetUrl().Fragment = ""
|
||||
|
||||
body, err := l.factory.refContents(refToURL)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
return decodeJSONUsingNumber(bytes.NewReader(body))
|
||||
}
|
||||
|
||||
// decodeJSONUsingNumber returns JSON parsed from an io.Reader
|
||||
func decodeJSONUsingNumber(r io.Reader) (interface{}, error) {
|
||||
// Copied from gojsonschema.
|
||||
var document interface{}
|
||||
|
||||
decoder := json.NewDecoder(r)
|
||||
decoder.UseNumber()
|
||||
|
||||
err := decoder.Decode(&document)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
return document, nil
|
||||
}
|
||||
|
||||
// JsonReference implements gojsonschema.JSONLoader.JsonReference. The "Json" capitalization needs to be maintained to conform to the interface.
|
||||
func (l *fsLoader) JsonReference() (gojsonreference.JsonReference, error) { // revive:disable-line:var-naming
|
||||
return gojsonreference.NewJsonReference(l.JsonSource().(string))
|
||||
}
|
||||
|
||||
func (l *fsLoader) LoaderFactory() gojsonschema.JSONLoaderFactory {
|
||||
return l.factory
|
||||
}
|
@ -23,56 +23,62 @@ import (
|
||||
|
||||
// Media types for the OCI image formats
|
||||
const (
|
||||
ValidatorMediaTypeDescriptor Validator = v1.MediaTypeDescriptor
|
||||
ValidatorMediaTypeLayoutHeader Validator = v1.MediaTypeLayoutHeader
|
||||
ValidatorMediaTypeManifest Validator = v1.MediaTypeImageManifest
|
||||
ValidatorMediaTypeImageIndex Validator = v1.MediaTypeImageIndex
|
||||
ValidatorMediaTypeImageConfig Validator = v1.MediaTypeImageConfig
|
||||
ValidatorMediaTypeImageLayer unimplemented = v1.MediaTypeImageLayer
|
||||
ValidatorMediaTypeDescriptor Validator = v1.MediaTypeDescriptor
|
||||
ValidatorMediaTypeLayoutHeader Validator = v1.MediaTypeLayoutHeader
|
||||
ValidatorMediaTypeManifest Validator = v1.MediaTypeImageManifest
|
||||
ValidatorMediaTypeImageIndex Validator = v1.MediaTypeImageIndex
|
||||
ValidatorMediaTypeImageConfig Validator = v1.MediaTypeImageConfig
|
||||
ValidatorMediaTypeImageLayer Validator = v1.MediaTypeImageLayer
|
||||
)
|
||||
|
||||
var (
|
||||
// fs stores the embedded http.FileSystem
|
||||
// having the OCI JSON schema files in root "/".
|
||||
// specFS stores the embedded http.FileSystem having the OCI JSON schema files in root "/".
|
||||
//go:embed *.json
|
||||
fs embed.FS
|
||||
specFS embed.FS
|
||||
|
||||
// schemaNamespaces is a set of URI prefixes which are treated as containing the schema files of fs.
|
||||
// This is necessary because *.json schema files in this directory use "id" and "$ref" attributes which evaluate to such URIs, e.g.
|
||||
// ./image-manifest-schema.json URI contains
|
||||
// "id": "https://opencontainers.org/schema/image/manifest",
|
||||
// and
|
||||
// "$ref": "content-descriptor.json"
|
||||
// which evaluates as a link to https://opencontainers.org/schema/image/content-descriptor.json .
|
||||
//
|
||||
// To support such links without accessing the network (and trying to load content which is not hosted at these URIs),
|
||||
// fsLoaderFactory accepts any URI starting with one of the schemaNamespaces below,
|
||||
// and uses _escFS to load them from the root of its in-memory filesystem tree.
|
||||
//
|
||||
// (Note that this must contain subdirectories before its parent directories for fsLoaderFactory.refContents to work.)
|
||||
schemaNamespaces = []string{
|
||||
"https://opencontainers.org/schema/image/descriptor/",
|
||||
"https://opencontainers.org/schema/image/index/",
|
||||
"https://opencontainers.org/schema/image/manifest/",
|
||||
"https://opencontainers.org/schema/image/",
|
||||
"https://opencontainers.org/schema/descriptor/",
|
||||
"https://opencontainers.org/schema/",
|
||||
// specsOrig maps OCI schema media types to schema files.
|
||||
specs = map[Validator]string{
|
||||
ValidatorMediaTypeDescriptor: "content-descriptor.json",
|
||||
ValidatorMediaTypeLayoutHeader: "image-layout-schema.json",
|
||||
ValidatorMediaTypeManifest: "image-manifest-schema.json",
|
||||
ValidatorMediaTypeImageIndex: "image-index-schema.json",
|
||||
ValidatorMediaTypeImageConfig: "config-schema.json",
|
||||
}
|
||||
|
||||
// specs maps OCI schema media types to schema URIs.
|
||||
// These URIs are expected to be used only by fsLoaderFactory (which trims schemaNamespaces defined above)
|
||||
// and should never cause a network access.
|
||||
specs = map[Validator]string{
|
||||
ValidatorMediaTypeDescriptor: "https://opencontainers.org/schema/content-descriptor.json",
|
||||
ValidatorMediaTypeLayoutHeader: "https://opencontainers.org/schema/image/image-layout-schema.json",
|
||||
ValidatorMediaTypeManifest: "https://opencontainers.org/schema/image/image-manifest-schema.json",
|
||||
ValidatorMediaTypeImageIndex: "https://opencontainers.org/schema/image/image-index-schema.json",
|
||||
ValidatorMediaTypeImageConfig: "https://opencontainers.org/schema/image/config-schema.json",
|
||||
// specURLs lists the various URLs a given spec may be known by.
|
||||
// This is generated from the "id" value in each spec and relative ref values they contain.
|
||||
specURLs = map[string][]string{
|
||||
"config-schema.json": {
|
||||
"https://opencontainers.org/schema/image/config",
|
||||
},
|
||||
"content-descriptor.json": {
|
||||
"https://opencontainers.org/schema/descriptor",
|
||||
"https://opencontainers.org/schema/image/content-descriptor.json",
|
||||
},
|
||||
"defs-descriptor.json": {
|
||||
"https://opencontainers.org/schema/image/descriptor/mediaType",
|
||||
"https://opencontainers.org/schema/defs-descriptor.json",
|
||||
"https://opencontainers.org/schema/image/defs-descriptor.json",
|
||||
},
|
||||
"defs.json": {
|
||||
"https://opencontainers.org/schema/defs.json",
|
||||
"https://opencontainers.org/schema/image/defs.json",
|
||||
"https://opencontainers.org/schema/image/descriptor/defs.json",
|
||||
},
|
||||
"image-index-schema.json": {
|
||||
"https://opencontainers.org/schema/image/index",
|
||||
},
|
||||
"image-layout-schema.json": {
|
||||
"https://opencontainers.org/schema/image/layout",
|
||||
},
|
||||
"image-manifest-schema.json": {
|
||||
"https://opencontainers.org/schema/image/manifest",
|
||||
},
|
||||
}
|
||||
)
|
||||
|
||||
// FileSystem returns an in-memory filesystem including the schema files.
|
||||
// The schema files are located at the root directory.
|
||||
func FileSystem() http.FileSystem {
|
||||
return http.FS(fs)
|
||||
return http.FS(specFS)
|
||||
}
|
||||
|
@ -106,27 +106,11 @@ func validate(t *testing.T, name string) {
|
||||
err = schema.Validator(example.Mediatype).Validate(strings.NewReader(example.Body))
|
||||
if err == nil {
|
||||
printFields(t, "ok", example.Mediatype, example.Title)
|
||||
t.Log(example.Body, "---")
|
||||
continue
|
||||
}
|
||||
|
||||
var errs []error
|
||||
var verr schema.ValidationError
|
||||
if errors.As(err, &verr) {
|
||||
errs = verr.Errs
|
||||
} else {
|
||||
printFields(t, "error", example.Mediatype, example.Title, err)
|
||||
t.Error(err)
|
||||
t.Log(example.Body, "---")
|
||||
continue
|
||||
}
|
||||
|
||||
for _, err := range errs {
|
||||
printFields(t, "invalid", example.Mediatype, example.Title)
|
||||
t.Error(err)
|
||||
fmt.Println(example.Body, "---")
|
||||
continue
|
||||
}
|
||||
t.Log(example.Body, "---")
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -20,90 +20,121 @@ import (
|
||||
"errors"
|
||||
"fmt"
|
||||
"io"
|
||||
"os"
|
||||
"regexp"
|
||||
|
||||
digest "github.com/opencontainers/go-digest"
|
||||
v1 "github.com/opencontainers/image-spec/specs-go/v1"
|
||||
"github.com/xeipuuv/gojsonschema"
|
||||
"github.com/santhosh-tekuri/jsonschema/v5"
|
||||
)
|
||||
|
||||
// Validator wraps a media type string identifier
|
||||
// and implements validation against a JSON schema.
|
||||
// Validator wraps a media type string identifier and implements validation against a JSON schema.
|
||||
type Validator string
|
||||
|
||||
type validateFunc func(r io.Reader) error
|
||||
|
||||
var mapValidate = map[Validator]validateFunc{
|
||||
ValidatorMediaTypeImageConfig: validateConfig,
|
||||
ValidatorMediaTypeDescriptor: validateDescriptor,
|
||||
ValidatorMediaTypeImageIndex: validateIndex,
|
||||
ValidatorMediaTypeManifest: validateManifest,
|
||||
}
|
||||
|
||||
// ValidationError contains all the errors that happened during validation.
|
||||
//
|
||||
// Deprecated: this is no longer used by [Validator].
|
||||
type ValidationError struct {
|
||||
Errs []error
|
||||
}
|
||||
|
||||
// Error returns the error message.
|
||||
//
|
||||
// Deprecated: this is no longer used by [Validator].
|
||||
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 := io.ReadAll(src)
|
||||
if err != nil {
|
||||
return fmt.Errorf("unable to read the document file: %w", err)
|
||||
}
|
||||
|
||||
if f, ok := mapValidate[v]; ok {
|
||||
if f == nil {
|
||||
return fmt.Errorf("internal error: mapValidate[%q] is nil", v)
|
||||
// run the media type specific validation
|
||||
if fn, ok := validateByMediaType[v]; ok {
|
||||
if fn == nil {
|
||||
return fmt.Errorf("internal error: mapValidate is nil for %s", string(v))
|
||||
}
|
||||
err = f(bytes.NewReader(buf))
|
||||
// buffer the src so the media type validation and the schema validation can both read it
|
||||
buf, err := io.ReadAll(src)
|
||||
if err != nil {
|
||||
return fmt.Errorf("failed to read input: %w", err)
|
||||
}
|
||||
src = bytes.NewReader(buf)
|
||||
err = fn(buf)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
}
|
||||
|
||||
sl := newFSLoaderFactory(schemaNamespaces, FileSystem()).New(specs[v])
|
||||
ml := gojsonschema.NewStringLoader(string(buf))
|
||||
// json schema validation
|
||||
return v.validateSchema(src)
|
||||
}
|
||||
|
||||
result, err := gojsonschema.Validate(sl, ml)
|
||||
func (v Validator) validateSchema(src io.Reader) error {
|
||||
if _, ok := specs[v]; !ok {
|
||||
return fmt.Errorf("no validator available for %s", string(v))
|
||||
}
|
||||
|
||||
c := jsonschema.NewCompiler()
|
||||
|
||||
// load the schema files from the embedded FS
|
||||
dir, err := specFS.ReadDir(".")
|
||||
if err != nil {
|
||||
return fmt.Errorf("schema %s: unable to validate: %w", v,
|
||||
WrapSyntaxError(bytes.NewReader(buf), err))
|
||||
return fmt.Errorf("spec embedded directory could not be loaded: %w", err)
|
||||
}
|
||||
for _, file := range dir {
|
||||
if file.IsDir() {
|
||||
continue
|
||||
}
|
||||
specBuf, err := specFS.ReadFile(file.Name())
|
||||
if err != nil {
|
||||
return fmt.Errorf("could not read spec file %s: %w", file.Name(), err)
|
||||
}
|
||||
err = c.AddResource(file.Name(), bytes.NewReader(specBuf))
|
||||
if err != nil {
|
||||
return fmt.Errorf("failed to add spec file %s: %w", file.Name(), err)
|
||||
}
|
||||
if len(specURLs[file.Name()]) == 0 {
|
||||
fmt.Fprintf(os.Stderr, "warning: spec file has no aliases: %s", file.Name())
|
||||
}
|
||||
for _, specURL := range specURLs[file.Name()] {
|
||||
err = c.AddResource(specURL, bytes.NewReader(specBuf))
|
||||
if err != nil {
|
||||
return fmt.Errorf("failed to add spec file %s as url %s: %w", file.Name(), specURL, err)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
if result.Valid() {
|
||||
return nil
|
||||
// compile based on the type of validator
|
||||
schema, err := c.Compile(specs[v])
|
||||
if err != nil {
|
||||
return fmt.Errorf("failed to compile schema %s: %w", string(v), err)
|
||||
}
|
||||
|
||||
errs := make([]error, 0, len(result.Errors()))
|
||||
for _, desc := range result.Errors() {
|
||||
errs = append(errs, fmt.Errorf("%s", desc))
|
||||
// read in the user input and validate
|
||||
var input interface{}
|
||||
err = json.NewDecoder(src).Decode(&input)
|
||||
if err != nil {
|
||||
return fmt.Errorf("unable to parse json to validate: %w", err)
|
||||
}
|
||||
|
||||
return ValidationError{
|
||||
Errs: errs,
|
||||
err = schema.Validate(input)
|
||||
if err != nil {
|
||||
return fmt.Errorf("validation failed: %w", err)
|
||||
}
|
||||
return nil
|
||||
}
|
||||
|
||||
type unimplemented string
|
||||
type validateFunc func([]byte) error
|
||||
|
||||
func (v unimplemented) Validate(_ io.Reader) error {
|
||||
return fmt.Errorf("%s: unimplemented", v)
|
||||
var validateByMediaType = map[Validator]validateFunc{
|
||||
ValidatorMediaTypeImageConfig: validateConfig,
|
||||
ValidatorMediaTypeDescriptor: validateDescriptor,
|
||||
ValidatorMediaTypeImageIndex: validateIndex,
|
||||
ValidatorMediaTypeManifest: validateManifest,
|
||||
}
|
||||
|
||||
func validateManifest(r io.Reader) error {
|
||||
func validateManifest(buf []byte) error {
|
||||
header := v1.Manifest{}
|
||||
|
||||
buf, err := io.ReadAll(r)
|
||||
if err != nil {
|
||||
return fmt.Errorf("error reading the io stream: %w", err)
|
||||
}
|
||||
|
||||
err = json.Unmarshal(buf, &header)
|
||||
err := json.Unmarshal(buf, &header)
|
||||
if err != nil {
|
||||
return fmt.Errorf("manifest format mismatch: %w", err)
|
||||
}
|
||||
@ -125,15 +156,10 @@ func validateManifest(r io.Reader) error {
|
||||
return nil
|
||||
}
|
||||
|
||||
func validateDescriptor(r io.Reader) error {
|
||||
func validateDescriptor(buf []byte) error {
|
||||
header := v1.Descriptor{}
|
||||
|
||||
buf, err := io.ReadAll(r)
|
||||
if err != nil {
|
||||
return fmt.Errorf("error reading the io stream: %w", err)
|
||||
}
|
||||
|
||||
err = json.Unmarshal(buf, &header)
|
||||
err := json.Unmarshal(buf, &header)
|
||||
if err != nil {
|
||||
return fmt.Errorf("descriptor format mismatch: %w", err)
|
||||
}
|
||||
@ -147,15 +173,10 @@ func validateDescriptor(r io.Reader) error {
|
||||
return err
|
||||
}
|
||||
|
||||
func validateIndex(r io.Reader) error {
|
||||
func validateIndex(buf []byte) error {
|
||||
header := v1.Index{}
|
||||
|
||||
buf, err := io.ReadAll(r)
|
||||
if err != nil {
|
||||
return fmt.Errorf("error reading the io stream: %w", err)
|
||||
}
|
||||
|
||||
err = json.Unmarshal(buf, &header)
|
||||
err := json.Unmarshal(buf, &header)
|
||||
if err != nil {
|
||||
return fmt.Errorf("index format mismatch: %w", err)
|
||||
}
|
||||
@ -174,15 +195,10 @@ func validateIndex(r io.Reader) error {
|
||||
return nil
|
||||
}
|
||||
|
||||
func validateConfig(r io.Reader) error {
|
||||
func validateConfig(buf []byte) error {
|
||||
header := v1.Image{}
|
||||
|
||||
buf, err := io.ReadAll(r)
|
||||
if err != nil {
|
||||
return fmt.Errorf("error reading the io stream: %w", err)
|
||||
}
|
||||
|
||||
err = json.Unmarshal(buf, &header)
|
||||
err := json.Unmarshal(buf, &header)
|
||||
if err != nil {
|
||||
return fmt.Errorf("config format mismatch: %w", err)
|
||||
}
|
||||
|
Loading…
x
Reference in New Issue
Block a user