Repository: ambari Updated Branches: refs/heads/trunk e88ca22cf -> ddb201f8c
AMBARI-16836 : View Instance: Data Migration. (Nitiraj Rathore via dipayanb) Project: http://git-wip-us.apache.org/repos/asf/ambari/repo Commit: http://git-wip-us.apache.org/repos/asf/ambari/commit/ddb201f8 Tree: http://git-wip-us.apache.org/repos/asf/ambari/tree/ddb201f8 Diff: http://git-wip-us.apache.org/repos/asf/ambari/diff/ddb201f8 Branch: refs/heads/trunk Commit: ddb201f8ce8ecdc2a563a5ae69c071655e481b04 Parents: e88ca22 Author: Dipayan Bhowmick <[email protected]> Authored: Thu Jun 9 17:53:44 2016 +0530 Committer: Dipayan Bhowmick <[email protected]> Committed: Thu Jun 9 17:54:14 2016 +0530 ---------------------------------------------------------------------- .../api/services/ViewDataMigrationService.java | 143 ++---------- .../view/ViewDataMigrationContextImpl.java | 44 +++- .../server/view/ViewDataMigrationUtility.java | 228 +++++++++++++++++++ .../ambari/server/view/ViewExtractor.java | 7 +- .../apache/ambari/server/view/ViewRegistry.java | 147 +++++++++++- .../main/python/ambari_server/serverUpgrade.py | 5 - .../services/ViewDataMigrationServiceTest.java | 178 ++------------- .../view/ViewDataMigrationContextImplTest.java | 55 ++--- .../view/ViewDataMigrationUtilityTest.java | 184 +++++++++++++++ .../ambari/server/view/ViewRegistryTest.java | 6 +- 10 files changed, 658 insertions(+), 339 deletions(-) ---------------------------------------------------------------------- http://git-wip-us.apache.org/repos/asf/ambari/blob/ddb201f8/ambari-server/src/main/java/org/apache/ambari/server/api/services/ViewDataMigrationService.java ---------------------------------------------------------------------- diff --git a/ambari-server/src/main/java/org/apache/ambari/server/api/services/ViewDataMigrationService.java b/ambari-server/src/main/java/org/apache/ambari/server/api/services/ViewDataMigrationService.java index c6846ce..4a71ce2 100644 --- a/ambari-server/src/main/java/org/apache/ambari/server/api/services/ViewDataMigrationService.java +++ b/ambari-server/src/main/java/org/apache/ambari/server/api/services/ViewDataMigrationService.java @@ -18,11 +18,9 @@ package org.apache.ambari.server.api.services; import org.apache.ambari.server.orm.entities.ViewInstanceEntity; -import org.apache.ambari.server.view.ViewDataMigrationContextImpl; +import org.apache.ambari.server.view.ViewDataMigrationUtility; import org.apache.ambari.server.view.ViewRegistry; -import org.apache.ambari.view.migration.ViewDataMigrationContext; import org.apache.ambari.view.migration.ViewDataMigrationException; -import org.apache.ambari.view.migration.ViewDataMigrator; import org.apache.commons.logging.Log; import org.apache.commons.logging.LogFactory; @@ -31,7 +29,6 @@ import javax.ws.rs.Path; import javax.ws.rs.PathParam; import javax.ws.rs.WebApplicationException; import javax.ws.rs.core.Response; -import java.util.Map; /** * Service responsible for data migration between view instances. @@ -57,7 +54,15 @@ public class ViewDataMigrationService extends BaseService { */ private final String instanceName; - private ViewRegistry viewRegistry; + /** + * The singleton view registry. + */ + ViewRegistry viewRegistry; + + /** + * The view data migration utility. + */ + private ViewDataMigrationUtility viewDataMigrationUtility; /** * Constructor. @@ -77,8 +82,8 @@ public class ViewDataMigrationService extends BaseService { * Migrates view instance persistence data from origin view instance * specified in the path params. * - * @param originViewVersion the origin view version - * @param originInstanceName the origin view instance name + * @param originViewVersion the origin view version + * @param originInstanceName the origin view instance name */ @PUT @Path("{originVersion}/{originInstanceName}") @@ -93,127 +98,25 @@ public class ViewDataMigrationService extends BaseService { LOG.info("Data Migration to view instance " + viewName + "/" + viewVersion + "/" + instanceName + " from " + viewName + "/" + originViewVersion + "/" + originInstanceName); - ViewInstanceEntity instanceDefinition = getViewInstanceEntity(viewName, viewVersion, instanceName); - ViewInstanceEntity originInstanceDefinition = getViewInstanceEntity(viewName, originViewVersion, originInstanceName); + ViewInstanceEntity instanceDefinition = viewRegistry.getInstanceDefinition( + viewName, viewVersion, instanceName); + ViewInstanceEntity originInstanceDefinition = viewRegistry.getInstanceDefinition( + viewName, originViewVersion, originInstanceName); - ViewDataMigrationContextImpl migrationContext = getViewDataMigrationContext(instanceDefinition, originInstanceDefinition); - - ViewDataMigrator dataMigrator = getViewDataMigrator(instanceDefinition, migrationContext); - - LOG.debug("Running before-migration hook"); - if (!dataMigrator.beforeMigration()) { - String msg = "View " + viewName + "/" + viewVersion + "/" + instanceName + " canceled the migration process"; - - LOG.error(msg); - throw new ViewDataMigrationException(msg); - } - - Map<String, Class> originClasses = migrationContext.getOriginEntityClasses(); - Map<String, Class> currentClasses = migrationContext.getCurrentEntityClasses(); - for (Map.Entry<String, Class> originEntity : originClasses.entrySet()) { - LOG.debug("Migrating persistence entity " + originEntity.getKey()); - if (currentClasses.containsKey(originEntity.getKey())) { - Class entity = currentClasses.get(originEntity.getKey()); - dataMigrator.migrateEntity(originEntity.getValue(), entity); - } else { - LOG.debug("Entity " + originEntity.getKey() + " not found in target view"); - dataMigrator.migrateEntity(originEntity.getValue(), null); - } - } - - LOG.debug("Migrating instance data"); - dataMigrator.migrateInstanceData(); - - LOG.debug("Running after-migration hook"); - dataMigrator.afterMigration(); - - LOG.debug("Copying user permissions"); - viewRegistry.copyPrivileges(originInstanceDefinition, instanceDefinition); + getViewDataMigrationUtility().migrateData(instanceDefinition, originInstanceDefinition, false); Response.ResponseBuilder builder = Response.status(Response.Status.OK); return builder.build(); } - protected ViewDataMigrationContextImpl getViewDataMigrationContext(ViewInstanceEntity instanceDefinition, ViewInstanceEntity originInstanceDefinition) { - return new ViewDataMigrationContextImpl( - originInstanceDefinition, instanceDefinition); - } - - protected ViewInstanceEntity getViewInstanceEntity(String viewName, String viewVersion, String instanceName) { - return viewRegistry.getInstanceDefinition(viewName, viewVersion, instanceName); - } - - /** - * Get the migrator instance for view instance with injected migration context. - * If versions of instances are same returns copy-all-data migrator. - * If versions are different, loads the migrator from the current view (view should - * contain ViewDataMigrator implementation, otherwise exception will be raised). - * - * @param currentInstanceDefinition the current view instance definition - * @param migrationContext the migration context to inject into migrator - * @throws ViewDataMigrationException if view does not support migration - * @return the data migration instance - */ - protected ViewDataMigrator getViewDataMigrator(ViewInstanceEntity currentInstanceDefinition, - ViewDataMigrationContextImpl migrationContext) - throws ViewDataMigrationException { - ViewDataMigrator dataMigrator; - - LOG.info("Migrating " + viewName + "/" + viewVersion + "/" + instanceName + - " data from " + migrationContext.getOriginDataVersion() + " to " + - migrationContext.getCurrentDataVersion() + " data version"); - - if (migrationContext.getOriginDataVersion() == migrationContext.getCurrentDataVersion()) { - - LOG.info("Instances of same version, copying all data."); - dataMigrator = new CopyAllDataMigrator(migrationContext); - } else { - try { - dataMigrator = currentInstanceDefinition.getDataMigrator(migrationContext); - if (dataMigrator == null) { - throw new ViewDataMigrationException("A view instance " + - viewName + "/" + viewVersion + "/" + instanceName + " does not support migration."); - } - LOG.debug("Data migrator loaded"); - } catch (ClassNotFoundException e) { - String msg = "Caught exception loading data migrator of " + viewName + "/" + viewVersion + "/" + instanceName; - - LOG.error(msg, e); - throw new RuntimeException(msg); - } + protected ViewDataMigrationUtility getViewDataMigrationUtility() { + if (viewDataMigrationUtility == null) { + viewDataMigrationUtility = new ViewDataMigrationUtility(viewRegistry); } - return dataMigrator; + return viewDataMigrationUtility; } - /** - * The data migrator implementation that copies all data without modification. - * Used to copy data between instances of same version. - */ - public static class CopyAllDataMigrator implements ViewDataMigrator { - private ViewDataMigrationContext migrationContext; - - public CopyAllDataMigrator(ViewDataMigrationContext migrationContext) { - this.migrationContext = migrationContext; - } - - @Override - public boolean beforeMigration() { - return true; - } - - @Override - public void afterMigration() { - } - - @Override - public void migrateEntity(Class originEntityClass, Class currentEntityClass) - throws ViewDataMigrationException { - migrationContext.copyAllObjects(originEntityClass, currentEntityClass); - } - - @Override - public void migrateInstanceData() { - migrationContext.copyAllInstanceData(); - } + protected void setViewDataMigrationUtility(ViewDataMigrationUtility viewDataMigrationUtility) { + this.viewDataMigrationUtility = viewDataMigrationUtility; } } http://git-wip-us.apache.org/repos/asf/ambari/blob/ddb201f8/ambari-server/src/main/java/org/apache/ambari/server/view/ViewDataMigrationContextImpl.java ---------------------------------------------------------------------- diff --git a/ambari-server/src/main/java/org/apache/ambari/server/view/ViewDataMigrationContextImpl.java b/ambari-server/src/main/java/org/apache/ambari/server/view/ViewDataMigrationContextImpl.java index 94e3e28..909b3a1 100644 --- a/ambari-server/src/main/java/org/apache/ambari/server/view/ViewDataMigrationContextImpl.java +++ b/ambari-server/src/main/java/org/apache/ambari/server/view/ViewDataMigrationContextImpl.java @@ -20,6 +20,7 @@ package org.apache.ambari.server.view; import com.google.inject.Guice; import com.google.inject.Injector; +import com.google.inject.persist.Transactional; import org.apache.ambari.server.orm.entities.ViewEntity; import org.apache.ambari.server.orm.entities.ViewInstanceDataEntity; import org.apache.ambari.server.orm.entities.ViewInstanceEntity; @@ -119,6 +120,7 @@ public class ViewDataMigrationContextImpl implements ViewDataMigrationContext { } @Override + @Transactional public void putCurrentInstanceData(String user, String key, String value) { putInstanceData(currentInstanceDefinition, user, key, value); } @@ -135,6 +137,7 @@ public class ViewDataMigrationContextImpl implements ViewDataMigrationContext { } @Override + @Transactional public void copyAllObjects(Class originEntityClass, Class currentEntityClass, EntityConverter entityConverter) throws ViewDataMigrationException { try{ @@ -209,6 +212,7 @@ public class ViewDataMigrationContextImpl implements ViewDataMigrationContext { } @Override + @Transactional public void putOriginInstanceData(String user, String key, String value) { putInstanceData(originInstanceDefinition, user, key, value); } @@ -227,15 +231,37 @@ public class ViewDataMigrationContextImpl implements ViewDataMigrationContext { * @param value the value */ private static void putInstanceData(ViewInstanceEntity instanceDefinition, String user, String name, String value) { - ViewInstanceDataEntity viewInstanceDataEntity = new ViewInstanceDataEntity(); - viewInstanceDataEntity.setViewName(instanceDefinition.getViewName()); - viewInstanceDataEntity.setViewInstanceName(instanceDefinition.getName()); - viewInstanceDataEntity.setName(name); - viewInstanceDataEntity.setUser(user); - viewInstanceDataEntity.setValue(value); - viewInstanceDataEntity.setViewInstanceEntity(instanceDefinition); - - instanceDefinition.getData().add(viewInstanceDataEntity); + ViewInstanceDataEntity oldInstanceDataEntity = getInstanceData(instanceDefinition, user, name); + if (oldInstanceDataEntity != null) { + instanceDefinition.getData().remove(oldInstanceDataEntity); + } + + ViewInstanceDataEntity instanceDataEntity = new ViewInstanceDataEntity(); + instanceDataEntity.setViewName(instanceDefinition.getViewName()); + instanceDataEntity.setViewInstanceName(instanceDefinition.getName()); + instanceDataEntity.setName(name); + instanceDataEntity.setUser(user); + instanceDataEntity.setValue(value); + instanceDataEntity.setViewInstanceEntity(instanceDefinition); + + instanceDefinition.getData().add(instanceDataEntity); + } + + /** + * Get the instance data entity for the given key and user. + * + * @param user owner of the data + * @param key the key + * @return the instance data entity associated with the given key and user + */ + private static ViewInstanceDataEntity getInstanceData(ViewInstanceEntity instanceDefinition, String user, String key) { + for (ViewInstanceDataEntity viewInstanceDataEntity : instanceDefinition.getData()) { + if (viewInstanceDataEntity.getName().equals(key) && + viewInstanceDataEntity.getUser().equals(user)) { + return viewInstanceDataEntity; + } + } + return null; } /** http://git-wip-us.apache.org/repos/asf/ambari/blob/ddb201f8/ambari-server/src/main/java/org/apache/ambari/server/view/ViewDataMigrationUtility.java ---------------------------------------------------------------------- diff --git a/ambari-server/src/main/java/org/apache/ambari/server/view/ViewDataMigrationUtility.java b/ambari-server/src/main/java/org/apache/ambari/server/view/ViewDataMigrationUtility.java new file mode 100644 index 0000000..15318c0 --- /dev/null +++ b/ambari-server/src/main/java/org/apache/ambari/server/view/ViewDataMigrationUtility.java @@ -0,0 +1,228 @@ +/** + * Licensed to the Apache Software Foundation (ASF) under one + * or more contributor license agreements. See the NOTICE file + * distributed with this work for additional information + * regarding copyright ownership. The ASF licenses this file + * to you under the Apache License, Version 2.0 (the + * "License"); you may not use this file except in compliance + * with the License. You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +package org.apache.ambari.server.view; + +import org.apache.ambari.server.orm.entities.ViewInstanceEntity; +import org.apache.ambari.view.PersistenceException; +import org.apache.ambari.view.ViewInstanceDefinition; +import org.apache.ambari.view.migration.ViewDataMigrationContext; +import org.apache.ambari.view.migration.ViewDataMigrationException; +import org.apache.ambari.view.migration.ViewDataMigrator; +import org.slf4j.Logger; +import org.slf4j.LoggerFactory; +import java.util.Map; + +/** + * Helper class for view data migration. + */ +public class ViewDataMigrationUtility { + + /** + * The logger. + */ + protected final static Logger LOG = LoggerFactory.getLogger(ViewDataMigrationUtility.class); + + /** + * The View Registry. + */ + private ViewRegistry viewRegistry; + + /** + * Constructor. + * @param viewRegistry the view registry + */ + public ViewDataMigrationUtility(ViewRegistry viewRegistry) { + this.viewRegistry = viewRegistry; + } + + /** + * Migrates data from source to target instance + * @param targetInstanceDefinition target instance entity + * @param sourceInstanceDefinition source instance entity + * + * @throws ViewDataMigrationException when view does not support migration or an error during migration occurs. + */ + public void migrateDataOnce(ViewInstanceEntity targetInstanceDefinition, ViewInstanceEntity sourceInstanceDefinition) + throws ViewDataMigrationException { + ViewDataMigrationContextImpl migrationContext = getViewDataMigrationContext(targetInstanceDefinition, sourceInstanceDefinition); + } + + /** + * Migrates data from source to target instance + * @param targetInstanceDefinition target instance entity + * @param sourceInstanceDefinition source instance entity + * @param migrateOnce cancel if previously migrated + * + * @throws ViewDataMigrationException when view does not support migration or an error during migration occurs. + */ + public void migrateData(ViewInstanceEntity targetInstanceDefinition, ViewInstanceEntity sourceInstanceDefinition, + boolean migrateOnce) + throws ViewDataMigrationException { + ViewDataMigrationContextImpl migrationContext = getViewDataMigrationContext(targetInstanceDefinition, sourceInstanceDefinition); + + if (migrateOnce) { + if (!isTargetEmpty(migrationContext)) { + LOG.error("Migration canceled because target instance is not empty"); + return; + } + } + + ViewDataMigrator dataMigrator = getViewDataMigrator(targetInstanceDefinition, migrationContext); + + LOG.debug("Running before-migration hook"); + if (!dataMigrator.beforeMigration()) { + String msg = "View " + targetInstanceDefinition.getInstanceName() + " canceled the migration process"; + + LOG.error(msg); + throw new ViewDataMigrationException(msg); + } + + Map<String, Class> originClasses = migrationContext.getOriginEntityClasses(); + Map<String, Class> currentClasses = migrationContext.getCurrentEntityClasses(); + for (Map.Entry<String, Class> originEntity : originClasses.entrySet()) { + LOG.debug("Migrating persistence entity " + originEntity.getKey()); + if (currentClasses.containsKey(originEntity.getKey())) { + Class entity = currentClasses.get(originEntity.getKey()); + dataMigrator.migrateEntity(originEntity.getValue(), entity); + } else { + LOG.debug("Entity " + originEntity.getKey() + " not found in target view"); + dataMigrator.migrateEntity(originEntity.getValue(), null); + } + } + + LOG.debug("Migrating instance data"); + dataMigrator.migrateInstanceData(); + + LOG.debug("Running after-migration hook"); + dataMigrator.afterMigration(); + + LOG.debug("Copying user permissions"); + viewRegistry.copyPrivileges(sourceInstanceDefinition, targetInstanceDefinition); + + migrationContext.putCurrentInstanceData("upgrade", "upgradedFrom", sourceInstanceDefinition.getViewEntity().getVersion()); + } + + private boolean isTargetEmpty(ViewDataMigrationContext migrationContext) { + if (migrationContext.getCurrentInstanceDataByUser().size() > 0) { + return false; + } + + try { + for (Class entity : migrationContext.getCurrentEntityClasses().values()) { + if (migrationContext.getCurrentDataStore().findAll(entity, null).size() > 0) { + return false; + } + } + } catch (PersistenceException e) { + ViewInstanceDefinition current = migrationContext.getCurrentInstanceDefinition(); + LOG.error("Persistence exception while check if instance is empty: " + + current.getViewDefinition().getViewName() + "{" + current.getViewDefinition().getVersion() + "}/" + + current.getInstanceName(), e); + } + + return true; + } + + /** + * Create the data migration context for DataMigrator to access data of current + * and origin instances. + * @param targetInstanceDefinition target instance definition + * @param sourceInstanceDefinition source instance definition + * @return data migration context + */ + protected ViewDataMigrationContextImpl getViewDataMigrationContext(ViewInstanceEntity targetInstanceDefinition, + ViewInstanceEntity sourceInstanceDefinition) { + return new ViewDataMigrationContextImpl(sourceInstanceDefinition, targetInstanceDefinition); + } + + /** + * Get the migrator instance for view instance with injected migration context. + * If versions of instances are same returns copy-all-data migrator. + * If versions are different, loads the migrator from the current view (view should + * contain ViewDataMigrator implementation, otherwise exception will be raised). + * + * @param currentInstanceDefinition the current view instance definition + * @param migrationContext the migration context to inject into migrator + * @throws ViewDataMigrationException if view does not support migration + * @return the data migration instance + */ + protected ViewDataMigrator getViewDataMigrator(ViewInstanceEntity currentInstanceDefinition, + ViewDataMigrationContextImpl migrationContext) + throws ViewDataMigrationException { + ViewDataMigrator dataMigrator; + + LOG.info("Migrating " + currentInstanceDefinition.getInstanceName() + + " data from " + migrationContext.getOriginDataVersion() + " to " + + migrationContext.getCurrentDataVersion() + " data version"); + + if (migrationContext.getOriginDataVersion() == migrationContext.getCurrentDataVersion()) { + + LOG.info("Instances of same version, copying all data."); + dataMigrator = new CopyAllDataMigrator(migrationContext); + } else { + try { + dataMigrator = currentInstanceDefinition.getDataMigrator(migrationContext); + if (dataMigrator == null) { + throw new ViewDataMigrationException("A view instance " + + currentInstanceDefinition.getInstanceName() + " does not support migration."); + } + LOG.debug("Data migrator loaded"); + } catch (ClassNotFoundException e) { + String msg = "Caught exception loading data migrator of " + currentInstanceDefinition.getInstanceName(); + + LOG.error(msg, e); + throw new RuntimeException(msg); + } + } + return dataMigrator; + } + + + /** + * The data migrator implementation that copies all data without modification. + * Used to copy data between instances of same version. + */ + public static class CopyAllDataMigrator implements ViewDataMigrator { + private ViewDataMigrationContext migrationContext; + + public CopyAllDataMigrator(ViewDataMigrationContext migrationContext) { + this.migrationContext = migrationContext; + } + + @Override + public boolean beforeMigration() { + return true; + } + + @Override + public void afterMigration() { + } + + @Override + public void migrateEntity(Class originEntityClass, Class currentEntityClass) + throws ViewDataMigrationException { + migrationContext.copyAllObjects(originEntityClass, currentEntityClass); + } + + @Override + public void migrateInstanceData() { + migrationContext.copyAllInstanceData(); + } + } +} http://git-wip-us.apache.org/repos/asf/ambari/blob/ddb201f8/ambari-server/src/main/java/org/apache/ambari/server/view/ViewExtractor.java ---------------------------------------------------------------------- diff --git a/ambari-server/src/main/java/org/apache/ambari/server/view/ViewExtractor.java b/ambari-server/src/main/java/org/apache/ambari/server/view/ViewExtractor.java index 3550f98..3425691 100644 --- a/ambari-server/src/main/java/org/apache/ambari/server/view/ViewExtractor.java +++ b/ambari-server/src/main/java/org/apache/ambari/server/view/ViewExtractor.java @@ -19,6 +19,7 @@ package org.apache.ambari.server.view; import org.apache.ambari.server.orm.entities.ViewEntity; import org.apache.ambari.server.view.configuration.ViewConfig; +import org.apache.commons.io.FileUtils; import org.slf4j.Logger; import org.slf4j.LoggerFactory; @@ -72,9 +73,13 @@ public class ViewExtractor { String archivePath = archiveDir.getAbsolutePath(); try { + // Remove directory if jar was updated since last extracting + if (archiveDir.exists() && viewArchive.lastModified() > archiveDir.lastModified()) { + FileUtils.deleteDirectory(archiveDir); + } + // Skip if the archive has already been extracted if (!archiveDir.exists()) { - String msg = "Creating archive folder " + archivePath + "."; view.setStatusDetail(msg); http://git-wip-us.apache.org/repos/asf/ambari/blob/ddb201f8/ambari-server/src/main/java/org/apache/ambari/server/view/ViewRegistry.java ---------------------------------------------------------------------- diff --git a/ambari-server/src/main/java/org/apache/ambari/server/view/ViewRegistry.java b/ambari-server/src/main/java/org/apache/ambari/server/view/ViewRegistry.java index bda1079..90144e2 100644 --- a/ambari-server/src/main/java/org/apache/ambari/server/view/ViewRegistry.java +++ b/ambari-server/src/main/java/org/apache/ambari/server/view/ViewRegistry.java @@ -97,14 +97,17 @@ import org.apache.ambari.view.ViewResourceHandler; import org.apache.ambari.view.cluster.Cluster; import org.apache.ambari.view.events.Event; import org.apache.ambari.view.events.Listener; +import org.apache.ambari.view.migration.ViewDataMigrationException; import org.apache.ambari.view.validation.Validator; import org.apache.log4j.PropertyConfigurator; import org.slf4j.Logger; import org.slf4j.LoggerFactory; +import org.xml.sax.SAXException; import javax.inject.Inject; import javax.inject.Provider; import javax.inject.Singleton; +import javax.xml.bind.JAXBException; import java.beans.IntrospectionException; import java.io.File; import java.io.FileInputStream; @@ -192,6 +195,11 @@ public class ViewRegistry { protected final static Logger LOG = LoggerFactory.getLogger(ViewRegistry.class); /** + * View Data Migration Utility + */ + protected ViewDataMigrationUtility viewDataMigrationUtility; + + /** * View data access object. */ @Inject @@ -305,7 +313,6 @@ public class ViewRegistry { @Inject RemoteAmbariClusterDAO remoteAmbariClusterDAO; - // ----- Constructors ----------------------------------------------------- /** @@ -709,12 +716,14 @@ public class ViewRegistry { } List<PrivilegeEntity> sourceInstancePrivileges = privilegeDAO.findByResourceId(sourceInstanceEntity.getResource().getId()); - for (PrivilegeEntity privilegeEntity : sourceInstancePrivileges) { - privilegeDAO.detach(privilegeEntity); - privilegeEntity.setResource(targetInstanceEntity.getResource()); - privilegeEntity.setId(null); - privilegeDAO.create(privilegeEntity); - privilegeEntity.getPrincipal().getPrivileges().add(privilegeEntity); + for (PrivilegeEntity sourcePrivilege : sourceInstancePrivileges) { + PrivilegeEntity targetPrivilege = new PrivilegeEntity(); + targetPrivilege.setPrincipal(sourcePrivilege.getPrincipal()); + targetPrivilege.setResource(targetInstanceEntity.getResource()); + targetPrivilege.setPermission(sourcePrivilege.getPermission()); + privilegeDAO.create(targetPrivilege); + + targetPrivilege.getPrincipal().getPrivileges().add(sourcePrivilege); } } @@ -1582,6 +1591,7 @@ public class ViewRegistry { @Override public void run() { readViewArchive(viewDefinition, archiveFile, extractedArchiveDirFile, serverVersion); + migrateDataFromPreviousVersion(viewDefinition, serverVersion); } }); } @@ -1593,6 +1603,13 @@ public class ViewRegistry { } } + for(ViewEntity view : getDefinitions()) { + if (view.getStatus() == ViewDefinition.ViewStatus.DEPLOYED) { + // migrate views that are not need extraction, for ones that need call will be done in the runnable. + migrateDataFromPreviousVersion(view, serverVersion); + } + } + if (useExecutor && extractionRunnables.size() > 0) { final ExecutorService executorService = getExecutorService(configuration); @@ -1630,7 +1647,7 @@ public class ViewRegistry { // extract the archive and get the class loader ClassLoader cl = extractor.extractViewArchive(viewDefinition, archiveFile, extractedArchiveDirFile); - configureViewLogging(viewDefinition,cl); + configureViewLogging(viewDefinition, cl); ViewConfig viewConfig = archiveUtility.getViewConfigFromExtractedArchive(extractedArchiveDirPath, configuration.isViewValidationEnabled()); @@ -1664,6 +1681,39 @@ public class ViewRegistry { } } + private void migrateDataFromPreviousVersion(ViewEntity viewDefinition, String serverVersion) { + if (!viewDefinitions.containsKey(viewDefinition.getName())) { // migrate only registered views to avoid recursive calls + LOG.debug("Cancel auto migration of not loaded view: " + viewDefinition.getName() + "."); + return; + } + try { + + for (ViewInstanceEntity instance : viewDefinition.getInstances()) { + LOG.debug("Try to migrate the data from previous version of: " + viewDefinition.getName() + "/" + + instance.getInstanceName() + "."); + ViewInstanceEntity latestUnregisteredView = getLatestUnregisteredInstance(serverVersion, instance); + + if (latestUnregisteredView != null) { + String instanceName = instance.getViewEntity().getName() + "/" + instance.getName(); + try { + LOG.info("Found previous version of the view instance " + instanceName + ": " + + latestUnregisteredView.getViewEntity().getName() + "/" + latestUnregisteredView.getName()); + getViewDataMigrationUtility().migrateData(instance, latestUnregisteredView, true); + LOG.info("View data migrated: " + viewDefinition.getName() + "."); + } catch (ViewDataMigrationException e) { + LOG.error("Error occurred during migration", e); + } + } + } + + } catch (Exception e) { + String msg = "Caught exception migrating data in view " + viewDefinition.getName(); + + setViewStatus(viewDefinition, ViewEntity.ViewStatus.ERROR, msg + " : " + e.getMessage()); + LOG.error(msg, e); + } + } + /** * copies non-log4j properties (like ambari.log.dir) from ambari's log4j.properties into view's log4j properties * and removes log4j specific properties (log4j.rootLogger) inside ambari's log4j.properties from view's log4j properties @@ -1972,8 +2022,89 @@ public class ViewRegistry { return url.substring(0,index); } + /** + * From all extracted views in the work directory finds ones that are present only in + * extracted version (not registered in the registry during startup). + * If jar is not exists, that means that this view is previous version of view. + * Finds latest between unregistered instances and returns it. + * + * @param serverVersion server version + * @param instance view instance entity + * @return latest unregistered instance of same name of same view. + */ + private ViewInstanceEntity getLatestUnregisteredInstance(String serverVersion, ViewInstanceEntity instance) + throws JAXBException, IOException, SAXException { + File viewDir = configuration.getViewsDir(); + String extractedArchivesPath = viewDir.getAbsolutePath() + + File.separator + EXTRACTED_ARCHIVES_DIR; + + File extractedArchivesDir = new File(extractedArchivesPath); + File[] extractedArchives = extractedArchivesDir.listFiles(); + + // find all view archives from previous Ambari versions + Map<ViewInstanceEntity, Long> unregInstancesTimestamps = new HashMap<>(); + if (extractedArchives != null) { + + for (File archiveDir : extractedArchives) { + if (archiveDir.isDirectory()) { + ViewConfig uViewConfig = archiveUtility.getViewConfigFromExtractedArchive(archiveDir.getPath(), false); + if (!uViewConfig.isSystem()) { + // load prev versions of same view + if (!uViewConfig.getName().equals(instance.getViewEntity().getViewName())) { + continue; + } + + // check if it's not registered yet. It means that jar file is not present while directory in + // work dir present, so maybe it's prev version of view. + if (viewDefinitions.containsKey(ViewEntity.getViewName(uViewConfig.getName(), uViewConfig.getVersion()))) { + continue; + } + + LOG.debug("Unregistered extracted view found: " + archiveDir.getPath()); + + ViewEntity uViewDefinition = new ViewEntity(uViewConfig, configuration, archiveDir.getPath()); + readViewArchive(uViewDefinition, archiveDir, archiveDir, serverVersion); + for (ViewInstanceEntity instanceEntity : uViewDefinition.getInstances()) { + LOG.debug(uViewDefinition.getName() + " instance found: " + instanceEntity.getInstanceName()); + unregInstancesTimestamps.put(instanceEntity, archiveDir.lastModified()); + } + } + } + } + } + + // Find latest previous version + long latestPrevInstanceTimestamp = 0; + ViewInstanceEntity latestPrevInstance = null; + for (ViewInstanceEntity unregInstance : unregInstancesTimestamps.keySet()) { + if (unregInstance.getName().equals(instance.getName())) { + if (unregInstancesTimestamps.get(unregInstance) > latestPrevInstanceTimestamp) { + latestPrevInstance = unregInstance; + latestPrevInstanceTimestamp = unregInstancesTimestamps.get(latestPrevInstance); + } + } + } + if (latestPrevInstance != null) { + LOG.debug("Previous version of " + instance.getViewEntity().getName() + "/" + instance.getName() + " found: " + + latestPrevInstance.getViewEntity().getName() + "/" + latestPrevInstance.getName()); + } else { + LOG.debug("Previous version of " + instance.getViewEntity().getName() + "/" + instance.getName() + " not found"); + } + return latestPrevInstance; + } + + protected ViewDataMigrationUtility getViewDataMigrationUtility() { + if (viewDataMigrationUtility == null) { + viewDataMigrationUtility = new ViewDataMigrationUtility(this); + } + return viewDataMigrationUtility; + } + + protected void setViewDataMigrationUtility(ViewDataMigrationUtility viewDataMigrationUtility) { + this.viewDataMigrationUtility = viewDataMigrationUtility; + } /** * Module for stand alone view registry. http://git-wip-us.apache.org/repos/asf/ambari/blob/ddb201f8/ambari-server/src/main/python/ambari_server/serverUpgrade.py ---------------------------------------------------------------------- diff --git a/ambari-server/src/main/python/ambari_server/serverUpgrade.py b/ambari-server/src/main/python/ambari_server/serverUpgrade.py index 483b79e..8bd7c51 100644 --- a/ambari-server/src/main/python/ambari_server/serverUpgrade.py +++ b/ambari-server/src/main/python/ambari_server/serverUpgrade.py @@ -404,11 +404,6 @@ def upgrade(args): for admin_views_dir in admin_views_dirs: shutil.rmtree(admin_views_dir) - # Remove ambari views directory for the rest of the jars, at the time of upgrade. At restart all jars present in Ambari will be extracted into work directory - views_dir = get_views_dir(properties) - for views in views_dir: - shutil.rmtree(views) - # check if ambari has obsolete LDAP configuration if properties.get_property(LDAP_PRIMARY_URL_PROPERTY) and not properties.get_property(IS_LDAP_CONFIGURED): args.warnings.append("Existing LDAP configuration is detected. You must run the \"ambari-server setup-ldap\" command to adjust existing LDAP configuration.") http://git-wip-us.apache.org/repos/asf/ambari/blob/ddb201f8/ambari-server/src/test/java/org/apache/ambari/server/api/services/ViewDataMigrationServiceTest.java ---------------------------------------------------------------------- diff --git a/ambari-server/src/test/java/org/apache/ambari/server/api/services/ViewDataMigrationServiceTest.java b/ambari-server/src/test/java/org/apache/ambari/server/api/services/ViewDataMigrationServiceTest.java index 1eb7ac2..39b2d96 100644 --- a/ambari-server/src/test/java/org/apache/ambari/server/api/services/ViewDataMigrationServiceTest.java +++ b/ambari-server/src/test/java/org/apache/ambari/server/api/services/ViewDataMigrationServiceTest.java @@ -17,21 +17,14 @@ */ package org.apache.ambari.server.api.services; -import junit.framework.Assert; -import org.apache.ambari.server.orm.entities.ViewEntity; import org.apache.ambari.server.orm.entities.ViewInstanceEntity; -import org.apache.ambari.server.view.ViewDataMigrationContextImpl; +import org.apache.ambari.server.view.ViewDataMigrationUtility; import org.apache.ambari.server.view.ViewRegistry; -import org.apache.ambari.view.migration.ViewDataMigrationContext; -import org.apache.ambari.view.migration.ViewDataMigrationException; -import org.apache.ambari.view.migration.ViewDataMigrator; -import org.junit.Before; import org.junit.Rule; import org.junit.Test; import org.junit.rules.ExpectedException; -import java.util.Collections; -import java.util.Map; +import javax.ws.rs.WebApplicationException; import static org.easymock.EasyMock.*; @@ -45,171 +38,38 @@ public class ViewDataMigrationServiceTest { private static String version1 = "1.0.0"; private static String version2 = "2.0.0"; - private static String xml_view_with_migrator_v2 = "<view>\n" + - " <name>" + viewName + "</name>\n" + - " <label>My View!</label>\n" + - " <version>" + version2 + "</version>\n" + - " <data-version>1</data-version>\n" + - " <data-migrator-class>org.apache.ambari.server.api.services.ViewDataMigrationServiceTest$MyDataMigrator</data-migrator-class>\n" + - " <instance>\n" + - " <name>" + instanceName + "</name>\n" + - " <label>My Instance 1!</label>\n" + - " </instance>\n" + - "</view>"; - - private static String xml_view_with_migrator_v1 = "<view>\n" + - " <name>" + viewName + "</name>\n" + - " <label>My View!</label>\n" + - " <version>" + version1 + "</version>\n" + - " <instance>\n" + - " <name>" + instanceName + "</name>\n" + - " <label>My Instance 1!</label>\n" + - " </instance>\n" + - "</view>"; - @Rule public ExpectedException thrown = ExpectedException.none(); - @Before - public void setUp() throws Exception { + @Test + public void testServiceMigrateCallAdmin() throws Exception { ViewRegistry viewRegistry = createNiceMock(ViewRegistry.class); expect(viewRegistry.checkAdmin()).andReturn(true).anyTimes(); - viewRegistry.copyPrivileges(anyObject(ViewInstanceEntity.class), anyObject(ViewInstanceEntity.class)); - expectLastCall().anyTimes(); replay(viewRegistry); ViewRegistry.initInstance(viewRegistry); - } - @Test - public void testMigrateDataSameVersions() throws Exception { - TestViewDataMigrationService service = new TestViewDataMigrationService(viewName, version2, instanceName); + ViewDataMigrationService service = new ViewDataMigrationService(viewName, version1, instanceName); - ViewDataMigrationContextImpl context = createNiceMock(ViewDataMigrationContextImpl.class); - expect(context.getOriginDataVersion()).andReturn(42); - expect(context.getCurrentDataVersion()).andReturn(42); - replay(context); - service.setMigrationContext(context); + ViewDataMigrationUtility migrationUtility = createStrictMock(ViewDataMigrationUtility.class); + migrationUtility.migrateData(anyObject(ViewInstanceEntity.class), anyObject(ViewInstanceEntity.class), eq(false)); + replay(migrationUtility); + service.setViewDataMigrationUtility(migrationUtility); - ViewDataMigrator migrator = service.getViewDataMigrator( - service.getViewInstanceEntity(viewName, version2, instanceName), context); + service.migrateData(version2, instanceName); - Assert.assertTrue(migrator instanceof ViewDataMigrationService.CopyAllDataMigrator); + verify(migrationUtility); } @Test - public void testMigrateDataDifferentVersions() throws Exception { - TestViewDataMigrationService service = new TestViewDataMigrationService(viewName, version2, instanceName); - - ViewDataMigrationContextImpl context = getViewDataMigrationContext(); - service.setMigrationContext(context); - - ViewDataMigrator migrator = createStrictMock(ViewDataMigrator.class); - expect(migrator.beforeMigration()).andReturn(true); - migrator.migrateEntity(anyObject(Class.class), anyObject(Class.class)); expectLastCall(); - migrator.migrateInstanceData(); expectLastCall(); - migrator.afterMigration(); expectLastCall(); - - replay(migrator); - service.setMigrator(migrator); - - service.migrateData(version1, instanceName); - - verify(migrator); - } - - @Test - public void testMigrateDataDifferentVersionsCancel() throws Exception { - TestViewDataMigrationService service = new TestViewDataMigrationService(viewName, version2, instanceName); - - ViewDataMigrationContextImpl context = getViewDataMigrationContext(); - service.setMigrationContext(context); - - ViewDataMigrator migrator = createStrictMock(ViewDataMigrator.class); - expect(migrator.beforeMigration()).andReturn(false); - - replay(migrator); - service.setMigrator(migrator); - - thrown.expect(ViewDataMigrationException.class); - service.migrateData(version1, instanceName); - } - - private static ViewDataMigrationContextImpl getViewDataMigrationContext() { - Map<String, Class> entities = Collections.<String, Class>singletonMap("MyEntityClass", Object.class); - ViewDataMigrationContextImpl context = createNiceMock(ViewDataMigrationContextImpl.class); - expect(context.getOriginDataVersion()).andReturn(2).anyTimes(); - expect(context.getCurrentDataVersion()).andReturn(1).anyTimes(); - expect(context.getOriginEntityClasses()).andReturn(entities).anyTimes(); - expect(context.getCurrentEntityClasses()).andReturn(entities).anyTimes(); - replay(context); - return context; - } - - //Migration service that avoids ViewRegistry and DB calls - private static class TestViewDataMigrationService extends ViewDataMigrationService { - - private ViewDataMigrator migrator; - private ViewDataMigrationContextImpl migrationContext; - - public TestViewDataMigrationService(String viewName, String viewVersion, String instanceName) { - super(viewName, viewVersion, instanceName); - } - - @Override - protected ViewInstanceEntity getViewInstanceEntity(String viewName, String viewVersion, String instanceName) { - ViewEntity viewEntity = createNiceMock(ViewEntity.class); - expect(viewEntity.getViewName()).andReturn(viewName); - expect(viewEntity.getVersion()).andReturn(viewVersion); - - replay(viewEntity); - - ViewInstanceEntity instanceEntity = createNiceMock(ViewInstanceEntity.class); - expect(instanceEntity.getViewEntity()).andReturn(viewEntity); - expect(instanceEntity.getViewName()).andReturn(viewName); - expect(instanceEntity.getInstanceName()).andReturn(instanceName); - - try { - ViewDataMigrator mockMigrator; - if (migrator == null) { - mockMigrator = createNiceMock(ViewDataMigrator.class); - } else { - mockMigrator = migrator; - } - expect(instanceEntity.getDataMigrator(anyObject(ViewDataMigrationContext.class))). - andReturn(mockMigrator); - } catch (Exception e) { - e.printStackTrace(); - } - - replay(instanceEntity); - return instanceEntity; - } - - @Override - protected ViewDataMigrationContextImpl getViewDataMigrationContext(ViewInstanceEntity instanceDefinition, - ViewInstanceEntity originInstanceDefinition) { - if (migrationContext == null) { - ViewDataMigrationContextImpl contextMock = createNiceMock(ViewDataMigrationContextImpl.class); - replay(contextMock); - return contextMock; - } - return migrationContext; - } - - public ViewDataMigrator getMigrator() { - return migrator; - } - - public void setMigrator(ViewDataMigrator migrator) { - this.migrator = migrator; - } + public void testServiceMigrateCallNotAdmin() throws Exception { + ViewRegistry viewRegistry = createNiceMock(ViewRegistry.class); + expect(viewRegistry.checkAdmin()).andReturn(false).anyTimes(); + replay(viewRegistry); + ViewRegistry.initInstance(viewRegistry); - public ViewDataMigrationContextImpl getMigrationContext() { - return migrationContext; - } + ViewDataMigrationService service = new ViewDataMigrationService(viewName, version1, instanceName); - public void setMigrationContext(ViewDataMigrationContextImpl migrationContext) { - this.migrationContext = migrationContext; - } + thrown.expect(WebApplicationException.class); + service.migrateData(version2, instanceName); } } http://git-wip-us.apache.org/repos/asf/ambari/blob/ddb201f8/ambari-server/src/test/java/org/apache/ambari/server/view/ViewDataMigrationContextImplTest.java ---------------------------------------------------------------------- diff --git a/ambari-server/src/test/java/org/apache/ambari/server/view/ViewDataMigrationContextImplTest.java b/ambari-server/src/test/java/org/apache/ambari/server/view/ViewDataMigrationContextImplTest.java index a636e0b..1ae7afb 100644 --- a/ambari-server/src/test/java/org/apache/ambari/server/view/ViewDataMigrationContextImplTest.java +++ b/ambari-server/src/test/java/org/apache/ambari/server/view/ViewDataMigrationContextImplTest.java @@ -30,9 +30,7 @@ import org.apache.ambari.view.migration.EntityConverter; import org.easymock.Capture; import org.junit.Test; -import java.util.Arrays; -import java.util.Collection; -import java.util.Map; +import java.util.*; import static org.easymock.EasyMock.*; @@ -93,34 +91,27 @@ public class ViewDataMigrationContextImplTest { ViewEntity entity2 = getViewEntityMock(VERSION_2); replay(entity1, entity2); - Capture<ViewInstanceDataEntity> capturedInstanceData1 = Capture.newInstance(); - Collection data1 = createNiceMock(Collection.class); - expect(data1.add(capture(capturedInstanceData1))).andReturn(true); - replay(data1); - - Capture<ViewInstanceDataEntity> capturedInstanceData2 = Capture.newInstance(); - Collection data2 = createStrictMock(Collection.class); - expect(data2.add(capture(capturedInstanceData2))).andReturn(true); - replay(data2); - ViewInstanceEntity instanceEntity1 = getViewInstanceEntityMock(entity1); - expect(instanceEntity1.getData()).andReturn(data1); + List<ViewInstanceDataEntity> data1 = new ArrayList<>(); + expect(instanceEntity1.getData()).andReturn(data1).anyTimes(); ViewInstanceEntity instanceEntity2 = getViewInstanceEntityMock(entity2); - expect(instanceEntity2.getData()).andReturn(data2); + List<ViewInstanceDataEntity> data2 = new ArrayList<>(); + expect(instanceEntity2.getData()).andReturn(data2).anyTimes(); replay(instanceEntity1, instanceEntity2); ViewDataMigrationContextImpl context = new TestViewDataMigrationContextImpl(instanceEntity1, instanceEntity2); context.putOriginInstanceData("user1", "key1", "val1"); context.putCurrentInstanceData("user2", "key2", "val2"); - verify(data2); - Assert.assertEquals("user1", capturedInstanceData1.getValue().getUser()); - Assert.assertEquals("key1", capturedInstanceData1.getValue().getName()); - Assert.assertEquals("val1", capturedInstanceData1.getValue().getValue()); + Assert.assertEquals(1, data1.size()); + Assert.assertEquals("user1", data1.get(0).getUser()); + Assert.assertEquals("key1", data1.get(0).getName()); + Assert.assertEquals("val1", data1.get(0).getValue()); - Assert.assertEquals("user2", capturedInstanceData2.getValue().getUser()); - Assert.assertEquals("key2", capturedInstanceData2.getValue().getName()); - Assert.assertEquals("val2", capturedInstanceData2.getValue().getValue()); + Assert.assertEquals(1, data2.size()); + Assert.assertEquals("user2", data2.get(0).getUser()); + Assert.assertEquals("key2", data2.get(0).getName()); + Assert.assertEquals("val2", data2.get(0).getValue()); } @Test @@ -215,26 +206,22 @@ public class ViewDataMigrationContextImplTest { dataEntity.setName("name1"); dataEntity.setValue("value1"); dataEntity.setUser("user1"); - Collection data1 = Arrays.asList(dataEntity); - - Capture<ViewInstanceDataEntity> capturedInstanceData = Capture.newInstance(); - Collection data2 = createStrictMock(Collection.class); - expect(data2.add(capture(capturedInstanceData))).andReturn(true); - replay(data2); + Collection<ViewInstanceDataEntity> data1 = Arrays.asList(dataEntity); + List<ViewInstanceDataEntity> data2 = new ArrayList<>(); ViewInstanceEntity instanceEntity1 = getViewInstanceEntityMock(entity1); - expect(instanceEntity1.getData()).andReturn(data1); + expect(instanceEntity1.getData()).andReturn(data1).anyTimes(); ViewInstanceEntity instanceEntity2 = getViewInstanceEntityMock(entity2); - expect(instanceEntity2.getData()).andReturn(data2); + expect(instanceEntity2.getData()).andReturn(data2).anyTimes(); replay(instanceEntity1, instanceEntity2); ViewDataMigrationContextImpl context = new TestViewDataMigrationContextImpl(instanceEntity1, instanceEntity2); context.copyAllInstanceData(); - verify(data2); - Assert.assertEquals("user1", capturedInstanceData.getValue().getUser()); - Assert.assertEquals("name1", capturedInstanceData.getValue().getName()); - Assert.assertEquals("value1", capturedInstanceData.getValue().getValue()); + Assert.assertEquals(1, data2.size()); + Assert.assertEquals("user1", data2.get(0).getUser()); + Assert.assertEquals("name1", data2.get(0).getName()); + Assert.assertEquals("value1", data2.get(0).getValue()); } @Test http://git-wip-us.apache.org/repos/asf/ambari/blob/ddb201f8/ambari-server/src/test/java/org/apache/ambari/server/view/ViewDataMigrationUtilityTest.java ---------------------------------------------------------------------- diff --git a/ambari-server/src/test/java/org/apache/ambari/server/view/ViewDataMigrationUtilityTest.java b/ambari-server/src/test/java/org/apache/ambari/server/view/ViewDataMigrationUtilityTest.java new file mode 100644 index 0000000..f997589 --- /dev/null +++ b/ambari-server/src/test/java/org/apache/ambari/server/view/ViewDataMigrationUtilityTest.java @@ -0,0 +1,184 @@ +/** + * Licensed to the Apache Software Foundation (ASF) under one + * or more contributor license agreements. See the NOTICE file + * distributed with this work for additional information + * regarding copyright ownership. The ASF licenses this file + * to you under the Apache License, Version 2.0 (the + * "License"); you may not use this file except in compliance + * with the License. You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +package org.apache.ambari.server.view; + +import junit.framework.Assert; +import org.apache.ambari.server.api.services.ViewDataMigrationService; +import org.apache.ambari.server.orm.entities.ViewEntity; +import org.apache.ambari.server.orm.entities.ViewInstanceEntity; +import org.apache.ambari.view.migration.ViewDataMigrationContext; +import org.apache.ambari.view.migration.ViewDataMigrationException; +import org.apache.ambari.view.migration.ViewDataMigrator; +import org.junit.Before; +import org.junit.Rule; +import org.junit.Test; +import org.junit.rules.ExpectedException; + +import java.util.Collections; +import java.util.HashMap; +import java.util.Map; + +import static org.easymock.EasyMock.*; +import static org.easymock.EasyMock.replay; +import static org.junit.Assert.*; + +/** + * ViewDataMigrationUtility Tests. + */ +public class ViewDataMigrationUtilityTest { + + private static String viewName = "MY_VIEW"; + private static String instanceName = "INSTANCE1"; + private static String version1 = "1.0.0"; + private static String version2 = "2.0.0"; + + @Rule + public ExpectedException thrown = ExpectedException.none(); + + ViewRegistry viewRegistry; + + @Before + public void setUp() throws Exception { + viewRegistry = createNiceMock(ViewRegistry.class); + viewRegistry.copyPrivileges(anyObject(ViewInstanceEntity.class), anyObject(ViewInstanceEntity.class)); + expectLastCall().anyTimes(); + replay(viewRegistry); + ViewRegistry.initInstance(viewRegistry); + } + + @Test + public void testMigrateDataSameVersions() throws Exception { + TestViewDataMigrationUtility migrationUtility = new TestViewDataMigrationUtility(viewRegistry); + + ViewDataMigrationContextImpl context = getViewDataMigrationContext(42, 42); + migrationUtility.setMigrationContext(context); + + ViewDataMigrator migrator = migrationUtility.getViewDataMigrator( + getInstanceDefinition(viewName, version2, instanceName), context); + + Assert.assertTrue(migrator instanceof ViewDataMigrationUtility.CopyAllDataMigrator); + } + + @Test + public void testMigrateDataDifferentVersions() throws Exception { + TestViewDataMigrationUtility migrationUtility = new TestViewDataMigrationUtility(viewRegistry); + + ViewDataMigrationContextImpl context = getViewDataMigrationContext(2, 1); + migrationUtility.setMigrationContext(context); + + ViewDataMigrator migrator = createStrictMock(ViewDataMigrator.class); + expect(migrator.beforeMigration()).andReturn(true); + migrator.migrateEntity(anyObject(Class.class), anyObject(Class.class)); expectLastCall(); + migrator.migrateInstanceData(); expectLastCall(); + migrator.afterMigration(); expectLastCall(); + + replay(migrator); + + ViewInstanceEntity targetInstance = getInstanceDefinition(viewName, version2, instanceName, migrator); + ViewInstanceEntity sourceInstance = getInstanceDefinition(viewName, version1, instanceName); + migrationUtility.migrateData(targetInstance, sourceInstance, false); + + verify(migrator); + } + + @Test + public void testMigrateDataDifferentVersionsCancel() throws Exception { + TestViewDataMigrationUtility migrationUtility = new TestViewDataMigrationUtility(viewRegistry); + + ViewDataMigrationContextImpl context = getViewDataMigrationContext(2, 1); + migrationUtility.setMigrationContext(context); + + ViewDataMigrator migrator = createStrictMock(ViewDataMigrator.class); + expect(migrator.beforeMigration()).andReturn(false); + + ViewInstanceEntity targetInstance = getInstanceDefinition(viewName, version2, instanceName, migrator); + ViewInstanceEntity sourceInstance = getInstanceDefinition(viewName, version1, instanceName); + + thrown.expect(ViewDataMigrationException.class); + migrationUtility.migrateData(targetInstance, sourceInstance, false); + } + + private static ViewDataMigrationContextImpl getViewDataMigrationContext(int currentVersion, int originVersion) { + Map<String, Class> entities = Collections.<String, Class>singletonMap("MyEntityClass", Object.class); + ViewDataMigrationContextImpl context = createNiceMock(ViewDataMigrationContextImpl.class); + expect(context.getOriginDataVersion()).andReturn(originVersion).anyTimes(); + expect(context.getCurrentDataVersion()).andReturn(currentVersion).anyTimes(); + expect(context.getOriginEntityClasses()).andReturn(entities).anyTimes(); + expect(context.getCurrentEntityClasses()).andReturn(entities).anyTimes(); + + expect(context.getCurrentInstanceDataByUser()).andReturn(new HashMap<String, Map<String, String>>()); + replay(context); + return context; + } + + private static class TestViewDataMigrationUtility extends ViewDataMigrationUtility { + private ViewDataMigrationContextImpl migrationContext; + + public TestViewDataMigrationUtility(ViewRegistry viewRegistry) { + super(viewRegistry); + } + + @Override + protected ViewDataMigrationContextImpl getViewDataMigrationContext(ViewInstanceEntity targetInstanceDefinition, + ViewInstanceEntity sourceInstanceDefinition) { + if (migrationContext == null) { + return super.getViewDataMigrationContext(targetInstanceDefinition, sourceInstanceDefinition); + } + return migrationContext; + } + + public ViewDataMigrationContextImpl getMigrationContext() { + return migrationContext; + } + + public void setMigrationContext(ViewDataMigrationContextImpl migrationContext) { + this.migrationContext = migrationContext; + } + } + + private ViewInstanceEntity getInstanceDefinition(String viewName, String viewVersion, String instanceName) { + ViewDataMigrator migrator = createNiceMock(ViewDataMigrator.class); + replay(migrator); + return getInstanceDefinition(viewName, viewVersion, instanceName, migrator); + } + + private ViewInstanceEntity getInstanceDefinition(String viewName, String viewVersion, String instanceName, + ViewDataMigrator migrator) { + ViewEntity viewEntity = createNiceMock(ViewEntity.class); + expect(viewEntity.getViewName()).andReturn(viewName); + expect(viewEntity.getVersion()).andReturn(viewVersion); + + replay(viewEntity); + + ViewInstanceEntity instanceEntity = createNiceMock(ViewInstanceEntity.class); + expect(instanceEntity.getViewEntity()).andReturn(viewEntity); + expect(instanceEntity.getViewName()).andReturn(viewName); + expect(instanceEntity.getInstanceName()).andReturn(instanceName); + + try { + expect(instanceEntity.getDataMigrator(anyObject(ViewDataMigrationContext.class))). + andReturn(migrator); + } catch (Exception e) { + e.printStackTrace(); + } + + replay(instanceEntity); + return instanceEntity; + } + +} http://git-wip-us.apache.org/repos/asf/ambari/blob/ddb201f8/ambari-server/src/test/java/org/apache/ambari/server/view/ViewRegistryTest.java ---------------------------------------------------------------------- diff --git a/ambari-server/src/test/java/org/apache/ambari/server/view/ViewRegistryTest.java b/ambari-server/src/test/java/org/apache/ambari/server/view/ViewRegistryTest.java index 5b24b19..6b560ee 100644 --- a/ambari-server/src/test/java/org/apache/ambari/server/view/ViewRegistryTest.java +++ b/ambari-server/src/test/java/org/apache/ambari/server/view/ViewRegistryTest.java @@ -334,12 +334,12 @@ public class ViewRegistryTest { jarFiles.put(viewArchive, viewJarFile); // set expectations - expect(configuration.getViewsDir()).andReturn(viewDir); + expect(configuration.getViewsDir()).andReturn(viewDir).anyTimes(); if (System.getProperty("os.name").contains("Windows")) { - expect(viewDir.getAbsolutePath()).andReturn("\\var\\lib\\ambari-server\\resources\\views"); + expect(viewDir.getAbsolutePath()).andReturn("\\var\\lib\\ambari-server\\resources\\views").anyTimes(); } else { - expect(viewDir.getAbsolutePath()).andReturn("/var/lib/ambari-server/resources/views"); + expect(viewDir.getAbsolutePath()).andReturn("/var/lib/ambari-server/resources/views").anyTimes(); } expect(configuration.getViewExtractionThreadPoolCoreSize()).andReturn(2).anyTimes();
