func ConvertAs2_0_0(in Config) (types.Config, error) { out := types.Config{ Ignition: types.Ignition{ Version: types.IgnitionVersion{Major: 2, Minor: 0}, }, } for _, ref := range in.Ignition.Config.Append { newRef, err := convertConfigReference(ref) if err != nil { return types.Config{}, err } 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 types.Config{}, err } out.Ignition.Config.Replace = &newRef } for _, disk := range in.Storage.Disks { newDisk := types.Disk{ Device: types.Path(disk.Device), WipeTable: disk.WipeTable, } for _, partition := range disk.Partitions { size, err := convertPartitionDimension(partition.Size) if err != nil { return types.Config{}, err } start, err := convertPartitionDimension(partition.Start) if err != nil { return types.Config{}, err } newDisk.Partitions = append(newDisk.Partitions, types.Partition{ Label: types.PartitionLabel(partition.Label), Number: partition.Number, Size: size, Start: start, TypeGUID: types.PartitionTypeGUID(partition.TypeGUID), }) } out.Storage.Disks = append(out.Storage.Disks, newDisk) } for _, array := range in.Storage.Arrays { newArray := types.Raid{ Name: array.Name, Level: array.Level, Spares: array.Spares, } for _, device := range array.Devices { newArray.Devices = append(newArray.Devices, types.Path(device)) } out.Storage.Arrays = append(out.Storage.Arrays, newArray) } for _, filesystem := range in.Storage.Filesystems { newFilesystem := types.Filesystem{ Name: filesystem.Name, Path: func(p types.Path) *types.Path { if p == "" { return nil } return &p }(types.Path(filesystem.Path)), } if filesystem.Mount != nil { newFilesystem.Mount = &types.FilesystemMount{ Device: types.Path(filesystem.Mount.Device), Format: types.FilesystemFormat(filesystem.Mount.Format), } if filesystem.Mount.Create != nil { newFilesystem.Mount.Create = &types.FilesystemCreate{ Force: filesystem.Mount.Create.Force, Options: types.MkfsOptions(filesystem.Mount.Create.Options), } } } out.Storage.Filesystems = append(out.Storage.Filesystems, newFilesystem) } for _, file := range in.Storage.Files { newFile := types.File{ Filesystem: file.Filesystem, Path: types.Path(file.Path), Mode: types.FileMode(file.Mode), User: types.FileUser{Id: file.User.Id}, Group: types.FileGroup{Id: file.Group.Id}, } if file.Contents.Inline != "" { newFile.Contents = types.FileContents{ Source: types.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 types.Config{}, err } newFile.Contents = types.FileContents{Source: types.Url(*source)} } if newFile.Contents == (types.FileContents{}) { newFile.Contents = types.FileContents{ Source: types.Url{ Scheme: "data", Opaque: ",", }, } } newFile.Contents.Compression = types.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 := types.SystemdUnit{ Name: types.SystemdUnitName(unit.Name), Enable: unit.Enable, Mask: unit.Mask, Contents: unit.Contents, } for _, dropIn := range unit.DropIns { newUnit.DropIns = append(newUnit.DropIns, types.SystemdUnitDropIn{ Name: types.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, types.NetworkdUnit{ Name: types.NetworkdUnitName(unit.Name), Contents: unit.Contents, }) } for _, user := range in.Passwd.Users { newUser := types.User{ Name: user.Name, PasswordHash: user.PasswordHash, SSHAuthorizedKeys: user.SSHAuthorizedKeys, } if user.Create != nil { newUser.Create = &types.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, types.Group{ Name: group.Name, Gid: group.Gid, PasswordHash: group.PasswordHash, System: group.System, }) } if err := out.AssertValid(); err != nil { return types.Config{}, err } return out, nil }
func TestParseAsV2_0_0(t *testing.T) { type in struct { data string } type out struct { cfg types.Config err error } tests := []struct { in in out out }{ { in: in{data: ``}, out: out{cfg: types.Config{Ignition: types.Ignition{Version: types.IgnitionVersion{Major: 2}}}}, }, // Errors { in: in{data: `foo:`}, out: out{err: ErrKeysUnrecognized{"foo"}}, }, { in: in{data: ` networkd: units: - name: bad.blah contents: not valid `}, out: out{err: errors.New("invalid networkd unit extension")}, }, // Config { in: in{data: ` ignition: config: append: - source: http://example.com/test1 verification: hash: function: sha512 sum: 00000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000 - source: http://example.com/test2 replace: source: http://example.com/test3 verification: hash: function: sha512 sum: 00000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000 `}, out: out{cfg: types.Config{ Ignition: types.Ignition{ Version: types.IgnitionVersion{Major: 2}, Config: types.IgnitionConfig{ Append: []types.ConfigReference{ { Source: types.Url{ Scheme: "http", Host: "example.com", Path: "/test1", }, Verification: types.Verification{ Hash: &types.Hash{ Function: "sha512", Sum: "00000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000", }, }, }, { Source: types.Url{ Scheme: "http", Host: "example.com", Path: "/test2", }, }, }, Replace: &types.ConfigReference{ Source: types.Url{ Scheme: "http", Host: "example.com", Path: "/test3", }, Verification: types.Verification{ Hash: &types.Hash{ Function: "sha512", Sum: "00000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000", }, }, }, }, }, }}, }, // Storage { in: in{data: ` storage: disks: - device: /dev/sda wipe_table: true partitions: - label: ROOT number: 7 size: 100MB start: 50MB type_guid: 11111111-1111-1111-1111-111111111111 - label: DATA number: 12 size: 1GB start: 300MB type_guid: 00000000-0000-0000-0000-000000000000 - label: NOTHING - device: /dev/sdb wipe_table: true raid: - name: fast level: raid0 devices: - /dev/sdc - /dev/sdd - name: durable level: raid1 devices: - /dev/sde - /dev/sdf - /dev/sdg spares: 1 filesystems: - name: filesystem1 mount: device: /dev/disk/by-partlabel/ROOT format: btrfs create: force: true options: - -L - ROOT - name: filesystem2 mount: device: /dev/disk/by-partlabel/DATA format: ext4 - name: filesystem3 path: /sysroot files: - path: /opt/file1 filesystem: filesystem1 contents: inline: file1 mode: 0644 user: id: 500 group: id: 501 - path: /opt/file2 filesystem: filesystem1 contents: remote: url: http://example.com/file2 compression: gzip verification: hash: function: sha512 sum: 00000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000 mode: 0644 user: id: 502 group: id: 503 - path: /opt/file3 filesystem: filesystem2 contents: remote: url: http://example.com/file3 compression: gzip mode: 0400 user: id: 1000 group: id: 1001 - path: /opt/file4 filesystem: filesystem2 `}, out: out{cfg: types.Config{ Ignition: types.Ignition{Version: types.IgnitionVersion{Major: 2}}, Storage: types.Storage{ Disks: []types.Disk{ { Device: types.Path("/dev/sda"), WipeTable: true, Partitions: []types.Partition{ { Label: types.PartitionLabel("ROOT"), Number: 7, Size: types.PartitionDimension(0x32000), Start: types.PartitionDimension(0x19000), TypeGUID: "11111111-1111-1111-1111-111111111111", }, { Label: types.PartitionLabel("DATA"), Number: 12, Size: types.PartitionDimension(0x200000), Start: types.PartitionDimension(0x96000), TypeGUID: "00000000-0000-0000-0000-000000000000", }, { Label: types.PartitionLabel("NOTHING"), }, }, }, { Device: types.Path("/dev/sdb"), WipeTable: true, }, }, Arrays: []types.Raid{ { Name: "fast", Level: "raid0", Devices: []types.Path{types.Path("/dev/sdc"), types.Path("/dev/sdd")}, }, { Name: "durable", Level: "raid1", Devices: []types.Path{types.Path("/dev/sde"), types.Path("/dev/sdf"), types.Path("/dev/sdg")}, Spares: 1, }, }, Filesystems: []types.Filesystem{ { Name: "filesystem1", Mount: &types.FilesystemMount{ Device: types.Path("/dev/disk/by-partlabel/ROOT"), Format: types.FilesystemFormat("btrfs"), Create: &types.FilesystemCreate{ Force: true, Options: types.MkfsOptions([]string{"-L", "ROOT"}), }, }, }, { Name: "filesystem2", Mount: &types.FilesystemMount{ Device: types.Path("/dev/disk/by-partlabel/DATA"), Format: types.FilesystemFormat("ext4"), }, }, { Name: "filesystem3", Path: func(p types.Path) *types.Path { return &p }("/sysroot"), }, }, Files: []types.File{ { Filesystem: "filesystem1", Path: types.Path("/opt/file1"), Contents: types.FileContents{ Source: types.Url{ Scheme: "data", Opaque: ",file1", }, }, Mode: types.FileMode(0644), User: types.FileUser{Id: 500}, Group: types.FileGroup{Id: 501}, }, { Filesystem: "filesystem1", Path: types.Path("/opt/file2"), Contents: types.FileContents{ Source: types.Url{ Scheme: "http", Host: "example.com", Path: "/file2", }, Compression: "gzip", Verification: types.Verification{ Hash: &types.Hash{ Function: "sha512", Sum: "00000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000", }, }, }, Mode: types.FileMode(0644), User: types.FileUser{Id: 502}, Group: types.FileGroup{Id: 503}, }, { Filesystem: "filesystem2", Path: types.Path("/opt/file3"), Contents: types.FileContents{ Source: types.Url{ Scheme: "http", Host: "example.com", Path: "/file3", }, Compression: "gzip", }, Mode: types.FileMode(0400), User: types.FileUser{Id: 1000}, Group: types.FileGroup{Id: 1001}, }, { Filesystem: "filesystem2", Path: types.Path("/opt/file4"), Contents: types.FileContents{ Source: types.Url{ Scheme: "data", Opaque: ",", }, }, }, }, }, }}, }, // systemd { in: in{data: ` systemd: units: - name: test1.service enable: true contents: test1 contents dropins: - name: conf1.conf contents: conf1 contents - name: conf2.conf contents: conf2 contents - name: test2.service mask: true contents: test2 contents `}, out: out{cfg: types.Config{ Ignition: types.Ignition{Version: types.IgnitionVersion{Major: 2}}, Systemd: types.Systemd{ Units: []types.SystemdUnit{ { Name: "test1.service", Enable: true, Contents: "test1 contents", DropIns: []types.SystemdUnitDropIn{ { Name: "conf1.conf", Contents: "conf1 contents", }, { Name: "conf2.conf", Contents: "conf2 contents", }, }, }, { Name: "test2.service", Mask: true, Contents: "test2 contents", }, }, }, }}, }, // networkd { in: in{data: ` networkd: units: - name: empty.netdev - name: test.network contents: test config `}, out: out{cfg: types.Config{ Ignition: types.Ignition{Version: types.IgnitionVersion{Major: 2}}, Networkd: types.Networkd{ Units: []types.NetworkdUnit{ { Name: "empty.netdev", }, { Name: "test.network", Contents: "test config", }, }, }, }}, }, // passwd { in: in{data: ` passwd: users: - name: user 1 password_hash: password 1 ssh_authorized_keys: - key1 - key2 - name: user 2 password_hash: password 2 ssh_authorized_keys: - key3 - key4 create: uid: 123 gecos: gecos home_dir: /home/user 2 no_create_home: true primary_group: wheel groups: - wheel - plugdev no_user_group: true system: true no_log_init: true shell: /bin/zsh - name: user 3 password_hash: password 3 ssh_authorized_keys: - key5 - key6 create: {} groups: - name: group 1 gid: 1000 password_hash: password 1 system: true - name: group 2 password_hash: password 2 `}, out: out{cfg: types.Config{ Ignition: types.Ignition{Version: types.IgnitionVersion{Major: 2}}, Passwd: types.Passwd{ Users: []types.User{ { Name: "user 1", PasswordHash: "password 1", SSHAuthorizedKeys: []string{"key1", "key2"}, }, { Name: "user 2", PasswordHash: "password 2", SSHAuthorizedKeys: []string{"key3", "key4"}, Create: &types.UserCreate{ Uid: func(i uint) *uint { return &i }(123), GECOS: "gecos", Homedir: "/home/user 2", NoCreateHome: true, PrimaryGroup: "wheel", Groups: []string{"wheel", "plugdev"}, NoUserGroup: true, System: true, NoLogInit: true, Shell: "/bin/zsh", }, }, { Name: "user 3", PasswordHash: "password 3", SSHAuthorizedKeys: []string{"key5", "key6"}, Create: &types.UserCreate{}, }, }, Groups: []types.Group{ { Name: "group 1", Gid: func(i uint) *uint { return &i }(1000), PasswordHash: "password 1", System: true, }, { Name: "group 2", PasswordHash: "password 2", }, }, }, }}, }, } for i, test := range tests { cfg, err := ParseAsV2_0_0([]byte(test.in.data)) if !reflect.DeepEqual(err, test.out.err) { t.Errorf("#%d: bad error: want %v, got %v", i, test.out.err, err) } if !reflect.DeepEqual(cfg, test.out.cfg) { t.Errorf("#%d: bad config: want %#v, got %#v", i, test.out.cfg, cfg) } } }
func TestConvertAs2_0_0(t *testing.T) { type in struct { cfg types.Config } type out struct { cfg ignTypes.Config r report.Report } tests := []struct { in in out out }{ { in: in{cfg: types.Config{}}, out: out{cfg: ignTypes.Config{Ignition: ignTypes.Ignition{Version: ignTypes.IgnitionVersion{Major: 2}}}}, }, { in: in{cfg: types.Config{ Networkd: types.Networkd{ Units: []types.NetworkdUnit{ {Name: "bad.blah", Contents: "not valid"}, }, }, }}, out: out{r: report.ReportFromError(errors.New("invalid networkd unit extension"), report.EntryError)}, }, // Config { in: in{cfg: types.Config{ Ignition: types.Ignition{ Config: types.IgnitionConfig{ Append: []types.ConfigReference{ { Source: "http://example.com/test1", Verification: types.Verification{ Hash: types.Hash{ Function: "sha512", Sum: "00000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000", }, }, }, { Source: "http://example.com/test2", }, }, Replace: &types.ConfigReference{ Source: "http://example.com/test3", Verification: types.Verification{ Hash: types.Hash{ Function: "sha512", Sum: "00000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000", }, }, }, }, }, }}, out: out{cfg: ignTypes.Config{ Ignition: ignTypes.Ignition{ Version: ignTypes.IgnitionVersion{Major: 2}, Config: ignTypes.IgnitionConfig{ Append: []ignTypes.ConfigReference{ { Source: ignTypes.Url{ Scheme: "http", Host: "example.com", Path: "/test1", }, Verification: ignTypes.Verification{ Hash: &ignTypes.Hash{ Function: "sha512", Sum: "00000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000", }, }, }, { Source: ignTypes.Url{ Scheme: "http", Host: "example.com", Path: "/test2", }, }, }, Replace: &ignTypes.ConfigReference{ Source: ignTypes.Url{ Scheme: "http", Host: "example.com", Path: "/test3", }, Verification: ignTypes.Verification{ Hash: &ignTypes.Hash{ Function: "sha512", Sum: "00000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000", }, }, }, }, }, }}, }, // Storage { in: in{cfg: types.Config{ Storage: types.Storage{ Disks: []types.Disk{ { Device: "/dev/sda", WipeTable: true, Partitions: []types.Partition{ { Label: "ROOT", Number: 7, Size: "100MB", Start: "50MB", TypeGUID: "11111111-1111-1111-1111-111111111111", }, { Label: "DATA", Number: 12, Size: "1GB", Start: "300MB", TypeGUID: "00000000-0000-0000-0000-000000000000", }, { Label: "NOTHING", }, }, }, { Device: "/dev/sdb", WipeTable: true, }, }, Arrays: []types.Raid{ { Name: "fast", Level: "raid0", Devices: []string{"/dev/sdc", "/dev/sdd"}, }, { Name: "durable", Level: "raid1", Devices: []string{"/dev/sde", "/dev/sdf", "/dev/sdg"}, Spares: 1, }, }, Filesystems: []types.Filesystem{ { Name: "filesystem1", Mount: &types.Mount{ Device: "/dev/disk/by-partlabel/ROOT", Format: "btrfs", Create: &types.Create{ Force: true, Options: []string{"-L", "ROOT"}, }, }, }, { Name: "filesystem2", Mount: &types.Mount{ Device: "/dev/disk/by-partlabel/DATA", Format: "ext4", }, }, { Name: "filesystem3", Path: "/sysroot", }, }, Files: []types.File{ { Filesystem: "filesystem1", Path: "/opt/file1", Contents: types.FileContents{ Inline: "file1", }, Mode: 0644, User: types.FileUser{Id: 500}, Group: types.FileGroup{Id: 501}, }, { Filesystem: "filesystem1", Path: "/opt/file2", Contents: types.FileContents{ Remote: types.Remote{ Url: "http://example.com/file2", Compression: "gzip", Verification: types.Verification{ Hash: types.Hash{ Function: "sha512", Sum: "00000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000", }, }, }, }, Mode: 0644, User: types.FileUser{Id: 502}, Group: types.FileGroup{Id: 503}, }, { Filesystem: "filesystem2", Path: "/opt/file3", Contents: types.FileContents{ Remote: types.Remote{ Url: "http://example.com/file3", Compression: "gzip", }, }, Mode: 0400, User: types.FileUser{Id: 1000}, Group: types.FileGroup{Id: 1001}, }, { Filesystem: "filesystem2", Path: "/opt/file4", Contents: types.FileContents{ Inline: "", }, }, }, }, }}, out: out{cfg: ignTypes.Config{ Ignition: ignTypes.Ignition{Version: ignTypes.IgnitionVersion{Major: 2}}, Storage: ignTypes.Storage{ Disks: []ignTypes.Disk{ { Device: ignTypes.Path("/dev/sda"), WipeTable: true, Partitions: []ignTypes.Partition{ { Label: ignTypes.PartitionLabel("ROOT"), Number: 7, Size: ignTypes.PartitionDimension(0x32000), Start: ignTypes.PartitionDimension(0x19000), TypeGUID: "11111111-1111-1111-1111-111111111111", }, { Label: ignTypes.PartitionLabel("DATA"), Number: 12, Size: ignTypes.PartitionDimension(0x200000), Start: ignTypes.PartitionDimension(0x96000), TypeGUID: "00000000-0000-0000-0000-000000000000", }, { Label: ignTypes.PartitionLabel("NOTHING"), }, }, }, { Device: ignTypes.Path("/dev/sdb"), WipeTable: true, }, }, Arrays: []ignTypes.Raid{ { Name: "fast", Level: "raid0", Devices: []ignTypes.Path{ignTypes.Path("/dev/sdc"), ignTypes.Path("/dev/sdd")}, }, { Name: "durable", Level: "raid1", Devices: []ignTypes.Path{ignTypes.Path("/dev/sde"), ignTypes.Path("/dev/sdf"), ignTypes.Path("/dev/sdg")}, Spares: 1, }, }, Filesystems: []ignTypes.Filesystem{ { Name: "filesystem1", Mount: &ignTypes.FilesystemMount{ Device: ignTypes.Path("/dev/disk/by-partlabel/ROOT"), Format: ignTypes.FilesystemFormat("btrfs"), Create: &ignTypes.FilesystemCreate{ Force: true, Options: ignTypes.MkfsOptions([]string{"-L", "ROOT"}), }, }, }, { Name: "filesystem2", Mount: &ignTypes.FilesystemMount{ Device: ignTypes.Path("/dev/disk/by-partlabel/DATA"), Format: ignTypes.FilesystemFormat("ext4"), }, }, { Name: "filesystem3", Path: func(p ignTypes.Path) *ignTypes.Path { return &p }("/sysroot"), }, }, Files: []ignTypes.File{ { Filesystem: "filesystem1", Path: ignTypes.Path("/opt/file1"), Contents: ignTypes.FileContents{ Source: ignTypes.Url{ Scheme: "data", Opaque: ",file1", }, }, Mode: ignTypes.FileMode(0644), User: ignTypes.FileUser{Id: 500}, Group: ignTypes.FileGroup{Id: 501}, }, { Filesystem: "filesystem1", Path: ignTypes.Path("/opt/file2"), Contents: ignTypes.FileContents{ Source: ignTypes.Url{ Scheme: "http", Host: "example.com", Path: "/file2", }, Compression: "gzip", Verification: ignTypes.Verification{ Hash: &ignTypes.Hash{ Function: "sha512", Sum: "00000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000", }, }, }, Mode: ignTypes.FileMode(0644), User: ignTypes.FileUser{Id: 502}, Group: ignTypes.FileGroup{Id: 503}, }, { Filesystem: "filesystem2", Path: ignTypes.Path("/opt/file3"), Contents: ignTypes.FileContents{ Source: ignTypes.Url{ Scheme: "http", Host: "example.com", Path: "/file3", }, Compression: "gzip", }, Mode: ignTypes.FileMode(0400), User: ignTypes.FileUser{Id: 1000}, Group: ignTypes.FileGroup{Id: 1001}, }, { Filesystem: "filesystem2", Path: ignTypes.Path("/opt/file4"), Contents: ignTypes.FileContents{ Source: ignTypes.Url{ Scheme: "data", Opaque: ",", }, }, }, }, }, }}, }, // systemd { in: in{cfg: types.Config{ Systemd: types.Systemd{ Units: []types.SystemdUnit{ { Name: "test1.service", Enable: true, Contents: "test1 contents", DropIns: []types.SystemdUnitDropIn{ { Name: "conf1.conf", Contents: "conf1 contents", }, { Name: "conf2.conf", Contents: "conf2 contents", }, }, }, { Name: "test2.service", Mask: true, Contents: "test2 contents", }, }, }, }}, out: out{cfg: ignTypes.Config{ Ignition: ignTypes.Ignition{Version: ignTypes.IgnitionVersion{Major: 2}}, Systemd: ignTypes.Systemd{ Units: []ignTypes.SystemdUnit{ { Name: "test1.service", Enable: true, Contents: "test1 contents", DropIns: []ignTypes.SystemdUnitDropIn{ { Name: "conf1.conf", Contents: "conf1 contents", }, { Name: "conf2.conf", Contents: "conf2 contents", }, }, }, { Name: "test2.service", Mask: true, Contents: "test2 contents", }, }, }, }}, }, // networkd { in: in{cfg: types.Config{ Networkd: types.Networkd{ Units: []types.NetworkdUnit{ { Name: "empty.netdev", }, { Name: "test.network", Contents: "test config", }, }, }, }}, out: out{cfg: ignTypes.Config{ Ignition: ignTypes.Ignition{Version: ignTypes.IgnitionVersion{Major: 2}}, Networkd: ignTypes.Networkd{ Units: []ignTypes.NetworkdUnit{ { Name: "empty.netdev", }, { Name: "test.network", Contents: "test config", }, }, }, }}, }, // passwd { in: in{cfg: types.Config{ Passwd: types.Passwd{ Users: []types.User{ { Name: "user 1", PasswordHash: "password 1", SSHAuthorizedKeys: []string{"key1", "key2"}, }, { Name: "user 2", PasswordHash: "password 2", SSHAuthorizedKeys: []string{"key3", "key4"}, Create: &types.UserCreate{ Uid: func(i uint) *uint { return &i }(123), GECOS: "gecos", Homedir: "/home/user 2", NoCreateHome: true, PrimaryGroup: "wheel", Groups: []string{"wheel", "plugdev"}, NoUserGroup: true, System: true, NoLogInit: true, Shell: "/bin/zsh", }, }, { Name: "user 3", PasswordHash: "password 3", SSHAuthorizedKeys: []string{"key5", "key6"}, Create: &types.UserCreate{}, }, }, Groups: []types.Group{ { Name: "group 1", Gid: func(i uint) *uint { return &i }(1000), PasswordHash: "password 1", System: true, }, { Name: "group 2", PasswordHash: "password 2", }, }, }, }}, out: out{cfg: ignTypes.Config{ Ignition: ignTypes.Ignition{Version: ignTypes.IgnitionVersion{Major: 2}}, Passwd: ignTypes.Passwd{ Users: []ignTypes.User{ { Name: "user 1", PasswordHash: "password 1", SSHAuthorizedKeys: []string{"key1", "key2"}, }, { Name: "user 2", PasswordHash: "password 2", SSHAuthorizedKeys: []string{"key3", "key4"}, Create: &ignTypes.UserCreate{ Uid: func(i uint) *uint { return &i }(123), GECOS: "gecos", Homedir: "/home/user 2", NoCreateHome: true, PrimaryGroup: "wheel", Groups: []string{"wheel", "plugdev"}, NoUserGroup: true, System: true, NoLogInit: true, Shell: "/bin/zsh", }, }, { Name: "user 3", PasswordHash: "password 3", SSHAuthorizedKeys: []string{"key5", "key6"}, Create: &ignTypes.UserCreate{}, }, }, Groups: []ignTypes.Group{ { Name: "group 1", Gid: func(i uint) *uint { return &i }(1000), PasswordHash: "password 1", System: true, }, { Name: "group 2", PasswordHash: "password 2", }, }, }, }}, }, } for i, test := range tests { cfg, r := ConvertAs2_0_0(test.in.cfg) if !reflect.DeepEqual(r, test.out.r) { t.Errorf("#%d: bad error: want %v, got %v", i, test.out.r, r) } if !reflect.DeepEqual(cfg, test.out.cfg) { t.Errorf("#%d: bad config: want %#v, got %#v", i, test.out.cfg, cfg) } } }
func buildFile(d *schema.ResourceData, c *cache) (string, error) { _, hasContent := d.GetOk("content") _, hasSource := d.GetOk("source") if hasContent && hasSource { return "", fmt.Errorf("content and source options are incompatible") } if !hasContent && !hasSource { return "", fmt.Errorf("content or source options must be present") } var compression types.Compression var source types.Url var hash *types.Hash var err error if hasContent { source, err = encodeDataURL( d.Get("content.0.mime").(string), d.Get("content.0.content").(string), ) if err != nil { return "", err } } if hasSource { source, err = buildURL(d.Get("source.0.source").(string)) if err != nil { return "", err } compression = types.Compression(d.Get("source.0.compression").(string)) h, err := buildHash(d.Get("source.0.verification").(string)) if err != nil { return "", err } hash = &h } return c.addFile(&types.File{ Filesystem: d.Get("filesystem").(string), Path: types.Path(d.Get("path").(string)), Contents: types.FileContents{ Compression: compression, Source: source, Verification: types.Verification{ Hash: hash, }, }, User: types.FileUser{ Id: d.Get("uid").(int), }, Group: types.FileGroup{ Id: d.Get("gid").(int), }, Mode: types.FileMode(d.Get("mode").(int)), }), nil }
func TestTranslateFromV1(t *testing.T) { type in struct { config v1.Config } type out struct { config types.Config err error } tests := []struct { in in out out }{ { in: in{}, out: out{config: types.Config{Ignition: types.Ignition{Version: types.IgnitionVersion{Major: 2}}}}, }, { in: in{config: v1.Config{ Storage: v1.Storage{ Disks: []v1.Disk{ { Device: v1.Path("/dev/sda"), WipeTable: true, Partitions: []v1.Partition{ { Label: v1.PartitionLabel("ROOT"), Number: 7, Size: v1.PartitionDimension(100), Start: v1.PartitionDimension(50), TypeGUID: "HI", }, { Label: v1.PartitionLabel("DATA"), Number: 12, Size: v1.PartitionDimension(1000), Start: v1.PartitionDimension(300), TypeGUID: "LO", }, }, }, { Device: v1.Path("/dev/sdb"), WipeTable: true, }, }, Arrays: []v1.Raid{ { Name: "fast", Level: "raid0", Devices: []v1.Path{v1.Path("/dev/sdc"), v1.Path("/dev/sdd")}, Spares: 2, }, { Name: "durable", Level: "raid1", Devices: []v1.Path{v1.Path("/dev/sde"), v1.Path("/dev/sdf")}, Spares: 3, }, }, Filesystems: []v1.Filesystem{ { Device: v1.Path("/dev/disk/by-partlabel/ROOT"), Format: v1.FilesystemFormat("btrfs"), Create: &v1.FilesystemCreate{ Force: true, Options: v1.MkfsOptions([]string{"-L", "ROOT"}), }, Files: []v1.File{ { Path: v1.Path("/opt/file1"), Contents: "file1", Mode: v1.FileMode(0664), Uid: 500, Gid: 501, }, { Path: v1.Path("/opt/file2"), Contents: "file2", Mode: v1.FileMode(0644), Uid: 502, Gid: 503, }, }, }, { Device: v1.Path("/dev/disk/by-partlabel/DATA"), Format: v1.FilesystemFormat("ext4"), Files: []v1.File{ { Path: v1.Path("/opt/file3"), Contents: "file3", Mode: v1.FileMode(0400), Uid: 1000, Gid: 1001, }, }, }, }, }, }}, out: out{config: types.Config{ Ignition: types.Ignition{Version: types.IgnitionVersion{Major: 2}}, Storage: types.Storage{ Disks: []types.Disk{ { Device: types.Path("/dev/sda"), WipeTable: true, Partitions: []types.Partition{ { Label: types.PartitionLabel("ROOT"), Number: 7, Size: types.PartitionDimension(100), Start: types.PartitionDimension(50), TypeGUID: "HI", }, { Label: types.PartitionLabel("DATA"), Number: 12, Size: types.PartitionDimension(1000), Start: types.PartitionDimension(300), TypeGUID: "LO", }, }, }, { Device: types.Path("/dev/sdb"), WipeTable: true, }, }, Arrays: []types.Raid{ { Name: "fast", Level: "raid0", Devices: []types.Path{types.Path("/dev/sdc"), types.Path("/dev/sdd")}, Spares: 2, }, { Name: "durable", Level: "raid1", Devices: []types.Path{types.Path("/dev/sde"), types.Path("/dev/sdf")}, Spares: 3, }, }, Filesystems: []types.Filesystem{ { Name: "_translate-filesystem-0", Mount: &types.FilesystemMount{ Device: types.Path("/dev/disk/by-partlabel/ROOT"), Format: types.FilesystemFormat("btrfs"), Create: &types.FilesystemCreate{ Force: true, Options: types.MkfsOptions([]string{"-L", "ROOT"}), }, }, }, { Name: "_translate-filesystem-1", Mount: &types.FilesystemMount{ Device: types.Path("/dev/disk/by-partlabel/DATA"), Format: types.FilesystemFormat("ext4"), }, }, }, Files: []types.File{ { Filesystem: "_translate-filesystem-0", Path: types.Path("/opt/file1"), Contents: types.FileContents{ Source: types.Url{ Scheme: "data", Opaque: ",file1", }, }, Mode: types.FileMode(0664), User: types.FileUser{Id: 500}, Group: types.FileGroup{Id: 501}, }, { Filesystem: "_translate-filesystem-0", Path: types.Path("/opt/file2"), Contents: types.FileContents{ Source: types.Url{ Scheme: "data", Opaque: ",file2", }, }, Mode: types.FileMode(0644), User: types.FileUser{Id: 502}, Group: types.FileGroup{Id: 503}, }, { Filesystem: "_translate-filesystem-1", Path: types.Path("/opt/file3"), Contents: types.FileContents{ Source: types.Url{ Scheme: "data", Opaque: ",file3", }, }, Mode: types.FileMode(0400), User: types.FileUser{Id: 1000}, Group: types.FileGroup{Id: 1001}, }, }, }, }}, }, { in: in{v1.Config{ Systemd: v1.Systemd{ Units: []v1.SystemdUnit{ { Name: "test1.service", Enable: true, Contents: "test1 contents", DropIns: []v1.SystemdUnitDropIn{ { Name: "conf1.conf", Contents: "conf1 contents", }, { Name: "conf2.conf", Contents: "conf2 contents", }, }, }, { Name: "test2.service", Mask: true, Contents: "test2 contents", }, }, }, }}, out: out{config: types.Config{ Ignition: types.Ignition{Version: types.IgnitionVersion{Major: 2}}, Systemd: types.Systemd{ Units: []types.SystemdUnit{ { Name: "test1.service", Enable: true, Contents: "test1 contents", DropIns: []types.SystemdUnitDropIn{ { Name: "conf1.conf", Contents: "conf1 contents", }, { Name: "conf2.conf", Contents: "conf2 contents", }, }, }, { Name: "test2.service", Mask: true, Contents: "test2 contents", }, }, }, }}, }, { in: in{v1.Config{ Networkd: v1.Networkd{ Units: []v1.NetworkdUnit{ { Name: "test1.network", Contents: "test1 contents", }, { Name: "test2.network", Contents: "test2 contents", }, }, }, }}, out: out{config: types.Config{ Ignition: types.Ignition{Version: types.IgnitionVersion{Major: 2}}, Networkd: types.Networkd{ Units: []types.NetworkdUnit{ { Name: "test1.network", Contents: "test1 contents", }, { Name: "test2.network", Contents: "test2 contents", }, }, }, }}, }, { in: in{v1.Config{ Passwd: v1.Passwd{ Users: []v1.User{ { Name: "user 1", PasswordHash: "password 1", SSHAuthorizedKeys: []string{"key1", "key2"}, }, { Name: "user 2", PasswordHash: "password 2", SSHAuthorizedKeys: []string{"key3", "key4"}, Create: &v1.UserCreate{ Uid: func(i uint) *uint { return &i }(123), GECOS: "gecos", Homedir: "/home/user 2", NoCreateHome: true, PrimaryGroup: "wheel", Groups: []string{"wheel", "plugdev"}, NoUserGroup: true, System: true, NoLogInit: true, Shell: "/bin/zsh", }, }, { Name: "user 3", PasswordHash: "password 3", SSHAuthorizedKeys: []string{"key5", "key6"}, Create: &v1.UserCreate{}, }, }, Groups: []v1.Group{ { Name: "group 1", Gid: func(i uint) *uint { return &i }(1000), PasswordHash: "password 1", System: true, }, { Name: "group 2", PasswordHash: "password 2", }, }, }, }}, out: out{config: types.Config{ Ignition: types.Ignition{Version: types.IgnitionVersion{Major: 2}}, Passwd: types.Passwd{ Users: []types.User{ { Name: "user 1", PasswordHash: "password 1", SSHAuthorizedKeys: []string{"key1", "key2"}, }, { Name: "user 2", PasswordHash: "password 2", SSHAuthorizedKeys: []string{"key3", "key4"}, Create: &types.UserCreate{ Uid: func(i uint) *uint { return &i }(123), GECOS: "gecos", Homedir: "/home/user 2", NoCreateHome: true, PrimaryGroup: "wheel", Groups: []string{"wheel", "plugdev"}, NoUserGroup: true, System: true, NoLogInit: true, Shell: "/bin/zsh", }, }, { Name: "user 3", PasswordHash: "password 3", SSHAuthorizedKeys: []string{"key5", "key6"}, Create: &types.UserCreate{}, }, }, Groups: []types.Group{ { Name: "group 1", Gid: func(i uint) *uint { return &i }(1000), PasswordHash: "password 1", System: true, }, { Name: "group 2", PasswordHash: "password 2", }, }, }, }}, }, } for i, test := range tests { config, err := TranslateFromV1(test.in.config) if test.out.err != err { t.Errorf("#%d: bad error: want %v, got %v", i, test.out.err, err) } if !reflect.DeepEqual(test.out.config, config) { t.Errorf("#%d: bad config: want %+v, got %+v", i, test.out.config, config) } } }
func TestIngnitionFile(t *testing.T) { testIgnition(t, ` resource "ignition_file" "foo" { filesystem = "foo" path = "/foo" content { content = "foo" } mode = 420 uid = 42 gid = 84 } resource "ignition_file" "qux" { filesystem = "qux" path = "/qux" source { source = "qux" compression = "gzip" verification = "sha512-0123456789abcdef0123456789abcdef0123456789abcdef0123456789abcdef0123456789abcdef0123456789abcdef0123456789abcdef0123456789abcdef" } } resource "ignition_config" "test" { files = [ "${ignition_file.foo.id}", "${ignition_file.qux.id}", ] } `, func(c *types.Config) error { if len(c.Storage.Files) != 2 { return fmt.Errorf("arrays, found %d", len(c.Storage.Arrays)) } f := c.Storage.Files[0] if f.Filesystem != "foo" { return fmt.Errorf("filesystem, found %q", f.Filesystem) } if f.Path != "/foo" { return fmt.Errorf("path, found %q", f.Path) } if f.Contents.Source.String() != "data:text/plain;charset=utf-8;base64,Zm9v" { return fmt.Errorf("contents.source, found %q", f.Contents.Source) } if f.Mode != types.FileMode(420) { return fmt.Errorf("mode, found %q", f.Mode) } if f.User.Id != 42 { return fmt.Errorf("uid, found %q", f.User.Id) } if f.Group.Id != 84 { return fmt.Errorf("gid, found %q", f.Group.Id) } f = c.Storage.Files[1] if f.Filesystem != "qux" { return fmt.Errorf("filesystem, found %q", f.Filesystem) } if f.Path != "/qux" { return fmt.Errorf("path, found %q", f.Path) } if f.Contents.Source.String() != "qux" { return fmt.Errorf("contents.source, found %q", f.Contents.Source) } if f.Contents.Compression != "gzip" { return fmt.Errorf("contents.compression, found %q", f.Contents.Compression) } if f.Contents.Verification.Hash.Sum != "0123456789abcdef0123456789abcdef0123456789abcdef0123456789abcdef0123456789abcdef0123456789abcdef0123456789abcdef0123456789abcdef" { return fmt.Errorf("config.replace.verification, found %q", f.Contents.Verification.Hash) } return nil }) }
func TranslateFromV1(old v1.Config) (types.Config, error) { config := types.Config{ Ignition: types.Ignition{ Version: types.IgnitionVersion{Major: 2}, }, } for _, oldDisk := range old.Storage.Disks { disk := types.Disk{ Device: types.Path(oldDisk.Device), WipeTable: oldDisk.WipeTable, } for _, oldPartition := range oldDisk.Partitions { disk.Partitions = append(disk.Partitions, types.Partition{ Label: types.PartitionLabel(oldPartition.Label), Number: oldPartition.Number, Size: types.PartitionDimension(oldPartition.Size), Start: types.PartitionDimension(oldPartition.Start), TypeGUID: types.PartitionTypeGUID(oldPartition.TypeGUID), }) } config.Storage.Disks = append(config.Storage.Disks, disk) } for _, oldArray := range old.Storage.Arrays { array := types.Raid{ Name: oldArray.Name, Level: oldArray.Level, Spares: oldArray.Spares, } for _, oldDevice := range oldArray.Devices { array.Devices = append(array.Devices, types.Path(oldDevice)) } config.Storage.Arrays = append(config.Storage.Arrays, array) } for i, oldFilesystem := range old.Storage.Filesystems { filesystem := types.Filesystem{ Name: fmt.Sprintf("_translate-filesystem-%d", i), Mount: &types.FilesystemMount{ Device: types.Path(oldFilesystem.Device), Format: types.FilesystemFormat(oldFilesystem.Format), }, } if oldFilesystem.Create != nil { filesystem.Mount.Create = &types.FilesystemCreate{ Force: oldFilesystem.Create.Force, Options: types.MkfsOptions(oldFilesystem.Create.Options), } } config.Storage.Filesystems = append(config.Storage.Filesystems, filesystem) for _, oldFile := range oldFilesystem.Files { file := types.File{ Filesystem: filesystem.Name, Path: types.Path(oldFile.Path), Contents: types.FileContents{ Source: types.Url{ Scheme: "data", Opaque: "," + dataurl.EscapeString(oldFile.Contents), }, }, Mode: types.FileMode(oldFile.Mode), User: types.FileUser{Id: oldFile.Uid}, Group: types.FileGroup{Id: oldFile.Gid}, } config.Storage.Files = append(config.Storage.Files, file) } } for _, oldUnit := range old.Systemd.Units { unit := types.SystemdUnit{ Name: types.SystemdUnitName(oldUnit.Name), Enable: oldUnit.Enable, Mask: oldUnit.Mask, Contents: oldUnit.Contents, } for _, oldDropIn := range oldUnit.DropIns { unit.DropIns = append(unit.DropIns, types.SystemdUnitDropIn{ Name: types.SystemdUnitDropInName(oldDropIn.Name), Contents: oldDropIn.Contents, }) } config.Systemd.Units = append(config.Systemd.Units, unit) } for _, oldUnit := range old.Networkd.Units { config.Networkd.Units = append(config.Networkd.Units, types.NetworkdUnit{ Name: types.NetworkdUnitName(oldUnit.Name), Contents: oldUnit.Contents, }) } for _, oldUser := range old.Passwd.Users { user := types.User{ Name: oldUser.Name, PasswordHash: oldUser.PasswordHash, SSHAuthorizedKeys: oldUser.SSHAuthorizedKeys, } if oldUser.Create != nil { user.Create = &types.UserCreate{ Uid: oldUser.Create.Uid, GECOS: oldUser.Create.GECOS, Homedir: oldUser.Create.Homedir, NoCreateHome: oldUser.Create.NoCreateHome, PrimaryGroup: oldUser.Create.PrimaryGroup, Groups: oldUser.Create.Groups, NoUserGroup: oldUser.Create.NoUserGroup, System: oldUser.Create.System, NoLogInit: oldUser.Create.NoLogInit, Shell: oldUser.Create.Shell, } } config.Passwd.Users = append(config.Passwd.Users, user) } for _, oldGroup := range old.Passwd.Groups { config.Passwd.Groups = append(config.Passwd.Groups, types.Group{ Name: oldGroup.Name, Gid: oldGroup.Gid, PasswordHash: oldGroup.PasswordHash, System: oldGroup.System, }) } return config, nil }