Skip to content

Commit

Permalink
change elemental handling
Browse files Browse the repository at this point in the history
  • Loading branch information
dbw7 committed Sep 24, 2024
1 parent d0a8af1 commit 6d381d6
Show file tree
Hide file tree
Showing 4 changed files with 194 additions and 12 deletions.
1 change: 1 addition & 0 deletions RELEASE_NOTES.md
Original file line number Diff line number Diff line change
Expand Up @@ -33,6 +33,7 @@
* An optional directory named `os-files` may be included to copy files into the resulting image's filesystem at runtime
* The `custom/files` directory may now include subdirectories, which will be maintained when copied to the image
* Elemental configuration now requires a registration code in order to install the necessary RPMs from the official sources
* Alternatively, the necessary Elemental RPMs can be manually side-loaded instead

## Bug Fixes

Expand Down
15 changes: 13 additions & 2 deletions pkg/eib/eib.go
Original file line number Diff line number Diff line change
Expand Up @@ -6,6 +6,8 @@ import (
"io/fs"
"os"
"path/filepath"
"slices"
"strings"
"time"

"github.com/suse-edge/edge-image-builder/pkg/build"
Expand Down Expand Up @@ -96,9 +98,18 @@ func appendElementalRPMs(ctx *image.Context) {
return
}

log.AuditInfo("Elemental registration is configured. The necessary RPM packages will be downloaded.")
appendRPMs(ctx, nil, combustion.ElementalPackages...)
rpmsPath := combustion.RPMsPath(ctx)
rpmDirEntries, err := os.ReadDir(rpmsPath)
if err != nil && !os.IsNotExist(err) {
zap.S().Warnf("Looking for '%s' dir failed unexpectedly: %s", rpmsPath, err)
}

if !slices.ContainsFunc(rpmDirEntries, func(entry os.DirEntry) bool {
return strings.Contains(entry.Name(), combustion.ElementalPackages[0])
}) {
log.AuditInfo("Elemental registration is configured. The necessary RPM packages will be downloaded.")
appendRPMs(ctx, nil, combustion.ElementalPackages...)
}
}

func appendFips(ctx *image.Context) {
Expand Down
49 changes: 43 additions & 6 deletions pkg/image/validation/elemental.go
Original file line number Diff line number Diff line change
Expand Up @@ -4,7 +4,10 @@ import (
"fmt"
"os"
"path/filepath"
"slices"
"strings"

"github.com/suse-edge/edge-image-builder/pkg/combustion"
"github.com/suse-edge/edge-image-builder/pkg/image"
)

Expand All @@ -29,14 +32,9 @@ func validateElemental(ctx *image.Context) []FailedValidation {
return failures
}

failures = append(failures, validateElementalConfiguration(ctx)...)
failures = append(failures, validateElementalDir(elementalConfigDir)...)

if ctx.ImageDefinition.OperatingSystem.Packages.RegCode == "" {
failures = append(failures, FailedValidation{
UserMessage: "Operating system package registration code field must be defined when using Elemental with SL Micro 6.0",
})
}

return failures
}

Expand Down Expand Up @@ -72,3 +70,42 @@ func validateElementalDir(elementalConfigDir string) []FailedValidation {

return failures
}

func validateElementalConfiguration(ctx *image.Context) []FailedValidation {
var failures []FailedValidation

rpmDirEntries, err := os.ReadDir(combustion.RPMsPath(ctx))
if err != nil && !os.IsNotExist(err) {
failures = append(failures, FailedValidation{
UserMessage: "RPM directory could not be read",
Error: err,
})
}

var foundPackages []string
var notFoundPackages []string
for _, pkg := range combustion.ElementalPackages {
if slices.ContainsFunc(rpmDirEntries, func(entry os.DirEntry) bool {
return strings.Contains(entry.Name(), pkg)
}) {
foundPackages = append(foundPackages, pkg)
} else {
notFoundPackages = append(notFoundPackages, pkg)
}
}

if len(foundPackages) == 0 {
if ctx.ImageDefinition.OperatingSystem.Packages.RegCode == "" {
failures = append(failures, FailedValidation{
UserMessage: fmt.Sprintf("Operating system package registration code field must be defined when using Elemental "+
"or the %s RPMs must be manually side-loaded", combustion.ElementalPackages),
})
}
} else if len(foundPackages) != len(combustion.ElementalPackages) {
failures = append(failures, FailedValidation{
UserMessage: fmt.Sprintf("Not all of the necessary Elemental packages are provided, packages found: %s, packages missing: %s", foundPackages, notFoundPackages),
})
}

return failures
}
141 changes: 137 additions & 4 deletions pkg/image/validation/elemental_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -17,7 +17,7 @@ func TestValidateElementalNoDir(t *testing.T) {
assert.Len(t, failures, 0)
}

func TestValidateElementalValid(t *testing.T) {
func TestValidateElemental(t *testing.T) {
configDir, err := os.MkdirTemp("", "eib-config-")
require.NoError(t, err)

Expand All @@ -35,7 +35,7 @@ func TestValidateElementalValid(t *testing.T) {
ImageDefinition *image.Definition
ExpectedFailedMessages []string
}{
`valid`: {
`valid, registration code no side-loaded rpms`: {
ImageDefinition: &image.Definition{
OperatingSystem: image.OperatingSystem{
Packages: image.Packages{
Expand All @@ -44,10 +44,11 @@ func TestValidateElementalValid(t *testing.T) {
},
},
},
`no registration code`: {
`invalid, no registration code no side-loaded rpms`: {
ImageDefinition: &image.Definition{},
ExpectedFailedMessages: []string{
"Operating system package registration code field must be defined when using Elemental with SL Micro 6.0",
"Operating system package registration code field must be defined when using Elemental or the " +
"[elemental-register elemental-system-agent] RPMs must be manually side-loaded",
},
},
}
Expand Down Expand Up @@ -148,3 +149,135 @@ func TestValidateElementalConfigDirUnreadable(t *testing.T) {

assert.Contains(t, failures[0].UserMessage, "Elemental config directory could not be read")
}

func TestValidateElementalConfigurationManualRPMsNoRegistrationCode(t *testing.T) {
configDir, err := os.MkdirTemp("", "eib-config-")
require.NoError(t, err)

defer func() {
assert.NoError(t, os.RemoveAll(configDir))
}()

ctx := &image.Context{
ImageConfigDir: configDir,
ImageDefinition: &image.Definition{},
}

elementalDir := filepath.Join(configDir, "elemental")
require.NoError(t, os.MkdirAll(elementalDir, os.ModePerm))

elementalConfig := filepath.Join(elementalDir, "elemental_config.yaml")
require.NoError(t, os.WriteFile(elementalConfig, []byte(""), 0o600))

rpmDir := filepath.Join(configDir, "rpms")
require.NoError(t, os.MkdirAll(rpmDir, os.ModePerm))

elementalAgent := filepath.Join(rpmDir, "elemental-system-agent.rpm")
require.NoError(t, os.WriteFile(elementalAgent, []byte(""), 0o600))

elementalRegister := filepath.Join(rpmDir, "elemental-register.rpm")
require.NoError(t, os.WriteFile(elementalRegister, []byte(""), 0o600))

failures := validateElementalConfiguration(ctx)
assert.Len(t, failures, 0)
}

func TestValidateElementalConfigurationManualRPMsWithRegistrationCode(t *testing.T) {
configDir, err := os.MkdirTemp("", "eib-config-")
require.NoError(t, err)

defer func() {
assert.NoError(t, os.RemoveAll(configDir))
}()

ctx := &image.Context{
ImageConfigDir: configDir,
ImageDefinition: &image.Definition{
OperatingSystem: image.OperatingSystem{
Packages: image.Packages{
RegCode: "registration-code",
},
},
},
}

elementalDir := filepath.Join(configDir, "elemental")
require.NoError(t, os.MkdirAll(elementalDir, os.ModePerm))

elementalConfig := filepath.Join(elementalDir, "elemental_config.yaml")
require.NoError(t, os.WriteFile(elementalConfig, []byte(""), 0o600))

rpmDir := filepath.Join(configDir, "rpms")
require.NoError(t, os.MkdirAll(rpmDir, os.ModePerm))

elementalAgent := filepath.Join(rpmDir, "elemental-system-agent.rpm")
require.NoError(t, os.WriteFile(elementalAgent, []byte(""), 0o600))

elementalRegister := filepath.Join(rpmDir, "elemental-register.rpm")
require.NoError(t, os.WriteFile(elementalRegister, []byte(""), 0o600))

failures := validateElementalConfiguration(ctx)
assert.Len(t, failures, 0)
}

func TestValidateElementalConfigurationManualRPMsMissingAgent(t *testing.T) {
configDir, err := os.MkdirTemp("", "eib-config-")
require.NoError(t, err)

defer func() {
assert.NoError(t, os.RemoveAll(configDir))
}()

ctx := &image.Context{
ImageConfigDir: configDir,
ImageDefinition: &image.Definition{},
}

elementalDir := filepath.Join(configDir, "elemental")
require.NoError(t, os.MkdirAll(elementalDir, os.ModePerm))

elementalConfig := filepath.Join(elementalDir, "elemental_config.yaml")
require.NoError(t, os.WriteFile(elementalConfig, []byte(""), 0o600))

rpmDir := filepath.Join(configDir, "rpms")
require.NoError(t, os.MkdirAll(rpmDir, os.ModePerm))

elementalRegister := filepath.Join(rpmDir, "elemental-register.rpm")
require.NoError(t, os.WriteFile(elementalRegister, []byte(""), 0o600))

failures := validateElementalConfiguration(ctx)
assert.Len(t, failures, 1)

assert.Contains(t, failures[0].UserMessage, "Not all of the necessary Elemental packages are provided, packages found: [elemental-register], packages missing: [elemental-system-agent]")
}

func TestValidateElementalConfigurationManualRPMsMissingRegister(t *testing.T) {
configDir, err := os.MkdirTemp("", "eib-config-")
require.NoError(t, err)

defer func() {
assert.NoError(t, os.RemoveAll(configDir))
}()

ctx := &image.Context{
ImageConfigDir: configDir,
ImageDefinition: &image.Definition{},
}

elementalDir := filepath.Join(configDir, "elemental")
require.NoError(t, os.MkdirAll(elementalDir, os.ModePerm))

elementalConfig := filepath.Join(elementalDir, "elemental_config.yaml")
require.NoError(t, os.WriteFile(elementalConfig, []byte(""), 0o600))

rpmDir := filepath.Join(configDir, "rpms")
require.NoError(t, os.MkdirAll(rpmDir, os.ModePerm))

elementalAgent := filepath.Join(rpmDir, "elemental-system-agent.rpm")
require.NoError(t, os.WriteFile(elementalAgent, []byte(""), 0o600))

failures := validateElementalConfiguration(ctx)
assert.Len(t, failures, 1)

assert.Contains(t, failures[0].UserMessage, "Not all of the necessary Elemental packages are provided, packages found: [elemental-system-agent], packages missing: [elemental-register]")
}

0 comments on commit 6d381d6

Please sign in to comment.