diff --git a/pkg/asset/appliance/appliance_liveiso.go b/pkg/asset/appliance/appliance_liveiso.go index d6e8bd33..d4cbb2a2 100644 --- a/pkg/asset/appliance/appliance_liveiso.go +++ b/pkg/asset/appliance/appliance_liveiso.go @@ -3,9 +3,9 @@ package appliance import ( "encoding/json" "fmt" + "io" "os" "path/filepath" - "regexp" "github.com/openshift/appliance/pkg/asset/config" "github.com/openshift/appliance/pkg/asset/data" @@ -23,13 +23,8 @@ import ( ) const ( - liveIsoWorkDir = "live-iso" - liveIsoDataDir = "registry" - bootstrapImageName = "/images/bootstrap-appliance.img" - bootstrapIgnitionPath = "/usr/lib/ignition/base.d/99-bootstrap.ign" - defaultGrubConfigFilePath = "EFI/redhat/grub.cfg" - defaultIsolinuxConfigFilePath = "isolinux/isolinux.cfg" - defaultKargsConfigFilePath = "coreos/kargs.json" + liveIsoWorkDir = "live-iso" + liveIsoDataDir = "registry" ) // ApplianceLiveISO is an asset that generates the OpenShift-based appliance. @@ -165,39 +160,45 @@ func (a *ApplianceLiveISO) buildLiveISO( ) spinner.FileToMonitor = consts.DeployIsoName - // Create bootstrap.img file - coreOSConfig := coreos.CoreOSConfig{ - ApplianceConfig: applianceConfig, - EnvConfig: envConfig, - } - c := coreos.NewCoreOS(coreOSConfig) + // Append bootstrap ignition to initrd using isoeditor library ignitionBytes, err := json.Marshal(recoveryIgnition.Bootstrap) if err != nil { logrus.Errorf("Failed to marshal recovery ignition to json: %s", err.Error()) return log.StopSpinner(spinner, err) } - bootstrapImagePath := filepath.Join(workDir, bootstrapImageName) - if err := c.WrapIgnition(ignitionBytes, bootstrapIgnitionPath, bootstrapImagePath); err != nil { - logrus.Errorf("Failed to create bootstrap image: %s", err.Error()) + ignitionContent := &isoeditor.IgnitionContent{ + SystemConfigs: map[string][]byte{ + "99-bootstrap.ign": ignitionBytes, + }, + } + initrdReader, err := isoeditor.NewInitRamFSStreamReaderFromISO(coreosIsoPath, ignitionContent) + if err != nil { + logrus.Errorf("Failed to create initrd with bootstrap ignition: %s", err.Error()) return log.StopSpinner(spinner, err) } + defer func() { + if err := initrdReader.Close(); err != nil { + logrus.Errorf("Failed to close initrd reader: %s", err.Error()) + } + }() - // Add bootstrap.img to initrd - replacement := fmt.Sprintf("$1 $2 %s", bootstrapImageName) - grubCfgPath := filepath.Join(workDir, defaultGrubConfigFilePath) - if err := editFile(grubCfgPath, `(?m)^(\s+initrd) (.+| )+$`, replacement); err != nil { - return err + // Write the updated initrd to the extracted ISO + initrdPath := filepath.Join(workDir, "images/pxeboot/initrd.img") + initrdFile, err := os.Create(initrdPath) + if err != nil { + logrus.Errorf("Failed to create initrd file %s: %s", initrdPath, err.Error()) + return log.StopSpinner(spinner, err) } - replacement = fmt.Sprintf("${1},%s ${2}", bootstrapImageName) - isolinuxConfigFilePath := filepath.Join(workDir, defaultIsolinuxConfigFilePath) - if err := editFile(isolinuxConfigFilePath, `(?m)^(\s+append.*initrd=\S+) (.*)$`, replacement); err != nil { - return err + if _, err := io.Copy(initrdFile, initrdReader); err != nil { + if closeErr := initrdFile.Close(); closeErr != nil { + logrus.Errorf("Failed to close initrd file: %s", closeErr.Error()) + } + logrus.Errorf("Failed to write initrd file %s: %s", initrdPath, err.Error()) + return log.StopSpinner(spinner, err) } - - // Fix offset in kargs.json - initrdImageOffset := int64(len(bootstrapImageName) + 1) - if err := fixKargsOffset(workDir, defaultIsolinuxConfigFilePath, initrdImageOffset); err != nil { - return err + if err := initrdFile.Close(); err != nil { + logrus.Errorf("Failed to close initrd file: %s", err.Error()) + return log.StopSpinner(spinner, err) } // Generate live ISO @@ -218,56 +219,3 @@ func (a *ApplianceLiveISO) buildLiveISO( return log.StopSpinner(spinner, nil) } - -func editFile(fileName string, reString string, replacement string) error { - content, err := os.ReadFile(fileName) - if err != nil { - return err - } - - re := regexp.MustCompile(reString) - newContent := re.ReplaceAllString(string(content), replacement) - - if err := os.WriteFile(fileName, []byte(newContent), 0600); err != nil { - return err - } - - return nil -} - -func fixKargsOffset(workDir, configPath string, offset int64) error { - kargsConfigFilePath := filepath.Join(workDir, defaultKargsConfigFilePath) - kargsData, err := os.ReadFile(kargsConfigFilePath) - if err != nil { - return err - } - - var kargsConfig struct { - Default string `json:"default"` - Files []struct { - End string `json:"end"` - Offset int64 `json:"offset"` - Pad string `json:"pad"` - Path string `json:"path"` - } `json:"files"` - Size int64 `json:"size"` - } - if err := json.Unmarshal(kargsData, &kargsConfig); err != nil { - return err - } - for i, file := range kargsConfig.Files { - if file.Path == configPath { - kargsConfig.Files[i].Offset = file.Offset + offset - } - } - - workConfigFileContent, err := json.MarshalIndent(kargsConfig, "", " ") - if err != nil { - return err - } - if err := os.WriteFile(kargsConfigFilePath, workConfigFileContent, 0600); err != nil { - return err - } - - return nil -} diff --git a/pkg/coreos/coreos.go b/pkg/coreos/coreos.go index d40d2b88..f91b9cb0 100644 --- a/pkg/coreos/coreos.go +++ b/pkg/coreos/coreos.go @@ -1,14 +1,11 @@ package coreos import ( - "bytes" - "compress/gzip" "encoding/json" "fmt" "os" "path/filepath" - "github.com/cavaliercoder/go-cpio" "github.com/cavaliergopher/grab/v3" "github.com/itchyny/gojq" "github.com/openshift/appliance/pkg/asset/config" @@ -32,7 +29,6 @@ type CoreOS interface { DownloadDiskImage() (string, error) DownloadISO() (string, error) EmbedIgnition(ignition []byte, isoPath string) error - WrapIgnition(ignition []byte, ignitionPath, imagePath string) error FetchCoreOSStream() (map[string]any, error) } @@ -121,31 +117,6 @@ func (c *coreos) EmbedIgnition(ignition []byte, isoPath string) error { return err } -func (c *coreos) WrapIgnition(ignition []byte, ignitionPath, imagePath string) error { - ignitionImgFile, err := os.OpenFile(imagePath, os.O_CREATE|os.O_RDWR, 0664) - if err != nil { - return err - } - defer func() { - if err := ignitionImgFile.Close(); err != nil { - logrus.Errorf("Failed to close ignition image file: %s", err.Error()) - } - }() - - compressedCpio, err := generateCompressedCPIO(ignition, ignitionPath, 0o100_644) - if err != nil { - return err - } - - _, err = ignitionImgFile.Write(compressedCpio) - if err != nil { - logrus.Errorf("Failed to write ignition data into %s: %s", ignitionImgFile.Name(), err.Error()) - return err - } - - return nil -} - func (c *coreos) FetchCoreOSStream() (map[string]any, error) { path, err := c.Release.ExtractFile(machineOsImageName, coreOsStream) if err != nil { @@ -164,38 +135,3 @@ func (c *coreos) FetchCoreOSStream() (map[string]any, error) { return m, nil } - -func generateCompressedCPIO(fileContent []byte, filePath string, mode cpio.FileMode) ([]byte, error) { - // Run gzip compression - compressedBuffer := new(bytes.Buffer) - gzipWriter := gzip.NewWriter(compressedBuffer) - // Create CPIO archive - cpioWriter := cpio.NewWriter(gzipWriter) - - if err := cpioWriter.WriteHeader(&cpio.Header{ - Name: filePath, - Mode: mode, - Size: int64(len(fileContent)), - }); err != nil { - return nil, errors.Wrap(err, "Failed to write CPIO header") - } - if _, err := cpioWriter.Write(fileContent); err != nil { - return nil, errors.Wrap(err, "Failed to write CPIO archive") - } - - if err := cpioWriter.Close(); err != nil { - return nil, errors.Wrap(err, "Failed to close CPIO archive") - } - if err := gzipWriter.Close(); err != nil { - return nil, errors.Wrap(err, "Failed to gzip ignition config") - } - - padSize := (4 - (compressedBuffer.Len() % 4)) % 4 - for i := 0; i < padSize; i++ { - if err := compressedBuffer.WriteByte(0); err != nil { - return nil, err - } - } - - return compressedBuffer.Bytes(), nil -}