Skip to content

Commit 335f144

Browse files
author
Daan Hoogland
committed
some unit tests
1 parent 0315005 commit 335f144

File tree

3 files changed

+278
-15
lines changed

3 files changed

+278
-15
lines changed

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

Lines changed: 27 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -457,7 +457,7 @@ public void check() {
457457
}
458458
}
459459

460-
private boolean isStandalone() throws CloudRuntimeException {
460+
boolean isStandalone() throws CloudRuntimeException {
461461
return Transaction.execute(new TransactionCallback<>() {
462462
@Override
463463
public Boolean doInTransaction(TransactionStatus status) {
@@ -483,18 +483,19 @@ public Boolean doInTransaction(TransactionStatus status) {
483483
});
484484
}
485485

486-
private void doUpgrades(GlobalLock lock) {
486+
@VisibleForTesting
487+
protected void doUpgrades(GlobalLock lock) {
487488
try {
488489
initializeDatabaseEncryptors();
489490

490491
final CloudStackVersion dbVersion = CloudStackVersion.parse(_dao.getCurrentVersion());
491-
final String currentVersionValue = this.getClass().getPackage().getImplementationVersion();
492+
final String currentVersionValue = getImplementationVersion();
492493

493494
if (StringUtils.isBlank(currentVersionValue)) {
494495
return;
495496
}
496497

497-
String csVersion = SystemVmTemplateRegistration.parseMetadataFile();
498+
String csVersion = parseSystemVmMetadata();
498499
final CloudStackVersion sysVmVersion = CloudStackVersion.parse(csVersion);
499500
final CloudStackVersion currentVersion = CloudStackVersion.parse(currentVersionValue);
500501
SystemVmTemplateRegistration.CS_MAJOR_VERSION = String.valueOf(sysVmVersion.getMajorRelease()) + "." + String.valueOf(sysVmVersion.getMinorRelease());
@@ -517,14 +518,34 @@ private void doUpgrades(GlobalLock lock) {
517518
String errorMessage = "Database upgrade is required but the management server is running in a clustered environment. " +
518519
"Please perform the database upgrade when the management server is not running in a clustered environment.";
519520
LOGGER.error(errorMessage);
520-
System.exit(5); // I would prefer ServerDaemon.abort(errorMessage) but that would create a dependency hell
521+
handleClusteredUpgradeRequired(); // allow tests to override behavior
521522
}
522523
} finally {
523524
lock.unlock();
524525
}
525526
}
526527

527-
private void initializeDatabaseEncryptors() {
528+
/**
529+
* Hook that is called when an upgrade is required but the management server is clustered.
530+
* Default behavior is to exit the JVM, tests can override to throw instead.
531+
*/
532+
@VisibleForTesting
533+
protected void handleClusteredUpgradeRequired() {
534+
System.exit(5); // I would prefer ServerDaemon.abort(errorMessage) but that would create a dependency hell
535+
}
536+
537+
@VisibleForTesting
538+
protected String getImplementationVersion() {
539+
return this.getClass().getPackage().getImplementationVersion();
540+
}
541+
542+
@VisibleForTesting
543+
protected String parseSystemVmMetadata() {
544+
return SystemVmTemplateRegistration.parseMetadataFile();
545+
}
546+
547+
// Make this protected so tests can noop it out
548+
protected void initializeDatabaseEncryptors() {
528549
TransactionLegacy txn = TransactionLegacy.open("initializeDatabaseEncryptors");
529550
txn.start();
530551
String errorMessage = "Unable to get the database connections";
Lines changed: 173 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,173 @@
1+
// Licensed to the Apache Software Foundation (ASF) under one
2+
// or more contributor license agreements. See the NOTICE file
3+
// distributed with this work for additional information
4+
// regarding copyright ownership. The ASF licenses this file
5+
// to you under the Apache License, Version 2.0 (the
6+
// "License"); you may not use this file except in compliance
7+
// with the License. You may obtain a copy of the License at
8+
//
9+
// http://www.apache.org/licenses/LICENSE-2.0
10+
//
11+
// Unless required by applicable law or agreed to in writing,
12+
// software distributed under the License is distributed on an
13+
// "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
14+
// KIND, either express or implied. See the License for the
15+
// specific language governing permissions and limitations
16+
// under the License.
17+
package com.cloud.upgrade;
18+
19+
import static org.junit.Assert.assertFalse;
20+
import static org.junit.Assert.assertTrue;
21+
22+
import com.cloud.upgrade.dao.VersionDao;
23+
import com.cloud.upgrade.dao.VersionDaoImpl;
24+
import com.cloud.upgrade.dao.VersionVO;
25+
import com.cloud.utils.db.GlobalLock;
26+
import org.junit.Test;
27+
28+
public class DatabaseUpgradeCheckerDoUpgradesTest {
29+
30+
static class StubVersionDao extends VersionDaoImpl implements VersionDao {
31+
private final String currentVersion;
32+
33+
StubVersionDao(String currentVersion) {
34+
this.currentVersion = currentVersion;
35+
}
36+
37+
@Override
38+
public VersionVO findByVersion(String version, VersionVO.Step step) {
39+
return null;
40+
}
41+
42+
@Override
43+
public String getCurrentVersion() {
44+
return currentVersion;
45+
}
46+
47+
}
48+
49+
private static class TestableChecker extends DatabaseUpgradeChecker {
50+
boolean initializeCalled = false;
51+
boolean upgradeCalled = false;
52+
boolean clusterHandlerCalled = false;
53+
String implVersionOverride = null;
54+
String sysVmMetadataOverride = "4.8.0";
55+
boolean standaloneOverride = true;
56+
57+
TestableChecker(String daoVersion) {
58+
// set a stub DAO
59+
this._dao = new StubVersionDao(daoVersion);
60+
}
61+
62+
@Override
63+
protected void initializeDatabaseEncryptors() {
64+
initializeCalled = true;
65+
// noop instead of doing DB work
66+
}
67+
68+
@Override
69+
protected String getImplementationVersion() {
70+
return implVersionOverride;
71+
}
72+
73+
@Override
74+
protected String parseSystemVmMetadata() {
75+
return sysVmMetadataOverride;
76+
}
77+
78+
@Override
79+
boolean isStandalone() {
80+
return standaloneOverride;
81+
}
82+
83+
@Override
84+
protected void upgrade(org.apache.cloudstack.utils.CloudStackVersion dbVersion, org.apache.cloudstack.utils.CloudStackVersion currentVersion) {
85+
upgradeCalled = true;
86+
}
87+
88+
@Override
89+
protected void handleClusteredUpgradeRequired() {
90+
clusterHandlerCalled = true;
91+
}
92+
}
93+
94+
@Test
95+
public void testDoUpgrades_noImplementationVersion_returnsEarly() {
96+
TestableChecker checker = new TestableChecker("4.8.0");
97+
checker.implVersionOverride = ""; // blank -> should return early
98+
99+
GlobalLock lock = GlobalLock.getInternLock("test-noimpl");
100+
try {
101+
// acquire lock so doUpgrades can safely call unlock in finally
102+
lock.lock(1);
103+
checker.doUpgrades(lock);
104+
} finally {
105+
// ensure lock released if still held
106+
lock.releaseRef();
107+
}
108+
109+
assertTrue("initializeDatabaseEncryptors should be called before returning", checker.initializeCalled);
110+
assertFalse("upgrade should not be called when implementation version is blank", checker.upgradeCalled);
111+
assertFalse("cluster handler should not be called", checker.clusterHandlerCalled);
112+
}
113+
114+
@Test
115+
public void testDoUpgrades_dbUpToDate_noUpgrade() {
116+
// DB version = code version -> no upgrade
117+
TestableChecker checker = new TestableChecker("4.8.1");
118+
checker.implVersionOverride = "4.8.1";
119+
checker.sysVmMetadataOverride = "4.8.1";
120+
121+
GlobalLock lock = GlobalLock.getInternLock("test-uptodate");
122+
try {
123+
lock.lock(1);
124+
checker.doUpgrades(lock);
125+
} finally {
126+
lock.releaseRef();
127+
}
128+
129+
assertTrue(checker.initializeCalled);
130+
assertFalse(checker.upgradeCalled);
131+
assertFalse(checker.clusterHandlerCalled);
132+
}
133+
134+
@Test
135+
public void testDoUpgrades_requiresUpgrade_standalone_invokesUpgrade() {
136+
TestableChecker checker = new TestableChecker("4.8.0");
137+
checker.implVersionOverride = "4.8.2"; // code is newer than DB
138+
checker.sysVmMetadataOverride = "4.8.2";
139+
checker.standaloneOverride = true;
140+
141+
GlobalLock lock = GlobalLock.getInternLock("test-upgrade-standalone");
142+
try {
143+
lock.lock(1);
144+
checker.doUpgrades(lock);
145+
} finally {
146+
lock.releaseRef();
147+
}
148+
149+
assertTrue(checker.initializeCalled);
150+
assertTrue("upgrade should be invoked in standalone mode", checker.upgradeCalled);
151+
assertFalse(checker.clusterHandlerCalled);
152+
}
153+
154+
@Test
155+
public void testDoUpgrades_requiresUpgrade_clustered_invokesHandler() {
156+
TestableChecker checker = new TestableChecker("4.8.0");
157+
checker.implVersionOverride = "4.8.2"; // code is newer than DB
158+
checker.sysVmMetadataOverride = "4.8.2";
159+
checker.standaloneOverride = false;
160+
161+
GlobalLock lock = GlobalLock.getInternLock("test-upgrade-clustered");
162+
try {
163+
lock.lock(1);
164+
checker.doUpgrades(lock);
165+
} finally {
166+
lock.releaseRef();
167+
}
168+
169+
assertTrue(checker.initializeCalled);
170+
assertFalse("upgrade should not be invoked in clustered mode", checker.upgradeCalled);
171+
assertTrue("cluster handler should be invoked in clustered mode", checker.clusterHandlerCalled);
172+
}
173+
}

engine/schema/src/test/java/com/cloud/upgrade/DatabaseUpgradeCheckerTest.java

Lines changed: 78 additions & 9 deletions
Original file line numberDiff line numberDiff line change
@@ -16,14 +16,24 @@
1616
// under the License.
1717
package com.cloud.upgrade;
1818

19-
import static org.junit.Assert.assertEquals;
20-
import static org.junit.Assert.assertNotNull;
21-
import static org.junit.Assert.assertTrue;
19+
import java.sql.SQLException;
20+
import java.lang.reflect.Field;
21+
import java.sql.Connection;
22+
import java.sql.PreparedStatement;
23+
import java.sql.ResultSet;
2224

23-
import java.util.Arrays;
25+
import javax.sql.DataSource;
2426

2527
import org.apache.cloudstack.utils.CloudStackVersion;
2628
import org.junit.Test;
29+
import org.junit.Before;
30+
import org.junit.After;
31+
import org.junit.runner.RunWith;
32+
33+
import org.mockito.ArgumentMatchers;
34+
import org.mockito.Mock;
35+
import org.mockito.Mockito;
36+
import org.mockito.junit.MockitoJUnitRunner;
2737

2838
import com.cloud.upgrade.DatabaseUpgradeChecker.NoopDbUpgrade;
2939
import com.cloud.upgrade.dao.DbUpgrade;
@@ -43,8 +53,46 @@
4353
import com.cloud.upgrade.dao.Upgrade480to481;
4454
import com.cloud.upgrade.dao.Upgrade490to4910;
4555

56+
import com.cloud.utils.db.TransactionLegacy;
57+
58+
import static org.junit.Assert.*;
59+
60+
@RunWith(MockitoJUnitRunner.class)
4661
public class DatabaseUpgradeCheckerTest {
4762

63+
@Mock
64+
DataSource dataSource;
65+
66+
@Mock
67+
Connection connection;
68+
69+
@Mock
70+
PreparedStatement preparedStatement;
71+
72+
@Mock
73+
ResultSet resultSet;
74+
75+
private DataSource backupDataSource;
76+
77+
@Before
78+
public void setup() throws Exception {
79+
Field dsField = TransactionLegacy.class.getDeclaredField("s_ds");
80+
dsField.setAccessible(true);
81+
backupDataSource = (DataSource) dsField.get(null);
82+
dsField.set(null, dataSource);
83+
84+
Mockito.when(dataSource.getConnection()).thenReturn(connection);
85+
Mockito.when(connection.prepareStatement(ArgumentMatchers.anyString())).thenReturn(preparedStatement);
86+
Mockito.when(preparedStatement.executeQuery()).thenReturn(resultSet);
87+
}
88+
89+
@After
90+
public void cleanup() throws Exception {
91+
Field dsField = TransactionLegacy.class.getDeclaredField("s_ds");
92+
dsField.setAccessible(true);
93+
dsField.set(null, backupDataSource);
94+
}
95+
4896
@Test
4997
public void testCalculateUpgradePath480to481() {
5098

@@ -79,7 +127,7 @@ public void testCalculateUpgradePath490to4910() {
79127
assertTrue(upgrades.length >= 1);
80128
assertTrue(upgrades[0] instanceof Upgrade490to4910);
81129

82-
assertTrue(Arrays.equals(new String[] {"4.9.0", currentVersion.toString()}, upgrades[0].getUpgradableVersionRange()));
130+
assertArrayEquals(new String[]{"4.9.0", currentVersion.toString()}, upgrades[0].getUpgradableVersionRange());
83131
assertEquals(currentVersion.toString(), upgrades[0].getUpgradedVersion());
84132

85133
}
@@ -104,7 +152,7 @@ public void testCalculateUpgradePath410to412() {
104152
assertTrue(upgrades[3] instanceof Upgrade41120to41130);
105153
assertTrue(upgrades[4] instanceof Upgrade41120to41200);
106154

107-
assertTrue(Arrays.equals(new String[] {"4.11.0.0", "4.11.1.0"}, upgrades[1].getUpgradableVersionRange()));
155+
assertArrayEquals(new String[]{"4.11.0.0", "4.11.1.0"}, upgrades[1].getUpgradableVersionRange());
108156
assertEquals(currentVersion.toString(), upgrades[4].getUpgradedVersion());
109157

110158
}
@@ -151,12 +199,12 @@ public void testFindUpgradePath452to490() {
151199
assertTrue(upgrades[5] instanceof Upgrade471to480);
152200
assertTrue(upgrades[6] instanceof Upgrade480to481);
153201

154-
assertTrue(Arrays.equals(new String[] {"4.8.1", currentVersion.toString()}, upgrades[upgrades.length - 1].getUpgradableVersionRange()));
202+
assertArrayEquals(new String[]{"4.8.1", currentVersion.toString()}, upgrades[upgrades.length - 1].getUpgradableVersionRange());
155203
assertEquals(currentVersion.toString(), upgrades[upgrades.length - 1].getUpgradedVersion());
156204
}
157205

158206
@Test
159-
public void testCalculateUpgradePathUnkownDbVersion() {
207+
public void testCalculateUpgradePathUnknownDbVersion() {
160208

161209
final CloudStackVersion dbVersion = CloudStackVersion.parse("4.99.0.0");
162210
assertNotNull(dbVersion);
@@ -173,7 +221,7 @@ public void testCalculateUpgradePathUnkownDbVersion() {
173221
}
174222

175223
@Test
176-
public void testCalculateUpgradePathFromKownDbVersion() {
224+
public void testCalculateUpgradePathFromKnownDbVersion() {
177225

178226
final CloudStackVersion dbVersion = CloudStackVersion.parse("4.17.0.0");
179227
assertNotNull(dbVersion);
@@ -306,4 +354,25 @@ public void testCalculateUpgradePathFromSecurityReleaseToNextSecurityRelease() {
306354
assertEquals(upgrades.length + 1, upgradesFromSecurityReleaseToNext.length);
307355
assertTrue(upgradesFromSecurityReleaseToNext[upgradesFromSecurityReleaseToNext.length - 1] instanceof NoopDbUpgrade);
308356
}
357+
358+
@Test
359+
public void isStandalone() throws SQLException {
360+
// simulate zero 'UP' hosts -> standalone
361+
Mockito.when(resultSet.next()).thenReturn(true);
362+
Mockito.when(resultSet.getInt(1)).thenReturn(0);
363+
364+
final DatabaseUpgradeChecker checker = new DatabaseUpgradeChecker();
365+
assertTrue("DatabaseUpgradeChecker should be a standalone component", checker.isStandalone());
366+
}
367+
368+
@Test
369+
public void isNotStandalone() throws SQLException {
370+
// simulate at least one 'UP' host -> not standalone
371+
Mockito.when(resultSet.next()).thenReturn(true);
372+
Mockito.when(resultSet.getInt(1)).thenReturn(1);
373+
374+
final DatabaseUpgradeChecker checker = new DatabaseUpgradeChecker();
375+
assertFalse("DatabaseUpgradeChecker should not be a standalone component", checker.isStandalone());
376+
}
377+
309378
}

0 commit comments

Comments
 (0)