diff --git a/cmd/eib/main.go b/cmd/eib/main.go index f3e625df..ec0af369 100644 --- a/cmd/eib/main.go +++ b/cmd/eib/main.go @@ -13,6 +13,7 @@ func main() { app := cmd.NewApp() app.Commands = []*cli.Command{ cmd.NewBuildCommand(build.Run), + cmd.NewExtractCommand(build.Extract), cmd.NewValidateCommand(build.Validate), cmd.NewVersionCommand(build.Version), } diff --git a/pkg/build/extract.go b/pkg/build/extract.go new file mode 100644 index 00000000..f374fdea --- /dev/null +++ b/pkg/build/extract.go @@ -0,0 +1,21 @@ +package build + +import ( + "fmt" + + "github.com/suse-edge/edge-image-builder/pkg/log" +) + + +func (b *Builder) Extract() error { + log.Audit("Generating image customization components...") + + if err := b.configureCombustion(b.context); err != nil { + log.Audit("Error configuring customization components, check the logs under the build directory for more information.") + return fmt.Errorf("configuring combustion: %w", err) + } + + + log.Audit("Extract complete!") + return nil +} \ No newline at end of file diff --git a/pkg/cli/build/extract.go b/pkg/cli/build/extract.go new file mode 100644 index 00000000..04100bf4 --- /dev/null +++ b/pkg/cli/build/extract.go @@ -0,0 +1,119 @@ +package build + +import ( + "fmt" + "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/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/urfave/cli/v2" + "go.uber.org/zap" +) + +const ( + extractLogFilename = "eib-extract.log" + checkExtractLogMessage = "Please check the eib-extract.log file under the build directory for more information." +) + +func Extract(_ *cli.Context) error { + args := &cmd.BuildArgs + + rootBuildDir := args.RootBuildDir + if rootBuildDir == "" { + const defaultBuildDir = "_extract" + + rootBuildDir = filepath.Join(args.ConfigDir, defaultBuildDir) + if err := os.MkdirAll(rootBuildDir, os.ModePerm); err != nil { + log.Auditf("The root build directory could not be set up under the configuration directory '%s'.", args.ConfigDir) + return err + } + } + + buildDir, err := build.SetupBuildDirectory(rootBuildDir) + if err != nil { + log.Audit("The build directory could not be set up.") + return err + } + + // This needs to occur as early as possible so that the subsequent calls can use the log + log.ConfigureGlobalLogger(filepath.Join(buildDir, extractLogFilename)) + + if cmdErr := imageConfigDirExists(args.ConfigDir); cmdErr != nil { + cmd.LogError(cmdErr, checkExtractLogMessage) + os.Exit(1) + } + + imageDefinition, cmdErr := parseImageDefinition(args.ConfigDir, args.DefinitionFile) + if cmdErr != nil { + cmd.LogError(cmdErr, checkExtractLogMessage) + os.Exit(1) + } + + combustionDir, artefactsDir, err := build.SetupCombustionDirectory(buildDir) + if err != nil { + log.Auditf("Setting up the combustion directory failed. %s", checkExtractLogMessage) + zap.S().Fatalf("Failed to create combustion directories: %s", err) + } + + ctx := buildContext(buildDir, combustionDir, artefactsDir, args.ConfigDir, imageDefinition) + + ctx.ImageDefinition.OperatingSystem.Packages = image.Packages{} + if cmdErr = validateImageDefinition(ctx); cmdErr != nil { + cmd.LogError(cmdErr, checkExtractLogMessage) + os.Exit(1) + } + + appendHelm(ctx) + + if cmdErr = bootstrapExtractDependencyServices(ctx, rootBuildDir); cmdErr != nil { + cmd.LogError(cmdErr, checkExtractLogMessage) + os.Exit(1) + } + + defer func() { + if r := recover(); r != nil { + log.AuditInfo("Extract failed unexpectedly, check the logs under the extract directory for more information.") + zap.S().Fatalf("Unexpected error occurred: %s", r) + } + }() + + builder := build.NewBuilder(ctx) + if err = builder.Extract(); err != nil { + zap.S().Fatalf("An error occurred extracting the image: %s", err) + } + + return nil +} + + +// If the image definition requires it, starts the necessary services, returning an error in the event of failure. +func bootstrapExtractDependencyServices(ctx *image.Context, rootDir string) *cmd.Error { + if combustion.IsEmbeddedArtifactRegistryConfigured(ctx) { + certsDir := filepath.Join(ctx.ImageConfigDir, combustion.K8sDir, combustion.HelmDir, combustion.CertsDir) + ctx.HelmClient = helm.New(ctx.BuildDir, certsDir) + } + + if ctx.ImageDefinition.Kubernetes.Version != "" { + c, err := cache.New(rootDir) + if err != nil { + return &cmd.Error{ + UserMessage: "Setting up file caching failed.", + LogMessage: fmt.Sprintf("Initializing cache instance failed: %v", err), + } + } + + ctx.KubernetesScriptDownloader = kubernetes.ScriptDownloader{} + ctx.KubernetesArtefactDownloader = kubernetes.ArtefactDownloader{ + Cache: c, + } + } + + return nil +} diff --git a/pkg/cli/cmd/extract.go b/pkg/cli/cmd/extract.go new file mode 100644 index 00000000..a556e543 --- /dev/null +++ b/pkg/cli/cmd/extract.go @@ -0,0 +1,26 @@ +package cmd + +import ( + "fmt" + + "github.com/urfave/cli/v2" +) + + +func NewExtractCommand(action func(*cli.Context) error) *cli.Command { + return &cli.Command{ + Name: "extract", + Usage: "Extract new image", + UsageText: fmt.Sprintf("%s extract [OPTIONS]", appName), + Action: action, + Flags: []cli.Flag{ + DefinitionFileFlag, + ConfigDirFlag, + &cli.StringFlag{ + Name: "extract-dir", + Usage: "Full path to the directory to store extract artifacts", + Destination: &BuildArgs.RootBuildDir, + }, + }, + } +}