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  
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()); // default is success
77  
78          when(secondPluginUpgradeTask.getPluginKey()).thenReturn(PLUGIN_KEY);
79          when(secondPluginUpgradeTask.getBuildNumber()).thenReturn(2);
80          when(secondPluginUpgradeTask.doUpgrade()).thenReturn(Collections.<Message>emptyList()); // default is success
81  
82          when(thirdPluginUpgradeTask.getPluginKey()).thenReturn(SECOND_PLUGIN_KEY);
83          when(thirdPluginUpgradeTask.getBuildNumber()).thenReturn(1);
84          when(thirdPluginUpgradeTask.doUpgrade()).thenReturn(Collections.<Message>emptyList()); // default is success
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)); // last upgrade task (data build number)
104         when(clusterLock.tryLock(DefaultPluginUpgradeManager.LOCK_TIMEOUT_SECONDS, TimeUnit.SECONDS)).thenReturn(true);
105 
106         List<Message> messages = pluginUpgradeManager.upgradeInternal(plugin);
107 
108         // A successful upgrade task should increment the build data number by a put on plugin settings
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         // A failed upgrade task should not increment the build data number in plugin settings
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"); // from pluginUpgradeTask, secondPluginUpgradeTask
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"); // from pluginUpgradeTask, secondPluginUpgradeTask
173         verify(pluginSettings).put(SECOND_PLUGIN_KEY + BUILD, "1"); // thirdPluginUpgradeTask
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 }