Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
525 changes: 525 additions & 0 deletions data/data/coreos/coreos-rhel-10.json
Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

This is the fresh file @ravanelli provided, thanks!

Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Is this file included in this PR simply for testing, or would we expect to merge it along with this PR?

Large diffs are not rendered by default.

File renamed without changes.
163 changes: 116 additions & 47 deletions hack/build-coreos-manifest.go
Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Reviewers: Please take a look at this change. It's the safest approach I could come up with. Basically, the structure of the ConfigMap is preserved and the stream field will continue to be reported, but from now on, it will use the default stream. A new streams field with a JSON map<streamName, streamJson> is now present. The format for SCOS remains unchanged, and the marketplace file is merged only if the file exists, which is only true for rhcos-9 for now.

cc:@djoshy

Original file line number Diff line number Diff line change
Expand Up @@ -9,31 +9,51 @@ package main
import (
"encoding/json"
"fmt"
"maps"
"os"
"path/filepath"
"slices"
"strings"

"github.com/coreos/stream-metadata-go/stream"
"github.com/coreos/stream-metadata-go/stream/rhcos"
corev1 "k8s.io/api/core/v1"
metav1 "k8s.io/apimachinery/pkg/apis/meta/v1"
"sigs.k8s.io/yaml"

instRhcos "github.com/openshift/installer/pkg/rhcos"
)

const (
streamRHCOSJSON = "data/data/coreos/rhcos.json"
streamSCOSJSON = "data/data/coreos/scos.json"
streamMarketplaceRHCOSJSON = "data/data/coreos/marketplace-rhcos.json"
scosTAG = "scos"
dest = "bin/manifests/coreos-bootimages.yaml"
scosTAG = "scos"
dest = "bin/manifests/coreos-bootimages.yaml"
)

func run() error {
bootimages, err := getBootImages()
streamsFiles, err := getOSImageStreams()
if err != nil {
return err
}

var defaultStreamFiles *streamFiles
if len(streamsFiles) == 0 {
return fmt.Errorf("no OS image streams found")
} else if len(streamsFiles) == 1 {
defaultFile := streamsFiles[slices.Collect(maps.Keys(streamsFiles))[0]]
defaultStreamFiles = &defaultFile
} else {
rhel9Files, ok := streamsFiles[string(instRhcos.DefaultOSImageStream)]
if !ok {
return fmt.Errorf("no %v image streams found. %v is considered the default stream", instRhcos.DefaultOSImageStream, instRhcos.DefaultOSImageStream)
}
defaultStreamFiles = &rhel9Files
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Could we use a more generic name like streamFiles instead of rhel9Files? The contents would change when we update DefaultOSImageStream if I'm understanding correctly?

}

defaultStreamBytes, err := defaultStreamFiles.getBytes()
if err != nil {
return fmt.Errorf("could not read default stream bytes: %w", err)
}

cm := &corev1.ConfigMap{
TypeMeta: metav1.TypeMeta{
APIVersion: corev1.SchemeGroupVersion.String(),
Expand All @@ -50,10 +70,28 @@ func run() error {
},
Data: map[string]string{
"releaseVersion": "0.0.1-snapshot",
"stream": string(bootimages),
"stream": string(defaultStreamBytes),
},
}

if len(streamsFiles) > 1 {
data := &streamsData{
Streams: make(map[string]string),
}
for name, files := range streamsFiles {
streamBytes, err := files.getBytes()
if err != nil {
return fmt.Errorf("could not read %s stream bytes: %w", name, err)
}
data.Streams[name] = string(streamBytes)
}
streamsJsonData, err := json.Marshal(data)
if err != nil {
return fmt.Errorf("failed to marshal streams data: %w", err)
}
cm.Data["streams"] = string(streamsJsonData)
}

b, err := yaml.Marshal(cm)
if err != nil {
return err
Expand All @@ -71,64 +109,95 @@ func run() error {
return nil
}

func getBootImages() ([]byte, error) {
var okd bool
var streamJSON string
tags, _ := os.LookupEnv("TAGS")
switch {
case strings.Contains(tags, scosTAG):
streamJSON = streamSCOSJSON
okd = true
default:
streamJSON = streamRHCOSJSON
}

bootimages, err := os.ReadFile(streamJSON)
if err != nil {
return nil, err
}

if okd {
// okd does not yet have marketplace images, so we are done
return bootimages, nil
}

return mergeMarketplaceStream(bootimages)
type streamsData struct {
Streams map[string]string `json:"streams"`
}

type marketplaceStream map[string]*rhcos.Marketplace
type streamFiles struct {
streamFile string
marketplaceFile string
name string
}

func mergeMarketplaceStream(streamJSON []byte) ([]byte, error) {
mktStream := marketplaceStream{}
mktJSON, err := os.ReadFile(streamMarketplaceRHCOSJSON)
func (s *streamFiles) getBytes() ([]byte, error) {
streamJson, err := os.ReadFile(s.streamFile)
if err != nil {
return nil, fmt.Errorf("failed to open marketplace file: %w", err)
}
if err := json.Unmarshal(mktJSON, &mktStream); err != nil {
return nil, fmt.Errorf("failed to unmarshal market stream: %w", err)
return nil, fmt.Errorf("failed to open stream file: %w", err)
}

stream := stream.Stream{}
if err := json.Unmarshal(streamJSON, &stream); err != nil {
bootImageStream := stream.Stream{}
if err := json.Unmarshal(streamJson, &bootImageStream); err != nil {
return nil, fmt.Errorf("failed to unmarshal boot image stream: %w", err)
}

for name, arch := range stream.Architectures {
if mkt, ok := mktStream[name]; ok {
if arch.RHELCoreOSExtensions == nil {
arch.RHELCoreOSExtensions = &rhcos.Extensions{}
if s.marketplaceFile != "" {
mktStream := marketplaceStream{}
mktJSON, err := os.ReadFile(s.marketplaceFile)
if err != nil {
return nil, fmt.Errorf("failed to open marketplace file: %w", err)
}
if err := json.Unmarshal(mktJSON, &mktStream); err != nil {
return nil, fmt.Errorf("failed to unmarshal market stream: %w", err)
}
for name, arch := range bootImageStream.Architectures {
if mkt, ok := mktStream[name]; ok {
if arch.RHELCoreOSExtensions == nil {
arch.RHELCoreOSExtensions = &rhcos.Extensions{}
}
arch.RHELCoreOSExtensions.Marketplace = mkt
}
arch.RHELCoreOSExtensions.Marketplace = mkt
}
}

bootImgs, err := json.Marshal(stream)
bootImgs, err := json.Marshal(bootImageStream)
if err != nil {
return nil, fmt.Errorf("failed to marshal merged boot image stream: %w", err)
}
return bootImgs, nil
}

func getOSImageStreams() (map[string]streamFiles, error) {
name := "coreos"
if isOKD() {
name = "scos"
}
matches, err := filepath.Glob(fmt.Sprintf("data/data/coreos/%s*.json", name))
if err != nil {
return nil, fmt.Errorf("failed to find image streams: %v", err)
}
streams := make(map[string]streamFiles)
for _, match := range matches {
filename := filepath.Base(match)
if strings.HasPrefix(filename, name+"-") {
// It's a stream

streamName := strings.TrimPrefix(strings.TrimSuffix(filename, ".json"), name+"-")
streamFile := streamFiles{
streamFile: match,
name: streamName,
}

marketPlaceFile := filepath.Join(filepath.Dir(match), "marketplace-"+filename)
if _, err := os.Stat(marketPlaceFile); err == nil {
streamFile.marketplaceFile = marketPlaceFile
}
Comment on lines +173 to +182
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

We could also move marketplace files to a different directory... perhaps that would be cleaner. WDYT?

streams[streamName] = streamFile
} else if !strings.Contains(filename, "-") {
// It's a plain no OSImageStream, ie SCOS
streams[""] = streamFiles{
streamFile: match,
}
}
}
return streams, nil
}

func isOKD() bool {
tags, _ := os.LookupEnv("TAGS")
return strings.Contains(tags, scosTAG)
}

type marketplaceStream map[string]*rhcos.Marketplace

func main() {
if err := run(); err != nil {
fmt.Fprintf(os.Stderr, "%s\n", err)
Expand Down
4 changes: 2 additions & 2 deletions hack/rhcos/populate-marketplace-imagestream.go
Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Simplest ever approach, should this be ok for now? I'm not too familiar with the concept of marketplace, so not really sure.

Original file line number Diff line number Diff line change
Expand Up @@ -15,8 +15,8 @@ import (
)

const (
streamRHCOSJSON = "data/data/coreos/rhcos.json"
streamMarketplaceRHCOSJSON = "data/data/coreos/marketplace-rhcos.json"
streamRHCOSJSON = "data/data/coreos/coreos-rhel-9.json"
streamMarketplaceRHCOSJSON = "data/data/coreos/marketplace-coreos-rhel-9.json"

x86 = "x86_64"
arm64 = "aarch64"
Expand Down
6 changes: 4 additions & 2 deletions pkg/asset/agent/image/baseiso.go
Original file line number Diff line number Diff line change
Expand Up @@ -35,10 +35,12 @@ type BaseIso struct {
// CoreOSBuildFetcher will be to used to switch the source of the coreos metadata.
type CoreOSBuildFetcher func(ctx context.Context) (*stream.Stream, error)

func DefaultCoreOSStreamGetter(ctx context.Context) (*stream.Stream, error) {
return rhcos.FetchCoreOSBuild(ctx, rhcos.DefaultOSImageStream)
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Not sure if I'm missing something, but my expectations for this asset weres to modify setStreamGetter method to take into account the OSImageStream (by reading it from the agent.OptionalInstallConfig{}, already listed as a dependency). Otherwise the live ISO will remain pinned to rhcos9.

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I haven't covered the agent cause I saw that I'd need to cover the case where the install-config is not provided but the AgentConfig is given, that means I need to add a field to it and so on. To avoid making the change bigger and bigger I thought about leaving the agent side consistent and ready for a follow up.
Anyways, if you think that for now supporting the intall-config.yaml only is enought, cool, I can incorporate this simple change to this PR and do the rest as a follow up.

Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I see that as an incremental step. The first step usually is to support a new install-config field in the connected environment (ABI may be used also for that). With a simple change that part could be covered - otherwise with the current code a user may be confused by the fact that a config field was specified but "ignored".
The alternative (still valid) would be to put an explicit validation rule in agent.OptionalInstallConfig{} to fail if a value different from the default is specified as "unsupported". At least there will not be any confusing behavior for the end user

Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

@pablintino please disregard my previous comment. I understood (also from @djoshy comment) that for the 4.22 scope the current approach is correct (just default to rhcos.DefaultOSImageStream)

}

var (
baseIsoFilename = ""
// DefaultCoreOSStreamGetter uses the pinned metadata.
DefaultCoreOSStreamGetter = rhcos.FetchCoreOSBuild
)

var _ asset.WritableAsset = (*BaseIso)(nil)
Expand Down
8 changes: 4 additions & 4 deletions pkg/asset/imagebased/image/baseiso.go
Original file line number Diff line number Diff line change
Expand Up @@ -16,10 +16,6 @@ import (
"github.com/openshift/installer/pkg/types"
)

var (
defaultCoreOSStreamGetter = rhcos.FetchCoreOSBuild
)

// BaseIso generates the base ISO file for the image.
type BaseIso struct {
File *asset.File
Expand All @@ -30,6 +26,10 @@ type BaseIso struct {
// CoreOSBuildFetcher will be to used to switch the source of the coreos metadata.
type CoreOSBuildFetcher func(ctx context.Context) (*stream.Stream, error)

func defaultCoreOSStreamGetter(ctx context.Context) (*stream.Stream, error) {
return rhcos.FetchCoreOSBuild(ctx, rhcos.DefaultOSImageStream)
}

var _ asset.WritableAsset = (*BaseIso)(nil)

// Name returns the human-friendly name of the asset.
Expand Down
7 changes: 4 additions & 3 deletions pkg/asset/installconfig/aws/platform.go
Original file line number Diff line number Diff line change
Expand Up @@ -10,14 +10,15 @@ import (
"github.com/AlecAivazis/survey/v2/core"
"github.com/sirupsen/logrus"

"github.com/openshift/installer/pkg/rhcos"
"github.com/openshift/installer/pkg/types/aws"
"github.com/openshift/installer/pkg/version"
)

// Platform collects AWS-specific configuration.
func Platform(ctx context.Context) (*aws.Platform, error) {
architecture := version.DefaultArch()
regions, err := knownPublicRegions(architecture)
regions, err := knownPublicRegions(architecture, rhcos.DefaultOSImageStream)
if err != nil {
return nil, fmt.Errorf("failed to get AWS public regions: %w", err)
}
Expand All @@ -43,7 +44,7 @@ func Platform(ctx context.Context) (*aws.Platform, error) {
}

defaultRegion := "us-east-1"
if found, err := IsKnownPublicRegion(defaultRegion, architecture); !found || err != nil {
if found, err := IsKnownPublicRegion(defaultRegion, architecture, rhcos.DefaultOSImageStream); !found || err != nil {
panic(fmt.Sprintf("installer bug: invalid default AWS region %q", defaultRegion))
}

Expand All @@ -53,7 +54,7 @@ func Platform(ctx context.Context) (*aws.Platform, error) {
}

if config.Region != "" {
found, err := IsKnownPublicRegion(config.Region, architecture)
found, err := IsKnownPublicRegion(config.Region, architecture, rhcos.DefaultOSImageStream)
if err != nil {
return nil, fmt.Errorf("failed to determine if region is public: %w", err)
}
Expand Down
8 changes: 4 additions & 4 deletions pkg/asset/installconfig/aws/regions.go
Original file line number Diff line number Diff line change
Expand Up @@ -19,8 +19,8 @@ const (
// knownPublicRegions is the subset of public AWS regions where RHEL CoreOS images are published.
// This subset does not include supported regions which are found in other partitions, such as us-gov-east-1.
// Returns: a list of region names.
func knownPublicRegions(architecture types.Architecture) ([]string, error) {
required := rhcos.AMIRegions(architecture)
func knownPublicRegions(architecture types.Architecture, osImageStream types.OSImageStream) ([]string, error) {
required := rhcos.AMIRegions(architecture, osImageStream)

ctx := context.Background()
client, err := NewEC2Client(ctx, EndpointOptions{
Expand Down Expand Up @@ -49,8 +49,8 @@ func knownPublicRegions(architecture types.Architecture) ([]string, error) {

// IsKnownPublicRegion returns true if a specified region is Known to the installer.
// A known region is the subset of public AWS regions where RHEL CoreOS images are published.
func IsKnownPublicRegion(region string, architecture types.Architecture) (bool, error) {
publicRegions, err := knownPublicRegions(architecture)
func IsKnownPublicRegion(region string, architecture types.Architecture, osImageStream types.OSImageStream) (bool, error) {
publicRegions, err := knownPublicRegions(architecture, osImageStream)
if err != nil {
return false, err
}
Expand Down
2 changes: 1 addition & 1 deletion pkg/asset/installconfig/aws/validation.go
Original file line number Diff line number Diff line change
Expand Up @@ -121,7 +121,7 @@ func validatePlatform(ctx context.Context, meta *Metadata, fldPath *field.Path,

func validateAMI(ctx context.Context, meta *Metadata, config *types.InstallConfig) field.ErrorList {
// accept AMI from the rhcos stream metadata
if rhcos.AMIRegions(config.ControlPlane.Architecture).Has(config.Platform.AWS.Region) {
if rhcos.AMIRegions(config.ControlPlane.Architecture, config.OSImageStream).Has(config.Platform.AWS.Region) {
return nil
}

Expand Down
6 changes: 3 additions & 3 deletions pkg/asset/installconfig/nutanix/validation.go
Original file line number Diff line number Diff line change
Expand Up @@ -66,7 +66,7 @@ func ValidateForProvisioning(ic *types.InstallConfig) error {

// validate PreloadedOSImageName if configured
if p.PreloadedOSImageName != "" {
err = validatePreloadedImage(ctx, nc, p)
err = validatePreloadedImage(ctx, nc, p, ic.OSImageStream)
if err != nil {
errList = append(errList, field.Invalid(parentPath.Child("preloadedOSImageName"), p.PreloadedOSImageName, fmt.Sprintf("fail to validate the preloaded rhcos image: %v", err)))
}
Expand Down Expand Up @@ -114,9 +114,9 @@ func ValidateForProvisioning(ic *types.InstallConfig) error {
return errList.ToAggregate()
}

func validatePreloadedImage(ctx context.Context, nc *nutanixclientv3.Client, p *nutanixtypes.Platform) error {
func validatePreloadedImage(ctx context.Context, nc *nutanixclientv3.Client, p *nutanixtypes.Platform, osImageStream types.OSImageStream) error {
// retrieve the rhcos release version
rhcosStream, err := rhcos.FetchCoreOSBuild(ctx)
rhcosStream, err := rhcos.FetchCoreOSBuild(ctx, osImageStream)
if err != nil {
return err
}
Expand Down
6 changes: 3 additions & 3 deletions pkg/asset/installconfig/vsphere/validation.go
Original file line number Diff line number Diff line change
Expand Up @@ -122,7 +122,7 @@ func ValidateForProvisioning(ic *types.InstallConfig) error {
}
defer cleanup()

err = getRhcosStream(validationCtx)
err = getRhcosStream(validationCtx, ic.OSImageStream)
if err != nil {
return err
}
Expand Down Expand Up @@ -874,12 +874,12 @@ func compareCurrentToTemplate(templateProductVersion, rhcosStreamVersion string)
return nil
}

func getRhcosStream(validationCtx *validationContext) error {
func getRhcosStream(validationCtx *validationContext, osImageStream types.OSImageStream) error {
var err error
ctx, cancel := context.WithTimeout(context.TODO(), 60*time.Second)
defer cancel()

validationCtx.rhcosStream, err = rhcos.FetchCoreOSBuild(ctx)
validationCtx.rhcosStream, err = rhcos.FetchCoreOSBuild(ctx, osImageStream)

if err != nil {
return err
Expand Down
2 changes: 1 addition & 1 deletion pkg/asset/rhcos/bootstrap_image.go
Original file line number Diff line number Diff line change
Expand Up @@ -48,7 +48,7 @@ func (i *BootstrapImage) Generate(ctx context.Context, p asset.Parents) error {
switch config.Platform.Name() {
case baremetal.Name:
archName := arch.RpmArch(string(config.ControlPlane.Architecture))
st, err := rhcos.FetchCoreOSBuild(ctx)
st, err := rhcos.FetchCoreOSBuild(ctx, ic.Config.OSImageStream)
if err != nil {
return err
}
Expand Down
Loading