// ProcessObject checks if the object is valid from this schema's standpoint // and returns an object with defaults set up according to schema's spec func (j *JSONSchema) ProcessObject(in interface{}) (interface{}, error) { result, err := j.schema.Validate(gojsonschema.NewGoLoader(in)) if err != nil { return nil, trace.Wrap(err) } if !result.Valid() { return nil, trace.Wrap(trace.Errorf("errors: %v", schemaErrors(result.Errors()))) } return setDefaults(j.rawSchema, in), nil }
// ParseCommandLine takes a pointer to a function and attempts // to initialize it from environment variables. func ParseCommandLine(v interface{}, args []string) error { app, err := NewCommandLineApp(v) if err != nil { return trace.Wrap(err) } if _, err := app.Parse(args); err != nil { return trace.Wrap(err) } return nil }
// New returns JSON schema created from JSON byte string // returns a valid schema or error if schema is invalid func New(data []byte) (*JSONSchema, error) { j := JSONSchema{} err := json.Unmarshal(data, &j.rawSchema) if err != nil { return nil, trace.Wrap(err) } loader := gojsonschema.NewGoLoader(j.rawSchema) j.schema, err = gojsonschema.NewSchema(loader) if err != nil { return nil, trace.Wrap(err) } return &j, nil }
func (p *cparser) parseEnum(s paramSpec) (Param, error) { var e *enumSpec if err := json.Unmarshal(s.S, &e); err != nil { return nil, trace.Wrap( err, fmt.Sprintf("failed to parse: '%v'", string(s.S))) } if len(e.Values) == 0 { return nil, trace.Errorf("provide at least one value for '%v'", s.Name) } values := make([]string, len(e.Values)) seen := make(map[string]bool, len(e.Values)) for i, v := range e.Values { if v == "" { return nil, trace.Errorf("value can not be an empty string") } if seen[v] { return nil, trace.Errorf("duplicate value: '%v'", v) } values[i] = v } ep := &EnumParam{values: values} ep.paramCommon = s.common() return ep, nil }
// TransformXML parses the XML tree, traverses it and calls TransformFunc // on each XML token, writing the output to the writer, resulting in a // transformed XML tree func TransformXML(decoder *xml.Decoder, encoder *xml.Encoder, fn TransformFunc) error { parentNodes := &NodeList{} for { token, err := decoder.Token() if err != nil { if err != io.EOF { return trace.Wrap(err) } break } for _, t := range fn(parentNodes, token) { if err := encoder.EncodeToken(t); err != nil { return err } } switch e := token.(type) { case xml.StartElement: parentNodes.Push(e) case xml.EndElement: parentNodes.Pop() } } encoder.Flush() return nil }
func (p *cparser) parseKeyVal(s paramSpec) (Param, error) { var k *kvSpec if err := json.Unmarshal(s.S, &k); err != nil { return nil, trace.Wrap( err, fmt.Sprintf("failed to parse: '%v'", string(s.S))) } if len(k.Keys) == 0 { return nil, trace.Errorf("provide at least one key for '%v'", s.Name) } keys := make([]Param, len(k.Keys)) for i, ks := range k.Keys { k, err := p.parseParam(ks, true) if err != nil { return nil, err } keys[i] = k } if err := checkSameNames(keys); err != nil { return nil, err } kv := &KVParam{keys: keys, separator: k.Separator} kv.paramCommon = s.common() return kv, nil }
// SetEnv sets the value from environment variable using json encoding func (kv *KeyValSlice) SetEnv(v string) error { if err := json.Unmarshal([]byte(v), &kv); err != nil { return trace.Wrap( err, "failed to parse environment variable, expected JSON map") } return nil }
func (c *ctx) File(path string) (string, error) { o, err := ioutil.ReadFile(path) if err != nil { return "", trace.Wrap(err, fmt.Sprintf("reading file: %v", path)) } return string(o), nil }
func (c *cliBoolValue) Set(v string) error { boolVal, err := strconv.ParseBool(v) if err != nil { return trace.Wrap(err) } *c.v = boolVal return nil }
// ParseYAML parses yaml-encoded byte string into the struct // passed to the function. // EnableTemplating() argument allows to treat configuration file as a template // for example, it will support {{env "VAR"}} - that will substitute // environment variable "VAR" and pass it to YAML file parser func ParseYAML(data []byte, cfg interface{}, funcArgs ...ParseOption) error { var opts parseOptions for _, fn := range funcArgs { fn(&opts) } var err error if opts.templating { if data, err = renderTemplate(data); err != nil { return trace.Wrap(err) } } if err := yaml.Unmarshal(data, cfg); err != nil { return trace.Wrap(err) } return nil }
func (c *CIDR) Set(v string) error { out, err := ParseCIDR(v) if err != nil { return trace.Wrap(err) } *c = *out return nil }
func ParseVariablesJSON(r io.Reader) (*Config, error) { var variables []paramSpec if err := json.NewDecoder(r).Decode(&variables); err != nil { return nil, trace.Wrap(err) } return newParser().parse(configV1{Params: variables}) }
func ParseJSON(r io.Reader) (*Config, error) { var c *configV1 if err := json.NewDecoder(r).Decode(&c); err != nil { return nil, trace.Wrap(err) } return newParser().parse(*c) }
func (p *cparser) checkName(n string) error { for _, pr := range p.params { if pr.Name() == n { return trace.Errorf("parameter '%v' is already defined", n) } } e, err := parser.ParseExpr(n) if err != nil { return trace.Wrap( err, fmt.Sprintf("failed to parse name: '%v'", n)) } if _, ok := e.(*ast.Ident); !ok { return trace.Wrap( err, fmt.Sprintf("name should be a valid identifier: '%v'", n)) } return nil }
// NewCommandLineApp generates a command line parsing tool based on the struct // that was passed in as a parameter func NewCommandLineApp(v interface{}) (*kingpin.Application, error) { s := reflect.ValueOf(v).Elem() app := kingpin.New("app", "Auto generated command line application") if err := setupApp(app, s); err != nil { return nil, trace.Wrap(err) } return app, nil }
func (c *cliInt32Value) Set(v string) error { intValue, err := strconv.ParseInt(v, 0, 32) if err != nil { return trace.Wrap(err) } *c.v = int32(intValue) return nil }
// Set accepts string with arguments in the form "key:val,key2:val2" func (kv *KeyValSlice) Set(v string) error { if len(*kv) == 0 { *kv = make([]map[string]string, 0) } var i KeyVal if err := i.Set(v); err != nil { return trace.Wrap(err) } *kv = append(*kv, i) return nil }
func renderTemplate(data []byte) ([]byte, error) { t := template.New("tpl") c, err := newCtx() if err != nil { return nil, trace.Wrap(err) } t.Funcs(map[string]interface{}{ "env": c.Env, "file": c.File, }) t, err = t.Parse(string(data)) if err != nil { return nil, trace.Wrap(err) } buf := &bytes.Buffer{} if err := t.Execute(buf, nil); err != nil { return nil, trace.Wrap(err) } return buf.Bytes(), nil }
func (c *cliSliceMapValue) Set(v string) error { if len(*c.v) == 0 { (*c.v) = make([]map[string]string, 0) } var kv map[string]string if err := setMap(&kv, v); err != nil { return trace.Wrap(err) } *c.v = append(*c.v, kv) return nil }
func (p *cparser) parseList(s paramSpec) (Param, error) { var ps *paramSpec if err := json.Unmarshal(s.S, &ps); err != nil { return nil, trace.Wrap(err, "failed to parse: '%v'", string(s.S)) } el, err := p.parseParam(*ps, false) if err != nil { return nil, err } l := &ListParam{el: el} l.paramCommon = s.common() return l, nil }
func (c *Config) ParseVars(vars map[string]string) error { for _, p := range c.Params { val, ok := vars[p.Name()] if !ok { if p.Required() { return trace.Errorf("missing value for required variable: %v", p.Name()) } else { val = p.Default() } } if err := p.Set(val); err != nil { return trace.Wrap(err) } } return nil }
// ProcessObject checks if the object is valid from this schema's standpoint // and returns an object with defaults set up according to schema's spec func (j *JSONSchema) ProcessObject(in interface{}) (interface{}, error) { defaults := setDefaults(j.rawSchema, in) result, err := j.schema.Validate(gojsonschema.NewGoLoader(defaults)) if err != nil { return nil, trace.Wrap(err) } if !result.Valid() { errors := result.Errors() output := make([]string, len(errors)) for i, err := range errors { output[i] = fmt.Sprintf("%v", err) } return nil, trace.Errorf("failed to validate: %v", strings.Join(output, ",")) } return defaults, nil }
func setEnv(v reflect.Value, env map[string]string) error { // for structs, walk every element and parse vType := v.Type() if v.Kind() != reflect.Struct { return nil } for i := 0; i < v.NumField(); i++ { structField := vType.Field(i) field := v.Field(i) if !field.CanSet() { continue } kind := field.Kind() if kind == reflect.Struct { if err := setEnv(field, env); err != nil { return trace.Wrap(err, fmt.Sprintf("failed parsing struct field %v", structField.Name)) } } envKey := structField.Tag.Get("env") if envKey == "" { continue } val, ok := env[envKey] if !ok || val == "" { // assume defaults continue } if field.CanAddr() { if s, ok := field.Addr().Interface().(EnvSetter); ok { if err := s.SetEnv(val); err != nil { return trace.Wrap(err) } continue } if s, ok := field.Addr().Interface().(StringSetter); ok { if err := s.Set(val); err != nil { return trace.Wrap(err) } continue } } switch kind { case reflect.Slice: if _, ok := field.Interface().([]map[string]string); ok { var kv KeyValSlice if err := kv.SetEnv(val); err != nil { return trace.Wrap(err, "error parsing key value list") } field.Set(reflect.ValueOf([]map[string]string(kv))) } case reflect.Map: if _, ok := field.Interface().(map[string]string); ok { var kv KeyVal if err := kv.SetEnv(val); err != nil { return trace.Wrap(err, "error parsing key value list") } field.Set(reflect.ValueOf(map[string]string(kv))) } case reflect.Int, reflect.Int8, reflect.Int16, reflect.Int32, reflect.Int64: intValue, err := strconv.ParseInt(val, 0, field.Type().Bits()) if err != nil { return trace.Wrap(err) } field.SetInt(intValue) case reflect.String: field.SetString(val) case reflect.Bool: boolVal, err := strconv.ParseBool(val) if err != nil { return trace.Wrap( err, fmt.Sprintf("failed parsing struct field %v, expected bool, got '%v'", structField.Name, val)) } field.SetBool(boolVal) } } return nil }
func setupApp(app *kingpin.Application, v reflect.Value) error { // for structs, walk every element and parse vType := v.Type() if v.Kind() != reflect.Struct { return nil } for i := 0; i < v.NumField(); i++ { structField := vType.Field(i) field := v.Field(i) if !field.CanSet() { continue } kind := field.Kind() if kind == reflect.Struct { if err := setupApp(app, field); err != nil { return trace.Wrap(err, fmt.Sprintf("failed parsing struct field %v", structField.Name)) } } cliFlag := structField.Tag.Get("cli") if cliFlag == "" { continue } if !field.CanAddr() { continue } f := app.Flag(cliFlag, cliFlag) fieldPtr := field.Addr().Interface() if setter, ok := fieldPtr.(CLISetter); ok { f.SetValue(&cliValue{setter: setter}) continue } if setter, ok := fieldPtr.(StringSetter); ok { f.SetValue(&cliValue{setter: &cliStringSetter{setter: setter}}) continue } switch ptr := fieldPtr.(type) { case *map[string]string: f.SetValue(&cliMapValue{v: ptr}) continue case *[]map[string]string: f.SetValue(&cliSliceMapValue{v: ptr}) continue case *int: f.SetValue(&cliIntValue{v: ptr}) continue case *int32: f.SetValue(&cliInt32Value{v: ptr}) continue case *int64: f.SetValue(&cliInt64Value{v: ptr}) continue case *string: f.SetValue(&cliStringValue{v: ptr}) case *bool: f.SetValue(&cliBoolValue{v: ptr}) default: return trace.Errorf("unsupported type: %T", ptr) } } return nil }