mirror of
https://github.com/rancher/os.git
synced 2025-09-02 15:24:32 +00:00
Add command to validate configuration
This commit is contained in:
@@ -76,6 +76,17 @@ func configSubcommands() []cli.Command {
|
||||
},
|
||||
},
|
||||
},
|
||||
{
|
||||
Name: "validate",
|
||||
Usage: "validate configuration from stdin",
|
||||
Action: validate,
|
||||
Flags: []cli.Flag{
|
||||
cli.StringFlag{
|
||||
Name: "input, i",
|
||||
Usage: "File from which to read",
|
||||
},
|
||||
},
|
||||
},
|
||||
}
|
||||
}
|
||||
|
||||
@@ -183,18 +194,7 @@ func configGet(c *cli.Context) error {
|
||||
}
|
||||
|
||||
func merge(c *cli.Context) error {
|
||||
input := os.Stdin
|
||||
inputFile := c.String("input")
|
||||
if inputFile != "" {
|
||||
var err error
|
||||
input, err = os.Open(inputFile)
|
||||
if err != nil {
|
||||
log.Fatal(err)
|
||||
}
|
||||
defer input.Close()
|
||||
}
|
||||
|
||||
bytes, err := ioutil.ReadAll(input)
|
||||
bytes, err := inputBytes(c)
|
||||
if err != nil {
|
||||
log.Fatal(err)
|
||||
}
|
||||
@@ -224,3 +224,32 @@ func export(c *cli.Context) error {
|
||||
|
||||
return nil
|
||||
}
|
||||
|
||||
func validate(c *cli.Context) error {
|
||||
bytes, err := inputBytes(c)
|
||||
if err != nil {
|
||||
log.Fatal(err)
|
||||
}
|
||||
validationErrors, err := config.Validate(bytes)
|
||||
if err != nil {
|
||||
log.Fatal(err)
|
||||
}
|
||||
for _, validationError := range validationErrors.Errors() {
|
||||
log.Error(validationError)
|
||||
}
|
||||
return nil
|
||||
}
|
||||
|
||||
func inputBytes(c *cli.Context) ([]byte, error) {
|
||||
input := os.Stdin
|
||||
inputFile := c.String("input")
|
||||
if inputFile != "" {
|
||||
var err error
|
||||
input, err = os.Open(inputFile)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
defer input.Close()
|
||||
}
|
||||
return ioutil.ReadAll(input)
|
||||
}
|
||||
|
195
config/schema.go
Normal file
195
config/schema.go
Normal file
@@ -0,0 +1,195 @@
|
||||
package config
|
||||
|
||||
var schema = `{
|
||||
"type": "object",
|
||||
"additionalProperties": false,
|
||||
|
||||
"properties": {
|
||||
"ssh_authorized_keys": {"$ref": "#/definitions/list_of_strings"},
|
||||
"write_files": {
|
||||
"type": "array",
|
||||
"items": {"$ref": "#/definitions/file_config"}
|
||||
},
|
||||
"hostname": {"type": "string"},
|
||||
"mounts": {"type": "array"},
|
||||
"rancher": {"$ref": "#/definitions/rancher_config"},
|
||||
"runcmd": {"type": "array"}
|
||||
},
|
||||
|
||||
"definitions": {
|
||||
"rancher_config": {
|
||||
"id": "#/definitions/rancher_config",
|
||||
"type": "object",
|
||||
"additionalProperties": false,
|
||||
|
||||
"properties": {
|
||||
"console": {"type": "string"},
|
||||
"environment": {"type": "object"},
|
||||
"services": {"type": "object"},
|
||||
"bootstrap_containers": {"type": "object"},
|
||||
"autoformat": {"type": "object"},
|
||||
"bootstrap_docker": {"$ref": "#/definitions/docker_config"},
|
||||
"cloud_init": {"$ref": "#/definitions/cloud_init_config"},
|
||||
"debug": {"type": "boolean"},
|
||||
"rm_usr": {"type": "boolean"},
|
||||
"no_sharedroot": {"type": "boolean"},
|
||||
"log": {"type": "boolean"},
|
||||
"force_console_rebuild": {"type": "boolean"},
|
||||
"disable": {"$ref": "#/definitions/list_of_strings"},
|
||||
"services_include": {"type": "object"},
|
||||
"modules": {"$ref": "#/definitions/list_of_strings"},
|
||||
"network": {"$ref": "#/definitions/network_config"},
|
||||
"default_network": {"type": "object"},
|
||||
"repositories": {"type": "object"},
|
||||
"ssh": {"$ref": "#/definitions/ssh_config"},
|
||||
"state": {"$ref": "#/definitions/state_config"},
|
||||
"system_docker": {"$ref": "#/definitions/docker_config"},
|
||||
"upgrade": {"$ref": "#/definitions/upgrade_config"},
|
||||
"docker": {"$ref": "#/definitions/docker_config"},
|
||||
"registry_auths": {"type": "object"},
|
||||
"defaults": {"$ref": "#/definitions/defaults_config"},
|
||||
"resize_device": {"type": "string"},
|
||||
"sysctl": {"type": "object"}
|
||||
}
|
||||
},
|
||||
|
||||
"file_config": {
|
||||
"id": "#/definitions/file_config",
|
||||
"type": "object",
|
||||
"additionalProperties": false,
|
||||
|
||||
"properties": {
|
||||
"encoding": {"type": "string"},
|
||||
"content": {"type": "string"},
|
||||
"owner": {"type": "string"},
|
||||
"path": {"type": "string"},
|
||||
"permissions": {"type": "string"}
|
||||
}
|
||||
},
|
||||
|
||||
"network_config": {
|
||||
"id": "#/definitions/network_config",
|
||||
"type": "object",
|
||||
"additionalProperties": false,
|
||||
|
||||
"properties": {
|
||||
"pre_cmds": {"$ref": "#/definitions/list_of_strings"},
|
||||
"dns": {"type": "object"},
|
||||
"interfaces": {"type": "object"},
|
||||
"post_cmds": {"$ref": "#/definitions/list_of_strings"},
|
||||
"http_proxy": {"type": "string"},
|
||||
"https_proxy": {"type": "string"},
|
||||
"no_proxy": {"type": "string"}
|
||||
}
|
||||
},
|
||||
|
||||
"upgrade_config": {
|
||||
"id": "#/definitions/upgrade_config",
|
||||
"type": "object",
|
||||
"additionalProperties": false,
|
||||
|
||||
"properties": {
|
||||
"url": {"type": "string"},
|
||||
"image": {"type": "string"},
|
||||
"rollback": {"type": "string"}
|
||||
}
|
||||
},
|
||||
|
||||
"docker_config": {
|
||||
"id": "#/definitions/docker_config",
|
||||
"type": "object",
|
||||
"additionalProperties": false,
|
||||
|
||||
"properties": {
|
||||
"engine": {"type": "string"},
|
||||
"tls": {"type": "boolean"},
|
||||
"tls_args": {"$ref": "#/definitions/list_of_strings"},
|
||||
"args": {"$ref": "#/definitions/list_of_strings"},
|
||||
"extra_args": {"$ref": "#/definitions/list_of_strings"},
|
||||
"server_cert": {"type": "string"},
|
||||
"server_key": {"type": "string"},
|
||||
"ca_cert": {"type": "string"},
|
||||
"ca_key": {"type": "string"},
|
||||
"environment": {"$ref": "#/definitions/list_of_strings"},
|
||||
"storage_context": {"type": "string"},
|
||||
"exec": {"type": "boolean"},
|
||||
"bridge": {"type": "string"},
|
||||
"config_file": {"type": "string"},
|
||||
"containerd": {"type": "string"},
|
||||
"debug": {"type": "boolean"},
|
||||
"exec_root": {"type": "string"},
|
||||
"group": {"type": "string"},
|
||||
"graph": {"type": "string"},
|
||||
"host": {"type": "string"},
|
||||
"live_restore": {"type": "boolean"},
|
||||
"log_driver": {"type": "string"},
|
||||
"log_opts": {"type": "object"},
|
||||
"pid_file": {"type": "string"},
|
||||
"registry_mirror": {"type": "string"},
|
||||
"restart": {"type": "boolean"},
|
||||
"selinux_enabled": {"type": "boolean"},
|
||||
"storage_driver": {"type": "string"},
|
||||
"userland_proxy": {"type": "boolean"}
|
||||
}
|
||||
},
|
||||
|
||||
"ssh_config": {
|
||||
"id": "#/definitions/ssh_config",
|
||||
"type": "object",
|
||||
"additionalProperties": false,
|
||||
|
||||
"properties": {
|
||||
"keys": {"type": "object"}
|
||||
}
|
||||
},
|
||||
|
||||
"state_config": {
|
||||
"id": "#/definitions/state_config",
|
||||
"type": "object",
|
||||
"additionalProperties": false,
|
||||
|
||||
"properties": {
|
||||
"directory": {"type": "string"},
|
||||
"fstype": {"type": "string"},
|
||||
"dev": {"type": "string"},
|
||||
"wait": {"type": "boolean"},
|
||||
"required": {"type": "boolean"},
|
||||
"autoformat": {"$ref": "#/definitions/list_of_strings"},
|
||||
"mdadm_scan": {"type": "boolean"},
|
||||
"script": {"type": "string"},
|
||||
"oem_fstype": {"type": "string"},
|
||||
"oem_dev": {"type": "string"}
|
||||
}
|
||||
},
|
||||
|
||||
"cloud_init_config": {
|
||||
"id": "#/definitions/cloud_init_config",
|
||||
"type": "object",
|
||||
"additionalProperties": false,
|
||||
|
||||
"properties": {
|
||||
"datasources": {"$ref": "#/definitions/list_of_strings"}
|
||||
}
|
||||
},
|
||||
|
||||
"defaults_config": {
|
||||
"id": "#/definitions/defaults_config",
|
||||
"type": "object",
|
||||
"additionalProperties": false,
|
||||
|
||||
"properties": {
|
||||
"hostname": {"type": "string"},
|
||||
"docker": {"type": "object"},
|
||||
"network": {"$ref": "#/definitions/network_config"}
|
||||
}
|
||||
},
|
||||
|
||||
"list_of_strings": {
|
||||
"type": "array",
|
||||
"items": {"type": "string"},
|
||||
"uniqueItems": true
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
`
|
42
config/validate.go
Normal file
42
config/validate.go
Normal file
@@ -0,0 +1,42 @@
|
||||
package config
|
||||
|
||||
import (
|
||||
yaml "github.com/cloudfoundry-incubator/candiedyaml"
|
||||
"github.com/xeipuuv/gojsonschema"
|
||||
)
|
||||
|
||||
// TODO: use this function from libcompose
|
||||
func convertKeysToStrings(item interface{}) interface{} {
|
||||
switch typedDatas := item.(type) {
|
||||
case map[string]interface{}:
|
||||
for key, value := range typedDatas {
|
||||
typedDatas[key] = convertKeysToStrings(value)
|
||||
}
|
||||
return typedDatas
|
||||
case map[interface{}]interface{}:
|
||||
newMap := make(map[string]interface{})
|
||||
for key, value := range typedDatas {
|
||||
stringKey := key.(string)
|
||||
newMap[stringKey] = convertKeysToStrings(value)
|
||||
}
|
||||
return newMap
|
||||
case []interface{}:
|
||||
for i, value := range typedDatas {
|
||||
typedDatas[i] = append(typedDatas, convertKeysToStrings(value))
|
||||
}
|
||||
return typedDatas
|
||||
default:
|
||||
return item
|
||||
}
|
||||
}
|
||||
|
||||
func Validate(bytes []byte) (*gojsonschema.Result, error) {
|
||||
var rawCfg map[string]interface{}
|
||||
if err := yaml.Unmarshal([]byte(bytes), &rawCfg); err != nil {
|
||||
return nil, err
|
||||
}
|
||||
rawCfg = convertKeysToStrings(rawCfg).(map[string]interface{})
|
||||
loader := gojsonschema.NewGoLoader(rawCfg)
|
||||
schemaLoader := gojsonschema.NewStringLoader(schema)
|
||||
return gojsonschema.Validate(schemaLoader, loader)
|
||||
}
|
29
config/validate_test.go
Normal file
29
config/validate_test.go
Normal file
@@ -0,0 +1,29 @@
|
||||
package config
|
||||
|
||||
import (
|
||||
"fmt"
|
||||
"strings"
|
||||
"testing"
|
||||
)
|
||||
|
||||
func testValidate(t *testing.T, cfg []byte, contains string) {
|
||||
validationErrors, err := Validate(cfg)
|
||||
if err != nil {
|
||||
t.Fatal(err)
|
||||
}
|
||||
if contains == "" && len(validationErrors.Errors()) != 0 {
|
||||
t.Fail()
|
||||
}
|
||||
if !strings.Contains(fmt.Sprint(validationErrors.Errors()), contains) {
|
||||
t.Fail()
|
||||
}
|
||||
}
|
||||
|
||||
func TestValidate(t *testing.T) {
|
||||
testValidate(t, []byte("{}"), "")
|
||||
testValidate(t, []byte(`rancher:
|
||||
log: true
|
||||
`), "")
|
||||
testValidate(t, []byte("bad_key: {}"), "Additional property bad_key is not allowed")
|
||||
testValidate(t, []byte("rancher: []"), "rancher: Invalid type. Expected: object, given: array")
|
||||
}
|
32
scripts/inline_schema.go
Normal file
32
scripts/inline_schema.go
Normal file
@@ -0,0 +1,32 @@
|
||||
package main
|
||||
|
||||
import (
|
||||
"io/ioutil"
|
||||
"os"
|
||||
"text/template"
|
||||
)
|
||||
|
||||
func main() {
|
||||
t, err := template.New("schema_template").ParseFiles("./scripts/schema_template")
|
||||
if err != nil {
|
||||
panic(err)
|
||||
}
|
||||
|
||||
schema, err := ioutil.ReadFile("./scripts/schema.json")
|
||||
if err != nil {
|
||||
panic(err)
|
||||
}
|
||||
|
||||
inlinedFile, err := os.Create("config/schema.go")
|
||||
if err != nil {
|
||||
panic(err)
|
||||
}
|
||||
|
||||
err = t.Execute(inlinedFile, map[string]string{
|
||||
"schema": string(schema),
|
||||
})
|
||||
|
||||
if err != nil {
|
||||
panic(err)
|
||||
}
|
||||
}
|
192
scripts/schema.json
Normal file
192
scripts/schema.json
Normal file
@@ -0,0 +1,192 @@
|
||||
{
|
||||
"type": "object",
|
||||
"additionalProperties": false,
|
||||
|
||||
"properties": {
|
||||
"ssh_authorized_keys": {"$ref": "#/definitions/list_of_strings"},
|
||||
"write_files": {
|
||||
"type": "array",
|
||||
"items": {"$ref": "#/definitions/file_config"}
|
||||
},
|
||||
"hostname": {"type": "string"},
|
||||
"mounts": {"type": "array"},
|
||||
"rancher": {"$ref": "#/definitions/rancher_config"},
|
||||
"runcmd": {"type": "array"}
|
||||
},
|
||||
|
||||
"definitions": {
|
||||
"rancher_config": {
|
||||
"id": "#/definitions/rancher_config",
|
||||
"type": "object",
|
||||
"additionalProperties": false,
|
||||
|
||||
"properties": {
|
||||
"console": {"type": "string"},
|
||||
"environment": {"type": "object"},
|
||||
"services": {"type": "object"},
|
||||
"bootstrap_containers": {"type": "object"},
|
||||
"autoformat": {"type": "object"},
|
||||
"bootstrap_docker": {"$ref": "#/definitions/docker_config"},
|
||||
"cloud_init": {"$ref": "#/definitions/cloud_init_config"},
|
||||
"debug": {"type": "boolean"},
|
||||
"rm_usr": {"type": "boolean"},
|
||||
"no_sharedroot": {"type": "boolean"},
|
||||
"log": {"type": "boolean"},
|
||||
"force_console_rebuild": {"type": "boolean"},
|
||||
"disable": {"$ref": "#/definitions/list_of_strings"},
|
||||
"services_include": {"type": "object"},
|
||||
"modules": {"$ref": "#/definitions/list_of_strings"},
|
||||
"network": {"$ref": "#/definitions/network_config"},
|
||||
"default_network": {"type": "object"},
|
||||
"repositories": {"type": "object"},
|
||||
"ssh": {"$ref": "#/definitions/ssh_config"},
|
||||
"state": {"$ref": "#/definitions/state_config"},
|
||||
"system_docker": {"$ref": "#/definitions/docker_config"},
|
||||
"upgrade": {"$ref": "#/definitions/upgrade_config"},
|
||||
"docker": {"$ref": "#/definitions/docker_config"},
|
||||
"registry_auths": {"type": "object"},
|
||||
"defaults": {"$ref": "#/definitions/defaults_config"},
|
||||
"resize_device": {"type": "string"},
|
||||
"sysctl": {"type": "object"}
|
||||
}
|
||||
},
|
||||
|
||||
"file_config": {
|
||||
"id": "#/definitions/file_config",
|
||||
"type": "object",
|
||||
"additionalProperties": false,
|
||||
|
||||
"properties": {
|
||||
"encoding": {"type": "string"},
|
||||
"content": {"type": "string"},
|
||||
"owner": {"type": "string"},
|
||||
"path": {"type": "string"},
|
||||
"permissions": {"type": "string"}
|
||||
}
|
||||
},
|
||||
|
||||
"network_config": {
|
||||
"id": "#/definitions/network_config",
|
||||
"type": "object",
|
||||
"additionalProperties": false,
|
||||
|
||||
"properties": {
|
||||
"pre_cmds": {"$ref": "#/definitions/list_of_strings"},
|
||||
"dns": {"type": "object"},
|
||||
"interfaces": {"type": "object"},
|
||||
"post_cmds": {"$ref": "#/definitions/list_of_strings"},
|
||||
"http_proxy": {"type": "string"},
|
||||
"https_proxy": {"type": "string"},
|
||||
"no_proxy": {"type": "string"}
|
||||
}
|
||||
},
|
||||
|
||||
"upgrade_config": {
|
||||
"id": "#/definitions/upgrade_config",
|
||||
"type": "object",
|
||||
"additionalProperties": false,
|
||||
|
||||
"properties": {
|
||||
"url": {"type": "string"},
|
||||
"image": {"type": "string"},
|
||||
"rollback": {"type": "string"}
|
||||
}
|
||||
},
|
||||
|
||||
"docker_config": {
|
||||
"id": "#/definitions/docker_config",
|
||||
"type": "object",
|
||||
"additionalProperties": false,
|
||||
|
||||
"properties": {
|
||||
"engine": {"type": "string"},
|
||||
"tls": {"type": "boolean"},
|
||||
"tls_args": {"$ref": "#/definitions/list_of_strings"},
|
||||
"args": {"$ref": "#/definitions/list_of_strings"},
|
||||
"extra_args": {"$ref": "#/definitions/list_of_strings"},
|
||||
"server_cert": {"type": "string"},
|
||||
"server_key": {"type": "string"},
|
||||
"ca_cert": {"type": "string"},
|
||||
"ca_key": {"type": "string"},
|
||||
"environment": {"$ref": "#/definitions/list_of_strings"},
|
||||
"storage_context": {"type": "string"},
|
||||
"exec": {"type": "boolean"},
|
||||
"bridge": {"type": "string"},
|
||||
"config_file": {"type": "string"},
|
||||
"containerd": {"type": "string"},
|
||||
"debug": {"type": "boolean"},
|
||||
"exec_root": {"type": "string"},
|
||||
"group": {"type": "string"},
|
||||
"graph": {"type": "string"},
|
||||
"host": {"type": "string"},
|
||||
"live_restore": {"type": "boolean"},
|
||||
"log_driver": {"type": "string"},
|
||||
"log_opts": {"type": "object"},
|
||||
"pid_file": {"type": "string"},
|
||||
"registry_mirror": {"type": "string"},
|
||||
"restart": {"type": "boolean"},
|
||||
"selinux_enabled": {"type": "boolean"},
|
||||
"storage_driver": {"type": "string"},
|
||||
"userland_proxy": {"type": "boolean"}
|
||||
}
|
||||
},
|
||||
|
||||
"ssh_config": {
|
||||
"id": "#/definitions/ssh_config",
|
||||
"type": "object",
|
||||
"additionalProperties": false,
|
||||
|
||||
"properties": {
|
||||
"keys": {"type": "object"}
|
||||
}
|
||||
},
|
||||
|
||||
"state_config": {
|
||||
"id": "#/definitions/state_config",
|
||||
"type": "object",
|
||||
"additionalProperties": false,
|
||||
|
||||
"properties": {
|
||||
"directory": {"type": "string"},
|
||||
"fstype": {"type": "string"},
|
||||
"dev": {"type": "string"},
|
||||
"wait": {"type": "boolean"},
|
||||
"required": {"type": "boolean"},
|
||||
"autoformat": {"$ref": "#/definitions/list_of_strings"},
|
||||
"mdadm_scan": {"type": "boolean"},
|
||||
"script": {"type": "string"},
|
||||
"oem_fstype": {"type": "string"},
|
||||
"oem_dev": {"type": "string"}
|
||||
}
|
||||
},
|
||||
|
||||
"cloud_init_config": {
|
||||
"id": "#/definitions/cloud_init_config",
|
||||
"type": "object",
|
||||
"additionalProperties": false,
|
||||
|
||||
"properties": {
|
||||
"datasources": {"$ref": "#/definitions/list_of_strings"}
|
||||
}
|
||||
},
|
||||
|
||||
"defaults_config": {
|
||||
"id": "#/definitions/defaults_config",
|
||||
"type": "object",
|
||||
"additionalProperties": false,
|
||||
|
||||
"properties": {
|
||||
"hostname": {"type": "string"},
|
||||
"docker": {"type": "object"},
|
||||
"network": {"$ref": "#/definitions/network_config"}
|
||||
}
|
||||
},
|
||||
|
||||
"list_of_strings": {
|
||||
"type": "array",
|
||||
"items": {"type": "string"},
|
||||
"uniqueItems": true
|
||||
}
|
||||
}
|
||||
}
|
||||
|
3
scripts/schema_template
Normal file
3
scripts/schema_template
Normal file
@@ -0,0 +1,3 @@
|
||||
package config
|
||||
|
||||
var schema = `{{.schema}}`
|
Reference in New Issue
Block a user