From bb86dfc766ab4301f03a41fcefb07b3dbde9756d Mon Sep 17 00:00:00 2001 From: Stephen Benjamin Date: Tue, 31 Mar 2026 08:29:35 -0400 Subject: [PATCH 1/2] Add node image RPM diff to release info API response The API was missing the node image RPM diff data that the HTML page displays. This adds a nodeImageRpmDiff field to the APIReleaseInfo struct and fetches it concurrently alongside the changelog generation. Co-Authored-By: Claude Opus 4.6 --- cmd/release-controller-api/http.go | 28 +++++++++++++++++++++------- pkg/release-controller/types.go | 2 ++ 2 files changed, 23 insertions(+), 7 deletions(-) diff --git a/cmd/release-controller-api/http.go b/cmd/release-controller-api/http.go index 6ee33fd2b..df40e9943 100644 --- a/cmd/release-controller-api/http.go +++ b/cmd/release-controller-api/http.go @@ -668,6 +668,7 @@ func (c *Controller) apiReleaseInfo(w http.ResponseWriter, req *http.Request) { var changeLog []byte var changeLogJson releasecontroller.ChangeLog + var rpmDiff *releasecontroller.RpmDiff if tagInfo.Info.Previous != nil && len(tagInfo.PreviousTagPullSpec) > 0 && len(tagInfo.TagPullSpec) > 0 { var wg sync.WaitGroup @@ -686,6 +687,18 @@ func (c *Controller) apiReleaseInfo(w http.ResponseWriter, req *http.Request) { c.changeLogWorker(result, tagInfo, format) }() } + + wg.Add(1) + go func() { + defer wg.Done() + diff, err := c.releaseInfo.RpmDiff(tagInfo.PreviousTagPullSpec, tagInfo.TagPullSpec) + if err != nil { + klog.V(4).Infof("Unable to retrieve RPM diff for %s: %v", tagInfo.Tag, err) + return + } + rpmDiff = &diff + }() + wg.Wait() if renderHTML.err == nil { @@ -706,13 +719,14 @@ func (c *Controller) apiReleaseInfo(w http.ResponseWriter, req *http.Request) { } summary := releasecontroller.APIReleaseInfo{ - Name: tagInfo.Tag, - Phase: tagInfo.Info.Tag.Annotations[releasecontroller.ReleaseAnnotationPhase], - Results: verificationJobs, - UpgradesTo: c.graph.UpgradesTo(tagInfo.Tag), - UpgradesFrom: c.graph.UpgradesFrom(tagInfo.Tag), - ChangeLog: changeLog, - ChangeLogJson: changeLogJson, + Name: tagInfo.Tag, + Phase: tagInfo.Info.Tag.Annotations[releasecontroller.ReleaseAnnotationPhase], + Results: verificationJobs, + UpgradesTo: c.graph.UpgradesTo(tagInfo.Tag), + UpgradesFrom: c.graph.UpgradesFrom(tagInfo.Tag), + ChangeLog: changeLog, + ChangeLogJson: changeLogJson, + NodeImageRpmDiff: rpmDiff, } data, err := json.MarshalIndent(&summary, "", " ") diff --git a/pkg/release-controller/types.go b/pkg/release-controller/types.go index 771ff0828..77ca83689 100644 --- a/pkg/release-controller/types.go +++ b/pkg/release-controller/types.go @@ -54,6 +54,8 @@ type APIReleaseInfo struct { ChangeLog []byte `json:"changeLog,omitempty"` //ChangeLogJson is the json representation of the changes included in this release tag ChangeLogJson ChangeLog `json:"changeLogJson,omitempty"` + // NodeImageRpmDiff is the RPM package diff between the previous and current node images + NodeImageRpmDiff *RpmDiff `json:"nodeImageRpmDiff,omitempty"` } // Release holds information about the release used during processing. From b84c3c76d54cd9fef7bdefa30b13d61d16ab0db0 Mon Sep 17 00:00:00 2001 From: Stephen Benjamin Date: Fri, 17 Apr 2026 10:01:27 -0400 Subject: [PATCH 2/2] Add multi-stream support to release info API node image RPM diffs Replace the single nodeImageRpmDiff field with a nodeImageStreams array that returns per-stream RPM diffs (e.g. rhel-coreos and rhel-coreos-10 separately). For older payloads without multiple streams, falls back to the legacy single RpmDiff wrapped in a one-element array. Co-Authored-By: Claude Opus 4.6 --- cmd/release-controller-api/http.go | 54 +++++++++++++++++++++++------- pkg/release-controller/types.go | 14 ++++++-- 2 files changed, 54 insertions(+), 14 deletions(-) diff --git a/cmd/release-controller-api/http.go b/cmd/release-controller-api/http.go index df40e9943..a4ce29474 100644 --- a/cmd/release-controller-api/http.go +++ b/cmd/release-controller-api/http.go @@ -668,7 +668,7 @@ func (c *Controller) apiReleaseInfo(w http.ResponseWriter, req *http.Request) { var changeLog []byte var changeLogJson releasecontroller.ChangeLog - var rpmDiff *releasecontroller.RpmDiff + var nodeImageStreams []releasecontroller.APINodeImageStream if tagInfo.Info.Previous != nil && len(tagInfo.PreviousTagPullSpec) > 0 && len(tagInfo.TagPullSpec) > 0 { var wg sync.WaitGroup @@ -691,12 +691,42 @@ func (c *Controller) apiReleaseInfo(w http.ResponseWriter, req *http.Request) { wg.Add(1) go func() { defer wg.Done() - diff, err := c.releaseInfo.RpmDiff(tagInfo.PreviousTagPullSpec, tagInfo.TagPullSpec) + streams, err := c.releaseInfo.ListMachineOSStreams(tagInfo.TagPullSpec) if err != nil { - klog.V(4).Infof("Unable to retrieve RPM diff for %s: %v", tagInfo.Tag, err) + klog.V(4).Infof("Unable to list machine-OS streams for %s: %v", tagInfo.Tag, err) + } + + if len(streams) == 0 { + diff, err := c.releaseInfo.RpmDiff(tagInfo.PreviousTagPullSpec, tagInfo.TagPullSpec) + if err != nil { + klog.V(4).Infof("Unable to retrieve RPM diff for %s: %v", tagInfo.Tag, err) + return + } + nodeImageStreams = []releasecontroller.APINodeImageStream{{ + Name: "Red Hat Enterprise Linux CoreOS", + Tag: "rhel-coreos", + RpmDiff: &diff, + }} return } - rpmDiff = &diff + + result := make([]releasecontroller.APINodeImageStream, 0, len(streams)) + for _, stream := range streams { + entry := releasecontroller.APINodeImageStream{ + Name: stream.DisplayName, + Tag: stream.Tag, + } + if _, errFrom := c.releaseInfo.ImageReferenceForComponent(tagInfo.PreviousTagPullSpec, stream.Tag); errFrom == nil { + diff, err := c.releaseInfo.RpmDiffForStream(tagInfo.PreviousTagPullSpec, tagInfo.TagPullSpec, stream.Tag) + if err != nil { + klog.V(4).Infof("Unable to retrieve RPM diff for stream %s in %s: %v", stream.Tag, tagInfo.Tag, err) + } else { + entry.RpmDiff = &diff + } + } + result = append(result, entry) + } + nodeImageStreams = result }() wg.Wait() @@ -719,14 +749,14 @@ func (c *Controller) apiReleaseInfo(w http.ResponseWriter, req *http.Request) { } summary := releasecontroller.APIReleaseInfo{ - Name: tagInfo.Tag, - Phase: tagInfo.Info.Tag.Annotations[releasecontroller.ReleaseAnnotationPhase], - Results: verificationJobs, - UpgradesTo: c.graph.UpgradesTo(tagInfo.Tag), - UpgradesFrom: c.graph.UpgradesFrom(tagInfo.Tag), - ChangeLog: changeLog, - ChangeLogJson: changeLogJson, - NodeImageRpmDiff: rpmDiff, + Name: tagInfo.Tag, + Phase: tagInfo.Info.Tag.Annotations[releasecontroller.ReleaseAnnotationPhase], + Results: verificationJobs, + UpgradesTo: c.graph.UpgradesTo(tagInfo.Tag), + UpgradesFrom: c.graph.UpgradesFrom(tagInfo.Tag), + ChangeLog: changeLog, + ChangeLogJson: changeLogJson, + NodeImageStreams: nodeImageStreams, } data, err := json.MarshalIndent(&summary, "", " ") diff --git a/pkg/release-controller/types.go b/pkg/release-controller/types.go index 77ca83689..db380ec54 100644 --- a/pkg/release-controller/types.go +++ b/pkg/release-controller/types.go @@ -54,8 +54,18 @@ type APIReleaseInfo struct { ChangeLog []byte `json:"changeLog,omitempty"` //ChangeLogJson is the json representation of the changes included in this release tag ChangeLogJson ChangeLog `json:"changeLogJson,omitempty"` - // NodeImageRpmDiff is the RPM package diff between the previous and current node images - NodeImageRpmDiff *RpmDiff `json:"nodeImageRpmDiff,omitempty"` + // NodeImageStreams contains per-stream RPM diffs for each machine-OS image (e.g. rhel-coreos, rhel-coreos-10). + NodeImageStreams []APINodeImageStream `json:"nodeImageStreams,omitempty"` +} + +// APINodeImageStream holds RPM diff information for a single machine-OS stream in a release payload. +type APINodeImageStream struct { + // Name is the human-readable display name (e.g. "Red Hat Enterprise Linux CoreOS"). + Name string `json:"name"` + // Tag is the payload component tag (e.g. "rhel-coreos", "rhel-coreos-10"). + Tag string `json:"tag"` + // RpmDiff is the RPM package diff between previous and current releases for this stream. + RpmDiff *RpmDiff `json:"rpmDiff,omitempty"` } // Release holds information about the release used during processing.