diff --git a/data/data/coreos/marketplace/coreos-rhel-10.json b/data/data/coreos/marketplace/coreos-rhel-10.json new file mode 100644 index 00000000000..aa6ca21ce1f --- /dev/null +++ b/data/data/coreos/marketplace/coreos-rhel-10.json @@ -0,0 +1,116 @@ +{ + "aarch64": { + "azure": { + "no-purchase-plan": { + "hyperVGen2": { + "publisher": "azureopenshift", + "offer": "aro4", + "sku": "aro_422-arm", + "version": "9.8.20260428" + } + } + } + }, + "x86_64": { + "azure": { + "no-purchase-plan": { + "hyperVGen1": { + "publisher": "azureopenshift", + "offer": "aro4", + "sku": "aro_422", + "version": "9.8.20260428" + }, + "hyperVGen2": { + "publisher": "azureopenshift", + "offer": "aro4", + "sku": "aro_422-v2", + "version": "9.8.20260428" + } + }, + "ocp": { + "hyperVGen1": { + "publisher": "redhat", + "offer": "rh-ocp-worker", + "sku": "rh-ocp-worker-gen1", + "version": "9.6.2026030314" + }, + "hyperVGen2": { + "publisher": "redhat", + "offer": "rh-ocp-worker", + "sku": "rh-ocp-worker", + "version": "9.6.2026030314" + } + }, + "opp": { + "hyperVGen1": { + "publisher": "redhat", + "offer": "rh-opp-worker", + "sku": "rh-opp-worker-gen1", + "version": "9.6.2026030314" + }, + "hyperVGen2": { + "publisher": "redhat", + "offer": "rh-opp-worker", + "sku": "rh-opp-worker", + "version": "9.6.2026030314" + } + }, + "oke": { + "hyperVGen1": { + "publisher": "redhat", + "offer": "rh-oke-worker", + "sku": "rh-oke-worker-gen1", + "version": "9.6.2026030314" + }, + "hyperVGen2": { + "publisher": "redhat", + "offer": "rh-oke-worker", + "sku": "rh-oke-worker", + "version": "9.6.2026030314" + } + }, + "ocp-emea": { + "hyperVGen1": { + "publisher": "redhat-limited", + "offer": "rh-ocp-worker", + "sku": "rh-ocp-worker-gen1", + "version": "4.18.2026012111" + }, + "hyperVGen2": { + "publisher": "redhat-limited", + "offer": "rh-ocp-worker", + "sku": "rh-ocp-worker", + "version": "4.18.2026012111" + } + }, + "opp-emea": { + "hyperVGen1": { + "publisher": "redhat-limited", + "offer": "rh-opp-worker", + "sku": "rh-opp-worker-gen1", + "version": "4.18.2026012111" + }, + "hyperVGen2": { + "publisher": "redhat-limited", + "offer": "rh-opp-worker", + "sku": "rh-opp-worker", + "version": "4.18.2026012111" + } + }, + "oke-emea": { + "hyperVGen1": { + "publisher": "redhat-limited", + "offer": "rh-oke-worker", + "sku": "rh-oke-worker-gen1", + "version": "4.18.2026012111" + }, + "hyperVGen2": { + "publisher": "redhat-limited", + "offer": "rh-oke-worker", + "sku": "rh-oke-worker", + "version": "4.18.2026012111" + } + } + } + } +} diff --git a/data/data/coreos/marketplace/coreos-rhel-9.json b/data/data/coreos/marketplace/coreos-rhel-9.json index dde107a847b..aa6ca21ce1f 100644 --- a/data/data/coreos/marketplace/coreos-rhel-9.json +++ b/data/data/coreos/marketplace/coreos-rhel-9.json @@ -5,8 +5,8 @@ "hyperVGen2": { "publisher": "azureopenshift", "offer": "aro4", - "sku": "420-arm", - "version": "9.6.20251015" + "sku": "aro_422-arm", + "version": "9.8.20260428" } } } @@ -17,14 +17,14 @@ "hyperVGen1": { "publisher": "azureopenshift", "offer": "aro4", - "sku": "aro_420", - "version": "9.6.20251015" + "sku": "aro_422", + "version": "9.8.20260428" }, "hyperVGen2": { "publisher": "azureopenshift", "offer": "aro4", - "sku": "420-v2", - "version": "9.6.20251015" + "sku": "aro_422-v2", + "version": "9.8.20260428" } }, "ocp": { @@ -32,13 +32,13 @@ "publisher": "redhat", "offer": "rh-ocp-worker", "sku": "rh-ocp-worker-gen1", - "version": "4.18.2025031114" + "version": "9.6.2026030314" }, "hyperVGen2": { "publisher": "redhat", "offer": "rh-ocp-worker", "sku": "rh-ocp-worker", - "version": "4.18.2025031114" + "version": "9.6.2026030314" } }, "opp": { @@ -46,13 +46,13 @@ "publisher": "redhat", "offer": "rh-opp-worker", "sku": "rh-opp-worker-gen1", - "version": "4.18.2025031114" + "version": "9.6.2026030314" }, "hyperVGen2": { "publisher": "redhat", "offer": "rh-opp-worker", "sku": "rh-opp-worker", - "version": "4.18.2025031114" + "version": "9.6.2026030314" } }, "oke": { @@ -60,13 +60,13 @@ "publisher": "redhat", "offer": "rh-oke-worker", "sku": "rh-oke-worker-gen1", - "version": "4.18.2025031114" + "version": "9.6.2026030314" }, "hyperVGen2": { "publisher": "redhat", "offer": "rh-oke-worker", "sku": "rh-oke-worker", - "version": "4.18.2025031114" + "version": "9.6.2026030314" } }, "ocp-emea": { @@ -74,13 +74,13 @@ "publisher": "redhat-limited", "offer": "rh-ocp-worker", "sku": "rh-ocp-worker-gen1", - "version": "4.18.2025031114" + "version": "4.18.2026012111" }, "hyperVGen2": { "publisher": "redhat-limited", "offer": "rh-ocp-worker", "sku": "rh-ocp-worker", - "version": "4.18.2025031114" + "version": "4.18.2026012111" } }, "opp-emea": { @@ -88,13 +88,13 @@ "publisher": "redhat-limited", "offer": "rh-opp-worker", "sku": "rh-opp-worker-gen1", - "version": "4.18.2025031114" + "version": "4.18.2026012111" }, "hyperVGen2": { "publisher": "redhat-limited", "offer": "rh-opp-worker", "sku": "rh-opp-worker", - "version": "4.18.2025031114" + "version": "4.18.2026012111" } }, "oke-emea": { @@ -102,13 +102,13 @@ "publisher": "redhat-limited", "offer": "rh-oke-worker", "sku": "rh-oke-worker-gen1", - "version": "4.18.2025031114" + "version": "4.18.2026012111" }, "hyperVGen2": { "publisher": "redhat-limited", "offer": "rh-oke-worker", "sku": "rh-oke-worker", - "version": "4.18.2025031114" + "version": "4.18.2026012111" } } } diff --git a/hack/rhcos/populate-marketplace-imagestream.go b/hack/rhcos/populate-marketplace-imagestream.go index 86ade34b707..0b9800441dc 100644 --- a/hack/rhcos/populate-marketplace-imagestream.go +++ b/hack/rhcos/populate-marketplace-imagestream.go @@ -15,41 +15,65 @@ import ( ) const ( - streamRHCOSJSON = "data/data/coreos/coreos-rhel-9.json" - streamMarketplaceRHCOSJSON = "data/data/coreos/marketplace-coreos-rhel-9.json" - x86 = "x86_64" arm64 = "aarch64" ) +type streamConfig struct { + name string + inputFile string + outputFile string +} + +var ( + streamRHEL9 = streamConfig{ + name: "rhel-9", + inputFile: "data/data/coreos/coreos-rhel-9.json", + outputFile: "data/data/coreos/marketplace/coreos-rhel-9.json", + } + streamRHEL10 = streamConfig{ + name: "rhel-10", + inputFile: "data/data/coreos/coreos-rhel-10.json", + outputFile: "data/data/coreos/marketplace/coreos-rhel-10.json", + } +) + // arch -> marketplace type marketplaceStream map[string]*rhcos.Marketplace func main() { ctx := context.Background() - stream := marketplaceStream{} - if err := stream.populate(ctx); err != nil { - log.Fatalln("Failed to populate marketplace stream:", err) + rhel9 := marketplaceStream{} + if err := rhel9.populate(ctx, streamRHEL9); err != nil { + log.Fatalln("Failed to populate RHEL 9 marketplace stream:", err) + } + if err := rhel9.write(streamRHEL9); err != nil { + log.Fatalln("Failed to write RHEL 9 marketplace stream:", err) } + log.Printf("Successfully wrote marketplace stream to %s", streamRHEL9.outputFile) - if err := stream.write(); err != nil { - log.Fatalln("Failed to write marketplace stream:", err) + rhel10 := marketplaceStream{} + if err := rhel10.populateWithFallback(ctx, streamRHEL10, rhel9); err != nil { + log.Fatalln("Failed to populate RHEL 10 marketplace stream:", err) } - log.Printf("Successfully wrote marketplace stream to %s", streamMarketplaceRHCOSJSON) + if err := rhel10.write(streamRHEL10); err != nil { + log.Fatalln("Failed to write RHEL 10 marketplace stream:", err) + } + log.Printf("Successfully wrote marketplace stream to %s", streamRHEL10.outputFile) } // populate gathers the marketplace images for each cloud // and adds them to the marketplace stream data structure. -func (s marketplaceStream) populate(ctx context.Context) error { - clouds := []func(ctx context.Context, arch string) error{ +func (s marketplaceStream) populate(ctx context.Context, cfg streamConfig) error { + clouds := []func(ctx context.Context, arch string, cfg streamConfig) error{ s.azure, } for _, supportedArch := range []string{arm64, x86} { s[supportedArch] = &rhcos.Marketplace{} for _, populateCloud := range clouds { - if err := populateCloud(ctx, supportedArch); err != nil { + if err := populateCloud(ctx, supportedArch, cfg); err != nil { return err } } @@ -57,31 +81,46 @@ func (s marketplaceStream) populate(ctx context.Context) error { return nil } +// populateWithFallback attempts to populate marketplace data for each architecture. +// Any individual image types not found are filled in from the fallback stream. +func (s marketplaceStream) populateWithFallback(ctx context.Context, cfg streamConfig, fallback marketplaceStream) error { + for _, supportedArch := range []string{arm64, x86} { + s[supportedArch] = &rhcos.Marketplace{} + + if err := s.azure(ctx, supportedArch, cfg); err != nil { + return err + } + + if fb, ok := fallback[supportedArch]; ok && fb != nil { + azure.FillMissing(s[supportedArch].Azure, fb.Azure) + } + } + return nil +} + // write serializes the marketplace stream to disk. -func (s marketplaceStream) write() error { +func (s marketplaceStream) write(cfg streamConfig) error { contents, err := json.MarshalIndent(s, "", " ") if err != nil { return fmt.Errorf("error marshaling stream: %w", err) } - // TODO(padillon): dumb question time, git is still complaining \ No newline at end of file - // what am I doing wrong? contents = append(contents, []byte("\n")...) - if err := os.WriteFile(streamMarketplaceRHCOSJSON, contents, 0644); err != nil { + if err := os.WriteFile(cfg.outputFile, contents, 0644); err != nil { return fmt.Errorf("error writing stream: %w", err) } return nil } -func (s marketplaceStream) azure(ctx context.Context, arch string) error { +func (s marketplaceStream) azure(ctx context.Context, arch string, cfg streamConfig) error { var err error s[arch].Azure = &rhcos.AzureMarketplace{} - rel, err := getReleaseFromStream() + rel, err := getReleaseFromStream(cfg) if err != nil { - return fmt.Errorf("failed to get release from rhcos stream: %w", err) + return fmt.Errorf("failed to get release from %s rhcos stream: %w", cfg.name, err) } azClient, err := azure.NewStreamClient() @@ -96,13 +135,13 @@ func (s marketplaceStream) azure(ctx context.Context, arch string) error { return nil } -// getXYFromStream obtains the X.Y version from rhcos.json. -func getReleaseFromStream() (string, error) { +// getReleaseFromStream obtains the X.Y version from rhcos.json. +func getReleaseFromStream(cfg streamConfig) (string, error) { if rel, ok := os.LookupEnv("STREAM_RELEASE_OVERRIDE"); ok { log.Printf("Found STREAM_RELEASE_OVERRIDE %s", rel) return rel, nil } - fileContents, err := os.ReadFile(streamRHCOSJSON) + fileContents, err := os.ReadFile(cfg.inputFile) if err != nil { return "", err } @@ -112,5 +151,9 @@ func getReleaseFromStream() (string, error) { return "", fmt.Errorf("failed to unmarshal RHCOS stream: %w", err) } - return strings.Split(st.Stream, "-")[1], nil + parts := strings.Split(st.Stream, "-") + if len(parts) < 2 { + return "", fmt.Errorf("unexpected stream format in %s: %q", cfg.inputFile, st.Stream) + } + return parts[1], nil } diff --git a/pkg/rhcos/marketplace/azure/azure.go b/pkg/rhcos/marketplace/azure/azure.go index a7f56582477..cf34f98abb9 100644 --- a/pkg/rhcos/marketplace/azure/azure.go +++ b/pkg/rhcos/marketplace/azure/azure.go @@ -191,6 +191,11 @@ func (az *MarketplaceStream) getImage(ctx context.Context, pub, offer, sku, xyVe } } + if foundVersion == "" { + logrus.Infof("No matching version found for publisher: %s, offer: %s, sku: %s, architecture: %s in release %s", pub, offer, sku, arch, xyVersion) + return nil, nil + } + // Now that we've found the version, check the architecture and the plan. img, err := az.client.Get(ctx, region, pub, offer, sku, foundVersion, nil) if err != nil { @@ -221,20 +226,21 @@ func (az *MarketplaceStream) getImage(ctx context.Context, pub, offer, sku, xyVe // parseARO takes the release from coreos stream and // uses conventions to generate the SKU (gen1 & gen2) and version. -// For instance, with a coreos release of "4.19" -// gen1SKU: "aro_418" -// gen2SKU: "418-v2" -// version: "418.94.20241009" (removes timestamp & build number) +// For instance, with a coreos release of "4.22": +// +// gen1SKU: "aro_422" +// gen2SKU: "aro_422-v2" +// armSKU: "aro_422-arm" func parseAROSKUs(release, arch string) (string, string) { xyVersion := strings.ReplaceAll(release, ".", "") var gen1SKU, gen2SKU string switch arch { case x86: gen1SKU = fmt.Sprintf("aro_%s", xyVersion) - gen2SKU = fmt.Sprintf("%s-v2", xyVersion) + gen2SKU = fmt.Sprintf("aro_%s-v2", xyVersion) case arm64: gen1SKU = "" - gen2SKU = fmt.Sprintf("%s-arm", xyVersion) + gen2SKU = fmt.Sprintf("aro_%s-arm", xyVersion) } return gen1SKU, gen2SKU } @@ -273,16 +279,64 @@ func convertToSemver(ver string) string { return "" } +// FillMissing copies any nil image types from fallback into target. +func FillMissing(target, fallback *rhcos.AzureMarketplace) { + if target == nil || fallback == nil { + return + } + if target.NoPurchasePlan == nil { + target.NoPurchasePlan = fallback.NoPurchasePlan + } + if target.OCP == nil { + target.OCP = fallback.OCP + } + if target.OPP == nil { + target.OPP = fallback.OPP + } + if target.OKE == nil { + target.OKE = fallback.OKE + } + if target.OCPEMEA == nil { + target.OCPEMEA = fallback.OCPEMEA + } + if target.OPPEMEA == nil { + target.OPPEMEA = fallback.OPPEMEA + } + if target.OKEEMEA == nil { + target.OKEEMEA = fallback.OKEEMEA + } +} + func checkIfNewer(candidate, release string) bool { - img, err := strconv.Atoi(strings.Split(semver.MajorMinor(candidate), ".")[1]) + mm := semver.MajorMinor(candidate) + imgMajor, err := strconv.Atoi(strings.TrimPrefix(strings.Split(mm, ".")[0], "v")) + if err != nil { + logrus.Infof("Error converting major version to int with version %s", candidate) + return true + } + // Images using RHEL version numbering (9.x, 10.x) are not comparable + // to OCP release versions and should never be filtered out. + if imgMajor == 9 || imgMajor == 10 { + return false + } + imgMinor, err := strconv.Atoi(strings.Split(mm, ".")[1]) if err != nil { logrus.Infof("Error converting minor version to int with version %s", candidate) return true } - rel, err := strconv.Atoi(strings.Split(release, ".")[1]) + relParts := strings.Split(release, ".") + relMajor, err := strconv.Atoi(relParts[0]) + if err != nil { + logrus.Infof("Error converting major version to int with version %s", release) + return true + } + relMinor, err := strconv.Atoi(relParts[1]) if err != nil { logrus.Infof("Error converting minor version to int with version %s", release) return true } - return img > rel + if imgMajor != relMajor { + return imgMajor > relMajor + } + return imgMinor > relMinor }