// wrapper for errorutil that handles missing sources sanely and resets the reader afterwards func posFromOffset(offset int, source io.ReadSeeker) (int, int, string) { if source == nil { return 0, 0, "" } line, col, highlight := errorutil.HighlightBytePosition(source, int64(offset)) source.Seek(0, 0) // Reset the reader to the start so the next call isn't relative to this position return line, col, highlight }
func (pm *PodManifest) UnmarshalJSON(data []byte) error { p := podManifest(*pm) err := json.Unmarshal(data, &p) if err != nil { if serr, ok := err.(*json.SyntaxError); ok { line, col, highlight := errorutil.HighlightBytePosition(bytes.NewReader(data), serr.Offset) return fmt.Errorf("\nError at line %d, column %d\n%s%v", line, col, highlight, err) } return err } npm := PodManifest(p) if err := npm.assertValid(); err != nil { return err } *pm = npm return nil }
func ParseFromLatest(rawConfig []byte) (config types.Config, err error) { if err = json.Unmarshal(rawConfig, &config); err == nil { err = config.Ignition.Version.AssertValid() } else if isEmpty(rawConfig) { err = ErrEmpty } else if isCloudConfig(rawConfig) { err = ErrCloudConfig } else if isScript(rawConfig) { err = ErrScript } if serr, ok := err.(*json.SyntaxError); ok { line, col, highlight := errorutil.HighlightBytePosition(bytes.NewReader(rawConfig), serr.Offset) err = fmt.Errorf("error at line %d, column %d\n%s%v", line, col, highlight, err) } return }
func (im *ImageManifest) UnmarshalJSON(data []byte) error { a := imageManifest(*im) err := json.Unmarshal(data, &a) if err != nil { if serr, ok := err.(*json.SyntaxError); ok { line, col, highlight := errorutil.HighlightBytePosition(bytes.NewReader(data), serr.Offset) return fmt.Errorf("\nError at line %d, column %d\n%s%v", line, col, highlight, err) } return err } nim := ImageManifest(a) if err := nim.assertValid(); err != nil { return err } *im = nim return nil }
// Decodes and evaluates a json config file, watching for include cycles. func (c *ConfigParser) recursiveReadJSON(configPath string) (decodedObject map[string]interface{}, err error) { if configPath != "" { absConfigPath, err := filepath.Abs(configPath) if err != nil { return nil, fmt.Errorf("Failed to expand absolute path for %s", configPath) } if c.touchedFiles[absConfigPath] { return nil, fmt.Errorf("ConfigParser include cycle detected reading config: %v", absConfigPath) } c.touchedFiles[absConfigPath] = true c.includeStack.Push(absConfigPath) defer c.includeStack.Pop() } var f File if f, err = c.open(configPath); err != nil { return nil, fmt.Errorf("Failed to open config: %v", err) } defer f.Close() decodedObject = make(map[string]interface{}) dj := json.NewDecoder(f) if err = dj.Decode(&decodedObject); err != nil { extra := "" if serr, ok := err.(*json.SyntaxError); ok { if _, serr := f.Seek(0, os.SEEK_SET); serr != nil { log.Fatalf("seek error: %v", serr) } line, col, highlight := errorutil.HighlightBytePosition(f, serr.Offset) extra = fmt.Sprintf(":\nError at line %d, column %d (file offset %d):\n%s", line, col, serr.Offset, highlight) } return nil, fmt.Errorf("error parsing JSON object in config file %s%s\n%v", f.Name(), extra, err) } if err = c.evaluateExpressions(decodedObject, nil, false); err != nil { return nil, fmt.Errorf("error expanding JSON config expressions in %s:\n%v", f.Name(), err) } return decodedObject, nil }
func ParseFromLatest(rawConfig []byte) (types.Config, report.Report, error) { if isEmpty(rawConfig) { return types.Config{}, report.Report{}, ErrEmpty } else if isCloudConfig(rawConfig) { return types.Config{}, report.Report{}, ErrCloudConfig } else if isScript(rawConfig) { return types.Config{}, report.Report{}, ErrScript } var err error var config types.Config // These errors are fatal and the config should not be further validated if err = json.Unmarshal(rawConfig, &config); err == nil { versionReport := config.Ignition.Version.Validate() if versionReport.IsFatal() { return types.Config{}, versionReport, ErrInvalid } } // Handle json syntax and type errors first, since they are fatal but have offset info if serr, ok := err.(*json.SyntaxError); ok { line, col, highlight := errorutil.HighlightBytePosition(bytes.NewReader(rawConfig), serr.Offset) return types.Config{}, report.Report{ Entries: []report.Entry{{ Kind: report.EntryError, Message: serr.Error(), Line: line, Column: col, Highlight: highlight, }}, }, ErrInvalid } if terr, ok := err.(*json.UnmarshalTypeError); ok { line, col, highlight := errorutil.HighlightBytePosition(bytes.NewReader(rawConfig), terr.Offset) return types.Config{}, report.Report{ Entries: []report.Entry{{ Kind: report.EntryError, Message: terr.Error(), Line: line, Column: col, Highlight: highlight, }}, }, ErrInvalid } // Handle other fatal errors (i.e. invalid version) if err != nil { return types.Config{}, report.ReportFromError(err, report.EntryError), err } // Unmarshal again to a json.Node to get offset information for building a report var ast json.Node var r report.Report configValue := reflect.ValueOf(config) if err := json.Unmarshal(rawConfig, &ast); err != nil { r.Add(report.Entry{ Kind: report.EntryWarning, Message: "Ignition could not unmarshal your config for reporting line numbers. This should never happen. Please file a bug.", }) r.Merge(validate.ValidateWithoutSource(configValue)) } else { r.Merge(validate.Validate(configValue, astjson.FromJsonRoot(ast), bytes.NewReader(rawConfig))) } if r.IsFatal() { return types.Config{}, r, ErrInvalid } return config, r, nil }