github.com/openshift/installer@v1.4.17/pkg/asset/agent/image/agentartifacts.go (about)

     1  package image
     2  
     3  import (
     4  	"context"
     5  	"encoding/json"
     6  	"fmt"
     7  	"os"
     8  	"path/filepath"
     9  	"strings"
    10  
    11  	"github.com/openshift/assisted-image-service/pkg/isoeditor"
    12  	"github.com/openshift/assisted-service/models"
    13  	"github.com/openshift/installer/pkg/asset"
    14  	config "github.com/openshift/installer/pkg/asset/agent/agentconfig"
    15  	"github.com/openshift/installer/pkg/asset/agent/manifests"
    16  	"github.com/openshift/installer/pkg/asset/agent/mirror"
    17  )
    18  
    19  const (
    20  	// bootArtifactsPath is the path where boot files are created.
    21  	// e.g. initrd, kernel and rootfs.
    22  	bootArtifactsPath = "boot-artifacts"
    23  )
    24  
    25  // AgentArtifacts is an asset that generates all the artifacts that could be used
    26  // for a subsequent generation of an ISO image or PXE files, starting from the
    27  // content of the rhcos image enriched with agent specific files.
    28  type AgentArtifacts struct {
    29  	CPUArch              string
    30  	RendezvousIP         string
    31  	TmpPath              string
    32  	IgnitionByte         []byte
    33  	Kargs                string
    34  	ISOPath              string
    35  	BootArtifactsBaseURL string
    36  }
    37  
    38  // Dependencies returns the assets on which the AgentArtifacts asset depends.
    39  func (a *AgentArtifacts) Dependencies() []asset.Asset {
    40  	return []asset.Asset{
    41  		&Ignition{},
    42  		&Kargs{},
    43  		&BaseIso{},
    44  		&manifests.AgentManifests{},
    45  		&manifests.AgentClusterInstall{},
    46  		&mirror.RegistriesConf{},
    47  		&config.AgentConfig{},
    48  	}
    49  }
    50  
    51  // Generate generates the configurations for the agent ISO image and PXE assets.
    52  func (a *AgentArtifacts) Generate(_ context.Context, dependencies asset.Parents) error {
    53  	ignition := &Ignition{}
    54  	kargs := &Kargs{}
    55  	baseIso := &BaseIso{}
    56  	agentManifests := &manifests.AgentManifests{}
    57  	agentClusterInstall := &manifests.AgentClusterInstall{}
    58  	registriesConf := &mirror.RegistriesConf{}
    59  	agentconfig := &config.AgentConfig{}
    60  
    61  	dependencies.Get(ignition, kargs, baseIso, agentManifests, agentClusterInstall, registriesConf, agentconfig)
    62  
    63  	ignitionByte, err := json.Marshal(ignition.Config)
    64  	if err != nil {
    65  		return err
    66  	}
    67  
    68  	a.CPUArch = ignition.CPUArch
    69  	a.RendezvousIP = ignition.RendezvousIP
    70  	a.IgnitionByte = ignitionByte
    71  	a.ISOPath = baseIso.File.Filename
    72  	a.Kargs = kargs.KernelCmdLine()
    73  
    74  	if agentconfig.Config != nil {
    75  		a.BootArtifactsBaseURL = strings.Trim(agentconfig.Config.BootArtifactsBaseURL, "/")
    76  	}
    77  
    78  	var agentTuiFiles []string
    79  	if agentClusterInstall.GetExternalPlatformName() != string(models.PlatformTypeOci) {
    80  		agentTuiFiles, err = a.fetchAgentTuiFiles(agentManifests.ClusterImageSet.Spec.ReleaseImage, agentManifests.GetPullSecretData(), registriesConf.MirrorConfig)
    81  		if err != nil {
    82  			return err
    83  		}
    84  	}
    85  	err = a.prepareAgentArtifacts(a.ISOPath, agentTuiFiles)
    86  	if err != nil {
    87  		return err
    88  	}
    89  
    90  	return nil
    91  }
    92  
    93  func (a *AgentArtifacts) fetchAgentTuiFiles(releaseImage string, pullSecret string, mirrorConfig []mirror.RegistriesConfig) ([]string, error) {
    94  	release := NewRelease(
    95  		Config{MaxTries: OcDefaultTries, RetryDelay: OcDefaultRetryDelay},
    96  		releaseImage, pullSecret, mirrorConfig, nil)
    97  
    98  	agentTuiFilenames := []string{"/usr/bin/agent-tui", "/usr/lib64/libnmstate.so.*"}
    99  	files := []string{}
   100  
   101  	for _, srcFile := range agentTuiFilenames {
   102  		extracted, err := release.ExtractFile("agent-installer-utils", srcFile, a.CPUArch)
   103  		if err != nil {
   104  			return nil, err
   105  		}
   106  
   107  		for _, f := range extracted {
   108  			// Make sure it could be executed
   109  			err = os.Chmod(f, 0o555)
   110  			if err != nil {
   111  				return nil, err
   112  			}
   113  			files = append(files, f)
   114  		}
   115  	}
   116  
   117  	return files, nil
   118  }
   119  
   120  func (a *AgentArtifacts) prepareAgentArtifacts(iso string, additionalFiles []string) error {
   121  	// Create a tmp folder to store all the pieces required to generate the agent artifacts.
   122  	tmpPath, err := os.MkdirTemp("", "agent")
   123  	if err != nil {
   124  		return err
   125  	}
   126  	a.TmpPath = tmpPath
   127  
   128  	err = isoeditor.Extract(iso, a.TmpPath)
   129  	if err != nil {
   130  		return err
   131  	}
   132  
   133  	err = a.appendAgentFilesToInitrd(additionalFiles)
   134  	if err != nil {
   135  		return err
   136  	}
   137  
   138  	return nil
   139  }
   140  
   141  func (a *AgentArtifacts) appendAgentFilesToInitrd(additionalFiles []string) error {
   142  	ca := NewCpioArchive()
   143  
   144  	dstPath := "/agent-files/"
   145  	err := ca.StorePath(dstPath)
   146  	if err != nil {
   147  		return err
   148  	}
   149  
   150  	// Add the required agent files to the archive
   151  	for _, f := range additionalFiles {
   152  		err := ca.StoreFile(f, dstPath)
   153  		if err != nil {
   154  			return err
   155  		}
   156  	}
   157  
   158  	// Add a dracut hook to copy the files. The $NEWROOT environment variable is exported by
   159  	// dracut during the startup and it refers the mountpoint for the root filesystem.
   160  	dracutHookScript := `#!/bin/sh
   161  cp -R /agent-files/* $NEWROOT/usr/local/bin/
   162  # Fix the selinux label
   163  for i in $(find /agent-files/ -printf "%P\n"); do chcon system_u:object_r:bin_t:s0 $NEWROOT/usr/local/bin/$i; done`
   164  
   165  	err = ca.StoreBytes("/usr/lib/dracut/hooks/pre-pivot/99-agent-copy-files.sh", []byte(dracutHookScript), 0o755)
   166  	if err != nil {
   167  		return err
   168  	}
   169  
   170  	buff, err := ca.SaveBuffer()
   171  	if err != nil {
   172  		return err
   173  	}
   174  
   175  	// Append the archive to initrd.img
   176  	initrdImgPath := filepath.Join(a.TmpPath, "images", "pxeboot", "initrd.img")
   177  	initrdImg, err := os.OpenFile(initrdImgPath, os.O_WRONLY|os.O_APPEND, 0)
   178  	if err != nil {
   179  		return err
   180  	}
   181  	defer initrdImg.Close()
   182  
   183  	_, err = initrdImg.Write(buff)
   184  	if err != nil {
   185  		return err
   186  	}
   187  
   188  	return nil
   189  }
   190  
   191  // Name returns the human-friendly name of the asset.
   192  func (a *AgentArtifacts) Name() string {
   193  	return "Agent Installer Artifacts"
   194  }
   195  
   196  func createDir(bootArtifactsFullPath string) error {
   197  	os.RemoveAll(bootArtifactsFullPath)
   198  
   199  	err := os.Mkdir(bootArtifactsFullPath, 0750)
   200  	if err != nil {
   201  		return err
   202  	}
   203  	return nil
   204  }
   205  
   206  func extractRootFS(bootArtifactsFullPath, agentISOPath, arch string) error {
   207  	agentRootfsimgFile := filepath.Join(bootArtifactsFullPath, fmt.Sprintf("agent.%s-rootfs.img", arch))
   208  	rootfsReader, err := os.Open(filepath.Join(agentISOPath, "images", "pxeboot", "rootfs.img"))
   209  	if err != nil {
   210  		return err
   211  	}
   212  	defer rootfsReader.Close()
   213  
   214  	err = copyfile(agentRootfsimgFile, rootfsReader)
   215  	if err != nil {
   216  		return err
   217  	}
   218  
   219  	return nil
   220  }