Skip to content

Commit 1451196

Browse files
prestistyasminvalimjmarreroc4rt0
committed
base/v0_6_exp: add parent directory sugar
Add a field called 'Parent' which is used to specify a file's parent directory. When a parent is specified, all directories from the parent to the file will be created, with the 'mode' supplied in the parent directory. Co-authored-by: Yasmin Valim <[email protected]> Co-authored-by: Joseph Corchado <[email protected]> Co-authored-by: Adam Piasecki <[email protected]>
1 parent 4f1b414 commit 1451196

File tree

5 files changed

+257
-5
lines changed

5 files changed

+257
-5
lines changed

base/v0_6_exp/schema.go

+6
Original file line numberDiff line numberDiff line change
@@ -67,6 +67,12 @@ type File struct {
6767
Append []Resource `yaml:"append"`
6868
Contents Resource `yaml:"contents"`
6969
Mode *int `yaml:"mode"`
70+
Parent Parent `yaml:"parent"`
71+
}
72+
73+
type Parent struct {
74+
Path *string `yaml:"path"`
75+
Mode *int `yaml:"mode"`
7076
}
7177

7278
type Filesystem struct {

base/v0_6_exp/translate.go

+83-5
Original file line numberDiff line numberDiff line change
@@ -83,9 +83,7 @@ func (c Config) ToIgn3_5Unvalidated(options common.TranslateOptions) (types.Conf
8383

8484
tr := translate.NewTranslator("yaml", "json", options)
8585
tr.AddCustomTranslator(translateIgnition)
86-
tr.AddCustomTranslator(translateFile)
87-
tr.AddCustomTranslator(translateDirectory)
88-
tr.AddCustomTranslator(translateLink)
86+
tr.AddCustomTranslator(translateStorage)
8987
tr.AddCustomTranslator(translateResource)
9088
tr.AddCustomTranslator(translatePasswdUser)
9189
tr.AddCustomTranslator(translateUnit)
@@ -99,7 +97,6 @@ func (c Config) ToIgn3_5Unvalidated(options common.TranslateOptions) (types.Conf
9997
translate.MergeP(tr, tm, &r, "systemd", &c.Systemd, &ret.Systemd)
10098

10199
c.addMountUnits(&ret, &tm)
102-
103100
tm2, r2 := c.processTrees(&ret, options)
104101
tm.Merge(tm2)
105102
r.Merge(r2)
@@ -121,6 +118,71 @@ func translateIgnition(from Ignition, options common.TranslateOptions) (to types
121118
return
122119
}
123120

121+
func translateStorage(from Storage, options common.TranslateOptions) (to types.Storage, tm translate.TranslationSet, r report.Report) {
122+
tr := translate.NewTranslator("yaml", "json", options)
123+
tr.AddCustomTranslator(translateFile)
124+
tr.AddCustomTranslator(translateDirectory)
125+
tr.AddCustomTranslator(translateLink)
126+
tr.AddCustomTranslator(translateLuks)
127+
tm, r = translate.Prefixed(tr, "directories", &from.Directories, &to.Directories)
128+
translate.MergeP(tr, tm, &r, "disks", &from.Disks, &to.Disks)
129+
translate.MergeP(tr, tm, &r, "files", &from.Files, &to.Files)
130+
translate.MergeP(tr, tm, &r, "filesystems", &from.Filesystems, &to.Filesystems)
131+
translate.MergeP(tr, tm, &r, "links", &from.Links, &to.Links)
132+
translate.MergeP(tr, tm, &r, "luks", &from.Luks, &to.Luks)
133+
translate.MergeP(tr, tm, &r, "raid", &from.Raid, &to.Raid)
134+
for i, file := range from.Files {
135+
if util.NotEmpty(file.Parent.Path) {
136+
yamlPath := path.New("yaml", "files", i, "parent")
137+
if !strings.Contains(file.Path, *file.Parent.Path) {
138+
r.AddOnError(yamlPath, common.ErrInvalidParent)
139+
continue
140+
}
141+
142+
parentDirectory := types.Directory{
143+
Node: types.Node{
144+
Path: *file.Parent.Path,
145+
Group: types.NodeGroup{ID: file.Group.ID, Name: file.Group.Name},
146+
User: types.NodeUser{ID: file.User.ID, Name: file.User.Name},
147+
},
148+
DirectoryEmbedded1: types.DirectoryEmbedded1{
149+
Mode: file.Parent.Mode,
150+
},
151+
}
152+
153+
to.Directories = append(to.Directories, parentDirectory)
154+
// find what is between parent and file path
155+
theInBetween := strings.Replace(file.Path, *file.Parent.Path, "", 1)
156+
157+
workingDirectory := *file.Parent.Path
158+
// render all directories between the filepath and the parent with the parent's mode
159+
// must be a goline for splitting on path.
160+
arrayDirectory := strings.Split(theInBetween, "/")
161+
for i := 0; i < len(arrayDirectory)-1; i++ {
162+
dirTobeCreated := arrayDirectory[i]
163+
if dirTobeCreated == "" {
164+
continue
165+
}
166+
workingDirectory += "/" + dirTobeCreated
167+
directoryToBeRendered := types.Directory{
168+
Node: types.Node{
169+
Path: workingDirectory,
170+
Group: types.NodeGroup{ID: file.Group.ID, Name: file.Group.Name},
171+
User: types.NodeUser{ID: file.User.ID, Name: file.User.Name},
172+
},
173+
DirectoryEmbedded1: types.DirectoryEmbedded1{
174+
Mode: file.Parent.Mode,
175+
},
176+
}
177+
to.Directories = append(to.Directories, directoryToBeRendered)
178+
}
179+
tm.AddFromCommonSource(yamlPath, path.New("json", "directories"), to.Directories)
180+
}
181+
182+
}
183+
return
184+
}
185+
124186
func translateFile(from File, options common.TranslateOptions) (to types.File, tm translate.TranslationSet, r report.Report) {
125187
tr := translate.NewTranslator("yaml", "json", options)
126188
tr.AddCustomTranslator(translateResource)
@@ -134,6 +196,22 @@ func translateFile(from File, options common.TranslateOptions) (to types.File, t
134196
return
135197
}
136198

199+
func translateLuks(from Luks, options common.TranslateOptions) (to types.Luks, tm translate.TranslationSet, r report.Report) {
200+
tr := translate.NewTranslator("yaml", "json", options)
201+
tr.AddCustomTranslator(translateResource)
202+
tm, r = translate.Prefixed(tr, "clevis", &from.Clevis, &to.Clevis)
203+
translate.MergeP(tr, tm, &r, "device", &from.Device, &to.Device)
204+
translate.MergeP(tr, tm, &r, "discard", &from.Discard, &to.Discard)
205+
translate.MergeP2(tr, tm, &r, "key_file", &from.KeyFile, "keyFile", &to.KeyFile)
206+
translate.MergeP(tr, tm, &r, "label", &from.Label, &to.Label)
207+
translate.MergeP(tr, tm, &r, "name", &from.Name, &to.Name)
208+
translate.MergeP2(tr, tm, &r, "open_options", &from.OpenOptions, "openOptions", &to.OpenOptions)
209+
translate.MergeP(tr, tm, &r, "options", &from.Options, &to.Options)
210+
translate.MergeP(tr, tm, &r, "uuid", &from.UUID, &to.UUID)
211+
translate.MergeP2(tr, tm, &r, "wipe_volume", &from.WipeVolume, "wipeVolume", &to.WipeVolume)
212+
return
213+
}
214+
137215
func translateResource(from Resource, options common.TranslateOptions) (to types.Resource, tm translate.TranslationSet, r report.Report) {
138216
tr := translate.NewTranslator("yaml", "json", options)
139217
tm, r = translate.Prefixed(tr, "verification", &from.Verification, &to.Verification)
@@ -294,7 +372,7 @@ func (c Config) processTrees(ret *types.Config, options common.TranslateOptions)
294372
return ts, r
295373
}
296374
t := newNodeTracker(ret)
297-
375+
ts.AddTranslation(path.New("yaml", "storage"), path.New("json", "storage"))
298376
for i, tree := range c.Storage.Trees {
299377
yamlPath := path.New("yaml", "storage", "trees", i)
300378
if options.FilesDir == "" {

base/v0_6_exp/translate_test.go

+165
Original file line numberDiff line numberDiff line change
@@ -599,6 +599,171 @@ func TestTranslateFile(t *testing.T) {
599599
})
600600
}
601601
}
602+
func TestTranslateStorage(t *testing.T) {
603+
tests := []struct {
604+
in Storage
605+
out types.Storage
606+
errPath path.ContextPath
607+
errors error
608+
}{
609+
// Basic parent file directory
610+
{
611+
Storage{
612+
Files: []File{
613+
{
614+
Path: "/foo/bar/txt.txt",
615+
Contents: Resource{},
616+
Mode: util.IntToPtr(420),
617+
Parent: Parent{
618+
Path: util.StrToPtr("/foo"),
619+
Mode: util.IntToPtr(420),
620+
},
621+
},
622+
},
623+
},
624+
types.Storage{
625+
Files: []types.File{
626+
{
627+
Node: types.Node{
628+
Path: "/foo/bar/txt.txt",
629+
},
630+
FileEmbedded1: types.FileEmbedded1{
631+
Mode: util.IntToPtr(420),
632+
Contents: types.Resource{},
633+
},
634+
},
635+
},
636+
Directories: []types.Directory{
637+
{
638+
Node: types.Node{
639+
Path: "/foo",
640+
},
641+
DirectoryEmbedded1: types.DirectoryEmbedded1{
642+
Mode: util.IntToPtr(420),
643+
},
644+
},
645+
{
646+
Node: types.Node{
647+
Path: "/foo/bar",
648+
},
649+
DirectoryEmbedded1: types.DirectoryEmbedded1{
650+
Mode: util.IntToPtr(420),
651+
},
652+
},
653+
},
654+
},
655+
path.ContextPath{},
656+
nil,
657+
},
658+
// Empty parent file directory
659+
{
660+
Storage{
661+
Files: []File{
662+
{
663+
Path: "/foo/bar/txt.txt",
664+
Contents: Resource{},
665+
Mode: util.IntToPtr(420),
666+
Parent: Parent{
667+
Path: util.StrToPtr(""),
668+
Mode: util.IntToPtr(420),
669+
},
670+
},
671+
},
672+
},
673+
types.Storage{
674+
Files: []types.File{
675+
{
676+
Node: types.Node{
677+
Path: "/foo/bar/txt.txt",
678+
},
679+
FileEmbedded1: types.FileEmbedded1{
680+
Mode: util.IntToPtr(420),
681+
Contents: types.Resource{},
682+
},
683+
},
684+
},
685+
},
686+
path.ContextPath{},
687+
nil,
688+
},
689+
// Parent not defined
690+
{
691+
Storage{
692+
Files: []File{
693+
{
694+
Path: "/foo/bar/txt.txt",
695+
Contents: Resource{},
696+
Mode: util.IntToPtr(420),
697+
},
698+
},
699+
},
700+
types.Storage{
701+
Files: []types.File{
702+
{
703+
Node: types.Node{
704+
Path: "/foo/bar/txt.txt",
705+
},
706+
FileEmbedded1: types.FileEmbedded1{
707+
Mode: util.IntToPtr(420),
708+
Contents: types.Resource{},
709+
},
710+
},
711+
},
712+
},
713+
path.ContextPath{},
714+
nil,
715+
},
716+
717+
// Parent path is not related to file path
718+
{
719+
Storage{
720+
Files: []File{
721+
{
722+
Path: "/foo/bar/txt.txt",
723+
Contents: Resource{},
724+
Mode: util.IntToPtr(420),
725+
Parent: Parent{
726+
Path: util.StrToPtr("/godzilla"),
727+
Mode: util.IntToPtr(420),
728+
},
729+
},
730+
},
731+
},
732+
types.Storage{
733+
Files: []types.File{
734+
{
735+
Node: types.Node{
736+
Path: "/foo/bar/txt.txt",
737+
},
738+
FileEmbedded1: types.FileEmbedded1{
739+
Mode: util.IntToPtr(420),
740+
Contents: types.Resource{},
741+
},
742+
},
743+
},
744+
},
745+
path.New("yaml", "files", 0, "parent"),
746+
common.ErrInvalidParent,
747+
},
748+
}
749+
750+
for i, test := range tests {
751+
t.Run(fmt.Sprintf("translate %d", i), func(t *testing.T) {
752+
actual, translations, r := translateStorage(test.in, common.TranslateOptions{})
753+
r = confutil.TranslateReportPaths(r, translations)
754+
baseutil.VerifyReport(t, test.in, r)
755+
assert.Equal(t, test.out, actual, "translation mismatch")
756+
assert.NoError(t, translations.DebugVerifyCoverage(actual), "incomplete TranslationSet coverage")
757+
if test.errors != nil {
758+
expected := report.Report{}
759+
expected.AddOnError(test.errPath, test.errors)
760+
assert.Equal(t, expected, r, "bad report for test case %d", i)
761+
} else {
762+
assert.Equal(t, report.Report{}, r, "non-empty report")
763+
}
764+
})
765+
}
766+
}
602767

603768
// TestTranslateDirectory tests translating the ct storage.directories.[i] entries to ignition storage.directories.[i] entires.
604769
func TestTranslateDirectory(t *testing.T) {

config/common/errors.go

+1
Original file line numberDiff line numberDiff line change
@@ -89,6 +89,7 @@ var (
8989
ErrLinkSupport = errors.New("links are not supported in this spec version")
9090
ErrLuksSupport = errors.New("luks is not supported in this spec version")
9191
ErrRaidSupport = errors.New("raid is not supported in this spec version")
92+
ErrInvalidParent = errors.New("parent must be included in the file path")
9293

9394
// Grub
9495
ErrGrubUserNameNotSpecified = errors.New("field \"name\" is required")

docs/release-notes.md

+2
Original file line numberDiff line numberDiff line change
@@ -13,6 +13,8 @@ nav_order: 9
1313
### Features
1414

1515
- Support s390x layouts in `boot_device` section (fcos 1.6.0-exp, openshift 4.15.0-exp)
16+
- Add `parent` field to `files`, to reduce verbosity when configuring a deeply
17+
nested file. _(base 0.6.0-exp)_
1618

1719
### Bug fixes
1820

0 commit comments

Comments
 (0)