View Javadoc

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