feat: generate json schema for func.yaml (#460)

Signed-off-by: Zbynek Roubalik <zroubali@redhat.com>
This commit is contained in:
Zbynek Roubalik 2021-08-09 12:28:11 +02:00 committed by GitHub
parent 85683ea933
commit 8939f89bea
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
8 changed files with 316 additions and 30 deletions

View File

@ -6,7 +6,7 @@ on:
- "main"
jobs:
test:
runs-on: 'ubuntu-latest'
runs-on: "ubuntu-latest"
steps:
- uses: actions/checkout@v2
- uses: actions/setup-go@v2
@ -39,6 +39,8 @@ jobs:
PKGER: "./pkger"
- name: Lint
run: make check
- name: Check that 'func.yaml schema' is up-to-date
run: make schema-check
outputs:
pkger: ${{ steps.pkger-download-url.outputs.result }}
@ -80,10 +82,10 @@ jobs:
run: |
curl -s -L -o pkger.tgz ${{ needs.test.outputs.pkger }}
tar xvzf pkger.tgz
# Standard build tasks
- name: Build
run: make cross-platform
run: make cross-platform
env:
PKGER: "./pkger"
# NOTE:

View File

@ -7,7 +7,7 @@ jobs:
build:
strategy:
matrix:
os: ['ubuntu-latest', 'windows-latest', 'macos-latest']
os: ["ubuntu-latest", "windows-latest", "macos-latest"]
runs-on: ${{ matrix.os }}
steps:
- uses: actions/checkout@v2
@ -58,25 +58,27 @@ jobs:
run: |
curl -s -L -o pkger.tgz ${{ steps.pkger-download-url.outputs.result }}
tar xvzf pkger.tgz
- name: Unit Test
run: make test
run: make test
env:
PKGER: "./${{ steps.pkger-binaries.outputs.binary }}"
- name: Template Unit Tests
run: make test-templates
run: make test-templates
env:
PKGER: "./${{ steps.pkger-binaries.outputs.binary }}"
- name: Build
run: make build
run: make build
env:
PKGER: "./${{ steps.pkger-binaries.outputs.binary }}"
- name: Lint
run: make check
- name: Check that 'func.yaml schema' is up-to-date
if: matrix.os != 'windows-latest'
run: make schema-check
integration-test:
needs: build
runs-on: 'ubuntu-latest'
runs-on: "ubuntu-latest"
steps:
- name: Checkout
uses: actions/checkout@v2
@ -106,14 +108,14 @@ jobs:
e2e-test:
needs: integration-test
runs-on: 'ubuntu-latest'
runs-on: "ubuntu-latest"
steps:
- name: Checkout
uses: actions/checkout@v2
- name: Setup Go
uses: actions/setup-go@v2
with:
go-version: '^1.16'
go-version: "^1.16"
- name: Determine download URL for latest pkger
id: pkger-download-url
uses: actions/github-script@v2

View File

@ -42,6 +42,7 @@ help:
@echo 'Available targets are:'
@awk 'BEGIN {FS = ":.*##"; printf "\nUsage:\n make \033[36m<target>\033[0m\n"} /^[a-zA-Z0-9_-]+:.*?##/ { printf " \033[36m%-15s\033[0m %s\n", $$1, $$2 } /^##@/ { printf "\n\033[1m%s\033[0m\n", substr($$0, 5) } ' $(MAKEFILE_LIST)
###############
##@ Development
###############
@ -109,11 +110,11 @@ test-typescript: ## Test Typescript templates
cd templates/typescript/events && npm ci && npm test && rm -rf node_modules build
cd templates/typescript/http && npm ci && npm test && rm -rf node_modules build
###################
##@ Extended Testing (cluster required)
###################
test-integration: ## Run integration tests using an available cluster.
go test -tags integration ./... -v
@ -142,3 +143,15 @@ windows: $(BIN_WINDOWS) ## Build for Windows
$(BIN_WINDOWS): pkged.go
env CGO_ENABLED=0 GOOS=windows GOARCH=amd64 go build -o $(BIN_WINDOWS) -ldflags $(LDFLAGS) ./cmd/$(BIN)
######################
##@ Schemas
######################
schema-generate: ## Generate func.yaml schema
go run schema/generator/main.go
schema-check: ## Check that func.yaml schema is up-to-date
mv schema/func_yaml-schema.json schema/func_yaml-schema-previous.json
make schema-generate
diff schema/func_yaml-schema.json schema/func_yaml-schema-previous.json ||\
(echo "\n\nFunction config schema 'schema/func_yaml-schema.json' is obsolete, please run 'make schema-generate'.\n\n"; rm -rf schema/func_yaml-schema-previous.json; exit 1)
rm -rf schema/func_yaml-schema-previous.json

View File

@ -27,8 +27,8 @@ var (
type Volumes []Volume
type Volume struct {
Secret *string `yaml:"secret,omitempty"`
ConfigMap *string `yaml:"configMap,omitempty"`
Secret *string `yaml:"secret,omitempty" jsonschema:"oneof_required=secret"`
ConfigMap *string `yaml:"configMap,omitempty" jsonschema:"oneof_required=configmap"`
Path *string `yaml:"path"`
}
@ -83,11 +83,11 @@ type Options struct {
}
type ScaleOptions struct {
Min *int64 `yaml:"min,omitempty"`
Max *int64 `yaml:"max,omitempty"`
Metric *string `yaml:"metric,omitempty"`
Target *float64 `yaml:"target,omitempty"`
Utilization *float64 `yaml:"utilization,omitempty"`
Min *int64 `yaml:"min,omitempty" jsonschema_extras:"minimum=0"`
Max *int64 `yaml:"max,omitempty" jsonschema_extras:"minimum=0"`
Metric *string `yaml:"metric,omitempty" jsonschema:"enum=concurrency,enum=rps"`
Target *float64 `yaml:"target,omitempty" jsonschema_extras:"minimum=0.01"`
Utilization *float64 `yaml:"utilization,omitempty" jsonschema:"minimum=1,maximum=100"`
}
type ResourcesOptions struct {
@ -96,20 +96,20 @@ type ResourcesOptions struct {
}
type ResourcesLimitsOptions struct {
CPU *string `yaml:"cpu,omitempty"`
Memory *string `yaml:"memory,omitempty"`
Concurrency *int64 `yaml:"concurrency,omitempty"`
CPU *string `yaml:"cpu,omitempty" jsonschema:"pattern=^([+-]?[0-9.]+)([eEinumkKMGTP]*[-+]?[0-9]*)$"`
Memory *string `yaml:"memory,omitempty" jsonschema:"pattern=^([+-]?[0-9.]+)([eEinumkKMGTP]*[-+]?[0-9]*)$"`
Concurrency *int64 `yaml:"concurrency,omitempty" jsonschema_extras:"minimum=0"`
}
type ResourcesRequestsOptions struct {
CPU *string `yaml:"cpu,omitempty"`
Memory *string `yaml:"memory,omitempty"`
CPU *string `yaml:"cpu,omitempty" jsonschema:"pattern=^([+-]?[0-9.]+)([eEinumkKMGTP]*[-+]?[0-9]*)$"`
Memory *string `yaml:"memory,omitempty" jsonschema:"pattern=^([+-]?[0-9.]+)([eEinumkKMGTP]*[-+]?[0-9]*)$"`
}
// Config represents the serialized state of a Function's metadata.
// See the Function struct for attribute documentation.
type config struct {
Name string `yaml:"name"`
type Config struct {
Name string `yaml:"name" jsonschema:"pattern=^[a-z0-9]([-a-z0-9]*[a-z0-9])?$"`
Namespace string `yaml:"namespace"`
Runtime string `yaml:"runtime"`
Image string `yaml:"image"`
@ -129,7 +129,7 @@ type config struct {
// errors accessing an extant config file, or the contents of the file do not
// unmarshall. A missing file at a valid path does not error but returns the
// empty value of Config.
func newConfig(root string) (c config, err error) {
func newConfig(root string) (c Config, err error) {
filename := filepath.Join(root, ConfigFile)
if _, err = os.Stat(filename); err != nil {
// do not consider a missing config file an error. Just return.
@ -217,7 +217,7 @@ func newConfig(root string) (c config, err error) {
// fromConfig returns a Function populated from config.
// Note that config does not include ancillary fields not serialized, such as Root.
func fromConfig(c config) (f Function) {
func fromConfig(c Config) (f Function) {
return Function{
Name: c.Name,
Namespace: c.Namespace,
@ -235,8 +235,8 @@ func fromConfig(c config) (f Function) {
}
// toConfig serializes a Function to a config object.
func toConfig(f Function) config {
return config{
func toConfig(f Function) Config {
return Config{
Name: f.Name,
Namespace: f.Namespace,
Runtime: f.Runtime,

1
go.mod
View File

@ -4,6 +4,7 @@ go 1.15
require (
github.com/AlecAivazis/survey/v2 v2.2.14
github.com/alecthomas/jsonschema v0.0.0-20210526225647-edb03dcab7bc
github.com/buildpacks/pack v0.19.0
github.com/cloudevents/sdk-go/v2 v2.4.1
github.com/containers/image/v5 v5.10.6

5
go.sum
View File

@ -118,6 +118,8 @@ github.com/ahmetb/gen-crd-api-reference-docs v0.3.1-0.20210420163308-c1402a70e2f
github.com/alcortesm/tgz v0.0.0-20161220082320-9c5fe88206d7 h1:uSoVVbwJiQipAclBbw+8quDsfcvFjOpI5iCf4p/cqCs=
github.com/alcortesm/tgz v0.0.0-20161220082320-9c5fe88206d7/go.mod h1:6zEj6s6u/ghQa61ZWa/C2Aw3RkjiTBOix7dkqa1VLIs=
github.com/alecthomas/jsonschema v0.0.0-20180308105923-f2c93856175a/go.mod h1:qpebaTNSsyUn5rPSJMsfqEtDw71TTggXM6stUDI16HA=
github.com/alecthomas/jsonschema v0.0.0-20210526225647-edb03dcab7bc h1:mT8qSzuyEAkxbv4GBln7yeuQZpBnfikr3PTuiPs6Z3k=
github.com/alecthomas/jsonschema v0.0.0-20210526225647-edb03dcab7bc/go.mod h1:/n6+1/DWPltRLWL/VKyUxg6tzsl5kHUCcraimt4vr60=
github.com/alecthomas/template v0.0.0-20160405071501-a0175ee3bccc/go.mod h1:LOuyumcjzFXgccqObfd/Ljyb9UuFJ6TxHnclSeseNhc=
github.com/alecthomas/template v0.0.0-20190718012654-fb15b899a751 h1:JYp7IbQjafoB+tBA3gMyHYHrpOtNuDiK/uB5uXxq5wM=
github.com/alecthomas/template v0.0.0-20190718012654-fb15b899a751/go.mod h1:LOuyumcjzFXgccqObfd/Ljyb9UuFJ6TxHnclSeseNhc=
@ -556,6 +558,8 @@ github.com/hinshun/vt10x v0.0.0-20180616224451-1954e6464174 h1:WlZsjVhE8Af9IcZDG
github.com/hinshun/vt10x v0.0.0-20180616224451-1954e6464174/go.mod h1:DqJ97dSdRW1W22yXSB90986pcOyQ7r45iio1KN2ez1A=
github.com/hpcloud/tail v1.0.0/go.mod h1:ab1qPbhIpdTxEkNHXyeSf5vhxWSCs/tWer42PpOxQnU=
github.com/hudl/fargo v1.3.0/go.mod h1:y3CKSmjA+wD2gak7sUSXTAoopbhU08POFhmITJgmKTg=
github.com/iancoleman/orderedmap v0.0.0-20190318233801-ac98e3ecb4b0 h1:i462o439ZjprVSFSZLZxcsoAe592sZB1rci2Z8j4wdk=
github.com/iancoleman/orderedmap v0.0.0-20190318233801-ac98e3ecb4b0/go.mod h1:N0Wam8K1arqPXNWjMo21EXnBPOPp36vB07FNRdD2geA=
github.com/ianlancetaylor/demangle v0.0.0-20181102032728-5e5cf60278f6/go.mod h1:aSSvb/t6k1mPoxDqO4vJh6VOCGPwU4O0C2/Eqndh1Sc=
github.com/ianlancetaylor/demangle v0.0.0-20200824232613-28f6c0f3b639/go.mod h1:aSSvb/t6k1mPoxDqO4vJh6VOCGPwU4O0C2/Eqndh1Sc=
github.com/imdario/mergo v0.3.5/go.mod h1:2EnlNZ0deacrJVfApfmtdGgDfMuh/nq6Ok1EcJh5FfA=
@ -942,6 +946,7 @@ github.com/stretchr/objx v0.2.0/go.mod h1:qt09Ya8vawLte6SNmTgCsAVtYtaKzEcn8ATUoH
github.com/stretchr/testify v1.2.1/go.mod h1:a8OnRcib4nhh0OaRAV+Yts87kKdq0PP7pXfy6kDkUVs=
github.com/stretchr/testify v1.2.2/go.mod h1:a8OnRcib4nhh0OaRAV+Yts87kKdq0PP7pXfy6kDkUVs=
github.com/stretchr/testify v1.3.0/go.mod h1:M5WIy9Dh21IEIfnGCwXGc5bZfKNJtfHm1UVUgZn+9EI=
github.com/stretchr/testify v1.3.1-0.20190311161405-34c6fa2dc709/go.mod h1:M5WIy9Dh21IEIfnGCwXGc5bZfKNJtfHm1UVUgZn+9EI=
github.com/stretchr/testify v1.4.0/go.mod h1:j7eGeouHqKxXV5pUuKE4zz7dFj8WfuZ+81PSLYec5m4=
github.com/stretchr/testify v1.5.1/go.mod h1:5W2xD1RspED5o8YsWQXVCued0rvSQ+mT+I5cxcmMvtA=
github.com/stretchr/testify v1.6.1/go.mod h1:6Fq8oRcR53rry900zMqJjRRixrwX3KX962/h/Wwjteg=

View File

@ -0,0 +1,222 @@
{
"$schema": "http://json-schema.org/draft-04/schema#",
"$ref": "#/definitions/Config",
"definitions": {
"Config": {
"required": [
"name",
"namespace",
"runtime",
"image",
"imageDigest",
"builder",
"builderMap",
"volumes",
"envs",
"annotations",
"options",
"labels"
],
"properties": {
"name": {
"pattern": "^[a-z0-9]([-a-z0-9]*[a-z0-9])?$",
"type": "string"
},
"namespace": {
"type": "string"
},
"runtime": {
"type": "string"
},
"image": {
"type": "string"
},
"imageDigest": {
"type": "string"
},
"builder": {
"type": "string"
},
"builderMap": {
"patternProperties": {
".*": {
"type": "string"
}
},
"type": "object"
},
"volumes": {
"items": {
"$schema": "http://json-schema.org/draft-04/schema#",
"$ref": "#/definitions/Volume"
},
"type": "array"
},
"envs": {
"items": {
"$schema": "http://json-schema.org/draft-04/schema#",
"$ref": "#/definitions/Pair"
},
"type": "array"
},
"annotations": {
"patternProperties": {
".*": {
"type": "string"
}
},
"type": "object"
},
"options": {
"$schema": "http://json-schema.org/draft-04/schema#",
"$ref": "#/definitions/Options"
},
"labels": {
"items": {
"$ref": "#/definitions/Pair"
},
"type": "array"
}
},
"additionalProperties": false,
"type": "object"
},
"Options": {
"properties": {
"scale": {
"$schema": "http://json-schema.org/draft-04/schema#",
"$ref": "#/definitions/ScaleOptions"
},
"resources": {
"$schema": "http://json-schema.org/draft-04/schema#",
"$ref": "#/definitions/ResourcesOptions"
}
},
"additionalProperties": false,
"type": "object"
},
"Pair": {
"required": [
"value"
],
"properties": {
"name": {
"type": "string"
},
"value": {
"type": "string"
}
},
"additionalProperties": false,
"type": "object"
},
"ResourcesLimitsOptions": {
"properties": {
"cpu": {
"pattern": "^([+-]?[0-9.]+)([eEinumkKMGTP]*[-+]?[0-9]*)$",
"type": "string"
},
"memory": {
"pattern": "^([+-]?[0-9.]+)([eEinumkKMGTP]*[-+]?[0-9]*)$",
"type": "string"
},
"concurrency": {
"type": "integer",
"minimum": 0
}
},
"additionalProperties": false,
"type": "object"
},
"ResourcesOptions": {
"properties": {
"requests": {
"$schema": "http://json-schema.org/draft-04/schema#",
"$ref": "#/definitions/ResourcesRequestsOptions"
},
"limits": {
"$schema": "http://json-schema.org/draft-04/schema#",
"$ref": "#/definitions/ResourcesLimitsOptions"
}
},
"additionalProperties": false,
"type": "object"
},
"ResourcesRequestsOptions": {
"properties": {
"cpu": {
"pattern": "^([+-]?[0-9.]+)([eEinumkKMGTP]*[-+]?[0-9]*)$",
"type": "string"
},
"memory": {
"pattern": "^([+-]?[0-9.]+)([eEinumkKMGTP]*[-+]?[0-9]*)$",
"type": "string"
}
},
"additionalProperties": false,
"type": "object"
},
"ScaleOptions": {
"properties": {
"min": {
"type": "integer",
"minimum": 0
},
"max": {
"type": "integer",
"minimum": 0
},
"metric": {
"enum": [
"concurrency",
"rps"
],
"type": "string"
},
"target": {
"type": "number",
"minimum": 0
},
"utilization": {
"maximum": 100,
"minimum": 1,
"type": "number"
}
},
"additionalProperties": false,
"type": "object"
},
"Volume": {
"required": [
"path"
],
"properties": {
"secret": {
"type": "string"
},
"configMap": {
"type": "string"
},
"path": {
"type": "string"
}
},
"additionalProperties": false,
"type": "object",
"oneOf": [
{
"required": [
"secret"
],
"title": "secret"
},
{
"required": [
"configMap"
],
"title": "configmap"
}
]
}
}
}

41
schema/generator/main.go Normal file
View File

@ -0,0 +1,41 @@
package main
import (
"bytes"
"encoding/json"
"io/ioutil"
"github.com/alecthomas/jsonschema"
fn "knative.dev/kn-plugin-func"
)
// This helper application generates json schemas:
// - schema for func.yaml stored in schema/func_yaml-schema.json
func main() {
err := generateFuncYamlSchema()
if err != nil {
panic(err)
}
}
// generateFuncYamlSchema generates json schema for Function configuration file - func.yaml.
// Genereated schema is written into schema/func_yaml-schema.json file
func generateFuncYamlSchema() error {
// generate json schema for Function struct
js := jsonschema.Reflect(&fn.Config{})
schema, err := js.MarshalJSON()
if err != nil {
return err
}
// indent the generated json
var indentedSchema bytes.Buffer
err = json.Indent(&indentedSchema, schema, "", "\t")
if err != nil {
return err
}
// write schema to the file
return ioutil.WriteFile("schema/func_yaml-schema.json", indentedSchema.Bytes(), 0644)
}