Skip to content

Commit 95685e0

Browse files
Merge pull request #1231 from wking/log-release-metadata-parsing
OTA-1627: pkg/cincinnati: Centralize release metadata parsing
2 parents 2f5d502 + 9186228 commit 95685e0

File tree

4 files changed

+85
-61
lines changed

4 files changed

+85
-61
lines changed

pkg/cincinnati/cincinnati.go

Lines changed: 60 additions & 22 deletions
Original file line numberDiff line numberDiff line change
@@ -3,6 +3,7 @@ package cincinnati
33
import (
44
"context"
55
"encoding/json"
6+
"errors"
67
"fmt"
78
"io"
89
"net/http"
@@ -289,9 +290,9 @@ type graph struct {
289290
}
290291

291292
type node struct {
292-
Version semver.Version `json:"version"`
293-
Image string `json:"payload"`
294-
Metadata map[string]string `json:"metadata,omitempty"`
293+
Version semver.Version `json:"version"`
294+
Image string `json:"payload"`
295+
Metadata map[string]interface{} `json:"metadata,omitempty"`
295296
}
296297

297298
type edge struct {
@@ -329,28 +330,65 @@ func (e *edge) UnmarshalJSON(data []byte) error {
329330
}
330331

331332
func convertRetrievedUpdateToRelease(update node) (configv1.Release, error) {
332-
cvoUpdate := configv1.Release{
333-
Version: update.Version.String(),
334-
Image: update.Image,
335-
}
336-
if urlString, ok := update.Metadata["url"]; ok {
337-
_, err := url.Parse(urlString)
338-
if err != nil {
339-
return cvoUpdate, fmt.Errorf("invalid URL for %s: %s", cvoUpdate.Version, err)
333+
release, err := ParseMetadata(update.Metadata)
334+
release.Version = update.Version.String()
335+
release.Image = update.Image
336+
return release, err
337+
}
338+
339+
// ParseMetadata parses release metadata (URL, channels, etc.). It does not populate the version or image properties.
340+
func ParseMetadata(metadata map[string]interface{}) (configv1.Release, error) {
341+
release := configv1.Release{}
342+
errs := []error{}
343+
if urlInterface, hasURL := metadata["url"]; hasURL {
344+
if urlString, isString := urlInterface.(string); isString {
345+
if _, err := url.Parse(urlString); err == nil {
346+
release.URL = configv1.URL(urlString)
347+
} else {
348+
errs = append(errs, fmt.Errorf("invalid release URL: %s", err))
349+
}
350+
} else {
351+
errs = append(errs, fmt.Errorf("URL is not a string: %v", urlInterface))
340352
}
341-
cvoUpdate.URL = configv1.URL(urlString)
342353
}
343-
if arch, ok := update.Metadata["release.openshift.io/architecture"]; ok {
344-
switch arch {
345-
case "multi":
346-
cvoUpdate.Architecture = configv1.ClusterVersionArchitectureMulti
347-
default:
348-
klog.Warningf("Unrecognized release.openshift.io/architecture value %q", arch)
354+
if archInterface, hasArch := metadata["release.openshift.io/architecture"]; hasArch {
355+
if arch, isString := archInterface.(string); isString {
356+
if arch == "multi" {
357+
release.Architecture = configv1.ClusterVersionArchitectureMulti
358+
} else {
359+
errs = append(errs, fmt.Errorf("unrecognized release.openshift.io/architecture value %q", arch))
360+
}
361+
} else {
362+
errs = append(errs, fmt.Errorf("release.openshift.io/architecture is not a string: %v", archInterface))
349363
}
350364
}
351-
if channels, ok := update.Metadata["io.openshift.upgrades.graph.release.channels"]; ok {
352-
cvoUpdate.Channels = strings.Split(channels, ",")
353-
sort.Strings(cvoUpdate.Channels)
365+
if channelsInterface, hasChannels := metadata["io.openshift.upgrades.graph.release.channels"]; hasChannels {
366+
if channelsString, isString := channelsInterface.(string); isString {
367+
if len(channelsString) == 0 {
368+
errs = append(errs, fmt.Errorf("io.openshift.upgrades.graph.release.channels is an empty string"))
369+
} else {
370+
channels := strings.Split(channelsString, ",")
371+
if len(channels) == 0 {
372+
errs = append(errs, fmt.Errorf("no comma-delimited channels in io.openshift.upgrades.graph.release.channels %q", channelsString))
373+
} else {
374+
for i := len(channels) - 1; i >= 0; i-- {
375+
if len(channels[i]) == 0 {
376+
errs = append(errs, fmt.Errorf("io.openshift.upgrades.graph.release.channels entry %d is an empty string: %q", i, channelsString))
377+
channels = append(channels[:i], channels[i+1:]...)
378+
}
379+
}
380+
if len(channels) == 0 {
381+
errs = append(errs, fmt.Errorf("no non-empty channels in io.openshift.upgrades.graph.release.channels %q", channels))
382+
} else {
383+
sort.Strings(channels)
384+
release.Channels = channels
385+
}
386+
}
387+
}
388+
} else {
389+
errs = append(errs, fmt.Errorf("io.openshift.upgrades.graph.release.channels is not a string: %v", channelsInterface))
390+
}
354391
}
355-
return cvoUpdate, nil
392+
klog.V(2).Infof("parsed metadata: URL %q, architecture %q, channels %v, errors %v", release.URL, release.Architecture, release.Channels, errs)
393+
return release, errors.Join(errs...)
356394
}

pkg/cincinnati/cincinnati_test.go

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -816,7 +816,7 @@ func Test_nodeUnmarshalJSON(t *testing.T) {
816816
exp: node{
817817
Version: semver.MustParse("4.0.0-5"),
818818
Image: "quay.io/openshift-release-dev/ocp-release:4.0.0-5",
819-
Metadata: map[string]string{},
819+
Metadata: map[string]interface{}{},
820820
},
821821
}, {
822822
raw: []byte(`{
@@ -829,7 +829,7 @@ func Test_nodeUnmarshalJSON(t *testing.T) {
829829
exp: node{
830830
Version: semver.MustParse("4.0.0-0.1"),
831831
Image: "quay.io/openshift-release-dev/ocp-release:4.0.0-0.1",
832-
Metadata: map[string]string{
832+
Metadata: map[string]interface{}{
833833
"description": "This is the beta1 image based on the 4.0.0-0.nightly-2019-01-15-010905 build",
834834
},
835835
},

pkg/cvo/cvo.go

Lines changed: 18 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -918,7 +918,24 @@ func mergeReleaseMetadata(release configv1.Release, getAvailableUpdates func() *
918918
}
919919
}
920920
}
921-
if update != nil {
921+
if update == nil {
922+
var versionMatch *configv1.Release
923+
if availableUpdates.Current.Version == merged.Version {
924+
versionMatch = &availableUpdates.Current
925+
} else {
926+
for i, u := range availableUpdates.Updates {
927+
if u.Version == merged.Version {
928+
versionMatch = &availableUpdates.Updates[i]
929+
break
930+
}
931+
}
932+
}
933+
if versionMatch == nil {
934+
klog.V(2).Infof("No available update found matching the digest of %q or the version %q", merged.Image, merged.Version)
935+
} else {
936+
klog.V(2).Infof("No available update found matching the digest of %q, although there was a match for version %q with a different tag or digest %q", merged.Image, merged.Version, versionMatch.Image)
937+
}
938+
} else {
922939
if merged.Version == "" {
923940
merged.Version = update.Version
924941
}

pkg/payload/payload.go

Lines changed: 5 additions & 36 deletions
Original file line numberDiff line numberDiff line change
@@ -10,8 +10,6 @@ import (
1010
"os"
1111
"path/filepath"
1212
"runtime"
13-
"sort"
14-
"strings"
1513
"time"
1614

1715
"k8s.io/klog/v2"
@@ -27,6 +25,7 @@ import (
2725
"github.com/openshift/cluster-version-operator/lib/capability"
2826
localmanifest "github.com/openshift/cluster-version-operator/lib/manifest"
2927
"github.com/openshift/cluster-version-operator/lib/resourceread"
28+
"github.com/openshift/cluster-version-operator/pkg/cincinnati"
3029
)
3130

3231
// State describes the state of the payload and alters
@@ -68,9 +67,6 @@ const (
6867
// provide better visibility during install and upgrade of
6968
// error conditions.
7069
PrecreatingPayload
71-
72-
// releaseMultiArchID identifies a multi architecture release.
73-
releaseMultiArchID = "multi"
7470
)
7571

7672
// Initializing is true if the state is InitializingPayload.
@@ -368,39 +364,12 @@ func loadReleaseMetadata(releaseDir string) (configv1.Release, error) {
368364
return release, fmt.Errorf("Cincinnati metadata version %q is not a valid semantic version: %v", metadata.Version, err)
369365
}
370366

371-
release.Version = metadata.Version
372-
373-
if archRaw, hasArch := metadata.Metadata["release.openshift.io/architecture"]; hasArch {
374-
arch, isString := archRaw.(string)
375-
if !isString {
376-
return release, fmt.Errorf("Architecture from %s (%s) is not a string: %v",
377-
cincinnatiJSONFile, release.Version, archRaw)
378-
}
379-
380-
if arch != releaseMultiArchID {
381-
return release, fmt.Errorf("Architecture from %s (%s) contains invalid value: %q. Valid value is %q.",
382-
cincinnatiJSONFile, release.Version, arch, releaseMultiArchID)
383-
}
384-
385-
release.Architecture = configv1.ClusterVersionArchitectureMulti
386-
klog.V(2).Infof("Architecture from %s (%s) is multi: %q", cincinnatiJSONFile, release.Version, arch)
367+
release, err = cincinnati.ParseMetadata(metadata.Metadata)
368+
if err != nil {
369+
klog.Warningf("errors while parsing metadata from %s (%s): %v", cincinnatiJSONFile, release.Version, err)
387370
}
388371

389-
if urlInterface, ok := metadata.Metadata["url"]; ok {
390-
if urlString, ok := urlInterface.(string); ok {
391-
release.URL = configv1.URL(urlString)
392-
} else {
393-
klog.Warningf("URL from %s (%s) is not a string: %v", cincinnatiJSONFile, release.Version, urlInterface)
394-
}
395-
}
396-
if channelsInterface, ok := metadata.Metadata["io.openshift.upgrades.graph.release.channels"]; ok {
397-
if channelsString, ok := channelsInterface.(string); ok {
398-
release.Channels = strings.Split(channelsString, ",")
399-
sort.Strings(release.Channels)
400-
} else {
401-
klog.Warningf("channel list from %s (%s) is not a string: %v", cincinnatiJSONFile, release.Version, channelsInterface)
402-
}
403-
}
372+
release.Version = metadata.Version
404373

405374
return release, nil
406375
}

0 commit comments

Comments
 (0)