@Inject public DataMigrationManagerImpl( final Set<MigrationPlugin> plugins, final MigrationInfoSerialization migrationInfoSerialization, final MigrationInfoCache migrationInfoCache ) { this.migrationInfoCache = migrationInfoCache; Preconditions.checkNotNull( plugins, "plugins must not be null" ); Preconditions.checkNotNull( migrationInfoSerialization, "migrationInfoSerialization must not be null" ); this.migrationInfoSerialization = migrationInfoSerialization; this.executionOrder = new ArrayList<>(plugins.size()); this.migrationPlugins = new HashMap<>(); for ( MigrationPlugin plugin : plugins ) { final String name = plugin.getName(); final MigrationPlugin existing = migrationPlugins.get( name ); if ( existing != null ) { throw new IllegalArgumentException( "Duplicate plugin name detected. A plugin with name " + name + " is already implemented by class '" + existing.getClass().getName() + "'. Class '" + plugin .getClass().getName() + "' is also trying to implement this name." ); } this.migrationPlugins.put( name, plugin ); this.executionOrder.add( plugin ); } //now sort based on execution order Collections.sort(executionOrder, MigrationPluginComparator.INSTANCE); } @Override
@Override public void migrate(final String name) throws MigrationException { /** * Invoke each plugin to attempt a migration */ final MigrationPlugin plugin = migrationPlugins.get( name ); if(plugin != null){ final ProgressObserver observer = new CassandraProgressObserver(plugin.getName()); plugin.run(observer); migrationInfoCache.invalidateAll(); }else { throw new IllegalArgumentException(name + " does not match a current plugin."); } }
@Override public void migrate() throws MigrationException { /** * Invoke each plugin to attempt a migration */ executionOrder.forEach(plugin -> { final ProgressObserver observer = new CassandraProgressObserver(plugin.getName()); plugin.run(observer); migrationInfoCache.invalidateAll(); }); }
@Override public int compare( final MigrationPlugin o1, final MigrationPlugin o2 ) { //first one is less if(o1.getPhase().ordinal() < o2.getPhase().ordinal()){ return -1; } //second one is first if(o2.getPhase().ordinal() < o1.getPhase().ordinal()){ return 1; } //if our phase for return o1.getName().compareTo( o2.getName() ); } }
@Test public void test2PluginsNameOrder() throws MigrationException { final Set<MigrationPlugin> plugins = new HashSet<>(); MigrationPlugin plugin1 = mock( MigrationPlugin.class ); when( plugin1.getPhase() ).thenReturn( PluginPhase.MIGRATE ); when( plugin1.getName() ).thenReturn( "plugin2" ); MigrationPlugin plugin2 = mock( MigrationPlugin.class ); when( plugin2.getPhase() ).thenReturn( PluginPhase.MIGRATE ); when( plugin2.getName() ).thenReturn( "plugin1" ); plugins.add( plugin1 ); plugins.add( plugin2 ); final MigrationInfoSerialization migrationInfoSerialization = mock( MigrationInfoSerialization.class ); final MigrationInfoCache migrationInfoCache = mock(MigrationInfoCache.class); DataMigrationManagerImpl migrationManager = new DataMigrationManagerImpl( plugins, migrationInfoSerialization, migrationInfoCache ); assertTrue(migrationManager.getExecutionOrder().get(0).getName() == "plugin1"); assertTrue(migrationManager.getExecutionOrder().get(1).getName() == "plugin2"); } @Test
@Test public void test2PluginsPhaseOrder() throws MigrationException { final Set<MigrationPlugin> plugins = new HashSet<>(); MigrationPlugin plugin1 = mock( MigrationPlugin.class ); when( plugin1.getPhase() ).thenReturn( PluginPhase.BOOTSTRAP ); when( plugin1.getName() ).thenReturn( "plugin2a" ); MigrationPlugin plugin1a = mock( MigrationPlugin.class ); when( plugin1a.getPhase() ).thenReturn( PluginPhase.BOOTSTRAP ); when( plugin1a.getName() ).thenReturn( "plugin2" ); MigrationPlugin plugin2 = mock( MigrationPlugin.class ); when( plugin2.getPhase() ).thenReturn( PluginPhase.MIGRATE ); when( plugin2.getName() ).thenReturn( "plugin1" ); plugins.add( plugin1 ); plugins.add( plugin2 ); plugins.add( plugin1a); final MigrationInfoSerialization migrationInfoSerialization = mock( MigrationInfoSerialization.class ); final MigrationInfoCache migrationInfoCache = mock(MigrationInfoCache.class); DataMigrationManagerImpl migrationManager = new DataMigrationManagerImpl( plugins, migrationInfoSerialization, migrationInfoCache ); assertTrue(migrationManager.getExecutionOrder().get(0).getName() == "plugin2"); assertTrue(migrationManager.getExecutionOrder().get(1).getName() == "plugin2a"); assertTrue(migrationManager.getExecutionOrder().get(2).getName() == "plugin1"); }
/** * Reset of version that is too high or too low */ @Test( expected = IllegalArgumentException.class ) public void testResetToInvalidVersions() { final String name = "plugin1"; final int version = 10; //linked hash set is intentional here. For iteration order we can boostrap to come second so we can //verify it was actually run first final Set<MigrationPlugin> plugins = new LinkedHashSet<>(); MigrationPlugin plugin1 = mock( MigrationPlugin.class ); when( plugin1.getPhase() ).thenReturn( PluginPhase.MIGRATE ); when( plugin1.getName() ).thenReturn( name ); when( plugin1.getMaxVersion() ).thenReturn( version ); plugins.add( plugin1 ); final MigrationInfoSerialization migrationInfoSerialization = mock( MigrationInfoSerialization.class ); final MigrationInfoCache migrationInfoCache = mock(MigrationInfoCache.class); DataMigrationManagerImpl migrationManager = new DataMigrationManagerImpl( plugins, migrationInfoSerialization, migrationInfoCache ); migrationManager.resetToVersion( name, version + 1 ); }
/** * Reset with no plugin name */ @Test( expected = IllegalArgumentException.class ) public void testResetInvalidName() { final String name = "plugin1"; final int version = 10; //linked hash set is intentional here. For iteration order we can boostrap to come second so we can //verify it was actually run first final Set<MigrationPlugin> plugins = new LinkedHashSet<>(); MigrationPlugin plugin1 = mock( MigrationPlugin.class ); when( plugin1.getPhase() ).thenReturn( PluginPhase.MIGRATE ); when( plugin1.getName() ).thenReturn( name ); when( plugin1.getMaxVersion() ).thenReturn( version ); plugins.add( plugin1 ); final MigrationInfoSerialization migrationInfoSerialization = mock( MigrationInfoSerialization.class ); final MigrationInfoCache migrationInfoCache = mock(MigrationInfoCache.class); DataMigrationManagerImpl migrationManager = new DataMigrationManagerImpl( plugins, migrationInfoSerialization, migrationInfoCache ); migrationManager.resetToVersion( name + "foo", version ); }
@Test public void testLastStatus() { final String name = "plugin1"; final String status = "some status"; //linked hash set is intentional here. For iteration order we can boostrap to come second so we can //verify it was actually run first final Set<MigrationPlugin> plugins = new LinkedHashSet<>(); MigrationPlugin plugin1 = mock( MigrationPlugin.class ); when( plugin1.getPhase() ).thenReturn( PluginPhase.MIGRATE ); when( plugin1.getName() ).thenReturn( name ); plugins.add( plugin1 ); final MigrationInfoSerialization migrationInfoSerialization = mock( MigrationInfoSerialization.class ); when(migrationInfoSerialization.getStatusMessage( name )).thenReturn( status ); final MigrationInfoCache migrationInfoCache = mock(MigrationInfoCache.class); DataMigrationManagerImpl migrationManager = new DataMigrationManagerImpl( plugins, migrationInfoSerialization, migrationInfoCache ); final String returnedStatus = migrationManager.getLastStatus( name ); assertEquals(status, returnedStatus); }
@Test public void testPluginByName() throws MigrationException { final Set<MigrationPlugin> plugins = new HashSet<>(); MigrationPlugin plugin1 = mock( MigrationPlugin.class ); when( plugin1.getPhase() ).thenReturn( PluginPhase.MIGRATE ); when( plugin1.getName() ).thenReturn( "plugin1" ); MigrationPlugin plugin2 = mock( MigrationPlugin.class ); when( plugin2.getPhase() ).thenReturn( PluginPhase.MIGRATE ); when( plugin2.getName() ).thenReturn( "plugin2" ); plugins.add( plugin1 ); plugins.add( plugin2 ); final MigrationInfoSerialization migrationInfoSerialization = mock( MigrationInfoSerialization.class ); final MigrationInfoCache migrationInfoCache = mock(MigrationInfoCache.class); DataMigrationManagerImpl migrationManager = new DataMigrationManagerImpl( plugins, migrationInfoSerialization,migrationInfoCache ); Set<String> pluginNames = migrationManager.getPluginNames(); assertEquals(2, pluginNames.size()); assertTrue(pluginNames.contains("plugin1")); assertTrue(pluginNames.contains("plugin2")); //now run them migrationManager.migrate("plugin1"); verify( plugin1 ).run(any(ProgressObserver.class)); }
@Test public void test2Plugins() throws MigrationException { final Set<MigrationPlugin> plugins = new HashSet<>(); MigrationPlugin plugin1 = mock( MigrationPlugin.class ); when( plugin1.getPhase() ).thenReturn( PluginPhase.MIGRATE ); when( plugin1.getName() ).thenReturn( "plugin1" ); MigrationPlugin plugin2 = mock( MigrationPlugin.class ); when( plugin2.getPhase() ).thenReturn( PluginPhase.MIGRATE ); when( plugin2.getName() ).thenReturn( "plugin2" ); plugins.add( plugin1 ); plugins.add( plugin2 ); final MigrationInfoSerialization migrationInfoSerialization = mock( MigrationInfoSerialization.class ); final MigrationInfoCache migrationInfoCache = mock(MigrationInfoCache.class); DataMigrationManagerImpl migrationManager = new DataMigrationManagerImpl( plugins, migrationInfoSerialization,migrationInfoCache ); Set<String> pluginNames = migrationManager.getPluginNames(); assertEquals(2, pluginNames.size()); assertTrue( pluginNames.contains( "plugin1" ) ); assertTrue(pluginNames.contains("plugin2")); //now run them migrationManager.migrate(); verify( plugin1 ).run(any(ProgressObserver.class)); verify( plugin2 ).run( any( ProgressObserver.class ) ); verify(migrationInfoCache,Mockito.times(2)).invalidateAll(); }
@Test public void testExecutionOrder() throws MigrationException { //linked hash set is intentional here. For iteration order we can boostrap to come second so we can //verify it was actually run first final Set<MigrationPlugin> plugins = new LinkedHashSet<>(); MigrationPlugin plugin1 = mock( MigrationPlugin.class ); when( plugin1.getPhase() ).thenReturn( PluginPhase.MIGRATE ); when( plugin1.getName() ).thenReturn( "plugin1" ); //boostrap plugin, should run first MigrationPlugin plugin2 = mock( MigrationPlugin.class ); when( plugin2.getPhase() ).thenReturn( PluginPhase.BOOTSTRAP ); when( plugin2.getName() ).thenReturn( "plugin2" ); plugins.add( plugin1 ); plugins.add( plugin2 ); final MigrationInfoSerialization migrationInfoSerialization = mock( MigrationInfoSerialization.class ); final MigrationInfoCache migrationInfoCache = mock(MigrationInfoCache.class); DataMigrationManagerImpl migrationManager = new DataMigrationManagerImpl( plugins, migrationInfoSerialization, migrationInfoCache ); Set<String> pluginNames = migrationManager.getPluginNames(); assertEquals( 2, pluginNames.size() ); assertTrue( pluginNames.contains( "plugin1" ) ); assertTrue( pluginNames.contains( "plugin2" ) ); //now run them migrationManager.migrate(); //we want to verify the bootsrap plugin was called first InOrder inOrderVerification = inOrder( plugin1, plugin2 ); inOrderVerification.verify( plugin2 ).run( any( ProgressObserver.class ) ); inOrderVerification.verify( plugin1 ).run( any( ProgressObserver.class ) ); verify(migrationInfoCache, Mockito.times(2)).invalidateAll(); }
/** * Happy path of version reset */ @Test public void testResetToVersion() { final String name = "plugin1"; final int version = 10; //linked hash set is intentional here. For iteration order we can boostrap to come second so we can //verify it was actually run first final Set<MigrationPlugin> plugins = new LinkedHashSet<>(); MigrationPlugin plugin1 = mock( MigrationPlugin.class ); when( plugin1.getPhase() ).thenReturn( PluginPhase.MIGRATE ); when( plugin1.getName() ).thenReturn( name ); when( plugin1.getMaxVersion() ).thenReturn( version ); plugins.add( plugin1 ); final MigrationInfoSerialization migrationInfoSerialization = mock( MigrationInfoSerialization.class ); final MigrationInfoCache migrationInfoCache = mock(MigrationInfoCache.class); DataMigrationManagerImpl migrationManager = new DataMigrationManagerImpl( plugins, migrationInfoSerialization, migrationInfoCache ); migrationManager.resetToVersion( name, 0 ); verify( migrationInfoSerialization ).setVersion( name, 0 ); migrationManager.resetToVersion( name, version ); verify( migrationInfoSerialization ).setVersion( name, version ); }
@Test public void testPluginExists() throws MigrationException { final Set<MigrationPlugin> plugins = new HashSet<>(); MigrationPlugin plugin1 = mock( MigrationPlugin.class ); when( plugin1.getPhase() ).thenReturn( PluginPhase.MIGRATE ); when( plugin1.getName() ).thenReturn( "plugin1" ); MigrationPlugin plugin2 = mock( MigrationPlugin.class ); when( plugin2.getPhase() ).thenReturn( PluginPhase.MIGRATE ); when( plugin2.getName() ).thenReturn( "plugin2" ); plugins.add( plugin1 ); plugins.add( plugin2 ); final MigrationInfoSerialization migrationInfoSerialization = mock( MigrationInfoSerialization.class ); final MigrationInfoCache migrationInfoCache = mock(MigrationInfoCache.class); DataMigrationManagerImpl migrationManager = new DataMigrationManagerImpl( plugins, migrationInfoSerialization,migrationInfoCache ); Set<String> pluginNames = migrationManager.getPluginNames(); assertEquals(2, pluginNames.size()); assertTrue(pluginNames.contains("plugin1")); assertTrue(pluginNames.contains("plugin2")); //now run them assertTrue( migrationManager.pluginExists("plugin1") ); assertTrue( migrationManager.pluginExists("plugin2") ); assertFalse( migrationManager.pluginExists("plugin3") ); }
@Test public void testRunning() throws MigrationException { final Set<MigrationPlugin> plugins = new HashSet<>(); MigrationPlugin plugin1 = mock( MigrationPlugin.class ); when( plugin1.getName() ).thenReturn( "plugin1" ); when( plugin1.getPhase() ).thenReturn( PluginPhase.MIGRATE ); plugins.add( plugin1 ); final MigrationInfoSerialization migrationInfoSerialization = mock( MigrationInfoSerialization.class ); when( migrationInfoSerialization.getStatusCode( "plugin1" ) ) .thenReturn( DataMigrationManagerImpl.StatusCode.RUNNING.status ); final MigrationInfoCache migrationInfoCache = mock(MigrationInfoCache.class); DataMigrationManagerImpl migrationManager = new DataMigrationManagerImpl( plugins, migrationInfoSerialization, migrationInfoCache ); boolean status = migrationManager.isRunning(); assertTrue( "Status is set", status ); when( migrationInfoSerialization.getStatusCode( "plugin1" ) ) .thenReturn( DataMigrationManagerImpl.StatusCode.COMPLETE.status ); status = migrationManager.isRunning(); assertFalse( "Status is not running", status ); when( migrationInfoSerialization.getStatusCode( "plugin1" ) ) .thenReturn( DataMigrationManagerImpl.StatusCode.ERROR.status ); status = migrationManager.isRunning(); assertFalse( "Status is not running", status ); }