osbuild2: add support for org.osbuild.modprobe stage

Add support for the `org.osbuild.modprobe` osbuild stage [1], which
allows one to configure modprobe by creating configuration files.

Add unit test cases for the newly added stage.

[1] https://github.com/osbuild/osbuild/pull/670

Signed-off-by: Tomas Hozza <thozza@redhat.com>
This commit is contained in:
Tomas Hozza 2021-06-29 17:52:00 +02:00 committed by Ondřej Budai
parent 9386a02984
commit c0e8d4bdc4
4 changed files with 171 additions and 0 deletions

View file

@ -0,0 +1,91 @@
package osbuild2
import (
"encoding/json"
"fmt"
)
type ModprobeStageOptions struct {
ConfigFiles map[string]ModprobeConfigCmdList `json:"configuration_files,omitempty"`
}
func (ModprobeStageOptions) isStageOptions() {}
func NewModprobeStage(options *ModprobeStageOptions) *Stage {
return &Stage{
Type: "org.osbuild.modprobe",
Options: options,
}
}
type ModprobeConfigCmd interface {
isModprobeConfigCmd()
}
// ModprobeConfigCmdList represents a modprobe configuration file, which contains
// a list of commands.
type ModprobeConfigCmdList []ModprobeConfigCmd
func (configFile *ModprobeConfigCmdList) UnmarshalJSON(data []byte) error {
var rawConfigFile []interface{}
if err := json.Unmarshal(data, &rawConfigFile); err != nil {
return err
}
for _, rawConfigCmd := range rawConfigFile {
var modprobeCmd ModprobeConfigCmd
// The command object structure depends on the value of "command"
// item, therefore make no assumptions on the structure.
configCmdMap, ok := rawConfigCmd.(map[string]interface{})
if !ok {
return fmt.Errorf("unexpected modprobe configuration file format")
}
command, ok := configCmdMap["command"].(string)
if !ok {
return fmt.Errorf("'command' item should be string, not %T", configCmdMap["command"])
}
switch command {
case "blacklist":
modulename, ok := configCmdMap["modulename"].(string)
if !ok {
return fmt.Errorf("'modulename' item should be string, not %T", configCmdMap["modulename"])
}
modprobeCmd = NewModprobeConfigCmdBlacklist(modulename)
default:
return fmt.Errorf("unexpected modprobe command: %s", command)
}
*configFile = append(*configFile, modprobeCmd)
}
return nil
}
func (o ModprobeConfigCmdList) MarshalJSON() ([]byte, error) {
if len(o) == 0 {
return nil, fmt.Errorf("at least one modprobe command must be specified for a configuration file")
}
var configList []ModprobeConfigCmd = o
return json.Marshal(configList)
}
// ModprobeConfigCmdBlacklist represents the 'blacklist' command in the
// modprobe configuration.
type ModprobeConfigCmdBlacklist struct {
Command string `json:"command"`
Modulename string `json:"modulename"`
}
func (ModprobeConfigCmdBlacklist) isModprobeConfigCmd() {}
// NewModprobeConfigCmdBlacklist creates a new instance of ModprobeConfigCmdBlacklist
// for the provided modulename.
func NewModprobeConfigCmdBlacklist(modulename string) *ModprobeConfigCmdBlacklist {
return &ModprobeConfigCmdBlacklist{
Command: "blacklist",
Modulename: modulename,
}
}

View file

@ -0,0 +1,39 @@
package osbuild2
import (
"encoding/json"
"testing"
"github.com/stretchr/testify/assert"
)
func TestNewModprobeStage(t *testing.T) {
expectedStage := &Stage{
Type: "org.osbuild.modprobe",
Options: &ModprobeStageOptions{},
}
actualStage := NewModprobeStage(&ModprobeStageOptions{})
assert.Equal(t, expectedStage, actualStage)
}
func TestModprobeStage_MarshalJSON_Invalid(t *testing.T) {
tests := []struct {
name string
options ModprobeStageOptions
}{
{
name: "no-commands",
options: ModprobeStageOptions{
ConfigFiles: map[string]ModprobeConfigCmdList{
"disallow-modules.conf": {},
},
},
},
}
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)
})
}
}

View file

@ -89,6 +89,8 @@ func (stage *Stage) UnmarshalJSON(data []byte) error {
options = new(ChronyStageOptions)
case "org.osbuild.keymap":
options = new(KeymapStageOptions)
case "org.osbuild.modprobe":
options = new(ModprobeStageOptions)
case "org.osbuild.firewall":
options = new(FirewallStageOptions)
case "org.osbuild.rhsm":

View file

@ -162,6 +162,45 @@ func TestStage_UnmarshalJSON(t *testing.T) {
data: []byte(`{"type":"org.osbuild.keymap","options":{"keymap":""}}`),
},
},
{
name: "modprobe",
fields: fields{
Type: "org.osbuild.modprobe",
Options: &ModprobeStageOptions{},
},
args: args{
data: []byte(`{"type":"org.osbuild.modprobe","options":{}}`),
},
},
{
name: "modprobe-data",
fields: fields{
Type: "org.osbuild.modprobe",
Options: &ModprobeStageOptions{
ConfigFiles: map[string]ModprobeConfigCmdList{
"disallow-modules.conf": {
&ModprobeConfigCmdBlacklist{
Command: "blacklist",
Modulename: "nouveau",
},
&ModprobeConfigCmdBlacklist{
Command: "blacklist",
Modulename: "floppy",
},
},
"disallow-additional-modules.conf": {
&ModprobeConfigCmdBlacklist{
Command: "blacklist",
Modulename: "my-module",
},
},
},
},
},
args: args{
data: []byte(`{"type":"org.osbuild.modprobe","options":{"configuration_files":{"disallow-additional-modules.conf":[{"command":"blacklist","modulename":"my-module"}],"disallow-modules.conf":[{"command":"blacklist","modulename":"nouveau"},{"command":"blacklist","modulename":"floppy"}]}}}`),
},
},
{
name: "locale",
fields: fields{