Example #1
0
func Parse(data []byte) (types.Config, report.Report) {
	var cfg types.Config
	var r report.Report

	if err := yaml.Unmarshal(data, &cfg); err != nil {
		return types.Config{}, report.ReportFromError(err, report.EntryError)
	}

	nodes := yaml.UnmarshalToNode(data)
	if nodes == nil {
		r.Add(report.Entry{
			Kind:    report.EntryWarning,
			Message: "Configuration is empty",
		})
		r.Merge(validate.ValidateWithoutSource(reflect.ValueOf(cfg)))
	} else {
		root, err := fromYamlDocumentNode(*nodes)
		if err != nil {
			return types.Config{}, report.ReportFromError(err, report.EntryError)
		}

		r.Merge(validate.Validate(reflect.ValueOf(cfg), root, nil))
	}

	if r.IsFatal() {
		return types.Config{}, r
	}
	return cfg, r
}
Example #2
0
File: disk.go Project: coreos/fuze
func (n Disk) Validate() report.Report {
	r := report.Report{}
	if len(n.Device) == 0 {
		r.Add(report.Entry{
			Message: "disk device is required",
			Kind:    report.EntryError,
		})
	}
	if n.partitionNumbersCollide() {
		r.Add(report.Entry{
			Message: fmt.Sprintf("disk %q: partition numbers collide", n.Device),
			Kind:    report.EntryError,
		})
	}
	if n.partitionsOverlap() {
		r.Add(report.Entry{
			Message: fmt.Sprintf("disk %q: partitions overlap", n.Device),
			Kind:    report.EntryError,
		})
	}
	if n.partitionsMisaligned() {
		r.Add(report.Entry{
			Message: fmt.Sprintf("disk %q: partitions misaligned", n.Device),
			Kind:    report.EntryError,
		})
	}
	// Disks which have no errors at this point will likely succeed in sgdisk
	return r
}
Example #3
0
func checkDuplicateFilesystems(cfg Config, r *report.Report) {
	filesystems := map[string]struct{}{"root": {}}
	for _, filesystem := range cfg.Storage.Filesystems {
		if _, ok := filesystems[filesystem.Name]; ok {
			r.Add(report.Entry{
				Kind:    report.EntryWarning,
				Message: fmt.Sprintf("Filesystem %q shadows exising filesystem definition", filesystem.Name),
			})
		}
		filesystems[filesystem.Name] = struct{}{}
	}
}
Example #4
0
func checkFilesFilesystems(cfg Config, r *report.Report) {
	filesystems := map[string]struct{}{"root": {}}
	for _, filesystem := range cfg.Storage.Filesystems {
		filesystems[filesystem.Name] = struct{}{}
	}
	for _, file := range cfg.Storage.Files {
		if file.Filesystem == "" {
			// Filesystem was not specified. This is an error, but its handled in types.File's Validate, not here
			continue
		}
		_, ok := filesystems[file.Filesystem]
		if !ok {
			r.Add(report.Entry{
				Kind: report.EntryWarning,
				Message: fmt.Sprintf("File %q references nonexistent filesystem %q. (This is ok if it is defined in a referenced config)",
					file.Path, file.Filesystem),
			})
		}
	}
}
Example #5
0
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
}
Example #6
0
func validateStruct(vObj reflect.Value, ast AstNode, source io.ReadSeeker) report.Report {
	r := report.Report{}

	// isFromObject will be true if this struct was unmarshalled from a JSON object.
	keys, isFromObject := map[string]AstNode{}, false
	if ast != nil {
		keys, isFromObject = ast.KeyValueMap()
	}

	// Maintain a set of key's that have been used.
	usedKeys := map[string]struct{}{}

	// Maintain a list of all the tags in the struct for fuzzy matching later.
	tags := []string{}

	for _, f := range getFields(vObj) {
		// Default to nil AstNode if the field's corrosponding node cannot be found.
		var sub_node AstNode
		// Default to passing a nil source if the field's corrosponding node cannot be found.
		// This ensures the line numbers reported from all sub-structs are 0 and will be changed by AddPosition
		var src io.ReadSeeker

		// Try to determine the json.Node that corrosponds with the struct field
		if isFromObject {
			tag := strings.SplitN(f.Type.Tag.Get(ast.Tag()), ",", 2)[0]
			// Save the tag so we have a list of all the tags in the struct
			tags = append(tags, tag)
			// mark that this key was used
			usedKeys[tag] = struct{}{}

			if sub, ok := keys[tag]; ok {
				// Found it
				sub_node = sub
				src = source
			}
		}
		sub_report := Validate(f.Value, sub_node, src)
		// Default to deepest node if the node's type isn't an object,
		// such as when a json string actually unmarshal to structs (like with version)
		line, col := 0, 0
		if ast != nil {
			line, col, _ = ast.ValueLineCol(src)
		}
		sub_report.AddPosition(line, col, "")
		r.Merge(sub_report)
	}
	if !isFromObject {
		// If this struct was not unmarshalled from a JSON object, there cannot be unused keys.
		return r
	}

	for k, v := range keys {
		if _, hasKey := usedKeys[k]; hasKey {
			continue
		}
		line, col, highlight := v.KeyLineCol(source)
		typo := similar(k, tags)

		r.Add(report.Entry{
			Kind:      report.EntryWarning,
			Message:   fmt.Sprintf("Config has unrecognized key: %s", k),
			Line:      line,
			Column:    col,
			Highlight: highlight,
		})

		if typo != "" {
			r.Add(report.Entry{
				Kind:      report.EntryInfo,
				Message:   fmt.Sprintf("Did you mean %s instead of %s", typo, k),
				Line:      line,
				Column:    col,
				Highlight: highlight,
			})
		}
	}

	return r
}