Skip to content

Commit f8bc57e

Browse files
committed
Allow Innovative upgrades to be skipped during upgrade
1 parent b3b8e51 commit f8bc57e

File tree

3 files changed

+154
-0
lines changed

3 files changed

+154
-0
lines changed

e2e/upgrades/upgrades_test.go

Lines changed: 47 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -198,6 +198,53 @@ func TestUpgradesMajorVersion20_1To20_2(t *testing.T) {
198198
steps.Run(t)
199199
}
200200

201+
func TestUpgradesMajorVersionSkippingInnovativeRelease24_3To25_1(t *testing.T) {
202+
203+
if testing.Short() {
204+
t.Skip("skipping test in short mode.")
205+
}
206+
207+
testLog := zapr.NewLogger(zaptest.NewLogger(t))
208+
209+
e := testenv.CreateActiveEnvForTest()
210+
env := e.Start()
211+
defer e.Stop()
212+
213+
sb := testenv.NewDiffingSandbox(t, env)
214+
sb.StartManager(t, controller.InitClusterReconcilerWithLogger(testLog))
215+
216+
builder := testutil.NewBuilder("crdb").WithNodeCount(3).WithTLS().
217+
WithImage("cockroachdb/cockroach:v24.3.4").
218+
WithPVDataStore("1Gi").WithResources(resRequirements)
219+
220+
steps := testutil.Steps{
221+
{
222+
Name: "creates a 3-node secure cluster",
223+
Test: func(t *testing.T) {
224+
require.NoError(t, sb.Create(builder.Cr()))
225+
testutil.RequireClusterToBeReadyEventuallyTimeout(t, sb, builder, e2e.CreateClusterTimeout)
226+
},
227+
},
228+
{
229+
Name: "upgrades the cluster to the next major version",
230+
Test: func(t *testing.T) {
231+
current := builder.Cr()
232+
require.NoError(t, sb.Get(current))
233+
234+
updated := current.DeepCopy()
235+
updated.Spec.Image.Name = "cockroachdb/cockroach:v25.1.0"
236+
require.NoError(t, sb.Patch(updated, client.MergeFrom(current)))
237+
// we wait 10 min because we will be waiting 3 min for each pod.
238+
testutil.RequireClusterToBeReadyEventuallyTimeout(t, sb, builder, e2e.CreateClusterTimeout)
239+
testutil.RequireDbContainersToUseImage(t, sb, updated)
240+
t.Log("Done with major upgrade")
241+
},
242+
},
243+
}
244+
245+
steps.Run(t)
246+
}
247+
201248
// TestUpgradesMinorVersionThenRollback tests a minor version bump
202249
// then rollsback that upgrade
203250
func TestUpgradesMinorVersionThenRollback(t *testing.T) {

pkg/update/internal.go

Lines changed: 25 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -25,6 +25,31 @@ const (
2525
AdminRole = "admin"
2626
)
2727

28+
// ReleaseType represents the type of a release
29+
type ReleaseType int
30+
31+
const (
32+
Regular ReleaseType = iota
33+
Innovative
34+
)
35+
36+
var (
37+
// ReleaseMap is maintained in accordance with Cockroach Labs Release Notes: https://www.cockroachlabs.com/docs/releases/.
38+
ReleaseMap = []struct {
39+
version string
40+
rType ReleaseType
41+
}{
42+
{"24.1", Regular},
43+
{"24.2", Innovative},
44+
{"24.3", Regular},
45+
{"24.4", Innovative},
46+
{"25.1", Innovative},
47+
{"25.2", Regular},
48+
{"25.3", Innovative},
49+
{"25.4", Regular},
50+
}
51+
)
52+
2853
// internalUsers is a set of SQL users created as part of the managed service, not to be used
2954
// by customers. This struct is used to hide specific users in the console.
3055
var internalUsers = map[string]struct{}{

pkg/update/update_cockroach_version_common.go

Lines changed: 82 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -134,10 +134,74 @@ func isPatch(wantVersion *semver.Version, currentVersion *semver.Version) bool {
134134
return currentVersion.Major() == wantVersion.Major() && currentVersion.Minor() == wantVersion.Minor()
135135
}
136136

137+
// getNextReleases returns the list of possible upgrade targets
138+
func getNextReleases(currentVersion string) []string {
139+
var nextReleases []string
140+
var found bool
141+
142+
for _, releaseVersion := range ReleaseMap {
143+
if found {
144+
nextReleases = append(nextReleases, releaseVersion.version)
145+
if releaseVersion.rType == Regular {
146+
break
147+
}
148+
}
149+
if releaseVersion.version == currentVersion {
150+
found = true
151+
}
152+
}
153+
154+
return nextReleases
155+
}
156+
157+
// getPreviousReleases returns the list of possible rollback targets
158+
func getPreviousReleases(currentVersion string) []string {
159+
var prevReleases []string
160+
var found bool
161+
162+
for i := len(ReleaseMap) - 1; i >= 0; i-- {
163+
releaseVersion := ReleaseMap[i]
164+
if found {
165+
prevReleases = append(prevReleases, releaseVersion.version)
166+
if releaseVersion.rType == Regular {
167+
break
168+
}
169+
}
170+
if releaseVersion.version == currentVersion {
171+
found = true
172+
}
173+
}
174+
175+
return prevReleases
176+
}
177+
137178
func isForwardOneMajorVersion(wantVersion *semver.Version, currentVersion *semver.Version) bool {
138179
// Two cases:
139180
// 19.1 to 19.2 -> same year
140181
// 19.2 to 20.1 -> next year
182+
183+
// Since 2024, we have adopted a quarterly release cycle, with two of the four annual releases designated
184+
// as innovative releases. Users have the option to skip upgrading to an innovative release.
185+
if currentVersion.Major() >= 24 {
186+
// Four Cases:
187+
// 24.1 to 24.2 -> Same year without skipping innovative release
188+
// 24.1 to 24.3 -> Same year with skipping innovative release
189+
// 24.4 to 25.1 -> Next year without skipping innovative release
190+
// 24.3 to 25.1 -> Next year with skipping innovative release
191+
nextPossibleRelease := getNextReleases(fmt.Sprintf("%d.%d", currentVersion.Major(), currentVersion.Minor()))
192+
for _, version := range nextPossibleRelease {
193+
if version == fmt.Sprintf("%d.%d", wantVersion.Major(), wantVersion.Minor()) {
194+
return true
195+
}
196+
}
197+
198+
// This condition allows user to upgrade one version at a time.
199+
// ReleaseMap needs to be maintained if we want to skip the Innovative upgrades else this condition
200+
// is enough to do forward one major version.
201+
return (currentVersion.Major() == wantVersion.Major() && currentVersion.Minor()+1 == wantVersion.Minor()) ||
202+
(currentVersion.Major()+1 == wantVersion.Major() && currentVersion.Minor()-3 == wantVersion.Minor())
203+
}
204+
141205
return (currentVersion.Major() == wantVersion.Major() && currentVersion.Minor()+1 == wantVersion.Minor()) ||
142206
(currentVersion.Major()+1 == wantVersion.Major() && currentVersion.Minor()-1 == wantVersion.Minor())
143207
}
@@ -146,6 +210,24 @@ func isBackOneMajorVersion(wantVersion *semver.Version, currentVersion *semver.V
146210
// Two cases:
147211
// 19.2 to 19.1 -> same year
148212
// 20.1 to 19.2 -> previous year
213+
214+
// Since 2024, users have the option to skip rollback to an innovative release.
215+
if wantVersion.Major() >= 24 {
216+
// Four cases:
217+
// 24.2 -> 24.1 -> Same year without skipping innovative release
218+
// 24.3 -> 24.1 -> Same year with skipping innovative release
219+
// 25.1 -> 24.4 -> Previous year without skipping innovative release
220+
// 25.1 -> 24.3 -> Previous year with skipping innovative release
221+
rollbackReleases := getPreviousReleases(fmt.Sprintf("%d.%d", currentVersion.Major(), currentVersion.Minor()))
222+
for _, version := range rollbackReleases {
223+
if version == fmt.Sprintf("%d.%d", wantVersion.Major(), wantVersion.Minor()) {
224+
return true
225+
}
226+
}
227+
return (currentVersion.Major() == wantVersion.Major() && currentVersion.Minor() == wantVersion.Minor()+1) ||
228+
(currentVersion.Major() == wantVersion.Major()+1 && currentVersion.Minor() == wantVersion.Minor()-3)
229+
}
230+
149231
return (currentVersion.Major() == wantVersion.Major() && currentVersion.Minor() == wantVersion.Minor()+1) ||
150232
(currentVersion.Major() == wantVersion.Major()+1 && currentVersion.Minor() == wantVersion.Minor()-1)
151233
}

0 commit comments

Comments
 (0)