From 8e5d37afa1fff6f8a86d4bea5f108e474a7d1f78 Mon Sep 17 00:00:00 2001 From: Atanas Dinov Date: Tue, 28 May 2024 22:42:42 +0300 Subject: [PATCH] Extract eib package Signed-off-by: Atanas Dinov --- pkg/build/build.go | 25 ------ pkg/build/build_test.go | 47 ----------- pkg/cli/build/build.go | 155 +--------------------------------- pkg/eib/eib.go | 181 ++++++++++++++++++++++++++++++++++++++++ pkg/eib/eib_test.go | 57 +++++++++++++ 5 files changed, 242 insertions(+), 223 deletions(-) create mode 100644 pkg/eib/eib.go create mode 100644 pkg/eib/eib_test.go diff --git a/pkg/build/build.go b/pkg/build/build.go index 051ec1a4..938b7259 100644 --- a/pkg/build/build.go +++ b/pkg/build/build.go @@ -4,7 +4,6 @@ import ( "fmt" "os" "path/filepath" - "time" "github.com/suse-edge/edge-image-builder/pkg/image" "github.com/suse-edge/edge-image-builder/pkg/log" @@ -78,27 +77,3 @@ func (b *Builder) deleteExistingOutputImage() error { } return nil } - -func SetupBuildDirectory(rootDir string) (string, error) { - timestamp := time.Now().Format("Jan02_15-04-05") - buildDir := filepath.Join(rootDir, fmt.Sprintf("build-%s", timestamp)) - if err := os.MkdirAll(buildDir, os.ModePerm); err != nil { - return "", fmt.Errorf("creating a build directory: %w", err) - } - - return buildDir, nil -} - -func SetupCombustionDirectory(buildDir string) (combustionDir, artefactsDir string, err error) { - combustionDir = filepath.Join(buildDir, "combustion") - if err = os.MkdirAll(combustionDir, os.ModePerm); err != nil { - return "", "", fmt.Errorf("creating a combustion directory: %w", err) - } - - artefactsDir = filepath.Join(buildDir, "artefacts") - if err = os.MkdirAll(artefactsDir, os.ModePerm); err != nil { - return "", "", fmt.Errorf("creating an artefacts directory: %w", err) - } - - return combustionDir, artefactsDir, nil -} diff --git a/pkg/build/build_test.go b/pkg/build/build_test.go index 4cc8ba81..c2e21520 100644 --- a/pkg/build/build_test.go +++ b/pkg/build/build_test.go @@ -10,53 +10,6 @@ import ( "github.com/suse-edge/edge-image-builder/pkg/image" ) -func TestSetupBuildDirectory_EmptyRootDir(t *testing.T) { - buildDir, err := SetupBuildDirectory("") - require.NoError(t, err) - - defer func() { - assert.NoError(t, os.RemoveAll(buildDir)) - }() - - require.DirExists(t, buildDir) - assert.Contains(t, buildDir, "build-") -} - -func TestSetupBuildDir_NonEmptyRootDir(t *testing.T) { - tests := []struct { - name string - rootDir string - }{ - { - name: "Existing root dir", - rootDir: func() string { - tmpDir, err := os.MkdirTemp("", "eib-test-") - require.NoError(t, err) - - return tmpDir - }(), - }, - { - name: "Non-existing root dir", - rootDir: "some-non-existing-dir", - }, - } - - for _, test := range tests { - t.Run(test.name, func(t *testing.T) { - defer func() { - assert.NoError(t, os.RemoveAll(test.rootDir)) - }() - - buildDir, err := SetupBuildDirectory(test.rootDir) - require.NoError(t, err) - - require.DirExists(t, buildDir) - assert.Contains(t, buildDir, filepath.Join(test.rootDir, "build-")) - }) - } -} - func TestGenerateBuildDirFilename(t *testing.T) { // Setup tmpDir, err := os.MkdirTemp("", "eib-") diff --git a/pkg/cli/build/build.go b/pkg/cli/build/build.go index 3be01ed8..dce73abf 100644 --- a/pkg/cli/build/build.go +++ b/pkg/cli/build/build.go @@ -7,19 +7,10 @@ import ( "os" "path/filepath" - "github.com/suse-edge/edge-image-builder/pkg/build" - "github.com/suse-edge/edge-image-builder/pkg/cache" "github.com/suse-edge/edge-image-builder/pkg/cli/cmd" - "github.com/suse-edge/edge-image-builder/pkg/combustion" - "github.com/suse-edge/edge-image-builder/pkg/env" - "github.com/suse-edge/edge-image-builder/pkg/helm" + "github.com/suse-edge/edge-image-builder/pkg/eib" "github.com/suse-edge/edge-image-builder/pkg/image" - "github.com/suse-edge/edge-image-builder/pkg/kubernetes" "github.com/suse-edge/edge-image-builder/pkg/log" - "github.com/suse-edge/edge-image-builder/pkg/network" - "github.com/suse-edge/edge-image-builder/pkg/podman" - "github.com/suse-edge/edge-image-builder/pkg/rpm" - "github.com/suse-edge/edge-image-builder/pkg/rpm/resolver" "github.com/urfave/cli/v2" "go.uber.org/zap" ) @@ -43,7 +34,7 @@ func Run(_ *cli.Context) error { } } - buildDir, err := build.SetupBuildDirectory(rootBuildDir) + buildDir, err := eib.SetupBuildDirectory(rootBuildDir) if err != nil { log.Audit("The build directory could not be set up.") return err @@ -63,7 +54,7 @@ func Run(_ *cli.Context) error { os.Exit(1) } - combustionDir, artefactsDir, err := build.SetupCombustionDirectory(buildDir) + combustionDir, artefactsDir, err := eib.SetupCombustionDirectory(buildDir) if err != nil { log.Auditf("Setting up the combustion directory failed. %s", checkBuildLogMessage) zap.S().Fatalf("Failed to create combustion directories: %s", err) @@ -76,21 +67,6 @@ func Run(_ *cli.Context) error { os.Exit(1) } - if err = appendKubernetesSELinuxRPMs(ctx); err != nil { - log.Auditf("Configuring Kubernetes failed. %s", checkBuildLogMessage) - zap.S().Fatalf("Failed to configure Kubernetes SELinux policy: %s", err) - } - - appendElementalRPMs(ctx) - - appendHelm(ctx) - - combustionConfigurator, cmdErr := buildCombustionConfigurator(ctx, rootBuildDir) - if cmdErr != nil { - cmd.LogError(cmdErr, checkBuildLogMessage) - os.Exit(1) - } - defer func() { if r := recover(); r != nil { log.AuditInfo("Build failed unexpectedly, check the logs under the build directory for more information.") @@ -98,8 +74,7 @@ func Run(_ *cli.Context) error { } }() - builder := build.NewBuilder(ctx, combustionConfigurator) - if err = builder.Build(); err != nil { + if err = eib.Run(ctx, rootBuildDir); err != nil { zap.S().Fatalf("An error occurred building the image: %s", err) } @@ -163,125 +138,3 @@ func buildContext(buildDir, combustionDir, artefactsDir, configDir string, image } return ctx } - -func appendKubernetesSELinuxRPMs(ctx *image.Context) error { - if ctx.ImageDefinition.Kubernetes.Version == "" { - return nil - } - - configPath := combustion.KubernetesConfigPath(ctx) - config, err := kubernetes.ParseKubernetesConfig(configPath) - if err != nil { - return fmt.Errorf("parsing kubernetes server config: %w", err) - } - - selinuxEnabled, _ := config["selinux"].(bool) - if !selinuxEnabled { - return nil - } - - log.AuditInfo("SELinux is enabled in the Kubernetes configuration. " + - "The necessary RPM packages will be downloaded.") - - selinuxPackage, err := kubernetes.SELinuxPackage(ctx.ImageDefinition.Kubernetes.Version) - if err != nil { - return fmt.Errorf("identifying selinux package: %w", err) - } - - repository, err := kubernetes.SELinuxRepository(ctx.ImageDefinition.Kubernetes.Version) - if err != nil { - return fmt.Errorf("identifying selinux repository: %w", err) - } - - appendRPMs(ctx, repository, selinuxPackage) - - gpgKeysDir := combustion.GPGKeysPath(ctx) - if err = os.MkdirAll(gpgKeysDir, os.ModePerm); err != nil { - return fmt.Errorf("creating directory '%s': %w", gpgKeysDir, err) - } - - if err = kubernetes.DownloadSELinuxRPMsSigningKey(gpgKeysDir); err != nil { - return fmt.Errorf("downloading signing key: %w", err) - } - - return nil -} - -func appendElementalRPMs(ctx *image.Context) { - elementalDir := combustion.ElementalPath(ctx) - if _, err := os.Stat(elementalDir); err != nil { - if !errors.Is(err, fs.ErrNotExist) { - zap.S().Warnf("Looking for '%s' dir failed unexpectedly: %s", elementalDir, err) - } - - return - } - - log.AuditInfo("Elemental registration is configured. The necessary RPM packages will be downloaded.") - - appendRPMs(ctx, image.AddRepo{URL: env.ElementalPackageRepository}, combustion.ElementalPackages...) -} - -func appendRPMs(ctx *image.Context, repository image.AddRepo, packages ...string) { - repositories := ctx.ImageDefinition.OperatingSystem.Packages.AdditionalRepos - repositories = append(repositories, repository) - - packageList := ctx.ImageDefinition.OperatingSystem.Packages.PKGList - packageList = append(packageList, packages...) - - ctx.ImageDefinition.OperatingSystem.Packages.PKGList = packageList - ctx.ImageDefinition.OperatingSystem.Packages.AdditionalRepos = repositories -} - -func appendHelm(ctx *image.Context) { - componentCharts, componentRepos := combustion.ComponentHelmCharts(ctx) - - ctx.ImageDefinition.Kubernetes.Helm.Charts = append(ctx.ImageDefinition.Kubernetes.Helm.Charts, componentCharts...) - ctx.ImageDefinition.Kubernetes.Helm.Repositories = append(ctx.ImageDefinition.Kubernetes.Helm.Repositories, componentRepos...) -} - -func buildCombustionConfigurator(ctx *image.Context, rootDir string) (*combustion.Combustion, *cmd.Error) { - combustionConfigurator := &combustion.Combustion{ - NetworkConfigGenerator: network.ConfigGenerator{}, - NetworkConfiguratorInstaller: network.ConfiguratorInstaller{}, - } - - if !combustion.SkipRPMComponent(ctx) { - p, err := podman.New(ctx.BuildDir) - if err != nil { - return nil, &cmd.Error{ - UserMessage: "The services for RPM dependency resolution failed to start.", - LogMessage: fmt.Sprintf("Setting up Podman instance failed: %v", err), - } - } - - imgPath := filepath.Join(ctx.ImageConfigDir, "base-images", ctx.ImageDefinition.Image.BaseImage) - imgType := ctx.ImageDefinition.Image.ImageType - baseBuilder := resolver.NewTarballBuilder(ctx.BuildDir, imgPath, imgType, p) - - combustionConfigurator.RPMResolver = resolver.New(ctx.BuildDir, p, baseBuilder, "") - combustionConfigurator.RPMRepoCreator = rpm.NewRepoCreator(ctx.BuildDir) - } - - if combustion.IsEmbeddedArtifactRegistryConfigured(ctx) { - certsDir := filepath.Join(ctx.ImageConfigDir, combustion.K8sDir, combustion.HelmDir, combustion.CertsDir) - combustionConfigurator.HelmClient = helm.New(ctx.BuildDir, certsDir) - } - - if ctx.ImageDefinition.Kubernetes.Version != "" { - c, err := cache.New(rootDir) - if err != nil { - return nil, &cmd.Error{ - UserMessage: "Setting up file caching failed.", - LogMessage: fmt.Sprintf("Initialising cache instance failed: %v", err), - } - } - - combustionConfigurator.KubernetesScriptDownloader = kubernetes.ScriptDownloader{} - combustionConfigurator.KubernetesArtefactDownloader = kubernetes.ArtefactDownloader{ - Cache: c, - } - } - - return combustionConfigurator, nil -} diff --git a/pkg/eib/eib.go b/pkg/eib/eib.go new file mode 100644 index 00000000..4f0e34c5 --- /dev/null +++ b/pkg/eib/eib.go @@ -0,0 +1,181 @@ +package eib + +import ( + "errors" + "fmt" + "io/fs" + "os" + "path/filepath" + "time" + + "github.com/suse-edge/edge-image-builder/pkg/build" + "github.com/suse-edge/edge-image-builder/pkg/cache" + "github.com/suse-edge/edge-image-builder/pkg/combustion" + "github.com/suse-edge/edge-image-builder/pkg/env" + "github.com/suse-edge/edge-image-builder/pkg/helm" + "github.com/suse-edge/edge-image-builder/pkg/image" + "github.com/suse-edge/edge-image-builder/pkg/kubernetes" + "github.com/suse-edge/edge-image-builder/pkg/log" + "github.com/suse-edge/edge-image-builder/pkg/network" + "github.com/suse-edge/edge-image-builder/pkg/podman" + "github.com/suse-edge/edge-image-builder/pkg/rpm" + "github.com/suse-edge/edge-image-builder/pkg/rpm/resolver" + "go.uber.org/zap" +) + +func Run(ctx *image.Context, rootBuildDir string) error { + if err := appendKubernetesSELinuxRPMs(ctx); err != nil { + return fmt.Errorf("configuring kubernetes selinux policy: %w", err) + } + + appendElementalRPMs(ctx) + appendHelm(ctx) + + c, err := buildCombustion(ctx, rootBuildDir) + if err != nil { + return fmt.Errorf("building combustion: %w", err) + } + + builder := build.NewBuilder(ctx, c) + return builder.Build() +} + +func appendKubernetesSELinuxRPMs(ctx *image.Context) error { + if ctx.ImageDefinition.Kubernetes.Version == "" { + return nil + } + + configPath := combustion.KubernetesConfigPath(ctx) + config, err := kubernetes.ParseKubernetesConfig(configPath) + if err != nil { + return fmt.Errorf("parsing kubernetes server config: %w", err) + } + + selinuxEnabled, _ := config["selinux"].(bool) + if !selinuxEnabled { + return nil + } + + log.AuditInfo("SELinux is enabled in the Kubernetes configuration. " + + "The necessary RPM packages will be downloaded.") + + selinuxPackage, err := kubernetes.SELinuxPackage(ctx.ImageDefinition.Kubernetes.Version) + if err != nil { + return fmt.Errorf("identifying selinux package: %w", err) + } + + repository, err := kubernetes.SELinuxRepository(ctx.ImageDefinition.Kubernetes.Version) + if err != nil { + return fmt.Errorf("identifying selinux repository: %w", err) + } + + appendRPMs(ctx, repository, selinuxPackage) + + gpgKeysDir := combustion.GPGKeysPath(ctx) + if err = os.MkdirAll(gpgKeysDir, os.ModePerm); err != nil { + return fmt.Errorf("creating directory '%s': %w", gpgKeysDir, err) + } + + if err = kubernetes.DownloadSELinuxRPMsSigningKey(gpgKeysDir); err != nil { + return fmt.Errorf("downloading signing key: %w", err) + } + + return nil +} + +func appendElementalRPMs(ctx *image.Context) { + elementalDir := combustion.ElementalPath(ctx) + if _, err := os.Stat(elementalDir); err != nil { + if !errors.Is(err, fs.ErrNotExist) { + zap.S().Warnf("Looking for '%s' dir failed unexpectedly: %s", elementalDir, err) + } + + return + } + + log.AuditInfo("Elemental registration is configured. The necessary RPM packages will be downloaded.") + + appendRPMs(ctx, image.AddRepo{URL: env.ElementalPackageRepository}, combustion.ElementalPackages...) +} + +func appendRPMs(ctx *image.Context, repository image.AddRepo, packages ...string) { + repositories := ctx.ImageDefinition.OperatingSystem.Packages.AdditionalRepos + repositories = append(repositories, repository) + + packageList := ctx.ImageDefinition.OperatingSystem.Packages.PKGList + packageList = append(packageList, packages...) + + ctx.ImageDefinition.OperatingSystem.Packages.PKGList = packageList + ctx.ImageDefinition.OperatingSystem.Packages.AdditionalRepos = repositories +} + +func appendHelm(ctx *image.Context) { + componentCharts, componentRepos := combustion.ComponentHelmCharts(ctx) + + ctx.ImageDefinition.Kubernetes.Helm.Charts = append(ctx.ImageDefinition.Kubernetes.Helm.Charts, componentCharts...) + ctx.ImageDefinition.Kubernetes.Helm.Repositories = append(ctx.ImageDefinition.Kubernetes.Helm.Repositories, componentRepos...) +} + +func buildCombustion(ctx *image.Context, rootDir string) (*combustion.Combustion, error) { + combustionHandler := &combustion.Combustion{ + NetworkConfigGenerator: network.ConfigGenerator{}, + NetworkConfiguratorInstaller: network.ConfiguratorInstaller{}, + } + + if !combustion.SkipRPMComponent(ctx) { + p, err := podman.New(ctx.BuildDir) + if err != nil { + return nil, fmt.Errorf("setting up Podman instance: %w", err) + } + + imgPath := filepath.Join(ctx.ImageConfigDir, "base-images", ctx.ImageDefinition.Image.BaseImage) + imgType := ctx.ImageDefinition.Image.ImageType + baseBuilder := resolver.NewTarballBuilder(ctx.BuildDir, imgPath, imgType, p) + + combustionHandler.RPMResolver = resolver.New(ctx.BuildDir, p, baseBuilder, "") + combustionHandler.RPMRepoCreator = rpm.NewRepoCreator(ctx.BuildDir) + } + + if combustion.IsEmbeddedArtifactRegistryConfigured(ctx) { + certsDir := filepath.Join(ctx.ImageConfigDir, combustion.K8sDir, combustion.HelmDir, combustion.CertsDir) + combustionHandler.HelmClient = helm.New(ctx.BuildDir, certsDir) + } + + if ctx.ImageDefinition.Kubernetes.Version != "" { + c, err := cache.New(rootDir) + if err != nil { + return nil, fmt.Errorf("initialising cache instance: %w", err) + } + + combustionHandler.KubernetesScriptDownloader = kubernetes.ScriptDownloader{} + combustionHandler.KubernetesArtefactDownloader = kubernetes.ArtefactDownloader{ + Cache: c, + } + } + + return combustionHandler, nil +} + +func SetupBuildDirectory(rootDir string) (string, error) { + timestamp := time.Now().Format("Jan02_15-04-05") + buildDir := filepath.Join(rootDir, fmt.Sprintf("build-%s", timestamp)) + if err := os.MkdirAll(buildDir, os.ModePerm); err != nil { + return "", fmt.Errorf("creating a build directory: %w", err) + } + + return buildDir, nil +} + +func SetupCombustionDirectory(buildDir string) (combustionDir, artefactsDir string, err error) { + combustionDir = filepath.Join(buildDir, "combustion") + if err = os.MkdirAll(combustionDir, os.ModePerm); err != nil { + return "", "", fmt.Errorf("creating a combustion directory: %w", err) + } + + artefactsDir = filepath.Join(buildDir, "artefacts") + if err = os.MkdirAll(artefactsDir, os.ModePerm); err != nil { + return "", "", fmt.Errorf("creating an artefacts directory: %w", err) + } + + return combustionDir, artefactsDir, nil +} diff --git a/pkg/eib/eib_test.go b/pkg/eib/eib_test.go new file mode 100644 index 00000000..8fe6be8f --- /dev/null +++ b/pkg/eib/eib_test.go @@ -0,0 +1,57 @@ +package eib + +import ( + "os" + "path/filepath" + "testing" + + "github.com/stretchr/testify/assert" + "github.com/stretchr/testify/require" +) + +func TestSetupBuildDirectory_EmptyRootDir(t *testing.T) { + buildDir, err := SetupBuildDirectory("") + require.NoError(t, err) + + defer func() { + assert.NoError(t, os.RemoveAll(buildDir)) + }() + + require.DirExists(t, buildDir) + assert.Contains(t, buildDir, "build-") +} + +func TestSetupBuildDir_NonEmptyRootDir(t *testing.T) { + tests := []struct { + name string + rootDir string + }{ + { + name: "Existing root dir", + rootDir: func() string { + tmpDir, err := os.MkdirTemp("", "eib-test-") + require.NoError(t, err) + + return tmpDir + }(), + }, + { + name: "Non-existing root dir", + rootDir: "some-non-existing-dir", + }, + } + + for _, test := range tests { + t.Run(test.name, func(t *testing.T) { + defer func() { + assert.NoError(t, os.RemoveAll(test.rootDir)) + }() + + buildDir, err := SetupBuildDirectory(test.rootDir) + require.NoError(t, err) + + require.DirExists(t, buildDir) + assert.Contains(t, buildDir, filepath.Join(test.rootDir, "build-")) + }) + } +}