osbuild2: add support for org.osbuild.tmpfilesd stage

Add support for a new osbuild stage `org.osbuild.tmpfilesd`, for
creating tmpfiles.d configuration files.

Add unit tests for the new stage.

Related to https://github.com/osbuild/osbuild/pull/801.

Signed-off-by: Tomas Hozza <thozza@redhat.com>
This commit is contained in:
Tomas Hozza 2021-09-09 08:22:52 +02:00 committed by Ondřej Budai
parent fbb70c2d10
commit 3f52af2adb
4 changed files with 131 additions and 0 deletions

View file

@ -91,6 +91,8 @@ func (stage *Stage) UnmarshalJSON(data []byte) error {
options = new(ScriptStageOptions)
case "org.osbuild.sysconfig":
options = new(SysconfigStageOptions)
case "org.osbuild.tmpfilesd":
options = new(TmpfilesdStageOptions)
case "org.osbuild.kernel-cmdline":
options = new(KernelCmdlineStageOptions)
case "org.osbuild.rpm":

View file

@ -526,6 +526,24 @@ func TestStage_UnmarshalJSON(t *testing.T) {
data: []byte(`{"type":"org.osbuild.timezone","options":{"zone":""}}`),
},
},
{
name: "tmpfilesd",
fields: fields{
Type: "org.osbuild.tmpfilesd",
Options: &TmpfilesdStageOptions{
Filename: "example.conf",
Config: []TmpfilesdConfigLine{
{
Type: "d",
Path: "/tmp/my-example-path",
},
},
},
},
args: args{
data: []byte(`{"type":"org.osbuild.tmpfilesd","options":{"filename":"example.conf","config":[{"type":"d","path":"/tmp/my-example-path"}]}}`),
},
},
{
name: "users",
fields: fields{

View file

@ -0,0 +1,61 @@
package osbuild2
import (
"encoding/json"
"fmt"
)
// TmpfilesdStageOptions represents a single tmpfiles.d configuration file.
type TmpfilesdStageOptions struct {
// Filename of the configuration file to be created. Must end with '.conf'.
Filename string `json:"filename"`
// List of configuration directives. The list must contain at least one item.
Config []TmpfilesdConfigLine `json:"config"`
}
func (TmpfilesdStageOptions) isStageOptions() {}
// NewTmpfilesdStageOptions creates a new Tmpfilesd Stage options object.
func NewTmpfilesdStageOptions(filename string, config []TmpfilesdConfigLine) *TmpfilesdStageOptions {
return &TmpfilesdStageOptions{
Filename: filename,
Config: config,
}
}
// Unexported alias for use in TmpfilesdStageOptions's MarshalJSON() to prevent recursion
type tmpfilesdStageOptions TmpfilesdStageOptions
func (o TmpfilesdStageOptions) MarshalJSON() ([]byte, error) {
if len(o.Config) == 0 {
return nil, fmt.Errorf("the 'Config' list must contain at least one item")
}
options := tmpfilesdStageOptions(o)
return json.Marshal(options)
}
// NewTmpfilesdStage creates a new Tmpfilesd Stage object.
func NewTmpfilesdStage(options *TmpfilesdStageOptions) *Stage {
return &Stage{
Type: "org.osbuild.tmpfilesd",
Options: options,
}
}
// TmpfilesdConfigLine represents a single line in a tmpfiles.d configuration.
type TmpfilesdConfigLine struct {
// The file system path type
Type string `json:"type"`
// Absolute file system path
Path string `json:"path"`
// The file access mode when creating the file or directory
Mode string `json:"mode,omitempty"`
// The user to use for the file or directory
User string `json:"user,omitempty"`
// The group to use for the file or directory
Group string `json:"group,omitempty"`
// Date field used to decide what files to delete when cleaning
Age string `json:"age,omitempty"`
// Argument with its meaning being specific to the path type
Argument string `json:"argument,omitempty"`
}

View file

@ -0,0 +1,50 @@
package osbuild2
import (
"encoding/json"
"testing"
"github.com/stretchr/testify/assert"
)
func TestNewTmpfilesdStageOptions(t *testing.T) {
filename := "example.conf"
config := []TmpfilesdConfigLine{{
Type: "d",
Path: "/tmp/my-example-path",
}}
expectedOptions := &TmpfilesdStageOptions{
Filename: filename,
Config: config,
}
actualOptions := NewTmpfilesdStageOptions(filename, config)
assert.Equal(t, expectedOptions, actualOptions)
}
func TestNewTmpfilesdStage(t *testing.T) {
expectedStage := &Stage{
Type: "org.osbuild.tmpfilesd",
Options: &TmpfilesdStageOptions{},
}
actualStage := NewTmpfilesdStage(&TmpfilesdStageOptions{})
assert.Equal(t, expectedStage, actualStage)
}
func TestTmpfilesdStageOptions_MarshalJSON_Invalid(t *testing.T) {
tests := []struct {
name string
options TmpfilesdStageOptions
}{
{
name: "empty-options",
options: TmpfilesdStageOptions{},
},
}
for idx, tt := range tests {
t.Run(tt.name, func(t *testing.T) {
gotBytes, err := json.Marshal(tt.options)
assert.NotNilf(t, err, "json.Marshal() didn't return an error, but: %s [idx: %d]", string(gotBytes), idx)
})
}
}