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 }