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 }
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 }
func ConvertAs2_0_0(in types.Config) (ignTypes.Config, report.Report) { out := ignTypes.Config{ Ignition: ignTypes.Ignition{ Version: ignTypes.IgnitionVersion{Major: 2, Minor: 0}, }, } for _, ref := range in.Ignition.Config.Append { newRef, err := convertConfigReference(ref) if err != nil { return ignTypes.Config{}, report.ReportFromError(err, report.EntryError) } out.Ignition.Config.Append = append(out.Ignition.Config.Append, newRef) } if in.Ignition.Config.Replace != nil { newRef, err := convertConfigReference(*in.Ignition.Config.Replace) if err != nil { return ignTypes.Config{}, report.ReportFromError(err, report.EntryError) } out.Ignition.Config.Replace = &newRef } for _, disk := range in.Storage.Disks { newDisk := ignTypes.Disk{ Device: ignTypes.Path(disk.Device), WipeTable: disk.WipeTable, } for _, partition := range disk.Partitions { size, err := convertPartitionDimension(partition.Size) if err != nil { return ignTypes.Config{}, report.ReportFromError(err, report.EntryError) } start, err := convertPartitionDimension(partition.Start) if err != nil { return ignTypes.Config{}, report.ReportFromError(err, report.EntryError) } newDisk.Partitions = append(newDisk.Partitions, ignTypes.Partition{ Label: ignTypes.PartitionLabel(partition.Label), Number: partition.Number, Size: size, Start: start, TypeGUID: ignTypes.PartitionTypeGUID(partition.TypeGUID), }) } out.Storage.Disks = append(out.Storage.Disks, newDisk) } for _, array := range in.Storage.Arrays { newArray := ignTypes.Raid{ Name: array.Name, Level: array.Level, Spares: array.Spares, } for _, device := range array.Devices { newArray.Devices = append(newArray.Devices, ignTypes.Path(device)) } out.Storage.Arrays = append(out.Storage.Arrays, newArray) } for _, filesystem := range in.Storage.Filesystems { newFilesystem := ignTypes.Filesystem{ Name: filesystem.Name, Path: func(p ignTypes.Path) *ignTypes.Path { if p == "" { return nil } return &p }(ignTypes.Path(filesystem.Path)), } if filesystem.Mount != nil { newFilesystem.Mount = &ignTypes.FilesystemMount{ Device: ignTypes.Path(filesystem.Mount.Device), Format: ignTypes.FilesystemFormat(filesystem.Mount.Format), } if filesystem.Mount.Create != nil { newFilesystem.Mount.Create = &ignTypes.FilesystemCreate{ Force: filesystem.Mount.Create.Force, Options: ignTypes.MkfsOptions(filesystem.Mount.Create.Options), } } } out.Storage.Filesystems = append(out.Storage.Filesystems, newFilesystem) } for _, file := range in.Storage.Files { newFile := ignTypes.File{ Filesystem: file.Filesystem, Path: ignTypes.Path(file.Path), Mode: ignTypes.FileMode(file.Mode), User: ignTypes.FileUser{Id: file.User.Id}, Group: ignTypes.FileGroup{Id: file.Group.Id}, } if file.Contents.Inline != "" { newFile.Contents = ignTypes.FileContents{ Source: ignTypes.Url{ Scheme: "data", Opaque: "," + dataurl.EscapeString(file.Contents.Inline), }, } } if file.Contents.Remote.Url != "" { source, err := url.Parse(file.Contents.Remote.Url) if err != nil { return ignTypes.Config{}, report.ReportFromError(err, report.EntryError) } newFile.Contents = ignTypes.FileContents{Source: ignTypes.Url(*source)} } if newFile.Contents == (ignTypes.FileContents{}) { newFile.Contents = ignTypes.FileContents{ Source: ignTypes.Url{ Scheme: "data", Opaque: ",", }, } } newFile.Contents.Compression = ignTypes.Compression(file.Contents.Remote.Compression) newFile.Contents.Verification = convertVerification(file.Contents.Remote.Verification) out.Storage.Files = append(out.Storage.Files, newFile) } for _, unit := range in.Systemd.Units { newUnit := ignTypes.SystemdUnit{ Name: ignTypes.SystemdUnitName(unit.Name), Enable: unit.Enable, Mask: unit.Mask, Contents: unit.Contents, } for _, dropIn := range unit.DropIns { newUnit.DropIns = append(newUnit.DropIns, ignTypes.SystemdUnitDropIn{ Name: ignTypes.SystemdUnitDropInName(dropIn.Name), Contents: dropIn.Contents, }) } out.Systemd.Units = append(out.Systemd.Units, newUnit) } for _, unit := range in.Networkd.Units { out.Networkd.Units = append(out.Networkd.Units, ignTypes.NetworkdUnit{ Name: ignTypes.NetworkdUnitName(unit.Name), Contents: unit.Contents, }) } for _, user := range in.Passwd.Users { newUser := ignTypes.User{ Name: user.Name, PasswordHash: user.PasswordHash, SSHAuthorizedKeys: user.SSHAuthorizedKeys, } if user.Create != nil { newUser.Create = &ignTypes.UserCreate{ Uid: user.Create.Uid, GECOS: user.Create.GECOS, Homedir: user.Create.Homedir, NoCreateHome: user.Create.NoCreateHome, PrimaryGroup: user.Create.PrimaryGroup, Groups: user.Create.Groups, NoUserGroup: user.Create.NoUserGroup, System: user.Create.System, NoLogInit: user.Create.NoLogInit, Shell: user.Create.Shell, } } out.Passwd.Users = append(out.Passwd.Users, newUser) } for _, group := range in.Passwd.Groups { out.Passwd.Groups = append(out.Passwd.Groups, ignTypes.Group{ Name: group.Name, Gid: group.Gid, PasswordHash: group.PasswordHash, System: group.System, }) } r := validate.ValidateWithoutSource(reflect.ValueOf(out)) if r.IsFatal() { return ignTypes.Config{}, r } return out, r }