osbuild2: add support for org.osbuild.sysctld stage
Add support for a new osbuild stage `org.osbuild.sysctld`, for creating tmpfiles.d configuration files. Add unit tests for the new stage. Related to https://github.com/osbuild/osbuild/pull/804. Signed-off-by: Tomas Hozza <thozza@redhat.com>
This commit is contained in:
parent
a5097b2141
commit
d9610b97fc
4 changed files with 159 additions and 0 deletions
|
|
@ -91,6 +91,8 @@ func (stage *Stage) UnmarshalJSON(data []byte) error {
|
|||
options = new(ScriptStageOptions)
|
||||
case "org.osbuild.sysconfig":
|
||||
options = new(SysconfigStageOptions)
|
||||
case "org.osbuild.sysctld":
|
||||
options = new(SysctldStageOptions)
|
||||
case "org.osbuild.tmpfilesd":
|
||||
options = new(TmpfilesdStageOptions)
|
||||
case "org.osbuild.kernel-cmdline":
|
||||
|
|
|
|||
|
|
@ -499,6 +499,27 @@ func TestStage_UnmarshalJSON(t *testing.T) {
|
|||
data: []byte(`{"type":"org.osbuild.sysconfig","options":{"kernel":{"update_default":true,"default_kernel":"kernel"},"network":{"networking":true,"no_zero_conf":true},"network-scripts":{"ifcfg":{"eth0":{"bootproto":"dhcp","device":"eth0","ipv6init":false,"onboot":true,"peerdns":true,"type":"Ethernet","userctl":true},"eth1":{"bootproto":"dhcp","device":"eth1","ipv6init":true,"onboot":true,"peerdns":true,"type":"Ethernet","userctl":false}}}}}`),
|
||||
},
|
||||
},
|
||||
{
|
||||
name: "sysctld",
|
||||
fields: fields{
|
||||
Type: "org.osbuild.sysctld",
|
||||
Options: &SysctldStageOptions{
|
||||
Filename: "example.conf",
|
||||
Config: []SysctldConfigLine{
|
||||
{
|
||||
Key: "net.ipv4.conf.*.rp_filter",
|
||||
Value: "2",
|
||||
},
|
||||
{
|
||||
Key: "-net.ipv4.conf.all.rp_filter",
|
||||
},
|
||||
},
|
||||
},
|
||||
},
|
||||
args: args{
|
||||
data: []byte(`{"type":"org.osbuild.sysctld","options":{"filename":"example.conf","config":[{"key":"net.ipv4.conf.*.rp_filter","value":"2"},{"key":"-net.ipv4.conf.all.rp_filter"}]}}`),
|
||||
},
|
||||
},
|
||||
{
|
||||
name: "systemd",
|
||||
fields: fields{
|
||||
|
|
|
|||
66
internal/osbuild2/sysctld_stage.go
Normal file
66
internal/osbuild2/sysctld_stage.go
Normal file
|
|
@ -0,0 +1,66 @@
|
|||
package osbuild2
|
||||
|
||||
import (
|
||||
"encoding/json"
|
||||
"fmt"
|
||||
"strings"
|
||||
)
|
||||
|
||||
// SysctldStageOptions represents a single sysctl.d configuration file.
|
||||
type SysctldStageOptions 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 []SysctldConfigLine `json:"config"`
|
||||
}
|
||||
|
||||
func (SysctldStageOptions) isStageOptions() {}
|
||||
|
||||
// NewSysctldStageOptions creates a new PamLimitsConf Stage options object.
|
||||
func NewSysctldStageOptions(filename string, config []SysctldConfigLine) *SysctldStageOptions {
|
||||
return &SysctldStageOptions{
|
||||
Filename: filename,
|
||||
Config: config,
|
||||
}
|
||||
}
|
||||
|
||||
// Unexported alias for use in SysctldStageOptions's MarshalJSON() to prevent recursion
|
||||
type sysctldStageOptions SysctldStageOptions
|
||||
|
||||
func (o SysctldStageOptions) MarshalJSON() ([]byte, error) {
|
||||
if len(o.Config) == 0 {
|
||||
return nil, fmt.Errorf("the 'Config' list must contain at least one item")
|
||||
}
|
||||
options := sysctldStageOptions(o)
|
||||
return json.Marshal(options)
|
||||
}
|
||||
|
||||
// NewSysctldStage creates a new Sysctld Stage object.
|
||||
func NewSysctldStage(options *SysctldStageOptions) *Stage {
|
||||
return &Stage{
|
||||
Type: "org.osbuild.sysctld",
|
||||
Options: options,
|
||||
}
|
||||
}
|
||||
|
||||
// SysctldConfigLine represents a single line in a sysctl.d configuration.
|
||||
type SysctldConfigLine struct {
|
||||
// Kernel parameter name.
|
||||
// If the string starts with "-" and the Value is not set,
|
||||
// then the key is excluded from being set by a matching glob.
|
||||
Key string `json:"key"`
|
||||
// Kernel parameter value.
|
||||
// Must be set, unless the Key value starts with "-".
|
||||
Value string `json:"value,omitempty"`
|
||||
}
|
||||
|
||||
// Unexported alias for use in SysctldConfigLine's MarshalJSON() to prevent recursion.
|
||||
type sysctldConfigLine SysctldConfigLine
|
||||
|
||||
func (l SysctldConfigLine) MarshalJSON() ([]byte, error) {
|
||||
if l.Value == "" && !strings.HasPrefix(l.Key, "-") {
|
||||
return nil, fmt.Errorf("only Keys starting with '-' can have an empty Value")
|
||||
}
|
||||
line := sysctldConfigLine(l)
|
||||
return json.Marshal(line)
|
||||
}
|
||||
70
internal/osbuild2/sysctld_stage_test.go
Normal file
70
internal/osbuild2/sysctld_stage_test.go
Normal file
|
|
@ -0,0 +1,70 @@
|
|||
package osbuild2
|
||||
|
||||
import (
|
||||
"encoding/json"
|
||||
"testing"
|
||||
|
||||
"github.com/stretchr/testify/assert"
|
||||
)
|
||||
|
||||
func TestNewSysctldStageOptions(t *testing.T) {
|
||||
filename := "example.conf"
|
||||
config := []SysctldConfigLine{{
|
||||
Key: "net.ipv4.conf.default.rp_filter",
|
||||
Value: "2",
|
||||
}}
|
||||
|
||||
expectedOptions := &SysctldStageOptions{
|
||||
Filename: filename,
|
||||
Config: config,
|
||||
}
|
||||
actualOptions := NewSysctldStageOptions(filename, config)
|
||||
assert.Equal(t, expectedOptions, actualOptions)
|
||||
}
|
||||
|
||||
func TestNewSysctldStage(t *testing.T) {
|
||||
expectedStage := &Stage{
|
||||
Type: "org.osbuild.sysctld",
|
||||
Options: &SysctldStageOptions{},
|
||||
}
|
||||
actualStage := NewSysctldStage(&SysctldStageOptions{})
|
||||
assert.Equal(t, expectedStage, actualStage)
|
||||
}
|
||||
|
||||
func TestSysctldStageOptions_MarshalJSON_Invalid(t *testing.T) {
|
||||
tests := []struct {
|
||||
name string
|
||||
options SysctldStageOptions
|
||||
}{
|
||||
{
|
||||
name: "empty-options",
|
||||
options: SysctldStageOptions{},
|
||||
},
|
||||
}
|
||||
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)
|
||||
})
|
||||
}
|
||||
}
|
||||
|
||||
func TestSysctldConfigLine_MarshalJSON_Invalid(t *testing.T) {
|
||||
tests := []struct {
|
||||
name string
|
||||
options SysctldConfigLine
|
||||
}{
|
||||
{
|
||||
name: "no-value-without-prefix",
|
||||
options: SysctldConfigLine{
|
||||
Key: "key-without-prefix",
|
||||
},
|
||||
},
|
||||
}
|
||||
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)
|
||||
})
|
||||
}
|
||||
}
|
||||
Loading…
Add table
Add a link
Reference in a new issue