cmd/hub/manifest/validate.go (109 lines of code) (raw):

// Copyright (c) 2022 EPAM Systems, Inc. // // This Source Code Form is subject to the terms of the Mozilla Public // License, v. 2.0. If a copy of the MPL was not distributed with this // file, You can obtain one at http://mozilla.org/MPL/2.0/. package manifest import ( _ "embed" "fmt" "log" "strings" "github.com/xeipuuv/gojsonschema" "gopkg.in/yaml.v2" "github.com/epam/hubctl/cmd/hub/config" "github.com/epam/hubctl/cmd/hub/util" ) //go:embed manifest.schema.json var manifestSchemaFile []byte var schemaLoader gojsonschema.JSONLoader func validateManifest(name string, yamlDocument []byte) { err := validate(name, yamlDocument) if err != nil { util.Warn("Unable to validate `%s`: %v", name, err) } } func validate(name string, yamlDocument []byte) error { schema, err := manifestSchema() if err != nil { return err } var manifest interface{} err = yaml.Unmarshal(yamlDocument, &manifest) if err != nil { return err } converted, err := convertToStringKeysRecursive(manifest, "") if err != nil { return err } data := gojsonschema.NewGoLoader(converted) result, err := gojsonschema.Validate(schema, data) if err != nil { return err } if result.Valid() { if config.Trace { log.Printf("`%s` schema is valid", name) } } else { sep := "\n\t- " var errs []string for _, jserr := range result.Errors() { errs = append(errs, jserr.String()) } util.Warn("`%s` schema is not valid:%s%s", name, sep, strings.Join(errs, sep)) if config.Trace { log.Printf("Document validated:\n%+v", converted) } } return nil } func manifestSchema() (gojsonschema.JSONLoader, error) { if schemaLoader == nil { schemaLoader = gojsonschema.NewBytesLoader(manifestSchemaFile) } return schemaLoader, nil } //lint:ignore U1000 still needed? func convertForValidator(value interface{}) (interface{}, error) { return convertToStringKeysRecursive(value, "") } func convertToStringKeysRecursive(value interface{}, keyPrefix string) (interface{}, error) { if mapping, ok := value.(map[interface{}]interface{}); ok { dict := make(map[string]interface{}) for key, entry := range mapping { str, ok := key.(string) if !ok { return nil, formatInvalidKeyError(keyPrefix, key) } var newKeyPrefix string if keyPrefix == "" { newKeyPrefix = str } else { newKeyPrefix = fmt.Sprintf("%s.%s", keyPrefix, str) } convertedEntry, err := convertToStringKeysRecursive(entry, newKeyPrefix) if err != nil { return nil, err } dict[str] = convertedEntry } return dict, nil } if list, ok := value.([]interface{}); ok { var convertedList []interface{} for index, entry := range list { newKeyPrefix := fmt.Sprintf("%s[%d]", keyPrefix, index) convertedEntry, err := convertToStringKeysRecursive(entry, newKeyPrefix) if err != nil { return nil, err } convertedList = append(convertedList, convertedEntry) } return convertedList, nil } return value, nil } func formatInvalidKeyError(keyPrefix string, key interface{}) error { var location string if keyPrefix == "" { location = "at top level" } else { location = fmt.Sprintf("in %s", keyPrefix) } return fmt.Errorf("Non-string key %s: %#v", location, key) }