diff --git a/pkg/asset/agent/image/agentimage.go b/pkg/asset/agent/image/agentimage.go index 71bcbf97ba9..0f3788c3c3c 100644 --- a/pkg/asset/agent/image/agentimage.go +++ b/pkg/asset/agent/image/agentimage.go @@ -53,6 +53,7 @@ func (a *AgentImage) Dependencies() []asset.Asset { &manifests.AgentManifests{}, &BaseIso{}, &gencrypto.AuthConfig{}, + &manifests.AgentOSImageStream{}, } } @@ -63,7 +64,8 @@ func (a *AgentImage) Generate(ctx context.Context, dependencies asset.Parents) e agentArtifacts := &AgentArtifacts{} agentManifests := &manifests.AgentManifests{} baseIso := &BaseIso{} - dependencies.Get(agentArtifacts, agentManifests, baseIso, agentWorkflow, clusterInfo) + osImageStream := &manifests.AgentOSImageStream{} + dependencies.Get(agentArtifacts, agentManifests, baseIso, agentWorkflow, clusterInfo, osImageStream) if err := workflowreport.GetReport(ctx).Stage(workflow.StageGenerateISO); err != nil { return err @@ -106,7 +108,7 @@ func (a *AgentImage) Generate(ctx context.Context, dependencies asset.Parents) e logrus.Debugf("Using custom rootfs URL: %s", a.rootFSURL) } else { // Default to the URL from the RHCOS streams file - defaultRootFSURL, err := baseIso.getRootFSURL(ctx, a.cpuArch, agentWorkflow, clusterInfo) + defaultRootFSURL, err := baseIso.getRootFSURL(ctx, a.cpuArch, agentWorkflow, clusterInfo, osImageStream.Stream) if err != nil { return err } diff --git a/pkg/asset/agent/image/baseiso.go b/pkg/asset/agent/image/baseiso.go index 16fd508a3d2..a9709ba5f07 100644 --- a/pkg/asset/agent/image/baseiso.go +++ b/pkg/asset/agent/image/baseiso.go @@ -10,7 +10,6 @@ import ( "github.com/sirupsen/logrus" "github.com/openshift/installer/pkg/asset" - "github.com/openshift/installer/pkg/asset/agent" "github.com/openshift/installer/pkg/asset/agent/joiner" "github.com/openshift/installer/pkg/asset/agent/manifests" "github.com/openshift/installer/pkg/asset/agent/mirror" @@ -37,9 +36,8 @@ func (i *BaseIso) Name() string { } // Fetch RootFS URL using the rhcos.json. -func (i *BaseIso) getRootFSURL(ctx context.Context, archName string, agentWorkflow *workflow.AgentWorkflow, clusterInfo *joiner.ClusterInfo) (string, error) { - metal, err := rhcos.GetMetalArtifact( - ctx, archName, customStreamGetter(agentWorkflow, clusterInfo)) +func (i *BaseIso) getRootFSURL(ctx context.Context, archName string, agentWorkflow *workflow.AgentWorkflow, clusterInfo *joiner.ClusterInfo, osImageStream types.OSImageStream) (string, error) { + metal, err := rhcos.GetMetalArtifact(ctx, archName, customStreamGetter(agentWorkflow, clusterInfo), osImageStream) if err != nil { return "", err } @@ -58,7 +56,7 @@ func (i *BaseIso) Dependencies() []asset.Asset { &workflow.AgentWorkflow{}, &joiner.ClusterInfo{}, &manifests.AgentManifests{}, - &agent.OptionalInstallConfig{}, + &manifests.AgentOSImageStream{}, &mirror.RegistriesConf{}, } } @@ -69,11 +67,13 @@ func (i *BaseIso) Generate(ctx context.Context, dependencies asset.Parents) erro registriesConf := &mirror.RegistriesConf{} agentWorkflow := &workflow.AgentWorkflow{} clusterInfo := &joiner.ClusterInfo{} - dependencies.Get(agentManifests, registriesConf, agentWorkflow, clusterInfo) + osImageStream := &manifests.AgentOSImageStream{} + dependencies.Get(agentManifests, registriesConf, agentWorkflow, clusterInfo, osImageStream) baseIsoFileName, err := rhcos.NewBaseISOFetcher( i.getRelease(agentManifests, registriesConf.MirrorConfig), - customStreamGetter(agentWorkflow, clusterInfo)).GetBaseISOFilename(ctx, agentManifests.InfraEnv.Spec.CpuArchitecture) + customStreamGetter(agentWorkflow, clusterInfo), + osImageStream.Stream).GetBaseISOFilename(ctx, agentManifests.InfraEnv.Spec.CpuArchitecture) if err == nil { logrus.Debugf("Using base ISO image %s", baseIsoFileName) diff --git a/pkg/asset/agent/image/ignition.go b/pkg/asset/agent/image/ignition.go index 6b64060e0cd..6204d59d63c 100644 --- a/pkg/asset/agent/image/ignition.go +++ b/pkg/asset/agent/image/ignition.go @@ -115,6 +115,7 @@ func (a *Ignition) Dependencies() []asset.Asset { &mirror.CaBundle{}, &gencrypto.AuthConfig{}, &common.InfraEnvID{}, + &manifests.AgentOSImageStream{}, } } @@ -128,7 +129,8 @@ func (a *Ignition) Generate(ctx context.Context, dependencies asset.Parents) err fencingCredentials := &agentconfig.FencingCredentials{} authConfig := &gencrypto.AuthConfig{} infraEnvAsset := &common.InfraEnvID{} - dependencies.Get(agentManifests, agentConfigAsset, agentHostsAsset, extraManifests, fencingCredentials, authConfig, agentWorkflow, infraEnvAsset) + osImageStream := &manifests.AgentOSImageStream{} + dependencies.Get(agentManifests, agentConfigAsset, agentHostsAsset, extraManifests, fencingCredentials, authConfig, agentWorkflow, infraEnvAsset, osImageStream) clusterInfo := &joiner.ClusterInfo{} if err := workflowreport.GetReport(ctx).Stage(workflow.StageIgnition); err != nil { @@ -269,7 +271,7 @@ func (a *Ignition) Generate(ctx context.Context, dependencies asset.Parents) err infraEnvID := infraEnvAsset.ID logrus.Debug("Generated random infra-env id ", infraEnvID) - osImage, err := getOSImagesInfo(ctx, archName, openshiftVersion, customStreamGetter(agentWorkflow, clusterInfo)) + osImage, err := getOSImagesInfo(ctx, archName, openshiftVersion, customStreamGetter(agentWorkflow, clusterInfo), osImageStream.Stream) if err != nil { return err } @@ -745,13 +747,13 @@ func addExtraManifests(config *igntypes.Config, extraManifests *manifests.ExtraM return nil } -func getOSImagesInfo(ctx context.Context, cpuArch string, openshiftVersion string, streamGetter rhcos.CoreOSBuildFetcher) (*models.OsImage, error) { +func getOSImagesInfo(ctx context.Context, cpuArch string, openshiftVersion string, streamGetter rhcos.CoreOSBuildFetcher, osImageStream types.OSImageStream) (*models.OsImage, error) { osImage := &models.OsImage{ CPUArchitecture: &cpuArch, } osImage.OpenshiftVersion = &openshiftVersion - artifacts, err := rhcos.GetMetalArtifact(ctx, cpuArch, streamGetter) + artifacts, err := rhcos.GetMetalArtifact(ctx, cpuArch, streamGetter, osImageStream) if err != nil { return nil, err } diff --git a/pkg/asset/agent/image/unconfigured_ignition.go b/pkg/asset/agent/image/unconfigured_ignition.go index 00e3cf66da4..f046ac6e0b7 100644 --- a/pkg/asset/agent/image/unconfigured_ignition.go +++ b/pkg/asset/agent/image/unconfigured_ignition.go @@ -155,7 +155,7 @@ func (a *UnconfiguredIgnition) Generate(ctx context.Context, dependencies asset. if err != nil { return err } - osImage, err := getOSImagesInfo(ctx, archName, openshiftVersion, nil) + osImage, err := getOSImagesInfo(ctx, archName, openshiftVersion, nil, "") if err != nil { return err } diff --git a/pkg/asset/agent/manifests/agent_test.go b/pkg/asset/agent/manifests/agent_test.go index bff8008ae16..bd0a530f71a 100644 --- a/pkg/asset/agent/manifests/agent_test.go +++ b/pkg/asset/agent/manifests/agent_test.go @@ -13,6 +13,7 @@ import ( "github.com/openshift/assisted-service/models" hivev1 "github.com/openshift/hive/apis/hive/v1" "github.com/openshift/installer/pkg/asset" + "github.com/openshift/installer/pkg/asset/manifests" ) func TestAgentManifests_Generate(t *testing.T) { @@ -59,6 +60,7 @@ func TestAgentManifests_Generate(t *testing.T) { &AgentClusterInstall{Config: fakeAgentClusterInstall}, &ClusterDeployment{Config: fakeClusterDeployment}, &ClusterImageSet{Config: fakeClusterImageSet}, + &manifests.OSImageStream{}, }, ExpectedPullSecret: fakeSecret, ExpectedInfraEnv: fakeInfraEnv, @@ -93,6 +95,7 @@ func TestAgentManifests_Generate(t *testing.T) { &AgentClusterInstall{}, &ClusterDeployment{}, &ClusterImageSet{}, + &manifests.OSImageStream{}, }, ExpectedError: "invalid agent configuration: spec.nmStateConfigLabelSelector.matchLabels: Required value: infraEnv and fake-nmState NMState Config labels do not match. Expected: map[missing-label:missing-label] Found: map[]", }, diff --git a/pkg/asset/agent/manifests/agentclusterinstall.go b/pkg/asset/agent/manifests/agentclusterinstall.go index eef1c71b10c..e8998851d21 100644 --- a/pkg/asset/agent/manifests/agentclusterinstall.go +++ b/pkg/asset/agent/manifests/agentclusterinstall.go @@ -131,6 +131,8 @@ type agentClusterInstallInstallConfigOverrides struct { FeatureSet configv1.FeatureSet `json:"featureSet,omitempty"` // Allow override of FeatureGates FeatureGates []string `json:"featureGates,omitempty"` + // OSImageStream is the OS Image Stream to be used for all machines in the cluster + OSImageStream types.OSImageStream `json:"osImageStream,omitempty"` } var _ asset.WritableAsset = (*AgentClusterInstall)(nil) @@ -262,6 +264,11 @@ func (a *AgentClusterInstall) Generate(_ context.Context, dependencies asset.Par icOverrides.FeatureGates = installConfig.Config.FeatureGates } + if installConfig.Config.OSImageStream != "" { + icOverridden = true + icOverrides.OSImageStream = installConfig.Config.OSImageStream + } + if installConfig.Config.Proxy != nil { rendezvousIP := "" if agentConfig.Config != nil { diff --git a/pkg/asset/agent/manifests/osimagestream.go b/pkg/asset/agent/manifests/osimagestream.go new file mode 100644 index 00000000000..817f4baccb1 --- /dev/null +++ b/pkg/asset/agent/manifests/osimagestream.go @@ -0,0 +1,118 @@ +package manifests + +import ( + "context" + "encoding/json" + + "github.com/sirupsen/logrus" + metav1 "k8s.io/apimachinery/pkg/apis/meta/v1" + + hiveext "github.com/openshift/assisted-service/api/hiveextension/v1beta1" + mcfgclient "github.com/openshift/client-go/machineconfiguration/clientset/versioned" + "github.com/openshift/installer/pkg/asset" + "github.com/openshift/installer/pkg/asset/agent/joiner" + "github.com/openshift/installer/pkg/asset/agent/workflow" + "github.com/openshift/installer/pkg/rhcos" + "github.com/openshift/installer/pkg/types" +) + +// installConfigOverridesData is used to parse the installConfigOverrides annotation. +type installConfigOverridesData struct { + OSImageStream types.OSImageStream `json:"osImageStream,omitempty"` +} + +// AgentOSImageStream exposes the OS image stream value for Agent workflows. +// This is a pure data asset that does not generate any files. +type AgentOSImageStream struct { + Stream types.OSImageStream +} + +var _ asset.Asset = (*AgentOSImageStream)(nil) + +// Name returns the human-friendly name of the asset. +func (*AgentOSImageStream) Name() string { + return "Agent OS Image Stream" +} + +// Dependencies returns all of the dependencies directly needed to generate +// the asset. +func (*AgentOSImageStream) Dependencies() []asset.Asset { + return []asset.Asset{ + &workflow.AgentWorkflow{}, + &joiner.ClusterInfo{}, + &AgentManifests{}, + } +} + +// Generate extracts the OS image stream value from available sources. +func (a *AgentOSImageStream) Generate(ctx context.Context, dependencies asset.Parents) error { + agentWorkflow := &workflow.AgentWorkflow{} + clusterInfo := &joiner.ClusterInfo{} + agentManifests := &AgentManifests{} + dependencies.Get(agentWorkflow, clusterInfo, agentManifests) + + var stream types.OSImageStream + + switch agentWorkflow.Workflow { + case workflow.AgentWorkflowTypeInstall: + // For install workflow, read from AgentClusterInstall manifest (Layer 2) + if agentManifests.AgentClusterInstall != nil { + stream = a.getStreamFromAgentClusterInstall(agentManifests.AgentClusterInstall) + } + + case workflow.AgentWorkflowTypeAddNodes: + // For add-nodes workflow, query the cluster's OSImageStream CR + if clusterInfo.OpenshiftMachineConfigClient != nil { + var err error + stream, err = a.getStreamFromCluster(ctx, clusterInfo.OpenshiftMachineConfigClient) + if err != nil { + // Log but don't fail - fall back to default + logrus.Warnf("Failed to query cluster OSImageStream: %v", err) + } + } + } + + // Default + if stream == "" { + stream = rhcos.DefaultOSImageStream + } + + a.Stream = stream + return nil +} + +// getStreamFromAgentClusterInstall extracts the osImageStream from the +// AgentClusterInstall installConfigOverrides annotation. +func (a *AgentOSImageStream) getStreamFromAgentClusterInstall(aci *hiveext.AgentClusterInstall) types.OSImageStream { + if aci.Annotations == nil { + return "" + } + + overridesJSON, ok := aci.Annotations[installConfigOverrides] + if !ok { + return "" + } + + var overrides installConfigOverridesData + if err := json.Unmarshal([]byte(overridesJSON), &overrides); err != nil { + logrus.Debugf("Failed to parse installConfigOverrides: %v", err) + return "" + } + + return overrides.OSImageStream +} + +// getStreamFromCluster queries the cluster's OSImageStream CR to determine +// the default stream. +func (a *AgentOSImageStream) getStreamFromCluster(ctx context.Context, client mcfgclient.Interface) (types.OSImageStream, error) { + osImageStream, err := client.MachineconfigurationV1alpha1().OSImageStreams().Get(ctx, "cluster", metav1.GetOptions{}) + if err != nil { + return "", err + } + + if osImageStream.Spec == nil { + return "", nil + } + + return types.OSImageStream(osImageStream.Spec.DefaultStream), nil +} diff --git a/pkg/asset/manifests/osimagestream.go b/pkg/asset/manifests/osimagestream.go index 4c6c9d46bb8..14f4068e62b 100644 --- a/pkg/asset/manifests/osimagestream.go +++ b/pkg/asset/manifests/osimagestream.go @@ -13,13 +13,15 @@ import ( "github.com/openshift/installer/pkg/asset" "github.com/openshift/installer/pkg/asset/installconfig" "github.com/openshift/installer/pkg/rhcos" + "github.com/openshift/installer/pkg/types" ) var osImageStreamFileName = path.Join(openshiftManifestDir, "99_osimagestream.yaml") -// OSImageStream generates the OSImageStream manifest. +// OSImageStream generates the OSImageStream manifest and exposes the stream value. type OSImageStream struct { FileList []*asset.File + Stream types.OSImageStream } var _ asset.WritableAsset = (*OSImageStream)(nil) @@ -42,20 +44,39 @@ func (f *OSImageStream) Generate(_ context.Context, dependencies asset.Parents) installConfig := &installconfig.InstallConfig{} dependencies.Get(installConfig) + if installConfig.Config == nil { + return nil + } + + config := installConfig.Config + // If one of the following are true the OSImageStream CR is not generated // 1. The feature is not enabled // 2. The target is CentOS Stream CoreOS - if ic := installConfig.Config; !ic.Enabled(features.FeatureGateOSStreams) || ic.IsSCOS() { + if !config.Enabled(features.FeatureGateOSStreams) || config.IsSCOS() { // FG disabled or not OCP return nil } - // If no stream was set just report the default one for the current version - stream := installConfig.Config.OSImageStream + // Extract and store the stream value + stream := config.OSImageStream if stream == "" { stream = rhcos.DefaultOSImageStream } + f.Stream = stream + // Generate manifest file + manifest, err := f.createManifest(stream) + if err != nil { + return err + } + f.FileList = append(f.FileList, manifest) + + return nil +} + +// createManifest generates the OSImageStream manifest file. +func (f *OSImageStream) createManifest(stream types.OSImageStream) (*asset.File, error) { osImageStream := &mcfgv1alpha.OSImageStream{ TypeMeta: metav1.TypeMeta{ APIVersion: mcfgv1alpha.SchemeGroupVersion.String(), @@ -71,14 +92,13 @@ func (f *OSImageStream) Generate(_ context.Context, dependencies asset.Parents) osImageStreamData, err := yaml.Marshal(osImageStream) if err != nil { - return fmt.Errorf("failed to create %s manifests from InstallConfig. Error: %w", f.Name(), err) + return nil, fmt.Errorf("failed to create %s manifests from InstallConfig. Error: %w", f.Name(), err) } - f.FileList = append(f.FileList, &asset.File{ + return &asset.File{ Filename: osImageStreamFileName, Data: osImageStreamData, - }) - return nil + }, nil } // Files returns the files generated by the asset. diff --git a/pkg/asset/rhcos/iso.go b/pkg/asset/rhcos/iso.go index ae7bb26fdfc..6c289609e91 100644 --- a/pkg/asset/rhcos/iso.go +++ b/pkg/asset/rhcos/iso.go @@ -22,8 +22,9 @@ import ( // BaseIso generates the base ISO file for the image. type BaseIso struct { - streamGetter CoreOSBuildFetcher - ocRelease ReleasePayload + streamGetter CoreOSBuildFetcher + ocRelease ReleasePayload + osImageStream types.OSImageStream } // CoreOSBuildFetcher will be to used to switch the source of the coreos metadata. @@ -35,14 +36,24 @@ var defaultCoreOSStreamGetter = func(ctx context.Context) (*stream.Stream, error } // NewBaseISOFetcher returns a struct that can be used to fetch a base ISO using -// the default method. -func NewBaseISOFetcher(ocRelease ReleasePayload, streamGetter CoreOSBuildFetcher) *BaseIso { +// the default method. If streamGetter is nil, osImageStream will be used if provided. +func NewBaseISOFetcher(ocRelease ReleasePayload, streamGetter CoreOSBuildFetcher, osImageStream types.OSImageStream) *BaseIso { if streamGetter == nil { - streamGetter = defaultCoreOSStreamGetter + if osImageStream != "" { + streamGetter = func(ctx context.Context) (*stream.Stream, error) { + return rhcos.FetchCoreOSBuild(ctx, osImageStream) + } + } else { + streamGetter = defaultCoreOSStreamGetter + osImageStream = rhcos.DefaultOSImageStream + } + } else if osImageStream == "" { + osImageStream = rhcos.DefaultOSImageStream } return &BaseIso{ - streamGetter: streamGetter, - ocRelease: ocRelease, + streamGetter: streamGetter, + ocRelease: ocRelease, + osImageStream: osImageStream, } } @@ -65,10 +76,18 @@ func (i *BaseIso) GetBaseISOFilename(ctx context.Context, arch string) (baseIsoF } // GetMetalArtifact returns the CoreOS artifacts for metal for a given arch -// from a given stream. -func GetMetalArtifact(ctx context.Context, archName string, streamGetter CoreOSBuildFetcher) (stream.PlatformArtifacts, error) { +// from a given stream. If streamGetter is nil, uses osImageStream if provided, +// otherwise falls back to the default stream. +func GetMetalArtifact(ctx context.Context, archName string, streamGetter CoreOSBuildFetcher, osImageStream types.OSImageStream) (stream.PlatformArtifacts, error) { if streamGetter == nil { - streamGetter = defaultCoreOSStreamGetter + // OSImageStream is part of the default implementation + if osImageStream != "" { + streamGetter = func(ctx context.Context) (*stream.Stream, error) { + return rhcos.FetchCoreOSBuild(ctx, osImageStream) + } + } else { + streamGetter = defaultCoreOSStreamGetter + } } ctx, cancel := context.WithTimeout(ctx, 30*time.Second) @@ -95,7 +114,7 @@ func GetMetalArtifact(ctx context.Context, archName string, streamGetter CoreOSB // Download the ISO using the URL in rhcos.json. func (i *BaseIso) downloadIso(ctx context.Context, archName string) (string, error) { - metal, err := GetMetalArtifact(ctx, archName, i.streamGetter) + metal, err := GetMetalArtifact(ctx, archName, i.streamGetter, "") if err != nil { return "", err } @@ -119,14 +138,14 @@ func (i *BaseIso) checkReleasePayloadBaseISOVersion(ctx context.Context, r Relea logrus.Debugf("Checking release payload base ISO version") // Get current release payload CoreOS version - payloadRelease, err := r.GetBaseIsoVersion(archName) + payloadRelease, err := r.GetBaseIsoVersion(archName, i.osImageStream) if err != nil { logrus.Warnf("unable to determine base ISO version: %s", err.Error()) return } // Get pinned version from installer - metal, err := GetMetalArtifact(ctx, archName, i.streamGetter) + metal, err := GetMetalArtifact(ctx, archName, i.streamGetter, i.osImageStream) if err != nil { logrus.Warnf("unable to determine base ISO version: %s", err.Error()) return @@ -151,7 +170,7 @@ func (i *BaseIso) retrieveBaseIso(ctx context.Context, archName string) (string, if err := workflowreport.GetReport(ctx).SubStage(workflow.StageFetchBaseISOExtract); err != nil { return "", err } - baseIsoFileName, err := i.ocRelease.GetBaseIso(archName, i.streamGetter) + baseIsoFileName, err := i.ocRelease.GetBaseIso(archName, i.streamGetter, i.osImageStream) if err == nil { if err := workflowreport.GetReport(ctx).SubStage(workflow.StageFetchBaseISOVerify); err != nil { return "", err diff --git a/pkg/asset/rhcos/iso_test.go b/pkg/asset/rhcos/iso_test.go index 60bbbb20fdd..36622d0480e 100644 --- a/pkg/asset/rhcos/iso_test.go +++ b/pkg/asset/rhcos/iso_test.go @@ -12,6 +12,8 @@ import ( "github.com/coreos/stream-metadata-go/stream" "github.com/stretchr/testify/assert" + + "github.com/openshift/installer/pkg/types" ) func TestBaseIso(t *testing.T) { @@ -96,7 +98,8 @@ func TestBaseIso(t *testing.T) { }, }, }, nil - }) + }, + "") filename, err := fetcher.GetBaseISOFilename(context.Background(), "") if tc.expectedError == "" { @@ -115,14 +118,14 @@ type mockRelease struct { baseIsoError error } -func (m *mockRelease) GetBaseIso(architecture string, streamGetter CoreOSBuildFetcher) (string, error) { +func (m *mockRelease) GetBaseIso(architecture string, streamGetter CoreOSBuildFetcher, osImageStream types.OSImageStream) (string, error) { if m.baseIsoError != nil { return "", m.baseIsoError } return m.baseIsoFileName, nil } -func (m *mockRelease) GetBaseIsoVersion(architecture string) (string, error) { +func (m *mockRelease) GetBaseIsoVersion(architecture string, osImageStream types.OSImageStream) (string, error) { return m.isoBaseVersion, nil } diff --git a/pkg/asset/rhcos/releaseextract.go b/pkg/asset/rhcos/releaseextract.go index 5cf80760f58..d7bd92d8df0 100644 --- a/pkg/asset/rhcos/releaseextract.go +++ b/pkg/asset/rhcos/releaseextract.go @@ -27,16 +27,38 @@ import ( ) const ( - machineOsImageName = "machine-os-images" - coreOsFileName = "/coreos/coreos-%s.iso" - coreOsSha256FileName = "/coreos/coreos-%s.iso.sha256" - coreOsStreamFileName = "/coreos/coreos-stream.json" + machineOsImageName = "machine-os-images" // ocDefaultTries is the number of times to execute the oc command on failures. ocDefaultTries = 5 // ocDefaultRetryDelay is the time between retries. ocDefaultRetryDelay = time.Second * 5 ) +// getCoreOsFileName returns the ISO file path in machine-os-images based on stream. +func getCoreOsFileName(stream types.OSImageStream, architecture string) string { + // rhel-10 uses coreos10- prefix, rhel-9 uses coreos- prefix + if stream == types.OSImageStreamRHCOS10 { + return fmt.Sprintf("/coreos/coreos10-%s.iso", architecture) + } + return fmt.Sprintf("/coreos/coreos-%s.iso", architecture) +} + +// getCoreOsSha256FileName returns the ISO checksum file path based on stream. +func getCoreOsSha256FileName(stream types.OSImageStream, architecture string) string { + if stream == types.OSImageStreamRHCOS10 { + return fmt.Sprintf("/coreos/coreos10-%s.iso.sha256", architecture) + } + return fmt.Sprintf("/coreos/coreos-%s.iso.sha256", architecture) +} + +// getCoreOsStreamFileName returns the stream metadata file path based on stream. +func getCoreOsStreamFileName(stream types.OSImageStream) string { + if stream == types.OSImageStreamRHCOS10 { + return "/coreos/coreos10-stream.json" + } + return "/coreos/coreos-stream.json" +} + // ExtractConfig is used to set up the retries for extracting the base ISO. type ExtractConfig struct { MaxTries uint @@ -45,8 +67,8 @@ type ExtractConfig struct { // ReleasePayload is the interface to use the oc command to the get image info. type ReleasePayload interface { - GetBaseIso(architecture string, streamGetter CoreOSBuildFetcher) (string, error) - GetBaseIsoVersion(architecture string) (string, error) + GetBaseIso(architecture string, streamGetter CoreOSBuildFetcher, osImageStream types.OSImageStream) (string, error) + GetBaseIsoVersion(architecture string, osImageStream types.OSImageStream) (string, error) ExtractFile(image string, filename string, architecture string) ([]string, error) } @@ -93,7 +115,7 @@ func (r *releasePayload) ExtractFile(image string, filename string, architecture } // Get the CoreOS ISO from the releaseImage. -func (r *releasePayload) GetBaseIso(architecture string, streamGetter CoreOSBuildFetcher) (string, error) { +func (r *releasePayload) GetBaseIso(architecture string, streamGetter CoreOSBuildFetcher, osImageStream types.OSImageStream) (string, error) { // Get the machine-os-images pullspec from the release and use that to get the CoreOS ISO image, err := r.getImageFromRelease(machineOsImageName, architecture) if err != nil { @@ -105,7 +127,7 @@ func (r *releasePayload) GetBaseIso(architecture string, streamGetter CoreOSBuil return "", err } - filename := fmt.Sprintf(coreOsFileName, architecture) + filename := getCoreOsFileName(osImageStream, architecture) // Check if file is already cached cachedFile, err := cache.GetFileFromCache(path.Base(filename), cacheDir) if err != nil { @@ -113,7 +135,7 @@ func (r *releasePayload) GetBaseIso(architecture string, streamGetter CoreOSBuil } if cachedFile != "" { logrus.Info("Verifying cached file") - valid, err := r.verifyCacheFile(image, cachedFile, architecture, streamGetter) + valid, err := r.verifyCacheFile(image, cachedFile, architecture, streamGetter, osImageStream) if err != nil { return "", err } @@ -132,14 +154,15 @@ func (r *releasePayload) GetBaseIso(architecture string, streamGetter CoreOSBuil return path[0], err } -func (r *releasePayload) GetBaseIsoVersion(architecture string) (string, error) { - files, err := r.ExtractFile(machineOsImageName, coreOsStreamFileName, architecture) +func (r *releasePayload) GetBaseIsoVersion(architecture string, osImageStream types.OSImageStream) (string, error) { + streamFileName := getCoreOsStreamFileName(osImageStream) + files, err := r.ExtractFile(machineOsImageName, streamFileName, architecture) if err != nil { return "", err } if len(files) > 1 { - return "", fmt.Errorf("too many files found for %s", coreOsStreamFileName) + return "", fmt.Errorf("too many files found for %s", streamFileName) } rawData, err := os.ReadFile(files[0]) @@ -298,7 +321,7 @@ func matchingHash(imageSha []byte, sha string) bool { } // Check if there is a different base ISO in the release payload. -func (r *releasePayload) verifyCacheFile(image, file, architecture string, streamGetter CoreOSBuildFetcher) (bool, error) { +func (r *releasePayload) verifyCacheFile(image, file, architecture string, streamGetter CoreOSBuildFetcher, osImageStream types.OSImageStream) (bool, error) { // Get hash of cached file f, err := os.Open(file) if err != nil { @@ -327,7 +350,7 @@ func (r *releasePayload) verifyCacheFile(image, file, architecture string, strea defer os.RemoveAll(tempDir) - shaFilename := fmt.Sprintf(coreOsSha256FileName, architecture) + shaFilename := getCoreOsSha256FileName(osImageStream, architecture) shaFile, err := r.extractFileFromImage(image, shaFilename, tempDir, architecture) if err != nil { logrus.Debug("Could not get SHA from payload for cache comparison") diff --git a/pkg/infrastructure/baremetal/bootstrap.go b/pkg/infrastructure/baremetal/bootstrap.go index e4f4304b487..231b55a1cc5 100644 --- a/pkg/infrastructure/baremetal/bootstrap.go +++ b/pkg/infrastructure/baremetal/bootstrap.go @@ -178,7 +178,8 @@ func getLiveISO(config baremetalConfig, arch string) (string, error) { config.PullSecret, config.MirrorConfig, ), - nil) + nil, + "") return fetcher.GetBaseISOFilename(context.Background(), arch) } diff --git a/pkg/types/validation/installconfig_test.go b/pkg/types/validation/installconfig_test.go index 7b76fbbba6a..8b1a629f22a 100644 --- a/pkg/types/validation/installconfig_test.go +++ b/pkg/types/validation/installconfig_test.go @@ -3015,6 +3015,16 @@ func TestValidateInstallConfig(t *testing.T) { }(), expectedError: "the Ingress capability is required", }, + { + name: "invalid OSImageStream value", + installConfig: func() *types.InstallConfig { + c := validInstallConfig() + c.FeatureSet = configv1.TechPreviewNoUpgrade + c.OSImageStream = "invalid" + return c + }(), + expectedError: "Unsupported OS Image Stream. Supported values are: rhel-9, rhel-10", + }, { name: "invalid OSImageStream set", installConfig: func() *types.InstallConfig {