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  }