mirror of
https://github.com/containers/skopeo.git
synced 2025-09-03 07:35:02 +00:00
integration: add upstream validator to OCI roundtrip tests
In order to make sure that we don't create invalid OCI images that are consistently invalid, add additional checks to ensure that both of the generated OCI images in the round-trip test are valid according to the upstream validator. This commit vendors the following packages (deep breath): * oci/image-tools@7575a09363, which requires * oci/image-spec@v1.0.0-rc4 [revendor, but is technically an update because I couldn't figure out what version was vendored last time] * oci/runtime-spec@v1.0.0-rc4 * xeipuuv/gojsonschema@6b67b3fab7 * xeipuuv/gojsonreference@e02fc20de9 * xeipuuv/gojsonpointer@e0fe6f6830 * camlistore/go4@7ce08ca145 Signed-off-by: Aleksa Sarai <asarai@suse.de>
This commit is contained in:
@@ -4,6 +4,7 @@ import (
|
||||
"bytes"
|
||||
"fmt"
|
||||
"io/ioutil"
|
||||
"log"
|
||||
"net/http"
|
||||
"net/http/httptest"
|
||||
"os"
|
||||
@@ -13,6 +14,7 @@ import (
|
||||
"github.com/containers/image/manifest"
|
||||
"github.com/go-check/check"
|
||||
"github.com/opencontainers/go-digest"
|
||||
"github.com/opencontainers/image-tools/image"
|
||||
)
|
||||
|
||||
func init() {
|
||||
@@ -216,7 +218,14 @@ func (s *CopySuite) TestCopyOCIRoundTrip(c *check.C) {
|
||||
out := combinedOutputOfCommand(c, "diff", "-urN", oci1+"/blobs", oci2+"/blobs")
|
||||
c.Assert(out, check.Equals, "")
|
||||
|
||||
// For some silly reason we pass a logger to the OCI library here...
|
||||
logger := log.New(os.Stderr, "", 0)
|
||||
|
||||
// TODO: Verify using the upstream OCI image validator.
|
||||
err = image.ValidateLayout(oci1, nil, logger)
|
||||
c.Assert(err, check.IsNil)
|
||||
err = image.ValidateLayout(oci2, nil, logger)
|
||||
c.Assert(err, check.IsNil)
|
||||
|
||||
// Now verify that everything is identical. Currently this is true, but
|
||||
// because we recompute the manifests on-the-fly this doesn't necessarily
|
||||
|
10
vendor.conf
10
vendor.conf
@@ -22,7 +22,15 @@ golang.org/x/net master
|
||||
github.com/docker/distribution master
|
||||
github.com/docker/libtrust master
|
||||
github.com/opencontainers/runc master
|
||||
github.com/opencontainers/image-spec master
|
||||
github.com/opencontainers/image-spec v1.0.0-rc4
|
||||
# -- start OCI image validation requirements.
|
||||
github.com/opencontainers/runtime-spec v1.0.0-rc4
|
||||
github.com/opencontainers/image-tools master
|
||||
github.com/xeipuuv/gojsonschema master
|
||||
github.com/xeipuuv/gojsonreference master
|
||||
github.com/xeipuuv/gojsonpointer master
|
||||
go4.org/errorutil master https://github.com/camlistore/go4
|
||||
# -- end OCI image validation requirements
|
||||
github.com/mtrmac/gpgme master
|
||||
# openshift/origin' k8s dependencies as of OpenShift v1.1.5
|
||||
github.com/golang/glog 44145f04b68cf362d9c4df2182967c2275eaefed
|
||||
|
16
vendor/github.com/opencontainers/image-spec/schema/doc.go
generated
vendored
Normal file
16
vendor/github.com/opencontainers/image-spec/schema/doc.go
generated
vendored
Normal file
@@ -0,0 +1,16 @@
|
||||
// Copyright 2016 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 defines the OCI image media types, schema definitions and validation functions.
|
||||
package schema
|
44
vendor/github.com/opencontainers/image-spec/schema/error.go
generated
vendored
Normal file
44
vendor/github.com/opencontainers/image-spec/schema/error.go
generated
vendored
Normal file
@@ -0,0 +1,44 @@
|
||||
// Copyright 2016 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 (
|
||||
"encoding/json"
|
||||
"io"
|
||||
|
||||
"go4.org/errorutil"
|
||||
)
|
||||
|
||||
// A SyntaxError is a description of a JSON syntax error
|
||||
// including line, column and offset in the JSON file.
|
||||
type SyntaxError struct {
|
||||
msg string
|
||||
Line, Col int
|
||||
Offset int64
|
||||
}
|
||||
|
||||
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.
|
||||
func WrapSyntaxError(r io.Reader, err error) error {
|
||||
if serr, ok := err.(*json.SyntaxError); ok {
|
||||
line, col, _ := errorutil.HighlightBytePosition(r, serr.Offset)
|
||||
return &SyntaxError{serr.Error(), line, col, serr.Offset}
|
||||
}
|
||||
|
||||
return err
|
||||
}
|
335
vendor/github.com/opencontainers/image-spec/schema/fs.go
generated
vendored
Normal file
335
vendor/github.com/opencontainers/image-spec/schema/fs.go
generated
vendored
Normal file
@@ -0,0 +1,335 @@
|
||||
// Copyright 2016 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"
|
||||
"compress/gzip"
|
||||
"encoding/base64"
|
||||
"io/ioutil"
|
||||
"net/http"
|
||||
"os"
|
||||
"path"
|
||||
"sync"
|
||||
"time"
|
||||
)
|
||||
|
||||
type _escLocalFS struct{}
|
||||
|
||||
var _escLocal _escLocalFS
|
||||
|
||||
type _escStaticFS struct{}
|
||||
|
||||
var _escStatic _escStaticFS
|
||||
|
||||
type _escDirectory struct {
|
||||
fs http.FileSystem
|
||||
name string
|
||||
}
|
||||
|
||||
type _escFile struct {
|
||||
compressed string
|
||||
size int64
|
||||
modtime int64
|
||||
local string
|
||||
isDir bool
|
||||
|
||||
once sync.Once
|
||||
data []byte
|
||||
name string
|
||||
}
|
||||
|
||||
func (_escLocalFS) Open(name string) (http.File, error) {
|
||||
f, present := _escData[path.Clean(name)]
|
||||
if !present {
|
||||
return nil, os.ErrNotExist
|
||||
}
|
||||
return os.Open(f.local)
|
||||
}
|
||||
|
||||
func (_escStaticFS) prepare(name string) (*_escFile, error) {
|
||||
f, present := _escData[path.Clean(name)]
|
||||
if !present {
|
||||
return nil, os.ErrNotExist
|
||||
}
|
||||
var err error
|
||||
f.once.Do(func() {
|
||||
f.name = path.Base(name)
|
||||
if f.size == 0 {
|
||||
return
|
||||
}
|
||||
var gr *gzip.Reader
|
||||
b64 := base64.NewDecoder(base64.StdEncoding, bytes.NewBufferString(f.compressed))
|
||||
gr, err = gzip.NewReader(b64)
|
||||
if err != nil {
|
||||
return
|
||||
}
|
||||
f.data, err = ioutil.ReadAll(gr)
|
||||
})
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
return f, nil
|
||||
}
|
||||
|
||||
func (fs _escStaticFS) Open(name string) (http.File, error) {
|
||||
f, err := fs.prepare(name)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
return f.File()
|
||||
}
|
||||
|
||||
func (dir _escDirectory) Open(name string) (http.File, error) {
|
||||
return dir.fs.Open(dir.name + name)
|
||||
}
|
||||
|
||||
func (f *_escFile) File() (http.File, error) {
|
||||
type httpFile struct {
|
||||
*bytes.Reader
|
||||
*_escFile
|
||||
}
|
||||
return &httpFile{
|
||||
Reader: bytes.NewReader(f.data),
|
||||
_escFile: f,
|
||||
}, nil
|
||||
}
|
||||
|
||||
func (f *_escFile) Close() error {
|
||||
return nil
|
||||
}
|
||||
|
||||
func (f *_escFile) Readdir(count int) ([]os.FileInfo, error) {
|
||||
return nil, nil
|
||||
}
|
||||
|
||||
func (f *_escFile) Stat() (os.FileInfo, error) {
|
||||
return f, nil
|
||||
}
|
||||
|
||||
func (f *_escFile) Name() string {
|
||||
return f.name
|
||||
}
|
||||
|
||||
func (f *_escFile) Size() int64 {
|
||||
return f.size
|
||||
}
|
||||
|
||||
func (f *_escFile) Mode() os.FileMode {
|
||||
return 0
|
||||
}
|
||||
|
||||
func (f *_escFile) ModTime() time.Time {
|
||||
return time.Unix(f.modtime, 0)
|
||||
}
|
||||
|
||||
func (f *_escFile) IsDir() bool {
|
||||
return f.isDir
|
||||
}
|
||||
|
||||
func (f *_escFile) Sys() interface{} {
|
||||
return f
|
||||
}
|
||||
|
||||
// _escFS returns a http.Filesystem for the embedded assets. If useLocal is true,
|
||||
// the filesystem's contents are instead used.
|
||||
func _escFS(useLocal bool) http.FileSystem {
|
||||
if useLocal {
|
||||
return _escLocal
|
||||
}
|
||||
return _escStatic
|
||||
}
|
||||
|
||||
// _escDir returns a http.Filesystem for the embedded assets on a given prefix dir.
|
||||
// If useLocal is true, the filesystem's contents are instead used.
|
||||
func _escDir(useLocal bool, name string) http.FileSystem {
|
||||
if useLocal {
|
||||
return _escDirectory{fs: _escLocal, name: name}
|
||||
}
|
||||
return _escDirectory{fs: _escStatic, name: name}
|
||||
}
|
||||
|
||||
// _escFSByte returns the named file from the embedded assets. If useLocal is
|
||||
// true, the filesystem's contents are instead used.
|
||||
func _escFSByte(useLocal bool, name string) ([]byte, error) {
|
||||
if useLocal {
|
||||
f, err := _escLocal.Open(name)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
b, err := ioutil.ReadAll(f)
|
||||
f.Close()
|
||||
return b, err
|
||||
}
|
||||
f, err := _escStatic.prepare(name)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
return f.data, nil
|
||||
}
|
||||
|
||||
// _escFSMustByte is the same as _escFSByte, but panics if name is not present.
|
||||
func _escFSMustByte(useLocal bool, name string) []byte {
|
||||
b, err := _escFSByte(useLocal, name)
|
||||
if err != nil {
|
||||
panic(err)
|
||||
}
|
||||
return b
|
||||
}
|
||||
|
||||
// _escFSString is the string version of _escFSByte.
|
||||
func _escFSString(useLocal bool, name string) (string, error) {
|
||||
b, err := _escFSByte(useLocal, name)
|
||||
return string(b), err
|
||||
}
|
||||
|
||||
// _escFSMustString is the string version of _escFSMustByte.
|
||||
func _escFSMustString(useLocal bool, name string) string {
|
||||
return string(_escFSMustByte(useLocal, name))
|
||||
}
|
||||
|
||||
var _escData = map[string]*_escFile{
|
||||
|
||||
"/config-schema.json": {
|
||||
local: "config-schema.json",
|
||||
size: 774,
|
||||
modtime: 1485388791,
|
||||
compressed: `
|
||||
H4sIAAAJbogA/5SRvW7rMAyFdz+F4WS8ju7QKWsfoEPHooMqUzEDWFRJZgiKvHv1EzcxEBTuEsSH/M4R
|
||||
ya+mbbsBxDFGRQrdvu1eIoRnCmoxALfpn8dD+xrBoUdnS9e/jG3FjTDZjIyqcW/MUSj0Vd0RH8zA1mv/
|
||||
/8lUbVM5HGZEEkMpzc1pUrDabXCyBzCu5FdSzxEySx9HcFq1yMmBFUFSJY+TNMdgFYYf4Q4VZQzVruie
|
||||
eLKaK0NCesUJulK71JbOnnQk/sVq2c1uRE2POzGsZUjWdl53cde9ZfDl8eClr+VdvsLGJAUD5mvJvMOF
|
||||
FxOpl797XbmF14iixOdHY1hme76tO+1mug9dHTtHXLlLM/+WN3QMnyfkcvK3B5e4bXo5ffp4by7NdwAA
|
||||
AP//XlvgsQYDAAA=
|
||||
`,
|
||||
},
|
||||
|
||||
"/content-descriptor.json": {
|
||||
local: "content-descriptor.json",
|
||||
size: 956,
|
||||
modtime: 1485388791,
|
||||
compressed: `
|
||||
H4sIAAAJbogA/5STP2/bMBDFd3+KgxIgSxx1CDoYQZZ279BuRQeaPEmXmn96PCNVC3/3HkUpttsigRfD
|
||||
fHyP9zvy9HsF0DjMlikJxdBsoPmUMHyIQQwFZCj/MAh8nE2R4XNCSx1ZMyVuyxHX2Q7oTYkPImnTtk85
|
||||
hnVV7yL3rWPTyfrdfVu1q5ojt0SyZqJWtkvlPMWqu3Uv1WtOxoQlGbdPaKVqiTXPQph1pzSmmkdH5ks1
|
||||
V+nffmVAmHzlUIgdFIGxQ1YadHBSY4pf617JOezymrzp8a40e6WQHQUqx+b2WHiKHWq6yfTrLZRiAQqw
|
||||
HQXzhTj/AaEg7+/PIRz1mOUNDMujXnfPJg1kQV/Bfs97DzW7YFWW24JblsmIIAe4eRhMHh43DwP+NE6H
|
||||
xZvdnHy8ufAiZ9izBva8y6/gG9hRZSxG6Dh6eNYuBoWkPEODNyNsEVx8DruolO4ItkyXYTbjUSZBf1r3
|
||||
xJmFKfQvVt3pIntTLllpqZn1w2r5nVppGH/sibF8BF//HtjTiTl/OF18Wx1WfwIAAP//z1UgVLwDAAA=
|
||||
`,
|
||||
},
|
||||
|
||||
"/defs-config.json": {
|
||||
local: "defs-config.json",
|
||||
size: 2236,
|
||||
modtime: 1485388791,
|
||||
compressed: `
|
||||
H4sIAAAJbogA/+RVzY7TMBC+5yksw7Gwd6673JCKVAEHhCo3Ge/OEnvMeIKI0L47TrZ0888qpac9VG0m
|
||||
/n7m89j9nSmlC4g5YxAkr98pfQMWPTZPUQXDgnlVGlZCahvAX5MXgx5YpV8Wb9UuQI4Wc9PiN4+EJ4ZE
|
||||
2GikYt4uPz2nitQBGkE63EMuLbStB6YASRdiZ3Wqf4rAvUqHIwqjv9WnVw+bJ9z7X4EiFB+JJQ7xrxls
|
||||
g0+W49v7SP7VVcf9lTNh1zJvHz1O8/ufc7YMs6n1pvsKBdzQxkIjSWpGVLgOhF6G2uRh2/T0tSfQl1u0
|
||||
uGDzH1b7dgeWF134qiz7TF2eb5MRXLvixfb+mcrKwWicn9n/2qm/dFdfiL8n2Rtcdc4/mAOUl45kN7Hx
|
||||
/z2SrPt9ZNdMJDaec4EWaO0ei1FEl7+tjuuXtrQnC75yoz3TpamBo17O7JQCw48KGYoez1MGQ3dZl/Fv
|
||||
5ncYhbg+J/ScwQiMbqql7i2xM9JOY4K+EXQwPfGmkjtadVaOrvaHehWanIPxP89zoOCC1Pt2J+fgB6IS
|
||||
jNdz5yFrPg/ZnwAAAP//3oH4m7wIAAA=
|
||||
`,
|
||||
},
|
||||
|
||||
"/defs-image.json": {
|
||||
local: "defs-image.json",
|
||||
size: 2916,
|
||||
modtime: 1485388791,
|
||||
compressed: `
|
||||
H4sIAAAJbogA/8SWTU/bTBDH7/kU+xj0cMiLaVUhNUJIVbn0xIGeigIa1uN4qL3r7m6gIfJ3767t+D3Q
|
||||
lKg9wc6u//P/zc443owY8wLUXFFqSApvzrxLDEmQW2mWgjLEVzEoZiS7SlF8lsIACVTsSwJLZNcpcgqJ
|
||||
Q/74pNCrBKyeS2GDCQYEX9cpViEbpMAljIxJ9dz3pZXnW3k9k2rpax5hAj65VH4tMdkKmELQ00aRWNbx
|
||||
FIxBlePc3nyafoPp8+n046L+97+j4/+nt3ez8WJzOnn3/izzf+/YsZenyIpMXkBL1KaJ1CmmiZBxtU6N
|
||||
XCpII+LMEvHvepWw4lkmQ+YOyfsH5GbCSOTLEoCdnEego4v5eYQ/IbClTiAun7w42bMOBdLdeDZdjOd2
|
||||
FTrWcYcoAUGhVb8sOaR6w4X1tXqOC+46rvDHihS6PDdlrNU9kzqo6bm1Li+jEUljMKFUiVeGFnVhlDVv
|
||||
ext1A29Hm+661/ys49jeocIQlS0JBqyDlUsc23337JHfmJBGV1dnsy7k617cMdc792uDek8/1o2ePWgp
|
||||
2sZImLMPw6Z6bf/vWv+FypYuBwlWKtav+AcWU2HSHWahkgl7shiRdUm6dM0SWLN7ZIF8ErG0NoO2s22b
|
||||
g1Kwbm+RwaTrYfcol7uum8FV3hKQ19jLBjGrAeig7jfHlcog2lBrDU5xvgOKR5acm5XCLpzUTaJFS3HH
|
||||
wPY1u7t/TOu/YLV/T63trA92OFtW7I1mZo82Q9HlhzNVib7VXIjgKn7YktWqO+31R7RIOTimr4I1J3II
|
||||
9BEUgei+Q/eu10vF+ktgwy+hUfPv9uMChJAG2l+Ge99qU6T6PZcCr8LW22azx09dAul1jnrdAW6UezP0
|
||||
7hOrOPZ60IvRdpWNstGvAAAA//83ffekZAsAAA==
|
||||
`,
|
||||
},
|
||||
|
||||
"/defs.json": {
|
||||
local: "defs.json",
|
||||
size: 3193,
|
||||
modtime: 1485388791,
|
||||
compressed: `
|
||||
H4sIAAAJbogA/7RWTXPaMBC98ys8tEfa2PIX9NYp/cghAzOZnjo9uGYBtSCpstxpmuG/VzLGWPZiMKWH
|
||||
JPau9r23T6tYzwPHGS4gSyUVinI2fOMMp7CkjJq3zMkzWDhqLXm+WvNc6UdwZgLYO85UQhlI51FASpc0
|
||||
TYry0R6vAtB4hkIHKVPj6k2/qycBhk3HYQWyqCwSW127zbc698oj42M4+V2GPRIXwd2oQvaivtA+iSMM
|
||||
3MRb8D7pC0+8IA7GfhRgHFWyRRQFfYkmhPh+TFw/GodBHEeu6yKMyCqLOr9idzAeEoYt3N57gwFHYei3
|
||||
oXvvCwYdkEkwiWIyaeP33g4M3xsHQRQHgRv7sTsJQ4KZ70VzbnBlnZAzmC114EsZcKpUkX4pwWSHL+5q
|
||||
B+6utLxauBvh1YduWL7Z1FaXT18RL26pUDt7U4WZkpSt+is8cOzrb6tpm4jHAnb/Gxsl/u07pOo4SSJR
|
||||
Wj+bSy5AKgpZrUinXz97o50V6mphUP/bEjXbU/9nUSXWGVGf76d1IafHRh94q/DjtYVvpUyeZktdn2EW
|
||||
JCZ9dIAq2Da6xqmMHrTDkm9v/ZWU+EbbPB/oBuaJWmMM9brD+vfs13kDG+ItgE+c/6gjiBNTImxRHWxV
|
||||
C9hh1DatsstwMNVNNLDa7wAzPp0Z4pLPGHLTmSocRhnvpw+JEI1/Lac2YM0zZZ2WDsr6iWlalh5ufrcA
|
||||
y+gfuBIFdeSB50xd4kbGc5leSN09kPryrChLysvzP8NxYd+bO6EuGfFy/Pp9MqoplfAzpxIW1hfU6nnU
|
||||
MrVJbn8cB+ZnN/gbAAD//0JyEpx5DAAA
|
||||
`,
|
||||
},
|
||||
|
||||
"/image-layout-schema.json": {
|
||||
local: "image-layout-schema.json",
|
||||
size: 414,
|
||||
modtime: 1485388791,
|
||||
compressed: `
|
||||
H4sIAAAJbogA/2yPsU7EMAyG9z6FFRhpUySmW286CekGJBbEEFpfmxNNQuIinVDfHSduYbhbWvmPP3/2
|
||||
TwWgekxdtIGsd2oH6hjQ7b0jYx1GOExmQHg2Fz8TvHQjTkY9ZOo+ScHESBR2Wp+Td7WkjY+D7qM5Ud0+
|
||||
acnuhLP9hiRmPMu6TZYKJt3aZnH9WcRC0iVgZv3HGbs1C5EnRLKY+CVfkw2ZlI1feaicJW/X135LB/gT
|
||||
0Ihw3B/gyly4zZ4oWjf85+jmifO3tebksWmbVq31e/kv/F3KwhG/Zhux/0NurVtlbql+AwAA//8bwMuB
|
||||
ngEAAA==
|
||||
`,
|
||||
},
|
||||
|
||||
"/image-manifest-schema.json": {
|
||||
local: "image-manifest-schema.json",
|
||||
size: 921,
|
||||
modtime: 1485389045,
|
||||
compressed: `
|
||||
H4sIAAAJbogA/5ySMU/rMBSF9/yKq7RjU7/39KauTB0QA4gFMZjkJrlVbQfbRVSo/51ru6Y1ZUAdc+xz
|
||||
7ndP/FEB1B261tLkyeh6BfXdhPrGaC9Jo4W1kgPCrdTUo/NwP2FLPbUy3l4E+9y1IyoZrKP300qIjTO6
|
||||
SerS2EF0Vva++fNfJG2WfNRli2OP4altnuqiLd0WFAiEOhIkr99PGNzmZYPtUZssZ1hP6PgkLMZainjk
|
||||
xLRcki93fhjJQU+47cClDdGBHxHicMjDIeXBWwoE6UBqIO1xQBspYvh1m4kS9ist73oxRpEmtVN89u+k
|
||||
yfesRemQTmoG6Gk4b2BusQ+xAQ21b3Ijxi7D/6sL+1buGevcnqmktXJfMK09qnD176mPo5LNv53O8wsK
|
||||
qbXx8eUVKNfV3WyJOz+PXHyvpsPeNdEVoWaCBe483i6cVWaNpLXF1x1ZDFhPP73D8p+UFfPHc3WoPgMA
|
||||
AP//UcoRdpkDAAA=
|
||||
`,
|
||||
},
|
||||
|
||||
"/manifest-list-schema.json": {
|
||||
local: "manifest-list-schema.json",
|
||||
size: 873,
|
||||
modtime: 1485389045,
|
||||
compressed: `
|
||||
H4sIAAAJbogA/6SSv0/7MBDF9/wVp/Q7flMjxNQVFiQQA4gFMZjk0lzV2OHORVSo/zv+EZdEZUDqUqnP
|
||||
fu8+7+KvAqBsUGqmwZE15QrKhwHNtTVOk0GG216vEe61oRbFwR35n8cBa2qp1tHyP2T8k7rDXgd/59yw
|
||||
Umoj1lRJXVpeq4Z166qLK5W0RfJRky3iPdaPrvNoibZ0W1HAUP2IUW09Rgpw+wFDhH3bYD1qA/sgdoTi
|
||||
T0JFr6WcZx+baib5tP1TRwIt4bYBSTVRwHUIkQBmBJBC4SOlghbQBsg4XCNHlDjhjI5qjn2MzK1PZvVk
|
||||
qN/1/uzyR9OfWYvSIZ2UeZJM15GTNbPeTzo47Kf3widnbMPNBlupIvsyfPOF8oKnCAuVY5ubccuWyzHh
|
||||
MGPRxlgX39OM5pzVTSOPPf4EPXUWmTWSlozvO2IMWC+/PayT1fr/r8Wh+A4AAP//b2/SMmkDAAA=
|
||||
`,
|
||||
},
|
||||
|
||||
"/": {
|
||||
isDir: true,
|
||||
local: "/",
|
||||
},
|
||||
}
|
21
vendor/github.com/opencontainers/image-spec/schema/gen.go
generated
vendored
Normal file
21
vendor/github.com/opencontainers/image-spec/schema/gen.go
generated
vendored
Normal file
@@ -0,0 +1,21 @@
|
||||
// Copyright 2016 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
|
||||
|
||||
// Generates an embbedded http.FileSystem for all schema files
|
||||
// using esc (https://github.com/mjibson/esc).
|
||||
|
||||
// This should generally be invoked with `make schema-fs`
|
||||
//go:generate esc -private -pkg=schema -ignore=.*go .
|
50
vendor/github.com/opencontainers/image-spec/schema/schema.go
generated
vendored
Normal file
50
vendor/github.com/opencontainers/image-spec/schema/schema.go
generated
vendored
Normal file
@@ -0,0 +1,50 @@
|
||||
// Copyright 2016 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 (
|
||||
"net/http"
|
||||
|
||||
"github.com/opencontainers/image-spec/specs-go/v1"
|
||||
)
|
||||
|
||||
// Media types for the OCI image formats
|
||||
const (
|
||||
MediaTypeDescriptor Validator = v1.MediaTypeDescriptor
|
||||
MediaTypeManifest Validator = v1.MediaTypeImageManifest
|
||||
MediaTypeManifestList Validator = v1.MediaTypeImageManifestList
|
||||
MediaTypeImageConfig Validator = v1.MediaTypeImageConfig
|
||||
MediaTypeImageLayer unimplemented = v1.MediaTypeImageLayer
|
||||
)
|
||||
|
||||
var (
|
||||
// fs stores the embedded http.FileSystem
|
||||
// having the OCI JSON schema files in root "/".
|
||||
fs = _escFS(false)
|
||||
|
||||
// specs maps OCI schema media types to schema files.
|
||||
specs = map[Validator]string{
|
||||
MediaTypeDescriptor: "content-descriptor.json",
|
||||
MediaTypeManifest: "image-manifest-schema.json",
|
||||
MediaTypeManifestList: "manifest-list-schema.json",
|
||||
MediaTypeImageConfig: "config-schema.json",
|
||||
}
|
||||
)
|
||||
|
||||
// FileSystem returns an in-memory file system including the schema files.
|
||||
// The schema files are located at the root directory.
|
||||
func FileSystem() http.FileSystem {
|
||||
return fs
|
||||
}
|
119
vendor/github.com/opencontainers/image-spec/schema/validator.go
generated
vendored
Normal file
119
vendor/github.com/opencontainers/image-spec/schema/validator.go
generated
vendored
Normal file
@@ -0,0 +1,119 @@
|
||||
// Copyright 2016 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"
|
||||
"io/ioutil"
|
||||
|
||||
"github.com/opencontainers/image-spec/specs-go/v1"
|
||||
"github.com/pkg/errors"
|
||||
"github.com/xeipuuv/gojsonschema"
|
||||
)
|
||||
|
||||
// Validator wraps a media type string identifier
|
||||
// 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
|
||||
}
|
||||
|
||||
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)
|
||||
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))
|
||||
|
||||
result, err := gojsonschema.Validate(sl, ml)
|
||||
if err != nil {
|
||||
return errors.Wrapf(
|
||||
WrapSyntaxError(bytes.NewReader(buf), err),
|
||||
"schema %s: unable to validate", v)
|
||||
}
|
||||
|
||||
if result.Valid() {
|
||||
return nil
|
||||
}
|
||||
|
||||
errs := make([]error, 0, len(result.Errors()))
|
||||
for _, desc := range result.Errors() {
|
||||
errs = append(errs, fmt.Errorf("%s", desc))
|
||||
}
|
||||
|
||||
return ValidationError{
|
||||
Errs: errs,
|
||||
}
|
||||
}
|
||||
|
||||
type unimplemented string
|
||||
|
||||
func (v unimplemented) Validate(src io.Reader) error {
|
||||
return fmt.Errorf("%s: unimplemented", v)
|
||||
}
|
||||
|
||||
func validateManifestDescendants(r io.Reader) error {
|
||||
header := v1.Manifest{}
|
||||
|
||||
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 format mismatch")
|
||||
}
|
||||
|
||||
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)
|
||||
}
|
||||
|
||||
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 nil
|
||||
}
|
2
vendor/github.com/opencontainers/image-spec/specs-go/version.go
generated
vendored
2
vendor/github.com/opencontainers/image-spec/specs-go/version.go
generated
vendored
@@ -25,7 +25,7 @@ const (
|
||||
VersionPatch = 0
|
||||
|
||||
// VersionDev indicates development branch. Releases will be empty string.
|
||||
VersionDev = "-rc3-dev"
|
||||
VersionDev = "-rc4"
|
||||
)
|
||||
|
||||
// Version is the specification version that the package types support.
|
||||
|
201
vendor/github.com/opencontainers/image-tools/LICENSE
generated
vendored
Normal file
201
vendor/github.com/opencontainers/image-tools/LICENSE
generated
vendored
Normal file
@@ -0,0 +1,201 @@
|
||||
Apache License
|
||||
Version 2.0, January 2004
|
||||
http://www.apache.org/licenses/
|
||||
|
||||
TERMS AND CONDITIONS FOR USE, REPRODUCTION, AND DISTRIBUTION
|
||||
|
||||
1. Definitions.
|
||||
|
||||
"License" shall mean the terms and conditions for use, reproduction,
|
||||
and distribution as defined by Sections 1 through 9 of this document.
|
||||
|
||||
"Licensor" shall mean the copyright owner or entity authorized by
|
||||
the copyright owner that is granting the License.
|
||||
|
||||
"Legal Entity" shall mean the union of the acting entity and all
|
||||
other entities that control, are controlled by, or are under common
|
||||
control with that entity. For the purposes of this definition,
|
||||
"control" means (i) the power, direct or indirect, to cause the
|
||||
direction or management of such entity, whether by contract or
|
||||
otherwise, or (ii) ownership of fifty percent (50%) or more of the
|
||||
outstanding shares, or (iii) beneficial ownership of such entity.
|
||||
|
||||
"You" (or "Your") shall mean an individual or Legal Entity
|
||||
exercising permissions granted by this License.
|
||||
|
||||
"Source" form shall mean the preferred form for making modifications,
|
||||
including but not limited to software source code, documentation
|
||||
source, and configuration files.
|
||||
|
||||
"Object" form shall mean any form resulting from mechanical
|
||||
transformation or translation of a Source form, including but
|
||||
not limited to compiled object code, generated documentation,
|
||||
and conversions to other media types.
|
||||
|
||||
"Work" shall mean the work of authorship, whether in Source or
|
||||
Object form, made available under the License, as indicated by a
|
||||
copyright notice that is included in or attached to the work
|
||||
(an example is provided in the Appendix below).
|
||||
|
||||
"Derivative Works" shall mean any work, whether in Source or Object
|
||||
form, that is based on (or derived from) the Work and for which the
|
||||
editorial revisions, annotations, elaborations, or other modifications
|
||||
represent, as a whole, an original work of authorship. For the purposes
|
||||
of this License, Derivative Works shall not include works that remain
|
||||
separable from, or merely link (or bind by name) to the interfaces of,
|
||||
the Work and Derivative Works thereof.
|
||||
|
||||
"Contribution" shall mean any work of authorship, including
|
||||
the original version of the Work and any modifications or additions
|
||||
to that Work or Derivative Works thereof, that is intentionally
|
||||
submitted to Licensor for inclusion in the Work by the copyright owner
|
||||
or by an individual or Legal Entity authorized to submit on behalf of
|
||||
the copyright owner. For the purposes of this definition, "submitted"
|
||||
means any form of electronic, verbal, or written communication sent
|
||||
to the Licensor or its representatives, including but not limited to
|
||||
communication on electronic mailing lists, source code control systems,
|
||||
and issue tracking systems that are managed by, or on behalf of, the
|
||||
Licensor for the purpose of discussing and improving the Work, but
|
||||
excluding communication that is conspicuously marked or otherwise
|
||||
designated in writing by the copyright owner as "Not a Contribution."
|
||||
|
||||
"Contributor" shall mean Licensor and any individual or Legal Entity
|
||||
on behalf of whom a Contribution has been received by Licensor and
|
||||
subsequently incorporated within the Work.
|
||||
|
||||
2. Grant of Copyright License. Subject to the terms and conditions of
|
||||
this License, each Contributor hereby grants to You a perpetual,
|
||||
worldwide, non-exclusive, no-charge, royalty-free, irrevocable
|
||||
copyright license to reproduce, prepare Derivative Works of,
|
||||
publicly display, publicly perform, sublicense, and distribute the
|
||||
Work and such Derivative Works in Source or Object form.
|
||||
|
||||
3. Grant of Patent License. Subject to the terms and conditions of
|
||||
this License, each Contributor hereby grants to You a perpetual,
|
||||
worldwide, non-exclusive, no-charge, royalty-free, irrevocable
|
||||
(except as stated in this section) patent license to make, have made,
|
||||
use, offer to sell, sell, import, and otherwise transfer the Work,
|
||||
where such license applies only to those patent claims licensable
|
||||
by such Contributor that are necessarily infringed by their
|
||||
Contribution(s) alone or by combination of their Contribution(s)
|
||||
with the Work to which such Contribution(s) was submitted. If You
|
||||
institute patent litigation against any entity (including a
|
||||
cross-claim or counterclaim in a lawsuit) alleging that the Work
|
||||
or a Contribution incorporated within the Work constitutes direct
|
||||
or contributory patent infringement, then any patent licenses
|
||||
granted to You under this License for that Work shall terminate
|
||||
as of the date such litigation is filed.
|
||||
|
||||
4. Redistribution. You may reproduce and distribute copies of the
|
||||
Work or Derivative Works thereof in any medium, with or without
|
||||
modifications, and in Source or Object form, provided that You
|
||||
meet the following conditions:
|
||||
|
||||
(a) You must give any other recipients of the Work or
|
||||
Derivative Works a copy of this License; and
|
||||
|
||||
(b) You must cause any modified files to carry prominent notices
|
||||
stating that You changed the files; and
|
||||
|
||||
(c) You must retain, in the Source form of any Derivative Works
|
||||
that You distribute, all copyright, patent, trademark, and
|
||||
attribution notices from the Source form of the Work,
|
||||
excluding those notices that do not pertain to any part of
|
||||
the Derivative Works; and
|
||||
|
||||
(d) If the Work includes a "NOTICE" text file as part of its
|
||||
distribution, then any Derivative Works that You distribute must
|
||||
include a readable copy of the attribution notices contained
|
||||
within such NOTICE file, excluding those notices that do not
|
||||
pertain to any part of the Derivative Works, in at least one
|
||||
of the following places: within a NOTICE text file distributed
|
||||
as part of the Derivative Works; within the Source form or
|
||||
documentation, if provided along with the Derivative Works; or,
|
||||
within a display generated by the Derivative Works, if and
|
||||
wherever such third-party notices normally appear. The contents
|
||||
of the NOTICE file are for informational purposes only and
|
||||
do not modify the License. You may add Your own attribution
|
||||
notices within Derivative Works that You distribute, alongside
|
||||
or as an addendum to the NOTICE text from the Work, provided
|
||||
that such additional attribution notices cannot be construed
|
||||
as modifying the License.
|
||||
|
||||
You may add Your own copyright statement to Your modifications and
|
||||
may provide additional or different license terms and conditions
|
||||
for use, reproduction, or distribution of Your modifications, or
|
||||
for any such Derivative Works as a whole, provided Your use,
|
||||
reproduction, and distribution of the Work otherwise complies with
|
||||
the conditions stated in this License.
|
||||
|
||||
5. Submission of Contributions. Unless You explicitly state otherwise,
|
||||
any Contribution intentionally submitted for inclusion in the Work
|
||||
by You to the Licensor shall be under the terms and conditions of
|
||||
this License, without any additional terms or conditions.
|
||||
Notwithstanding the above, nothing herein shall supersede or modify
|
||||
the terms of any separate license agreement you may have executed
|
||||
with Licensor regarding such Contributions.
|
||||
|
||||
6. Trademarks. This License does not grant permission to use the trade
|
||||
names, trademarks, service marks, or product names of the Licensor,
|
||||
except as required for reasonable and customary use in describing the
|
||||
origin of the Work and reproducing the content of the NOTICE file.
|
||||
|
||||
7. Disclaimer of Warranty. Unless required by applicable law or
|
||||
agreed to in writing, Licensor provides the Work (and each
|
||||
Contributor provides its Contributions) on an "AS IS" BASIS,
|
||||
WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or
|
||||
implied, including, without limitation, any warranties or conditions
|
||||
of TITLE, NON-INFRINGEMENT, MERCHANTABILITY, or FITNESS FOR A
|
||||
PARTICULAR PURPOSE. You are solely responsible for determining the
|
||||
appropriateness of using or redistributing the Work and assume any
|
||||
risks associated with Your exercise of permissions under this License.
|
||||
|
||||
8. Limitation of Liability. In no event and under no legal theory,
|
||||
whether in tort (including negligence), contract, or otherwise,
|
||||
unless required by applicable law (such as deliberate and grossly
|
||||
negligent acts) or agreed to in writing, shall any Contributor be
|
||||
liable to You for damages, including any direct, indirect, special,
|
||||
incidental, or consequential damages of any character arising as a
|
||||
result of this License or out of the use or inability to use the
|
||||
Work (including but not limited to damages for loss of goodwill,
|
||||
work stoppage, computer failure or malfunction, or any and all
|
||||
other commercial damages or losses), even if such Contributor
|
||||
has been advised of the possibility of such damages.
|
||||
|
||||
9. Accepting Warranty or Additional Liability. While redistributing
|
||||
the Work or Derivative Works thereof, You may choose to offer,
|
||||
and charge a fee for, acceptance of support, warranty, indemnity,
|
||||
or other liability obligations and/or rights consistent with this
|
||||
License. However, in accepting such obligations, You may act only
|
||||
on Your own behalf and on Your sole responsibility, not on behalf
|
||||
of any other Contributor, and only if You agree to indemnify,
|
||||
defend, and hold each Contributor harmless for any liability
|
||||
incurred by, or claims asserted against, such Contributor by reason
|
||||
of your accepting any such warranty or additional liability.
|
||||
|
||||
END OF TERMS AND CONDITIONS
|
||||
|
||||
APPENDIX: How to apply the Apache License to your work.
|
||||
|
||||
To apply the Apache License to your work, attach the following
|
||||
boilerplate notice, with the fields enclosed by brackets "{}"
|
||||
replaced with your own identifying information. (Don't include
|
||||
the brackets!) The text should be enclosed in the appropriate
|
||||
comment syntax for the file format. We also recommend that a
|
||||
file or class name and description of purpose be included on the
|
||||
same "printed page" as the copyright notice for easier
|
||||
identification within third-party archives.
|
||||
|
||||
Copyright {yyyy} {name of copyright owner}
|
||||
|
||||
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.
|
109
vendor/github.com/opencontainers/image-tools/image/autodetect.go
generated
vendored
Normal file
109
vendor/github.com/opencontainers/image-tools/image/autodetect.go
generated
vendored
Normal file
@@ -0,0 +1,109 @@
|
||||
// Copyright 2016 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 image
|
||||
|
||||
import (
|
||||
"encoding/json"
|
||||
"io"
|
||||
"io/ioutil"
|
||||
"net/http"
|
||||
"os"
|
||||
|
||||
"github.com/opencontainers/image-spec/schema"
|
||||
"github.com/pkg/errors"
|
||||
)
|
||||
|
||||
// supported autodetection types
|
||||
const (
|
||||
TypeImageLayout = "imageLayout"
|
||||
TypeImage = "image"
|
||||
TypeManifest = "manifest"
|
||||
TypeManifestList = "manifestList"
|
||||
TypeConfig = "config"
|
||||
)
|
||||
|
||||
// Autodetect detects the validation type for the given path
|
||||
// or an error if the validation type could not be resolved.
|
||||
func Autodetect(path string) (string, error) {
|
||||
fi, err := os.Stat(path)
|
||||
if err != nil {
|
||||
return "", errors.Wrapf(err, "unable to access path") // err from os.Stat includes path name
|
||||
}
|
||||
|
||||
if fi.IsDir() {
|
||||
return TypeImageLayout, nil
|
||||
}
|
||||
|
||||
f, err := os.Open(path)
|
||||
if err != nil {
|
||||
return "", errors.Wrap(err, "unable to open file") // os.Open includes the filename
|
||||
}
|
||||
defer f.Close()
|
||||
|
||||
buf, err := ioutil.ReadAll(io.LimitReader(f, 512)) // read some initial bytes to detect content
|
||||
if err != nil {
|
||||
return "", errors.Wrap(err, "unable to read")
|
||||
}
|
||||
|
||||
mimeType := http.DetectContentType(buf)
|
||||
|
||||
switch mimeType {
|
||||
case "application/x-gzip", "application/octet-stream":
|
||||
return TypeImage, nil
|
||||
|
||||
case "text/plain; charset=utf-8":
|
||||
// might be a JSON file, will be handled below
|
||||
|
||||
default:
|
||||
return "", errors.New("unknown file type")
|
||||
}
|
||||
|
||||
if _, err := f.Seek(0, os.SEEK_SET); err != nil {
|
||||
return "", errors.Wrap(err, "unable to seek")
|
||||
}
|
||||
|
||||
header := struct {
|
||||
SchemaVersion int `json:"schemaVersion"`
|
||||
MediaType string `json:"mediaType"`
|
||||
Config interface{} `json:"config"`
|
||||
}{}
|
||||
|
||||
if err := json.NewDecoder(f).Decode(&header); err != nil {
|
||||
if _, errSeek := f.Seek(0, os.SEEK_SET); errSeek != nil {
|
||||
return "", errors.Wrap(err, "unable to seek")
|
||||
}
|
||||
|
||||
e := errors.Wrap(
|
||||
schema.WrapSyntaxError(f, err),
|
||||
"unable to parse JSON",
|
||||
)
|
||||
|
||||
return "", e
|
||||
}
|
||||
|
||||
switch {
|
||||
case header.MediaType == string(schema.MediaTypeManifest):
|
||||
return TypeManifest, nil
|
||||
|
||||
case header.MediaType == string(schema.MediaTypeManifestList):
|
||||
return TypeManifestList, nil
|
||||
|
||||
case header.MediaType == "" && header.SchemaVersion == 0 && header.Config != nil:
|
||||
// config files don't have mediaType/schemaVersion header
|
||||
return TypeConfig, nil
|
||||
}
|
||||
|
||||
return "", errors.New("unknown media type")
|
||||
}
|
126
vendor/github.com/opencontainers/image-tools/image/config.go
generated
vendored
Normal file
126
vendor/github.com/opencontainers/image-tools/image/config.go
generated
vendored
Normal file
@@ -0,0 +1,126 @@
|
||||
// Copyright 2016 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 image
|
||||
|
||||
import (
|
||||
"bytes"
|
||||
"encoding/json"
|
||||
"fmt"
|
||||
"io"
|
||||
"io/ioutil"
|
||||
"os"
|
||||
"path/filepath"
|
||||
"strconv"
|
||||
"strings"
|
||||
|
||||
"github.com/opencontainers/image-spec/schema"
|
||||
"github.com/opencontainers/image-spec/specs-go/v1"
|
||||
"github.com/opencontainers/runtime-spec/specs-go"
|
||||
"github.com/pkg/errors"
|
||||
)
|
||||
|
||||
type config v1.Image
|
||||
|
||||
func findConfig(w walker, d *descriptor) (*config, error) {
|
||||
var c config
|
||||
cpath := filepath.Join("blobs", d.algo(), d.hash())
|
||||
|
||||
switch err := w.walk(func(path string, info os.FileInfo, r io.Reader) error {
|
||||
if info.IsDir() || filepath.Clean(path) != cpath {
|
||||
return nil
|
||||
}
|
||||
buf, err := ioutil.ReadAll(r)
|
||||
if err != nil {
|
||||
return errors.Wrapf(err, "%s: error reading config", path)
|
||||
}
|
||||
|
||||
if err := schema.MediaTypeImageConfig.Validate(bytes.NewReader(buf)); err != nil {
|
||||
return errors.Wrapf(err, "%s: config validation failed", path)
|
||||
}
|
||||
|
||||
if err := json.Unmarshal(buf, &c); err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
return errEOW
|
||||
}); err {
|
||||
case nil:
|
||||
return nil, fmt.Errorf("%s: config not found", cpath)
|
||||
case errEOW:
|
||||
return &c, nil
|
||||
default:
|
||||
return nil, err
|
||||
}
|
||||
}
|
||||
|
||||
func (c *config) runtimeSpec(rootfs string) (*specs.Spec, error) {
|
||||
if c.OS != "linux" {
|
||||
return nil, fmt.Errorf("%s: unsupported OS", c.OS)
|
||||
}
|
||||
|
||||
var s specs.Spec
|
||||
s.Version = specs.Version
|
||||
// we should at least apply the default spec, otherwise this is totally useless
|
||||
s.Process.Terminal = true
|
||||
s.Root.Path = rootfs
|
||||
s.Process.Cwd = "/"
|
||||
if c.Config.WorkingDir != "" {
|
||||
s.Process.Cwd = c.Config.WorkingDir
|
||||
}
|
||||
s.Process.Env = append(s.Process.Env, c.Config.Env...)
|
||||
s.Process.Args = append(s.Process.Args, c.Config.Entrypoint...)
|
||||
s.Process.Args = append(s.Process.Args, c.Config.Cmd...)
|
||||
|
||||
if len(s.Process.Args) == 0 {
|
||||
s.Process.Args = append(s.Process.Args, "sh")
|
||||
}
|
||||
|
||||
if uid, err := strconv.Atoi(c.Config.User); err == nil {
|
||||
s.Process.User.UID = uint32(uid)
|
||||
} else if ug := strings.Split(c.Config.User, ":"); len(ug) == 2 {
|
||||
uid, err := strconv.Atoi(ug[0])
|
||||
if err != nil {
|
||||
return nil, errors.New("config.User: unsupported uid format")
|
||||
}
|
||||
|
||||
gid, err := strconv.Atoi(ug[1])
|
||||
if err != nil {
|
||||
return nil, errors.New("config.User: unsupported gid format")
|
||||
}
|
||||
|
||||
s.Process.User.UID = uint32(uid)
|
||||
s.Process.User.GID = uint32(gid)
|
||||
} else if c.Config.User != "" {
|
||||
return nil, errors.New("config.User: unsupported format")
|
||||
}
|
||||
|
||||
s.Platform.OS = c.OS
|
||||
s.Platform.Arch = c.Architecture
|
||||
|
||||
s.Linux = &specs.Linux{}
|
||||
|
||||
for vol := range c.Config.Volumes {
|
||||
s.Mounts = append(
|
||||
s.Mounts,
|
||||
specs.Mount{
|
||||
Destination: vol,
|
||||
Type: "bind",
|
||||
Options: []string{"rbind"},
|
||||
},
|
||||
)
|
||||
}
|
||||
|
||||
return &s, nil
|
||||
}
|
138
vendor/github.com/opencontainers/image-tools/image/descriptor.go
generated
vendored
Normal file
138
vendor/github.com/opencontainers/image-tools/image/descriptor.go
generated
vendored
Normal file
@@ -0,0 +1,138 @@
|
||||
// Copyright 2016 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 image
|
||||
|
||||
import (
|
||||
"encoding/json"
|
||||
"fmt"
|
||||
"io"
|
||||
"os"
|
||||
"path/filepath"
|
||||
"strings"
|
||||
|
||||
"github.com/opencontainers/go-digest"
|
||||
"github.com/pkg/errors"
|
||||
)
|
||||
|
||||
type descriptor struct {
|
||||
MediaType string `json:"mediaType"`
|
||||
Digest string `json:"digest"`
|
||||
Size int64 `json:"size"`
|
||||
}
|
||||
|
||||
func (d *descriptor) algo() string {
|
||||
pts := strings.SplitN(d.Digest, ":", 2)
|
||||
if len(pts) != 2 {
|
||||
return ""
|
||||
}
|
||||
return pts[0]
|
||||
}
|
||||
|
||||
func (d *descriptor) hash() string {
|
||||
pts := strings.SplitN(d.Digest, ":", 2)
|
||||
if len(pts) != 2 {
|
||||
return ""
|
||||
}
|
||||
return pts[1]
|
||||
}
|
||||
|
||||
func listReferences(w walker) (map[string]*descriptor, error) {
|
||||
refs := make(map[string]*descriptor)
|
||||
|
||||
if err := w.walk(func(path string, info os.FileInfo, r io.Reader) error {
|
||||
if info.IsDir() || !strings.HasPrefix(path, "refs") {
|
||||
return nil
|
||||
}
|
||||
|
||||
var d descriptor
|
||||
if err := json.NewDecoder(r).Decode(&d); err != nil {
|
||||
return err
|
||||
}
|
||||
refs[info.Name()] = &d
|
||||
|
||||
return nil
|
||||
}); err != nil {
|
||||
return nil, err
|
||||
}
|
||||
return refs, nil
|
||||
}
|
||||
|
||||
func findDescriptor(w walker, name string) (*descriptor, error) {
|
||||
var d descriptor
|
||||
dpath := filepath.Join("refs", name)
|
||||
|
||||
switch err := w.walk(func(path string, info os.FileInfo, r io.Reader) error {
|
||||
if info.IsDir() || filepath.Clean(path) != dpath {
|
||||
return nil
|
||||
}
|
||||
|
||||
if err := json.NewDecoder(r).Decode(&d); err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
return errEOW
|
||||
}); err {
|
||||
case nil:
|
||||
return nil, fmt.Errorf("%s: descriptor not found", dpath)
|
||||
case errEOW:
|
||||
return &d, nil
|
||||
default:
|
||||
return nil, err
|
||||
}
|
||||
}
|
||||
|
||||
func (d *descriptor) validate(w walker, mts []string) error {
|
||||
var found bool
|
||||
for _, mt := range mts {
|
||||
if d.MediaType == mt {
|
||||
found = true
|
||||
break
|
||||
}
|
||||
}
|
||||
if !found {
|
||||
return fmt.Errorf("invalid descriptor MediaType %q", d.MediaType)
|
||||
}
|
||||
|
||||
rc, err := w.Get(*d)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
defer rc.Close()
|
||||
|
||||
return d.validateContent(rc)
|
||||
}
|
||||
|
||||
func (d *descriptor) validateContent(r io.Reader) error {
|
||||
parsed, err := digest.Parse(d.Digest)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
verifier := parsed.Verifier()
|
||||
n, err := io.Copy(verifier, r)
|
||||
if err != nil {
|
||||
return errors.Wrap(err, "error generating hash")
|
||||
}
|
||||
|
||||
if n != d.Size {
|
||||
return errors.New("size mismatch")
|
||||
}
|
||||
|
||||
if !verifier.Verified() {
|
||||
return errors.New("digest mismatch")
|
||||
}
|
||||
|
||||
return nil
|
||||
}
|
16
vendor/github.com/opencontainers/image-tools/image/doc.go
generated
vendored
Normal file
16
vendor/github.com/opencontainers/image-tools/image/doc.go
generated
vendored
Normal file
@@ -0,0 +1,16 @@
|
||||
// Copyright 2016 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 image defines methods for validating, and unpacking OCI images.
|
||||
package image
|
210
vendor/github.com/opencontainers/image-tools/image/image.go
generated
vendored
Normal file
210
vendor/github.com/opencontainers/image-tools/image/image.go
generated
vendored
Normal file
@@ -0,0 +1,210 @@
|
||||
// Copyright 2016 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 image
|
||||
|
||||
import (
|
||||
"encoding/json"
|
||||
"fmt"
|
||||
"log"
|
||||
"os"
|
||||
"path/filepath"
|
||||
|
||||
"github.com/opencontainers/image-spec/specs-go/v1"
|
||||
"github.com/pkg/errors"
|
||||
)
|
||||
|
||||
// ValidateLayout walks through the given file tree and validates the manifest
|
||||
// pointed to by the given refs or returns an error if the validation failed.
|
||||
func ValidateLayout(src string, refs []string, out *log.Logger) error {
|
||||
return validate(newPathWalker(src), refs, out)
|
||||
}
|
||||
|
||||
// Validate walks through the given .tar file and validates the manifest
|
||||
// pointed to by the given refs or returns an error if the validation failed.
|
||||
func Validate(tarFile string, refs []string, out *log.Logger) error {
|
||||
f, err := os.Open(tarFile)
|
||||
if err != nil {
|
||||
return errors.Wrap(err, "unable to open file")
|
||||
}
|
||||
defer f.Close()
|
||||
|
||||
return validate(newTarWalker(tarFile, f), refs, out)
|
||||
}
|
||||
|
||||
var validRefMediaTypes = []string{
|
||||
v1.MediaTypeImageManifest,
|
||||
v1.MediaTypeImageManifestList,
|
||||
}
|
||||
|
||||
func validate(w walker, refs []string, out *log.Logger) error {
|
||||
ds, err := listReferences(w)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
if len(refs) == 0 && len(ds) == 0 {
|
||||
// TODO(runcom): ugly, we'll need a better way and library
|
||||
// to express log levels.
|
||||
// see https://github.com/opencontainers/image-spec/issues/288
|
||||
out.Print("WARNING: no descriptors found")
|
||||
}
|
||||
|
||||
if len(refs) == 0 {
|
||||
for ref := range ds {
|
||||
refs = append(refs, ref)
|
||||
}
|
||||
}
|
||||
|
||||
for _, ref := range refs {
|
||||
d, ok := ds[ref]
|
||||
if !ok {
|
||||
// TODO(runcom):
|
||||
// soften this error to a warning if the user didn't ask for any specific reference
|
||||
// with --ref but she's just validating the whole image.
|
||||
return fmt.Errorf("reference %s not found", ref)
|
||||
}
|
||||
|
||||
if err = d.validate(w, validRefMediaTypes); err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
m, err := findManifest(w, d)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
if err := m.validate(w); err != nil {
|
||||
return err
|
||||
}
|
||||
if out != nil {
|
||||
out.Printf("reference %q: OK", ref)
|
||||
}
|
||||
}
|
||||
return nil
|
||||
}
|
||||
|
||||
// UnpackLayout walks through the file tree given by src and, using the layers
|
||||
// specified in the manifest pointed to by the given ref, unpacks all layers in
|
||||
// the given destination directory or returns an error if the unpacking failed.
|
||||
func UnpackLayout(src, dest, ref string) error {
|
||||
return unpack(newPathWalker(src), dest, ref)
|
||||
}
|
||||
|
||||
// Unpack walks through the given .tar file and, using the layers specified in
|
||||
// the manifest pointed to by the given ref, unpacks all layers in the given
|
||||
// destination directory or returns an error if the unpacking failed.
|
||||
func Unpack(tarFile, dest, ref string) error {
|
||||
f, err := os.Open(tarFile)
|
||||
if err != nil {
|
||||
return errors.Wrap(err, "unable to open file")
|
||||
}
|
||||
defer f.Close()
|
||||
|
||||
return unpack(newTarWalker(tarFile, f), dest, ref)
|
||||
}
|
||||
|
||||
func unpack(w walker, dest, refName string) error {
|
||||
ref, err := findDescriptor(w, refName)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
if err = ref.validate(w, validRefMediaTypes); err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
m, err := findManifest(w, ref)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
if err = m.validate(w); err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
return m.unpack(w, dest)
|
||||
}
|
||||
|
||||
// CreateRuntimeBundleLayout walks through the file tree given by src and
|
||||
// creates an OCI runtime bundle in the given destination dest
|
||||
// or returns an error if the unpacking failed.
|
||||
func CreateRuntimeBundleLayout(src, dest, ref, root string) error {
|
||||
return createRuntimeBundle(newPathWalker(src), dest, ref, root)
|
||||
}
|
||||
|
||||
// CreateRuntimeBundle walks through the given .tar file and
|
||||
// creates an OCI runtime bundle in the given destination dest
|
||||
// or returns an error if the unpacking failed.
|
||||
func CreateRuntimeBundle(tarFile, dest, ref, root string) error {
|
||||
f, err := os.Open(tarFile)
|
||||
if err != nil {
|
||||
return errors.Wrap(err, "unable to open file")
|
||||
}
|
||||
defer f.Close()
|
||||
|
||||
return createRuntimeBundle(newTarWalker(tarFile, f), dest, ref, root)
|
||||
}
|
||||
|
||||
func createRuntimeBundle(w walker, dest, refName, rootfs string) error {
|
||||
ref, err := findDescriptor(w, refName)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
if err = ref.validate(w, validRefMediaTypes); err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
m, err := findManifest(w, ref)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
if err = m.validate(w); err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
c, err := findConfig(w, &m.Config)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
if _, err = os.Stat(dest); err != nil {
|
||||
if os.IsNotExist(err) {
|
||||
if err2 := os.MkdirAll(dest, 0755); err2 != nil {
|
||||
return err2
|
||||
}
|
||||
} else {
|
||||
return err
|
||||
}
|
||||
}
|
||||
|
||||
err = m.unpack(w, filepath.Join(dest, rootfs))
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
spec, err := c.runtimeSpec(rootfs)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
f, err := os.Create(filepath.Join(dest, "config.json"))
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
defer f.Close()
|
||||
|
||||
return json.NewEncoder(f).Encode(spec)
|
||||
}
|
258
vendor/github.com/opencontainers/image-tools/image/manifest.go
generated
vendored
Normal file
258
vendor/github.com/opencontainers/image-tools/image/manifest.go
generated
vendored
Normal file
@@ -0,0 +1,258 @@
|
||||
// Copyright 2016 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 image
|
||||
|
||||
import (
|
||||
"archive/tar"
|
||||
"bytes"
|
||||
"compress/gzip"
|
||||
"encoding/json"
|
||||
"fmt"
|
||||
"io"
|
||||
"io/ioutil"
|
||||
"os"
|
||||
"path/filepath"
|
||||
"strings"
|
||||
"time"
|
||||
|
||||
"github.com/opencontainers/image-spec/schema"
|
||||
"github.com/opencontainers/image-spec/specs-go/v1"
|
||||
"github.com/pkg/errors"
|
||||
)
|
||||
|
||||
type manifest struct {
|
||||
Config descriptor `json:"config"`
|
||||
Layers []descriptor `json:"layers"`
|
||||
}
|
||||
|
||||
func findManifest(w walker, d *descriptor) (*manifest, error) {
|
||||
var m manifest
|
||||
mpath := filepath.Join("blobs", d.algo(), d.hash())
|
||||
|
||||
switch err := w.walk(func(path string, info os.FileInfo, r io.Reader) error {
|
||||
if info.IsDir() || filepath.Clean(path) != mpath {
|
||||
return nil
|
||||
}
|
||||
|
||||
buf, err := ioutil.ReadAll(r)
|
||||
if err != nil {
|
||||
return errors.Wrapf(err, "%s: error reading manifest", path)
|
||||
}
|
||||
|
||||
if err := schema.MediaTypeManifest.Validate(bytes.NewReader(buf)); err != nil {
|
||||
return errors.Wrapf(err, "%s: manifest validation failed", path)
|
||||
}
|
||||
|
||||
if err := json.Unmarshal(buf, &m); err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
return errEOW
|
||||
}); err {
|
||||
case nil:
|
||||
return nil, fmt.Errorf("%s: manifest not found", mpath)
|
||||
case errEOW:
|
||||
return &m, nil
|
||||
default:
|
||||
return nil, err
|
||||
}
|
||||
}
|
||||
|
||||
func (m *manifest) validate(w walker) error {
|
||||
if err := m.Config.validate(w, []string{v1.MediaTypeImageConfig}); err != nil {
|
||||
return errors.Wrap(err, "config validation failed")
|
||||
}
|
||||
|
||||
validLayerMediaTypes := []string{
|
||||
v1.MediaTypeImageLayer,
|
||||
v1.MediaTypeImageLayerGzip,
|
||||
v1.MediaTypeImageLayerNonDistributable,
|
||||
v1.MediaTypeImageLayerNonDistributableGzip,
|
||||
}
|
||||
|
||||
for _, d := range m.Layers {
|
||||
if err := d.validate(w, validLayerMediaTypes); err != nil {
|
||||
return errors.Wrap(err, "layer validation failed")
|
||||
}
|
||||
}
|
||||
|
||||
return nil
|
||||
}
|
||||
|
||||
func (m *manifest) unpack(w walker, dest string) (retErr error) {
|
||||
// error out if the dest directory is not empty
|
||||
s, err := ioutil.ReadDir(dest)
|
||||
if err != nil && !os.IsNotExist(err) {
|
||||
return errors.Wrap(err, "unable to open file") // err contains dest
|
||||
}
|
||||
if len(s) > 0 {
|
||||
return fmt.Errorf("%s is not empty", dest)
|
||||
}
|
||||
defer func() {
|
||||
// if we encounter error during unpacking
|
||||
// clean up the partially-unpacked destination
|
||||
if retErr != nil {
|
||||
if err := os.RemoveAll(dest); err != nil {
|
||||
fmt.Printf("Error: failed to remove partially-unpacked destination %v", err)
|
||||
}
|
||||
}
|
||||
}()
|
||||
for _, d := range m.Layers {
|
||||
switch err := w.walk(func(path string, info os.FileInfo, r io.Reader) error {
|
||||
if info.IsDir() {
|
||||
return nil
|
||||
}
|
||||
|
||||
dd, err := filepath.Rel(filepath.Join("blobs", d.algo()), filepath.Clean(path))
|
||||
if err != nil || d.hash() != dd {
|
||||
return nil
|
||||
}
|
||||
|
||||
if err := unpackLayer(dest, r); err != nil {
|
||||
return errors.Wrap(err, "error extracting layer")
|
||||
}
|
||||
|
||||
return errEOW
|
||||
}); err {
|
||||
case nil:
|
||||
return fmt.Errorf("%s: layer not found", dest)
|
||||
case errEOW:
|
||||
default:
|
||||
return err
|
||||
}
|
||||
}
|
||||
return nil
|
||||
}
|
||||
|
||||
func unpackLayer(dest string, r io.Reader) error {
|
||||
entries := make(map[string]bool)
|
||||
gz, err := gzip.NewReader(r)
|
||||
if err != nil {
|
||||
return errors.Wrap(err, "error creating gzip reader")
|
||||
}
|
||||
defer gz.Close()
|
||||
|
||||
var dirs []*tar.Header
|
||||
tr := tar.NewReader(gz)
|
||||
|
||||
loop:
|
||||
for {
|
||||
hdr, err := tr.Next()
|
||||
switch err {
|
||||
case io.EOF:
|
||||
break loop
|
||||
case nil:
|
||||
// success, continue below
|
||||
default:
|
||||
return errors.Wrapf(err, "error advancing tar stream")
|
||||
}
|
||||
|
||||
hdr.Name = filepath.Clean(hdr.Name)
|
||||
if !strings.HasSuffix(hdr.Name, string(os.PathSeparator)) {
|
||||
// Not the root directory, ensure that the parent directory exists
|
||||
parent := filepath.Dir(hdr.Name)
|
||||
parentPath := filepath.Join(dest, parent)
|
||||
if _, err2 := os.Lstat(parentPath); err2 != nil && os.IsNotExist(err2) {
|
||||
if err3 := os.MkdirAll(parentPath, 0755); err3 != nil {
|
||||
return err3
|
||||
}
|
||||
}
|
||||
}
|
||||
path := filepath.Join(dest, hdr.Name)
|
||||
if entries[path] {
|
||||
return fmt.Errorf("duplicate entry for %s", path)
|
||||
}
|
||||
entries[path] = true
|
||||
rel, err := filepath.Rel(dest, path)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
info := hdr.FileInfo()
|
||||
if strings.HasPrefix(rel, ".."+string(os.PathSeparator)) {
|
||||
return fmt.Errorf("%q is outside of %q", hdr.Name, dest)
|
||||
}
|
||||
|
||||
if strings.HasPrefix(info.Name(), ".wh.") {
|
||||
path = strings.Replace(path, ".wh.", "", 1)
|
||||
|
||||
if err := os.RemoveAll(path); err != nil {
|
||||
return errors.Wrap(err, "unable to delete whiteout path")
|
||||
}
|
||||
|
||||
continue loop
|
||||
}
|
||||
|
||||
switch hdr.Typeflag {
|
||||
case tar.TypeDir:
|
||||
if fi, err := os.Lstat(path); !(err == nil && fi.IsDir()) {
|
||||
if err2 := os.MkdirAll(path, info.Mode()); err2 != nil {
|
||||
return errors.Wrap(err2, "error creating directory")
|
||||
}
|
||||
}
|
||||
|
||||
case tar.TypeReg, tar.TypeRegA:
|
||||
f, err := os.OpenFile(path, os.O_CREATE|os.O_WRONLY, info.Mode())
|
||||
if err != nil {
|
||||
return errors.Wrap(err, "unable to open file")
|
||||
}
|
||||
|
||||
if _, err := io.Copy(f, tr); err != nil {
|
||||
f.Close()
|
||||
return errors.Wrap(err, "unable to copy")
|
||||
}
|
||||
f.Close()
|
||||
|
||||
case tar.TypeLink:
|
||||
target := filepath.Join(dest, hdr.Linkname)
|
||||
|
||||
if !strings.HasPrefix(target, dest) {
|
||||
return fmt.Errorf("invalid hardlink %q -> %q", target, hdr.Linkname)
|
||||
}
|
||||
|
||||
if err := os.Link(target, path); err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
case tar.TypeSymlink:
|
||||
target := filepath.Join(filepath.Dir(path), hdr.Linkname)
|
||||
|
||||
if !strings.HasPrefix(target, dest) {
|
||||
return fmt.Errorf("invalid symlink %q -> %q", path, hdr.Linkname)
|
||||
}
|
||||
|
||||
if err := os.Symlink(hdr.Linkname, path); err != nil {
|
||||
return err
|
||||
}
|
||||
case tar.TypeXGlobalHeader:
|
||||
return nil
|
||||
}
|
||||
// Directory mtimes must be handled at the end to avoid further
|
||||
// file creation in them to modify the directory mtime
|
||||
if hdr.Typeflag == tar.TypeDir {
|
||||
dirs = append(dirs, hdr)
|
||||
}
|
||||
}
|
||||
for _, hdr := range dirs {
|
||||
path := filepath.Join(dest, hdr.Name)
|
||||
|
||||
finfo := hdr.FileInfo()
|
||||
// I believe the old version was using time.Now().UTC() to overcome an
|
||||
// invalid error from chtimes.....but here we lose hdr.AccessTime like this...
|
||||
if err := os.Chtimes(path, time.Now().UTC(), finfo.ModTime()); err != nil {
|
||||
return errors.Wrap(err, "error changing time")
|
||||
}
|
||||
}
|
||||
return nil
|
||||
}
|
21
vendor/github.com/opencontainers/image-tools/image/project.go
generated
vendored
Normal file
21
vendor/github.com/opencontainers/image-tools/image/project.go
generated
vendored
Normal file
@@ -0,0 +1,21 @@
|
||||
// Copyright 2016 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 image
|
||||
|
||||
// SpecURL is the URL for the image-spec repository
|
||||
var SpecURL = "https://github.com/opencontainers/image-spec"
|
||||
|
||||
// IssuesURL is the URL for the issues of image-tools
|
||||
var IssuesURL = "https://github.com/opencontainers/image-tools/issues"
|
84
vendor/github.com/opencontainers/image-tools/image/reader.go
generated
vendored
Normal file
84
vendor/github.com/opencontainers/image-tools/image/reader.go
generated
vendored
Normal file
@@ -0,0 +1,84 @@
|
||||
// Copyright 2016 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 image
|
||||
|
||||
import (
|
||||
"archive/tar"
|
||||
"bytes"
|
||||
"fmt"
|
||||
"io"
|
||||
"io/ioutil"
|
||||
"os"
|
||||
"path/filepath"
|
||||
)
|
||||
|
||||
type reader interface {
|
||||
Get(desc descriptor) (io.ReadCloser, error)
|
||||
}
|
||||
|
||||
type tarReader struct {
|
||||
name string
|
||||
}
|
||||
|
||||
func (r *tarReader) Get(desc descriptor) (io.ReadCloser, error) {
|
||||
f, err := os.Open(r.name)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
defer f.Close()
|
||||
|
||||
tr := tar.NewReader(f)
|
||||
loop:
|
||||
for {
|
||||
hdr, err := tr.Next()
|
||||
switch err {
|
||||
case io.EOF:
|
||||
break loop
|
||||
case nil:
|
||||
// success, continue below
|
||||
default:
|
||||
return nil, err
|
||||
}
|
||||
if hdr.Name == filepath.Join("blobs", desc.algo(), desc.hash()) &&
|
||||
!hdr.FileInfo().IsDir() {
|
||||
buf, err := ioutil.ReadAll(tr)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
return ioutil.NopCloser(bytes.NewReader(buf)), nil
|
||||
}
|
||||
}
|
||||
|
||||
return nil, fmt.Errorf("object not found")
|
||||
}
|
||||
|
||||
type layoutReader struct {
|
||||
root string
|
||||
}
|
||||
|
||||
func (r *layoutReader) Get(desc descriptor) (io.ReadCloser, error) {
|
||||
name := filepath.Join(r.root, "blobs", desc.algo(), desc.hash())
|
||||
|
||||
info, err := os.Stat(name)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
if info.IsDir() {
|
||||
return nil, fmt.Errorf("object is dir")
|
||||
}
|
||||
|
||||
return os.Open(name)
|
||||
}
|
121
vendor/github.com/opencontainers/image-tools/image/walker.go
generated
vendored
Normal file
121
vendor/github.com/opencontainers/image-tools/image/walker.go
generated
vendored
Normal file
@@ -0,0 +1,121 @@
|
||||
// Copyright 2016 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 image
|
||||
|
||||
import (
|
||||
"archive/tar"
|
||||
"fmt"
|
||||
"io"
|
||||
"os"
|
||||
"path/filepath"
|
||||
|
||||
"github.com/pkg/errors"
|
||||
)
|
||||
|
||||
var (
|
||||
errEOW = fmt.Errorf("end of walk") // error to signal stop walking
|
||||
)
|
||||
|
||||
// walkFunc is a function type that gets called for each file or directory visited by the Walker.
|
||||
type walkFunc func(path string, _ os.FileInfo, _ io.Reader) error
|
||||
|
||||
// walker is the interface that walks through a file tree,
|
||||
// calling walk for each file or directory in the tree.
|
||||
type walker interface {
|
||||
walk(walkFunc) error
|
||||
reader
|
||||
}
|
||||
|
||||
type tarWalker struct {
|
||||
r io.ReadSeeker
|
||||
tarReader
|
||||
}
|
||||
|
||||
// newTarWalker returns a Walker that walks through .tar files.
|
||||
func newTarWalker(tarFile string, r io.ReadSeeker) walker {
|
||||
return &tarWalker{r, tarReader{name: tarFile}}
|
||||
}
|
||||
|
||||
func (w *tarWalker) walk(f walkFunc) error {
|
||||
if _, err := w.r.Seek(0, os.SEEK_SET); err != nil {
|
||||
return errors.Wrapf(err, "unable to reset")
|
||||
}
|
||||
|
||||
tr := tar.NewReader(w.r)
|
||||
|
||||
loop:
|
||||
for {
|
||||
hdr, err := tr.Next()
|
||||
switch err {
|
||||
case io.EOF:
|
||||
break loop
|
||||
case nil:
|
||||
// success, continue below
|
||||
default:
|
||||
return errors.Wrapf(err, "error advancing tar stream")
|
||||
}
|
||||
|
||||
info := hdr.FileInfo()
|
||||
if err := f(hdr.Name, info, tr); err != nil {
|
||||
return err
|
||||
}
|
||||
}
|
||||
|
||||
return nil
|
||||
}
|
||||
|
||||
type eofReader struct{}
|
||||
|
||||
func (eofReader) Read(_ []byte) (int, error) {
|
||||
return 0, io.EOF
|
||||
}
|
||||
|
||||
type pathWalker struct {
|
||||
root string
|
||||
layoutReader
|
||||
}
|
||||
|
||||
// newPathWalker returns a Walker that walks through directories
|
||||
// starting at the given root path. It does not follow symlinks.
|
||||
func newPathWalker(root string) walker {
|
||||
return &pathWalker{root, layoutReader{root: root}}
|
||||
}
|
||||
|
||||
func (w *pathWalker) walk(f walkFunc) error {
|
||||
return filepath.Walk(w.root, func(path string, info os.FileInfo, err error) error {
|
||||
// MUST check error value, to make sure the `os.FileInfo` is available.
|
||||
// Otherwise panic risk will exist.
|
||||
if err != nil {
|
||||
return errors.Wrap(err, "error walking path")
|
||||
}
|
||||
|
||||
rel, err := filepath.Rel(w.root, path)
|
||||
if err != nil {
|
||||
return errors.Wrap(err, "error walking path") // err from filepath.Walk includes path name
|
||||
}
|
||||
|
||||
if info.IsDir() { // behave like a tar reader for directories
|
||||
return f(rel, info, eofReader{})
|
||||
}
|
||||
|
||||
file, err := os.Open(path)
|
||||
if err != nil {
|
||||
return errors.Wrap(err, "unable to open file") // os.Open includes the path
|
||||
}
|
||||
defer file.Close()
|
||||
|
||||
return f(rel, info, file)
|
||||
})
|
||||
}
|
191
vendor/github.com/opencontainers/runtime-spec/LICENSE
generated
vendored
Normal file
191
vendor/github.com/opencontainers/runtime-spec/LICENSE
generated
vendored
Normal file
@@ -0,0 +1,191 @@
|
||||
|
||||
Apache License
|
||||
Version 2.0, January 2004
|
||||
http://www.apache.org/licenses/
|
||||
|
||||
TERMS AND CONDITIONS FOR USE, REPRODUCTION, AND DISTRIBUTION
|
||||
|
||||
1. Definitions.
|
||||
|
||||
"License" shall mean the terms and conditions for use, reproduction,
|
||||
and distribution as defined by Sections 1 through 9 of this document.
|
||||
|
||||
"Licensor" shall mean the copyright owner or entity authorized by
|
||||
the copyright owner that is granting the License.
|
||||
|
||||
"Legal Entity" shall mean the union of the acting entity and all
|
||||
other entities that control, are controlled by, or are under common
|
||||
control with that entity. For the purposes of this definition,
|
||||
"control" means (i) the power, direct or indirect, to cause the
|
||||
direction or management of such entity, whether by contract or
|
||||
otherwise, or (ii) ownership of fifty percent (50%) or more of the
|
||||
outstanding shares, or (iii) beneficial ownership of such entity.
|
||||
|
||||
"You" (or "Your") shall mean an individual or Legal Entity
|
||||
exercising permissions granted by this License.
|
||||
|
||||
"Source" form shall mean the preferred form for making modifications,
|
||||
including but not limited to software source code, documentation
|
||||
source, and configuration files.
|
||||
|
||||
"Object" form shall mean any form resulting from mechanical
|
||||
transformation or translation of a Source form, including but
|
||||
not limited to compiled object code, generated documentation,
|
||||
and conversions to other media types.
|
||||
|
||||
"Work" shall mean the work of authorship, whether in Source or
|
||||
Object form, made available under the License, as indicated by a
|
||||
copyright notice that is included in or attached to the work
|
||||
(an example is provided in the Appendix below).
|
||||
|
||||
"Derivative Works" shall mean any work, whether in Source or Object
|
||||
form, that is based on (or derived from) the Work and for which the
|
||||
editorial revisions, annotations, elaborations, or other modifications
|
||||
represent, as a whole, an original work of authorship. For the purposes
|
||||
of this License, Derivative Works shall not include works that remain
|
||||
separable from, or merely link (or bind by name) to the interfaces of,
|
||||
the Work and Derivative Works thereof.
|
||||
|
||||
"Contribution" shall mean any work of authorship, including
|
||||
the original version of the Work and any modifications or additions
|
||||
to that Work or Derivative Works thereof, that is intentionally
|
||||
submitted to Licensor for inclusion in the Work by the copyright owner
|
||||
or by an individual or Legal Entity authorized to submit on behalf of
|
||||
the copyright owner. For the purposes of this definition, "submitted"
|
||||
means any form of electronic, verbal, or written communication sent
|
||||
to the Licensor or its representatives, including but not limited to
|
||||
communication on electronic mailing lists, source code control systems,
|
||||
and issue tracking systems that are managed by, or on behalf of, the
|
||||
Licensor for the purpose of discussing and improving the Work, but
|
||||
excluding communication that is conspicuously marked or otherwise
|
||||
designated in writing by the copyright owner as "Not a Contribution."
|
||||
|
||||
"Contributor" shall mean Licensor and any individual or Legal Entity
|
||||
on behalf of whom a Contribution has been received by Licensor and
|
||||
subsequently incorporated within the Work.
|
||||
|
||||
2. Grant of Copyright License. Subject to the terms and conditions of
|
||||
this License, each Contributor hereby grants to You a perpetual,
|
||||
worldwide, non-exclusive, no-charge, royalty-free, irrevocable
|
||||
copyright license to reproduce, prepare Derivative Works of,
|
||||
publicly display, publicly perform, sublicense, and distribute the
|
||||
Work and such Derivative Works in Source or Object form.
|
||||
|
||||
3. Grant of Patent License. Subject to the terms and conditions of
|
||||
this License, each Contributor hereby grants to You a perpetual,
|
||||
worldwide, non-exclusive, no-charge, royalty-free, irrevocable
|
||||
(except as stated in this section) patent license to make, have made,
|
||||
use, offer to sell, sell, import, and otherwise transfer the Work,
|
||||
where such license applies only to those patent claims licensable
|
||||
by such Contributor that are necessarily infringed by their
|
||||
Contribution(s) alone or by combination of their Contribution(s)
|
||||
with the Work to which such Contribution(s) was submitted. If You
|
||||
institute patent litigation against any entity (including a
|
||||
cross-claim or counterclaim in a lawsuit) alleging that the Work
|
||||
or a Contribution incorporated within the Work constitutes direct
|
||||
or contributory patent infringement, then any patent licenses
|
||||
granted to You under this License for that Work shall terminate
|
||||
as of the date such litigation is filed.
|
||||
|
||||
4. Redistribution. You may reproduce and distribute copies of the
|
||||
Work or Derivative Works thereof in any medium, with or without
|
||||
modifications, and in Source or Object form, provided that You
|
||||
meet the following conditions:
|
||||
|
||||
(a) You must give any other recipients of the Work or
|
||||
Derivative Works a copy of this License; and
|
||||
|
||||
(b) You must cause any modified files to carry prominent notices
|
||||
stating that You changed the files; and
|
||||
|
||||
(c) You must retain, in the Source form of any Derivative Works
|
||||
that You distribute, all copyright, patent, trademark, and
|
||||
attribution notices from the Source form of the Work,
|
||||
excluding those notices that do not pertain to any part of
|
||||
the Derivative Works; and
|
||||
|
||||
(d) If the Work includes a "NOTICE" text file as part of its
|
||||
distribution, then any Derivative Works that You distribute must
|
||||
include a readable copy of the attribution notices contained
|
||||
within such NOTICE file, excluding those notices that do not
|
||||
pertain to any part of the Derivative Works, in at least one
|
||||
of the following places: within a NOTICE text file distributed
|
||||
as part of the Derivative Works; within the Source form or
|
||||
documentation, if provided along with the Derivative Works; or,
|
||||
within a display generated by the Derivative Works, if and
|
||||
wherever such third-party notices normally appear. The contents
|
||||
of the NOTICE file are for informational purposes only and
|
||||
do not modify the License. You may add Your own attribution
|
||||
notices within Derivative Works that You distribute, alongside
|
||||
or as an addendum to the NOTICE text from the Work, provided
|
||||
that such additional attribution notices cannot be construed
|
||||
as modifying the License.
|
||||
|
||||
You may add Your own copyright statement to Your modifications and
|
||||
may provide additional or different license terms and conditions
|
||||
for use, reproduction, or distribution of Your modifications, or
|
||||
for any such Derivative Works as a whole, provided Your use,
|
||||
reproduction, and distribution of the Work otherwise complies with
|
||||
the conditions stated in this License.
|
||||
|
||||
5. Submission of Contributions. Unless You explicitly state otherwise,
|
||||
any Contribution intentionally submitted for inclusion in the Work
|
||||
by You to the Licensor shall be under the terms and conditions of
|
||||
this License, without any additional terms or conditions.
|
||||
Notwithstanding the above, nothing herein shall supersede or modify
|
||||
the terms of any separate license agreement you may have executed
|
||||
with Licensor regarding such Contributions.
|
||||
|
||||
6. Trademarks. This License does not grant permission to use the trade
|
||||
names, trademarks, service marks, or product names of the Licensor,
|
||||
except as required for reasonable and customary use in describing the
|
||||
origin of the Work and reproducing the content of the NOTICE file.
|
||||
|
||||
7. Disclaimer of Warranty. Unless required by applicable law or
|
||||
agreed to in writing, Licensor provides the Work (and each
|
||||
Contributor provides its Contributions) on an "AS IS" BASIS,
|
||||
WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or
|
||||
implied, including, without limitation, any warranties or conditions
|
||||
of TITLE, NON-INFRINGEMENT, MERCHANTABILITY, or FITNESS FOR A
|
||||
PARTICULAR PURPOSE. You are solely responsible for determining the
|
||||
appropriateness of using or redistributing the Work and assume any
|
||||
risks associated with Your exercise of permissions under this License.
|
||||
|
||||
8. Limitation of Liability. In no event and under no legal theory,
|
||||
whether in tort (including negligence), contract, or otherwise,
|
||||
unless required by applicable law (such as deliberate and grossly
|
||||
negligent acts) or agreed to in writing, shall any Contributor be
|
||||
liable to You for damages, including any direct, indirect, special,
|
||||
incidental, or consequential damages of any character arising as a
|
||||
result of this License or out of the use or inability to use the
|
||||
Work (including but not limited to damages for loss of goodwill,
|
||||
work stoppage, computer failure or malfunction, or any and all
|
||||
other commercial damages or losses), even if such Contributor
|
||||
has been advised of the possibility of such damages.
|
||||
|
||||
9. Accepting Warranty or Additional Liability. While redistributing
|
||||
the Work or Derivative Works thereof, You may choose to offer,
|
||||
and charge a fee for, acceptance of support, warranty, indemnity,
|
||||
or other liability obligations and/or rights consistent with this
|
||||
License. However, in accepting such obligations, You may act only
|
||||
on Your own behalf and on Your sole responsibility, not on behalf
|
||||
of any other Contributor, and only if You agree to indemnify,
|
||||
defend, and hold each Contributor harmless for any liability
|
||||
incurred by, or claims asserted against, such Contributor by reason
|
||||
of your accepting any such warranty or additional liability.
|
||||
|
||||
END OF TERMS AND CONDITIONS
|
||||
|
||||
Copyright 2015 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.
|
535
vendor/github.com/opencontainers/runtime-spec/specs-go/config.go
generated
vendored
Normal file
535
vendor/github.com/opencontainers/runtime-spec/specs-go/config.go
generated
vendored
Normal file
@@ -0,0 +1,535 @@
|
||||
package specs
|
||||
|
||||
import "os"
|
||||
|
||||
// Spec is the base configuration for the container.
|
||||
type Spec struct {
|
||||
// Version of the Open Container Runtime Specification with which the bundle complies.
|
||||
Version string `json:"ociVersion"`
|
||||
// Platform specifies the configuration's target platform.
|
||||
Platform Platform `json:"platform"`
|
||||
// Process configures the container process.
|
||||
Process Process `json:"process"`
|
||||
// Root configures the container's root filesystem.
|
||||
Root Root `json:"root"`
|
||||
// Hostname configures the container's hostname.
|
||||
Hostname string `json:"hostname,omitempty"`
|
||||
// Mounts configures additional mounts (on top of Root).
|
||||
Mounts []Mount `json:"mounts,omitempty"`
|
||||
// Hooks configures callbacks for container lifecycle events.
|
||||
Hooks *Hooks `json:"hooks,omitempty"`
|
||||
// Annotations contains arbitrary metadata for the container.
|
||||
Annotations map[string]string `json:"annotations,omitempty"`
|
||||
|
||||
// Linux is platform specific configuration for Linux based containers.
|
||||
Linux *Linux `json:"linux,omitempty" platform:"linux"`
|
||||
// Solaris is platform specific configuration for Solaris containers.
|
||||
Solaris *Solaris `json:"solaris,omitempty" platform:"solaris"`
|
||||
// Windows is platform specific configuration for Windows based containers, including Hyper-V containers.
|
||||
Windows *Windows `json:"windows,omitempty" platform:"windows"`
|
||||
}
|
||||
|
||||
// Process contains information to start a specific application inside the container.
|
||||
type Process struct {
|
||||
// Terminal creates an interactive terminal for the container.
|
||||
Terminal bool `json:"terminal,omitempty"`
|
||||
// ConsoleSize specifies the size of the console.
|
||||
ConsoleSize Box `json:"consoleSize,omitempty"`
|
||||
// User specifies user information for the process.
|
||||
User User `json:"user"`
|
||||
// Args specifies the binary and arguments for the application to execute.
|
||||
Args []string `json:"args"`
|
||||
// Env populates the process environment for the process.
|
||||
Env []string `json:"env,omitempty"`
|
||||
// Cwd is the current working directory for the process and must be
|
||||
// relative to the container's root.
|
||||
Cwd string `json:"cwd"`
|
||||
// Capabilities are Linux capabilities that are kept for the container.
|
||||
Capabilities []string `json:"capabilities,omitempty" platform:"linux"`
|
||||
// Rlimits specifies rlimit options to apply to the process.
|
||||
Rlimits []LinuxRlimit `json:"rlimits,omitempty" platform:"linux"`
|
||||
// NoNewPrivileges controls whether additional privileges could be gained by processes in the container.
|
||||
NoNewPrivileges bool `json:"noNewPrivileges,omitempty" platform:"linux"`
|
||||
// ApparmorProfile specifies the apparmor profile for the container.
|
||||
ApparmorProfile string `json:"apparmorProfile,omitempty" platform:"linux"`
|
||||
// SelinuxLabel specifies the selinux context that the container process is run as.
|
||||
SelinuxLabel string `json:"selinuxLabel,omitempty" platform:"linux"`
|
||||
}
|
||||
|
||||
// Box specifies dimensions of a rectangle. Used for specifying the size of a console.
|
||||
type Box struct {
|
||||
// Height is the vertical dimension of a box.
|
||||
Height uint `json:"height"`
|
||||
// Width is the horizontal dimension of a box.
|
||||
Width uint `json:"width"`
|
||||
}
|
||||
|
||||
// User specifies specific user (and group) information for the container process.
|
||||
type User struct {
|
||||
// UID is the user id.
|
||||
UID uint32 `json:"uid" platform:"linux,solaris"`
|
||||
// GID is the group id.
|
||||
GID uint32 `json:"gid" platform:"linux,solaris"`
|
||||
// AdditionalGids are additional group ids set for the container's process.
|
||||
AdditionalGids []uint32 `json:"additionalGids,omitempty" platform:"linux,solaris"`
|
||||
// Username is the user name.
|
||||
Username string `json:"username,omitempty" platform:"windows"`
|
||||
}
|
||||
|
||||
// Root contains information about the container's root filesystem on the host.
|
||||
type Root struct {
|
||||
// Path is the absolute path to the container's root filesystem.
|
||||
Path string `json:"path"`
|
||||
// Readonly makes the root filesystem for the container readonly before the process is executed.
|
||||
Readonly bool `json:"readonly,omitempty"`
|
||||
}
|
||||
|
||||
// Platform specifies OS and arch information for the host system that the container
|
||||
// is created for.
|
||||
type Platform struct {
|
||||
// OS is the operating system.
|
||||
OS string `json:"os"`
|
||||
// Arch is the architecture
|
||||
Arch string `json:"arch"`
|
||||
}
|
||||
|
||||
// Mount specifies a mount for a container.
|
||||
type Mount struct {
|
||||
// Destination is the path where the mount will be placed relative to the container's root. The path and child directories MUST exist, a runtime MUST NOT create directories automatically to a mount point.
|
||||
Destination string `json:"destination"`
|
||||
// Type specifies the mount kind.
|
||||
Type string `json:"type"`
|
||||
// Source specifies the source path of the mount. In the case of bind mounts on
|
||||
// Linux based systems this would be the file on the host.
|
||||
Source string `json:"source"`
|
||||
// Options are fstab style mount options.
|
||||
Options []string `json:"options,omitempty"`
|
||||
}
|
||||
|
||||
// Hook specifies a command that is run at a particular event in the lifecycle of a container
|
||||
type Hook struct {
|
||||
Path string `json:"path"`
|
||||
Args []string `json:"args,omitempty"`
|
||||
Env []string `json:"env,omitempty"`
|
||||
Timeout *int `json:"timeout,omitempty"`
|
||||
}
|
||||
|
||||
// Hooks for container setup and teardown
|
||||
type Hooks struct {
|
||||
// Prestart is a list of hooks to be run before the container process is executed.
|
||||
// On Linux, they are run after the container namespaces are created.
|
||||
Prestart []Hook `json:"prestart,omitempty"`
|
||||
// Poststart is a list of hooks to be run after the container process is started.
|
||||
Poststart []Hook `json:"poststart,omitempty"`
|
||||
// Poststop is a list of hooks to be run after the container process exits.
|
||||
Poststop []Hook `json:"poststop,omitempty"`
|
||||
}
|
||||
|
||||
// Linux contains platform specific configuration for Linux based containers.
|
||||
type Linux struct {
|
||||
// UIDMapping specifies user mappings for supporting user namespaces on Linux.
|
||||
UIDMappings []LinuxIDMapping `json:"uidMappings,omitempty"`
|
||||
// GIDMapping specifies group mappings for supporting user namespaces on Linux.
|
||||
GIDMappings []LinuxIDMapping `json:"gidMappings,omitempty"`
|
||||
// Sysctl are a set of key value pairs that are set for the container on start
|
||||
Sysctl map[string]string `json:"sysctl,omitempty"`
|
||||
// Resources contain cgroup information for handling resource constraints
|
||||
// for the container
|
||||
Resources *LinuxResources `json:"resources,omitempty"`
|
||||
// CgroupsPath specifies the path to cgroups that are created and/or joined by the container.
|
||||
// The path is expected to be relative to the cgroups mountpoint.
|
||||
// If resources are specified, the cgroups at CgroupsPath will be updated based on resources.
|
||||
CgroupsPath string `json:"cgroupsPath,omitempty"`
|
||||
// Namespaces contains the namespaces that are created and/or joined by the container
|
||||
Namespaces []LinuxNamespace `json:"namespaces,omitempty"`
|
||||
// Devices are a list of device nodes that are created for the container
|
||||
Devices []LinuxDevice `json:"devices,omitempty"`
|
||||
// Seccomp specifies the seccomp security settings for the container.
|
||||
Seccomp *LinuxSeccomp `json:"seccomp,omitempty"`
|
||||
// RootfsPropagation is the rootfs mount propagation mode for the container.
|
||||
RootfsPropagation string `json:"rootfsPropagation,omitempty"`
|
||||
// MaskedPaths masks over the provided paths inside the container.
|
||||
MaskedPaths []string `json:"maskedPaths,omitempty"`
|
||||
// ReadonlyPaths sets the provided paths as RO inside the container.
|
||||
ReadonlyPaths []string `json:"readonlyPaths,omitempty"`
|
||||
// MountLabel specifies the selinux context for the mounts in the container.
|
||||
MountLabel string `json:"mountLabel,omitempty"`
|
||||
}
|
||||
|
||||
// LinuxNamespace is the configuration for a Linux namespace
|
||||
type LinuxNamespace struct {
|
||||
// Type is the type of Linux namespace
|
||||
Type LinuxNamespaceType `json:"type"`
|
||||
// Path is a path to an existing namespace persisted on disk that can be joined
|
||||
// and is of the same type
|
||||
Path string `json:"path,omitempty"`
|
||||
}
|
||||
|
||||
// LinuxNamespaceType is one of the Linux namespaces
|
||||
type LinuxNamespaceType string
|
||||
|
||||
const (
|
||||
// PIDNamespace for isolating process IDs
|
||||
PIDNamespace LinuxNamespaceType = "pid"
|
||||
// NetworkNamespace for isolating network devices, stacks, ports, etc
|
||||
NetworkNamespace = "network"
|
||||
// MountNamespace for isolating mount points
|
||||
MountNamespace = "mount"
|
||||
// IPCNamespace for isolating System V IPC, POSIX message queues
|
||||
IPCNamespace = "ipc"
|
||||
// UTSNamespace for isolating hostname and NIS domain name
|
||||
UTSNamespace = "uts"
|
||||
// UserNamespace for isolating user and group IDs
|
||||
UserNamespace = "user"
|
||||
// CgroupNamespace for isolating cgroup hierarchies
|
||||
CgroupNamespace = "cgroup"
|
||||
)
|
||||
|
||||
// LinuxIDMapping specifies UID/GID mappings
|
||||
type LinuxIDMapping struct {
|
||||
// HostID is the starting UID/GID on the host to be mapped to 'ContainerID'
|
||||
HostID uint32 `json:"hostID"`
|
||||
// ContainerID is the starting UID/GID in the container
|
||||
ContainerID uint32 `json:"containerID"`
|
||||
// Size is the number of IDs to be mapped
|
||||
Size uint32 `json:"size"`
|
||||
}
|
||||
|
||||
// LinuxRlimit type and restrictions
|
||||
type LinuxRlimit struct {
|
||||
// Type of the rlimit to set
|
||||
Type string `json:"type"`
|
||||
// Hard is the hard limit for the specified type
|
||||
Hard uint64 `json:"hard"`
|
||||
// Soft is the soft limit for the specified type
|
||||
Soft uint64 `json:"soft"`
|
||||
}
|
||||
|
||||
// LinuxHugepageLimit structure corresponds to limiting kernel hugepages
|
||||
type LinuxHugepageLimit struct {
|
||||
// Pagesize is the hugepage size
|
||||
Pagesize string `json:"pageSize"`
|
||||
// Limit is the limit of "hugepagesize" hugetlb usage
|
||||
Limit int64 `json:"limit"`
|
||||
}
|
||||
|
||||
// LinuxInterfacePriority for network interfaces
|
||||
type LinuxInterfacePriority struct {
|
||||
// Name is the name of the network interface
|
||||
Name string `json:"name"`
|
||||
// Priority for the interface
|
||||
Priority uint32 `json:"priority"`
|
||||
}
|
||||
|
||||
// linuxBlockIODevice holds major:minor format supported in blkio cgroup
|
||||
type linuxBlockIODevice struct {
|
||||
// Major is the device's major number.
|
||||
Major int64 `json:"major"`
|
||||
// Minor is the device's minor number.
|
||||
Minor int64 `json:"minor"`
|
||||
}
|
||||
|
||||
// LinuxWeightDevice struct holds a `major:minor weight` pair for blkioWeightDevice
|
||||
type LinuxWeightDevice struct {
|
||||
linuxBlockIODevice
|
||||
// Weight is the bandwidth rate for the device, range is from 10 to 1000
|
||||
Weight *uint16 `json:"weight,omitempty"`
|
||||
// LeafWeight is the bandwidth rate for the device while competing with the cgroup's child cgroups, range is from 10 to 1000, CFQ scheduler only
|
||||
LeafWeight *uint16 `json:"leafWeight,omitempty"`
|
||||
}
|
||||
|
||||
// LinuxThrottleDevice struct holds a `major:minor rate_per_second` pair
|
||||
type LinuxThrottleDevice struct {
|
||||
linuxBlockIODevice
|
||||
// Rate is the IO rate limit per cgroup per device
|
||||
Rate uint64 `json:"rate"`
|
||||
}
|
||||
|
||||
// LinuxBlockIO for Linux cgroup 'blkio' resource management
|
||||
type LinuxBlockIO struct {
|
||||
// Specifies per cgroup weight, range is from 10 to 1000
|
||||
Weight *uint16 `json:"blkioWeight,omitempty"`
|
||||
// Specifies tasks' weight in the given cgroup while competing with the cgroup's child cgroups, range is from 10 to 1000, CFQ scheduler only
|
||||
LeafWeight *uint16 `json:"blkioLeafWeight,omitempty"`
|
||||
// Weight per cgroup per device, can override BlkioWeight
|
||||
WeightDevice []LinuxWeightDevice `json:"blkioWeightDevice,omitempty"`
|
||||
// IO read rate limit per cgroup per device, bytes per second
|
||||
ThrottleReadBpsDevice []LinuxThrottleDevice `json:"blkioThrottleReadBpsDevice,omitempty"`
|
||||
// IO write rate limit per cgroup per device, bytes per second
|
||||
ThrottleWriteBpsDevice []LinuxThrottleDevice `json:"blkioThrottleWriteBpsDevice,omitempty"`
|
||||
// IO read rate limit per cgroup per device, IO per second
|
||||
ThrottleReadIOPSDevice []LinuxThrottleDevice `json:"blkioThrottleReadIOPSDevice,omitempty"`
|
||||
// IO write rate limit per cgroup per device, IO per second
|
||||
ThrottleWriteIOPSDevice []LinuxThrottleDevice `json:"blkioThrottleWriteIOPSDevice,omitempty"`
|
||||
}
|
||||
|
||||
// LinuxMemory for Linux cgroup 'memory' resource management
|
||||
type LinuxMemory struct {
|
||||
// Memory limit (in bytes).
|
||||
Limit *int64 `json:"limit,omitempty"`
|
||||
// Memory reservation or soft_limit (in bytes).
|
||||
Reservation *int64 `json:"reservation,omitempty"`
|
||||
// Total memory limit (memory + swap).
|
||||
Swap *int64 `json:"swap,omitempty"`
|
||||
// Kernel memory limit (in bytes).
|
||||
Kernel *int64 `json:"kernel,omitempty"`
|
||||
// Kernel memory limit for tcp (in bytes)
|
||||
KernelTCP *int64 `json:"kernelTCP,omitempty"`
|
||||
// How aggressive the kernel will swap memory pages. Range from 0 to 100.
|
||||
Swappiness *uint64 `json:"swappiness,omitempty"`
|
||||
}
|
||||
|
||||
// LinuxCPU for Linux cgroup 'cpu' resource management
|
||||
type LinuxCPU struct {
|
||||
// CPU shares (relative weight (ratio) vs. other cgroups with cpu shares).
|
||||
Shares *uint64 `json:"shares,omitempty"`
|
||||
// CPU hardcap limit (in usecs). Allowed cpu time in a given period.
|
||||
Quota *int64 `json:"quota,omitempty"`
|
||||
// CPU period to be used for hardcapping (in usecs).
|
||||
Period *uint64 `json:"period,omitempty"`
|
||||
// How much time realtime scheduling may use (in usecs).
|
||||
RealtimeRuntime *int64 `json:"realtimeRuntime,omitempty"`
|
||||
// CPU period to be used for realtime scheduling (in usecs).
|
||||
RealtimePeriod *uint64 `json:"realtimePeriod,omitempty"`
|
||||
// CPUs to use within the cpuset. Default is to use any CPU available.
|
||||
Cpus string `json:"cpus,omitempty"`
|
||||
// List of memory nodes in the cpuset. Default is to use any available memory node.
|
||||
Mems string `json:"mems,omitempty"`
|
||||
}
|
||||
|
||||
// LinuxPids for Linux cgroup 'pids' resource management (Linux 4.3)
|
||||
type LinuxPids struct {
|
||||
// Maximum number of PIDs. Default is "no limit".
|
||||
Limit int64 `json:"limit"`
|
||||
}
|
||||
|
||||
// LinuxNetwork identification and priority configuration
|
||||
type LinuxNetwork struct {
|
||||
// Set class identifier for container's network packets
|
||||
ClassID *uint32 `json:"classID,omitempty"`
|
||||
// Set priority of network traffic for container
|
||||
Priorities []LinuxInterfacePriority `json:"priorities,omitempty"`
|
||||
}
|
||||
|
||||
// LinuxResources has container runtime resource constraints
|
||||
type LinuxResources struct {
|
||||
// Devices configures the device whitelist.
|
||||
Devices []LinuxDeviceCgroup `json:"devices,omitempty"`
|
||||
// DisableOOMKiller disables the OOM killer for out of memory conditions
|
||||
DisableOOMKiller *bool `json:"disableOOMKiller,omitempty"`
|
||||
// Specify an oom_score_adj for the container.
|
||||
OOMScoreAdj *int `json:"oomScoreAdj,omitempty"`
|
||||
// Memory restriction configuration
|
||||
Memory *LinuxMemory `json:"memory,omitempty"`
|
||||
// CPU resource restriction configuration
|
||||
CPU *LinuxCPU `json:"cpu,omitempty"`
|
||||
// Task resource restriction configuration.
|
||||
Pids *LinuxPids `json:"pids,omitempty"`
|
||||
// BlockIO restriction configuration
|
||||
BlockIO *LinuxBlockIO `json:"blockIO,omitempty"`
|
||||
// Hugetlb limit (in bytes)
|
||||
HugepageLimits []LinuxHugepageLimit `json:"hugepageLimits,omitempty"`
|
||||
// Network restriction configuration
|
||||
Network *LinuxNetwork `json:"network,omitempty"`
|
||||
}
|
||||
|
||||
// LinuxDevice represents the mknod information for a Linux special device file
|
||||
type LinuxDevice struct {
|
||||
// Path to the device.
|
||||
Path string `json:"path"`
|
||||
// Device type, block, char, etc.
|
||||
Type string `json:"type"`
|
||||
// Major is the device's major number.
|
||||
Major int64 `json:"major"`
|
||||
// Minor is the device's minor number.
|
||||
Minor int64 `json:"minor"`
|
||||
// FileMode permission bits for the device.
|
||||
FileMode *os.FileMode `json:"fileMode,omitempty"`
|
||||
// UID of the device.
|
||||
UID *uint32 `json:"uid,omitempty"`
|
||||
// Gid of the device.
|
||||
GID *uint32 `json:"gid,omitempty"`
|
||||
}
|
||||
|
||||
// LinuxDeviceCgroup represents a device rule for the whitelist controller
|
||||
type LinuxDeviceCgroup struct {
|
||||
// Allow or deny
|
||||
Allow bool `json:"allow"`
|
||||
// Device type, block, char, etc.
|
||||
Type string `json:"type,omitempty"`
|
||||
// Major is the device's major number.
|
||||
Major *int64 `json:"major,omitempty"`
|
||||
// Minor is the device's minor number.
|
||||
Minor *int64 `json:"minor,omitempty"`
|
||||
// Cgroup access permissions format, rwm.
|
||||
Access string `json:"access,omitempty"`
|
||||
}
|
||||
|
||||
// LinuxSeccomp represents syscall restrictions
|
||||
type LinuxSeccomp struct {
|
||||
DefaultAction LinuxSeccompAction `json:"defaultAction"`
|
||||
Architectures []Arch `json:"architectures"`
|
||||
Syscalls []LinuxSyscall `json:"syscalls,omitempty"`
|
||||
}
|
||||
|
||||
// Solaris contains platform specific configuration for Solaris application containers.
|
||||
type Solaris struct {
|
||||
// SMF FMRI which should go "online" before we start the container process.
|
||||
Milestone string `json:"milestone,omitempty"`
|
||||
// Maximum set of privileges any process in this container can obtain.
|
||||
LimitPriv string `json:"limitpriv,omitempty"`
|
||||
// The maximum amount of shared memory allowed for this container.
|
||||
MaxShmMemory string `json:"maxShmMemory,omitempty"`
|
||||
// Specification for automatic creation of network resources for this container.
|
||||
Anet []SolarisAnet `json:"anet,omitempty"`
|
||||
// Set limit on the amount of CPU time that can be used by container.
|
||||
CappedCPU *SolarisCappedCPU `json:"cappedCPU,omitempty"`
|
||||
// The physical and swap caps on the memory that can be used by this container.
|
||||
CappedMemory *SolarisCappedMemory `json:"cappedMemory,omitempty"`
|
||||
}
|
||||
|
||||
// SolarisCappedCPU allows users to set limit on the amount of CPU time that can be used by container.
|
||||
type SolarisCappedCPU struct {
|
||||
Ncpus string `json:"ncpus,omitempty"`
|
||||
}
|
||||
|
||||
// SolarisCappedMemory allows users to set the physical and swap caps on the memory that can be used by this container.
|
||||
type SolarisCappedMemory struct {
|
||||
Physical string `json:"physical,omitempty"`
|
||||
Swap string `json:"swap,omitempty"`
|
||||
}
|
||||
|
||||
// SolarisAnet provides the specification for automatic creation of network resources for this container.
|
||||
type SolarisAnet struct {
|
||||
// Specify a name for the automatically created VNIC datalink.
|
||||
Linkname string `json:"linkname,omitempty"`
|
||||
// Specify the link over which the VNIC will be created.
|
||||
Lowerlink string `json:"lowerLink,omitempty"`
|
||||
// The set of IP addresses that the container can use.
|
||||
Allowedaddr string `json:"allowedAddress,omitempty"`
|
||||
// Specifies whether allowedAddress limitation is to be applied to the VNIC.
|
||||
Configallowedaddr string `json:"configureAllowedAddress,omitempty"`
|
||||
// The value of the optional default router.
|
||||
Defrouter string `json:"defrouter,omitempty"`
|
||||
// Enable one or more types of link protection.
|
||||
Linkprotection string `json:"linkProtection,omitempty"`
|
||||
// Set the VNIC's macAddress
|
||||
Macaddress string `json:"macAddress,omitempty"`
|
||||
}
|
||||
|
||||
// Windows defines the runtime configuration for Windows based containers, including Hyper-V containers.
|
||||
type Windows struct {
|
||||
// Resources contains information for handling resource constraints for the container.
|
||||
Resources *WindowsResources `json:"resources,omitempty"`
|
||||
}
|
||||
|
||||
// WindowsResources has container runtime resource constraints for containers running on Windows.
|
||||
type WindowsResources struct {
|
||||
// Memory restriction configuration.
|
||||
Memory *WindowsMemoryResources `json:"memory,omitempty"`
|
||||
// CPU resource restriction configuration.
|
||||
CPU *WindowsCPUResources `json:"cpu,omitempty"`
|
||||
// Storage restriction configuration.
|
||||
Storage *WindowsStorageResources `json:"storage,omitempty"`
|
||||
// Network restriction configuration.
|
||||
Network *WindowsNetworkResources `json:"network,omitempty"`
|
||||
}
|
||||
|
||||
// WindowsMemoryResources contains memory resource management settings.
|
||||
type WindowsMemoryResources struct {
|
||||
// Memory limit in bytes.
|
||||
Limit *uint64 `json:"limit,omitempty"`
|
||||
// Memory reservation in bytes.
|
||||
Reservation *uint64 `json:"reservation,omitempty"`
|
||||
}
|
||||
|
||||
// WindowsCPUResources contains CPU resource management settings.
|
||||
type WindowsCPUResources struct {
|
||||
// Number of CPUs available to the container.
|
||||
Count *uint64 `json:"count,omitempty"`
|
||||
// CPU shares (relative weight to other containers with cpu shares). Range is from 1 to 10000.
|
||||
Shares *uint16 `json:"shares,omitempty"`
|
||||
// Percent of available CPUs usable by the container.
|
||||
Percent *uint8 `json:"percent,omitempty"`
|
||||
}
|
||||
|
||||
// WindowsStorageResources contains storage resource management settings.
|
||||
type WindowsStorageResources struct {
|
||||
// Specifies maximum Iops for the system drive.
|
||||
Iops *uint64 `json:"iops,omitempty"`
|
||||
// Specifies maximum bytes per second for the system drive.
|
||||
Bps *uint64 `json:"bps,omitempty"`
|
||||
// Sandbox size specifies the minimum size of the system drive in bytes.
|
||||
SandboxSize *uint64 `json:"sandboxSize,omitempty"`
|
||||
}
|
||||
|
||||
// WindowsNetworkResources contains network resource management settings.
|
||||
type WindowsNetworkResources struct {
|
||||
// EgressBandwidth is the maximum egress bandwidth in bytes per second.
|
||||
EgressBandwidth *uint64 `json:"egressBandwidth,omitempty"`
|
||||
}
|
||||
|
||||
// Arch used for additional architectures
|
||||
type Arch string
|
||||
|
||||
// Additional architectures permitted to be used for system calls
|
||||
// By default only the native architecture of the kernel is permitted
|
||||
const (
|
||||
ArchX86 Arch = "SCMP_ARCH_X86"
|
||||
ArchX86_64 Arch = "SCMP_ARCH_X86_64"
|
||||
ArchX32 Arch = "SCMP_ARCH_X32"
|
||||
ArchARM Arch = "SCMP_ARCH_ARM"
|
||||
ArchAARCH64 Arch = "SCMP_ARCH_AARCH64"
|
||||
ArchMIPS Arch = "SCMP_ARCH_MIPS"
|
||||
ArchMIPS64 Arch = "SCMP_ARCH_MIPS64"
|
||||
ArchMIPS64N32 Arch = "SCMP_ARCH_MIPS64N32"
|
||||
ArchMIPSEL Arch = "SCMP_ARCH_MIPSEL"
|
||||
ArchMIPSEL64 Arch = "SCMP_ARCH_MIPSEL64"
|
||||
ArchMIPSEL64N32 Arch = "SCMP_ARCH_MIPSEL64N32"
|
||||
ArchPPC Arch = "SCMP_ARCH_PPC"
|
||||
ArchPPC64 Arch = "SCMP_ARCH_PPC64"
|
||||
ArchPPC64LE Arch = "SCMP_ARCH_PPC64LE"
|
||||
ArchS390 Arch = "SCMP_ARCH_S390"
|
||||
ArchS390X Arch = "SCMP_ARCH_S390X"
|
||||
)
|
||||
|
||||
// LinuxSeccompAction taken upon Seccomp rule match
|
||||
type LinuxSeccompAction string
|
||||
|
||||
// Define actions for Seccomp rules
|
||||
const (
|
||||
ActKill LinuxSeccompAction = "SCMP_ACT_KILL"
|
||||
ActTrap LinuxSeccompAction = "SCMP_ACT_TRAP"
|
||||
ActErrno LinuxSeccompAction = "SCMP_ACT_ERRNO"
|
||||
ActTrace LinuxSeccompAction = "SCMP_ACT_TRACE"
|
||||
ActAllow LinuxSeccompAction = "SCMP_ACT_ALLOW"
|
||||
)
|
||||
|
||||
// LinuxSeccompOperator used to match syscall arguments in Seccomp
|
||||
type LinuxSeccompOperator string
|
||||
|
||||
// Define operators for syscall arguments in Seccomp
|
||||
const (
|
||||
OpNotEqual LinuxSeccompOperator = "SCMP_CMP_NE"
|
||||
OpLessThan LinuxSeccompOperator = "SCMP_CMP_LT"
|
||||
OpLessEqual LinuxSeccompOperator = "SCMP_CMP_LE"
|
||||
OpEqualTo LinuxSeccompOperator = "SCMP_CMP_EQ"
|
||||
OpGreaterEqual LinuxSeccompOperator = "SCMP_CMP_GE"
|
||||
OpGreaterThan LinuxSeccompOperator = "SCMP_CMP_GT"
|
||||
OpMaskedEqual LinuxSeccompOperator = "SCMP_CMP_MASKED_EQ"
|
||||
)
|
||||
|
||||
// LinuxSeccompArg used for matching specific syscall arguments in Seccomp
|
||||
type LinuxSeccompArg struct {
|
||||
Index uint `json:"index"`
|
||||
Value uint64 `json:"value"`
|
||||
ValueTwo uint64 `json:"valueTwo"`
|
||||
Op LinuxSeccompOperator `json:"op"`
|
||||
}
|
||||
|
||||
// LinuxSyscall is used to match a syscall in Seccomp
|
||||
type LinuxSyscall struct {
|
||||
Name string `json:"name"`
|
||||
Action LinuxSeccompAction `json:"action"`
|
||||
Args []LinuxSeccompArg `json:"args,omitempty"`
|
||||
}
|
17
vendor/github.com/opencontainers/runtime-spec/specs-go/state.go
generated
vendored
Normal file
17
vendor/github.com/opencontainers/runtime-spec/specs-go/state.go
generated
vendored
Normal file
@@ -0,0 +1,17 @@
|
||||
package specs
|
||||
|
||||
// State holds information about the runtime state of the container.
|
||||
type State struct {
|
||||
// Version is the version of the specification that is supported.
|
||||
Version string `json:"ociVersion"`
|
||||
// ID is the container ID
|
||||
ID string `json:"id"`
|
||||
// Status is the runtime state of the container.
|
||||
Status string `json:"status"`
|
||||
// Pid is the process ID for the container process.
|
||||
Pid int `json:"pid"`
|
||||
// BundlePath is the path to the container's bundle directory.
|
||||
BundlePath string `json:"bundlePath"`
|
||||
// Annotations are the annotations associated with the container.
|
||||
Annotations map[string]string `json:"annotations,omitempty"`
|
||||
}
|
18
vendor/github.com/opencontainers/runtime-spec/specs-go/version.go
generated
vendored
Normal file
18
vendor/github.com/opencontainers/runtime-spec/specs-go/version.go
generated
vendored
Normal file
@@ -0,0 +1,18 @@
|
||||
package specs
|
||||
|
||||
import "fmt"
|
||||
|
||||
const (
|
||||
// VersionMajor is for an API incompatible changes
|
||||
VersionMajor = 1
|
||||
// VersionMinor is for functionality in a backwards-compatible manner
|
||||
VersionMinor = 0
|
||||
// VersionPatch is for backwards-compatible bug fixes
|
||||
VersionPatch = 0
|
||||
|
||||
// VersionDev indicates development branch. Releases will be empty string.
|
||||
VersionDev = "-rc4"
|
||||
)
|
||||
|
||||
// Version is the specification version that the package types support.
|
||||
var Version = fmt.Sprintf("%d.%d.%d%s", VersionMajor, VersionMinor, VersionPatch, VersionDev)
|
217
vendor/github.com/xeipuuv/gojsonpointer/pointer.go
generated
vendored
Normal file
217
vendor/github.com/xeipuuv/gojsonpointer/pointer.go
generated
vendored
Normal file
@@ -0,0 +1,217 @@
|
||||
// Copyright 2015 xeipuuv ( https://github.com/xeipuuv )
|
||||
//
|
||||
// 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.
|
||||
|
||||
// author xeipuuv
|
||||
// author-github https://github.com/xeipuuv
|
||||
// author-mail xeipuuv@gmail.com
|
||||
//
|
||||
// repository-name gojsonpointer
|
||||
// repository-desc An implementation of JSON Pointer - Go language
|
||||
//
|
||||
// description Main and unique file.
|
||||
//
|
||||
// created 25-02-2013
|
||||
|
||||
package gojsonpointer
|
||||
|
||||
import (
|
||||
"errors"
|
||||
"fmt"
|
||||
"reflect"
|
||||
"strconv"
|
||||
"strings"
|
||||
)
|
||||
|
||||
const (
|
||||
const_empty_pointer = ``
|
||||
const_pointer_separator = `/`
|
||||
|
||||
const_invalid_start = `JSON pointer must be empty or start with a "` + const_pointer_separator + `"`
|
||||
)
|
||||
|
||||
type implStruct struct {
|
||||
mode string // "SET" or "GET"
|
||||
|
||||
inDocument interface{}
|
||||
|
||||
setInValue interface{}
|
||||
|
||||
getOutNode interface{}
|
||||
getOutKind reflect.Kind
|
||||
outError error
|
||||
}
|
||||
|
||||
func NewJsonPointer(jsonPointerString string) (JsonPointer, error) {
|
||||
|
||||
var p JsonPointer
|
||||
err := p.parse(jsonPointerString)
|
||||
return p, err
|
||||
|
||||
}
|
||||
|
||||
type JsonPointer struct {
|
||||
referenceTokens []string
|
||||
}
|
||||
|
||||
// "Constructor", parses the given string JSON pointer
|
||||
func (p *JsonPointer) parse(jsonPointerString string) error {
|
||||
|
||||
var err error
|
||||
|
||||
if jsonPointerString != const_empty_pointer {
|
||||
if !strings.HasPrefix(jsonPointerString, const_pointer_separator) {
|
||||
err = errors.New(const_invalid_start)
|
||||
} else {
|
||||
referenceTokens := strings.Split(jsonPointerString, const_pointer_separator)
|
||||
for _, referenceToken := range referenceTokens[1:] {
|
||||
p.referenceTokens = append(p.referenceTokens, referenceToken)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
return err
|
||||
}
|
||||
|
||||
// Uses the pointer to retrieve a value from a JSON document
|
||||
func (p *JsonPointer) Get(document interface{}) (interface{}, reflect.Kind, error) {
|
||||
|
||||
is := &implStruct{mode: "GET", inDocument: document}
|
||||
p.implementation(is)
|
||||
return is.getOutNode, is.getOutKind, is.outError
|
||||
|
||||
}
|
||||
|
||||
// Uses the pointer to update a value from a JSON document
|
||||
func (p *JsonPointer) Set(document interface{}, value interface{}) (interface{}, error) {
|
||||
|
||||
is := &implStruct{mode: "SET", inDocument: document, setInValue: value}
|
||||
p.implementation(is)
|
||||
return document, is.outError
|
||||
|
||||
}
|
||||
|
||||
// Both Get and Set functions use the same implementation to avoid code duplication
|
||||
func (p *JsonPointer) implementation(i *implStruct) {
|
||||
|
||||
kind := reflect.Invalid
|
||||
|
||||
// Full document when empty
|
||||
if len(p.referenceTokens) == 0 {
|
||||
i.getOutNode = i.inDocument
|
||||
i.outError = nil
|
||||
i.getOutKind = kind
|
||||
i.outError = nil
|
||||
return
|
||||
}
|
||||
|
||||
node := i.inDocument
|
||||
|
||||
for ti, token := range p.referenceTokens {
|
||||
|
||||
decodedToken := decodeReferenceToken(token)
|
||||
isLastToken := ti == len(p.referenceTokens)-1
|
||||
|
||||
rValue := reflect.ValueOf(node)
|
||||
kind = rValue.Kind()
|
||||
|
||||
switch kind {
|
||||
|
||||
case reflect.Map:
|
||||
m := node.(map[string]interface{})
|
||||
if _, ok := m[decodedToken]; ok {
|
||||
node = m[decodedToken]
|
||||
if isLastToken && i.mode == "SET" {
|
||||
m[decodedToken] = i.setInValue
|
||||
}
|
||||
} else {
|
||||
i.outError = errors.New(fmt.Sprintf("Object has no key '%s'", token))
|
||||
i.getOutKind = kind
|
||||
i.getOutNode = nil
|
||||
return
|
||||
}
|
||||
|
||||
case reflect.Slice:
|
||||
s := node.([]interface{})
|
||||
tokenIndex, err := strconv.Atoi(token)
|
||||
if err != nil {
|
||||
i.outError = errors.New(fmt.Sprintf("Invalid array index '%s'", token))
|
||||
i.getOutKind = kind
|
||||
i.getOutNode = nil
|
||||
return
|
||||
}
|
||||
sLength := len(s)
|
||||
if tokenIndex < 0 || tokenIndex >= sLength {
|
||||
i.outError = errors.New(fmt.Sprintf("Out of bound array[0,%d] index '%d'", sLength, tokenIndex))
|
||||
i.getOutKind = kind
|
||||
i.getOutNode = nil
|
||||
return
|
||||
}
|
||||
|
||||
node = s[tokenIndex]
|
||||
if isLastToken && i.mode == "SET" {
|
||||
s[tokenIndex] = i.setInValue
|
||||
}
|
||||
|
||||
default:
|
||||
i.outError = errors.New(fmt.Sprintf("Invalid token reference '%s'", token))
|
||||
i.getOutKind = kind
|
||||
i.getOutNode = nil
|
||||
return
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
rValue := reflect.ValueOf(node)
|
||||
kind = rValue.Kind()
|
||||
|
||||
i.getOutNode = node
|
||||
i.getOutKind = kind
|
||||
i.outError = nil
|
||||
}
|
||||
|
||||
// Pointer to string representation function
|
||||
func (p *JsonPointer) String() string {
|
||||
|
||||
if len(p.referenceTokens) == 0 {
|
||||
return const_empty_pointer
|
||||
}
|
||||
|
||||
pointerString := const_pointer_separator + strings.Join(p.referenceTokens, const_pointer_separator)
|
||||
|
||||
return pointerString
|
||||
}
|
||||
|
||||
// Specific JSON pointer encoding here
|
||||
// ~0 => ~
|
||||
// ~1 => /
|
||||
// ... and vice versa
|
||||
|
||||
const (
|
||||
const_encoded_reference_token_0 = `~0`
|
||||
const_encoded_reference_token_1 = `~1`
|
||||
const_decoded_reference_token_0 = `~`
|
||||
const_decoded_reference_token_1 = `/`
|
||||
)
|
||||
|
||||
func decodeReferenceToken(token string) string {
|
||||
step1 := strings.Replace(token, const_encoded_reference_token_1, const_decoded_reference_token_1, -1)
|
||||
step2 := strings.Replace(step1, const_encoded_reference_token_0, const_decoded_reference_token_0, -1)
|
||||
return step2
|
||||
}
|
||||
|
||||
func encodeReferenceToken(token string) string {
|
||||
step1 := strings.Replace(token, const_decoded_reference_token_1, const_encoded_reference_token_1, -1)
|
||||
step2 := strings.Replace(step1, const_decoded_reference_token_0, const_encoded_reference_token_0, -1)
|
||||
return step2
|
||||
}
|
141
vendor/github.com/xeipuuv/gojsonreference/reference.go
generated
vendored
Normal file
141
vendor/github.com/xeipuuv/gojsonreference/reference.go
generated
vendored
Normal file
@@ -0,0 +1,141 @@
|
||||
// Copyright 2015 xeipuuv ( https://github.com/xeipuuv )
|
||||
//
|
||||
// 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.
|
||||
|
||||
// author xeipuuv
|
||||
// author-github https://github.com/xeipuuv
|
||||
// author-mail xeipuuv@gmail.com
|
||||
//
|
||||
// repository-name gojsonreference
|
||||
// repository-desc An implementation of JSON Reference - Go language
|
||||
//
|
||||
// description Main and unique file.
|
||||
//
|
||||
// created 26-02-2013
|
||||
|
||||
package gojsonreference
|
||||
|
||||
import (
|
||||
"errors"
|
||||
"github.com/xeipuuv/gojsonpointer"
|
||||
"net/url"
|
||||
"path/filepath"
|
||||
"runtime"
|
||||
"strings"
|
||||
)
|
||||
|
||||
const (
|
||||
const_fragment_char = `#`
|
||||
)
|
||||
|
||||
func NewJsonReference(jsonReferenceString string) (JsonReference, error) {
|
||||
|
||||
var r JsonReference
|
||||
err := r.parse(jsonReferenceString)
|
||||
return r, err
|
||||
|
||||
}
|
||||
|
||||
type JsonReference struct {
|
||||
referenceUrl *url.URL
|
||||
referencePointer gojsonpointer.JsonPointer
|
||||
|
||||
HasFullUrl bool
|
||||
HasUrlPathOnly bool
|
||||
HasFragmentOnly bool
|
||||
HasFileScheme bool
|
||||
HasFullFilePath bool
|
||||
}
|
||||
|
||||
func (r *JsonReference) GetUrl() *url.URL {
|
||||
return r.referenceUrl
|
||||
}
|
||||
|
||||
func (r *JsonReference) GetPointer() *gojsonpointer.JsonPointer {
|
||||
return &r.referencePointer
|
||||
}
|
||||
|
||||
func (r *JsonReference) String() string {
|
||||
|
||||
if r.referenceUrl != nil {
|
||||
return r.referenceUrl.String()
|
||||
}
|
||||
|
||||
if r.HasFragmentOnly {
|
||||
return const_fragment_char + r.referencePointer.String()
|
||||
}
|
||||
|
||||
return r.referencePointer.String()
|
||||
}
|
||||
|
||||
func (r *JsonReference) IsCanonical() bool {
|
||||
return (r.HasFileScheme && r.HasFullFilePath) || (!r.HasFileScheme && r.HasFullUrl)
|
||||
}
|
||||
|
||||
// "Constructor", parses the given string JSON reference
|
||||
func (r *JsonReference) parse(jsonReferenceString string) (err error) {
|
||||
|
||||
r.referenceUrl, err = url.Parse(jsonReferenceString)
|
||||
if err != nil {
|
||||
return
|
||||
}
|
||||
refUrl := r.referenceUrl
|
||||
|
||||
if refUrl.Scheme != "" && refUrl.Host != "" {
|
||||
r.HasFullUrl = true
|
||||
} else {
|
||||
if refUrl.Path != "" {
|
||||
r.HasUrlPathOnly = true
|
||||
} else if refUrl.RawQuery == "" && refUrl.Fragment != "" {
|
||||
r.HasFragmentOnly = true
|
||||
}
|
||||
}
|
||||
|
||||
r.HasFileScheme = refUrl.Scheme == "file"
|
||||
if runtime.GOOS == "windows" {
|
||||
// on Windows, a file URL may have an extra leading slash, and if it
|
||||
// doesn't then its first component will be treated as the host by the
|
||||
// Go runtime
|
||||
if refUrl.Host == "" && strings.HasPrefix(refUrl.Path, "/") {
|
||||
r.HasFullFilePath = filepath.IsAbs(refUrl.Path[1:])
|
||||
} else {
|
||||
r.HasFullFilePath = filepath.IsAbs(refUrl.Host + refUrl.Path)
|
||||
}
|
||||
} else {
|
||||
r.HasFullFilePath = filepath.IsAbs(refUrl.Path)
|
||||
}
|
||||
|
||||
// invalid json-pointer error means url has no json-pointer fragment. simply ignore error
|
||||
r.referencePointer, _ = gojsonpointer.NewJsonPointer(refUrl.Fragment)
|
||||
|
||||
return
|
||||
}
|
||||
|
||||
// Creates a new reference from a parent and a child
|
||||
// If the child cannot inherit from the parent, an error is returned
|
||||
func (r *JsonReference) Inherits(child JsonReference) (*JsonReference, error) {
|
||||
childUrl := child.GetUrl()
|
||||
parentUrl := r.GetUrl()
|
||||
if childUrl == nil {
|
||||
return nil, errors.New("childUrl is nil!")
|
||||
}
|
||||
if parentUrl == nil {
|
||||
return nil, errors.New("parentUrl is nil!")
|
||||
}
|
||||
|
||||
ref, err := NewJsonReference(parentUrl.ResolveReference(childUrl).String())
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
return &ref, err
|
||||
}
|
274
vendor/github.com/xeipuuv/gojsonschema/errors.go
generated
vendored
Normal file
274
vendor/github.com/xeipuuv/gojsonschema/errors.go
generated
vendored
Normal file
@@ -0,0 +1,274 @@
|
||||
package gojsonschema
|
||||
|
||||
import (
|
||||
"bytes"
|
||||
"sync"
|
||||
"text/template"
|
||||
)
|
||||
|
||||
var errorTemplates errorTemplate = errorTemplate{template.New("errors-new"),sync.RWMutex{}}
|
||||
|
||||
// template.Template is not thread-safe for writing, so some locking is done
|
||||
// sync.RWMutex is used for efficiently locking when new templates are created
|
||||
type errorTemplate struct {
|
||||
*template.Template
|
||||
sync.RWMutex
|
||||
}
|
||||
|
||||
type (
|
||||
// RequiredError. ErrorDetails: property string
|
||||
RequiredError struct {
|
||||
ResultErrorFields
|
||||
}
|
||||
|
||||
// InvalidTypeError. ErrorDetails: expected, given
|
||||
InvalidTypeError struct {
|
||||
ResultErrorFields
|
||||
}
|
||||
|
||||
// NumberAnyOfError. ErrorDetails: -
|
||||
NumberAnyOfError struct {
|
||||
ResultErrorFields
|
||||
}
|
||||
|
||||
// NumberOneOfError. ErrorDetails: -
|
||||
NumberOneOfError struct {
|
||||
ResultErrorFields
|
||||
}
|
||||
|
||||
// NumberAllOfError. ErrorDetails: -
|
||||
NumberAllOfError struct {
|
||||
ResultErrorFields
|
||||
}
|
||||
|
||||
// NumberNotError. ErrorDetails: -
|
||||
NumberNotError struct {
|
||||
ResultErrorFields
|
||||
}
|
||||
|
||||
// MissingDependencyError. ErrorDetails: dependency
|
||||
MissingDependencyError struct {
|
||||
ResultErrorFields
|
||||
}
|
||||
|
||||
// InternalError. ErrorDetails: error
|
||||
InternalError struct {
|
||||
ResultErrorFields
|
||||
}
|
||||
|
||||
// EnumError. ErrorDetails: allowed
|
||||
EnumError struct {
|
||||
ResultErrorFields
|
||||
}
|
||||
|
||||
// ArrayNoAdditionalItemsError. ErrorDetails: -
|
||||
ArrayNoAdditionalItemsError struct {
|
||||
ResultErrorFields
|
||||
}
|
||||
|
||||
// ArrayMinItemsError. ErrorDetails: min
|
||||
ArrayMinItemsError struct {
|
||||
ResultErrorFields
|
||||
}
|
||||
|
||||
// ArrayMaxItemsError. ErrorDetails: max
|
||||
ArrayMaxItemsError struct {
|
||||
ResultErrorFields
|
||||
}
|
||||
|
||||
// ItemsMustBeUniqueError. ErrorDetails: type
|
||||
ItemsMustBeUniqueError struct {
|
||||
ResultErrorFields
|
||||
}
|
||||
|
||||
// ArrayMinPropertiesError. ErrorDetails: min
|
||||
ArrayMinPropertiesError struct {
|
||||
ResultErrorFields
|
||||
}
|
||||
|
||||
// ArrayMaxPropertiesError. ErrorDetails: max
|
||||
ArrayMaxPropertiesError struct {
|
||||
ResultErrorFields
|
||||
}
|
||||
|
||||
// AdditionalPropertyNotAllowedError. ErrorDetails: property
|
||||
AdditionalPropertyNotAllowedError struct {
|
||||
ResultErrorFields
|
||||
}
|
||||
|
||||
// InvalidPropertyPatternError. ErrorDetails: property, pattern
|
||||
InvalidPropertyPatternError struct {
|
||||
ResultErrorFields
|
||||
}
|
||||
|
||||
// StringLengthGTEError. ErrorDetails: min
|
||||
StringLengthGTEError struct {
|
||||
ResultErrorFields
|
||||
}
|
||||
|
||||
// StringLengthLTEError. ErrorDetails: max
|
||||
StringLengthLTEError struct {
|
||||
ResultErrorFields
|
||||
}
|
||||
|
||||
// DoesNotMatchPatternError. ErrorDetails: pattern
|
||||
DoesNotMatchPatternError struct {
|
||||
ResultErrorFields
|
||||
}
|
||||
|
||||
// DoesNotMatchFormatError. ErrorDetails: format
|
||||
DoesNotMatchFormatError struct {
|
||||
ResultErrorFields
|
||||
}
|
||||
|
||||
// MultipleOfError. ErrorDetails: multiple
|
||||
MultipleOfError struct {
|
||||
ResultErrorFields
|
||||
}
|
||||
|
||||
// NumberGTEError. ErrorDetails: min
|
||||
NumberGTEError struct {
|
||||
ResultErrorFields
|
||||
}
|
||||
|
||||
// NumberGTError. ErrorDetails: min
|
||||
NumberGTError struct {
|
||||
ResultErrorFields
|
||||
}
|
||||
|
||||
// NumberLTEError. ErrorDetails: max
|
||||
NumberLTEError struct {
|
||||
ResultErrorFields
|
||||
}
|
||||
|
||||
// NumberLTError. ErrorDetails: max
|
||||
NumberLTError struct {
|
||||
ResultErrorFields
|
||||
}
|
||||
)
|
||||
|
||||
// newError takes a ResultError type and sets the type, context, description, details, value, and field
|
||||
func newError(err ResultError, context *jsonContext, value interface{}, locale locale, details ErrorDetails) {
|
||||
var t string
|
||||
var d string
|
||||
switch err.(type) {
|
||||
case *RequiredError:
|
||||
t = "required"
|
||||
d = locale.Required()
|
||||
case *InvalidTypeError:
|
||||
t = "invalid_type"
|
||||
d = locale.InvalidType()
|
||||
case *NumberAnyOfError:
|
||||
t = "number_any_of"
|
||||
d = locale.NumberAnyOf()
|
||||
case *NumberOneOfError:
|
||||
t = "number_one_of"
|
||||
d = locale.NumberOneOf()
|
||||
case *NumberAllOfError:
|
||||
t = "number_all_of"
|
||||
d = locale.NumberAllOf()
|
||||
case *NumberNotError:
|
||||
t = "number_not"
|
||||
d = locale.NumberNot()
|
||||
case *MissingDependencyError:
|
||||
t = "missing_dependency"
|
||||
d = locale.MissingDependency()
|
||||
case *InternalError:
|
||||
t = "internal"
|
||||
d = locale.Internal()
|
||||
case *EnumError:
|
||||
t = "enum"
|
||||
d = locale.Enum()
|
||||
case *ArrayNoAdditionalItemsError:
|
||||
t = "array_no_additional_items"
|
||||
d = locale.ArrayNoAdditionalItems()
|
||||
case *ArrayMinItemsError:
|
||||
t = "array_min_items"
|
||||
d = locale.ArrayMinItems()
|
||||
case *ArrayMaxItemsError:
|
||||
t = "array_max_items"
|
||||
d = locale.ArrayMaxItems()
|
||||
case *ItemsMustBeUniqueError:
|
||||
t = "unique"
|
||||
d = locale.Unique()
|
||||
case *ArrayMinPropertiesError:
|
||||
t = "array_min_properties"
|
||||
d = locale.ArrayMinProperties()
|
||||
case *ArrayMaxPropertiesError:
|
||||
t = "array_max_properties"
|
||||
d = locale.ArrayMaxProperties()
|
||||
case *AdditionalPropertyNotAllowedError:
|
||||
t = "additional_property_not_allowed"
|
||||
d = locale.AdditionalPropertyNotAllowed()
|
||||
case *InvalidPropertyPatternError:
|
||||
t = "invalid_property_pattern"
|
||||
d = locale.InvalidPropertyPattern()
|
||||
case *StringLengthGTEError:
|
||||
t = "string_gte"
|
||||
d = locale.StringGTE()
|
||||
case *StringLengthLTEError:
|
||||
t = "string_lte"
|
||||
d = locale.StringLTE()
|
||||
case *DoesNotMatchPatternError:
|
||||
t = "pattern"
|
||||
d = locale.DoesNotMatchPattern()
|
||||
case *DoesNotMatchFormatError:
|
||||
t = "format"
|
||||
d = locale.DoesNotMatchFormat()
|
||||
case *MultipleOfError:
|
||||
t = "multiple_of"
|
||||
d = locale.MultipleOf()
|
||||
case *NumberGTEError:
|
||||
t = "number_gte"
|
||||
d = locale.NumberGTE()
|
||||
case *NumberGTError:
|
||||
t = "number_gt"
|
||||
d = locale.NumberGT()
|
||||
case *NumberLTEError:
|
||||
t = "number_lte"
|
||||
d = locale.NumberLTE()
|
||||
case *NumberLTError:
|
||||
t = "number_lt"
|
||||
d = locale.NumberLT()
|
||||
}
|
||||
|
||||
err.SetType(t)
|
||||
err.SetContext(context)
|
||||
err.SetValue(value)
|
||||
err.SetDetails(details)
|
||||
details["field"] = err.Field()
|
||||
err.SetDescription(formatErrorDescription(d, details))
|
||||
}
|
||||
|
||||
// formatErrorDescription takes a string in the default text/template
|
||||
// format and converts it to a string with replacements. The fields come
|
||||
// from the ErrorDetails struct and vary for each type of error.
|
||||
func formatErrorDescription(s string, details ErrorDetails) string {
|
||||
|
||||
var tpl *template.Template
|
||||
var descrAsBuffer bytes.Buffer
|
||||
var err error
|
||||
|
||||
errorTemplates.RLock()
|
||||
tpl = errorTemplates.Lookup(s)
|
||||
errorTemplates.RUnlock()
|
||||
|
||||
if tpl == nil {
|
||||
errorTemplates.Lock()
|
||||
tpl = errorTemplates.New(s)
|
||||
|
||||
tpl, err = tpl.Parse(s)
|
||||
errorTemplates.Unlock()
|
||||
|
||||
if err != nil {
|
||||
return err.Error()
|
||||
}
|
||||
}
|
||||
|
||||
err = tpl.Execute(&descrAsBuffer, details)
|
||||
if err != nil {
|
||||
return err.Error()
|
||||
}
|
||||
|
||||
return descrAsBuffer.String()
|
||||
}
|
194
vendor/github.com/xeipuuv/gojsonschema/format_checkers.go
generated
vendored
Normal file
194
vendor/github.com/xeipuuv/gojsonschema/format_checkers.go
generated
vendored
Normal file
@@ -0,0 +1,194 @@
|
||||
package gojsonschema
|
||||
|
||||
import (
|
||||
"net"
|
||||
"net/url"
|
||||
"reflect"
|
||||
"regexp"
|
||||
"strings"
|
||||
"time"
|
||||
)
|
||||
|
||||
type (
|
||||
// FormatChecker is the interface all formatters added to FormatCheckerChain must implement
|
||||
FormatChecker interface {
|
||||
IsFormat(input string) bool
|
||||
}
|
||||
|
||||
// FormatCheckerChain holds the formatters
|
||||
FormatCheckerChain struct {
|
||||
formatters map[string]FormatChecker
|
||||
}
|
||||
|
||||
// EmailFormatter verifies email address formats
|
||||
EmailFormatChecker struct{}
|
||||
|
||||
// IPV4FormatChecker verifies IP addresses in the ipv4 format
|
||||
IPV4FormatChecker struct{}
|
||||
|
||||
// IPV6FormatChecker verifies IP addresses in the ipv6 format
|
||||
IPV6FormatChecker struct{}
|
||||
|
||||
// DateTimeFormatChecker verifies date/time formats per RFC3339 5.6
|
||||
//
|
||||
// Valid formats:
|
||||
// Partial Time: HH:MM:SS
|
||||
// Full Date: YYYY-MM-DD
|
||||
// Full Time: HH:MM:SSZ-07:00
|
||||
// Date Time: YYYY-MM-DDTHH:MM:SSZ-0700
|
||||
//
|
||||
// Where
|
||||
// YYYY = 4DIGIT year
|
||||
// MM = 2DIGIT month ; 01-12
|
||||
// DD = 2DIGIT day-month ; 01-28, 01-29, 01-30, 01-31 based on month/year
|
||||
// HH = 2DIGIT hour ; 00-23
|
||||
// MM = 2DIGIT ; 00-59
|
||||
// SS = 2DIGIT ; 00-58, 00-60 based on leap second rules
|
||||
// T = Literal
|
||||
// Z = Literal
|
||||
//
|
||||
// Note: Nanoseconds are also suported in all formats
|
||||
//
|
||||
// http://tools.ietf.org/html/rfc3339#section-5.6
|
||||
DateTimeFormatChecker struct{}
|
||||
|
||||
// URIFormatCheckers validates a URI with a valid Scheme per RFC3986
|
||||
URIFormatChecker struct{}
|
||||
|
||||
// HostnameFormatChecker validates a hostname is in the correct format
|
||||
HostnameFormatChecker struct{}
|
||||
|
||||
// UUIDFormatChecker validates a UUID is in the correct format
|
||||
UUIDFormatChecker struct{}
|
||||
|
||||
// RegexFormatChecker validates a regex is in the correct format
|
||||
RegexFormatChecker struct{}
|
||||
)
|
||||
|
||||
var (
|
||||
// Formatters holds the valid formatters, and is a public variable
|
||||
// so library users can add custom formatters
|
||||
FormatCheckers = FormatCheckerChain{
|
||||
formatters: map[string]FormatChecker{
|
||||
"date-time": DateTimeFormatChecker{},
|
||||
"hostname": HostnameFormatChecker{},
|
||||
"email": EmailFormatChecker{},
|
||||
"ipv4": IPV4FormatChecker{},
|
||||
"ipv6": IPV6FormatChecker{},
|
||||
"uri": URIFormatChecker{},
|
||||
"uuid": UUIDFormatChecker{},
|
||||
"regex": RegexFormatChecker{},
|
||||
},
|
||||
}
|
||||
|
||||
// Regex credit: https://github.com/asaskevich/govalidator
|
||||
rxEmail = regexp.MustCompile("^(((([a-zA-Z]|\\d|[!#\\$%&'\\*\\+\\-\\/=\\?\\^_`{\\|}~]|[\\x{00A0}-\\x{D7FF}\\x{F900}-\\x{FDCF}\\x{FDF0}-\\x{FFEF}])+(\\.([a-zA-Z]|\\d|[!#\\$%&'\\*\\+\\-\\/=\\?\\^_`{\\|}~]|[\\x{00A0}-\\x{D7FF}\\x{F900}-\\x{FDCF}\\x{FDF0}-\\x{FFEF}])+)*)|((\\x22)((((\\x20|\\x09)*(\\x0d\\x0a))?(\\x20|\\x09)+)?(([\\x01-\\x08\\x0b\\x0c\\x0e-\\x1f\\x7f]|\\x21|[\\x23-\\x5b]|[\\x5d-\\x7e]|[\\x{00A0}-\\x{D7FF}\\x{F900}-\\x{FDCF}\\x{FDF0}-\\x{FFEF}])|(\\([\\x01-\\x09\\x0b\\x0c\\x0d-\\x7f]|[\\x{00A0}-\\x{D7FF}\\x{F900}-\\x{FDCF}\\x{FDF0}-\\x{FFEF}]))))*(((\\x20|\\x09)*(\\x0d\\x0a))?(\\x20|\\x09)+)?(\\x22)))@((([a-zA-Z]|\\d|[\\x{00A0}-\\x{D7FF}\\x{F900}-\\x{FDCF}\\x{FDF0}-\\x{FFEF}])|(([a-zA-Z]|\\d|[\\x{00A0}-\\x{D7FF}\\x{F900}-\\x{FDCF}\\x{FDF0}-\\x{FFEF}])([a-zA-Z]|\\d|-|\\.|_|~|[\\x{00A0}-\\x{D7FF}\\x{F900}-\\x{FDCF}\\x{FDF0}-\\x{FFEF}])*([a-zA-Z]|\\d|[\\x{00A0}-\\x{D7FF}\\x{F900}-\\x{FDCF}\\x{FDF0}-\\x{FFEF}])))\\.)+(([a-zA-Z]|[\\x{00A0}-\\x{D7FF}\\x{F900}-\\x{FDCF}\\x{FDF0}-\\x{FFEF}])|(([a-zA-Z]|[\\x{00A0}-\\x{D7FF}\\x{F900}-\\x{FDCF}\\x{FDF0}-\\x{FFEF}])([a-zA-Z]|\\d|-|\\.|_|~|[\\x{00A0}-\\x{D7FF}\\x{F900}-\\x{FDCF}\\x{FDF0}-\\x{FFEF}])*([a-zA-Z]|[\\x{00A0}-\\x{D7FF}\\x{F900}-\\x{FDCF}\\x{FDF0}-\\x{FFEF}])))\\.?$")
|
||||
|
||||
// Regex credit: https://www.socketloop.com/tutorials/golang-validate-hostname
|
||||
rxHostname = regexp.MustCompile(`^([a-zA-Z0-9]|[a-zA-Z0-9][a-zA-Z0-9\-]{0,61}[a-zA-Z0-9])(\.([a-zA-Z0-9]|[a-zA-Z0-9][a-zA-Z0-9\-]{0,61}[a-zA-Z0-9]))*$`)
|
||||
|
||||
rxUUID = regexp.MustCompile("^[a-f0-9]{8}-[a-f0-9]{4}-[a-f0-9]{4}-[a-f0-9]{4}-[a-f0-9]{12}$")
|
||||
)
|
||||
|
||||
// Add adds a FormatChecker to the FormatCheckerChain
|
||||
// The name used will be the value used for the format key in your json schema
|
||||
func (c *FormatCheckerChain) Add(name string, f FormatChecker) *FormatCheckerChain {
|
||||
c.formatters[name] = f
|
||||
|
||||
return c
|
||||
}
|
||||
|
||||
// Remove deletes a FormatChecker from the FormatCheckerChain (if it exists)
|
||||
func (c *FormatCheckerChain) Remove(name string) *FormatCheckerChain {
|
||||
delete(c.formatters, name)
|
||||
|
||||
return c
|
||||
}
|
||||
|
||||
// Has checks to see if the FormatCheckerChain holds a FormatChecker with the given name
|
||||
func (c *FormatCheckerChain) Has(name string) bool {
|
||||
_, ok := c.formatters[name]
|
||||
|
||||
return ok
|
||||
}
|
||||
|
||||
// IsFormat will check an input against a FormatChecker with the given name
|
||||
// to see if it is the correct format
|
||||
func (c *FormatCheckerChain) IsFormat(name string, input interface{}) bool {
|
||||
f, ok := c.formatters[name]
|
||||
|
||||
if !ok {
|
||||
return false
|
||||
}
|
||||
|
||||
if !isKind(input, reflect.String) {
|
||||
return false
|
||||
}
|
||||
|
||||
inputString := input.(string)
|
||||
|
||||
return f.IsFormat(inputString)
|
||||
}
|
||||
|
||||
func (f EmailFormatChecker) IsFormat(input string) bool {
|
||||
return rxEmail.MatchString(input)
|
||||
}
|
||||
|
||||
// Credit: https://github.com/asaskevich/govalidator
|
||||
func (f IPV4FormatChecker) IsFormat(input string) bool {
|
||||
ip := net.ParseIP(input)
|
||||
return ip != nil && strings.Contains(input, ".")
|
||||
}
|
||||
|
||||
// Credit: https://github.com/asaskevich/govalidator
|
||||
func (f IPV6FormatChecker) IsFormat(input string) bool {
|
||||
ip := net.ParseIP(input)
|
||||
return ip != nil && strings.Contains(input, ":")
|
||||
}
|
||||
|
||||
func (f DateTimeFormatChecker) IsFormat(input string) bool {
|
||||
formats := []string{
|
||||
"15:04:05",
|
||||
"15:04:05Z07:00",
|
||||
"2006-01-02",
|
||||
time.RFC3339,
|
||||
time.RFC3339Nano,
|
||||
}
|
||||
|
||||
for _, format := range formats {
|
||||
if _, err := time.Parse(format, input); err == nil {
|
||||
return true
|
||||
}
|
||||
}
|
||||
|
||||
return false
|
||||
}
|
||||
|
||||
func (f URIFormatChecker) IsFormat(input string) bool {
|
||||
u, err := url.Parse(input)
|
||||
if err != nil || u.Scheme == "" {
|
||||
return false
|
||||
}
|
||||
|
||||
return true
|
||||
}
|
||||
|
||||
func (f HostnameFormatChecker) IsFormat(input string) bool {
|
||||
return rxHostname.MatchString(input) && len(input) < 256
|
||||
}
|
||||
|
||||
func (f UUIDFormatChecker) IsFormat(input string) bool {
|
||||
return rxUUID.MatchString(input)
|
||||
}
|
||||
|
||||
// IsFormat implements FormatChecker interface.
|
||||
func (f RegexFormatChecker) IsFormat(input string) bool {
|
||||
if input == "" {
|
||||
return true
|
||||
}
|
||||
_, err := regexp.Compile(input)
|
||||
if err != nil {
|
||||
return false
|
||||
}
|
||||
return true
|
||||
}
|
37
vendor/github.com/xeipuuv/gojsonschema/internalLog.go
generated
vendored
Normal file
37
vendor/github.com/xeipuuv/gojsonschema/internalLog.go
generated
vendored
Normal file
@@ -0,0 +1,37 @@
|
||||
// Copyright 2015 xeipuuv ( https://github.com/xeipuuv )
|
||||
//
|
||||
// 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.
|
||||
|
||||
// author xeipuuv
|
||||
// author-github https://github.com/xeipuuv
|
||||
// author-mail xeipuuv@gmail.com
|
||||
//
|
||||
// repository-name gojsonschema
|
||||
// repository-desc An implementation of JSON Schema, based on IETF's draft v4 - Go language.
|
||||
//
|
||||
// description Very simple log wrapper.
|
||||
// Used for debugging/testing purposes.
|
||||
//
|
||||
// created 01-01-2015
|
||||
|
||||
package gojsonschema
|
||||
|
||||
import (
|
||||
"log"
|
||||
)
|
||||
|
||||
const internalLogEnabled = false
|
||||
|
||||
func internalLog(format string, v ...interface{}) {
|
||||
log.Printf(format, v...)
|
||||
}
|
72
vendor/github.com/xeipuuv/gojsonschema/jsonContext.go
generated
vendored
Normal file
72
vendor/github.com/xeipuuv/gojsonschema/jsonContext.go
generated
vendored
Normal file
@@ -0,0 +1,72 @@
|
||||
// Copyright 2013 MongoDB, Inc.
|
||||
//
|
||||
// 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.
|
||||
|
||||
// author tolsen
|
||||
// author-github https://github.com/tolsen
|
||||
//
|
||||
// repository-name gojsonschema
|
||||
// repository-desc An implementation of JSON Schema, based on IETF's draft v4 - Go language.
|
||||
//
|
||||
// description Implements a persistent (immutable w/ shared structure) singly-linked list of strings for the purpose of storing a json context
|
||||
//
|
||||
// created 04-09-2013
|
||||
|
||||
package gojsonschema
|
||||
|
||||
import "bytes"
|
||||
|
||||
// jsonContext implements a persistent linked-list of strings
|
||||
type jsonContext struct {
|
||||
head string
|
||||
tail *jsonContext
|
||||
}
|
||||
|
||||
func newJsonContext(head string, tail *jsonContext) *jsonContext {
|
||||
return &jsonContext{head, tail}
|
||||
}
|
||||
|
||||
// String displays the context in reverse.
|
||||
// This plays well with the data structure's persistent nature with
|
||||
// Cons and a json document's tree structure.
|
||||
func (c *jsonContext) String(del ...string) string {
|
||||
byteArr := make([]byte, 0, c.stringLen())
|
||||
buf := bytes.NewBuffer(byteArr)
|
||||
c.writeStringToBuffer(buf, del)
|
||||
|
||||
return buf.String()
|
||||
}
|
||||
|
||||
func (c *jsonContext) stringLen() int {
|
||||
length := 0
|
||||
if c.tail != nil {
|
||||
length = c.tail.stringLen() + 1 // add 1 for "."
|
||||
}
|
||||
|
||||
length += len(c.head)
|
||||
return length
|
||||
}
|
||||
|
||||
func (c *jsonContext) writeStringToBuffer(buf *bytes.Buffer, del []string) {
|
||||
if c.tail != nil {
|
||||
c.tail.writeStringToBuffer(buf, del)
|
||||
|
||||
if len(del) > 0 {
|
||||
buf.WriteString(del[0])
|
||||
} else {
|
||||
buf.WriteString(".")
|
||||
}
|
||||
}
|
||||
|
||||
buf.WriteString(c.head)
|
||||
}
|
340
vendor/github.com/xeipuuv/gojsonschema/jsonLoader.go
generated
vendored
Normal file
340
vendor/github.com/xeipuuv/gojsonschema/jsonLoader.go
generated
vendored
Normal file
@@ -0,0 +1,340 @@
|
||||
// Copyright 2015 xeipuuv ( https://github.com/xeipuuv )
|
||||
//
|
||||
// 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.
|
||||
|
||||
// author xeipuuv
|
||||
// author-github https://github.com/xeipuuv
|
||||
// author-mail xeipuuv@gmail.com
|
||||
//
|
||||
// repository-name gojsonschema
|
||||
// repository-desc An implementation of JSON Schema, based on IETF's draft v4 - Go language.
|
||||
//
|
||||
// description Different strategies to load JSON files.
|
||||
// Includes References (file and HTTP), JSON strings and Go types.
|
||||
//
|
||||
// created 01-02-2015
|
||||
|
||||
package gojsonschema
|
||||
|
||||
import (
|
||||
"bytes"
|
||||
"encoding/json"
|
||||
"errors"
|
||||
"io"
|
||||
"io/ioutil"
|
||||
"net/http"
|
||||
"os"
|
||||
"path/filepath"
|
||||
"runtime"
|
||||
"strings"
|
||||
|
||||
"github.com/xeipuuv/gojsonreference"
|
||||
)
|
||||
|
||||
var osFS = osFileSystem(os.Open)
|
||||
|
||||
// JSON loader interface
|
||||
|
||||
type JSONLoader interface {
|
||||
JsonSource() interface{}
|
||||
LoadJSON() (interface{}, error)
|
||||
JsonReference() (gojsonreference.JsonReference, error)
|
||||
LoaderFactory() JSONLoaderFactory
|
||||
}
|
||||
|
||||
type JSONLoaderFactory interface {
|
||||
New(source string) JSONLoader
|
||||
}
|
||||
|
||||
type DefaultJSONLoaderFactory struct {
|
||||
}
|
||||
|
||||
type FileSystemJSONLoaderFactory struct {
|
||||
fs http.FileSystem
|
||||
}
|
||||
|
||||
func (d DefaultJSONLoaderFactory) New(source string) JSONLoader {
|
||||
return &jsonReferenceLoader{
|
||||
fs: osFS,
|
||||
source: source,
|
||||
}
|
||||
}
|
||||
|
||||
func (f FileSystemJSONLoaderFactory) New(source string) JSONLoader {
|
||||
return &jsonReferenceLoader{
|
||||
fs: f.fs,
|
||||
source: source,
|
||||
}
|
||||
}
|
||||
|
||||
// osFileSystem is a functional wrapper for os.Open that implements http.FileSystem.
|
||||
type osFileSystem func(string) (*os.File, error)
|
||||
|
||||
func (o osFileSystem) Open(name string) (http.File, error) {
|
||||
return o(name)
|
||||
}
|
||||
|
||||
// JSON Reference loader
|
||||
// references are used to load JSONs from files and HTTP
|
||||
|
||||
type jsonReferenceLoader struct {
|
||||
fs http.FileSystem
|
||||
source string
|
||||
}
|
||||
|
||||
func (l *jsonReferenceLoader) JsonSource() interface{} {
|
||||
return l.source
|
||||
}
|
||||
|
||||
func (l *jsonReferenceLoader) JsonReference() (gojsonreference.JsonReference, error) {
|
||||
return gojsonreference.NewJsonReference(l.JsonSource().(string))
|
||||
}
|
||||
|
||||
func (l *jsonReferenceLoader) LoaderFactory() JSONLoaderFactory {
|
||||
return &FileSystemJSONLoaderFactory{
|
||||
fs: l.fs,
|
||||
}
|
||||
}
|
||||
|
||||
// NewReferenceLoader returns a JSON reference loader using the given source and the local OS file system.
|
||||
func NewReferenceLoader(source string) *jsonReferenceLoader {
|
||||
return &jsonReferenceLoader{
|
||||
fs: osFS,
|
||||
source: source,
|
||||
}
|
||||
}
|
||||
|
||||
// NewReferenceLoaderFileSystem returns a JSON reference loader using the given source and file system.
|
||||
func NewReferenceLoaderFileSystem(source string, fs http.FileSystem) *jsonReferenceLoader {
|
||||
return &jsonReferenceLoader{
|
||||
fs: fs,
|
||||
source: source,
|
||||
}
|
||||
}
|
||||
|
||||
func (l *jsonReferenceLoader) LoadJSON() (interface{}, error) {
|
||||
|
||||
var err error
|
||||
|
||||
reference, err := gojsonreference.NewJsonReference(l.JsonSource().(string))
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
refToUrl := reference
|
||||
refToUrl.GetUrl().Fragment = ""
|
||||
|
||||
var document interface{}
|
||||
|
||||
if reference.HasFileScheme {
|
||||
|
||||
filename := strings.Replace(refToUrl.GetUrl().Path, "file://", "", -1)
|
||||
if runtime.GOOS == "windows" {
|
||||
// on Windows, a file URL may have an extra leading slash, use slashes
|
||||
// instead of backslashes, and have spaces escaped
|
||||
if strings.HasPrefix(filename, "/") {
|
||||
filename = filename[1:]
|
||||
}
|
||||
filename = filepath.FromSlash(filename)
|
||||
}
|
||||
|
||||
document, err = l.loadFromFile(filename)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
} else {
|
||||
|
||||
document, err = l.loadFromHTTP(refToUrl.String())
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
return document, nil
|
||||
|
||||
}
|
||||
|
||||
func (l *jsonReferenceLoader) loadFromHTTP(address string) (interface{}, error) {
|
||||
|
||||
resp, err := http.Get(address)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
// must return HTTP Status 200 OK
|
||||
if resp.StatusCode != http.StatusOK {
|
||||
return nil, errors.New(formatErrorDescription(Locale.httpBadStatus(), ErrorDetails{"status": resp.Status}))
|
||||
}
|
||||
|
||||
bodyBuff, err := ioutil.ReadAll(resp.Body)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
return decodeJsonUsingNumber(bytes.NewReader(bodyBuff))
|
||||
|
||||
}
|
||||
|
||||
func (l *jsonReferenceLoader) loadFromFile(path string) (interface{}, error) {
|
||||
f, err := l.fs.Open(path)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
defer f.Close()
|
||||
|
||||
bodyBuff, err := ioutil.ReadAll(f)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
return decodeJsonUsingNumber(bytes.NewReader(bodyBuff))
|
||||
|
||||
}
|
||||
|
||||
// JSON string loader
|
||||
|
||||
type jsonStringLoader struct {
|
||||
source string
|
||||
}
|
||||
|
||||
func (l *jsonStringLoader) JsonSource() interface{} {
|
||||
return l.source
|
||||
}
|
||||
|
||||
func (l *jsonStringLoader) JsonReference() (gojsonreference.JsonReference, error) {
|
||||
return gojsonreference.NewJsonReference("#")
|
||||
}
|
||||
|
||||
func (l *jsonStringLoader) LoaderFactory() JSONLoaderFactory {
|
||||
return &DefaultJSONLoaderFactory{}
|
||||
}
|
||||
|
||||
func NewStringLoader(source string) *jsonStringLoader {
|
||||
return &jsonStringLoader{source: source}
|
||||
}
|
||||
|
||||
func (l *jsonStringLoader) LoadJSON() (interface{}, error) {
|
||||
|
||||
return decodeJsonUsingNumber(strings.NewReader(l.JsonSource().(string)))
|
||||
|
||||
}
|
||||
|
||||
// JSON bytes loader
|
||||
|
||||
type jsonBytesLoader struct {
|
||||
source []byte
|
||||
}
|
||||
|
||||
func (l *jsonBytesLoader) JsonSource() interface{} {
|
||||
return l.source
|
||||
}
|
||||
|
||||
func (l *jsonBytesLoader) JsonReference() (gojsonreference.JsonReference, error) {
|
||||
return gojsonreference.NewJsonReference("#")
|
||||
}
|
||||
|
||||
func (l *jsonBytesLoader) LoaderFactory() JSONLoaderFactory {
|
||||
return &DefaultJSONLoaderFactory{}
|
||||
}
|
||||
|
||||
func NewBytesLoader(source []byte) *jsonBytesLoader {
|
||||
return &jsonBytesLoader{source: source}
|
||||
}
|
||||
|
||||
func (l *jsonBytesLoader) LoadJSON() (interface{}, error) {
|
||||
return decodeJsonUsingNumber(bytes.NewReader(l.JsonSource().([]byte)))
|
||||
}
|
||||
|
||||
// JSON Go (types) loader
|
||||
// used to load JSONs from the code as maps, interface{}, structs ...
|
||||
|
||||
type jsonGoLoader struct {
|
||||
source interface{}
|
||||
}
|
||||
|
||||
func (l *jsonGoLoader) JsonSource() interface{} {
|
||||
return l.source
|
||||
}
|
||||
|
||||
func (l *jsonGoLoader) JsonReference() (gojsonreference.JsonReference, error) {
|
||||
return gojsonreference.NewJsonReference("#")
|
||||
}
|
||||
|
||||
func (l *jsonGoLoader) LoaderFactory() JSONLoaderFactory {
|
||||
return &DefaultJSONLoaderFactory{}
|
||||
}
|
||||
|
||||
func NewGoLoader(source interface{}) *jsonGoLoader {
|
||||
return &jsonGoLoader{source: source}
|
||||
}
|
||||
|
||||
func (l *jsonGoLoader) LoadJSON() (interface{}, error) {
|
||||
|
||||
// convert it to a compliant JSON first to avoid types "mismatches"
|
||||
|
||||
jsonBytes, err := json.Marshal(l.JsonSource())
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
return decodeJsonUsingNumber(bytes.NewReader(jsonBytes))
|
||||
|
||||
}
|
||||
|
||||
type jsonIOLoader struct {
|
||||
buf *bytes.Buffer
|
||||
}
|
||||
|
||||
func NewReaderLoader(source io.Reader) (*jsonIOLoader, io.Reader) {
|
||||
buf := &bytes.Buffer{}
|
||||
return &jsonIOLoader{buf: buf}, io.TeeReader(source, buf)
|
||||
}
|
||||
|
||||
func NewWriterLoader(source io.Writer) (*jsonIOLoader, io.Writer) {
|
||||
buf := &bytes.Buffer{}
|
||||
return &jsonIOLoader{buf: buf}, io.MultiWriter(source, buf)
|
||||
}
|
||||
|
||||
func (l *jsonIOLoader) JsonSource() interface{} {
|
||||
return l.buf.String()
|
||||
}
|
||||
|
||||
func (l *jsonIOLoader) LoadJSON() (interface{}, error) {
|
||||
return decodeJsonUsingNumber(l.buf)
|
||||
}
|
||||
|
||||
func (l *jsonIOLoader) JsonReference() (gojsonreference.JsonReference, error) {
|
||||
return gojsonreference.NewJsonReference("#")
|
||||
}
|
||||
|
||||
func (l *jsonIOLoader) LoaderFactory() JSONLoaderFactory {
|
||||
return &DefaultJSONLoaderFactory{}
|
||||
}
|
||||
|
||||
func decodeJsonUsingNumber(r io.Reader) (interface{}, error) {
|
||||
|
||||
var document interface{}
|
||||
|
||||
decoder := json.NewDecoder(r)
|
||||
decoder.UseNumber()
|
||||
|
||||
err := decoder.Decode(&document)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
return document, nil
|
||||
|
||||
}
|
280
vendor/github.com/xeipuuv/gojsonschema/locales.go
generated
vendored
Normal file
280
vendor/github.com/xeipuuv/gojsonschema/locales.go
generated
vendored
Normal file
@@ -0,0 +1,280 @@
|
||||
// Copyright 2015 xeipuuv ( https://github.com/xeipuuv )
|
||||
//
|
||||
// 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.
|
||||
|
||||
// author xeipuuv
|
||||
// author-github https://github.com/xeipuuv
|
||||
// author-mail xeipuuv@gmail.com
|
||||
//
|
||||
// repository-name gojsonschema
|
||||
// repository-desc An implementation of JSON Schema, based on IETF's draft v4 - Go language.
|
||||
//
|
||||
// description Contains const string and messages.
|
||||
//
|
||||
// created 01-01-2015
|
||||
|
||||
package gojsonschema
|
||||
|
||||
type (
|
||||
// locale is an interface for definining custom error strings
|
||||
locale interface {
|
||||
Required() string
|
||||
InvalidType() string
|
||||
NumberAnyOf() string
|
||||
NumberOneOf() string
|
||||
NumberAllOf() string
|
||||
NumberNot() string
|
||||
MissingDependency() string
|
||||
Internal() string
|
||||
Enum() string
|
||||
ArrayNotEnoughItems() string
|
||||
ArrayNoAdditionalItems() string
|
||||
ArrayMinItems() string
|
||||
ArrayMaxItems() string
|
||||
Unique() string
|
||||
ArrayMinProperties() string
|
||||
ArrayMaxProperties() string
|
||||
AdditionalPropertyNotAllowed() string
|
||||
InvalidPropertyPattern() string
|
||||
StringGTE() string
|
||||
StringLTE() string
|
||||
DoesNotMatchPattern() string
|
||||
DoesNotMatchFormat() string
|
||||
MultipleOf() string
|
||||
NumberGTE() string
|
||||
NumberGT() string
|
||||
NumberLTE() string
|
||||
NumberLT() string
|
||||
|
||||
// Schema validations
|
||||
RegexPattern() string
|
||||
GreaterThanZero() string
|
||||
MustBeOfA() string
|
||||
MustBeOfAn() string
|
||||
CannotBeUsedWithout() string
|
||||
CannotBeGT() string
|
||||
MustBeOfType() string
|
||||
MustBeValidRegex() string
|
||||
MustBeValidFormat() string
|
||||
MustBeGTEZero() string
|
||||
KeyCannotBeGreaterThan() string
|
||||
KeyItemsMustBeOfType() string
|
||||
KeyItemsMustBeUnique() string
|
||||
ReferenceMustBeCanonical() string
|
||||
NotAValidType() string
|
||||
Duplicated() string
|
||||
httpBadStatus() string
|
||||
|
||||
// ErrorFormat
|
||||
ErrorFormat() string
|
||||
}
|
||||
|
||||
// DefaultLocale is the default locale for this package
|
||||
DefaultLocale struct{}
|
||||
)
|
||||
|
||||
func (l DefaultLocale) Required() string {
|
||||
return `{{.property}} is required`
|
||||
}
|
||||
|
||||
func (l DefaultLocale) InvalidType() string {
|
||||
return `Invalid type. Expected: {{.expected}}, given: {{.given}}`
|
||||
}
|
||||
|
||||
func (l DefaultLocale) NumberAnyOf() string {
|
||||
return `Must validate at least one schema (anyOf)`
|
||||
}
|
||||
|
||||
func (l DefaultLocale) NumberOneOf() string {
|
||||
return `Must validate one and only one schema (oneOf)`
|
||||
}
|
||||
|
||||
func (l DefaultLocale) NumberAllOf() string {
|
||||
return `Must validate all the schemas (allOf)`
|
||||
}
|
||||
|
||||
func (l DefaultLocale) NumberNot() string {
|
||||
return `Must not validate the schema (not)`
|
||||
}
|
||||
|
||||
func (l DefaultLocale) MissingDependency() string {
|
||||
return `Has a dependency on {{.dependency}}`
|
||||
}
|
||||
|
||||
func (l DefaultLocale) Internal() string {
|
||||
return `Internal Error {{.error}}`
|
||||
}
|
||||
|
||||
func (l DefaultLocale) Enum() string {
|
||||
return `{{.field}} must be one of the following: {{.allowed}}`
|
||||
}
|
||||
|
||||
func (l DefaultLocale) ArrayNoAdditionalItems() string {
|
||||
return `No additional items allowed on array`
|
||||
}
|
||||
|
||||
func (l DefaultLocale) ArrayNotEnoughItems() string {
|
||||
return `Not enough items on array to match positional list of schema`
|
||||
}
|
||||
|
||||
func (l DefaultLocale) ArrayMinItems() string {
|
||||
return `Array must have at least {{.min}} items`
|
||||
}
|
||||
|
||||
func (l DefaultLocale) ArrayMaxItems() string {
|
||||
return `Array must have at most {{.max}} items`
|
||||
}
|
||||
|
||||
func (l DefaultLocale) Unique() string {
|
||||
return `{{.type}} items must be unique`
|
||||
}
|
||||
|
||||
func (l DefaultLocale) ArrayMinProperties() string {
|
||||
return `Must have at least {{.min}} properties`
|
||||
}
|
||||
|
||||
func (l DefaultLocale) ArrayMaxProperties() string {
|
||||
return `Must have at most {{.max}} properties`
|
||||
}
|
||||
|
||||
func (l DefaultLocale) AdditionalPropertyNotAllowed() string {
|
||||
return `Additional property {{.property}} is not allowed`
|
||||
}
|
||||
|
||||
func (l DefaultLocale) InvalidPropertyPattern() string {
|
||||
return `Property "{{.property}}" does not match pattern {{.pattern}}`
|
||||
}
|
||||
|
||||
func (l DefaultLocale) StringGTE() string {
|
||||
return `String length must be greater than or equal to {{.min}}`
|
||||
}
|
||||
|
||||
func (l DefaultLocale) StringLTE() string {
|
||||
return `String length must be less than or equal to {{.max}}`
|
||||
}
|
||||
|
||||
func (l DefaultLocale) DoesNotMatchPattern() string {
|
||||
return `Does not match pattern '{{.pattern}}'`
|
||||
}
|
||||
|
||||
func (l DefaultLocale) DoesNotMatchFormat() string {
|
||||
return `Does not match format '{{.format}}'`
|
||||
}
|
||||
|
||||
func (l DefaultLocale) MultipleOf() string {
|
||||
return `Must be a multiple of {{.multiple}}`
|
||||
}
|
||||
|
||||
func (l DefaultLocale) NumberGTE() string {
|
||||
return `Must be greater than or equal to {{.min}}`
|
||||
}
|
||||
|
||||
func (l DefaultLocale) NumberGT() string {
|
||||
return `Must be greater than {{.min}}`
|
||||
}
|
||||
|
||||
func (l DefaultLocale) NumberLTE() string {
|
||||
return `Must be less than or equal to {{.max}}`
|
||||
}
|
||||
|
||||
func (l DefaultLocale) NumberLT() string {
|
||||
return `Must be less than {{.max}}`
|
||||
}
|
||||
|
||||
// Schema validators
|
||||
func (l DefaultLocale) RegexPattern() string {
|
||||
return `Invalid regex pattern '{{.pattern}}'`
|
||||
}
|
||||
|
||||
func (l DefaultLocale) GreaterThanZero() string {
|
||||
return `{{.number}} must be strictly greater than 0`
|
||||
}
|
||||
|
||||
func (l DefaultLocale) MustBeOfA() string {
|
||||
return `{{.x}} must be of a {{.y}}`
|
||||
}
|
||||
|
||||
func (l DefaultLocale) MustBeOfAn() string {
|
||||
return `{{.x}} must be of an {{.y}}`
|
||||
}
|
||||
|
||||
func (l DefaultLocale) CannotBeUsedWithout() string {
|
||||
return `{{.x}} cannot be used without {{.y}}`
|
||||
}
|
||||
|
||||
func (l DefaultLocale) CannotBeGT() string {
|
||||
return `{{.x}} cannot be greater than {{.y}}`
|
||||
}
|
||||
|
||||
func (l DefaultLocale) MustBeOfType() string {
|
||||
return `{{.key}} must be of type {{.type}}`
|
||||
}
|
||||
|
||||
func (l DefaultLocale) MustBeValidRegex() string {
|
||||
return `{{.key}} must be a valid regex`
|
||||
}
|
||||
|
||||
func (l DefaultLocale) MustBeValidFormat() string {
|
||||
return `{{.key}} must be a valid format {{.given}}`
|
||||
}
|
||||
|
||||
func (l DefaultLocale) MustBeGTEZero() string {
|
||||
return `{{.key}} must be greater than or equal to 0`
|
||||
}
|
||||
|
||||
func (l DefaultLocale) KeyCannotBeGreaterThan() string {
|
||||
return `{{.key}} cannot be greater than {{.y}}`
|
||||
}
|
||||
|
||||
func (l DefaultLocale) KeyItemsMustBeOfType() string {
|
||||
return `{{.key}} items must be {{.type}}`
|
||||
}
|
||||
|
||||
func (l DefaultLocale) KeyItemsMustBeUnique() string {
|
||||
return `{{.key}} items must be unique`
|
||||
}
|
||||
|
||||
func (l DefaultLocale) ReferenceMustBeCanonical() string {
|
||||
return `Reference {{.reference}} must be canonical`
|
||||
}
|
||||
|
||||
func (l DefaultLocale) NotAValidType() string {
|
||||
return `{{.type}} is not a valid type -- `
|
||||
}
|
||||
|
||||
func (l DefaultLocale) Duplicated() string {
|
||||
return `{{.type}} type is duplicated`
|
||||
}
|
||||
|
||||
func (l DefaultLocale) httpBadStatus() string {
|
||||
return `Could not read schema from HTTP, response status is {{.status}}`
|
||||
}
|
||||
|
||||
// Replacement options: field, description, context, value
|
||||
func (l DefaultLocale) ErrorFormat() string {
|
||||
return `{{.field}}: {{.description}}`
|
||||
}
|
||||
|
||||
const (
|
||||
STRING_NUMBER = "number"
|
||||
STRING_ARRAY_OF_STRINGS = "array of strings"
|
||||
STRING_ARRAY_OF_SCHEMAS = "array of schemas"
|
||||
STRING_SCHEMA = "schema"
|
||||
STRING_SCHEMA_OR_ARRAY_OF_STRINGS = "schema or array of strings"
|
||||
STRING_PROPERTIES = "properties"
|
||||
STRING_DEPENDENCY = "dependency"
|
||||
STRING_PROPERTY = "property"
|
||||
STRING_UNDEFINED = "undefined"
|
||||
STRING_CONTEXT_ROOT = "(root)"
|
||||
STRING_ROOT_SCHEMA_PROPERTY = "(root)"
|
||||
)
|
172
vendor/github.com/xeipuuv/gojsonschema/result.go
generated
vendored
Normal file
172
vendor/github.com/xeipuuv/gojsonschema/result.go
generated
vendored
Normal file
@@ -0,0 +1,172 @@
|
||||
// Copyright 2015 xeipuuv ( https://github.com/xeipuuv )
|
||||
//
|
||||
// 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.
|
||||
|
||||
// author xeipuuv
|
||||
// author-github https://github.com/xeipuuv
|
||||
// author-mail xeipuuv@gmail.com
|
||||
//
|
||||
// repository-name gojsonschema
|
||||
// repository-desc An implementation of JSON Schema, based on IETF's draft v4 - Go language.
|
||||
//
|
||||
// description Result and ResultError implementations.
|
||||
//
|
||||
// created 01-01-2015
|
||||
|
||||
package gojsonschema
|
||||
|
||||
import (
|
||||
"fmt"
|
||||
"strings"
|
||||
)
|
||||
|
||||
type (
|
||||
// ErrorDetails is a map of details specific to each error.
|
||||
// While the values will vary, every error will contain a "field" value
|
||||
ErrorDetails map[string]interface{}
|
||||
|
||||
// ResultError is the interface that library errors must implement
|
||||
ResultError interface {
|
||||
Field() string
|
||||
SetType(string)
|
||||
Type() string
|
||||
SetContext(*jsonContext)
|
||||
Context() *jsonContext
|
||||
SetDescription(string)
|
||||
Description() string
|
||||
SetValue(interface{})
|
||||
Value() interface{}
|
||||
SetDetails(ErrorDetails)
|
||||
Details() ErrorDetails
|
||||
String() string
|
||||
}
|
||||
|
||||
// ResultErrorFields holds the fields for each ResultError implementation.
|
||||
// ResultErrorFields implements the ResultError interface, so custom errors
|
||||
// can be defined by just embedding this type
|
||||
ResultErrorFields struct {
|
||||
errorType string // A string with the type of error (i.e. invalid_type)
|
||||
context *jsonContext // Tree like notation of the part that failed the validation. ex (root).a.b ...
|
||||
description string // A human readable error message
|
||||
value interface{} // Value given by the JSON file that is the source of the error
|
||||
details ErrorDetails
|
||||
}
|
||||
|
||||
Result struct {
|
||||
errors []ResultError
|
||||
// Scores how well the validation matched. Useful in generating
|
||||
// better error messages for anyOf and oneOf.
|
||||
score int
|
||||
}
|
||||
)
|
||||
|
||||
// Field outputs the field name without the root context
|
||||
// i.e. firstName or person.firstName instead of (root).firstName or (root).person.firstName
|
||||
func (v *ResultErrorFields) Field() string {
|
||||
if p, ok := v.Details()["property"]; ok {
|
||||
if str, isString := p.(string); isString {
|
||||
return str
|
||||
}
|
||||
}
|
||||
|
||||
return strings.TrimPrefix(v.context.String(), STRING_ROOT_SCHEMA_PROPERTY+".")
|
||||
}
|
||||
|
||||
func (v *ResultErrorFields) SetType(errorType string) {
|
||||
v.errorType = errorType
|
||||
}
|
||||
|
||||
func (v *ResultErrorFields) Type() string {
|
||||
return v.errorType
|
||||
}
|
||||
|
||||
func (v *ResultErrorFields) SetContext(context *jsonContext) {
|
||||
v.context = context
|
||||
}
|
||||
|
||||
func (v *ResultErrorFields) Context() *jsonContext {
|
||||
return v.context
|
||||
}
|
||||
|
||||
func (v *ResultErrorFields) SetDescription(description string) {
|
||||
v.description = description
|
||||
}
|
||||
|
||||
func (v *ResultErrorFields) Description() string {
|
||||
return v.description
|
||||
}
|
||||
|
||||
func (v *ResultErrorFields) SetValue(value interface{}) {
|
||||
v.value = value
|
||||
}
|
||||
|
||||
func (v *ResultErrorFields) Value() interface{} {
|
||||
return v.value
|
||||
}
|
||||
|
||||
func (v *ResultErrorFields) SetDetails(details ErrorDetails) {
|
||||
v.details = details
|
||||
}
|
||||
|
||||
func (v *ResultErrorFields) Details() ErrorDetails {
|
||||
return v.details
|
||||
}
|
||||
|
||||
func (v ResultErrorFields) String() string {
|
||||
// as a fallback, the value is displayed go style
|
||||
valueString := fmt.Sprintf("%v", v.value)
|
||||
|
||||
// marshal the go value value to json
|
||||
if v.value == nil {
|
||||
valueString = TYPE_NULL
|
||||
} else {
|
||||
if vs, err := marshalToJsonString(v.value); err == nil {
|
||||
if vs == nil {
|
||||
valueString = TYPE_NULL
|
||||
} else {
|
||||
valueString = *vs
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
return formatErrorDescription(Locale.ErrorFormat(), ErrorDetails{
|
||||
"context": v.context.String(),
|
||||
"description": v.description,
|
||||
"value": valueString,
|
||||
"field": v.Field(),
|
||||
})
|
||||
}
|
||||
|
||||
func (v *Result) Valid() bool {
|
||||
return len(v.errors) == 0
|
||||
}
|
||||
|
||||
func (v *Result) Errors() []ResultError {
|
||||
return v.errors
|
||||
}
|
||||
|
||||
func (v *Result) addError(err ResultError, context *jsonContext, value interface{}, details ErrorDetails) {
|
||||
newError(err, context, value, Locale, details)
|
||||
v.errors = append(v.errors, err)
|
||||
v.score -= 2 // results in a net -1 when added to the +1 we get at the end of the validation function
|
||||
}
|
||||
|
||||
// Used to copy errors from a sub-schema to the main one
|
||||
func (v *Result) mergeErrors(otherResult *Result) {
|
||||
v.errors = append(v.errors, otherResult.Errors()...)
|
||||
v.score += otherResult.score
|
||||
}
|
||||
|
||||
func (v *Result) incrementScore() {
|
||||
v.score++
|
||||
}
|
930
vendor/github.com/xeipuuv/gojsonschema/schema.go
generated
vendored
Normal file
930
vendor/github.com/xeipuuv/gojsonschema/schema.go
generated
vendored
Normal file
@@ -0,0 +1,930 @@
|
||||
// Copyright 2015 xeipuuv ( https://github.com/xeipuuv )
|
||||
//
|
||||
// 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.
|
||||
|
||||
// author xeipuuv
|
||||
// author-github https://github.com/xeipuuv
|
||||
// author-mail xeipuuv@gmail.com
|
||||
//
|
||||
// repository-name gojsonschema
|
||||
// repository-desc An implementation of JSON Schema, based on IETF's draft v4 - Go language.
|
||||
//
|
||||
// description Defines Schema, the main entry to every subSchema.
|
||||
// Contains the parsing logic and error checking.
|
||||
//
|
||||
// created 26-02-2013
|
||||
|
||||
package gojsonschema
|
||||
|
||||
import (
|
||||
// "encoding/json"
|
||||
"errors"
|
||||
"reflect"
|
||||
"regexp"
|
||||
|
||||
"github.com/xeipuuv/gojsonreference"
|
||||
)
|
||||
|
||||
var (
|
||||
// Locale is the default locale to use
|
||||
// Library users can overwrite with their own implementation
|
||||
Locale locale = DefaultLocale{}
|
||||
)
|
||||
|
||||
func NewSchema(l JSONLoader) (*Schema, error) {
|
||||
ref, err := l.JsonReference()
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
d := Schema{}
|
||||
d.pool = newSchemaPool(l.LoaderFactory())
|
||||
d.documentReference = ref
|
||||
d.referencePool = newSchemaReferencePool()
|
||||
|
||||
var doc interface{}
|
||||
if ref.String() != "" {
|
||||
// Get document from schema pool
|
||||
spd, err := d.pool.GetDocument(d.documentReference)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
doc = spd.Document
|
||||
} else {
|
||||
// Load JSON directly
|
||||
doc, err = l.LoadJSON()
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
d.pool.SetStandaloneDocument(doc)
|
||||
}
|
||||
|
||||
err = d.parse(doc)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
return &d, nil
|
||||
}
|
||||
|
||||
type Schema struct {
|
||||
documentReference gojsonreference.JsonReference
|
||||
rootSchema *subSchema
|
||||
pool *schemaPool
|
||||
referencePool *schemaReferencePool
|
||||
}
|
||||
|
||||
func (d *Schema) parse(document interface{}) error {
|
||||
d.rootSchema = &subSchema{property: STRING_ROOT_SCHEMA_PROPERTY}
|
||||
return d.parseSchema(document, d.rootSchema)
|
||||
}
|
||||
|
||||
func (d *Schema) SetRootSchemaName(name string) {
|
||||
d.rootSchema.property = name
|
||||
}
|
||||
|
||||
// Parses a subSchema
|
||||
//
|
||||
// Pretty long function ( sorry :) )... but pretty straight forward, repetitive and boring
|
||||
// Not much magic involved here, most of the job is to validate the key names and their values,
|
||||
// then the values are copied into subSchema struct
|
||||
//
|
||||
func (d *Schema) parseSchema(documentNode interface{}, currentSchema *subSchema) error {
|
||||
|
||||
if !isKind(documentNode, reflect.Map) {
|
||||
return errors.New(formatErrorDescription(
|
||||
Locale.InvalidType(),
|
||||
ErrorDetails{
|
||||
"expected": TYPE_OBJECT,
|
||||
"given": STRING_SCHEMA,
|
||||
},
|
||||
))
|
||||
}
|
||||
|
||||
m := documentNode.(map[string]interface{})
|
||||
|
||||
if currentSchema == d.rootSchema {
|
||||
currentSchema.ref = &d.documentReference
|
||||
}
|
||||
|
||||
// $subSchema
|
||||
if existsMapKey(m, KEY_SCHEMA) {
|
||||
if !isKind(m[KEY_SCHEMA], reflect.String) {
|
||||
return errors.New(formatErrorDescription(
|
||||
Locale.InvalidType(),
|
||||
ErrorDetails{
|
||||
"expected": TYPE_STRING,
|
||||
"given": KEY_SCHEMA,
|
||||
},
|
||||
))
|
||||
}
|
||||
schemaRef := m[KEY_SCHEMA].(string)
|
||||
schemaReference, err := gojsonreference.NewJsonReference(schemaRef)
|
||||
currentSchema.subSchema = &schemaReference
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
}
|
||||
|
||||
// $ref
|
||||
if existsMapKey(m, KEY_REF) && !isKind(m[KEY_REF], reflect.String) {
|
||||
return errors.New(formatErrorDescription(
|
||||
Locale.InvalidType(),
|
||||
ErrorDetails{
|
||||
"expected": TYPE_STRING,
|
||||
"given": KEY_REF,
|
||||
},
|
||||
))
|
||||
}
|
||||
if k, ok := m[KEY_REF].(string); ok {
|
||||
|
||||
jsonReference, err := gojsonreference.NewJsonReference(k)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
if jsonReference.HasFullUrl {
|
||||
currentSchema.ref = &jsonReference
|
||||
} else {
|
||||
inheritedReference, err := currentSchema.ref.Inherits(jsonReference)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
currentSchema.ref = inheritedReference
|
||||
}
|
||||
|
||||
if sch, ok := d.referencePool.Get(currentSchema.ref.String() + k); ok {
|
||||
currentSchema.refSchema = sch
|
||||
|
||||
} else {
|
||||
err := d.parseReference(documentNode, currentSchema, k)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
return nil
|
||||
}
|
||||
}
|
||||
|
||||
// definitions
|
||||
if existsMapKey(m, KEY_DEFINITIONS) {
|
||||
if isKind(m[KEY_DEFINITIONS], reflect.Map) {
|
||||
currentSchema.definitions = make(map[string]*subSchema)
|
||||
for dk, dv := range m[KEY_DEFINITIONS].(map[string]interface{}) {
|
||||
if isKind(dv, reflect.Map) {
|
||||
newSchema := &subSchema{property: KEY_DEFINITIONS, parent: currentSchema, ref: currentSchema.ref}
|
||||
currentSchema.definitions[dk] = newSchema
|
||||
err := d.parseSchema(dv, newSchema)
|
||||
if err != nil {
|
||||
return errors.New(err.Error())
|
||||
}
|
||||
} else {
|
||||
return errors.New(formatErrorDescription(
|
||||
Locale.InvalidType(),
|
||||
ErrorDetails{
|
||||
"expected": STRING_ARRAY_OF_SCHEMAS,
|
||||
"given": KEY_DEFINITIONS,
|
||||
},
|
||||
))
|
||||
}
|
||||
}
|
||||
} else {
|
||||
return errors.New(formatErrorDescription(
|
||||
Locale.InvalidType(),
|
||||
ErrorDetails{
|
||||
"expected": STRING_ARRAY_OF_SCHEMAS,
|
||||
"given": KEY_DEFINITIONS,
|
||||
},
|
||||
))
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
// id
|
||||
if existsMapKey(m, KEY_ID) && !isKind(m[KEY_ID], reflect.String) {
|
||||
return errors.New(formatErrorDescription(
|
||||
Locale.InvalidType(),
|
||||
ErrorDetails{
|
||||
"expected": TYPE_STRING,
|
||||
"given": KEY_ID,
|
||||
},
|
||||
))
|
||||
}
|
||||
if k, ok := m[KEY_ID].(string); ok {
|
||||
currentSchema.id = &k
|
||||
}
|
||||
|
||||
// title
|
||||
if existsMapKey(m, KEY_TITLE) && !isKind(m[KEY_TITLE], reflect.String) {
|
||||
return errors.New(formatErrorDescription(
|
||||
Locale.InvalidType(),
|
||||
ErrorDetails{
|
||||
"expected": TYPE_STRING,
|
||||
"given": KEY_TITLE,
|
||||
},
|
||||
))
|
||||
}
|
||||
if k, ok := m[KEY_TITLE].(string); ok {
|
||||
currentSchema.title = &k
|
||||
}
|
||||
|
||||
// description
|
||||
if existsMapKey(m, KEY_DESCRIPTION) && !isKind(m[KEY_DESCRIPTION], reflect.String) {
|
||||
return errors.New(formatErrorDescription(
|
||||
Locale.InvalidType(),
|
||||
ErrorDetails{
|
||||
"expected": TYPE_STRING,
|
||||
"given": KEY_DESCRIPTION,
|
||||
},
|
||||
))
|
||||
}
|
||||
if k, ok := m[KEY_DESCRIPTION].(string); ok {
|
||||
currentSchema.description = &k
|
||||
}
|
||||
|
||||
// type
|
||||
if existsMapKey(m, KEY_TYPE) {
|
||||
if isKind(m[KEY_TYPE], reflect.String) {
|
||||
if k, ok := m[KEY_TYPE].(string); ok {
|
||||
err := currentSchema.types.Add(k)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
}
|
||||
} else {
|
||||
if isKind(m[KEY_TYPE], reflect.Slice) {
|
||||
arrayOfTypes := m[KEY_TYPE].([]interface{})
|
||||
for _, typeInArray := range arrayOfTypes {
|
||||
if reflect.ValueOf(typeInArray).Kind() != reflect.String {
|
||||
return errors.New(formatErrorDescription(
|
||||
Locale.InvalidType(),
|
||||
ErrorDetails{
|
||||
"expected": TYPE_STRING + "/" + STRING_ARRAY_OF_STRINGS,
|
||||
"given": KEY_TYPE,
|
||||
},
|
||||
))
|
||||
} else {
|
||||
currentSchema.types.Add(typeInArray.(string))
|
||||
}
|
||||
}
|
||||
|
||||
} else {
|
||||
return errors.New(formatErrorDescription(
|
||||
Locale.InvalidType(),
|
||||
ErrorDetails{
|
||||
"expected": TYPE_STRING + "/" + STRING_ARRAY_OF_STRINGS,
|
||||
"given": KEY_TYPE,
|
||||
},
|
||||
))
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// properties
|
||||
if existsMapKey(m, KEY_PROPERTIES) {
|
||||
err := d.parseProperties(m[KEY_PROPERTIES], currentSchema)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
}
|
||||
|
||||
// additionalProperties
|
||||
if existsMapKey(m, KEY_ADDITIONAL_PROPERTIES) {
|
||||
if isKind(m[KEY_ADDITIONAL_PROPERTIES], reflect.Bool) {
|
||||
currentSchema.additionalProperties = m[KEY_ADDITIONAL_PROPERTIES].(bool)
|
||||
} else if isKind(m[KEY_ADDITIONAL_PROPERTIES], reflect.Map) {
|
||||
newSchema := &subSchema{property: KEY_ADDITIONAL_PROPERTIES, parent: currentSchema, ref: currentSchema.ref}
|
||||
currentSchema.additionalProperties = newSchema
|
||||
err := d.parseSchema(m[KEY_ADDITIONAL_PROPERTIES], newSchema)
|
||||
if err != nil {
|
||||
return errors.New(err.Error())
|
||||
}
|
||||
} else {
|
||||
return errors.New(formatErrorDescription(
|
||||
Locale.InvalidType(),
|
||||
ErrorDetails{
|
||||
"expected": TYPE_BOOLEAN + "/" + STRING_SCHEMA,
|
||||
"given": KEY_ADDITIONAL_PROPERTIES,
|
||||
},
|
||||
))
|
||||
}
|
||||
}
|
||||
|
||||
// patternProperties
|
||||
if existsMapKey(m, KEY_PATTERN_PROPERTIES) {
|
||||
if isKind(m[KEY_PATTERN_PROPERTIES], reflect.Map) {
|
||||
patternPropertiesMap := m[KEY_PATTERN_PROPERTIES].(map[string]interface{})
|
||||
if len(patternPropertiesMap) > 0 {
|
||||
currentSchema.patternProperties = make(map[string]*subSchema)
|
||||
for k, v := range patternPropertiesMap {
|
||||
_, err := regexp.MatchString(k, "")
|
||||
if err != nil {
|
||||
return errors.New(formatErrorDescription(
|
||||
Locale.RegexPattern(),
|
||||
ErrorDetails{"pattern": k},
|
||||
))
|
||||
}
|
||||
newSchema := &subSchema{property: k, parent: currentSchema, ref: currentSchema.ref}
|
||||
err = d.parseSchema(v, newSchema)
|
||||
if err != nil {
|
||||
return errors.New(err.Error())
|
||||
}
|
||||
currentSchema.patternProperties[k] = newSchema
|
||||
}
|
||||
}
|
||||
} else {
|
||||
return errors.New(formatErrorDescription(
|
||||
Locale.InvalidType(),
|
||||
ErrorDetails{
|
||||
"expected": STRING_SCHEMA,
|
||||
"given": KEY_PATTERN_PROPERTIES,
|
||||
},
|
||||
))
|
||||
}
|
||||
}
|
||||
|
||||
// dependencies
|
||||
if existsMapKey(m, KEY_DEPENDENCIES) {
|
||||
err := d.parseDependencies(m[KEY_DEPENDENCIES], currentSchema)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
}
|
||||
|
||||
// items
|
||||
if existsMapKey(m, KEY_ITEMS) {
|
||||
if isKind(m[KEY_ITEMS], reflect.Slice) {
|
||||
for _, itemElement := range m[KEY_ITEMS].([]interface{}) {
|
||||
if isKind(itemElement, reflect.Map) {
|
||||
newSchema := &subSchema{parent: currentSchema, property: KEY_ITEMS}
|
||||
newSchema.ref = currentSchema.ref
|
||||
currentSchema.AddItemsChild(newSchema)
|
||||
err := d.parseSchema(itemElement, newSchema)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
} else {
|
||||
return errors.New(formatErrorDescription(
|
||||
Locale.InvalidType(),
|
||||
ErrorDetails{
|
||||
"expected": STRING_SCHEMA + "/" + STRING_ARRAY_OF_SCHEMAS,
|
||||
"given": KEY_ITEMS,
|
||||
},
|
||||
))
|
||||
}
|
||||
currentSchema.itemsChildrenIsSingleSchema = false
|
||||
}
|
||||
} else if isKind(m[KEY_ITEMS], reflect.Map) {
|
||||
newSchema := &subSchema{parent: currentSchema, property: KEY_ITEMS}
|
||||
newSchema.ref = currentSchema.ref
|
||||
currentSchema.AddItemsChild(newSchema)
|
||||
err := d.parseSchema(m[KEY_ITEMS], newSchema)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
currentSchema.itemsChildrenIsSingleSchema = true
|
||||
} else {
|
||||
return errors.New(formatErrorDescription(
|
||||
Locale.InvalidType(),
|
||||
ErrorDetails{
|
||||
"expected": STRING_SCHEMA + "/" + STRING_ARRAY_OF_SCHEMAS,
|
||||
"given": KEY_ITEMS,
|
||||
},
|
||||
))
|
||||
}
|
||||
}
|
||||
|
||||
// additionalItems
|
||||
if existsMapKey(m, KEY_ADDITIONAL_ITEMS) {
|
||||
if isKind(m[KEY_ADDITIONAL_ITEMS], reflect.Bool) {
|
||||
currentSchema.additionalItems = m[KEY_ADDITIONAL_ITEMS].(bool)
|
||||
} else if isKind(m[KEY_ADDITIONAL_ITEMS], reflect.Map) {
|
||||
newSchema := &subSchema{property: KEY_ADDITIONAL_ITEMS, parent: currentSchema, ref: currentSchema.ref}
|
||||
currentSchema.additionalItems = newSchema
|
||||
err := d.parseSchema(m[KEY_ADDITIONAL_ITEMS], newSchema)
|
||||
if err != nil {
|
||||
return errors.New(err.Error())
|
||||
}
|
||||
} else {
|
||||
return errors.New(formatErrorDescription(
|
||||
Locale.InvalidType(),
|
||||
ErrorDetails{
|
||||
"expected": TYPE_BOOLEAN + "/" + STRING_SCHEMA,
|
||||
"given": KEY_ADDITIONAL_ITEMS,
|
||||
},
|
||||
))
|
||||
}
|
||||
}
|
||||
|
||||
// validation : number / integer
|
||||
|
||||
if existsMapKey(m, KEY_MULTIPLE_OF) {
|
||||
multipleOfValue := mustBeNumber(m[KEY_MULTIPLE_OF])
|
||||
if multipleOfValue == nil {
|
||||
return errors.New(formatErrorDescription(
|
||||
Locale.InvalidType(),
|
||||
ErrorDetails{
|
||||
"expected": STRING_NUMBER,
|
||||
"given": KEY_MULTIPLE_OF,
|
||||
},
|
||||
))
|
||||
}
|
||||
if *multipleOfValue <= 0 {
|
||||
return errors.New(formatErrorDescription(
|
||||
Locale.GreaterThanZero(),
|
||||
ErrorDetails{"number": KEY_MULTIPLE_OF},
|
||||
))
|
||||
}
|
||||
currentSchema.multipleOf = multipleOfValue
|
||||
}
|
||||
|
||||
if existsMapKey(m, KEY_MINIMUM) {
|
||||
minimumValue := mustBeNumber(m[KEY_MINIMUM])
|
||||
if minimumValue == nil {
|
||||
return errors.New(formatErrorDescription(
|
||||
Locale.MustBeOfA(),
|
||||
ErrorDetails{"x": KEY_MINIMUM, "y": STRING_NUMBER},
|
||||
))
|
||||
}
|
||||
currentSchema.minimum = minimumValue
|
||||
}
|
||||
|
||||
if existsMapKey(m, KEY_EXCLUSIVE_MINIMUM) {
|
||||
if isKind(m[KEY_EXCLUSIVE_MINIMUM], reflect.Bool) {
|
||||
if currentSchema.minimum == nil {
|
||||
return errors.New(formatErrorDescription(
|
||||
Locale.CannotBeUsedWithout(),
|
||||
ErrorDetails{"x": KEY_EXCLUSIVE_MINIMUM, "y": KEY_MINIMUM},
|
||||
))
|
||||
}
|
||||
exclusiveMinimumValue := m[KEY_EXCLUSIVE_MINIMUM].(bool)
|
||||
currentSchema.exclusiveMinimum = exclusiveMinimumValue
|
||||
} else {
|
||||
return errors.New(formatErrorDescription(
|
||||
Locale.MustBeOfA(),
|
||||
ErrorDetails{"x": KEY_EXCLUSIVE_MINIMUM, "y": TYPE_BOOLEAN},
|
||||
))
|
||||
}
|
||||
}
|
||||
|
||||
if existsMapKey(m, KEY_MAXIMUM) {
|
||||
maximumValue := mustBeNumber(m[KEY_MAXIMUM])
|
||||
if maximumValue == nil {
|
||||
return errors.New(formatErrorDescription(
|
||||
Locale.MustBeOfA(),
|
||||
ErrorDetails{"x": KEY_MAXIMUM, "y": STRING_NUMBER},
|
||||
))
|
||||
}
|
||||
currentSchema.maximum = maximumValue
|
||||
}
|
||||
|
||||
if existsMapKey(m, KEY_EXCLUSIVE_MAXIMUM) {
|
||||
if isKind(m[KEY_EXCLUSIVE_MAXIMUM], reflect.Bool) {
|
||||
if currentSchema.maximum == nil {
|
||||
return errors.New(formatErrorDescription(
|
||||
Locale.CannotBeUsedWithout(),
|
||||
ErrorDetails{"x": KEY_EXCLUSIVE_MAXIMUM, "y": KEY_MAXIMUM},
|
||||
))
|
||||
}
|
||||
exclusiveMaximumValue := m[KEY_EXCLUSIVE_MAXIMUM].(bool)
|
||||
currentSchema.exclusiveMaximum = exclusiveMaximumValue
|
||||
} else {
|
||||
return errors.New(formatErrorDescription(
|
||||
Locale.MustBeOfA(),
|
||||
ErrorDetails{"x": KEY_EXCLUSIVE_MAXIMUM, "y": STRING_NUMBER},
|
||||
))
|
||||
}
|
||||
}
|
||||
|
||||
if currentSchema.minimum != nil && currentSchema.maximum != nil {
|
||||
if *currentSchema.minimum > *currentSchema.maximum {
|
||||
return errors.New(formatErrorDescription(
|
||||
Locale.CannotBeGT(),
|
||||
ErrorDetails{"x": KEY_MINIMUM, "y": KEY_MAXIMUM},
|
||||
))
|
||||
}
|
||||
}
|
||||
|
||||
// validation : string
|
||||
|
||||
if existsMapKey(m, KEY_MIN_LENGTH) {
|
||||
minLengthIntegerValue := mustBeInteger(m[KEY_MIN_LENGTH])
|
||||
if minLengthIntegerValue == nil {
|
||||
return errors.New(formatErrorDescription(
|
||||
Locale.MustBeOfAn(),
|
||||
ErrorDetails{"x": KEY_MIN_LENGTH, "y": TYPE_INTEGER},
|
||||
))
|
||||
}
|
||||
if *minLengthIntegerValue < 0 {
|
||||
return errors.New(formatErrorDescription(
|
||||
Locale.MustBeGTEZero(),
|
||||
ErrorDetails{"key": KEY_MIN_LENGTH},
|
||||
))
|
||||
}
|
||||
currentSchema.minLength = minLengthIntegerValue
|
||||
}
|
||||
|
||||
if existsMapKey(m, KEY_MAX_LENGTH) {
|
||||
maxLengthIntegerValue := mustBeInteger(m[KEY_MAX_LENGTH])
|
||||
if maxLengthIntegerValue == nil {
|
||||
return errors.New(formatErrorDescription(
|
||||
Locale.MustBeOfAn(),
|
||||
ErrorDetails{"x": KEY_MAX_LENGTH, "y": TYPE_INTEGER},
|
||||
))
|
||||
}
|
||||
if *maxLengthIntegerValue < 0 {
|
||||
return errors.New(formatErrorDescription(
|
||||
Locale.MustBeGTEZero(),
|
||||
ErrorDetails{"key": KEY_MAX_LENGTH},
|
||||
))
|
||||
}
|
||||
currentSchema.maxLength = maxLengthIntegerValue
|
||||
}
|
||||
|
||||
if currentSchema.minLength != nil && currentSchema.maxLength != nil {
|
||||
if *currentSchema.minLength > *currentSchema.maxLength {
|
||||
return errors.New(formatErrorDescription(
|
||||
Locale.CannotBeGT(),
|
||||
ErrorDetails{"x": KEY_MIN_LENGTH, "y": KEY_MAX_LENGTH},
|
||||
))
|
||||
}
|
||||
}
|
||||
|
||||
if existsMapKey(m, KEY_PATTERN) {
|
||||
if isKind(m[KEY_PATTERN], reflect.String) {
|
||||
regexpObject, err := regexp.Compile(m[KEY_PATTERN].(string))
|
||||
if err != nil {
|
||||
return errors.New(formatErrorDescription(
|
||||
Locale.MustBeValidRegex(),
|
||||
ErrorDetails{"key": KEY_PATTERN},
|
||||
))
|
||||
}
|
||||
currentSchema.pattern = regexpObject
|
||||
} else {
|
||||
return errors.New(formatErrorDescription(
|
||||
Locale.MustBeOfA(),
|
||||
ErrorDetails{"x": KEY_PATTERN, "y": TYPE_STRING},
|
||||
))
|
||||
}
|
||||
}
|
||||
|
||||
if existsMapKey(m, KEY_FORMAT) {
|
||||
formatString, ok := m[KEY_FORMAT].(string)
|
||||
if ok && FormatCheckers.Has(formatString) {
|
||||
currentSchema.format = formatString
|
||||
} else {
|
||||
return errors.New(formatErrorDescription(
|
||||
Locale.MustBeValidFormat(),
|
||||
ErrorDetails{"key": KEY_FORMAT, "given": m[KEY_FORMAT]},
|
||||
))
|
||||
}
|
||||
}
|
||||
|
||||
// validation : object
|
||||
|
||||
if existsMapKey(m, KEY_MIN_PROPERTIES) {
|
||||
minPropertiesIntegerValue := mustBeInteger(m[KEY_MIN_PROPERTIES])
|
||||
if minPropertiesIntegerValue == nil {
|
||||
return errors.New(formatErrorDescription(
|
||||
Locale.MustBeOfAn(),
|
||||
ErrorDetails{"x": KEY_MIN_PROPERTIES, "y": TYPE_INTEGER},
|
||||
))
|
||||
}
|
||||
if *minPropertiesIntegerValue < 0 {
|
||||
return errors.New(formatErrorDescription(
|
||||
Locale.MustBeGTEZero(),
|
||||
ErrorDetails{"key": KEY_MIN_PROPERTIES},
|
||||
))
|
||||
}
|
||||
currentSchema.minProperties = minPropertiesIntegerValue
|
||||
}
|
||||
|
||||
if existsMapKey(m, KEY_MAX_PROPERTIES) {
|
||||
maxPropertiesIntegerValue := mustBeInteger(m[KEY_MAX_PROPERTIES])
|
||||
if maxPropertiesIntegerValue == nil {
|
||||
return errors.New(formatErrorDescription(
|
||||
Locale.MustBeOfAn(),
|
||||
ErrorDetails{"x": KEY_MAX_PROPERTIES, "y": TYPE_INTEGER},
|
||||
))
|
||||
}
|
||||
if *maxPropertiesIntegerValue < 0 {
|
||||
return errors.New(formatErrorDescription(
|
||||
Locale.MustBeGTEZero(),
|
||||
ErrorDetails{"key": KEY_MAX_PROPERTIES},
|
||||
))
|
||||
}
|
||||
currentSchema.maxProperties = maxPropertiesIntegerValue
|
||||
}
|
||||
|
||||
if currentSchema.minProperties != nil && currentSchema.maxProperties != nil {
|
||||
if *currentSchema.minProperties > *currentSchema.maxProperties {
|
||||
return errors.New(formatErrorDescription(
|
||||
Locale.KeyCannotBeGreaterThan(),
|
||||
ErrorDetails{"key": KEY_MIN_PROPERTIES, "y": KEY_MAX_PROPERTIES},
|
||||
))
|
||||
}
|
||||
}
|
||||
|
||||
if existsMapKey(m, KEY_REQUIRED) {
|
||||
if isKind(m[KEY_REQUIRED], reflect.Slice) {
|
||||
requiredValues := m[KEY_REQUIRED].([]interface{})
|
||||
for _, requiredValue := range requiredValues {
|
||||
if isKind(requiredValue, reflect.String) {
|
||||
err := currentSchema.AddRequired(requiredValue.(string))
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
} else {
|
||||
return errors.New(formatErrorDescription(
|
||||
Locale.KeyItemsMustBeOfType(),
|
||||
ErrorDetails{"key": KEY_REQUIRED, "type": TYPE_STRING},
|
||||
))
|
||||
}
|
||||
}
|
||||
} else {
|
||||
return errors.New(formatErrorDescription(
|
||||
Locale.MustBeOfAn(),
|
||||
ErrorDetails{"x": KEY_REQUIRED, "y": TYPE_ARRAY},
|
||||
))
|
||||
}
|
||||
}
|
||||
|
||||
// validation : array
|
||||
|
||||
if existsMapKey(m, KEY_MIN_ITEMS) {
|
||||
minItemsIntegerValue := mustBeInteger(m[KEY_MIN_ITEMS])
|
||||
if minItemsIntegerValue == nil {
|
||||
return errors.New(formatErrorDescription(
|
||||
Locale.MustBeOfAn(),
|
||||
ErrorDetails{"x": KEY_MIN_ITEMS, "y": TYPE_INTEGER},
|
||||
))
|
||||
}
|
||||
if *minItemsIntegerValue < 0 {
|
||||
return errors.New(formatErrorDescription(
|
||||
Locale.MustBeGTEZero(),
|
||||
ErrorDetails{"key": KEY_MIN_ITEMS},
|
||||
))
|
||||
}
|
||||
currentSchema.minItems = minItemsIntegerValue
|
||||
}
|
||||
|
||||
if existsMapKey(m, KEY_MAX_ITEMS) {
|
||||
maxItemsIntegerValue := mustBeInteger(m[KEY_MAX_ITEMS])
|
||||
if maxItemsIntegerValue == nil {
|
||||
return errors.New(formatErrorDescription(
|
||||
Locale.MustBeOfAn(),
|
||||
ErrorDetails{"x": KEY_MAX_ITEMS, "y": TYPE_INTEGER},
|
||||
))
|
||||
}
|
||||
if *maxItemsIntegerValue < 0 {
|
||||
return errors.New(formatErrorDescription(
|
||||
Locale.MustBeGTEZero(),
|
||||
ErrorDetails{"key": KEY_MAX_ITEMS},
|
||||
))
|
||||
}
|
||||
currentSchema.maxItems = maxItemsIntegerValue
|
||||
}
|
||||
|
||||
if existsMapKey(m, KEY_UNIQUE_ITEMS) {
|
||||
if isKind(m[KEY_UNIQUE_ITEMS], reflect.Bool) {
|
||||
currentSchema.uniqueItems = m[KEY_UNIQUE_ITEMS].(bool)
|
||||
} else {
|
||||
return errors.New(formatErrorDescription(
|
||||
Locale.MustBeOfA(),
|
||||
ErrorDetails{"x": KEY_UNIQUE_ITEMS, "y": TYPE_BOOLEAN},
|
||||
))
|
||||
}
|
||||
}
|
||||
|
||||
// validation : all
|
||||
|
||||
if existsMapKey(m, KEY_ENUM) {
|
||||
if isKind(m[KEY_ENUM], reflect.Slice) {
|
||||
for _, v := range m[KEY_ENUM].([]interface{}) {
|
||||
err := currentSchema.AddEnum(v)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
}
|
||||
} else {
|
||||
return errors.New(formatErrorDescription(
|
||||
Locale.MustBeOfAn(),
|
||||
ErrorDetails{"x": KEY_ENUM, "y": TYPE_ARRAY},
|
||||
))
|
||||
}
|
||||
}
|
||||
|
||||
// validation : subSchema
|
||||
|
||||
if existsMapKey(m, KEY_ONE_OF) {
|
||||
if isKind(m[KEY_ONE_OF], reflect.Slice) {
|
||||
for _, v := range m[KEY_ONE_OF].([]interface{}) {
|
||||
newSchema := &subSchema{property: KEY_ONE_OF, parent: currentSchema, ref: currentSchema.ref}
|
||||
currentSchema.AddOneOf(newSchema)
|
||||
err := d.parseSchema(v, newSchema)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
}
|
||||
} else {
|
||||
return errors.New(formatErrorDescription(
|
||||
Locale.MustBeOfAn(),
|
||||
ErrorDetails{"x": KEY_ONE_OF, "y": TYPE_ARRAY},
|
||||
))
|
||||
}
|
||||
}
|
||||
|
||||
if existsMapKey(m, KEY_ANY_OF) {
|
||||
if isKind(m[KEY_ANY_OF], reflect.Slice) {
|
||||
for _, v := range m[KEY_ANY_OF].([]interface{}) {
|
||||
newSchema := &subSchema{property: KEY_ANY_OF, parent: currentSchema, ref: currentSchema.ref}
|
||||
currentSchema.AddAnyOf(newSchema)
|
||||
err := d.parseSchema(v, newSchema)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
}
|
||||
} else {
|
||||
return errors.New(formatErrorDescription(
|
||||
Locale.MustBeOfAn(),
|
||||
ErrorDetails{"x": KEY_ANY_OF, "y": TYPE_ARRAY},
|
||||
))
|
||||
}
|
||||
}
|
||||
|
||||
if existsMapKey(m, KEY_ALL_OF) {
|
||||
if isKind(m[KEY_ALL_OF], reflect.Slice) {
|
||||
for _, v := range m[KEY_ALL_OF].([]interface{}) {
|
||||
newSchema := &subSchema{property: KEY_ALL_OF, parent: currentSchema, ref: currentSchema.ref}
|
||||
currentSchema.AddAllOf(newSchema)
|
||||
err := d.parseSchema(v, newSchema)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
}
|
||||
} else {
|
||||
return errors.New(formatErrorDescription(
|
||||
Locale.MustBeOfAn(),
|
||||
ErrorDetails{"x": KEY_ANY_OF, "y": TYPE_ARRAY},
|
||||
))
|
||||
}
|
||||
}
|
||||
|
||||
if existsMapKey(m, KEY_NOT) {
|
||||
if isKind(m[KEY_NOT], reflect.Map) {
|
||||
newSchema := &subSchema{property: KEY_NOT, parent: currentSchema, ref: currentSchema.ref}
|
||||
currentSchema.SetNot(newSchema)
|
||||
err := d.parseSchema(m[KEY_NOT], newSchema)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
} else {
|
||||
return errors.New(formatErrorDescription(
|
||||
Locale.MustBeOfAn(),
|
||||
ErrorDetails{"x": KEY_NOT, "y": TYPE_OBJECT},
|
||||
))
|
||||
}
|
||||
}
|
||||
|
||||
return nil
|
||||
}
|
||||
|
||||
func (d *Schema) parseReference(documentNode interface{}, currentSchema *subSchema, reference string) error {
|
||||
var refdDocumentNode interface{}
|
||||
jsonPointer := currentSchema.ref.GetPointer()
|
||||
standaloneDocument := d.pool.GetStandaloneDocument()
|
||||
|
||||
if standaloneDocument != nil {
|
||||
|
||||
var err error
|
||||
refdDocumentNode, _, err = jsonPointer.Get(standaloneDocument)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
} else {
|
||||
dsp, err := d.pool.GetDocument(*currentSchema.ref)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
refdDocumentNode, _, err = jsonPointer.Get(dsp.Document)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
if !isKind(refdDocumentNode, reflect.Map) {
|
||||
return errors.New(formatErrorDescription(
|
||||
Locale.MustBeOfType(),
|
||||
ErrorDetails{"key": STRING_SCHEMA, "type": TYPE_OBJECT},
|
||||
))
|
||||
}
|
||||
|
||||
// returns the loaded referenced subSchema for the caller to update its current subSchema
|
||||
newSchemaDocument := refdDocumentNode.(map[string]interface{})
|
||||
newSchema := &subSchema{property: KEY_REF, parent: currentSchema, ref: currentSchema.ref}
|
||||
d.referencePool.Add(currentSchema.ref.String()+reference, newSchema)
|
||||
|
||||
err := d.parseSchema(newSchemaDocument, newSchema)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
currentSchema.refSchema = newSchema
|
||||
|
||||
return nil
|
||||
|
||||
}
|
||||
|
||||
func (d *Schema) parseProperties(documentNode interface{}, currentSchema *subSchema) error {
|
||||
|
||||
if !isKind(documentNode, reflect.Map) {
|
||||
return errors.New(formatErrorDescription(
|
||||
Locale.MustBeOfType(),
|
||||
ErrorDetails{"key": STRING_PROPERTIES, "type": TYPE_OBJECT},
|
||||
))
|
||||
}
|
||||
|
||||
m := documentNode.(map[string]interface{})
|
||||
for k := range m {
|
||||
schemaProperty := k
|
||||
newSchema := &subSchema{property: schemaProperty, parent: currentSchema, ref: currentSchema.ref}
|
||||
currentSchema.AddPropertiesChild(newSchema)
|
||||
err := d.parseSchema(m[k], newSchema)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
}
|
||||
|
||||
return nil
|
||||
}
|
||||
|
||||
func (d *Schema) parseDependencies(documentNode interface{}, currentSchema *subSchema) error {
|
||||
|
||||
if !isKind(documentNode, reflect.Map) {
|
||||
return errors.New(formatErrorDescription(
|
||||
Locale.MustBeOfType(),
|
||||
ErrorDetails{"key": KEY_DEPENDENCIES, "type": TYPE_OBJECT},
|
||||
))
|
||||
}
|
||||
|
||||
m := documentNode.(map[string]interface{})
|
||||
currentSchema.dependencies = make(map[string]interface{})
|
||||
|
||||
for k := range m {
|
||||
switch reflect.ValueOf(m[k]).Kind() {
|
||||
|
||||
case reflect.Slice:
|
||||
values := m[k].([]interface{})
|
||||
var valuesToRegister []string
|
||||
|
||||
for _, value := range values {
|
||||
if !isKind(value, reflect.String) {
|
||||
return errors.New(formatErrorDescription(
|
||||
Locale.MustBeOfType(),
|
||||
ErrorDetails{
|
||||
"key": STRING_DEPENDENCY,
|
||||
"type": STRING_SCHEMA_OR_ARRAY_OF_STRINGS,
|
||||
},
|
||||
))
|
||||
} else {
|
||||
valuesToRegister = append(valuesToRegister, value.(string))
|
||||
}
|
||||
currentSchema.dependencies[k] = valuesToRegister
|
||||
}
|
||||
|
||||
case reflect.Map:
|
||||
depSchema := &subSchema{property: k, parent: currentSchema, ref: currentSchema.ref}
|
||||
err := d.parseSchema(m[k], depSchema)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
currentSchema.dependencies[k] = depSchema
|
||||
|
||||
default:
|
||||
return errors.New(formatErrorDescription(
|
||||
Locale.MustBeOfType(),
|
||||
ErrorDetails{
|
||||
"key": STRING_DEPENDENCY,
|
||||
"type": STRING_SCHEMA_OR_ARRAY_OF_STRINGS,
|
||||
},
|
||||
))
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
return nil
|
||||
}
|
109
vendor/github.com/xeipuuv/gojsonschema/schemaPool.go
generated
vendored
Normal file
109
vendor/github.com/xeipuuv/gojsonschema/schemaPool.go
generated
vendored
Normal file
@@ -0,0 +1,109 @@
|
||||
// Copyright 2015 xeipuuv ( https://github.com/xeipuuv )
|
||||
//
|
||||
// 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.
|
||||
|
||||
// author xeipuuv
|
||||
// author-github https://github.com/xeipuuv
|
||||
// author-mail xeipuuv@gmail.com
|
||||
//
|
||||
// repository-name gojsonschema
|
||||
// repository-desc An implementation of JSON Schema, based on IETF's draft v4 - Go language.
|
||||
//
|
||||
// description Defines resources pooling.
|
||||
// Eases referencing and avoids downloading the same resource twice.
|
||||
//
|
||||
// created 26-02-2013
|
||||
|
||||
package gojsonschema
|
||||
|
||||
import (
|
||||
"errors"
|
||||
|
||||
"github.com/xeipuuv/gojsonreference"
|
||||
)
|
||||
|
||||
type schemaPoolDocument struct {
|
||||
Document interface{}
|
||||
}
|
||||
|
||||
type schemaPool struct {
|
||||
schemaPoolDocuments map[string]*schemaPoolDocument
|
||||
standaloneDocument interface{}
|
||||
jsonLoaderFactory JSONLoaderFactory
|
||||
}
|
||||
|
||||
func newSchemaPool(f JSONLoaderFactory) *schemaPool {
|
||||
|
||||
p := &schemaPool{}
|
||||
p.schemaPoolDocuments = make(map[string]*schemaPoolDocument)
|
||||
p.standaloneDocument = nil
|
||||
p.jsonLoaderFactory = f
|
||||
|
||||
return p
|
||||
}
|
||||
|
||||
func (p *schemaPool) SetStandaloneDocument(document interface{}) {
|
||||
p.standaloneDocument = document
|
||||
}
|
||||
|
||||
func (p *schemaPool) GetStandaloneDocument() (document interface{}) {
|
||||
return p.standaloneDocument
|
||||
}
|
||||
|
||||
func (p *schemaPool) GetDocument(reference gojsonreference.JsonReference) (*schemaPoolDocument, error) {
|
||||
|
||||
if internalLogEnabled {
|
||||
internalLog("Get Document ( %s )", reference.String())
|
||||
}
|
||||
|
||||
var err error
|
||||
|
||||
// It is not possible to load anything that is not canonical...
|
||||
if !reference.IsCanonical() {
|
||||
return nil, errors.New(formatErrorDescription(
|
||||
Locale.ReferenceMustBeCanonical(),
|
||||
ErrorDetails{"reference": reference},
|
||||
))
|
||||
}
|
||||
|
||||
refToUrl := reference
|
||||
refToUrl.GetUrl().Fragment = ""
|
||||
|
||||
var spd *schemaPoolDocument
|
||||
|
||||
// Try to find the requested document in the pool
|
||||
for k := range p.schemaPoolDocuments {
|
||||
if k == refToUrl.String() {
|
||||
spd = p.schemaPoolDocuments[k]
|
||||
}
|
||||
}
|
||||
|
||||
if spd != nil {
|
||||
if internalLogEnabled {
|
||||
internalLog(" From pool")
|
||||
}
|
||||
return spd, nil
|
||||
}
|
||||
|
||||
jsonReferenceLoader := p.jsonLoaderFactory.New(reference.String())
|
||||
document, err := jsonReferenceLoader.LoadJSON()
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
spd = &schemaPoolDocument{Document: document}
|
||||
// add the document to the pool for potential later use
|
||||
p.schemaPoolDocuments[refToUrl.String()] = spd
|
||||
|
||||
return spd, nil
|
||||
}
|
67
vendor/github.com/xeipuuv/gojsonschema/schemaReferencePool.go
generated
vendored
Normal file
67
vendor/github.com/xeipuuv/gojsonschema/schemaReferencePool.go
generated
vendored
Normal file
@@ -0,0 +1,67 @@
|
||||
// Copyright 2015 xeipuuv ( https://github.com/xeipuuv )
|
||||
//
|
||||
// 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.
|
||||
|
||||
// author xeipuuv
|
||||
// author-github https://github.com/xeipuuv
|
||||
// author-mail xeipuuv@gmail.com
|
||||
//
|
||||
// repository-name gojsonschema
|
||||
// repository-desc An implementation of JSON Schema, based on IETF's draft v4 - Go language.
|
||||
//
|
||||
// description Pool of referenced schemas.
|
||||
//
|
||||
// created 25-06-2013
|
||||
|
||||
package gojsonschema
|
||||
|
||||
import (
|
||||
"fmt"
|
||||
)
|
||||
|
||||
type schemaReferencePool struct {
|
||||
documents map[string]*subSchema
|
||||
}
|
||||
|
||||
func newSchemaReferencePool() *schemaReferencePool {
|
||||
|
||||
p := &schemaReferencePool{}
|
||||
p.documents = make(map[string]*subSchema)
|
||||
|
||||
return p
|
||||
}
|
||||
|
||||
func (p *schemaReferencePool) Get(ref string) (r *subSchema, o bool) {
|
||||
|
||||
if internalLogEnabled {
|
||||
internalLog(fmt.Sprintf("Schema Reference ( %s )", ref))
|
||||
}
|
||||
|
||||
if sch, ok := p.documents[ref]; ok {
|
||||
if internalLogEnabled {
|
||||
internalLog(fmt.Sprintf(" From pool"))
|
||||
}
|
||||
return sch, true
|
||||
}
|
||||
|
||||
return nil, false
|
||||
}
|
||||
|
||||
func (p *schemaReferencePool) Add(ref string, sch *subSchema) {
|
||||
|
||||
if internalLogEnabled {
|
||||
internalLog(fmt.Sprintf("Add Schema Reference %s to pool", ref))
|
||||
}
|
||||
|
||||
p.documents[ref] = sch
|
||||
}
|
83
vendor/github.com/xeipuuv/gojsonschema/schemaType.go
generated
vendored
Normal file
83
vendor/github.com/xeipuuv/gojsonschema/schemaType.go
generated
vendored
Normal file
@@ -0,0 +1,83 @@
|
||||
// Copyright 2015 xeipuuv ( https://github.com/xeipuuv )
|
||||
//
|
||||
// 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.
|
||||
|
||||
// author xeipuuv
|
||||
// author-github https://github.com/xeipuuv
|
||||
// author-mail xeipuuv@gmail.com
|
||||
//
|
||||
// repository-name gojsonschema
|
||||
// repository-desc An implementation of JSON Schema, based on IETF's draft v4 - Go language.
|
||||
//
|
||||
// description Helper structure to handle schema types, and the combination of them.
|
||||
//
|
||||
// created 28-02-2013
|
||||
|
||||
package gojsonschema
|
||||
|
||||
import (
|
||||
"errors"
|
||||
"fmt"
|
||||
"strings"
|
||||
)
|
||||
|
||||
type jsonSchemaType struct {
|
||||
types []string
|
||||
}
|
||||
|
||||
// Is the schema typed ? that is containing at least one type
|
||||
// When not typed, the schema does not need any type validation
|
||||
func (t *jsonSchemaType) IsTyped() bool {
|
||||
return len(t.types) > 0
|
||||
}
|
||||
|
||||
func (t *jsonSchemaType) Add(etype string) error {
|
||||
|
||||
if !isStringInSlice(JSON_TYPES, etype) {
|
||||
return errors.New(formatErrorDescription(Locale.NotAValidType(), ErrorDetails{"type": etype}))
|
||||
}
|
||||
|
||||
if t.Contains(etype) {
|
||||
return errors.New(formatErrorDescription(Locale.Duplicated(), ErrorDetails{"type": etype}))
|
||||
}
|
||||
|
||||
t.types = append(t.types, etype)
|
||||
|
||||
return nil
|
||||
}
|
||||
|
||||
func (t *jsonSchemaType) Contains(etype string) bool {
|
||||
|
||||
for _, v := range t.types {
|
||||
if v == etype {
|
||||
return true
|
||||
}
|
||||
}
|
||||
|
||||
return false
|
||||
}
|
||||
|
||||
func (t *jsonSchemaType) String() string {
|
||||
|
||||
if len(t.types) == 0 {
|
||||
return STRING_UNDEFINED // should never happen
|
||||
}
|
||||
|
||||
// Displayed as a list [type1,type2,...]
|
||||
if len(t.types) > 1 {
|
||||
return fmt.Sprintf("[%s]", strings.Join(t.types, ","))
|
||||
}
|
||||
|
||||
// Only one type: name only
|
||||
return t.types[0]
|
||||
}
|
227
vendor/github.com/xeipuuv/gojsonschema/subSchema.go
generated
vendored
Normal file
227
vendor/github.com/xeipuuv/gojsonschema/subSchema.go
generated
vendored
Normal file
@@ -0,0 +1,227 @@
|
||||
// Copyright 2015 xeipuuv ( https://github.com/xeipuuv )
|
||||
//
|
||||
// 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.
|
||||
|
||||
// author xeipuuv
|
||||
// author-github https://github.com/xeipuuv
|
||||
// author-mail xeipuuv@gmail.com
|
||||
//
|
||||
// repository-name gojsonschema
|
||||
// repository-desc An implementation of JSON Schema, based on IETF's draft v4 - Go language.
|
||||
//
|
||||
// description Defines the structure of a sub-subSchema.
|
||||
// A sub-subSchema can contain other sub-schemas.
|
||||
//
|
||||
// created 27-02-2013
|
||||
|
||||
package gojsonschema
|
||||
|
||||
import (
|
||||
"errors"
|
||||
"regexp"
|
||||
"strings"
|
||||
|
||||
"github.com/xeipuuv/gojsonreference"
|
||||
)
|
||||
|
||||
const (
|
||||
KEY_SCHEMA = "$subSchema"
|
||||
KEY_ID = "$id"
|
||||
KEY_REF = "$ref"
|
||||
KEY_TITLE = "title"
|
||||
KEY_DESCRIPTION = "description"
|
||||
KEY_TYPE = "type"
|
||||
KEY_ITEMS = "items"
|
||||
KEY_ADDITIONAL_ITEMS = "additionalItems"
|
||||
KEY_PROPERTIES = "properties"
|
||||
KEY_PATTERN_PROPERTIES = "patternProperties"
|
||||
KEY_ADDITIONAL_PROPERTIES = "additionalProperties"
|
||||
KEY_DEFINITIONS = "definitions"
|
||||
KEY_MULTIPLE_OF = "multipleOf"
|
||||
KEY_MINIMUM = "minimum"
|
||||
KEY_MAXIMUM = "maximum"
|
||||
KEY_EXCLUSIVE_MINIMUM = "exclusiveMinimum"
|
||||
KEY_EXCLUSIVE_MAXIMUM = "exclusiveMaximum"
|
||||
KEY_MIN_LENGTH = "minLength"
|
||||
KEY_MAX_LENGTH = "maxLength"
|
||||
KEY_PATTERN = "pattern"
|
||||
KEY_FORMAT = "format"
|
||||
KEY_MIN_PROPERTIES = "minProperties"
|
||||
KEY_MAX_PROPERTIES = "maxProperties"
|
||||
KEY_DEPENDENCIES = "dependencies"
|
||||
KEY_REQUIRED = "required"
|
||||
KEY_MIN_ITEMS = "minItems"
|
||||
KEY_MAX_ITEMS = "maxItems"
|
||||
KEY_UNIQUE_ITEMS = "uniqueItems"
|
||||
KEY_ENUM = "enum"
|
||||
KEY_ONE_OF = "oneOf"
|
||||
KEY_ANY_OF = "anyOf"
|
||||
KEY_ALL_OF = "allOf"
|
||||
KEY_NOT = "not"
|
||||
)
|
||||
|
||||
type subSchema struct {
|
||||
|
||||
// basic subSchema meta properties
|
||||
id *string
|
||||
title *string
|
||||
description *string
|
||||
|
||||
property string
|
||||
|
||||
// Types associated with the subSchema
|
||||
types jsonSchemaType
|
||||
|
||||
// Reference url
|
||||
ref *gojsonreference.JsonReference
|
||||
// Schema referenced
|
||||
refSchema *subSchema
|
||||
// Json reference
|
||||
subSchema *gojsonreference.JsonReference
|
||||
|
||||
// hierarchy
|
||||
parent *subSchema
|
||||
definitions map[string]*subSchema
|
||||
definitionsChildren []*subSchema
|
||||
itemsChildren []*subSchema
|
||||
itemsChildrenIsSingleSchema bool
|
||||
propertiesChildren []*subSchema
|
||||
|
||||
// validation : number / integer
|
||||
multipleOf *float64
|
||||
maximum *float64
|
||||
exclusiveMaximum bool
|
||||
minimum *float64
|
||||
exclusiveMinimum bool
|
||||
|
||||
// validation : string
|
||||
minLength *int
|
||||
maxLength *int
|
||||
pattern *regexp.Regexp
|
||||
format string
|
||||
|
||||
// validation : object
|
||||
minProperties *int
|
||||
maxProperties *int
|
||||
required []string
|
||||
|
||||
dependencies map[string]interface{}
|
||||
additionalProperties interface{}
|
||||
patternProperties map[string]*subSchema
|
||||
|
||||
// validation : array
|
||||
minItems *int
|
||||
maxItems *int
|
||||
uniqueItems bool
|
||||
|
||||
additionalItems interface{}
|
||||
|
||||
// validation : all
|
||||
enum []string
|
||||
|
||||
// validation : subSchema
|
||||
oneOf []*subSchema
|
||||
anyOf []*subSchema
|
||||
allOf []*subSchema
|
||||
not *subSchema
|
||||
}
|
||||
|
||||
func (s *subSchema) AddEnum(i interface{}) error {
|
||||
|
||||
is, err := marshalToJsonString(i)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
if isStringInSlice(s.enum, *is) {
|
||||
return errors.New(formatErrorDescription(
|
||||
Locale.KeyItemsMustBeUnique(),
|
||||
ErrorDetails{"key": KEY_ENUM},
|
||||
))
|
||||
}
|
||||
|
||||
s.enum = append(s.enum, *is)
|
||||
|
||||
return nil
|
||||
}
|
||||
|
||||
func (s *subSchema) ContainsEnum(i interface{}) (bool, error) {
|
||||
|
||||
is, err := marshalToJsonString(i)
|
||||
if err != nil {
|
||||
return false, err
|
||||
}
|
||||
|
||||
return isStringInSlice(s.enum, *is), nil
|
||||
}
|
||||
|
||||
func (s *subSchema) AddOneOf(subSchema *subSchema) {
|
||||
s.oneOf = append(s.oneOf, subSchema)
|
||||
}
|
||||
|
||||
func (s *subSchema) AddAllOf(subSchema *subSchema) {
|
||||
s.allOf = append(s.allOf, subSchema)
|
||||
}
|
||||
|
||||
func (s *subSchema) AddAnyOf(subSchema *subSchema) {
|
||||
s.anyOf = append(s.anyOf, subSchema)
|
||||
}
|
||||
|
||||
func (s *subSchema) SetNot(subSchema *subSchema) {
|
||||
s.not = subSchema
|
||||
}
|
||||
|
||||
func (s *subSchema) AddRequired(value string) error {
|
||||
|
||||
if isStringInSlice(s.required, value) {
|
||||
return errors.New(formatErrorDescription(
|
||||
Locale.KeyItemsMustBeUnique(),
|
||||
ErrorDetails{"key": KEY_REQUIRED},
|
||||
))
|
||||
}
|
||||
|
||||
s.required = append(s.required, value)
|
||||
|
||||
return nil
|
||||
}
|
||||
|
||||
func (s *subSchema) AddDefinitionChild(child *subSchema) {
|
||||
s.definitionsChildren = append(s.definitionsChildren, child)
|
||||
}
|
||||
|
||||
func (s *subSchema) AddItemsChild(child *subSchema) {
|
||||
s.itemsChildren = append(s.itemsChildren, child)
|
||||
}
|
||||
|
||||
func (s *subSchema) AddPropertiesChild(child *subSchema) {
|
||||
s.propertiesChildren = append(s.propertiesChildren, child)
|
||||
}
|
||||
|
||||
func (s *subSchema) PatternPropertiesString() string {
|
||||
|
||||
if s.patternProperties == nil || len(s.patternProperties) == 0 {
|
||||
return STRING_UNDEFINED // should never happen
|
||||
}
|
||||
|
||||
patternPropertiesKeySlice := []string{}
|
||||
for pk := range s.patternProperties {
|
||||
patternPropertiesKeySlice = append(patternPropertiesKeySlice, `"`+pk+`"`)
|
||||
}
|
||||
|
||||
if len(patternPropertiesKeySlice) == 1 {
|
||||
return patternPropertiesKeySlice[0]
|
||||
}
|
||||
|
||||
return "[" + strings.Join(patternPropertiesKeySlice, ",") + "]"
|
||||
|
||||
}
|
58
vendor/github.com/xeipuuv/gojsonschema/types.go
generated
vendored
Normal file
58
vendor/github.com/xeipuuv/gojsonschema/types.go
generated
vendored
Normal file
@@ -0,0 +1,58 @@
|
||||
// Copyright 2015 xeipuuv ( https://github.com/xeipuuv )
|
||||
//
|
||||
// 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.
|
||||
|
||||
// author xeipuuv
|
||||
// author-github https://github.com/xeipuuv
|
||||
// author-mail xeipuuv@gmail.com
|
||||
//
|
||||
// repository-name gojsonschema
|
||||
// repository-desc An implementation of JSON Schema, based on IETF's draft v4 - Go language.
|
||||
//
|
||||
// description Contains const types for schema and JSON.
|
||||
//
|
||||
// created 28-02-2013
|
||||
|
||||
package gojsonschema
|
||||
|
||||
const (
|
||||
TYPE_ARRAY = `array`
|
||||
TYPE_BOOLEAN = `boolean`
|
||||
TYPE_INTEGER = `integer`
|
||||
TYPE_NUMBER = `number`
|
||||
TYPE_NULL = `null`
|
||||
TYPE_OBJECT = `object`
|
||||
TYPE_STRING = `string`
|
||||
)
|
||||
|
||||
var JSON_TYPES []string
|
||||
var SCHEMA_TYPES []string
|
||||
|
||||
func init() {
|
||||
JSON_TYPES = []string{
|
||||
TYPE_ARRAY,
|
||||
TYPE_BOOLEAN,
|
||||
TYPE_INTEGER,
|
||||
TYPE_NUMBER,
|
||||
TYPE_NULL,
|
||||
TYPE_OBJECT,
|
||||
TYPE_STRING}
|
||||
|
||||
SCHEMA_TYPES = []string{
|
||||
TYPE_ARRAY,
|
||||
TYPE_BOOLEAN,
|
||||
TYPE_INTEGER,
|
||||
TYPE_NUMBER,
|
||||
TYPE_OBJECT,
|
||||
TYPE_STRING}
|
||||
}
|
208
vendor/github.com/xeipuuv/gojsonschema/utils.go
generated
vendored
Normal file
208
vendor/github.com/xeipuuv/gojsonschema/utils.go
generated
vendored
Normal file
@@ -0,0 +1,208 @@
|
||||
// Copyright 2015 xeipuuv ( https://github.com/xeipuuv )
|
||||
//
|
||||
// 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.
|
||||
|
||||
// author xeipuuv
|
||||
// author-github https://github.com/xeipuuv
|
||||
// author-mail xeipuuv@gmail.com
|
||||
//
|
||||
// repository-name gojsonschema
|
||||
// repository-desc An implementation of JSON Schema, based on IETF's draft v4 - Go language.
|
||||
//
|
||||
// description Various utility functions.
|
||||
//
|
||||
// created 26-02-2013
|
||||
|
||||
package gojsonschema
|
||||
|
||||
import (
|
||||
"encoding/json"
|
||||
"fmt"
|
||||
"math"
|
||||
"reflect"
|
||||
"strconv"
|
||||
)
|
||||
|
||||
func isKind(what interface{}, kind reflect.Kind) bool {
|
||||
target := what
|
||||
if isJsonNumber(what) {
|
||||
// JSON Numbers are strings!
|
||||
target = *mustBeNumber(what)
|
||||
}
|
||||
return reflect.ValueOf(target).Kind() == kind
|
||||
}
|
||||
|
||||
func existsMapKey(m map[string]interface{}, k string) bool {
|
||||
_, ok := m[k]
|
||||
return ok
|
||||
}
|
||||
|
||||
func isStringInSlice(s []string, what string) bool {
|
||||
for i := range s {
|
||||
if s[i] == what {
|
||||
return true
|
||||
}
|
||||
}
|
||||
return false
|
||||
}
|
||||
|
||||
func marshalToJsonString(value interface{}) (*string, error) {
|
||||
|
||||
mBytes, err := json.Marshal(value)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
sBytes := string(mBytes)
|
||||
return &sBytes, nil
|
||||
}
|
||||
|
||||
func isJsonNumber(what interface{}) bool {
|
||||
|
||||
switch what.(type) {
|
||||
|
||||
case json.Number:
|
||||
return true
|
||||
}
|
||||
|
||||
return false
|
||||
}
|
||||
|
||||
func checkJsonNumber(what interface{}) (isValidFloat64 bool, isValidInt64 bool, isValidInt32 bool) {
|
||||
|
||||
jsonNumber := what.(json.Number)
|
||||
|
||||
f64, errFloat64 := jsonNumber.Float64()
|
||||
s64 := strconv.FormatFloat(f64, 'f', -1, 64)
|
||||
_, errInt64 := strconv.ParseInt(s64, 10, 64)
|
||||
|
||||
isValidFloat64 = errFloat64 == nil
|
||||
isValidInt64 = errInt64 == nil
|
||||
|
||||
_, errInt32 := strconv.ParseInt(s64, 10, 32)
|
||||
isValidInt32 = isValidInt64 && errInt32 == nil
|
||||
|
||||
return
|
||||
|
||||
}
|
||||
|
||||
// same as ECMA Number.MAX_SAFE_INTEGER and Number.MIN_SAFE_INTEGER
|
||||
const (
|
||||
max_json_float = float64(1<<53 - 1) // 9007199254740991.0 2^53 - 1
|
||||
min_json_float = -float64(1<<53 - 1) //-9007199254740991.0 -2^53 - 1
|
||||
)
|
||||
|
||||
func isFloat64AnInteger(f float64) bool {
|
||||
|
||||
if math.IsNaN(f) || math.IsInf(f, 0) || f < min_json_float || f > max_json_float {
|
||||
return false
|
||||
}
|
||||
|
||||
return f == float64(int64(f)) || f == float64(uint64(f))
|
||||
}
|
||||
|
||||
func mustBeInteger(what interface{}) *int {
|
||||
|
||||
if isJsonNumber(what) {
|
||||
|
||||
number := what.(json.Number)
|
||||
|
||||
_, _, isValidInt32 := checkJsonNumber(number)
|
||||
|
||||
if isValidInt32 {
|
||||
|
||||
int64Value, err := number.Int64()
|
||||
if err != nil {
|
||||
return nil
|
||||
}
|
||||
|
||||
int32Value := int(int64Value)
|
||||
return &int32Value
|
||||
|
||||
} else {
|
||||
return nil
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
return nil
|
||||
}
|
||||
|
||||
func mustBeNumber(what interface{}) *float64 {
|
||||
|
||||
if isJsonNumber(what) {
|
||||
|
||||
number := what.(json.Number)
|
||||
float64Value, err := number.Float64()
|
||||
|
||||
if err == nil {
|
||||
return &float64Value
|
||||
} else {
|
||||
return nil
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
return nil
|
||||
|
||||
}
|
||||
|
||||
// formats a number so that it is displayed as the smallest string possible
|
||||
func resultErrorFormatJsonNumber(n json.Number) string {
|
||||
|
||||
if int64Value, err := n.Int64(); err == nil {
|
||||
return fmt.Sprintf("%d", int64Value)
|
||||
}
|
||||
|
||||
float64Value, _ := n.Float64()
|
||||
|
||||
return fmt.Sprintf("%g", float64Value)
|
||||
}
|
||||
|
||||
// formats a number so that it is displayed as the smallest string possible
|
||||
func resultErrorFormatNumber(n float64) string {
|
||||
|
||||
if isFloat64AnInteger(n) {
|
||||
return fmt.Sprintf("%d", int64(n))
|
||||
}
|
||||
|
||||
return fmt.Sprintf("%g", n)
|
||||
}
|
||||
|
||||
func convertDocumentNode(val interface{}) interface{} {
|
||||
|
||||
if lval, ok := val.([]interface{}); ok {
|
||||
|
||||
res := []interface{}{}
|
||||
for _, v := range lval {
|
||||
res = append(res, convertDocumentNode(v))
|
||||
}
|
||||
|
||||
return res
|
||||
|
||||
}
|
||||
|
||||
if mval, ok := val.(map[interface{}]interface{}); ok {
|
||||
|
||||
res := map[string]interface{}{}
|
||||
|
||||
for k, v := range mval {
|
||||
res[k.(string)] = convertDocumentNode(v)
|
||||
}
|
||||
|
||||
return res
|
||||
|
||||
}
|
||||
|
||||
return val
|
||||
}
|
832
vendor/github.com/xeipuuv/gojsonschema/validation.go
generated
vendored
Normal file
832
vendor/github.com/xeipuuv/gojsonschema/validation.go
generated
vendored
Normal file
@@ -0,0 +1,832 @@
|
||||
// Copyright 2015 xeipuuv ( https://github.com/xeipuuv )
|
||||
//
|
||||
// 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.
|
||||
|
||||
// author xeipuuv
|
||||
// author-github https://github.com/xeipuuv
|
||||
// author-mail xeipuuv@gmail.com
|
||||
//
|
||||
// repository-name gojsonschema
|
||||
// repository-desc An implementation of JSON Schema, based on IETF's draft v4 - Go language.
|
||||
//
|
||||
// description Extends Schema and subSchema, implements the validation phase.
|
||||
//
|
||||
// created 28-02-2013
|
||||
|
||||
package gojsonschema
|
||||
|
||||
import (
|
||||
"encoding/json"
|
||||
"reflect"
|
||||
"regexp"
|
||||
"strconv"
|
||||
"strings"
|
||||
"unicode/utf8"
|
||||
)
|
||||
|
||||
func Validate(ls JSONLoader, ld JSONLoader) (*Result, error) {
|
||||
|
||||
var err error
|
||||
|
||||
// load schema
|
||||
|
||||
schema, err := NewSchema(ls)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
// begine validation
|
||||
|
||||
return schema.Validate(ld)
|
||||
|
||||
}
|
||||
|
||||
func (v *Schema) Validate(l JSONLoader) (*Result, error) {
|
||||
|
||||
// load document
|
||||
|
||||
root, err := l.LoadJSON()
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
// begin validation
|
||||
|
||||
result := &Result{}
|
||||
context := newJsonContext(STRING_CONTEXT_ROOT, nil)
|
||||
v.rootSchema.validateRecursive(v.rootSchema, root, result, context)
|
||||
|
||||
return result, nil
|
||||
|
||||
}
|
||||
|
||||
func (v *subSchema) subValidateWithContext(document interface{}, context *jsonContext) *Result {
|
||||
result := &Result{}
|
||||
v.validateRecursive(v, document, result, context)
|
||||
return result
|
||||
}
|
||||
|
||||
// Walker function to validate the json recursively against the subSchema
|
||||
func (v *subSchema) validateRecursive(currentSubSchema *subSchema, currentNode interface{}, result *Result, context *jsonContext) {
|
||||
|
||||
if internalLogEnabled {
|
||||
internalLog("validateRecursive %s", context.String())
|
||||
internalLog(" %v", currentNode)
|
||||
}
|
||||
|
||||
// Handle referenced schemas, returns directly when a $ref is found
|
||||
if currentSubSchema.refSchema != nil {
|
||||
v.validateRecursive(currentSubSchema.refSchema, currentNode, result, context)
|
||||
return
|
||||
}
|
||||
|
||||
// Check for null value
|
||||
if currentNode == nil {
|
||||
if currentSubSchema.types.IsTyped() && !currentSubSchema.types.Contains(TYPE_NULL) {
|
||||
result.addError(
|
||||
new(InvalidTypeError),
|
||||
context,
|
||||
currentNode,
|
||||
ErrorDetails{
|
||||
"expected": currentSubSchema.types.String(),
|
||||
"given": TYPE_NULL,
|
||||
},
|
||||
)
|
||||
return
|
||||
}
|
||||
|
||||
currentSubSchema.validateSchema(currentSubSchema, currentNode, result, context)
|
||||
v.validateCommon(currentSubSchema, currentNode, result, context)
|
||||
|
||||
} else { // Not a null value
|
||||
|
||||
if isJsonNumber(currentNode) {
|
||||
|
||||
value := currentNode.(json.Number)
|
||||
|
||||
_, isValidInt64, _ := checkJsonNumber(value)
|
||||
|
||||
validType := currentSubSchema.types.Contains(TYPE_NUMBER) || (isValidInt64 && currentSubSchema.types.Contains(TYPE_INTEGER))
|
||||
|
||||
if currentSubSchema.types.IsTyped() && !validType {
|
||||
|
||||
givenType := TYPE_INTEGER
|
||||
if !isValidInt64 {
|
||||
givenType = TYPE_NUMBER
|
||||
}
|
||||
|
||||
result.addError(
|
||||
new(InvalidTypeError),
|
||||
context,
|
||||
currentNode,
|
||||
ErrorDetails{
|
||||
"expected": currentSubSchema.types.String(),
|
||||
"given": givenType,
|
||||
},
|
||||
)
|
||||
return
|
||||
}
|
||||
|
||||
currentSubSchema.validateSchema(currentSubSchema, value, result, context)
|
||||
v.validateNumber(currentSubSchema, value, result, context)
|
||||
v.validateCommon(currentSubSchema, value, result, context)
|
||||
v.validateString(currentSubSchema, value, result, context)
|
||||
|
||||
} else {
|
||||
|
||||
rValue := reflect.ValueOf(currentNode)
|
||||
rKind := rValue.Kind()
|
||||
|
||||
switch rKind {
|
||||
|
||||
// Slice => JSON array
|
||||
|
||||
case reflect.Slice:
|
||||
|
||||
if currentSubSchema.types.IsTyped() && !currentSubSchema.types.Contains(TYPE_ARRAY) {
|
||||
result.addError(
|
||||
new(InvalidTypeError),
|
||||
context,
|
||||
currentNode,
|
||||
ErrorDetails{
|
||||
"expected": currentSubSchema.types.String(),
|
||||
"given": TYPE_ARRAY,
|
||||
},
|
||||
)
|
||||
return
|
||||
}
|
||||
|
||||
castCurrentNode := currentNode.([]interface{})
|
||||
|
||||
currentSubSchema.validateSchema(currentSubSchema, castCurrentNode, result, context)
|
||||
|
||||
v.validateArray(currentSubSchema, castCurrentNode, result, context)
|
||||
v.validateCommon(currentSubSchema, castCurrentNode, result, context)
|
||||
|
||||
// Map => JSON object
|
||||
|
||||
case reflect.Map:
|
||||
if currentSubSchema.types.IsTyped() && !currentSubSchema.types.Contains(TYPE_OBJECT) {
|
||||
result.addError(
|
||||
new(InvalidTypeError),
|
||||
context,
|
||||
currentNode,
|
||||
ErrorDetails{
|
||||
"expected": currentSubSchema.types.String(),
|
||||
"given": TYPE_OBJECT,
|
||||
},
|
||||
)
|
||||
return
|
||||
}
|
||||
|
||||
castCurrentNode, ok := currentNode.(map[string]interface{})
|
||||
if !ok {
|
||||
castCurrentNode = convertDocumentNode(currentNode).(map[string]interface{})
|
||||
}
|
||||
|
||||
currentSubSchema.validateSchema(currentSubSchema, castCurrentNode, result, context)
|
||||
|
||||
v.validateObject(currentSubSchema, castCurrentNode, result, context)
|
||||
v.validateCommon(currentSubSchema, castCurrentNode, result, context)
|
||||
|
||||
for _, pSchema := range currentSubSchema.propertiesChildren {
|
||||
nextNode, ok := castCurrentNode[pSchema.property]
|
||||
if ok {
|
||||
subContext := newJsonContext(pSchema.property, context)
|
||||
v.validateRecursive(pSchema, nextNode, result, subContext)
|
||||
}
|
||||
}
|
||||
|
||||
// Simple JSON values : string, number, boolean
|
||||
|
||||
case reflect.Bool:
|
||||
|
||||
if currentSubSchema.types.IsTyped() && !currentSubSchema.types.Contains(TYPE_BOOLEAN) {
|
||||
result.addError(
|
||||
new(InvalidTypeError),
|
||||
context,
|
||||
currentNode,
|
||||
ErrorDetails{
|
||||
"expected": currentSubSchema.types.String(),
|
||||
"given": TYPE_BOOLEAN,
|
||||
},
|
||||
)
|
||||
return
|
||||
}
|
||||
|
||||
value := currentNode.(bool)
|
||||
|
||||
currentSubSchema.validateSchema(currentSubSchema, value, result, context)
|
||||
v.validateNumber(currentSubSchema, value, result, context)
|
||||
v.validateCommon(currentSubSchema, value, result, context)
|
||||
v.validateString(currentSubSchema, value, result, context)
|
||||
|
||||
case reflect.String:
|
||||
|
||||
if currentSubSchema.types.IsTyped() && !currentSubSchema.types.Contains(TYPE_STRING) {
|
||||
result.addError(
|
||||
new(InvalidTypeError),
|
||||
context,
|
||||
currentNode,
|
||||
ErrorDetails{
|
||||
"expected": currentSubSchema.types.String(),
|
||||
"given": TYPE_STRING,
|
||||
},
|
||||
)
|
||||
return
|
||||
}
|
||||
|
||||
value := currentNode.(string)
|
||||
|
||||
currentSubSchema.validateSchema(currentSubSchema, value, result, context)
|
||||
v.validateNumber(currentSubSchema, value, result, context)
|
||||
v.validateCommon(currentSubSchema, value, result, context)
|
||||
v.validateString(currentSubSchema, value, result, context)
|
||||
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
result.incrementScore()
|
||||
}
|
||||
|
||||
// Different kinds of validation there, subSchema / common / array / object / string...
|
||||
func (v *subSchema) validateSchema(currentSubSchema *subSchema, currentNode interface{}, result *Result, context *jsonContext) {
|
||||
|
||||
if internalLogEnabled {
|
||||
internalLog("validateSchema %s", context.String())
|
||||
internalLog(" %v", currentNode)
|
||||
}
|
||||
|
||||
if len(currentSubSchema.anyOf) > 0 {
|
||||
|
||||
validatedAnyOf := false
|
||||
var bestValidationResult *Result
|
||||
|
||||
for _, anyOfSchema := range currentSubSchema.anyOf {
|
||||
if !validatedAnyOf {
|
||||
validationResult := anyOfSchema.subValidateWithContext(currentNode, context)
|
||||
validatedAnyOf = validationResult.Valid()
|
||||
|
||||
if !validatedAnyOf && (bestValidationResult == nil || validationResult.score > bestValidationResult.score) {
|
||||
bestValidationResult = validationResult
|
||||
}
|
||||
}
|
||||
}
|
||||
if !validatedAnyOf {
|
||||
|
||||
result.addError(new(NumberAnyOfError), context, currentNode, ErrorDetails{})
|
||||
|
||||
if bestValidationResult != nil {
|
||||
// add error messages of closest matching subSchema as
|
||||
// that's probably the one the user was trying to match
|
||||
result.mergeErrors(bestValidationResult)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
if len(currentSubSchema.oneOf) > 0 {
|
||||
|
||||
nbValidated := 0
|
||||
var bestValidationResult *Result
|
||||
|
||||
for _, oneOfSchema := range currentSubSchema.oneOf {
|
||||
validationResult := oneOfSchema.subValidateWithContext(currentNode, context)
|
||||
if validationResult.Valid() {
|
||||
nbValidated++
|
||||
} else if nbValidated == 0 && (bestValidationResult == nil || validationResult.score > bestValidationResult.score) {
|
||||
bestValidationResult = validationResult
|
||||
}
|
||||
}
|
||||
|
||||
if nbValidated != 1 {
|
||||
|
||||
result.addError(new(NumberOneOfError), context, currentNode, ErrorDetails{})
|
||||
|
||||
if nbValidated == 0 {
|
||||
// add error messages of closest matching subSchema as
|
||||
// that's probably the one the user was trying to match
|
||||
result.mergeErrors(bestValidationResult)
|
||||
}
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
if len(currentSubSchema.allOf) > 0 {
|
||||
nbValidated := 0
|
||||
|
||||
for _, allOfSchema := range currentSubSchema.allOf {
|
||||
validationResult := allOfSchema.subValidateWithContext(currentNode, context)
|
||||
if validationResult.Valid() {
|
||||
nbValidated++
|
||||
}
|
||||
result.mergeErrors(validationResult)
|
||||
}
|
||||
|
||||
if nbValidated != len(currentSubSchema.allOf) {
|
||||
result.addError(new(NumberAllOfError), context, currentNode, ErrorDetails{})
|
||||
}
|
||||
}
|
||||
|
||||
if currentSubSchema.not != nil {
|
||||
validationResult := currentSubSchema.not.subValidateWithContext(currentNode, context)
|
||||
if validationResult.Valid() {
|
||||
result.addError(new(NumberNotError), context, currentNode, ErrorDetails{})
|
||||
}
|
||||
}
|
||||
|
||||
if currentSubSchema.dependencies != nil && len(currentSubSchema.dependencies) > 0 {
|
||||
if isKind(currentNode, reflect.Map) {
|
||||
for elementKey := range currentNode.(map[string]interface{}) {
|
||||
if dependency, ok := currentSubSchema.dependencies[elementKey]; ok {
|
||||
switch dependency := dependency.(type) {
|
||||
|
||||
case []string:
|
||||
for _, dependOnKey := range dependency {
|
||||
if _, dependencyResolved := currentNode.(map[string]interface{})[dependOnKey]; !dependencyResolved {
|
||||
result.addError(
|
||||
new(MissingDependencyError),
|
||||
context,
|
||||
currentNode,
|
||||
ErrorDetails{"dependency": dependOnKey},
|
||||
)
|
||||
}
|
||||
}
|
||||
|
||||
case *subSchema:
|
||||
dependency.validateRecursive(dependency, currentNode, result, context)
|
||||
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
result.incrementScore()
|
||||
}
|
||||
|
||||
func (v *subSchema) validateCommon(currentSubSchema *subSchema, value interface{}, result *Result, context *jsonContext) {
|
||||
|
||||
if internalLogEnabled {
|
||||
internalLog("validateCommon %s", context.String())
|
||||
internalLog(" %v", value)
|
||||
}
|
||||
|
||||
// enum:
|
||||
if len(currentSubSchema.enum) > 0 {
|
||||
has, err := currentSubSchema.ContainsEnum(value)
|
||||
if err != nil {
|
||||
result.addError(new(InternalError), context, value, ErrorDetails{"error": err})
|
||||
}
|
||||
if !has {
|
||||
result.addError(
|
||||
new(EnumError),
|
||||
context,
|
||||
value,
|
||||
ErrorDetails{
|
||||
"allowed": strings.Join(currentSubSchema.enum, ", "),
|
||||
},
|
||||
)
|
||||
}
|
||||
}
|
||||
|
||||
result.incrementScore()
|
||||
}
|
||||
|
||||
func (v *subSchema) validateArray(currentSubSchema *subSchema, value []interface{}, result *Result, context *jsonContext) {
|
||||
|
||||
if internalLogEnabled {
|
||||
internalLog("validateArray %s", context.String())
|
||||
internalLog(" %v", value)
|
||||
}
|
||||
|
||||
nbValues := len(value)
|
||||
|
||||
// TODO explain
|
||||
if currentSubSchema.itemsChildrenIsSingleSchema {
|
||||
for i := range value {
|
||||
subContext := newJsonContext(strconv.Itoa(i), context)
|
||||
validationResult := currentSubSchema.itemsChildren[0].subValidateWithContext(value[i], subContext)
|
||||
result.mergeErrors(validationResult)
|
||||
}
|
||||
} else {
|
||||
if currentSubSchema.itemsChildren != nil && len(currentSubSchema.itemsChildren) > 0 {
|
||||
|
||||
nbItems := len(currentSubSchema.itemsChildren)
|
||||
|
||||
// while we have both schemas and values, check them against each other
|
||||
for i := 0; i != nbItems && i != nbValues; i++ {
|
||||
subContext := newJsonContext(strconv.Itoa(i), context)
|
||||
validationResult := currentSubSchema.itemsChildren[i].subValidateWithContext(value[i], subContext)
|
||||
result.mergeErrors(validationResult)
|
||||
}
|
||||
|
||||
if nbItems < nbValues {
|
||||
// we have less schemas than elements in the instance array,
|
||||
// but that might be ok if "additionalItems" is specified.
|
||||
|
||||
switch currentSubSchema.additionalItems.(type) {
|
||||
case bool:
|
||||
if !currentSubSchema.additionalItems.(bool) {
|
||||
result.addError(new(ArrayNoAdditionalItemsError), context, value, ErrorDetails{})
|
||||
}
|
||||
case *subSchema:
|
||||
additionalItemSchema := currentSubSchema.additionalItems.(*subSchema)
|
||||
for i := nbItems; i != nbValues; i++ {
|
||||
subContext := newJsonContext(strconv.Itoa(i), context)
|
||||
validationResult := additionalItemSchema.subValidateWithContext(value[i], subContext)
|
||||
result.mergeErrors(validationResult)
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// minItems & maxItems
|
||||
if currentSubSchema.minItems != nil {
|
||||
if nbValues < int(*currentSubSchema.minItems) {
|
||||
result.addError(
|
||||
new(ArrayMinItemsError),
|
||||
context,
|
||||
value,
|
||||
ErrorDetails{"min": *currentSubSchema.minItems},
|
||||
)
|
||||
}
|
||||
}
|
||||
if currentSubSchema.maxItems != nil {
|
||||
if nbValues > int(*currentSubSchema.maxItems) {
|
||||
result.addError(
|
||||
new(ArrayMaxItemsError),
|
||||
context,
|
||||
value,
|
||||
ErrorDetails{"max": *currentSubSchema.maxItems},
|
||||
)
|
||||
}
|
||||
}
|
||||
|
||||
// uniqueItems:
|
||||
if currentSubSchema.uniqueItems {
|
||||
var stringifiedItems []string
|
||||
for _, v := range value {
|
||||
vString, err := marshalToJsonString(v)
|
||||
if err != nil {
|
||||
result.addError(new(InternalError), context, value, ErrorDetails{"err": err})
|
||||
}
|
||||
if isStringInSlice(stringifiedItems, *vString) {
|
||||
result.addError(
|
||||
new(ItemsMustBeUniqueError),
|
||||
context,
|
||||
value,
|
||||
ErrorDetails{"type": TYPE_ARRAY},
|
||||
)
|
||||
}
|
||||
stringifiedItems = append(stringifiedItems, *vString)
|
||||
}
|
||||
}
|
||||
|
||||
result.incrementScore()
|
||||
}
|
||||
|
||||
func (v *subSchema) validateObject(currentSubSchema *subSchema, value map[string]interface{}, result *Result, context *jsonContext) {
|
||||
|
||||
if internalLogEnabled {
|
||||
internalLog("validateObject %s", context.String())
|
||||
internalLog(" %v", value)
|
||||
}
|
||||
|
||||
// minProperties & maxProperties:
|
||||
if currentSubSchema.minProperties != nil {
|
||||
if len(value) < int(*currentSubSchema.minProperties) {
|
||||
result.addError(
|
||||
new(ArrayMinPropertiesError),
|
||||
context,
|
||||
value,
|
||||
ErrorDetails{"min": *currentSubSchema.minProperties},
|
||||
)
|
||||
}
|
||||
}
|
||||
if currentSubSchema.maxProperties != nil {
|
||||
if len(value) > int(*currentSubSchema.maxProperties) {
|
||||
result.addError(
|
||||
new(ArrayMaxPropertiesError),
|
||||
context,
|
||||
value,
|
||||
ErrorDetails{"max": *currentSubSchema.maxProperties},
|
||||
)
|
||||
}
|
||||
}
|
||||
|
||||
// required:
|
||||
for _, requiredProperty := range currentSubSchema.required {
|
||||
_, ok := value[requiredProperty]
|
||||
if ok {
|
||||
result.incrementScore()
|
||||
} else {
|
||||
result.addError(
|
||||
new(RequiredError),
|
||||
context,
|
||||
value,
|
||||
ErrorDetails{"property": requiredProperty},
|
||||
)
|
||||
}
|
||||
}
|
||||
|
||||
// additionalProperty & patternProperty:
|
||||
if currentSubSchema.additionalProperties != nil {
|
||||
|
||||
switch currentSubSchema.additionalProperties.(type) {
|
||||
case bool:
|
||||
|
||||
if !currentSubSchema.additionalProperties.(bool) {
|
||||
|
||||
for pk := range value {
|
||||
|
||||
found := false
|
||||
for _, spValue := range currentSubSchema.propertiesChildren {
|
||||
if pk == spValue.property {
|
||||
found = true
|
||||
}
|
||||
}
|
||||
|
||||
pp_has, pp_match := v.validatePatternProperty(currentSubSchema, pk, value[pk], result, context)
|
||||
|
||||
if found {
|
||||
|
||||
if pp_has && !pp_match {
|
||||
result.addError(
|
||||
new(AdditionalPropertyNotAllowedError),
|
||||
context,
|
||||
value,
|
||||
ErrorDetails{"property": pk},
|
||||
)
|
||||
}
|
||||
|
||||
} else {
|
||||
|
||||
if !pp_has || !pp_match {
|
||||
result.addError(
|
||||
new(AdditionalPropertyNotAllowedError),
|
||||
context,
|
||||
value,
|
||||
ErrorDetails{"property": pk},
|
||||
)
|
||||
}
|
||||
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
case *subSchema:
|
||||
|
||||
additionalPropertiesSchema := currentSubSchema.additionalProperties.(*subSchema)
|
||||
for pk := range value {
|
||||
|
||||
found := false
|
||||
for _, spValue := range currentSubSchema.propertiesChildren {
|
||||
if pk == spValue.property {
|
||||
found = true
|
||||
}
|
||||
}
|
||||
|
||||
pp_has, pp_match := v.validatePatternProperty(currentSubSchema, pk, value[pk], result, context)
|
||||
|
||||
if found {
|
||||
|
||||
if pp_has && !pp_match {
|
||||
validationResult := additionalPropertiesSchema.subValidateWithContext(value[pk], context)
|
||||
result.mergeErrors(validationResult)
|
||||
}
|
||||
|
||||
} else {
|
||||
|
||||
if !pp_has || !pp_match {
|
||||
validationResult := additionalPropertiesSchema.subValidateWithContext(value[pk], context)
|
||||
result.mergeErrors(validationResult)
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
}
|
||||
}
|
||||
} else {
|
||||
|
||||
for pk := range value {
|
||||
|
||||
pp_has, pp_match := v.validatePatternProperty(currentSubSchema, pk, value[pk], result, context)
|
||||
|
||||
if pp_has && !pp_match {
|
||||
|
||||
result.addError(
|
||||
new(InvalidPropertyPatternError),
|
||||
context,
|
||||
value,
|
||||
ErrorDetails{
|
||||
"property": pk,
|
||||
"pattern": currentSubSchema.PatternPropertiesString(),
|
||||
},
|
||||
)
|
||||
}
|
||||
|
||||
}
|
||||
}
|
||||
|
||||
result.incrementScore()
|
||||
}
|
||||
|
||||
func (v *subSchema) validatePatternProperty(currentSubSchema *subSchema, key string, value interface{}, result *Result, context *jsonContext) (has bool, matched bool) {
|
||||
|
||||
if internalLogEnabled {
|
||||
internalLog("validatePatternProperty %s", context.String())
|
||||
internalLog(" %s %v", key, value)
|
||||
}
|
||||
|
||||
has = false
|
||||
|
||||
validatedkey := false
|
||||
|
||||
for pk, pv := range currentSubSchema.patternProperties {
|
||||
if matches, _ := regexp.MatchString(pk, key); matches {
|
||||
has = true
|
||||
subContext := newJsonContext(key, context)
|
||||
validationResult := pv.subValidateWithContext(value, subContext)
|
||||
result.mergeErrors(validationResult)
|
||||
if validationResult.Valid() {
|
||||
validatedkey = true
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
if !validatedkey {
|
||||
return has, false
|
||||
}
|
||||
|
||||
result.incrementScore()
|
||||
|
||||
return has, true
|
||||
}
|
||||
|
||||
func (v *subSchema) validateString(currentSubSchema *subSchema, value interface{}, result *Result, context *jsonContext) {
|
||||
|
||||
// Ignore JSON numbers
|
||||
if isJsonNumber(value) {
|
||||
return
|
||||
}
|
||||
|
||||
// Ignore non strings
|
||||
if !isKind(value, reflect.String) {
|
||||
return
|
||||
}
|
||||
|
||||
if internalLogEnabled {
|
||||
internalLog("validateString %s", context.String())
|
||||
internalLog(" %v", value)
|
||||
}
|
||||
|
||||
stringValue := value.(string)
|
||||
|
||||
// minLength & maxLength:
|
||||
if currentSubSchema.minLength != nil {
|
||||
if utf8.RuneCount([]byte(stringValue)) < int(*currentSubSchema.minLength) {
|
||||
result.addError(
|
||||
new(StringLengthGTEError),
|
||||
context,
|
||||
value,
|
||||
ErrorDetails{"min": *currentSubSchema.minLength},
|
||||
)
|
||||
}
|
||||
}
|
||||
if currentSubSchema.maxLength != nil {
|
||||
if utf8.RuneCount([]byte(stringValue)) > int(*currentSubSchema.maxLength) {
|
||||
result.addError(
|
||||
new(StringLengthLTEError),
|
||||
context,
|
||||
value,
|
||||
ErrorDetails{"max": *currentSubSchema.maxLength},
|
||||
)
|
||||
}
|
||||
}
|
||||
|
||||
// pattern:
|
||||
if currentSubSchema.pattern != nil {
|
||||
if !currentSubSchema.pattern.MatchString(stringValue) {
|
||||
result.addError(
|
||||
new(DoesNotMatchPatternError),
|
||||
context,
|
||||
value,
|
||||
ErrorDetails{"pattern": currentSubSchema.pattern},
|
||||
)
|
||||
|
||||
}
|
||||
}
|
||||
|
||||
// format
|
||||
if currentSubSchema.format != "" {
|
||||
if !FormatCheckers.IsFormat(currentSubSchema.format, stringValue) {
|
||||
result.addError(
|
||||
new(DoesNotMatchFormatError),
|
||||
context,
|
||||
value,
|
||||
ErrorDetails{"format": currentSubSchema.format},
|
||||
)
|
||||
}
|
||||
}
|
||||
|
||||
result.incrementScore()
|
||||
}
|
||||
|
||||
func (v *subSchema) validateNumber(currentSubSchema *subSchema, value interface{}, result *Result, context *jsonContext) {
|
||||
|
||||
// Ignore non numbers
|
||||
if !isJsonNumber(value) {
|
||||
return
|
||||
}
|
||||
|
||||
if internalLogEnabled {
|
||||
internalLog("validateNumber %s", context.String())
|
||||
internalLog(" %v", value)
|
||||
}
|
||||
|
||||
number := value.(json.Number)
|
||||
float64Value, _ := number.Float64()
|
||||
|
||||
// multipleOf:
|
||||
if currentSubSchema.multipleOf != nil {
|
||||
|
||||
if !isFloat64AnInteger(float64Value / *currentSubSchema.multipleOf) {
|
||||
result.addError(
|
||||
new(MultipleOfError),
|
||||
context,
|
||||
resultErrorFormatJsonNumber(number),
|
||||
ErrorDetails{"multiple": *currentSubSchema.multipleOf},
|
||||
)
|
||||
}
|
||||
}
|
||||
|
||||
//maximum & exclusiveMaximum:
|
||||
if currentSubSchema.maximum != nil {
|
||||
if currentSubSchema.exclusiveMaximum {
|
||||
if float64Value >= *currentSubSchema.maximum {
|
||||
result.addError(
|
||||
new(NumberLTError),
|
||||
context,
|
||||
resultErrorFormatJsonNumber(number),
|
||||
ErrorDetails{
|
||||
"max": resultErrorFormatNumber(*currentSubSchema.maximum),
|
||||
},
|
||||
)
|
||||
}
|
||||
} else {
|
||||
if float64Value > *currentSubSchema.maximum {
|
||||
result.addError(
|
||||
new(NumberLTEError),
|
||||
context,
|
||||
resultErrorFormatJsonNumber(number),
|
||||
ErrorDetails{
|
||||
"max": resultErrorFormatNumber(*currentSubSchema.maximum),
|
||||
},
|
||||
)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
//minimum & exclusiveMinimum:
|
||||
if currentSubSchema.minimum != nil {
|
||||
if currentSubSchema.exclusiveMinimum {
|
||||
if float64Value <= *currentSubSchema.minimum {
|
||||
result.addError(
|
||||
new(NumberGTError),
|
||||
context,
|
||||
resultErrorFormatJsonNumber(number),
|
||||
ErrorDetails{
|
||||
"min": resultErrorFormatNumber(*currentSubSchema.minimum),
|
||||
},
|
||||
)
|
||||
}
|
||||
} else {
|
||||
if float64Value < *currentSubSchema.minimum {
|
||||
result.addError(
|
||||
new(NumberGTEError),
|
||||
context,
|
||||
resultErrorFormatJsonNumber(number),
|
||||
ErrorDetails{
|
||||
"min": resultErrorFormatNumber(*currentSubSchema.minimum),
|
||||
},
|
||||
)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
result.incrementScore()
|
||||
}
|
202
vendor/go4.org/LICENSE
generated
vendored
Normal file
202
vendor/go4.org/LICENSE
generated
vendored
Normal file
@@ -0,0 +1,202 @@
|
||||
Apache License
|
||||
Version 2.0, January 2004
|
||||
http://www.apache.org/licenses/
|
||||
|
||||
TERMS AND CONDITIONS FOR USE, REPRODUCTION, AND DISTRIBUTION
|
||||
|
||||
1. Definitions.
|
||||
|
||||
"License" shall mean the terms and conditions for use, reproduction,
|
||||
and distribution as defined by Sections 1 through 9 of this document.
|
||||
|
||||
"Licensor" shall mean the copyright owner or entity authorized by
|
||||
the copyright owner that is granting the License.
|
||||
|
||||
"Legal Entity" shall mean the union of the acting entity and all
|
||||
other entities that control, are controlled by, or are under common
|
||||
control with that entity. For the purposes of this definition,
|
||||
"control" means (i) the power, direct or indirect, to cause the
|
||||
direction or management of such entity, whether by contract or
|
||||
otherwise, or (ii) ownership of fifty percent (50%) or more of the
|
||||
outstanding shares, or (iii) beneficial ownership of such entity.
|
||||
|
||||
"You" (or "Your") shall mean an individual or Legal Entity
|
||||
exercising permissions granted by this License.
|
||||
|
||||
"Source" form shall mean the preferred form for making modifications,
|
||||
including but not limited to software source code, documentation
|
||||
source, and configuration files.
|
||||
|
||||
"Object" form shall mean any form resulting from mechanical
|
||||
transformation or translation of a Source form, including but
|
||||
not limited to compiled object code, generated documentation,
|
||||
and conversions to other media types.
|
||||
|
||||
"Work" shall mean the work of authorship, whether in Source or
|
||||
Object form, made available under the License, as indicated by a
|
||||
copyright notice that is included in or attached to the work
|
||||
(an example is provided in the Appendix below).
|
||||
|
||||
"Derivative Works" shall mean any work, whether in Source or Object
|
||||
form, that is based on (or derived from) the Work and for which the
|
||||
editorial revisions, annotations, elaborations, or other modifications
|
||||
represent, as a whole, an original work of authorship. For the purposes
|
||||
of this License, Derivative Works shall not include works that remain
|
||||
separable from, or merely link (or bind by name) to the interfaces of,
|
||||
the Work and Derivative Works thereof.
|
||||
|
||||
"Contribution" shall mean any work of authorship, including
|
||||
the original version of the Work and any modifications or additions
|
||||
to that Work or Derivative Works thereof, that is intentionally
|
||||
submitted to Licensor for inclusion in the Work by the copyright owner
|
||||
or by an individual or Legal Entity authorized to submit on behalf of
|
||||
the copyright owner. For the purposes of this definition, "submitted"
|
||||
means any form of electronic, verbal, or written communication sent
|
||||
to the Licensor or its representatives, including but not limited to
|
||||
communication on electronic mailing lists, source code control systems,
|
||||
and issue tracking systems that are managed by, or on behalf of, the
|
||||
Licensor for the purpose of discussing and improving the Work, but
|
||||
excluding communication that is conspicuously marked or otherwise
|
||||
designated in writing by the copyright owner as "Not a Contribution."
|
||||
|
||||
"Contributor" shall mean Licensor and any individual or Legal Entity
|
||||
on behalf of whom a Contribution has been received by Licensor and
|
||||
subsequently incorporated within the Work.
|
||||
|
||||
2. Grant of Copyright License. Subject to the terms and conditions of
|
||||
this License, each Contributor hereby grants to You a perpetual,
|
||||
worldwide, non-exclusive, no-charge, royalty-free, irrevocable
|
||||
copyright license to reproduce, prepare Derivative Works of,
|
||||
publicly display, publicly perform, sublicense, and distribute the
|
||||
Work and such Derivative Works in Source or Object form.
|
||||
|
||||
3. Grant of Patent License. Subject to the terms and conditions of
|
||||
this License, each Contributor hereby grants to You a perpetual,
|
||||
worldwide, non-exclusive, no-charge, royalty-free, irrevocable
|
||||
(except as stated in this section) patent license to make, have made,
|
||||
use, offer to sell, sell, import, and otherwise transfer the Work,
|
||||
where such license applies only to those patent claims licensable
|
||||
by such Contributor that are necessarily infringed by their
|
||||
Contribution(s) alone or by combination of their Contribution(s)
|
||||
with the Work to which such Contribution(s) was submitted. If You
|
||||
institute patent litigation against any entity (including a
|
||||
cross-claim or counterclaim in a lawsuit) alleging that the Work
|
||||
or a Contribution incorporated within the Work constitutes direct
|
||||
or contributory patent infringement, then any patent licenses
|
||||
granted to You under this License for that Work shall terminate
|
||||
as of the date such litigation is filed.
|
||||
|
||||
4. Redistribution. You may reproduce and distribute copies of the
|
||||
Work or Derivative Works thereof in any medium, with or without
|
||||
modifications, and in Source or Object form, provided that You
|
||||
meet the following conditions:
|
||||
|
||||
(a) You must give any other recipients of the Work or
|
||||
Derivative Works a copy of this License; and
|
||||
|
||||
(b) You must cause any modified files to carry prominent notices
|
||||
stating that You changed the files; and
|
||||
|
||||
(c) You must retain, in the Source form of any Derivative Works
|
||||
that You distribute, all copyright, patent, trademark, and
|
||||
attribution notices from the Source form of the Work,
|
||||
excluding those notices that do not pertain to any part of
|
||||
the Derivative Works; and
|
||||
|
||||
(d) If the Work includes a "NOTICE" text file as part of its
|
||||
distribution, then any Derivative Works that You distribute must
|
||||
include a readable copy of the attribution notices contained
|
||||
within such NOTICE file, excluding those notices that do not
|
||||
pertain to any part of the Derivative Works, in at least one
|
||||
of the following places: within a NOTICE text file distributed
|
||||
as part of the Derivative Works; within the Source form or
|
||||
documentation, if provided along with the Derivative Works; or,
|
||||
within a display generated by the Derivative Works, if and
|
||||
wherever such third-party notices normally appear. The contents
|
||||
of the NOTICE file are for informational purposes only and
|
||||
do not modify the License. You may add Your own attribution
|
||||
notices within Derivative Works that You distribute, alongside
|
||||
or as an addendum to the NOTICE text from the Work, provided
|
||||
that such additional attribution notices cannot be construed
|
||||
as modifying the License.
|
||||
|
||||
You may add Your own copyright statement to Your modifications and
|
||||
may provide additional or different license terms and conditions
|
||||
for use, reproduction, or distribution of Your modifications, or
|
||||
for any such Derivative Works as a whole, provided Your use,
|
||||
reproduction, and distribution of the Work otherwise complies with
|
||||
the conditions stated in this License.
|
||||
|
||||
5. Submission of Contributions. Unless You explicitly state otherwise,
|
||||
any Contribution intentionally submitted for inclusion in the Work
|
||||
by You to the Licensor shall be under the terms and conditions of
|
||||
this License, without any additional terms or conditions.
|
||||
Notwithstanding the above, nothing herein shall supersede or modify
|
||||
the terms of any separate license agreement you may have executed
|
||||
with Licensor regarding such Contributions.
|
||||
|
||||
6. Trademarks. This License does not grant permission to use the trade
|
||||
names, trademarks, service marks, or product names of the Licensor,
|
||||
except as required for reasonable and customary use in describing the
|
||||
origin of the Work and reproducing the content of the NOTICE file.
|
||||
|
||||
7. Disclaimer of Warranty. Unless required by applicable law or
|
||||
agreed to in writing, Licensor provides the Work (and each
|
||||
Contributor provides its Contributions) on an "AS IS" BASIS,
|
||||
WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or
|
||||
implied, including, without limitation, any warranties or conditions
|
||||
of TITLE, NON-INFRINGEMENT, MERCHANTABILITY, or FITNESS FOR A
|
||||
PARTICULAR PURPOSE. You are solely responsible for determining the
|
||||
appropriateness of using or redistributing the Work and assume any
|
||||
risks associated with Your exercise of permissions under this License.
|
||||
|
||||
8. Limitation of Liability. In no event and under no legal theory,
|
||||
whether in tort (including negligence), contract, or otherwise,
|
||||
unless required by applicable law (such as deliberate and grossly
|
||||
negligent acts) or agreed to in writing, shall any Contributor be
|
||||
liable to You for damages, including any direct, indirect, special,
|
||||
incidental, or consequential damages of any character arising as a
|
||||
result of this License or out of the use or inability to use the
|
||||
Work (including but not limited to damages for loss of goodwill,
|
||||
work stoppage, computer failure or malfunction, or any and all
|
||||
other commercial damages or losses), even if such Contributor
|
||||
has been advised of the possibility of such damages.
|
||||
|
||||
9. Accepting Warranty or Additional Liability. While redistributing
|
||||
the Work or Derivative Works thereof, You may choose to offer,
|
||||
and charge a fee for, acceptance of support, warranty, indemnity,
|
||||
or other liability obligations and/or rights consistent with this
|
||||
License. However, in accepting such obligations, You may act only
|
||||
on Your own behalf and on Your sole responsibility, not on behalf
|
||||
of any other Contributor, and only if You agree to indemnify,
|
||||
defend, and hold each Contributor harmless for any liability
|
||||
incurred by, or claims asserted against, such Contributor by reason
|
||||
of your accepting any such warranty or additional liability.
|
||||
|
||||
END OF TERMS AND CONDITIONS
|
||||
|
||||
APPENDIX: How to apply the Apache License to your work.
|
||||
|
||||
To apply the Apache License to your work, attach the following
|
||||
boilerplate notice, with the fields enclosed by brackets "{}"
|
||||
replaced with your own identifying information. (Don't include
|
||||
the brackets!) The text should be enclosed in the appropriate
|
||||
comment syntax for the file format. We also recommend that a
|
||||
file or class name and description of purpose be included on the
|
||||
same "printed page" as the copyright notice for easier
|
||||
identification within third-party archives.
|
||||
|
||||
Copyright {yyyy} {name of copyright owner}
|
||||
|
||||
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.
|
||||
|
58
vendor/go4.org/errorutil/highlight.go
generated
vendored
Normal file
58
vendor/go4.org/errorutil/highlight.go
generated
vendored
Normal file
@@ -0,0 +1,58 @@
|
||||
/*
|
||||
Copyright 2011 Google Inc.
|
||||
|
||||
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 errorutil helps make better error messages.
|
||||
package errorutil // import "go4.org/errorutil"
|
||||
|
||||
import (
|
||||
"bufio"
|
||||
"bytes"
|
||||
"fmt"
|
||||
"io"
|
||||
"strings"
|
||||
)
|
||||
|
||||
// HighlightBytePosition takes a reader and the location in bytes of a parse
|
||||
// error (for instance, from json.SyntaxError.Offset) and returns the line, column,
|
||||
// and pretty-printed context around the error with an arrow indicating the exact
|
||||
// position of the syntax error.
|
||||
func HighlightBytePosition(f io.Reader, pos int64) (line, col int, highlight string) {
|
||||
line = 1
|
||||
br := bufio.NewReader(f)
|
||||
lastLine := ""
|
||||
thisLine := new(bytes.Buffer)
|
||||
for n := int64(0); n < pos; n++ {
|
||||
b, err := br.ReadByte()
|
||||
if err != nil {
|
||||
break
|
||||
}
|
||||
if b == '\n' {
|
||||
lastLine = thisLine.String()
|
||||
thisLine.Reset()
|
||||
line++
|
||||
col = 1
|
||||
} else {
|
||||
col++
|
||||
thisLine.WriteByte(b)
|
||||
}
|
||||
}
|
||||
if line > 1 {
|
||||
highlight += fmt.Sprintf("%5d: %s\n", line-1, lastLine)
|
||||
}
|
||||
highlight += fmt.Sprintf("%5d: %s\n", line, thisLine.String())
|
||||
highlight += fmt.Sprintf("%s^\n", strings.Repeat(" ", col+5))
|
||||
return
|
||||
}
|
Reference in New Issue
Block a user