mirror of
https://github.com/netbirdio/gvisor.git
synced 2026-05-22 17:12:49 -07:00
Add YAML validation for configuration files.
For validation, the "on" key in existing YAML files is changed to a literal string. In the YAML spec, on is a keyword which encodes a boolean value, so without relying on a specific implementation the YAML files are technically not encoding an object that complies with the specification. PiperOrigin-RevId: 350172147
This commit is contained in:
committed by
gVisor bot
parent
622db84e4b
commit
2a5d3c248f
@@ -6,7 +6,7 @@
|
||||
# This workflow also generates the build badge that is referred to by
|
||||
# the main README.
|
||||
name: "Build"
|
||||
on:
|
||||
"on":
|
||||
push:
|
||||
branches:
|
||||
- master
|
||||
|
||||
@@ -3,7 +3,7 @@
|
||||
# workflow simply generates and pushes the branch, as long as appropriate
|
||||
# permissions are available.
|
||||
name: "Go"
|
||||
on:
|
||||
"on":
|
||||
push:
|
||||
branches:
|
||||
- master
|
||||
|
||||
@@ -1,7 +1,7 @@
|
||||
# This workflow revives issues that are still referenced in the code, and may
|
||||
# have been accidentally closed or marked stale.
|
||||
name: "Issue reviver"
|
||||
on:
|
||||
"on":
|
||||
schedule:
|
||||
- cron: '0 0 * * *'
|
||||
|
||||
|
||||
@@ -1,6 +1,6 @@
|
||||
# Labeler labels incoming pull requests.
|
||||
name: "Labeler"
|
||||
on:
|
||||
"on":
|
||||
- pull_request
|
||||
|
||||
jobs:
|
||||
|
||||
@@ -1,7 +1,7 @@
|
||||
# The stale workflow closes stale issues and pull requests, unless specific
|
||||
# tags have been applied in order to keep them open.
|
||||
name: "Close stale issues"
|
||||
on:
|
||||
"on":
|
||||
schedule:
|
||||
- cron: "0 0 * * *"
|
||||
|
||||
|
||||
@@ -1,5 +1,6 @@
|
||||
load("//tools:defs.bzl", "build_test", "gazelle", "go_path")
|
||||
load("//tools/nogo:defs.bzl", "nogo_config")
|
||||
load("//tools/yamltest:defs.bzl", "yaml_test")
|
||||
load("//website:defs.bzl", "doc")
|
||||
|
||||
package(licenses = ["notice"])
|
||||
@@ -50,6 +51,24 @@ doc(
|
||||
weight = "99",
|
||||
)
|
||||
|
||||
yaml_test(
|
||||
name = "nogo_config_test",
|
||||
srcs = glob(["nogo*.yaml"]),
|
||||
schema = "//tools/nogo:config-schema.json",
|
||||
)
|
||||
|
||||
yaml_test(
|
||||
name = "github_workflows_test",
|
||||
srcs = glob([".github/workflows/*.yml"]),
|
||||
schema = "@github_workflow_schema//file",
|
||||
)
|
||||
|
||||
yaml_test(
|
||||
name = "buildkite_pipelines_test",
|
||||
srcs = glob([".buildkite/*.yaml"]),
|
||||
schema = "@buildkite_pipeline_schema//file",
|
||||
)
|
||||
|
||||
# The sandbox filegroup is used for sandbox-internal dependencies.
|
||||
package_group(
|
||||
name = "sandbox",
|
||||
|
||||
@@ -1,4 +1,4 @@
|
||||
load("@bazel_tools//tools/build_defs/repo:http.bzl", "http_archive")
|
||||
load("@bazel_tools//tools/build_defs/repo:http.bzl", "http_archive", "http_file")
|
||||
load("@bazel_tools//tools/build_defs/repo:git.bzl", "git_repository")
|
||||
|
||||
# Bazel/starlark utilities.
|
||||
@@ -176,6 +176,19 @@ http_archive(
|
||||
],
|
||||
)
|
||||
|
||||
# Schemas for testing.
|
||||
http_file(
|
||||
name = "buildkite_pipeline_schema",
|
||||
sha256 = "3369c58038b4d55c08928affafb653716eb1e7b3cabb4a391aef979dd921f4e1",
|
||||
urls = ["https://raw.githubusercontent.com/buildkite/pipeline-schema/f7a0894074d194bcf19eec5411fec0528f7f4180/schema.json"],
|
||||
)
|
||||
|
||||
http_file(
|
||||
name = "github_workflow_schema",
|
||||
sha256 = "2c375bb43dbc8b32b1bed46c290d0b70a8fa2aca7a5484dfca1b6e9c38cf9e7a",
|
||||
urls = ["https://raw.githubusercontent.com/SchemaStore/schemastore/27612065234778feaac216ce14dd47846fe0a2dd/src/schemas/json/github-workflow.json"],
|
||||
)
|
||||
|
||||
# External Go repositories.
|
||||
#
|
||||
# Unfortunately, gazelle will automatically parse go modules in the
|
||||
@@ -1391,3 +1404,24 @@ go_repository(
|
||||
sum = "h1:+ySTxfHnfzZb9ys375PXNlLhkJPLKgHajBU0N62BDvE=",
|
||||
version = "v0.0.0-20190801114015-581e00157fb1",
|
||||
)
|
||||
|
||||
go_repository(
|
||||
name = "com_github_xeipuuv_gojsonpointer",
|
||||
importpath = "github.com/xeipuuv/gojsonpointer",
|
||||
sum = "h1:zGWFAtiMcyryUHoUjUJX0/lt1H2+i2Ka2n+D3DImSNo=",
|
||||
version = "v0.0.0-20190905194746-02993c407bfb",
|
||||
)
|
||||
|
||||
go_repository(
|
||||
name = "com_github_xeipuuv_gojsonreference",
|
||||
importpath = "github.com/xeipuuv/gojsonreference",
|
||||
sum = "h1:EzJWgHovont7NscjpAxXsDA8S8BMYve8Y5+7cuRE7R0=",
|
||||
version = "v0.0.0-20180127040603-bd5ef7bd5415",
|
||||
)
|
||||
|
||||
go_repository(
|
||||
name = "com_github_xeipuuv_gojsonschema",
|
||||
importpath = "github.com/xeipuuv/gojsonschema",
|
||||
sum = "h1:LhYJRs+L4fBtjZUfuSZIKGeVu0QRy8e5Xi7D17UxZ74=",
|
||||
version = "v1.2.0",
|
||||
)
|
||||
|
||||
@@ -3,6 +3,8 @@ load("//tools/nogo:defs.bzl", "nogo_objdump_tool", "nogo_stdlib", "nogo_target")
|
||||
|
||||
package(licenses = ["notice"])
|
||||
|
||||
exports_files(["config-schema.json"])
|
||||
|
||||
nogo_target(
|
||||
name = "target",
|
||||
goarch = select_goarch(),
|
||||
|
||||
@@ -0,0 +1,97 @@
|
||||
{
|
||||
"$schema": "http://json-schema.org/draft-07/schema",
|
||||
"definitions": {
|
||||
"group": {
|
||||
"type": "object",
|
||||
"properties": {
|
||||
"name": {
|
||||
"description": "The name of the group.",
|
||||
"type": "string"
|
||||
},
|
||||
"regex": {
|
||||
"description": "A regular expression for matching paths.",
|
||||
"type": "string"
|
||||
},
|
||||
"default": {
|
||||
"description": "Whether the group is enabled by default.",
|
||||
"type": "boolean"
|
||||
}
|
||||
},
|
||||
"required": [
|
||||
"name",
|
||||
"regex",
|
||||
"default"
|
||||
],
|
||||
"additionalProperties": false
|
||||
},
|
||||
"regexlist": {
|
||||
"description": "A list of regular expressions.",
|
||||
"oneOf": [
|
||||
{
|
||||
"type": "array",
|
||||
"items": {
|
||||
"type": "string"
|
||||
}
|
||||
},
|
||||
{
|
||||
"type": "null"
|
||||
}
|
||||
]
|
||||
},
|
||||
"rule": {
|
||||
"type": "object",
|
||||
"properties": {
|
||||
"exclude": {
|
||||
"description": "A regular expression for paths to exclude.",
|
||||
"$ref": "#/definitions/regexlist"
|
||||
},
|
||||
"suppress": {
|
||||
"description": "A regular expression for messages to suppress.",
|
||||
"$ref": "#/definitions/regexlist"
|
||||
}
|
||||
},
|
||||
"additionalProperties": false
|
||||
},
|
||||
"ruleList": {
|
||||
"type": "object",
|
||||
"additionalProperties": {
|
||||
"oneOf": [
|
||||
{
|
||||
"$ref": "#/definitions/rule"
|
||||
},
|
||||
{
|
||||
"type": "null"
|
||||
}
|
||||
]
|
||||
}
|
||||
}
|
||||
},
|
||||
"properties": {
|
||||
"groups": {
|
||||
"description": "A definition of all groups.",
|
||||
"type": "array",
|
||||
"items": {
|
||||
"$ref": "#/definitions/group"
|
||||
},
|
||||
"minItems": 1
|
||||
},
|
||||
"global": {
|
||||
"description": "A global set of rules.",
|
||||
"type": "object",
|
||||
"additionalProperties": {
|
||||
"$ref": "#/definitions/rule"
|
||||
}
|
||||
},
|
||||
"analyzers": {
|
||||
"description": "A definition of all groups.",
|
||||
"type": "object",
|
||||
"additionalProperties": {
|
||||
"$ref": "#/definitions/ruleList"
|
||||
}
|
||||
}
|
||||
},
|
||||
"required": [
|
||||
"groups"
|
||||
],
|
||||
"additionalProperties": false
|
||||
}
|
||||
@@ -0,0 +1,13 @@
|
||||
load("//tools:defs.bzl", "go_binary")
|
||||
|
||||
package(licenses = ["notice"])
|
||||
|
||||
go_binary(
|
||||
name = "yamltest",
|
||||
srcs = ["main.go"],
|
||||
visibility = ["//visibility:public"],
|
||||
deps = [
|
||||
"@com_github_xeipuuv_gojsonschema//:go_default_library",
|
||||
"@in_gopkg_yaml_v2//:go_default_library",
|
||||
],
|
||||
)
|
||||
@@ -0,0 +1,41 @@
|
||||
"""Tools for testing yaml files against schemas."""
|
||||
|
||||
def _yaml_test_impl(ctx):
|
||||
"""Implementation for yaml_test."""
|
||||
runner = ctx.actions.declare_file(ctx.label.name)
|
||||
ctx.actions.write(runner, "\n".join([
|
||||
"#!/bin/bash",
|
||||
"set -euo pipefail",
|
||||
"%s -schema=%s -- %s" % (
|
||||
ctx.files._tool[0].short_path,
|
||||
ctx.files.schema[0].short_path,
|
||||
" ".join([f.short_path for f in ctx.files.srcs]),
|
||||
),
|
||||
]), is_executable = True)
|
||||
return [DefaultInfo(
|
||||
runfiles = ctx.runfiles(files = ctx.files._tool + ctx.files.schema + ctx.files.srcs),
|
||||
executable = runner,
|
||||
)]
|
||||
|
||||
yaml_test = rule(
|
||||
implementation = _yaml_test_impl,
|
||||
doc = "Tests a yaml file against a schema.",
|
||||
attrs = {
|
||||
"srcs": attr.label_list(
|
||||
doc = "The input yaml files.",
|
||||
mandatory = True,
|
||||
allow_files = True,
|
||||
),
|
||||
"schema": attr.label(
|
||||
doc = "The schema file in JSON schema format.",
|
||||
allow_single_file = True,
|
||||
mandatory = True,
|
||||
),
|
||||
"_tool": attr.label(
|
||||
executable = True,
|
||||
cfg = "host",
|
||||
default = Label("//tools/yamltest:yamltest"),
|
||||
),
|
||||
},
|
||||
test = True,
|
||||
)
|
||||
@@ -0,0 +1,133 @@
|
||||
// Copyright 2020 The gVisor Authors.
|
||||
//
|
||||
// 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.
|
||||
|
||||
// Binary yamltest does strict yaml parsing and validation.
|
||||
package main
|
||||
|
||||
import (
|
||||
"encoding/json"
|
||||
"errors"
|
||||
"flag"
|
||||
"fmt"
|
||||
"os"
|
||||
|
||||
"github.com/xeipuuv/gojsonschema"
|
||||
yaml "gopkg.in/yaml.v2"
|
||||
)
|
||||
|
||||
func fixup(v interface{}) (interface{}, error) {
|
||||
switch x := v.(type) {
|
||||
case map[interface{}]interface{}:
|
||||
// Coerse into a string-based map, required for yaml.
|
||||
strMap := make(map[string]interface{})
|
||||
for k, v := range x {
|
||||
strK, ok := k.(string)
|
||||
if !ok {
|
||||
// This cannot be converted to JSON at all.
|
||||
return nil, fmt.Errorf("invalid key %T in (%#v)", k, x)
|
||||
}
|
||||
fv, err := fixup(v)
|
||||
if err != nil {
|
||||
return nil, fmt.Errorf(".%s%w", strK, err)
|
||||
}
|
||||
strMap[strK] = fv
|
||||
}
|
||||
return strMap, nil
|
||||
case []interface{}:
|
||||
for i := range x {
|
||||
fv, err := fixup(x[i])
|
||||
if err != nil {
|
||||
return nil, fmt.Errorf("[%d]%w", i, err)
|
||||
}
|
||||
x[i] = fv
|
||||
}
|
||||
return x, nil
|
||||
default:
|
||||
return v, nil
|
||||
}
|
||||
}
|
||||
|
||||
func loadFile(filename string) (gojsonschema.JSONLoader, error) {
|
||||
f, err := os.Open(filename)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
defer f.Close()
|
||||
dec := yaml.NewDecoder(f)
|
||||
dec.SetStrict(true)
|
||||
var object interface{}
|
||||
if err := dec.Decode(&object); err != nil {
|
||||
return nil, err
|
||||
}
|
||||
fixedObject, err := fixup(object) // For serialization.
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
bytes, err := json.Marshal(fixedObject)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
return gojsonschema.NewStringLoader(string(bytes)), nil
|
||||
}
|
||||
|
||||
var schema = flag.String("schema", "", "path to JSON schema file.")
|
||||
|
||||
func main() {
|
||||
flag.Parse()
|
||||
if *schema == "" || len(flag.Args()) == 0 {
|
||||
flag.Usage()
|
||||
os.Exit(2)
|
||||
}
|
||||
|
||||
// Construct our schema loader.
|
||||
schemaLoader := gojsonschema.NewReferenceLoader(fmt.Sprintf("file://%s", *schema))
|
||||
|
||||
// Parse all documents.
|
||||
allErrors := make(map[string][]error)
|
||||
for _, filename := range flag.Args() {
|
||||
// Record the filename with an empty slice for below, where
|
||||
// we will emit all files (even those without any errors).
|
||||
allErrors[filename] = nil
|
||||
documentLoader, err := loadFile(filename)
|
||||
if err != nil {
|
||||
allErrors[filename] = append(allErrors[filename], err)
|
||||
continue
|
||||
}
|
||||
result, err := gojsonschema.Validate(schemaLoader, documentLoader)
|
||||
if err != nil {
|
||||
allErrors[filename] = append(allErrors[filename], err)
|
||||
continue
|
||||
}
|
||||
for _, desc := range result.Errors() {
|
||||
allErrors[filename] = append(allErrors[filename], errors.New(desc.String()))
|
||||
}
|
||||
}
|
||||
|
||||
// Print errors in yaml format.
|
||||
totalErrors := 0
|
||||
for filename, errs := range allErrors {
|
||||
totalErrors += len(errs)
|
||||
if len(errs) == 0 {
|
||||
fmt.Fprintf(os.Stderr, "%s: ✓\n", filename)
|
||||
continue
|
||||
}
|
||||
fmt.Fprintf(os.Stderr, "%s:\n", filename)
|
||||
for _, err := range errs {
|
||||
fmt.Fprintf(os.Stderr, "- %s\n", err)
|
||||
}
|
||||
}
|
||||
if totalErrors != 0 {
|
||||
os.Exit(1)
|
||||
}
|
||||
}
|
||||
Reference in New Issue
Block a user