Skip to content

Commit

Permalink
Merge branch 'suse-edge:main' into main
Browse files Browse the repository at this point in the history
  • Loading branch information
dbw7 authored Sep 24, 2024
2 parents 5d274a9 + d0a8af1 commit 39f9682
Show file tree
Hide file tree
Showing 8 changed files with 255 additions and 74 deletions.
75 changes: 21 additions & 54 deletions RELEASE_NOTES.md
Original file line number Diff line number Diff line change
@@ -1,34 +1,20 @@
# Edge Image Builder Releases

# Next
# v1.1.0

## General

* Extracted the K3S and RKE2 SELinux package and repository definitions into artifacts.yaml

## API

### Image Definition Changes

### Image Configuration Directory Changes

## Bug Fixes

* [#565](https://github.com/suse-edge/edge-image-builder/issues/565) - K3S SELinux uses an outdated package

---

# v1.1.0-rc2

## General

* The "custom files" functionality may now include directories, which will be maintained when copied to the image
* Improved Kubernetes definition validation
* Allow RKE2 deployments with Calico, Cilium and Multus on aarch64 platforms
* OS files and user provided certificates now maintain original permissions when copied to the final image
* Adds support for customizing SL Micro 6.0 base images (for SLE Micro 5.5 images, EIB 1.0.x must still be used)
* Added the ability to build aarch64 images on an aarch64 host machine
* Added the ability to automatically copy files into the built images filesystem (see Image Configuration Directory Changes below)
* Kubernetes manifests are now applied in a systemd service instead of using the `/manifests` directory
* Helm chart installation backOffLimit changed from 1000(default) to 20
* Improved Kubernetes resource installation handling
* Ensure that kernel arguments are applied during firstboot when kexec is used in ISO installations
* Added Elemental configuration validation
* Dropped `-chart` suffix from installed Helm chart names
* Added caching for container images
* Added built image name output to build command
* Leftover combustion artifacts are now removed on first boot
* OS files and user provided certificates now maintain original permissions when copied to the final image
* Dependency upgrades
* "Phone Home" deployments are now utilizing Elemental v1.6 (upgraded from v1.4)
* Embedded registry is now utilizing Hauler v1.0.7 (upgraded from v1.0.1)
Expand All @@ -38,43 +24,24 @@

### Image Definition Changes

* Introduced a dedicated FIPS mode option, adding the required packages, kernel arguments, and crypto selection

## Bug Fixes

* [#491](https://github.com/suse-edge/edge-image-builder/issues/491) - Large Helm manifests fail to install
* [#543](https://github.com/suse-edge/edge-image-builder/issues/543) - Kernel cmdline arguments aren't honoured in SL Micro 6.0 for SelfInstall ISO's
* [#550](https://github.com/suse-edge/edge-image-builder/issues/550) - PackageHub inclusion in RPM resolution silently errors on SLE Micro 6.0

---

# v1.1.0-rc1

## General

* Added the ability to automatically copy files into the built images filesystem
* Kubernetes manifests are now applied in a systemd service
* Artifact sources origin and metadata are now extracted from a configuration file (`config/artifacts.yaml`)
* Dropped `-chart` suffix from installed Helm chart names
* Added ability to build aarch64 images on an aarch64 host machine
* Added caching for container images
* Added built image name output to build command
* Leftover combustion artifacts are now removed on first boot

## API

### Image Definition Changes

* The `apiVersion` field now supports both `1.0` and `1.1` values
* The current version of the image definition has been incremented to `1.1` to include the changes below
* Existing definitions using the `1.0` version of the schema will continue to work with EIB
* Introduced a dedicated FIPS mode option (`enableFIPS`) which will enable FIPS mode on the node

### Image Configuration Directory Changes

* 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

## Bug Fixes

* [#498](https://github.com/suse-edge/edge-image-builder/issues/498) - Fix kernelArgs issue with Leap Micro 6.0
* [#481](https://github.com/suse-edge/edge-image-builder/issues/481) - Certain Helm charts fail when templated without specified API Versions
* [#491](https://github.com/suse-edge/edge-image-builder/issues/491) - Large Helm manifests fail to install
* [#498](https://github.com/suse-edge/edge-image-builder/issues/498) - Fix kernelArgs issue with Leap Micro 6.0
* [#543](https://github.com/suse-edge/edge-image-builder/issues/543) - Kernel cmdline arguments aren't honoured in SL Micro 6.0 for SelfInstall ISO's
* [#550](https://github.com/suse-edge/edge-image-builder/issues/550) - PackageHub inclusion in RPM resolution silently errors on SLE Micro 6.0
* [#565](https://github.com/suse-edge/edge-image-builder/issues/565) - K3S SELinux uses an outdated package

---

Expand Down
3 changes: 0 additions & 3 deletions config/artifacts.yaml
Original file line number Diff line number Diff line change
Expand Up @@ -6,9 +6,6 @@ endpoint-copier-operator:
chart: endpoint-copier-operator
repository: https://suse-edge.github.io/charts
version: 0.2.1
elemental:
register-repository: https://download.opensuse.org/repositories/isv:/Rancher:/Elemental:/Staging/standard
system-agent-repository: https://download.opensuse.org/repositories/isv:/Rancher:/Elemental:/Staging/standard
kubernetes:
k3s:
selinuxPackage: k3s-selinux-1.6-1.slemicro.noarch
Expand Down
3 changes: 3 additions & 0 deletions docs/building-images.md
Original file line number Diff line number Diff line change
Expand Up @@ -484,6 +484,9 @@ the built image and used to register with Elemental on boot.
> To ensure a successful build, this process requires the ```--privileged``` flag to be passed to the
> ```podman run``` command. For more info on why this is required, please see
> [Package resolution design](design/pkg-resolution.md#running-the-eib-container).
>
> Additionally, when using SL Micro 6.0, an [`sccRegistrationCode`](#operating-system) must be provided in the `operatingSystem` section
> of the image definition so that the necessary Elemental RPMs can be downloaded.

## Operating System Files

Expand Down
9 changes: 1 addition & 8 deletions pkg/eib/eib.go
Original file line number Diff line number Diff line change
Expand Up @@ -97,15 +97,8 @@ func appendElementalRPMs(ctx *image.Context) {
}

log.AuditInfo("Elemental registration is configured. The necessary RPM packages will be downloaded.")
appendRPMs(ctx, nil, combustion.ElementalPackages...)

appendRPMs(ctx, []image.AddRepo{
{
URL: ctx.ArtifactSources.Elemental.RegisterRepository,
},
{
URL: ctx.ArtifactSources.Elemental.SystemAgentRepository,
},
}, combustion.ElementalPackages...)
}

func appendFips(ctx *image.Context) {
Expand Down
4 changes: 0 additions & 4 deletions pkg/image/context.go
Original file line number Diff line number Diff line change
Expand Up @@ -35,10 +35,6 @@ type ArtifactSources struct {
Repository string `yaml:"repository"`
Version string `yaml:"version"`
} `yaml:"endpoint-copier-operator"`
Elemental struct {
RegisterRepository string `yaml:"register-repository"`
SystemAgentRepository string `yaml:"system-agent-repository"`
} `yaml:"elemental"`
Kubernetes struct {
K3s struct {
SELinuxPackage string `yaml:"selinuxPackage"`
Expand Down
74 changes: 74 additions & 0 deletions pkg/image/validation/elemental.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,74 @@
package validation

import (
"fmt"
"os"
"path/filepath"

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

const (
elementalComponent = "Elemental"
elementalConfigFilename = "elemental_config.yaml"
)

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

elementalConfigDir := filepath.Join(ctx.ImageConfigDir, "elemental")
if _, err := os.Stat(elementalConfigDir); err != nil {
if os.IsNotExist(err) {
return nil
}

failures = append(failures, FailedValidation{
UserMessage: "Elemental config directory could not be read",
Error: err,
})
return failures
}

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
}

func validateElementalDir(elementalConfigDir string) []FailedValidation {
var failures []FailedValidation

elementalConfigDirEntries, err := os.ReadDir(elementalConfigDir)
if err != nil {
failures = append(failures, FailedValidation{
UserMessage: "Elemental config directory could not be read",
Error: err,
})

return failures
}

switch len(elementalConfigDirEntries) {
case 0:
failures = append(failures, FailedValidation{
UserMessage: "Elemental config directory should not be present if it is empty",
})
case 1:
if elementalConfigDirEntries[0].Name() != elementalConfigFilename {
failures = append(failures, FailedValidation{
UserMessage: fmt.Sprintf("Elemental config file should only be named `%s`", elementalConfigFilename),
})
}
default:
failures = append(failures, FailedValidation{
UserMessage: fmt.Sprintf("Elemental config directory should only contain a singular '%s' file", elementalConfigFilename),
})
}

return failures
}
150 changes: 150 additions & 0 deletions pkg/image/validation/elemental_test.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,150 @@
package validation

import (
"os"
"path/filepath"
"testing"

"github.com/stretchr/testify/assert"
"github.com/stretchr/testify/require"
"github.com/suse-edge/edge-image-builder/pkg/image"
)

func TestValidateElementalNoDir(t *testing.T) {
ctx := image.Context{}

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

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

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

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

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

tests := map[string]struct {
ImageDefinition *image.Definition
ExpectedFailedMessages []string
}{
`valid`: {
ImageDefinition: &image.Definition{
OperatingSystem: image.OperatingSystem{
Packages: image.Packages{
RegCode: "registration-code",
},
},
},
},
`no registration code`: {
ImageDefinition: &image.Definition{},
ExpectedFailedMessages: []string{
"Operating system package registration code field must be defined when using Elemental with SL Micro 6.0",
},
},
}

for name, test := range tests {
t.Run(name, func(t *testing.T) {
ctx := image.Context{
ImageConfigDir: configDir,
ImageDefinition: test.ImageDefinition,
}
failures := validateElemental(&ctx)
assert.Len(t, failures, len(test.ExpectedFailedMessages))

var foundMessages []string
for _, foundValidation := range failures {
foundMessages = append(foundMessages, foundValidation.UserMessage)
}

for _, expectedMessage := range test.ExpectedFailedMessages {
assert.Contains(t, foundMessages, expectedMessage)
}

})
}
}

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

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

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))

failures := validateElementalDir(elementalDir)
assert.Len(t, failures, 0)
}

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

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

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

failures := validateElementalDir(elementalDir)
assert.Len(t, failures, 1)

assert.Contains(t, failures[0].UserMessage, "Elemental config directory should not be present if it is empty")
}

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

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

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

firstElementalConfig := filepath.Join(elementalDir, "elemental_config1.yaml")
require.NoError(t, os.WriteFile(firstElementalConfig, []byte(""), 0o600))
secondElementalConfig := filepath.Join(elementalDir, "elemental_config2.yaml")
require.NoError(t, os.WriteFile(secondElementalConfig, []byte(""), 0o600))

failures := validateElementalDir(elementalDir)
assert.Len(t, failures, 1)

assert.Contains(t, failures[0].UserMessage, "Elemental config directory should only contain a singular 'elemental_config.yaml' file")
}

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

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

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

failures := validateElementalDir(elementalDir)
assert.Len(t, failures, 1)

assert.Contains(t, failures[0].UserMessage, "Elemental config directory could not be read")
}
11 changes: 6 additions & 5 deletions pkg/image/validation/validation.go
Original file line number Diff line number Diff line change
Expand Up @@ -15,11 +15,12 @@ func ValidateDefinition(ctx *image.Context) map[string][]FailedValidation {
failures := map[string][]FailedValidation{}

validations := map[string]validateComponent{
versionComponent: validateVersion,
imageComponent: validateImage,
osComponent: validateOperatingSystem,
registryComponent: validateEmbeddedArtifactRegistry,
k8sComponent: validateKubernetes,
versionComponent: validateVersion,
imageComponent: validateImage,
osComponent: validateOperatingSystem,
registryComponent: validateEmbeddedArtifactRegistry,
k8sComponent: validateKubernetes,
elementalComponent: validateElemental,
}
for componentName, v := range validations {
componentFailures := v(ctx)
Expand Down

0 comments on commit 39f9682

Please sign in to comment.