Skip to content

Commit

Permalink
OSModifier: Add services and modules support in EMU API (#27)
Browse files Browse the repository at this point in the history
<!-- Description: Please provide a summary of the changes and the
motivation behind them. -->

---
cherry picked commits from the approved pr:
microsoft/azurelinux#11153

What does the PR accomplish, why was it needed?

the goal is to add services and modules support in EMU API to support
trident.

- Create a modulelist type to avoid code duplication
- Add functions to update services and modules in osmodifier

### **Checklist**
- [x] Tests added/updated
- [x] Documentation updated (if needed)
- [x] Code conforms to style guidelines
  • Loading branch information
elainezhao96 authored Dec 11, 2024
1 parent d60055e commit 6b4ec8f
Show file tree
Hide file tree
Showing 9 changed files with 104 additions and 65 deletions.
21 changes: 21 additions & 0 deletions toolkit/tools/imagecustomizerapi/module.go
Original file line number Diff line number Diff line change
Expand Up @@ -14,6 +14,27 @@ type Module struct {
Options map[string]string `yaml:"options"`
}

type ModuleList struct {
Modules []Module `yaml:"modules"`
}

func (m *ModuleList) IsValid() error {
moduleMap := make(map[string]int)
for i, module := range m.Modules {
// Check if module is duplicated to avoid conflicts with modules potentially having different LoadMode
if _, exists := moduleMap[module.Name]; exists {
return fmt.Errorf("duplicate module found: %s at index %d", module.Name, i)
}
moduleMap[module.Name] = i
err := module.IsValid()
if err != nil {
return fmt.Errorf("invalid modules item at index %d:\n%w", i, err)
}
}

return nil
}

func (m *Module) IsValid() error {
if err := validateModuleName(m.Name); err != nil {
return err
Expand Down
15 changes: 3 additions & 12 deletions toolkit/tools/imagecustomizerapi/os.go
Original file line number Diff line number Diff line change
Expand Up @@ -21,7 +21,7 @@ type OS struct {
AdditionalDirs DirConfigList `yaml:"additionalDirs"`
Users []User `yaml:"users"`
Services Services `yaml:"services"`
Modules []Module `yaml:"modules"`
Modules ModuleList `yaml:"modules"`
Overlays *[]Overlay `yaml:"overlays"`
}

Expand Down Expand Up @@ -69,17 +69,8 @@ func (s *OS) IsValid() error {
return err
}

moduleMap := make(map[string]int)
for i, module := range s.Modules {
// Check if module is duplicated to avoid conflicts with modules potentially having different LoadMode
if _, exists := moduleMap[module.Name]; exists {
return fmt.Errorf("duplicate module found: %s at index %d", module.Name, i)
}
moduleMap[module.Name] = i
err = module.IsValid()
if err != nil {
return fmt.Errorf("invalid modules item at index %d:\n%w", i, err)
}
if err := s.Modules.IsValid(); err != nil {
return err
}

if s.Overlays != nil {
Expand Down
22 changes: 13 additions & 9 deletions toolkit/tools/imagecustomizerapi/os_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -133,9 +133,11 @@ func TestOSIsValidInvalidServices(t *testing.T) {

func TestOSIsValidInvalidModule(t *testing.T) {
os := OS{
Modules: []Module{
{
Name: "",
Modules: ModuleList{
Modules: []Module{
{
Name: "",
},
},
},
}
Expand All @@ -147,12 +149,14 @@ func TestOSIsValidInvalidModule(t *testing.T) {

func TestOSIsValidModuleDuplicateName(t *testing.T) {
os := OS{
Modules: []Module{
{
Name: "nbd",
},
{
Name: "nbd",
Modules: ModuleList{
Modules: []Module{
{
Name: "nbd",
},
{
Name: "nbd",
},
},
},
}
Expand Down
11 changes: 8 additions & 3 deletions toolkit/tools/osmodifierapi/os.go
Original file line number Diff line number Diff line change
Expand Up @@ -18,6 +18,8 @@ type OS struct {
Users []imagecustomizerapi.User `yaml:"users"`
Overlays *[]Overlay `yaml:"overlays"`
KernelCommandLine imagecustomizerapi.KernelCommandLine `yaml:"kernelCommandLine"`
Services imagecustomizerapi.Services `yaml:"services"`
Modules imagecustomizerapi.ModuleList `yaml:"modules"`
}

func (s *OS) IsValid() error {
Expand Down Expand Up @@ -66,9 +68,12 @@ func (s *OS) IsValid() error {
}
}

err = s.KernelCommandLine.IsValid()
if err != nil {
return fmt.Errorf("invalid kernelCommandLine:\n%w", err)
if err := s.Services.IsValid(); err != nil {
return err
}

if err := s.Modules.IsValid(); err != nil {
return err
}

return nil
Expand Down
4 changes: 2 additions & 2 deletions toolkit/tools/pkg/imagecustomizerlib/customizeos.go
Original file line number Diff line number Diff line change
Expand Up @@ -49,12 +49,12 @@ func doOsCustomizations(buildDir string, baseConfigPath string, config *imagecus
return err
}

err = enableOrDisableServices(config.OS.Services, imageChroot)
err = EnableOrDisableServices(config.OS.Services, imageChroot)
if err != nil {
return err
}

err = loadOrDisableModules(config.OS.Modules, imageChroot.RootDir())
err = LoadOrDisableModules(config.OS.Modules, imageChroot.RootDir())
if err != nil {
return err
}
Expand Down
2 changes: 1 addition & 1 deletion toolkit/tools/pkg/imagecustomizerlib/customizeservices.go
Original file line number Diff line number Diff line change
Expand Up @@ -13,7 +13,7 @@ import (
"github.com/microsoft/azurelinux/toolkit/tools/internal/systemd"
)

func enableOrDisableServices(services imagecustomizerapi.Services, imageChroot safechroot.ChrootInterface) error {
func EnableOrDisableServices(services imagecustomizerapi.Services, imageChroot safechroot.ChrootInterface) error {
var err error

// Handle enabling services
Expand Down
4 changes: 2 additions & 2 deletions toolkit/tools/pkg/imagecustomizerlib/kernelmoduleutils.go
Original file line number Diff line number Diff line change
Expand Up @@ -27,7 +27,7 @@ const (
moduleOptionsPath = modprobeConfigDir + "/" + moduleOptionsFileName
)

func loadOrDisableModules(modules []imagecustomizerapi.Module, rootDir string) error {
func LoadOrDisableModules(modules imagecustomizerapi.ModuleList, rootDir string) error {
var err error
var modulesToLoad []string
var modulesToDisable []string
Expand All @@ -36,7 +36,7 @@ func loadOrDisableModules(modules []imagecustomizerapi.Module, rootDir string) e
moduleLoadFilePath := filepath.Join(rootDir, moduleLoadPath)
moduleOptionsFilePath := filepath.Join(rootDir, moduleOptionsPath)

for i, module := range modules {
for i, module := range modules.Modules {
switch module.LoadMode {
case imagecustomizerapi.ModuleLoadModeAlways:
// If a module is disabled, remove it. Add the module to modules-load.d/. Write options if provided.
Expand Down
80 changes: 44 additions & 36 deletions toolkit/tools/pkg/imagecustomizerlib/kernelmoduleutils_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -16,24 +16,26 @@ import (

func TestLoadOrDisableModules(t *testing.T) {
rootDir := filepath.Join(tmpDir, "TestLoadOrDisableModules")
modules := []imagecustomizerapi.Module{
{
Name: "module1",
LoadMode: imagecustomizerapi.ModuleLoadModeAlways,
Options: map[string]string{"option1": "value1"},
},
{
Name: "module2",
LoadMode: imagecustomizerapi.ModuleLoadModeDisable,
},
{
Name: "module3",
LoadMode: imagecustomizerapi.ModuleLoadModeAuto,
Options: map[string]string{"option3_1": "value3_1", "option3_2": "value3_2"},
modules := imagecustomizerapi.ModuleList{
Modules: []imagecustomizerapi.Module{
{
Name: "module1",
LoadMode: imagecustomizerapi.ModuleLoadModeAlways,
Options: map[string]string{"option1": "value1"},
},
{
Name: "module2",
LoadMode: imagecustomizerapi.ModuleLoadModeDisable,
},
{
Name: "module3",
LoadMode: imagecustomizerapi.ModuleLoadModeAuto,
Options: map[string]string{"option3_1": "value3_1", "option3_2": "value3_2"},
},
},
}

err := loadOrDisableModules(modules, rootDir)
err := LoadOrDisableModules(modules, rootDir)
assert.NoError(t, err)

moduleLoadFilePath := filepath.Join(rootDir, moduleLoadPath)
Expand Down Expand Up @@ -61,29 +63,33 @@ func TestLoadOrDisableModules(t *testing.T) {
assert.Contains(t, string(moduleOptionsContent), "option3_2=value3_2")

// Test add options for module2 which was disabled
modules = []imagecustomizerapi.Module{
{
Name: "module2",
Options: map[string]string{"option2": "value2"},
modules = imagecustomizerapi.ModuleList{
Modules: []imagecustomizerapi.Module{
{
Name: "module2",
Options: map[string]string{"option2": "value2"},
},
},
}

err = loadOrDisableModules(modules, rootDir)
err = LoadOrDisableModules(modules, rootDir)
assert.Contains(t, err.Error(), "cannot add options for disabled module (module2)")

// Test updating module2's loadmode and module3's option
modules = []imagecustomizerapi.Module{
{
Name: "module2",
LoadMode: imagecustomizerapi.ModuleLoadModeAuto,
Options: map[string]string{"option2": "value2"},
},
{
Name: "module3",
Options: map[string]string{"option3_1": "new_value3_1", "option3_3": "new_value3_3"},
modules = imagecustomizerapi.ModuleList{
Modules: []imagecustomizerapi.Module{
{
Name: "module2",
LoadMode: imagecustomizerapi.ModuleLoadModeAuto,
Options: map[string]string{"option2": "value2"},
},
{
Name: "module3",
Options: map[string]string{"option3_1": "new_value3_1", "option3_3": "new_value3_3"},
},
},
}
err = loadOrDisableModules(modules, rootDir)
err = LoadOrDisableModules(modules, rootDir)
assert.NoError(t, err)

moduleDisableContent, _ = os.ReadFile(moduleDisableFilePath)
Expand All @@ -96,15 +102,17 @@ func TestLoadOrDisableModules(t *testing.T) {
assert.Contains(t, string(moduleOptionsContent), "option3_3=new_value3_3")

// Test case where a module was already set to load at boot
modules = []imagecustomizerapi.Module{
{
Name: "module1",
LoadMode: imagecustomizerapi.ModuleLoadModeAlways,
Options: map[string]string{"option1": "value1"},
modules = imagecustomizerapi.ModuleList{
Modules: []imagecustomizerapi.Module{
{
Name: "module1",
LoadMode: imagecustomizerapi.ModuleLoadModeAlways,
Options: map[string]string{"option1": "value1"},
},
},
}

err = loadOrDisableModules(modules, rootDir)
err = LoadOrDisableModules(modules, rootDir)
assert.NoError(t, err)

moduleLoadContent, _ = os.ReadFile(moduleLoadFilePath)
Expand Down
10 changes: 10 additions & 0 deletions toolkit/tools/pkg/osmodifierlib/modifierutils.go
Original file line number Diff line number Diff line change
Expand Up @@ -27,6 +27,16 @@ func doModifications(baseConfigPath string, osConfig *osmodifierapi.OS) error {
return err
}

err = imagecustomizerlib.EnableOrDisableServices(osConfig.Services, dummyChroot)
if err != nil {
return err
}

err = imagecustomizerlib.LoadOrDisableModules(osConfig.Modules, dummyChroot.RootDir())
if err != nil {
return err
}

if osConfig.Overlays != nil {
bootCustomizer, err := imagecustomizerlib.NewBootCustomizer(dummyChroot)
if err != nil {
Expand Down

0 comments on commit 6b4ec8f

Please sign in to comment.