@@ -2,18 +2,17 @@ package clusterversion
2
2
3
3
import (
4
4
"context"
5
- "strconv"
6
- "strings"
7
5
"time"
8
6
7
+ "github.com/blang/semver/v4"
9
8
configv1 "github.com/openshift/api/config/v1"
10
9
configv1listers "github.com/openshift/client-go/config/listers/config/v1"
11
10
apierrors "k8s.io/apimachinery/pkg/api/errors"
12
11
"k8s.io/apimachinery/pkg/api/meta"
13
12
"k8s.io/klog/v2"
14
13
15
14
"github.com/openshift/cluster-version-operator/lib/resourcemerge"
16
- precondition "github.com/openshift/cluster-version-operator/pkg/payload/precondition"
15
+ "github.com/openshift/cluster-version-operator/pkg/payload/precondition"
17
16
)
18
17
19
18
// Upgradeable checks if clusterversion is upgradeable currently.
@@ -74,30 +73,40 @@ func (pf *Upgradeable) Run(ctx context.Context, releaseContext precondition.Rele
74
73
return nil
75
74
}
76
75
77
- // we can always allow the upgrade if there isn't a version already installed
78
- if len (cv .Status .History ) == 0 {
79
- klog .V (2 ).Infof ("Precondition %s passed: no release history." , pf .Name ())
80
- return nil
76
+ currentVersion , err := semver .Parse (cv .Status .Desired .Version )
77
+ if err != nil {
78
+ return & precondition.Error {
79
+ Nested : err ,
80
+ Reason : "InvalidCurrentVersion" ,
81
+ Message : err .Error (),
82
+ Name : pf .Name (),
83
+ NonBlockingWarning : true , // do not block on issues that require an update to fix
84
+ }
81
85
}
82
86
83
- currentVersion := GetCurrentVersion (cv .Status .History )
84
- currentMinor := GetEffectiveMinor (currentVersion )
85
- desiredMinor := GetEffectiveMinor (releaseContext .DesiredVersion )
86
- klog .V (2 ).Infof ("currentMinor %s releaseContext.DesiredVersion %s desiredMinor %s" , currentMinor , releaseContext .DesiredVersion , desiredMinor )
87
+ targetVersion , err := semver .Parse (releaseContext .DesiredVersion )
88
+ if err != nil {
89
+ return & precondition.Error {
90
+ Nested : err ,
91
+ Reason : "InvalidDesiredVersion" ,
92
+ Message : err .Error (),
93
+ Name : pf .Name (),
94
+ }
95
+ }
87
96
88
- // if there is no difference in the minor version (4.y.z where 4.y is the same for current and desired ), then we can still upgrade
89
- // if no cluster overrides have been set
90
- if ! minorVersionUpgrade ( currentMinor , desiredMinor ) {
91
- klog . V ( 2 ). Infof ( "Precondition %q passed: minor from the target %s is not a minor version update from the current %s.%s." , pf . Name (), releaseContext . DesiredVersion , currentVersion , currentMinor )
97
+ klog . V ( 4 ). Infof ( "The current version is %s parsed from %s and the target version is %s parsed from %s" , currentVersion . String ( ), cv . Status . Desired . Version , targetVersion . String (), releaseContext . DesiredVersion )
98
+ if targetVersion . LTE ( currentVersion ) || ( targetVersion . Major == currentVersion . Major && targetVersion . Minor == currentVersion . Minor ) {
99
+ // When Upgradeable==False, a patch level update with the same minor level is allowed unless overrides are set
100
+ // This Upgradeable precondition is only concerned about moving forward, i.e., do not care about downgrade which is taken care of by the Rollback precondition
92
101
if condition := ClusterVersionOverridesCondition (cv ); condition != nil {
93
- klog .V (2 ).Infof ("Update from %s to %s blocked by %s: %s" , currentVersion , releaseContext .DesiredVersion , condition .Reason , condition .Message )
94
-
102
+ klog .V (2 ).Infof ("Retarget from %s to %s is blocked by %s: %s" , currentVersion .String (), targetVersion .String (), condition .Reason , condition .Message )
95
103
return & precondition.Error {
96
104
Reason : condition .Reason ,
97
105
Message : condition .Message ,
98
106
Name : pf .Name (),
99
107
}
100
108
} else {
109
+ klog .V (2 ).Infof ("Precondition %q passed on update to %s" , pf .Name (), targetVersion .String ())
101
110
return nil
102
111
}
103
112
}
@@ -112,45 +121,3 @@ func (pf *Upgradeable) Run(ctx context.Context, releaseContext precondition.Rele
112
121
113
122
// Name returns Name for the precondition.
114
123
func (pf * Upgradeable ) Name () string { return "ClusterVersionUpgradeable" }
115
-
116
- // GetCurrentVersion determines and returns the cluster's current version by iterating through the
117
- // provided update history until it finds the first version with update State of Completed. If a
118
- // Completed version is not found the version of the oldest history entry, which is the originally
119
- // installed version, is returned. If history is empty the empty string is returned.
120
- func GetCurrentVersion (history []configv1.UpdateHistory ) string {
121
- for _ , h := range history {
122
- if h .State == configv1 .CompletedUpdate {
123
- klog .V (2 ).Infof ("Cluster current version=%s" , h .Version )
124
- return h .Version
125
- }
126
- }
127
- // Empty history should only occur if method is called early in startup before history is populated.
128
- if len (history ) != 0 {
129
- return history [len (history )- 1 ].Version
130
- }
131
- return ""
132
- }
133
-
134
- // GetEffectiveMinor attempts to do a simple parse of the version provided. If it does not parse, the value is considered
135
- // empty string, which works for the comparison done here for equivalence.
136
- func GetEffectiveMinor (version string ) string {
137
- splits := strings .Split (version , "." )
138
- if len (splits ) < 2 {
139
- return ""
140
- }
141
- return splits [1 ]
142
- }
143
-
144
- // minorVersionUpgrade returns true if the the desired update minor version number is greater
145
- // than the current version minor version number. Errors resulting from either version
146
- // number being unset or NaN are ignored simply resulting in false returned.
147
- func minorVersionUpgrade (currentMinor string , desiredMinor string ) bool {
148
- if currentMinorNum , err := strconv .Atoi (currentMinor ); err == nil {
149
- if desiredMinorNum , err := strconv .Atoi (desiredMinor ); err == nil {
150
- if desiredMinorNum > currentMinorNum {
151
- return true
152
- }
153
- }
154
- }
155
- return false
156
- }
0 commit comments