Skip to content

Commit 28b3b40

Browse files
committed
pkg: Propagate Release.Architecture
Pass the new property around in all clusters, but only propagate it out to 'status' in the in-cluster ClusterVersion when the relevant feature gate is enabled. I'm dropping the "report an error condition when architecture isn't set" test-case, because now that a non-Multi Operator.release is always defaulted to runtime.GOARCH in syncAvailableUpdates, we no longer have a way to trigger architecture-less Cincinnati requests. Setting 'Architecture: runtime.GOARCH' in TestOperator_availableUpdatesSync/if_last_successful_check_time_was_too_recent,_do_nothing avoids triggering a new request because the CVO thinks the architecture has changed. Without that pivot: $ go test -run TestOperator_availableUpdatesSync/if_last_successful_check_time_was_too_recent,_do_nothing ./pkg/cvo/ I1120 11:23:53.053380 24806 cvo.go:731] Started syncing available updates "test/default" I1120 11:23:53.057477 24806 availableupdates.go:79] Retrieving available updates again, because the architecture has changed from "" to "amd64" I1120 11:23:53.068889 24806 availableupdates.go:398] Update service http://127.0.0.1:46459 could not return available updates: ResponseFailed: unexpected HTTP status: 500 Internal Server Error I1120 11:23:53.070095 24806 cvo.go:733] Finished syncing available updates "test/default" (16.782722ms) --- FAIL: TestOperator_availableUpdatesSync (0.04s) --- FAIL: TestOperator_availableUpdatesSync/if_last_successful_check_time_was_too_recent,_do_nothing (0.04s) cvo_test.go:2784: unexpected: (*cvo.availableUpdates)( - nil, + &{ + UpdateService: "http://localhost:8080/graph", + Channel: "fast", + Architecture: "amd64", + Condition: v1.ClusterOperatorStatusCondition{ ...
1 parent ad87ec8 commit 28b3b40

File tree

11 files changed

+94
-83
lines changed

11 files changed

+94
-83
lines changed

pkg/cincinnati/cincinnati.go

Lines changed: 8 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -340,6 +340,14 @@ func convertRetrievedUpdateToRelease(update node) (configv1.Release, error) {
340340
}
341341
cvoUpdate.URL = configv1.URL(urlString)
342342
}
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)
349+
}
350+
}
343351
if channels, ok := update.Metadata["io.openshift.upgrades.graph.release.channels"]; ok {
344352
cvoUpdate.Channels = strings.Split(channels, ",")
345353
sort.Strings(cvoUpdate.Channels)

pkg/cvo/availableupdates.go

Lines changed: 31 additions & 20 deletions
Original file line numberDiff line numberDiff line change
@@ -6,6 +6,7 @@ import (
66
"net/http"
77
"net/url"
88
"regexp"
9+
"runtime"
910
"sort"
1011
"strings"
1112
"time"
@@ -48,7 +49,11 @@ func (optr *Operator) syncAvailableUpdates(ctx context.Context, config *configv1
4849

4950
channel := config.Spec.Channel
5051
desiredArch := optr.getDesiredArchitecture(config.Spec.DesiredUpdate)
51-
currentArch := optr.getArchitecture()
52+
currentArch := runtime.GOARCH
53+
54+
if optr.release.Architecture == configv1.ClusterVersionArchitectureMulti {
55+
currentArch = "multi"
56+
}
5257

5358
if desiredArch == "" {
5459
desiredArch = currentArch
@@ -205,24 +210,44 @@ func (u *availableUpdates) RecentlyChanged(interval time.Duration) bool {
205210
return u.LastSyncOrConfigChange.After(time.Now().Add(-interval))
206211
}
207212

208-
func (u *availableUpdates) NeedsUpdate(original *configv1.ClusterVersion) *configv1.ClusterVersion {
213+
func (u *availableUpdates) NeedsUpdate(original *configv1.ClusterVersion, statusReleaseArchitecture bool) *configv1.ClusterVersion {
209214
if u == nil {
210215
return nil
211216
}
212217
// Architecture could change but does not reside in ClusterVersion
213218
if u.UpdateService != string(original.Spec.Upstream) || u.Channel != original.Spec.Channel {
214219
return nil
215220
}
216-
if equality.Semantic.DeepEqual(u.Updates, original.Status.AvailableUpdates) &&
217-
equality.Semantic.DeepEqual(u.ConditionalUpdates, original.Status.ConditionalUpdates) &&
221+
222+
var updates []configv1.Release
223+
var conditionalUpdates []configv1.ConditionalUpdate
224+
225+
if statusReleaseArchitecture {
226+
updates = u.Updates
227+
conditionalUpdates = u.ConditionalUpdates
228+
} else {
229+
for _, update := range u.Updates {
230+
c := update.DeepCopy()
231+
c.Architecture = configv1.ClusterVersionArchitecture("")
232+
updates = append(updates, *c)
233+
}
234+
for _, conditionalUpdate := range u.ConditionalUpdates {
235+
c := conditionalUpdate.DeepCopy()
236+
c.Release.Architecture = configv1.ClusterVersionArchitecture("")
237+
conditionalUpdates = append(conditionalUpdates, *c)
238+
}
239+
}
240+
241+
if equality.Semantic.DeepEqual(updates, original.Status.AvailableUpdates) &&
242+
equality.Semantic.DeepEqual(conditionalUpdates, original.Status.ConditionalUpdates) &&
218243
equality.Semantic.DeepEqual(u.Condition, resourcemerge.FindOperatorStatusCondition(original.Status.Conditions, u.Condition.Type)) {
219244
return nil
220245
}
221246

222247
config := original.DeepCopy()
223248
resourcemerge.SetOperatorStatusCondition(&config.Status.Conditions, u.Condition)
224-
config.Status.AvailableUpdates = u.Updates
225-
config.Status.ConditionalUpdates = u.ConditionalUpdates
249+
config.Status.AvailableUpdates = updates
250+
config.Status.ConditionalUpdates = conditionalUpdates
226251
return config
227252
}
228253

@@ -301,20 +326,6 @@ func (optr *Operator) getAvailableUpdates() *availableUpdates {
301326
return u
302327
}
303328

304-
// getArchitecture returns the currently determined cluster architecture.
305-
func (optr *Operator) getArchitecture() string {
306-
optr.statusLock.Lock()
307-
defer optr.statusLock.Unlock()
308-
return optr.architecture
309-
}
310-
311-
// SetArchitecture sets the cluster architecture.
312-
func (optr *Operator) SetArchitecture(architecture string) {
313-
optr.statusLock.Lock()
314-
defer optr.statusLock.Unlock()
315-
optr.architecture = architecture
316-
}
317-
318329
func (optr *Operator) getDesiredArchitecture(update *configv1.Update) string {
319330
if update != nil {
320331
return string(update.Architecture)

pkg/cvo/cvo.go

Lines changed: 5 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -327,7 +327,6 @@ func (optr *Operator) InitializeFromPayload(update *payload.Update, requiredFeat
327327
optr.enabledFeatureGates = cvoFlags
328328
optr.release = update.Release
329329
optr.releaseCreated = update.ImageRef.CreationTimestamp.Time
330-
optr.SetArchitecture(update.Architecture)
331330

332331
// after the verifier has been loaded, initialize the sync worker with a payload retriever
333332
// which will consume the verifier
@@ -676,7 +675,7 @@ func (optr *Operator) sync(ctx context.Context, key string) error {
676675
config := validation.ClearInvalidFields(original, errs)
677676

678677
// identify the desired next version
679-
desired, found := findUpdateFromConfig(config, optr.getArchitecture())
678+
desired, found := findUpdateFromConfig(config, optr.release.Architecture)
680679
initialized := optr.configSync.Initialized()
681680
if found && initialized {
682681
klog.V(2).Infof("Desired version from spec is %#v after initialization", desired)
@@ -721,8 +720,6 @@ func (optr *Operator) sync(ctx context.Context, key string) error {
721720
// inform the config sync loop about our desired state
722721
status := optr.configSync.Update(ctx, config.Generation, desired, config, state)
723722

724-
optr.SetArchitecture(status.Architecture)
725-
726723
// write cluster version status
727724
return optr.syncStatus(ctx, original, config, status, errs)
728725
}
@@ -843,7 +840,7 @@ func (optr *Operator) currentVersion() configv1.Release {
843840
func mergeReleaseMetadata(release configv1.Release, getAvailableUpdates func() *availableUpdates) configv1.Release {
844841
merged := *release.DeepCopy()
845842

846-
if merged.Version == "" || len(merged.URL) == 0 || merged.Channels == nil {
843+
if merged.Version == "" || len(merged.URL) == 0 || len(merged.Architecture) == 0 || merged.Channels == nil {
847844
// only fill in missing values from availableUpdates, to avoid clobbering data from payload.LoadUpdate.
848845
availableUpdates := getAvailableUpdates()
849846
if availableUpdates != nil {
@@ -865,6 +862,9 @@ func mergeReleaseMetadata(release configv1.Release, getAvailableUpdates func() *
865862
if len(merged.URL) == 0 {
866863
merged.URL = update.URL
867864
}
865+
if len(merged.Architecture) == 0 {
866+
merged.Architecture = update.Architecture
867+
}
868868
if merged.Channels == nil {
869869
merged.Channels = append(update.Channels[:0:0], update.Channels...) // copy
870870
}

pkg/cvo/cvo_test.go

Lines changed: 5 additions & 44 deletions
Original file line numberDiff line numberDiff line change
@@ -9,6 +9,7 @@ import (
99
"os"
1010
"path/filepath"
1111
"reflect"
12+
"runtime"
1213
"strconv"
1314
"testing"
1415
"time"
@@ -21,7 +22,7 @@ import (
2122
"k8s.io/apimachinery/pkg/api/errors"
2223
metav1 "k8s.io/apimachinery/pkg/apis/meta/v1"
2324
"k8s.io/apimachinery/pkg/labels"
24-
"k8s.io/apimachinery/pkg/runtime"
25+
apiruntime "k8s.io/apimachinery/pkg/runtime"
2526
"k8s.io/apimachinery/pkg/runtime/schema"
2627
"k8s.io/apimachinery/pkg/types"
2728
"k8s.io/apimachinery/pkg/util/diff"
@@ -2365,47 +2366,6 @@ func TestOperator_availableUpdatesSync(t *testing.T) {
23652366
},
23662367
},
23672368
},
2368-
{
2369-
name: "report an error condition when architecture isn't set",
2370-
handler: func(w http.ResponseWriter, req *http.Request) {
2371-
http.Error(w, "bad things", http.StatusInternalServerError)
2372-
},
2373-
optr: &Operator{
2374-
updateService: "http://localhost:8080/graph",
2375-
release: configv1.Release{
2376-
Version: "v4.0.0",
2377-
Image: "image/image:v4.0.1",
2378-
},
2379-
namespace: "test",
2380-
name: "default",
2381-
client: fake.NewSimpleClientset(
2382-
&configv1.ClusterVersion{
2383-
ObjectMeta: metav1.ObjectMeta{
2384-
Name: "default",
2385-
},
2386-
Spec: configv1.ClusterVersionSpec{
2387-
ClusterID: configv1.ClusterID(id),
2388-
Channel: "",
2389-
},
2390-
Status: configv1.ClusterVersionStatus{
2391-
History: []configv1.UpdateHistory{
2392-
{Image: "image/image:v4.0.1"},
2393-
},
2394-
},
2395-
},
2396-
),
2397-
},
2398-
wantUpdates: &availableUpdates{
2399-
UpdateService: "http://localhost:8080/graph",
2400-
Channel: "",
2401-
Condition: configv1.ClusterOperatorStatusCondition{
2402-
Type: configv1.RetrievedUpdates,
2403-
Status: configv1.ConditionFalse,
2404-
Reason: noArchitecture,
2405-
Message: "Architecture has not been configured.",
2406-
},
2407-
},
2408-
},
24092369
{
24102370
name: "report an error condition when channel isn't set",
24112371
handler: func(w http.ResponseWriter, req *http.Request) {
@@ -2685,6 +2645,7 @@ func TestOperator_availableUpdatesSync(t *testing.T) {
26852645
availableUpdates: &availableUpdates{
26862646
UpdateService: "http://localhost:8080/graph",
26872647
Channel: "fast",
2648+
Architecture: runtime.GOARCH,
26882649
LastAttempt: time.Now(),
26892650
LastSyncOrConfigChange: time.Now(),
26902651
},
@@ -4156,7 +4117,7 @@ func expectMutation(t *testing.T, a ktesting.Action, verb string, resource, subr
41564117
}
41574118
switch at := a.(type) {
41584119
case ktesting.CreateAction:
4159-
expect, actual := obj.(runtime.Object).DeepCopyObject(), at.GetObject()
4120+
expect, actual := obj.(apiruntime.Object).DeepCopyObject(), at.GetObject()
41604121
// default autogenerated cluster ID
41614122
if in, ok := expect.(*configv1.ClusterVersion); ok {
41624123
if in.Spec.ClusterID == "" {
@@ -4199,7 +4160,7 @@ func expectMutation(t *testing.T, a ktesting.Action, verb string, resource, subr
41994160

42004161
func fakeClientsetWithUpdates(obj *configv1.ClusterVersion) *fake.Clientset {
42014162
client := &fake.Clientset{}
4202-
client.AddReactor("*", "*", func(action ktesting.Action) (handled bool, ret runtime.Object, err error) {
4163+
client.AddReactor("*", "*", func(action ktesting.Action) (handled bool, ret apiruntime.Object, err error) {
42034164
if action.GetVerb() == "get" {
42044165
return true, obj.DeepCopy(), nil
42054166
}

pkg/cvo/status.go

Lines changed: 7 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -186,7 +186,7 @@ func (optr *Operator) syncStatus(ctx context.Context, original, config *configv1
186186

187187
cvUpdated := false
188188
// update the config with the latest available updates
189-
if updated := optr.getAvailableUpdates().NeedsUpdate(config); updated != nil {
189+
if updated := optr.getAvailableUpdates().NeedsUpdate(config, optr.enabledFeatureGates.StatusReleaseArchitecture()); updated != nil {
190190
cvUpdated = true
191191
config = updated
192192
}
@@ -229,11 +229,17 @@ func updateClusterVersionStatus(cvStatus *configv1.ClusterVersionStatus, status
229229
if len(status.Actual.URL) == 0 {
230230
status.Actual.URL = release.URL
231231
}
232+
if len(status.Actual.Architecture) == 0 {
233+
status.Actual.Architecture = release.Architecture
234+
}
232235
if status.Actual.Channels == nil {
233236
status.Actual.Channels = append(release.Channels[:0:0], release.Channels...) // copy
234237
}
235238
}
236239
desired := mergeReleaseMetadata(status.Actual, getAvailableUpdates)
240+
if !enabledGates.StatusReleaseArchitecture() {
241+
desired.Architecture = configv1.ClusterVersionArchitecture("")
242+
}
237243

238244
risksMsg := ""
239245
if desired.Image == status.loadPayloadStatus.Update.Image {

pkg/cvo/status_test.go

Lines changed: 5 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -201,6 +201,7 @@ func TestOperator_syncFailingStatus(t *testing.T) {
201201
type fakeRiFlags struct {
202202
unknownVersion bool
203203
reconciliationIssuesCondition bool
204+
statusReleaseArchitecture bool
204205
}
205206

206207
func (f fakeRiFlags) UnknownVersion() bool {
@@ -211,6 +212,10 @@ func (f fakeRiFlags) ReconciliationIssuesCondition() bool {
211212
return f.reconciliationIssuesCondition
212213
}
213214

215+
func (f fakeRiFlags) StatusReleaseArchitecture() bool {
216+
return f.statusReleaseArchitecture
217+
}
218+
214219
func TestUpdateClusterVersionStatus_UnknownVersionAndReconciliationIssues(t *testing.T) {
215220
ignoreLastTransitionTime := cmpopts.IgnoreFields(configv1.ClusterOperatorStatusCondition{}, "LastTransitionTime")
216221

pkg/cvo/updatepayload.go

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -343,7 +343,7 @@ func (r *payloadRetriever) pruneJobs(ctx context.Context, retain int) error {
343343
// the desired architecture is changed to multi it resolves the payload using the current
344344
// version since that's the only valid available update. Otherwise it attempts to resolve
345345
// the payload using the specified desired version.
346-
func findUpdateFromConfig(config *configv1.ClusterVersion, currentArch string) (configv1.Update, bool) {
346+
func findUpdateFromConfig(config *configv1.ClusterVersion, currentArch configv1.ClusterVersionArchitecture) (configv1.Update, bool) {
347347
update := config.Spec.DesiredUpdate
348348
if update == nil {
349349
return configv1.Update{}, false
@@ -353,7 +353,7 @@ func findUpdateFromConfig(config *configv1.ClusterVersion, currentArch string) (
353353

354354
// Architecture changed to multi so only valid update is the multi arch version of current version
355355
if update.Architecture == configv1.ClusterVersionArchitectureMulti &&
356-
currentArch != string(configv1.ClusterVersionArchitectureMulti) {
356+
currentArch != configv1.ClusterVersionArchitectureMulti {
357357
version = config.Status.Desired.Version
358358
}
359359
return findUpdateFromConfigVersion(config, version, update.Force)

pkg/featuregates/featuregates.go

Lines changed: 23 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -25,6 +25,10 @@ type CvoGateChecker interface {
2525
// should never be relied upon by any production code. We may want to eventually turn this into
2626
// some kind of "real" API.
2727
ReconciliationIssuesCondition() bool
28+
29+
// StatusReleaseArchitecture controls whether CVO populates
30+
// Release.Architecture in status properties like status.desired and status.history[].
31+
StatusReleaseArchitecture() bool
2832
}
2933

3034
type panicOnUsageBeforeInitializationFunc func()
@@ -42,6 +46,11 @@ func (p panicOnUsageBeforeInitializationFunc) ReconciliationIssuesCondition() bo
4246
return false
4347
}
4448

49+
func (p panicOnUsageBeforeInitializationFunc) StatusReleaseArchitecture() bool {
50+
p()
51+
return false
52+
}
53+
4554
func (p panicOnUsageBeforeInitializationFunc) UnknownVersion() bool {
4655
p()
4756
return false
@@ -58,12 +67,17 @@ type CvoGates struct {
5867
// individual flags mirror the CvoGateChecker interface
5968
unknownVersion bool
6069
reconciliationIssuesCondition bool
70+
statusReleaseArchitecture bool
6171
}
6272

6373
func (c CvoGates) ReconciliationIssuesCondition() bool {
6474
return c.reconciliationIssuesCondition
6575
}
6676

77+
func (c CvoGates) StatusReleaseArchitecture() bool {
78+
return c.statusReleaseArchitecture
79+
}
80+
6781
func (c CvoGates) UnknownVersion() bool {
6882
return c.unknownVersion
6983
}
@@ -74,6 +88,7 @@ func DefaultCvoGates(version string) CvoGates {
7488
desiredVersion: version,
7589
unknownVersion: true,
7690
reconciliationIssuesCondition: false,
91+
statusReleaseArchitecture: false,
7792
}
7893
}
7994

@@ -90,13 +105,19 @@ func CvoGatesFromFeatureGate(gate *configv1.FeatureGate, version string) CvoGate
90105
// We found the matching version, so we do not need to run in the unknown version mode
91106
enabledGates.unknownVersion = false
92107
for _, enabled := range g.Enabled {
93-
if enabled.Name == features.FeatureGateUpgradeStatus {
108+
switch enabled.Name {
109+
case features.FeatureGateUpgradeStatus:
94110
enabledGates.reconciliationIssuesCondition = true
111+
case features.FeatureGateImageStreamImportMode:
112+
enabledGates.statusReleaseArchitecture = false
95113
}
96114
}
97115
for _, disabled := range g.Disabled {
98-
if disabled.Name == features.FeatureGateUpgradeStatus {
116+
switch disabled.Name {
117+
case features.FeatureGateUpgradeStatus:
99118
enabledGates.reconciliationIssuesCondition = false
119+
case features.FeatureGateImageStreamImportMode:
120+
enabledGates.statusReleaseArchitecture = false
100121
}
101122
}
102123
}

pkg/payload/payload.go

Lines changed: 2 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -386,7 +386,8 @@ func loadReleaseFromMetadata(releaseDir string) (configv1.Release, string, error
386386
if archInterface, ok := metadata.Metadata["release.openshift.io/architecture"]; ok {
387387
if archString, ok := archInterface.(string); ok {
388388
if archString == releaseMultiArchID {
389-
arch = string(configv1.ClusterVersionArchitectureMulti)
389+
release.Architecture = configv1.ClusterVersionArchitectureMulti
390+
arch = string(release.Architecture)
390391
} else {
391392
return release, "", fmt.Errorf("Architecture from %s (%s) contains invalid value: %q. Valid value is %q.",
392393
cincinnatiJSONFile, release.Version, archString, releaseMultiArchID)

0 commit comments

Comments
 (0)