-
Notifications
You must be signed in to change notification settings - Fork 0
Expand file tree
/
Copy pathrules.go
More file actions
248 lines (211 loc) · 8.36 KB
/
Copy pathrules.go
File metadata and controls
248 lines (211 loc) · 8.36 KB
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
248
//
// DISCLAIMER
//
// Copyright 2018 ArangoDB GmbH, Cologne, Germany
//
// Licensed under the Apache License, Version 2.0 (the "License");
// you may not use this file except in compliance with the License.
// You may obtain a copy of the License at
//
// http://www.apache.org/licenses/LICENSE-2.0
//
// Unless required by applicable law or agreed to in writing, software
// distributed under the License is distributed on an "AS IS" BASIS,
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
// See the License for the specific language governing permissions and
// limitations under the License.
//
// Copyright holder is ArangoDB GmbH, Cologne, Germany
//
// Author Ewout Prangsma
//
package upgraderules
import (
"fmt"
"strconv"
"strings"
driver "github.com/arangodb/go-driver/v2/arangodb"
)
// License is a strongly typed ArangoDB license type
type License int
const (
// LicenseCommunity is this license for the ArangoDB community edition
LicenseCommunity License = iota
// LicenseEnterprise is this license for the ArangoDB enterprise edition
LicenseEnterprise
)
// minPatchForMajorUpgrade defines the minimum source patch version required
// for an upgrade to the next major X.0. Key is "fromMajor.fromMinor" (e.g. "3.12").
//
// TEMPORARY: 3.12 is set to 0 so any 3.12.* with a valid numeric patch may upgrade
// to 4.0 until 3.12.9 is released. After that, restore to 10 (3.12.10+ required).
var minPatchForMajorUpgrade = map[string]int{
"3.12": 0, // TEMPORARY: was 10; restore after 3.12.9 release
}
// parsePatch extracts the numeric patch version (third component) from a driver.Version
// and reports whether the patch component has a valid format.
//
// driver.Version does not expose a Patch() method, so we parse the string form.
//
// Actual 3.12 release versioning:
// - Patch: 3.12.9 (three components; the third is the patch number).
// - Hotfix: 3.12.9.1, 3.12.9.10, … (four components; fourth is [1-9][0-9]*).
//
// As of 3.12, patch and hotfix releases are GA-only; only 3.12.0 had alphas, betas, or RCs.
//
// The third numeric component is treated as the patch basis. Supported formats include:
//
// "3.12.9" → 9 (patch)
// "3.12.9.1" → 9 (hotfix; fourth component ignored for min-patch check)
// "3.12.9-rc1" → 9 (suffix stripped; for generality; 3.12 patch/hotfix are GA-only in practice)
//
// Unsupported: patch component does not start with a digit, e.g. "3.12.rc1".
func parsePatch(v driver.Version) (int, bool) {
// Convert version to string (e.g. "3.12.7" or "3.12.7-rc1")
s := fmt.Sprint(v)
// Split by dot: ["3", "12", "7"] or ["3", "12", "7-rc1"]
parts := strings.Split(s, ".")
// If there are fewer than 3 components, patch is not present
if len(parts) < 3 {
return 0, false
}
// Extract the patch component (may include suffix like "-rc1")
patchStr := parts[2]
if patchStr == "" {
return 0, false
}
// Reject patch components that do not start with a digit (e.g. "3.12.rc1").
// Such versions are invalid for minimum-patch checks and are rejected here.
if patchStr[0] < '0' || patchStr[0] > '9' {
return 0, false
}
// Strip any non-digit suffix (e.g. "7-rc1" → "7")
// We stop at the first non-numeric character.
for i, r := range patchStr {
if r < '0' || r > '9' {
patchStr = patchStr[:i]
break
}
}
// Convert cleaned numeric string to integer
p, err := strconv.Atoi(patchStr)
if err != nil {
return 0, false
}
return p, true
}
// checkMinPatchForMajorUpgrade returns an error if from→to is a major upgrade
// that requires a minimum patch and from does not satisfy it.
func checkMinPatchForMajorUpgrade(from, to driver.Version) error {
if to.Major() != from.Major()+1 || to.Minor() != 0 {
return nil
}
key := fmt.Sprintf("%d.%d", from.Major(), from.Minor())
minPatch, hasRule := minPatchForMajorUpgrade[key]
if !hasRule {
return fmt.Errorf("Major versions are different: major upgrade from %d.%d to %d.0 is not allowed", from.Major(), from.Minor(), to.Major())
}
patch, ok := parsePatch(from)
if !ok {
return fmt.Errorf("Invalid source version format for major upgrade: expected numeric patch in %s", from)
}
if patch >= minPatch {
return nil
}
return fmt.Errorf("Upgrade to %d.0 requires at least %d.%d.%d", to.Major(), from.Major(), from.Minor(), minPatch)
}
// CheckUpgradeRules checks if it is allowed to upgrade an ArangoDB
// deployment from given `from` version to given `to` version.
// If this is allowed, nil is returned, otherwise and error is
// returning describing why the upgrade is not allowed.
func CheckUpgradeRules(from, to driver.Version) error {
// ---- Major version change ----
if from.Major() != to.Major() {
// Disallow downgrade
if to.Major() < from.Major() {
return fmt.Errorf("Major versions are different: downgrade of major version is not allowed")
}
// Only allow upgrade to next major version
if to.Major() != from.Major()+1 {
return fmt.Errorf("Major versions are different: major versions may only increment by 1")
}
// Only allow upgrade to X.0 (e.g. 3.12.x → 4.0.x)
if to.Minor() != 0 {
return fmt.Errorf("Major versions are different: major upgrades are only allowed to X.0")
}
if err := checkMinPatchForMajorUpgrade(from, to); err != nil {
return err
}
return nil
}
// ---- Minor version change (same major) ----
if from.Minor() != to.Minor() {
// Disallow downgrade
if to.Minor() < from.Minor() {
return fmt.Errorf("Downgrade of minor version is not allowed")
}
// Only allow increment by 1
if to.Minor() != from.Minor()+1 {
return fmt.Errorf("Minor versions may only increment by 1")
}
}
// Patch changes always allowed
return nil
}
// CheckSoftUpgradeRules checks if it is allowed to upgrade an ArangoDB
// deployment from given `from` version to given `to` version.
// If this is allowed, nil is returned, otherwise and error is
// returning describing why the upgrade is not allowed.
// This function allows to jump more than one minor version.
func CheckSoftUpgradeRules(from, to driver.Version) error {
// ---- Major version change ----
if from.Major() != to.Major() {
// Disallow major downgrade
if to.Major() < from.Major() {
return fmt.Errorf("Major versions are different: downgrade of major version is not allowed")
}
// Only allow upgrade to next major version
if to.Major() != from.Major()+1 {
return fmt.Errorf("Major versions are different: major versions may only increment by 1")
}
// Only allow upgrade to X.0
if to.Minor() != 0 {
return fmt.Errorf("Major versions are different: major upgrades are only allowed to X.0")
}
// Enforce minimum patch requirement (e.g. 3.12.10 → 4.0 when minPatch restored)
if err := checkMinPatchForMajorUpgrade(from, to); err != nil {
return err
}
return nil
}
// ---- Same major version ----
// Only major/minor rules: no minimum-patch for minor upgrades.
// (When minPatch for 3.12 is restored: major upgrade 3.12.10+ → 4.0.)
if to.Minor() < from.Minor() {
return fmt.Errorf("Downgrade of minor version is not allowed")
}
return nil
}
// CheckUpgradeRulesWithLicense checks if it is allowed to upgrade an ArangoDB
// deployment from given `fromVersion` version to given `toVersion` version.
// If also includes the given `fromLicense` and `toLicense` in this check.
// If this is allowed, nil is returned, otherwise and error is
// returning describing why the upgrade is not allowed.
func CheckUpgradeRulesWithLicense(fromVersion, toVersion driver.Version, fromLicense, toLicense License) error {
if fromLicense != toLicense && fromLicense == LicenseEnterprise {
return fmt.Errorf("Upgrade from Enterprise to Community edition is not possible")
}
return CheckUpgradeRules(fromVersion, toVersion)
}
// CheckUpgradeRulesWithLicense checks if it is allowed to upgrade an ArangoDB
// deployment from given `fromVersion` version to given `toVersion` version.
// If also includes the given `fromLicense` and `toLicense` in this check.
// If this is allowed, nil is returned, otherwise and error is
// returning describing why the upgrade is not allowed.
// This function allows to jump more than one minor version.
func CheckSoftUpgradeRulesWithLicense(fromVersion, toVersion driver.Version, fromLicense, toLicense License) error {
if fromLicense != toLicense && fromLicense == LicenseEnterprise {
return fmt.Errorf("Upgrade from Enterprise to Community edition is not possible")
}
return CheckSoftUpgradeRules(fromVersion, toVersion)
}