Update Go and Goreleaser to 1.20, update dependencies (#231)

This commit is contained in:
Yann Hamon 2023-09-04 00:11:25 +02:00 committed by GitHub
parent 13a78ebad8
commit 2e50b79b16
No known key found for this signature in database
GPG key ID: 4AEE18F83AFDEB23
15 changed files with 217 additions and 69 deletions

View file

@ -1,4 +1,4 @@
FROM alpine:3.14 as certs
FROM alpine:3.18.3 as certs
RUN apk add ca-certificates
FROM scratch AS kubeconform

View file

@ -1,4 +1,4 @@
FROM alpine:3.14 as certs
FROM alpine:3.18.3
LABEL org.opencontainers.image.authors="yann@mandragor.org" \
org.opencontainers.image.source="https://github.com/yannh/kubeconform/" \
org.opencontainers.image.description="A Kubernetes manifests validation tool" \

View file

@ -8,6 +8,7 @@ local-test:
go test -race ./...
local-build:
git config --global --add safe.directory $$PWD
go build -o bin/ ./...
local-build-static:
@ -15,13 +16,13 @@ local-build-static:
# These only used for development. Release artifacts and docker images are produced by goreleaser.
docker-test:
docker run -t -v $$PWD:/go/src/github.com/yannh/kubeconform -w /go/src/github.com/yannh/kubeconform golang:1.17 make local-test
docker run -t -v $$PWD:/go/src/github.com/yannh/kubeconform -w /go/src/github.com/yannh/kubeconform golang:1.20 make local-test
docker-build:
docker run -t -v $$PWD:/go/src/github.com/yannh/kubeconform -w /go/src/github.com/yannh/kubeconform golang:1.17 make local-build
docker run -t -v $$PWD:/go/src/github.com/yannh/kubeconform -w /go/src/github.com/yannh/kubeconform golang:1.20 make local-build
docker-build-static:
docker run -t -v $$PWD:/go/src/github.com/yannh/kubeconform -w /go/src/github.com/yannh/kubeconform golang:1.17 make local-build-static
docker run -t -v $$PWD:/go/src/github.com/yannh/kubeconform -w /go/src/github.com/yannh/kubeconform golang:1.20 make local-build-static
build-bats:
docker build -t bats -f Dockerfile.bats .
@ -31,11 +32,11 @@ docker-acceptance: build-bats
docker run --network none -t bats -p acceptance-nonetwork.bats
goreleaser-build-static:
docker run -t -e GOOS=linux -e GOARCH=amd64 -v $$PWD:/go/src/github.com/yannh/kubeconform -w /go/src/github.com/yannh/kubeconform goreleaser/goreleaser:v1.11.5 build --single-target --skip-post-hooks --rm-dist --snapshot
docker run -t -e GOOS=linux -e GOARCH=amd64 -v $$PWD:/go/src/github.com/yannh/kubeconform -w /go/src/github.com/yannh/kubeconform goreleaser/goreleaser:v1.20.0 build --single-target --skip-post-hooks --rm-dist --snapshot
cp dist/kubeconform_linux_amd64_v1/kubeconform bin/
release:
docker run -e GITHUB_TOKEN -e GIT_OWNER -t -v /var/run/docker.sock:/var/run/docker.sock -v $$PWD:/go/src/github.com/yannh/kubeconform -w /go/src/github.com/yannh/kubeconform goreleaser/goreleaser:v1.11.5 release --rm-dist
docker run -e GITHUB_TOKEN -e GIT_OWNER -t -v /var/run/docker.sock:/var/run/docker.sock -v $$PWD:/go/src/github.com/yannh/kubeconform -w /go/src/github.com/yannh/kubeconform goreleaser/goreleaser:v1.20.0 release --rm-dist
update-deps:
go get -u ./...

6
go.mod
View file

@ -1,10 +1,10 @@
module github.com/yannh/kubeconform
go 1.17
go 1.20
require (
github.com/santhosh-tekuri/jsonschema/v5 v5.1.1
sigs.k8s.io/yaml v1.2.0
github.com/santhosh-tekuri/jsonschema/v5 v5.3.1
sigs.k8s.io/yaml v1.3.0
)
require gopkg.in/yaml.v2 v2.4.0 // indirect

9
go.sum
View file

@ -1,11 +1,10 @@
github.com/davecgh/go-spew v1.1.1 h1:vj9j/u1bqnvCEfJOwUhtlOARqs3+rkHYY13jYWTU97c=
github.com/davecgh/go-spew v1.1.1/go.mod h1:J7Y8YcW2NihsgmVo/mv3lAwl/skON4iLHjSsI+c5H38=
github.com/santhosh-tekuri/jsonschema/v5 v5.1.1 h1:lEOLY2vyGIqKWUI9nzsOJRV3mb3WC9dXYORsLEUcoeY=
github.com/santhosh-tekuri/jsonschema/v5 v5.1.1/go.mod h1:FKdcjfQW6rpZSnxxUvEA5H/cDPdvJ/SZJQLWWXWGrZ0=
github.com/santhosh-tekuri/jsonschema/v5 v5.3.1 h1:lZUw3E0/J3roVtGQ+SCrUrg3ON6NgVqpn3+iol9aGu4=
github.com/santhosh-tekuri/jsonschema/v5 v5.3.1/go.mod h1:uToXkOrWAZ6/Oc07xWQrPOhJotwFIyu2bBVN41fcDUY=
gopkg.in/check.v1 v0.0.0-20161208181325-20d25e280405 h1:yhCVgyC4o1eVCa2tZl7eS0r+SDo693bJlVdllGtEeKM=
gopkg.in/check.v1 v0.0.0-20161208181325-20d25e280405/go.mod h1:Co6ibVJAznAaIkqp8huTwlJQCZ016jof/cbN4VW5Yz0=
gopkg.in/yaml.v2 v2.2.8/go.mod h1:hI93XBmqTisBFMUTm0b8Fm+jr3Dg1NNxqwp+5A1VGuI=
gopkg.in/yaml.v2 v2.4.0 h1:D8xgwECY7CYvx+Y2n4sBz93Jn9JRvxdiyyo8CTfuKaY=
gopkg.in/yaml.v2 v2.4.0/go.mod h1:RDklbk79AGWmwhnvt/jBztapEOGDOx6ZbXqjP6csGnQ=
sigs.k8s.io/yaml v1.2.0 h1:kr/MCeFWJWTwyaHoR9c8EjH9OumOmoF9YGiZd7lFm/Q=
sigs.k8s.io/yaml v1.2.0/go.mod h1:yfXDCHCao9+ENCvLSE62v9VSji2MKu5jeNfTrofGhJc=
sigs.k8s.io/yaml v1.3.0 h1:a2VclLzOGrwOHDiV8EfBGhvjHvP46CtW5j6POvhYGGo=
sigs.k8s.io/yaml v1.3.0/go.mod h1:GeOyir5tyXNByN85N/dRIT9es5UQNerPYEKK56eTBm8=

View file

@ -1,4 +1,4 @@
.vscode
.idea
*.swp
jv
cmd/jv/jv

View file

@ -0,0 +1,3 @@
[submodule "testdata/JSON-Schema-Test-Suite"]
path = testdata/JSON-Schema-Test-Suite
url = https://github.com/json-schema-org/JSON-Schema-Test-Suite.git

View file

@ -1,10 +1,10 @@
# jsonschema v5.1.1
# jsonschema v5.3.1
[![License](https://img.shields.io/badge/License-Apache%202.0-blue.svg)](https://opensource.org/licenses/Apache-2.0)
[![GoDoc](https://godoc.org/github.com/santhosh-tekuri/jsonschema?status.svg)](https://pkg.go.dev/github.com/santhosh-tekuri/jsonschema/v5)
[![Go Report Card](https://goreportcard.com/badge/github.com/santhosh-tekuri/jsonschema/v5)](https://goreportcard.com/report/github.com/santhosh-tekuri/jsonschema/v5)
[![Build Status](https://github.com/santhosh-tekuri/jsonschema/actions/workflows/go.yaml/badge.svg?branch=master)](https://github.com/santhosh-tekuri/jsonschema/actions/workflows/go.yaml)
[![codecov.io](https://codecov.io/github/santhosh-tekuri/jsonschema/coverage.svg?branch=master)](https://codecov.io/github/santhosh-tekuri/jsonschema?branch=master)
[![codecov](https://codecov.io/gh/santhosh-tekuri/jsonschema/branch/master/graph/badge.svg?token=JMVj1pFT2l)](https://codecov.io/gh/santhosh-tekuri/jsonschema)
Package jsonschema provides json-schema compilation and validation.
@ -183,10 +183,10 @@ Prints:
## CLI
to install `go install github.com/santhosh-tekuri/jsonschema/v5/cmd/jv@latest`
to install `go install github.com/santhosh-tekuri/jsonschema/cmd/jv@latest`
```bash
jv [-draft INT] [-output FORMAT] [-assertformat] [-assertcontent] <json-schema> [<json-doc>]...
jv [-draft INT] [-output FORMAT] [-assertformat] [-assertcontent] <json-schema> [<json-or-yaml-doc>]...
-assertcontent
enable content assertions with draft >= 2019
-assertformat
@ -197,19 +197,24 @@ jv [-draft INT] [-output FORMAT] [-assertformat] [-assertcontent] <json-schema>
output format. valid values flag, basic, detailed
```
if no `<json-doc>` arguments are passed, it simply validates the `<json-schema>`.
if no `<json-or-yaml-doc>` arguments are passed, it simply validates the `<json-schema>`.
if `$schema` attribute is missing in schema, it uses latest version. this can be overridden by passing `-draft` flag
exit-code is 1, if there are any validation errors
`jv` can also validate yaml files. It also accepts schema from yaml files.
## Validating YAML Documents
since yaml supports non-string keys, such yaml documents are rendered as invalid json documents.
yaml parser returns `map[interface{}]interface{}` for object, whereas json parser returns `map[string]interface{}`.
this package accepts only `map[string]interface{}`, so we need to manually convert them to `map[string]interface{}`
most yaml parser use `map[interface{}]interface{}` for object,
whereas json parser uses `map[string]interface{}`.
so we need to manually convert them to `map[string]interface{}`.
below code shows such conversion by `toStringKeys` function.
https://play.golang.org/p/Hhax3MrtD8r
the above example shows how to validate yaml document with jsonschema.
the conversion explained above is implemented by `toStringKeys` function
NOTE: if you are using `gopkg.in/yaml.v3`, then you do not need such conversion. since this library
returns `map[string]interface{}` if all keys are strings.

View file

@ -30,9 +30,21 @@ type Compiler struct {
// If nil, package global LoadURL is used.
LoadURL func(s string) (io.ReadCloser, error)
// Formats can be registered by adding to this map. Key is format name,
// value is function that knows how to validate that format.
Formats map[string]func(interface{}) bool
// AssertFormat for specifications >= draft2019-09.
AssertFormat bool
// Decoders can be registered by adding to this map. Key is encoding name,
// value is function that knows how to decode string in that format.
Decoders map[string]func(string) ([]byte, error)
// MediaTypes can be registered by adding to this map. Key is mediaType name,
// value is function that knows how to validate that mediaType.
MediaTypes map[string]func([]byte) error
// AssertContent for specifications >= draft2019-09.
AssertContent bool
}
@ -74,7 +86,14 @@ func MustCompileString(url, schema string) *Schema {
// if '$schema' attribute is missing, it is treated as draft7. to change this
// behavior change Compiler.Draft value
func NewCompiler() *Compiler {
return &Compiler{Draft: latest, resources: make(map[string]*resource), extensions: make(map[string]extension)}
return &Compiler{
Draft: latest,
resources: make(map[string]*resource),
Formats: make(map[string]func(interface{}) bool),
Decoders: make(map[string]func(string) ([]byte, error)),
MediaTypes: make(map[string]func([]byte) error),
extensions: make(map[string]extension),
}
}
// AddResource adds in-memory resource to the compiler.
@ -218,7 +237,7 @@ func (c *Compiler) compileRef(r *resource, stack []schemaRef, refPtr string, res
// ensure root resource is always compiled first.
// this is required to get schema.meta from root resource
if r.schema == nil {
r.schema = newSchema(r.url, r.floc, r.doc)
r.schema = newSchema(r.url, r.floc, r.draft, r.doc)
if _, err := c.compile(r, nil, schemaRef{"#", r.schema, false}, r); err != nil {
return nil, err
}
@ -239,7 +258,7 @@ func (c *Compiler) compileRef(r *resource, stack []schemaRef, refPtr string, res
return sr.schema, nil
}
sr.schema = newSchema(r.url, sr.floc, sr.doc)
sr.schema = newSchema(r.url, sr.floc, r.draft, sr.doc)
return c.compile(r, stack, schemaRef{refPtr, sr.schema, false}, sr)
}
@ -316,7 +335,10 @@ func (c *Compiler) compileMap(r *resource, stack []schemaRef, sref schemaRef, re
if r.draft.version >= 2019 {
if r == res { // root schema
if vocab, ok := m["$vocabulary"]; ok {
for url := range vocab.(map[string]interface{}) {
for url, reqd := range vocab.(map[string]interface{}) {
if reqd, ok := reqd.(bool); ok && !reqd {
continue
}
if !r.draft.isVocab(url) {
return fmt.Errorf("jsonschema: unsupported vocab %q in %s", url, res)
}
@ -340,6 +362,13 @@ func (c *Compiler) compileMap(r *resource, stack []schemaRef, sref schemaRef, re
if err != nil {
return err
}
if dref, ok := dref.(string); ok {
_, frag := split(dref)
if frag != "#" && !strings.HasPrefix(frag, "#/") {
// frag is anchor
s.dynamicRefAnchor = frag[1:]
}
}
}
}
@ -635,7 +664,11 @@ func (c *Compiler) compileMap(r *resource, stack []schemaRef, sref schemaRef, re
if format, ok := m["format"]; ok {
s.Format = format.(string)
if r.draft.version < 2019 || c.AssertFormat || r.schema.meta.hasVocab("format-assertion") {
s.format, _ = Formats[s.Format]
if format, ok := c.Formats[s.Format]; ok {
s.format = format
} else {
s.format, _ = Formats[s.Format]
}
}
}
@ -658,11 +691,19 @@ func (c *Compiler) compileMap(r *resource, stack []schemaRef, sref schemaRef, re
if r.draft.version >= 7 {
if encoding, ok := m["contentEncoding"]; ok {
s.ContentEncoding = encoding.(string)
s.decoder, _ = Decoders[s.ContentEncoding]
if decoder, ok := c.Decoders[s.ContentEncoding]; ok {
s.decoder = decoder
} else {
s.decoder, _ = Decoders[s.ContentEncoding]
}
}
if mediaType, ok := m["contentMediaType"]; ok {
s.ContentMediaType = mediaType.(string)
s.mediaType, _ = MediaTypes[s.ContentMediaType]
if mediaType, ok := c.MediaTypes[s.ContentMediaType]; ok {
s.mediaType = mediaType
} else {
s.mediaType, _ = MediaTypes[s.ContentMediaType]
}
if s.ContentSchema, err = loadSchema("contentSchema", stack); err != nil {
return err
}

View file

@ -1,6 +1,7 @@
package jsonschema
import (
"fmt"
"strconv"
"strings"
)
@ -16,6 +17,26 @@ type Draft struct {
subschemas map[string]position
}
func (d *Draft) URL() string {
switch d.version {
case 2020:
return "https://json-schema.org/draft/2020-12/schema"
case 2019:
return "https://json-schema.org/draft/2019-09/schema"
case 7:
return "https://json-schema.org/draft-07/schema"
case 6:
return "https://json-schema.org/draft-06/schema"
case 4:
return "https://json-schema.org/draft-04/schema"
}
return ""
}
func (d *Draft) String() string {
return fmt.Sprintf("Draft%d", d.version)
}
func (d *Draft) loadMeta(url, schema string) {
c := NewCompiler()
c.AssertFormat = true
@ -264,6 +285,7 @@ func init() {
subschemas["dependentSchemas"] = prop
subschemas["unevaluatedProperties"] = self
subschemas["unevaluatedItems"] = self
subschemas["contentSchema"] = self
Draft2019.subschemas = clone(subschemas)
subschemas["prefixItems"] = item

View file

@ -4,9 +4,11 @@ import (
"bytes"
"encoding/json"
"fmt"
"hash/maphash"
"math/big"
"net/url"
"regexp"
"sort"
"strconv"
"strings"
"unicode/utf8"
@ -16,30 +18,32 @@ import (
type Schema struct {
Location string // absolute location
Draft *Draft // draft used by schema.
meta *Schema
vocab []string
dynamicAnchors []*Schema
// type agnostic validations
Format string
format func(interface{}) bool
Always *bool // always pass/fail. used when booleans are used as schemas in draft-07.
Ref *Schema
RecursiveAnchor bool
RecursiveRef *Schema
DynamicAnchor string
DynamicRef *Schema
Types []string // allowed types.
Constant []interface{} // first element in slice is constant value. note: slice is used to capture nil constant.
Enum []interface{} // allowed values.
enumError string // error message for enum fail. captured here to avoid constructing error message every time.
Not *Schema
AllOf []*Schema
AnyOf []*Schema
OneOf []*Schema
If *Schema
Then *Schema // nil, when If is nil.
Else *Schema // nil, when If is nil.
Format string
format func(interface{}) bool
Always *bool // always pass/fail. used when booleans are used as schemas in draft-07.
Ref *Schema
RecursiveAnchor bool
RecursiveRef *Schema
DynamicAnchor string
DynamicRef *Schema
dynamicRefAnchor string
Types []string // allowed types.
Constant []interface{} // first element in slice is constant value. note: slice is used to capture nil constant.
Enum []interface{} // allowed values.
enumError string // error message for enum fail. captured here to avoid constructing error message every time.
Not *Schema
AllOf []*Schema
AnyOf []*Schema
OneOf []*Schema
If *Schema
Then *Schema // nil, when If is nil.
Else *Schema // nil, when If is nil.
// object validations
MinProperties int // -1 if not specified.
@ -104,10 +108,11 @@ func (s *Schema) String() string {
return s.Location
}
func newSchema(url, floc string, doc interface{}) *Schema {
func newSchema(url, floc string, draft *Draft, doc interface{}) *Schema {
// fill with default values
s := &Schema{
Location: url + floc,
Draft: draft,
MinProperties: -1,
MaxProperties: -1,
MinItems: -1,
@ -418,12 +423,39 @@ func (s *Schema) validate(scope []schemaRef, vscope int, spath string, v interfa
errors = append(errors, validationError("maxItems", "maximum %d items required, but found %d items", s.MaxItems, len(v)))
}
if s.UniqueItems {
for i := 1; i < len(v); i++ {
for j := 0; j < i; j++ {
if equals(v[i], v[j]) {
errors = append(errors, validationError("uniqueItems", "items at index %d and %d are equal", j, i))
if len(v) <= 20 {
outer1:
for i := 1; i < len(v); i++ {
for j := 0; j < i; j++ {
if equals(v[i], v[j]) {
errors = append(errors, validationError("uniqueItems", "items at index %d and %d are equal", j, i))
break outer1
}
}
}
} else {
m := make(map[uint64][]int)
var h maphash.Hash
outer2:
for i, item := range v {
h.Reset()
hash(item, &h)
k := h.Sum64()
if err != nil {
panic(err)
}
arr, ok := m[k]
if ok {
for _, j := range arr {
if equals(v[j], item) {
errors = append(errors, validationError("uniqueItems", "items at index %d and %d are equal", j, i))
break outer2
}
}
}
arr = append(arr, i)
m[k] = arr
}
}
}
@ -614,7 +646,7 @@ func (s *Schema) validate(scope []schemaRef, vscope int, spath string, v interfa
}
if s.DynamicRef != nil {
sch := s.DynamicRef
if sch.DynamicAnchor != "" {
if s.dynamicRefAnchor != "" && sch.DynamicAnchor == s.dynamicRefAnchor {
// dynamicRef based on scope
for i := len(scope) - 1; i >= 0; i-- {
sr := scope[i]
@ -708,13 +740,13 @@ func (s *Schema) validate(scope []schemaRef, vscope int, spath string, v interfa
}
}
// UnevaluatedProperties + UnevaluatedItems
// unevaluatedProperties + unevaluatedItems
switch v := v.(type) {
case map[string]interface{}:
if s.UnevaluatedProperties != nil {
for pname := range result.unevalProps {
if pvalue, ok := v[pname]; ok {
if err := validate(s.UnevaluatedProperties, "UnevaluatedProperties", pvalue, escape(pname)); err != nil {
if err := validate(s.UnevaluatedProperties, "unevaluatedProperties", pvalue, escape(pname)); err != nil {
errors = append(errors, err)
}
}
@ -724,7 +756,7 @@ func (s *Schema) validate(scope []schemaRef, vscope int, spath string, v interfa
case []interface{}:
if s.UnevaluatedItems != nil {
for i := range result.unevalItems {
if err := validate(s.UnevaluatedItems, "UnevaluatedItems", v[i], strconv.Itoa(i)); err != nil {
if err := validate(s.UnevaluatedItems, "unevaluatedItems", v[i], strconv.Itoa(i)); err != nil {
errors = append(errors, err)
}
}
@ -818,6 +850,48 @@ func equals(v1, v2 interface{}) bool {
}
}
func hash(v interface{}, h *maphash.Hash) {
switch v := v.(type) {
case nil:
h.WriteByte(0)
case bool:
h.WriteByte(1)
if v {
h.WriteByte(1)
} else {
h.WriteByte(0)
}
case json.Number, float32, float64, int, int8, int32, int64, uint, uint8, uint32, uint64:
h.WriteByte(2)
num, _ := new(big.Rat).SetString(fmt.Sprint(v))
h.Write(num.Num().Bytes())
h.Write(num.Denom().Bytes())
case string:
h.WriteByte(3)
h.WriteString(v)
case []interface{}:
h.WriteByte(4)
for _, item := range v {
hash(item, h)
}
case map[string]interface{}:
h.WriteByte(5)
props := make([]string, 0, len(v))
for prop := range v {
props = append(props, prop)
}
sort.Slice(props, func(i, j int) bool {
return props[i] < props[j]
})
for _, prop := range props {
hash(prop, h)
hash(v[prop], h)
}
default:
panic(InvalidJSONTypeError(fmt.Sprintf("%T", v)))
}
}
// escape converts given token to valid json-pointer token
func escape(token string) string {
token = strings.ReplaceAll(token, "~", "~0")

6
vendor/modules.txt vendored
View file

@ -1,10 +1,10 @@
# github.com/santhosh-tekuri/jsonschema/v5 v5.1.1
## explicit; go 1.15
# github.com/santhosh-tekuri/jsonschema/v5 v5.3.1
## explicit; go 1.19
github.com/santhosh-tekuri/jsonschema/v5
github.com/santhosh-tekuri/jsonschema/v5/httploader
# gopkg.in/yaml.v2 v2.4.0
## explicit; go 1.15
gopkg.in/yaml.v2
# sigs.k8s.io/yaml v1.2.0
# sigs.k8s.io/yaml v1.3.0
## explicit; go 1.12
sigs.k8s.io/yaml

4
vendor/sigs.k8s.io/yaml/.gitignore generated vendored
View file

@ -6,6 +6,10 @@
.project
.settings/**
# Idea files
.idea/**
.idea/
# Emacs save files
*~

View file

@ -1,8 +1,7 @@
language: go
dist: xenial
go:
- 1.12.x
- 1.13.x
arch: arm64
dist: focal
go: 1.15.x
script:
- diff -u <(echo -n) <(gofmt -d *.go)
- diff -u <(echo -n) <(golint $(go list -e ./...) | grep -v YAMLToJSON)

2
vendor/sigs.k8s.io/yaml/README.md generated vendored
View file

@ -107,8 +107,8 @@ func main() {
}
fmt.Println(string(y))
/* Output:
name: John
age: 30
name: John
*/
j2, err := yaml.YAMLToJSON(y)
if err != nil {