Skip to content

Commit 54b1fe4

Browse files
committed
more changes
Signed-off-by: Abhishek Kumar <abhishek.mrt22@gmail.com>
1 parent 539ae85 commit 54b1fe4

8 files changed

Lines changed: 181 additions & 27 deletions

File tree

‎engine/schema/src/main/java/com/cloud/upgrade/DatabaseUpgradeChecker.java‎

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -550,7 +550,7 @@ protected void doUpgrades(GlobalLock lock) {
550550
@VisibleForTesting
551551
protected static void updateSystemVmTemplateVersion(CloudStackVersion sysVmVersion) {
552552
SystemVmTemplateRegistration.CS_MAJOR_VERSION = String.format("%d.%d", sysVmVersion.getMajorRelease(), sysVmVersion.getMinorRelease());
553-
SystemVmTemplateRegistration.CS_TINY_VERSION = String.valueOf(sysVmVersion.usesNewVersioning() ? sysVmVersion.getSecurityRelease() : sysVmVersion.getPatchRelease());
553+
SystemVmTemplateRegistration.CS_TINY_VERSION = String.valueOf(sysVmVersion.getTinyRelease());
554554
}
555555

556556
/**

‎engine/schema/templateConfig.sh‎

Lines changed: 18 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -17,6 +17,8 @@
1717
# specific language governing permissions and limitations
1818
# under the License.
1919

20+
# Kept in sync with CloudStackVersion.NEW_VERSIONING_CUTOVER_MAJOR_VERSION (utils module) and the
21+
# same constant in scripts/installer/export-templates.sh.
2022
NEW_VERSIONING_CUTOVER_MAJOR_VERSION=24
2123

2224
function getTemplateVersion() {
@@ -25,17 +27,29 @@ function getTemplateVersion() {
2527
subversion1="$(cut -d'.' -f1 <<<"$version")"
2628
subversion2="$(cut -d'.' -f2 <<<"$version")"
2729
minorversion="$(cut -d'.' -f3 <<<"$version")"
30+
fourthversion="$(cut -d'.' -f4 <<<"$version")"
2831
export CS_VERSION="${subversion1}"."${subversion2}"
2932
export CS_MINOR_VERSION="${minorversion}"
3033
export VERSION="${CS_VERSION}.${CS_MINOR_VERSION}"
3134
if [[ "$subversion1" -ge "$NEW_VERSIONING_CUTOVER_MAJOR_VERSION" ]]; then
3235
# New versioning (major.minor.security): the third component is the
33-
# security release itself, there is no separate patch component.
36+
# security release itself, there is no separate patch component. A
37+
# 4th component is invalid in this scheme (matches CloudStackVersion.parse())
38+
# and must not be silently dropped.
39+
if [[ -n "$fourthversion" ]]; then
40+
echo "Invalid version '${projVersion}': major versions at or above ${NEW_VERSIONING_CUTOVER_MAJOR_VERSION} do not support a 4-component major.minor.patch.security format" >&2
41+
exit 1
42+
fi
3443
export FULL_VERSION="${VERSION}"
3544
else
36-
# Legacy versioning (major.minor.patch.security)
37-
securityversion="$(cut -d'.' -f4 <<<"$version")"
38-
export FULL_VERSION="${VERSION}.${securityversion}"
45+
# Legacy versioning: major.minor.patch, or major.minor.patch.security when a 4th
46+
# component is present. Without a 4th component, VERSION is already correct as-is;
47+
# appending an empty security component would leave a dangling trailing dot.
48+
if [[ -n "$fourthversion" ]]; then
49+
export FULL_VERSION="${VERSION}.${fourthversion}"
50+
else
51+
export FULL_VERSION="${VERSION}"
52+
fi
3953
fi
4054
export CS_SYSTEMTEMPLATE_REPO="https://download.cloudstack.org/systemvm/"
4155
}

‎engine/schema/test_templateConfig.sh‎

Lines changed: 12 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -54,6 +54,11 @@ getTemplateVersion "4.23.0.1-SNAPSHOT"
5454
assert_eq "legacy with -SNAPSHOT suffix: VERSION" "4.23.0" "$VERSION"
5555
assert_eq "legacy with -SNAPSHOT suffix: FULL_VERSION" "4.23.0.1" "$FULL_VERSION"
5656

57+
# A legacy 3-component major.minor.patch value (no security component) is a documented, valid
58+
# CloudStackVersion.parse() format below the cutover and must not get a dangling trailing dot.
59+
getTemplateVersion "4.23.1"
60+
assert_eq "legacy 3-component (no security): FULL_VERSION has no trailing dot" "4.23.1" "$FULL_VERSION"
61+
5762
# --- getTemplateVersion: new versioning (major >= 24, post-cutover) ---
5863

5964
getTemplateVersion "24.0.0"
@@ -69,6 +74,13 @@ getTemplateVersion "99.9.9"
6974
assert_eq "future major: VERSION" "99.9.9" "$VERSION"
7075
assert_eq "future major: FULL_VERSION" "99.9.9" "$FULL_VERSION"
7176

77+
# A 4-component major.minor.patch.security value is invalid at/after the cutover (matches
78+
# CloudStackVersion.parse()) and must fail the build rather than silently drop the 4th component.
79+
# Run in a subshell since getTemplateVersion calls `exit` on this input.
80+
( getTemplateVersion "24.0.0.1" ) >/dev/null 2>&1
81+
cutover_4component_exit_code="$?"
82+
assert_eq "cutover: 4-component version is rejected instead of silently normalized" "1" "$cutover_4component_exit_code"
83+
7284
# --- createMetadataFile: end-to-end metadata.ini "version" line ---
7385

7486
run_create_metadata_file() {

‎plugins/integrations/veeam-control-service/src/main/java/org/apache/cloudstack/veeam/api/dto/Version.java‎

Lines changed: 2 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -87,10 +87,8 @@ public static Version fromPackageAndCSVersion(boolean complete) {
8787
}
8888
version.setMajor(String.valueOf(csVersion.getMajorRelease()));
8989
version.setMinor(String.valueOf(csVersion.getMinorRelease()));
90-
if (csVersion.usesNewVersioning()) {
91-
version.setBuild(String.valueOf(csVersion.getSecurityRelease()));
92-
} else {
93-
version.setBuild(String.valueOf(csVersion.getPatchRelease()));
90+
version.setBuild(String.valueOf(csVersion.getTinyRelease()));
91+
if (!csVersion.usesNewVersioning()) {
9492
version.setRevision(String.valueOf(csVersion.getSecurityRelease()));
9593
}
9694
return version;

‎scripts/installer/export-templates.sh‎

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -16,6 +16,8 @@
1616
# specific language governing permissions and limitations
1717
# under the License.
1818

19+
# Kept in sync with CloudStackVersion.NEW_VERSIONING_CUTOVER_MAJOR_VERSION (utils module) and the
20+
# same constant in engine/schema/templateConfig.sh.
1921
NEW_VERSIONING_CUTOVER_MAJOR_VERSION=24
2022

2123
METADATA_FILE="metadata.ini"

‎utils/src/main/java/com/cloud/utils/nicira/nvp/plugin/NiciraNvpApiVersion.java‎

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -37,7 +37,7 @@ public static synchronized boolean isApiVersionLowerThan(String apiVersion){
3737
if (niciraApiVersion == null) {
3838
return false;
3939
}
40-
int compare = CloudStackVersion.compare(niciraApiVersion, apiVersion);
40+
int compare = CloudStackVersion.compare(niciraApiVersion, apiVersion, true);
4141
return (compare < 0);
4242
}
4343

‎utils/src/main/java/org/apache/cloudstack/utils/CloudStackVersion.java‎

Lines changed: 76 additions & 11 deletions
Original file line numberDiff line numberDiff line change
@@ -41,27 +41,38 @@ public final class CloudStackVersion implements Comparable<CloudStackVersion> {
4141

4242
private final static Pattern NUMBER_VERSION_FORMAT = Pattern.compile("\\d+\\.\\d+\\.\\d+(?:\\.\\d+)?");
4343
private final static Pattern FULL_VERSION_FORMAT = Pattern.compile("\\d+\\.\\d+\\.\\d+(?:\\.\\d+)?(?:-[a-zA-Z]+)?(?:-\\d+)?(?:-SNAPSHOT)?");
44+
// Kept in sync with the same constant in engine/schema/templateConfig.sh and
45+
// scripts/installer/export-templates.sh, which independently generate/consume
46+
// system VM template metadata using this same versioning cutover rule.
4447
private final static int NEW_VERSIONING_CUTOVER_MAJOR_VERSION = 24;
4548

4649
private final int majorRelease;
4750
private final int minorRelease;
48-
private final Integer patchRelease;
51+
private final int patchRelease;
4952
private final Integer securityRelease;
53+
// Whether this instance was parsed via parse(value, true) — a version scheme other than
54+
// CloudStack's own. Deliberately excluded from equals()/hashCode(): two instances with the same
55+
// component values represent the same version regardless of which parsing mode produced them.
56+
// It only gates whether the new-versioning cutover rule's *interpretation* (toString()'s
57+
// canonicalization, usesNewVersioning()) applies, since that rule is specific to CloudStack's own
58+
// release numbering.
59+
private final boolean external;
5060

51-
private CloudStackVersion(final int majorRelease, final int minorRelease, final Integer patchRelease, final Integer securityRelease) {
61+
private CloudStackVersion(final int majorRelease, final int minorRelease, final int patchRelease, final Integer securityRelease, final boolean external) {
5262

5363
super();
5464

5565
checkArgument(majorRelease >= 0, CloudStackVersion.class.getName() + "(int, int, int, Integer) requires a majorRelease greater than 0.");
5666
checkArgument(minorRelease >= 0, CloudStackVersion.class.getName() + "(int, int, int, Integer) requires a minorRelease greater than 0.");
57-
checkArgument(patchRelease == null || patchRelease >= 0, CloudStackVersion.class.getName() + "(int, int, int, Integer) requires a patchRelease greater than 0.");
67+
checkArgument(patchRelease >= 0, CloudStackVersion.class.getName() + "(int, int, int, Integer) requires a patchRelease greater than 0.");
5868
checkArgument(securityRelease == null || securityRelease >= 0,
5969
CloudStackVersion.class.getName() + "(int, int, int, Integer) requires a null securityRelease or a non-null value greater than 0.");
6070

6171
this.majorRelease = majorRelease;
6272
this.minorRelease = minorRelease;
6373
this.patchRelease = patchRelease;
6474
this.securityRelease = securityRelease;
75+
this.external = external;
6576

6677
}
6778

@@ -90,6 +101,26 @@ private CloudStackVersion(final int majorRelease, final int minorRelease, final
90101
*
91102
*/
92103
public static CloudStackVersion parse(final String value) {
104+
return parse(value, false);
105+
}
106+
107+
/**
108+
* Parses a version string the same way as {@link #parse(String)}, but with {@code external} set to
109+
* <code>true</code>, always applies the legacy major.minor.patch(.security) component mapping and
110+
* never the major-24-and-above new-versioning cutover rule.
111+
* <p>
112+
* CloudStack's own versioning cutover (see {@link #NEW_VERSIONING_CUTOVER_MAJOR_VERSION}) is a fact
113+
* about CloudStack's own release numbering. It has no bearing on unrelated version schemes, such as a
114+
* VMware/ESXi hypervisor version, that may coincidentally reach the same major version number. Callers
115+
* parsing such external version strings must pass <code>external = true</code> so a value like
116+
* <code>24.0.1</code> is not misread as a CloudStack security release.
117+
*
118+
* @param value The value to parse which must be non-blank and conform the formats listed above
119+
* @param external whether {@code value} comes from a version scheme other than CloudStack's own
120+
*
121+
* @return <code>value</code> parsed into a <code>CloudStackVersion</code> instance
122+
*/
123+
public static CloudStackVersion parse(final String value, final boolean external) {
93124

94125
// Strip out any legacy patch information from the version string ...
95126
final String trimmedValue = StringUtils.substringBefore(value, "-");
@@ -111,12 +142,12 @@ public static CloudStackVersion parse(final String value) {
111142
final Integer securityRelease;
112143

113144
if (components.length == 4) {
114-
checkArgument(isLegacyVersioning(majorRelease), CloudStackVersion.class.getName() + ".parse(String) passed " + value +
145+
checkArgument(external || isLegacyVersioning(majorRelease), CloudStackVersion.class.getName() + ".parse(String) passed " + value +
115146
", but major versions at or above 24 do not support legacy int.int.int.int format");
116147
// Deprecated legacy format: major.minor.patch.security
117148
patchRelease = thirdComponent;
118149
securityRelease = Integer.valueOf(components[3]);
119-
} else if (isNewVersioning(majorRelease)) {
150+
} else if (!external && isNewVersioning(majorRelease)) {
120151
// New format: major.minor.securityRelease (patch dropped)
121152
patchRelease = 0;
122153
securityRelease = thirdComponent;
@@ -126,7 +157,7 @@ public static CloudStackVersion parse(final String value) {
126157
securityRelease = null;
127158
}
128159

129-
return new CloudStackVersion(majorRelease, minorRelease, patchRelease, securityRelease);
160+
return new CloudStackVersion(majorRelease, minorRelease, patchRelease, securityRelease, external);
130161

131162
}
132163

@@ -143,7 +174,26 @@ public static CloudStackVersion parse(final String value) {
143174
* @since 4.12.0.0
144175
*/
145176
public static int compare(String version1, String version2) {
146-
return parse(version1).compareTo(parse(version2));
177+
return compare(version1, version2, false);
178+
}
179+
180+
/**
181+
* Shortcut method to {@link #parse(String, boolean)} and {@link #compareTo(CloudStackVersion)} two versions.
182+
* Pass <code>external = true</code> when comparing version strings from a scheme other than CloudStack's
183+
* own (e.g. a VMware/ESXi or NSX/Nicira NVP version), so CloudStack's own new-versioning cutover rule is
184+
* not applied to them.
185+
*
186+
* @param version1 the first value to be parsed and compared
187+
* @param version2 the second value to be parsed and compared
188+
* @param external whether <code>version1</code>/<code>version2</code> come from a version scheme other
189+
* than CloudStack's own
190+
*
191+
* @return A value less than zero (0) indicates <code>version1</code> is less than <code>version2</code>. A value
192+
* equal to zero (0) indicates <code>version1</code> equals <code>version2</code>. A value greater than zero (0)
193+
* indicates <code>version1</code> is greater than <code>version2</code>.
194+
*/
195+
public static int compare(String version1, String version2, boolean external) {
196+
return parse(version1, external).compareTo(parse(version2, external));
147197
}
148198

149199
/**
@@ -214,7 +264,11 @@ public static String trimRouterVersion(String version) {
214264
return tokens[2];
215265
}
216266

217-
return "0";
267+
// A sentinel that sorts lower than any real version, so callers that feed this straight into
268+
// parse()/compare() (as the router-version-check call sites do, with no try/catch) get a
269+
// "definitely needs upgrading" result instead of an uncaught parse failure. Must itself be a
270+
// value parse() accepts.
271+
return "0.0.0";
218272
}
219273

220274
/**
@@ -289,7 +343,18 @@ public Integer getSecurityRelease() {
289343
}
290344

291345
public boolean usesNewVersioning() {
292-
return isNewVersioning(majorRelease);
346+
return !external && isNewVersioning(majorRelease);
347+
}
348+
349+
/**
350+
* The release component that identifies this version's "tiny"/build release: the security
351+
* release for a new-versioning (post-cutover) CloudStack version, or the patch release otherwise.
352+
* Consolidates a mapping that callers (e.g. system VM template version resolution, the Veeam
353+
* integration's version reporting) would otherwise have to re-derive from
354+
* {@link #usesNewVersioning()} themselves.
355+
*/
356+
public int getTinyRelease() {
357+
return usesNewVersioning() ? securityRelease : patchRelease;
293358
}
294359

295360
@Override
@@ -319,7 +384,7 @@ public int hashCode() {
319384
@Override
320385
public String toString() {
321386
// Canonicalize cutover-and-later versions to major.minor.securityRelease.
322-
if (securityRelease != null && patchRelease == 0 && isNewVersioning(majorRelease)) {
387+
if (securityRelease != null && patchRelease == 0 && usesNewVersioning()) {
323388
return Joiner.on(".").join(ImmutableList.of(majorRelease, minorRelease, securityRelease));
324389
}
325390

@@ -332,7 +397,7 @@ public String toString() {
332397
*/
333398
public static String getVMwareParentVersion(String hypervisorVersion) {
334399
try {
335-
CloudStackVersion version = CloudStackVersion.parse(hypervisorVersion);
400+
CloudStackVersion version = CloudStackVersion.parse(hypervisorVersion, true);
336401
String parentVersion = String.format("%s.%s", version.getMajorRelease(), version.getMinorRelease());
337402
if (version.getPatchRelease() != 0) {
338403
parentVersion = String.format("%s.%s", parentVersion, version.getPatchRelease());

0 commit comments

Comments
 (0)