github.com/SagerNet/gvisor@v0.0.0-20210707092255-7731c139d75c/tools/yamltest/main.go (about) 1 // Copyright 2020 The gVisor Authors. 2 // 3 // Licensed under the Apache License, Version 2.0 (the "License"); 4 // you may not use this file except in compliance with the License. 5 // You may obtain a copy of the License at 6 // 7 // http://www.apache.org/licenses/LICENSE-2.0 8 // 9 // Unless required by applicable law or agreed to in writing, software 10 // distributed under the License is distributed on an "AS IS" BASIS, 11 // WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 12 // See the License for the specific language governing permissions and 13 // limitations under the License. 14 15 // Binary yamltest does strict yaml parsing and validation. 16 package main 17 18 import ( 19 "encoding/json" 20 "errors" 21 "flag" 22 "fmt" 23 "os" 24 25 "github.com/xeipuuv/gojsonschema" 26 yaml "gopkg.in/yaml.v2" 27 ) 28 29 func fixup(v interface{}) (interface{}, error) { 30 switch x := v.(type) { 31 case map[interface{}]interface{}: 32 // Coerse into a string-based map, required for yaml. 33 strMap := make(map[string]interface{}) 34 for k, v := range x { 35 strK, ok := k.(string) 36 if !ok { 37 // This cannot be converted to JSON at all. 38 return nil, fmt.Errorf("invalid key %T in (%#v)", k, x) 39 } 40 fv, err := fixup(v) 41 if err != nil { 42 return nil, fmt.Errorf(".%s%w", strK, err) 43 } 44 strMap[strK] = fv 45 } 46 return strMap, nil 47 case []interface{}: 48 for i := range x { 49 fv, err := fixup(x[i]) 50 if err != nil { 51 return nil, fmt.Errorf("[%d]%w", i, err) 52 } 53 x[i] = fv 54 } 55 return x, nil 56 default: 57 return v, nil 58 } 59 } 60 61 func loadFile(filename string) (gojsonschema.JSONLoader, error) { 62 f, err := os.Open(filename) 63 if err != nil { 64 return nil, err 65 } 66 defer f.Close() 67 dec := yaml.NewDecoder(f) 68 dec.SetStrict(true) 69 var object interface{} 70 if err := dec.Decode(&object); err != nil { 71 return nil, err 72 } 73 fixedObject, err := fixup(object) // For serialization. 74 if err != nil { 75 return nil, err 76 } 77 bytes, err := json.Marshal(fixedObject) 78 if err != nil { 79 return nil, err 80 } 81 return gojsonschema.NewStringLoader(string(bytes)), nil 82 } 83 84 var schema = flag.String("schema", "", "path to JSON schema file.") 85 86 func main() { 87 flag.Parse() 88 if *schema == "" || len(flag.Args()) == 0 { 89 flag.Usage() 90 os.Exit(2) 91 } 92 93 // Construct our schema loader. 94 schemaLoader := gojsonschema.NewReferenceLoader(fmt.Sprintf("file://%s", *schema)) 95 96 // Parse all documents. 97 allErrors := make(map[string][]error) 98 for _, filename := range flag.Args() { 99 // Record the filename with an empty slice for below, where 100 // we will emit all files (even those without any errors). 101 allErrors[filename] = nil 102 documentLoader, err := loadFile(filename) 103 if err != nil { 104 allErrors[filename] = append(allErrors[filename], err) 105 continue 106 } 107 result, err := gojsonschema.Validate(schemaLoader, documentLoader) 108 if err != nil { 109 allErrors[filename] = append(allErrors[filename], err) 110 continue 111 } 112 for _, desc := range result.Errors() { 113 allErrors[filename] = append(allErrors[filename], errors.New(desc.String())) 114 } 115 } 116 117 // Print errors in yaml format. 118 totalErrors := 0 119 for filename, errs := range allErrors { 120 totalErrors += len(errs) 121 if len(errs) == 0 { 122 fmt.Fprintf(os.Stderr, "%s: ✓\n", filename) 123 continue 124 } 125 fmt.Fprintf(os.Stderr, "%s:\n", filename) 126 for _, err := range errs { 127 fmt.Fprintf(os.Stderr, "- %s\n", err) 128 } 129 } 130 if totalErrors != 0 { 131 os.Exit(1) 132 } 133 }