From cddb7d5d6b2ed0364e95258fd651baed3e9427b0 Mon Sep 17 00:00:00 2001 From: Rintaro Okamura Date: Thu, 7 May 2020 17:34:30 +0900 Subject: [PATCH] :sparkles: Add JSON schema for Vald Helm Chart Signed-off-by: Rintaro Okamura --- Makefile.d/helm.mk | 9 ++ charts/vald/values.yaml | 26 +++++ hack/helm/schema/gen/main.go | 197 +++++++++++++++++++++++++++++++++++ 3 files changed, 232 insertions(+) create mode 100644 hack/helm/schema/gen/main.go diff --git a/Makefile.d/helm.mk b/Makefile.d/helm.mk index d937462086..6c341c0b62 100644 --- a/Makefile.d/helm.mk +++ b/Makefile.d/helm.mk @@ -76,3 +76,12 @@ charts/vald-helm-operator/README.md: \ charts/vald-helm-operator/README.md.gotmpl \ charts/vald-helm-operator/values.yaml helm-docs + +.PHONY: helm/schema/vald +## generate json schema for Vald Helm Chart +helm/schema/vald: charts/vald/values.schema.json + +charts/vald/values.schema.json: \ + charts/vald/values.yaml \ + hack/helm/schema/gen/main.go + go run hack/helm/schema/gen/main.go charts/vald/values.yaml > charts/vald/values.schema.json diff --git a/charts/vald/values.yaml b/charts/vald/values.yaml index 7baa83a083..a7dcf27a4d 100644 --- a/charts/vald/values.yaml +++ b/charts/vald/values.yaml @@ -14,50 +14,75 @@ # limitations under the License. # +# json-schema: {"name": "defaults", "type": "object"} # The values in the defaults section will be used for common settings in each component if not specified in its sections. defaults: + # json-schema: {"name": "defaults.time_zone", "type": "string"} # defaults.time_zone -- Time zone time_zone: UTC + # json-schema: {"name": "defaults.logging", "type": "object"} logging: + # json-schema: {"name": "defaults.logging.logger", "type": "string", "pattern": "glg"} # defaults.logging.logger -- logger name. # currently logger must be `glg`. logger: glg + # json-schema: {"name": "defaults.logging.level", "type": "string", "pattern": "^(debug|info|warn|error|fatal)$"} # defaults.logging.level -- logging level. # logging level must be `debug`, `info`, `warn`, `error` or `fatal`. level: debug + # json-schema: {"name": "defaults.logging.format", "type": "string", "pattern": "^(raw|json)$"} # defaults.logging.format -- logging format. # logging format must be `raw` or `json` format: raw + # json-schema: {"name": "defaults.image", "type": "object"} image: + # json-schema: {"name": "defaults.image.tag", "type": "string"} # defaults.image.tag -- docker image tag tag: v0.0.37 + # json-schema: {"name": "defaults.server_config", "type": "object"} server_config: + # json-schema: {"name": "defaults.server_config.servers", "type": "object"} servers: + # json-schema: {"name": "defaults.server_config.servers.rest", "type": "object"} rest: + # json-schema: {"name": "defaults.server_config.servers.rest.enabled", "type": "boolean"} # defaults.server_config.servers.rest.enabled -- REST server enabled enabled: false + # json-schema: {"name": "defaults.server_config.servers.rest.host", "type": "string"} # defaults.server_config.servers.rest.host -- REST server host host: 0.0.0.0 + # json-schema: {"name": "defaults.server_config.servers.rest.port", "type": "integer"} # defaults.server_config.servers.rest.port -- REST server port port: 8080 + # json-schema: {"name": "defaults.server_config.servers.rest.servicePort", "type": "integer"} # defaults.server_config.servers.rest.servicePort -- REST server service port servicePort: 8080 + # json-schema: {"name": "defaults.server_config.servers.rest.server", "type": "object"} server: + # json-schema: {"name": "defaults.server_config.servers.rest.server.mode", "type": "string"} # defaults.server_config.servers.rest.server.mode -- REST server server mode mode: REST + # json-schema: {"name": "defaults.server_config.servers.rest.server.probe_wait_time", "type": "string"} # defaults.server_config.servers.rest.server.probe_wait_time -- REST server probe wait time probe_wait_time: 3s + # json-schema: {"name": "defaults.server_config.servers.rest.server.http", "type": "object"} http: + # json-schema: {"name": "defaults.server_config.servers.rest.server.shutdown_duration", "type": "string"} # defaults.server_config.servers.rest.server.http.shutdown_duration -- REST server shutdown duration shutdown_duration: 5s + # json-schema: {"name": "defaults.server_config.servers.rest.server.handler_timeout", "type": "string"} # defaults.server_config.servers.rest.server.http.handler_timeout -- REST server handler timeout handler_timeout: 5s + # json-schema: {"name": "defaults.server_config.servers.rest.server.idle_timeout", "type": "string"} # defaults.server_config.servers.rest.server.http.idle_timeout -- REST server idle timeout idle_timeout: 2s + # json-schema: {"name": "defaults.server_config.servers.rest.server.read_header_timeout", "type": "string"} # defaults.server_config.servers.rest.server.http.read_header_timeout -- REST server read header timeout read_header_timeout: 1s + # json-schema: {"name": "defaults.server_config.servers.rest.server.read_timeout", "type": "string"} # defaults.server_config.servers.rest.server.http.read_timeout -- REST server read timeout read_timeout: 1s + # json-schema: {"name": "defaults.server_config.servers.rest.server.write_timeout", "type": "string"} # defaults.server_config.servers.rest.server.http.write_timeout -- REST server write timeout write_timeout: 1s grpc: @@ -410,6 +435,7 @@ defaults: # defaults.observability.jaeger.buffer_max_count -- Jaeger buffer max count buffer_max_count: 10 +# json-schema: {"name": "gateway", "type": "object"} gateway: # gateway.enabled -- gateway enabled enabled: true diff --git a/hack/helm/schema/gen/main.go b/hack/helm/schema/gen/main.go new file mode 100644 index 0000000000..0a609e2f80 --- /dev/null +++ b/hack/helm/schema/gen/main.go @@ -0,0 +1,197 @@ +// +// Copyright (C) 2019-2020 Vdaas.org Vald team ( kpango, rinx, kmrmt ) +// +// 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 +// +// https://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 main + +import ( + "bufio" + "encoding/json" + "fmt" + "os" + "strings" + + "github.com/vdaas/vald/internal/errors" + "github.com/vdaas/vald/internal/log" +) + +const ( + objectType = "object" + arrayType = "array" + stringType = "string" + intType = "integer" + boolType = "boolean" + + prefix = "# json-schema: " +) + +type VSchema struct { + Name string `json:"name"` + Type string `json:"type"` + Pattern string `json:"pattern,omitempty"` +} + +type Root struct { + SchemaKeyword string `json:"$schema"` + Title string `json:"title"` + Schema +} + +type Schema struct { + Type string `json:"type"` + Description string `json:"description,omitempty"` + Pattern string `json:"pattern,omitempty"` + Items *Schema `json:"items,omitempty"` + Required []string `json:"required,omitempty"` + Properties map[string]*Schema `json:"properties,omitempty"` +} + +func main() { + if len(os.Args) < 2 { + log.Fatal(errors.New("invalid argument: must be specify path to the values.yaml")) + } + err := genJSONSchema(os.Args[1]) + if err != nil { + log.Fatal(err) + } +} + +func genJSONSchema(path string) error { + f, err := os.Open(path) + if err != nil { + return errors.Errorf("cannot open %s", path) + } + defer func() { + err := f.Close() + if err != nil { + log.Fatal(err) + } + }() + + ls := make([]VSchema, 0) + sc := bufio.NewScanner(f) + for sc.Scan() { + tx := strings.TrimLeft(sc.Text(), " ") + if strings.HasPrefix(tx, prefix) { + var l VSchema + err = json.Unmarshal([]byte(strings.TrimPrefix(tx, prefix)), &l) + if err != nil { + log.Error(err) + } + ls = append(ls, l) + } + } + + schemas, err := objectProperties(ls) + if err != nil { + return errors.Errorf("error: %s", err) + } + + json, err := json.Marshal(newRoot(schemas)) + // json, err := json.Marshal(ls) + if err != nil { + return errors.Errorf("error: %s", err) + } + + fmt.Println(string(json)) + + return nil +} + +func objectProperties(ls []VSchema) (map[string]*Schema, error) { + if len(ls) <= 0 { + return nil, errors.New("empty list") + } + + groups := make(map[string][]VSchema) + for _, l := range ls { + root := strings.Split(l.Name, ".") + groups[root[0]] = append(groups[root[0]], l) + } + + schemas := make(map[string]*Schema) + for k, v := range groups { + s, err := genNode(v) + if err != nil { + return nil, errors.Errorf("error: %s", err) + } + schemas[k] = s + } + + return schemas, nil +} + +func genNode(ls []VSchema) (*Schema, error) { + if len(ls) <= 0 { + return nil, errors.New("empty list") + } + + l := ls[0] + switch l.Type { + case objectType: + if len(ls) <= 1 { + return &Schema{ + Type: objectType, + }, nil + } + + nls := make([]VSchema, 0, len(ls[1:])) + for _, nl := range ls[1:] { + nl.Name = strings.TrimLeft(strings.TrimPrefix(nl.Name, l.Name), ".") + nls = append(nls, nl) + } + + ps, err := objectProperties(nls) + if err != nil { + return nil, errors.Errorf("error: %s", err) + } + return &Schema{ + Type: objectType, + Properties: ps, + }, nil + case arrayType: + return &Schema{ + Type: arrayType, + }, nil + case stringType: + return &Schema{ + Type: stringType, + Pattern: l.Pattern, + }, nil + case intType: + return &Schema{ + Type: intType, + }, nil + case boolType: + return &Schema{ + Type: boolType, + }, nil + default: + return &Schema{ + Type: l.Type, + }, nil + } +} + +func newRoot(schemas map[string]*Schema) *Root { + return &Root{ + SchemaKeyword: "http://json-schema.org/draft-07/schema#", + Title: "Values", + Schema: Schema{ + Type: objectType, + Properties: schemas, + }, + } +}