Skip to content

Commit

Permalink
blueprints: fix filesystem customizations to allow toml Undecoded()
Browse files Browse the repository at this point in the history
The current toml filesytem customizations will decode a
`FilesystemCustomization` as a map[string]interface{}. This means
that the underlying toml engine will not konw what keys inside the
map are decoded and which are not decoded. This lead to the issue
osbuild/bootc-image-builder#655 in
`bootc-image-builder` where we check if we have unencoded entries
and if so error (in this case incorrectly).

This commit fixes the issue by moving the toml decoding from the
struct/map[string]interface{} to primitive types. It's a bit
ugly as is (partly because of the limited go type system) but
with that the tests pass and len(meta.Undecoded()) is the expected
zero.

I opened BurntSushi/toml#425 to see if
there is another way via a custom unmarshaler.
  • Loading branch information
mvo5 committed Sep 24, 2024
1 parent 50151b3 commit 709353c
Show file tree
Hide file tree
Showing 3 changed files with 38 additions and 17 deletions.
2 changes: 1 addition & 1 deletion pkg/blueprint/customizations.go
Original file line number Diff line number Diff line change
Expand Up @@ -301,7 +301,7 @@ func (c *Customizations) GetFilesystemsMinSize() uint64 {
}
var agg uint64
for _, m := range c.Filesystem {
agg += m.MinSize
agg += uint64(m.MinSize)
}
// This ensures that file system customization `size` is a multiple of
// sector size (512)
Expand Down
37 changes: 21 additions & 16 deletions pkg/blueprint/filesystem_customizations.go
Original file line number Diff line number Diff line change
Expand Up @@ -10,31 +10,36 @@ import (
)

type FilesystemCustomization struct {
Mountpoint string `json:"mountpoint,omitempty" toml:"mountpoint,omitempty"`
MinSize uint64 `json:"minsize,omitempty" toml:"minsize,omitempty"`
Mountpoint Mountpoint `json:"mountpoint,omitempty" toml:"mountpoint,omitempty"`
MinSize MinSize `json:"minsize,omitempty" toml:"minsize,omitempty"`
}

func (fsc *FilesystemCustomization) UnmarshalTOML(data interface{}) error {
d, _ := data.(map[string]interface{})
type Mountpoint string

switch d["mountpoint"].(type) {
func (mt *Mountpoint) UnmarshalTOML(d interface{}) error {
switch d.(type) {
case string:
fsc.Mountpoint = d["mountpoint"].(string)
*mt = Mountpoint(d.(string))
default:
return fmt.Errorf("TOML unmarshal: mountpoint must be string, got %v of type %T", d["mountpoint"], d["mountpoint"])
return fmt.Errorf("TOML unmarshal: mountpoint must be string, got %v of type %T", d, d)
}
return nil
}

switch d["minsize"].(type) {
type MinSize uint64

func (ms *MinSize) UnmarshalTOML(d interface{}) error {
switch d.(type) {
case int64:
fsc.MinSize = uint64(d["minsize"].(int64))
*ms = MinSize(d.(int64))
case string:
minSize, err := common.DataSizeToUint64(d["minsize"].(string))
minSize, err := common.DataSizeToUint64(d.(string))
if err != nil {
return fmt.Errorf("TOML unmarshal: minsize is not valid filesystem size (%w)", err)
}
fsc.MinSize = minSize
*ms = MinSize(minSize)
default:
return fmt.Errorf("TOML unmarshal: minsize must be integer or string, got %v of type %T", d["minsize"], d["minsize"])
return fmt.Errorf("TOML unmarshal: minsize must be integer or string, got %v of type %T", d, d)
}

return nil
Expand All @@ -49,21 +54,21 @@ func (fsc *FilesystemCustomization) UnmarshalJSON(data []byte) error {

switch d["mountpoint"].(type) {
case string:
fsc.Mountpoint = d["mountpoint"].(string)
fsc.Mountpoint = Mountpoint(d["mountpoint"].(string))
default:
return fmt.Errorf("JSON unmarshal: mountpoint must be string, got %v of type %T", d["mountpoint"], d["mountpoint"])
}

// The JSON specification only mentions float64 and Go defaults to it: https://go.dev/blog/json
switch d["minsize"].(type) {
case float64:
fsc.MinSize = uint64(d["minsize"].(float64))
fsc.MinSize = MinSize(d["minsize"].(float64))
case string:
minSize, err := common.DataSizeToUint64(d["minsize"].(string))
if err != nil {
return fmt.Errorf("JSON unmarshal: minsize is not valid filesystem size (%w)", err)
}
fsc.MinSize = minSize
fsc.MinSize = MinSize(minSize)
default:
return fmt.Errorf("JSON unmarshal: minsize must be float64 number or string, got %v of type %T", d["minsize"], d["minsize"])
}
Expand All @@ -75,7 +80,7 @@ func (fsc *FilesystemCustomization) UnmarshalJSON(data []byte) error {
func CheckMountpointsPolicy(mountpoints []FilesystemCustomization, mountpointAllowList *pathpolicy.PathPolicies) error {
var errs []error
for _, m := range mountpoints {
if err := mountpointAllowList.Check(m.Mountpoint); err != nil {
if err := mountpointAllowList.Check(string(m.Mountpoint)); err != nil {
errs = append(errs, err)
}
}
Expand Down
16 changes: 16 additions & 0 deletions pkg/blueprint/filesystem_customizations_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -120,3 +120,19 @@ path "/boot/" must be canonical`
err := blueprint.CheckMountpointsPolicy(mps, policy)
assert.EqualError(t, err, expectedErr)
}

func TestReadConfigNoUndecoded(t *testing.T) {
testTOML := `
mountpoint = "/foo"
minsize = 1000
`

var fsc blueprint.FilesystemCustomization
meta, err := toml.Decode(testTOML, &fsc)
assert.NoError(t, err)
assert.Equal(t, blueprint.FilesystemCustomization{
Mountpoint: "/foo",
MinSize: 1000,
}, fsc)
assert.Equal(t, 0, len(meta.Undecoded()))
}

0 comments on commit 709353c

Please sign in to comment.