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

Large diffs are not rendered by default.

File renamed without changes.
161 changes: 113 additions & 48 deletions hack/build-coreos-manifest.go
Copy link
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,53 @@ 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"

instTypes "github.com/openshift/installer/pkg/types"
)

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"
defaultStreamName = instTypes.OSImageStreamRHCOS9
)

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

var defaultStreamFiles *streamPath
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 {

defaultPaths, ok := streamsFiles[string(defaultStreamName)]
if !ok {
return fmt.Errorf("no %v image streams found. %v is considered the default stream", defaultStreamName, defaultStreamName)
}
defaultStreamFiles = &defaultPaths
}

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 +72,25 @@ func run() error {
},
Data: map[string]string{
"releaseVersion": "0.0.1-snapshot",
"stream": string(bootimages),
"stream": string(defaultStreamBytes),
},
}

streamsData := make(map[string]json.RawMessage)

for name, files := range streamsFiles {
streamBytes, err := files.getBytes()
if err != nil {
return fmt.Errorf("could not read %s stream bytes: %w", name, err)
}
streamsData[name] = streamBytes
}
streamsJsonData, err := json.MarshalIndent(streamsData, "", " ")
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 +108,92 @@ 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 streamPath struct {
streamFile string
marketplaceFile string
name string
}

type marketplaceStream map[string]*rhcos.Marketplace

func mergeMarketplaceStream(streamJSON []byte) ([]byte, error) {
mktStream := marketplaceStream{}
mktJSON, err := os.ReadFile(streamMarketplaceRHCOSJSON)
func (s *streamPath) 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
bootImageStream.Architectures[name] = arch
}
arch.RHELCoreOSExtensions.Marketplace = mkt
}
}

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

func getOSStreamPaths() (map[string]streamPath, 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]streamPath)
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 := streamPath{
streamFile: match,
name: streamName,
}

marketPlaceFile := filepath.Join(filepath.Dir(match), "marketplace", filename)
if _, err := os.Stat(marketPlaceFile); err == nil {
streamFile.marketplaceFile = marketPlaceFile
}
streams[streamName] = streamFile
} else if !strings.Contains(filename, "-") {
// It's a plain no OSImageStream, ie SCOS
streams[""] = streamPath{
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
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
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 @@ -15,8 +15,8 @@ import (
// 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 @@ -45,8 +45,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
4 changes: 2 additions & 2 deletions pkg/asset/rhcos/image.go
Original file line number Diff line number Diff line change
Expand Up @@ -90,7 +90,7 @@ func osImage(ctx context.Context, ic *installconfig.InstallConfig, machinePool *
nodeArch := machinePool.Architecture
archName := arch.RpmArch(string(nodeArch))

st, err := rhcos.FetchCoreOSBuild(ctx)
st, err := rhcos.FetchCoreOSBuild(ctx, ic.Config.OSImageStream)
if err != nil {
return "", err
}
Expand All @@ -103,7 +103,7 @@ func osImage(ctx context.Context, ic *installconfig.InstallConfig, machinePool *
switch platform.Name() {
case aws.Name:
region := platform.AWS.Region
if !rhcos.AMIRegions(nodeArch).Has(region) {
if !rhcos.AMIRegions(nodeArch, ic.Config.OSImageStream).Has(region) {
const globalResourceRegion = "us-east-1"
logrus.Debugf("No AMI found in %s. Using AMI from %s.", region, globalResourceRegion)
region = globalResourceRegion
Expand Down
4 changes: 3 additions & 1 deletion pkg/asset/rhcos/iso.go
Original file line number Diff line number Diff line change
Expand Up @@ -30,7 +30,9 @@ type BaseIso struct {
type CoreOSBuildFetcher func(ctx context.Context) (*stream.Stream, error)

// defaultCoreOSStreamGetter uses the pinned metadata.
var defaultCoreOSStreamGetter = rhcos.FetchCoreOSBuild
var defaultCoreOSStreamGetter = func(ctx context.Context) (*stream.Stream, error) {
return rhcos.FetchCoreOSBuild(ctx, rhcos.DefaultOSImageStream)
}

// NewBaseISOFetcher returns a struct that can be used to fetch a base ISO using
// the default method.
Expand Down
Loading