github.com/openshift/installer@v1.4.17/pkg/asset/agent/image/baseiso.go (about) 1 package image 2 3 import ( 4 "context" 5 "fmt" 6 "io/fs" 7 "os" 8 "os/exec" 9 "time" 10 11 "github.com/coreos/stream-metadata-go/arch" 12 "github.com/coreos/stream-metadata-go/stream" 13 "github.com/pkg/errors" 14 "github.com/sirupsen/logrus" 15 16 "github.com/openshift/installer/pkg/asset" 17 "github.com/openshift/installer/pkg/asset/agent" 18 "github.com/openshift/installer/pkg/asset/agent/joiner" 19 "github.com/openshift/installer/pkg/asset/agent/manifests" 20 "github.com/openshift/installer/pkg/asset/agent/mirror" 21 "github.com/openshift/installer/pkg/asset/agent/workflow" 22 "github.com/openshift/installer/pkg/rhcos" 23 "github.com/openshift/installer/pkg/rhcos/cache" 24 "github.com/openshift/installer/pkg/types" 25 ) 26 27 // BaseIso generates the base ISO file for the image 28 type BaseIso struct { 29 File *asset.File 30 streamGetter CoreOSBuildFetcher 31 ocRelease Release 32 } 33 34 // CoreOSBuildFetcher will be to used to switch the source of the coreos metadata. 35 type CoreOSBuildFetcher func(ctx context.Context) (*stream.Stream, error) 36 37 var ( 38 baseIsoFilename = "" 39 // DefaultCoreOSStreamGetter uses the pinned metadata. 40 DefaultCoreOSStreamGetter = rhcos.FetchCoreOSBuild 41 ) 42 43 var _ asset.WritableAsset = (*BaseIso)(nil) 44 45 // Name returns the human-friendly name of the asset. 46 func (i *BaseIso) Name() string { 47 return "BaseIso Image" 48 } 49 50 func (i *BaseIso) getMetalArtifact(ctx context.Context, archName string) (stream.PlatformArtifacts, error) { 51 ctx, cancel := context.WithTimeout(ctx, 30*time.Second) 52 defer cancel() 53 54 // Get the ISO to use from rhcos.json 55 st, err := i.streamGetter(ctx) 56 if err != nil { 57 return stream.PlatformArtifacts{}, err 58 } 59 60 streamArch, err := st.GetArchitecture(archName) 61 if err != nil { 62 return stream.PlatformArtifacts{}, err 63 } 64 65 metal, ok := streamArch.Artifacts["metal"] 66 if !ok { 67 return stream.PlatformArtifacts{}, fmt.Errorf("coreOs stream data not found for 'metal' artifact") 68 } 69 70 return metal, nil 71 } 72 73 // Download the ISO using the URL in rhcos.json. 74 func (i *BaseIso) downloadIso(ctx context.Context, archName string) (string, error) { 75 metal, err := i.getMetalArtifact(ctx, archName) 76 if err != nil { 77 return "", err 78 } 79 80 format, ok := metal.Formats["iso"] 81 if !ok { 82 return "", fmt.Errorf("no ISO found to download for %s", archName) 83 } 84 85 url := format.Disk.Location 86 sha := format.Disk.Sha256 87 cachedImage, err := cache.DownloadImageFileWithSha(url, cache.AgentApplicationName, sha) 88 if err != nil { 89 return "", errors.Wrapf(err, "failed to download base ISO image %s", url) 90 } 91 92 return cachedImage, nil 93 } 94 95 // Fetch RootFS URL using the rhcos.json. 96 func (i *BaseIso) getRootFSURL(ctx context.Context, archName string) (string, error) { 97 metal, err := i.getMetalArtifact(ctx, archName) 98 if err != nil { 99 return "", err 100 } 101 102 if format, ok := metal.Formats["pxe"]; ok { 103 rootFSUrl := format.Rootfs.Location 104 return rootFSUrl, nil 105 } 106 107 return "", fmt.Errorf("no RootFSURL found for %s", archName) 108 } 109 110 // Dependencies returns dependencies used by the asset. 111 func (i *BaseIso) Dependencies() []asset.Asset { 112 return []asset.Asset{ 113 &workflow.AgentWorkflow{}, 114 &joiner.ClusterInfo{}, 115 &manifests.AgentManifests{}, 116 &agent.OptionalInstallConfig{}, 117 &mirror.RegistriesConf{}, 118 } 119 } 120 121 func (i *BaseIso) checkReleasePayloadBaseISOVersion(ctx context.Context, r Release, archName string) { 122 logrus.Debugf("Checking release payload base ISO version") 123 124 // Get current release payload CoreOS version 125 payloadRelease, err := r.GetBaseIsoVersion(archName) 126 if err != nil { 127 logrus.Warnf("unable to determine base ISO version: %s", err.Error()) 128 return 129 } 130 131 // Get pinned version from installer 132 metal, err := i.getMetalArtifact(ctx, archName) 133 if err != nil { 134 logrus.Warnf("unable to determine base ISO version: %s", err.Error()) 135 return 136 } 137 138 // Check for a mismatch 139 if metal.Release != payloadRelease { 140 logrus.Warnf("base ISO version mismatch in release payload. Expected version %s but found %s", metal.Release, payloadRelease) 141 } 142 } 143 144 // Generate the baseIso 145 func (i *BaseIso) Generate(ctx context.Context, dependencies asset.Parents) error { 146 var err error 147 var baseIsoFileName string 148 149 if urlOverride, ok := os.LookupEnv("OPENSHIFT_INSTALL_OS_IMAGE_OVERRIDE"); ok && urlOverride != "" { 150 logrus.Warn("Found override for OS Image. Please be warned, this is not advised") 151 baseIsoFileName, err = cache.DownloadImageFile(urlOverride, cache.AgentApplicationName) 152 } else { 153 i.setStreamGetter(dependencies) 154 baseIsoFileName, err = i.retrieveBaseIso(ctx, dependencies) 155 } 156 157 if err == nil { 158 logrus.Debugf("Using base ISO image %s", baseIsoFileName) 159 i.File = &asset.File{Filename: baseIsoFileName} 160 return nil 161 } 162 logrus.Debugf("Failed to download base ISO: %s", err) 163 164 return errors.Wrap(err, "failed to get base ISO image") 165 } 166 167 func (i *BaseIso) setStreamGetter(dependencies asset.Parents) { 168 if i.streamGetter != nil { 169 return 170 } 171 172 agentWorkflow := &workflow.AgentWorkflow{} 173 clusterInfo := &joiner.ClusterInfo{} 174 dependencies.Get(agentWorkflow, clusterInfo) 175 176 i.streamGetter = DefaultCoreOSStreamGetter 177 if agentWorkflow.Workflow == workflow.AgentWorkflowTypeAddNodes { 178 i.streamGetter = func(ctx context.Context) (*stream.Stream, error) { 179 return clusterInfo.OSImage, nil 180 } 181 } 182 } 183 184 func (i *BaseIso) getRelease(agentManifests *manifests.AgentManifests, registriesConf *mirror.RegistriesConf) Release { 185 if i.ocRelease != nil { 186 return i.ocRelease 187 } 188 189 releaseImage := agentManifests.ClusterImageSet.Spec.ReleaseImage 190 pullSecret := agentManifests.GetPullSecretData() 191 192 i.ocRelease = NewRelease( 193 Config{MaxTries: OcDefaultTries, RetryDelay: OcDefaultRetryDelay}, 194 releaseImage, pullSecret, registriesConf.MirrorConfig, i.streamGetter) 195 196 return i.ocRelease 197 } 198 199 func (i *BaseIso) retrieveBaseIso(ctx context.Context, dependencies asset.Parents) (string, error) { 200 // use the GetIso function to get the BaseIso from the release payload 201 agentManifests := &manifests.AgentManifests{} 202 registriesConf := &mirror.RegistriesConf{} 203 dependencies.Get(agentManifests, registriesConf) 204 205 // Default iso archName to x86_64. 206 archName := arch.RpmArch(types.ArchitectureAMD64) 207 208 if agentManifests.ClusterImageSet != nil { 209 // If specified, use InfraEnv.Spec.CpuArchitecture for iso archName 210 if agentManifests.InfraEnv.Spec.CpuArchitecture != "" { 211 archName = agentManifests.InfraEnv.Spec.CpuArchitecture 212 } 213 214 // If we have the image registry location and 'oc' command is available then get from release payload 215 ocRelease := i.getRelease(agentManifests, registriesConf) 216 logrus.Info("Extracting base ISO from release payload") 217 baseIsoFileName, err := ocRelease.GetBaseIso(archName) 218 if err == nil { 219 i.checkReleasePayloadBaseISOVersion(ctx, ocRelease, archName) 220 221 logrus.Debugf("Extracted base ISO image %s from release payload", baseIsoFileName) 222 i.File = &asset.File{Filename: baseIsoFileName} 223 return baseIsoFileName, nil 224 } 225 226 if errors.Is(err, fs.ErrNotExist) { 227 // if image extract failed to extract the iso that architecture may be missing from release image 228 return "", fmt.Errorf("base ISO for %s not found in release image, check release image architecture", archName) 229 } 230 if !errors.Is(err, &exec.Error{}) { // Already warned about missing oc binary 231 logrus.Warning("Failed to extract base ISO from release payload - check registry configuration") 232 } 233 } 234 235 logrus.Info("Downloading base ISO") 236 return i.downloadIso(ctx, archName) 237 } 238 239 // Files returns the files generated by the asset. 240 func (i *BaseIso) Files() []*asset.File { 241 242 if i.File != nil { 243 return []*asset.File{i.File} 244 } 245 return []*asset.File{} 246 } 247 248 // Load returns the cached baseIso 249 func (i *BaseIso) Load(f asset.FileFetcher) (bool, error) { 250 251 if baseIsoFilename == "" { 252 return false, nil 253 } 254 255 baseIso, err := f.FetchByName(baseIsoFilename) 256 if err != nil { 257 if os.IsNotExist(err) { 258 return false, nil 259 } 260 return false, errors.Wrap(err, fmt.Sprintf("failed to load %s file", baseIsoFilename)) 261 } 262 263 i.File = baseIso 264 return true, nil 265 }