1 package com.atlassian.sal.core.upgrade;
2
3 import com.atlassian.beehive.ClusterLock;
4 import com.atlassian.beehive.ClusterLockService;
5 import com.atlassian.plugin.Plugin;
6 import com.atlassian.plugin.PluginAccessor;
7 import com.atlassian.plugin.event.PluginEventManager;
8 import com.atlassian.sal.api.message.Message;
9 import com.atlassian.sal.api.pluginsettings.PluginSettings;
10 import com.atlassian.sal.api.pluginsettings.PluginSettingsFactory;
11 import com.atlassian.sal.api.transaction.TransactionCallback;
12 import com.atlassian.sal.api.transaction.TransactionTemplate;
13 import com.atlassian.sal.api.upgrade.PluginUpgradeTask;
14 import com.atlassian.sal.core.message.DefaultMessage;
15
16 import org.junit.After;
17 import org.junit.Before;
18 import org.junit.Test;
19 import org.junit.runner.RunWith;
20 import org.mockito.ArgumentCaptor;
21 import org.mockito.Mock;
22 import org.mockito.runners.MockitoJUnitRunner;
23
24 import java.util.Arrays;
25 import java.util.Collections;
26 import java.util.List;
27 import java.util.concurrent.TimeUnit;
28
29 import static com.atlassian.sal.core.upgrade.PluginUpgrader.BUILD;
30 import static java.util.Collections.singletonList;
31 import static org.hamcrest.Matchers.contains;
32 import static org.hamcrest.Matchers.empty;
33 import static org.hamcrest.Matchers.is;
34 import static org.hamcrest.Matchers.not;
35 import static org.junit.Assert.assertThat;
36 import static org.mockito.Matchers.anyString;
37 import static org.mockito.Mockito.never;
38 import static org.mockito.Mockito.times;
39 import static org.mockito.Mockito.verify;
40 import static org.mockito.Mockito.when;
41
42 @RunWith (MockitoJUnitRunner.class)
43 public class DefaultPluginUpgradeManagerTest
44 {
45 @Mock private Plugin plugin;
46 @Mock private Plugin secondPlugin;
47 @Mock private PluginUpgradeTask pluginUpgradeTask;
48 @Mock private PluginUpgradeTask secondPluginUpgradeTask;
49 @Mock private PluginUpgradeTask thirdPluginUpgradeTask;
50 @Mock private PluginAccessor pluginAccessor;
51 @Mock private PluginSettingsFactory pluginSettingsFactory;
52 @Mock private PluginEventManager pluginEventManager;
53 @Mock private PluginSettings pluginSettings;
54 @Mock private ClusterLockService clusterLockService;
55 @Mock private ClusterLock clusterLock;
56
57 private static final String PLUGIN_KEY = "com.atlassian.plugin.test.upgrade-test";
58 private static final String SECOND_PLUGIN_KEY = "com.atlassian.plugin2.test.upgrade-test";
59
60 private DefaultPluginUpgradeManager pluginUpgradeManager;
61 private CountingTransactionTemplate transactionTemplate;
62
63 @Before
64 public void setUp() throws Exception
65 {
66 transactionTemplate = new CountingTransactionTemplate();
67
68 when(plugin.getKey()).thenReturn(PLUGIN_KEY);
69 when(secondPlugin.getKey()).thenReturn(SECOND_PLUGIN_KEY);
70
71 when(pluginAccessor.getPlugin(PLUGIN_KEY)).thenReturn(plugin);
72 when(pluginAccessor.getPlugin(SECOND_PLUGIN_KEY)).thenReturn(secondPlugin);
73
74 when(pluginUpgradeTask.getPluginKey()).thenReturn(PLUGIN_KEY);
75 when(pluginUpgradeTask.getBuildNumber()).thenReturn(1);
76 when(pluginUpgradeTask.doUpgrade()).thenReturn(Collections.<Message>emptyList());
77
78 when(secondPluginUpgradeTask.getPluginKey()).thenReturn(PLUGIN_KEY);
79 when(secondPluginUpgradeTask.getBuildNumber()).thenReturn(2);
80 when(secondPluginUpgradeTask.doUpgrade()).thenReturn(Collections.<Message>emptyList());
81
82 when(thirdPluginUpgradeTask.getPluginKey()).thenReturn(SECOND_PLUGIN_KEY);
83 when(thirdPluginUpgradeTask.getBuildNumber()).thenReturn(1);
84 when(thirdPluginUpgradeTask.doUpgrade()).thenReturn(Collections.<Message>emptyList());
85
86 when(pluginSettingsFactory.createGlobalSettings()).thenReturn(pluginSettings);
87
88 when(clusterLockService.getLockForName(anyString())).thenReturn(clusterLock);
89 }
90
91 @After
92 public void tearDown()
93 {
94 transactionTemplate.resetTransactionCount();
95 }
96
97 @Test
98 public void testUpgradeInternalIsSuccessful() throws Exception
99 {
100 pluginUpgradeManager = new DefaultPluginUpgradeManager(singletonList(pluginUpgradeTask), transactionTemplate,
101 pluginAccessor, pluginSettingsFactory, pluginEventManager, clusterLockService);
102
103 when(pluginSettings.get(plugin.getKey() + BUILD)).thenReturn(Integer.toString(0));
104 when(clusterLock.tryLock(DefaultPluginUpgradeManager.LOCK_TIMEOUT_SECONDS, TimeUnit.SECONDS)).thenReturn(true);
105
106 List<Message> messages = pluginUpgradeManager.upgradeInternal(plugin);
107
108
109 verify(pluginSettings).put(PLUGIN_KEY + BUILD, "1");
110
111 assertThat(messages, empty());
112
113 verify(clusterLock).tryLock(DefaultPluginUpgradeManager.LOCK_TIMEOUT_SECONDS, TimeUnit.SECONDS);
114 verify(clusterLock).unlock();
115 }
116
117 @Test
118 public void testUpgradeInternalFailsIfUpgradeFails() throws Exception
119 {
120 pluginUpgradeManager = new DefaultPluginUpgradeManager(singletonList(pluginUpgradeTask), transactionTemplate,
121 pluginAccessor, pluginSettingsFactory, pluginEventManager, clusterLockService);
122
123 when(pluginUpgradeTask.doUpgrade()).thenReturn(Collections.<Message>singletonList(new DefaultMessage("failed.upgrade")));
124 when(clusterLock.tryLock(DefaultPluginUpgradeManager.LOCK_TIMEOUT_SECONDS, TimeUnit.SECONDS)).thenReturn(true);
125
126 List<Message> messages = pluginUpgradeManager.upgradeInternal(plugin);
127
128
129 verify(pluginSettings, never()).put(anyString(), anyString());
130
131 assertThat(messages, not(empty()));
132
133 ArgumentCaptor<String> lockName = ArgumentCaptor.forClass(String.class);
134 verify(clusterLockService).getLockForName(lockName.capture());
135 verify(clusterLock).tryLock(DefaultPluginUpgradeManager.LOCK_TIMEOUT_SECONDS, TimeUnit.SECONDS);
136 verify(clusterLock).unlock();
137
138 messages = pluginUpgradeManager.upgradeInternal(plugin);
139
140 verify(clusterLockService, times(2)).getLockForName(lockName.getValue());
141 }
142
143 @Test
144 public void testThatOnlyOneTransactionTemplateIsExecutedPerPluginUpgrade() throws Exception
145 {
146 pluginUpgradeManager = new DefaultPluginUpgradeManager(Arrays.asList(pluginUpgradeTask, secondPluginUpgradeTask), transactionTemplate,
147 pluginAccessor, pluginSettingsFactory, pluginEventManager, clusterLockService);
148
149 when(clusterLock.tryLock(DefaultPluginUpgradeManager.LOCK_TIMEOUT_SECONDS, TimeUnit.SECONDS)).thenReturn(true);
150
151 List<Message> messages = pluginUpgradeManager.upgradeInternal(plugin);
152
153 assertThat(messages, empty());
154 assertThat(transactionTemplate.getTransactionCount(), is(1));
155
156 verify(pluginSettings).put(PLUGIN_KEY + BUILD, "2");
157
158 verify(clusterLock).tryLock(DefaultPluginUpgradeManager.LOCK_TIMEOUT_SECONDS, TimeUnit.SECONDS);
159 verify(clusterLock).unlock();
160 }
161
162 @Test
163 public void testThatOnlyOneTransactionTemplateIsExecutedPerPluginUpgradeWithMultiplePlugins() throws Exception
164 {
165 pluginUpgradeManager = new DefaultPluginUpgradeManager(Arrays.asList(pluginUpgradeTask, secondPluginUpgradeTask, thirdPluginUpgradeTask), transactionTemplate,
166 pluginAccessor, pluginSettingsFactory, pluginEventManager, clusterLockService);
167
168 when(clusterLock.tryLock(DefaultPluginUpgradeManager.LOCK_TIMEOUT_SECONDS, TimeUnit.SECONDS)).thenReturn(true);
169
170 List<Message> messages = pluginUpgradeManager.upgradeInternal();
171
172 verify(pluginSettings).put(PLUGIN_KEY + BUILD, "2");
173 verify(pluginSettings).put(SECOND_PLUGIN_KEY + BUILD, "1");
174
175 assertThat(messages, empty());
176 assertThat(transactionTemplate.getTransactionCount(), is(2));
177
178 verify(clusterLock, times(2)).tryLock(DefaultPluginUpgradeManager.LOCK_TIMEOUT_SECONDS, TimeUnit.SECONDS);
179 verify(clusterLock, times(2)).unlock();
180 }
181
182 @Test
183 public void testUpgradeLockTimeout() throws InterruptedException
184 {
185 pluginUpgradeManager = new DefaultPluginUpgradeManager(singletonList(pluginUpgradeTask), transactionTemplate,
186 pluginAccessor, pluginSettingsFactory, pluginEventManager, clusterLockService);
187
188 when(clusterLock.tryLock(DefaultPluginUpgradeManager.LOCK_TIMEOUT_SECONDS, TimeUnit.SECONDS)).thenReturn(false);
189
190 List<Message> messages = pluginUpgradeManager.upgradeInternal(plugin);
191
192 final Message expectedMessage = new DefaultMessage("unable to acquire cluster lock named 'sal.upgrade.com.atlassian.plugin.test.upgrade-test' after waiting " + DefaultPluginUpgradeManager.LOCK_TIMEOUT_SECONDS + " seconds; note that this timeout may be adjusted via the system property '" + DefaultPluginUpgradeManager.LOCK_TIMEOUT_PROPERTY + "'");
193 assertThat(messages, contains(expectedMessage));
194
195 verify(clusterLock).tryLock(DefaultPluginUpgradeManager.LOCK_TIMEOUT_SECONDS, TimeUnit.SECONDS);
196 }
197
198 @Test
199 public void testUpgradeLockInterrupted() throws InterruptedException
200 {
201 pluginUpgradeManager = new DefaultPluginUpgradeManager(singletonList(pluginUpgradeTask), transactionTemplate,
202 pluginAccessor, pluginSettingsFactory, pluginEventManager, clusterLockService);
203
204 when(clusterLock.tryLock(DefaultPluginUpgradeManager.LOCK_TIMEOUT_SECONDS, TimeUnit.SECONDS)).thenThrow(new InterruptedException("blargh"));
205
206 List<Message> messages = pluginUpgradeManager.upgradeInternal(plugin);
207
208 final Message expectedMessage = new DefaultMessage("interrupted while trying to acquire cluster lock named 'sal.upgrade.com.atlassian.plugin.test.upgrade-test' blargh");
209 assertThat(messages, contains(expectedMessage));
210
211 verify(clusterLock).tryLock(DefaultPluginUpgradeManager.LOCK_TIMEOUT_SECONDS, TimeUnit.SECONDS);
212 }
213
214 private static class CountingTransactionTemplate implements TransactionTemplate
215 {
216 private int transactionCount = 0;
217
218 @Override
219 public <T> T execute(final TransactionCallback<T> action)
220 {
221 transactionCount++;
222 return action.doInTransaction();
223 }
224
225 void resetTransactionCount()
226 {
227 transactionCount = 0;
228 }
229
230 int getTransactionCount()
231 {
232 return transactionCount;
233 }
234 }
235 }