/*
* 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.upgrade;
import static org.junit.Assert.assertTrue;
import java.io.IOException;
import java.sql.DriverManager;
import java.sql.SQLException;
import java.sql.SQLNonTransientConnectionException;
import java.util.ArrayList;
import java.util.Arrays;
import java.util.Collection;
import java.util.List;
import java.util.Properties;
import java.util.Set;
import org.apache.ambari.server.configuration.Configuration;
import org.apache.ambari.server.controller.ControllerModule;
import org.apache.ambari.server.orm.DBAccessor;
import org.apache.ambari.server.orm.dao.BlueprintDAO;
import org.apache.ambari.server.orm.dao.ClusterDAO;
import org.apache.ambari.server.orm.dao.ClusterServiceDAO;
import org.apache.ambari.server.orm.dao.ClusterStateDAO;
import org.apache.ambari.server.orm.dao.ConfigGroupConfigMappingDAO;
import org.apache.ambari.server.orm.dao.ConfigGroupDAO;
import org.apache.ambari.server.orm.dao.ConfigGroupHostMappingDAO;
import org.apache.ambari.server.orm.dao.ExecutionCommandDAO;
import org.apache.ambari.server.orm.dao.HostComponentDesiredStateDAO;
import org.apache.ambari.server.orm.dao.HostComponentStateDAO;
import org.apache.ambari.server.orm.dao.HostConfigMappingDAO;
import org.apache.ambari.server.orm.dao.HostDAO;
import org.apache.ambari.server.orm.dao.HostRoleCommandDAO;
import org.apache.ambari.server.orm.dao.HostStateDAO;
import org.apache.ambari.server.orm.dao.KeyValueDAO;
import org.apache.ambari.server.orm.dao.MetainfoDAO;
import org.apache.ambari.server.orm.dao.RequestDAO;
import org.apache.ambari.server.orm.dao.RequestScheduleBatchRequestDAO;
import org.apache.ambari.server.orm.dao.RequestScheduleDAO;
import org.apache.ambari.server.orm.dao.RoleSuccessCriteriaDAO;
import org.apache.ambari.server.orm.dao.ServiceComponentDesiredStateDAO;
import org.apache.ambari.server.orm.dao.ServiceDesiredStateDAO;
import org.apache.ambari.server.orm.dao.StageDAO;
import org.apache.ambari.server.orm.dao.UserDAO;
import org.apache.ambari.server.orm.dao.ViewDAO;
import org.apache.ambari.server.orm.dao.ViewInstanceDAO;
import org.apache.ambari.server.utils.VersionUtils;
import org.apache.ambari.server.view.ViewRegistry;
import org.easymock.EasyMock;
import org.junit.Ignore;
import org.junit.Test;
import org.junit.runner.RunWith;
import org.junit.runners.Parameterized;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import com.google.inject.Guice;
import com.google.inject.Injector;
import com.google.inject.Key;
import com.google.inject.TypeLiteral;
import com.google.inject.persist.PersistService;
@RunWith(Parameterized.class)
public class UpgradeTest {
private static final Logger LOG = LoggerFactory.getLogger(UpgradeTest.class);
private static String DDL_PATTERN = "ddl-scripts/Ambari-DDL-Derby-%s.sql";
private static List<String> VERSIONS = Arrays.asList("1.4.4",
"1.4.3", "1.4.2", "1.4.1", "1.4.0", "1.2.5", "1.2.4",
"1.2.3"); //TODO add all
private static String DROP_DERBY_URL = "jdbc:derby:memory:myDB/ambari;drop=true";
private final String sourceVersion;
private Properties properties = new Properties();
private Injector injector;
public UpgradeTest(String sourceVersion) {
this.sourceVersion = sourceVersion;
properties.setProperty(Configuration.SERVER_PERSISTENCE_TYPE.getKey(), "remote");
properties.setProperty(Configuration.SERVER_JDBC_URL.getKey(), Configuration.JDBC_IN_MEMORY_URL);
properties.setProperty(Configuration.SERVER_JDBC_DRIVER.getKey(), Configuration.JDBC_IN_MEMORY_DRIVER);
properties.setProperty(Configuration.METADATA_DIR_PATH.getKey(), "src/test/resources/stacks");
properties.setProperty(Configuration.SERVER_VERSION_FILE.getKey(), "src/test/resources/version");
properties.setProperty(Configuration.OS_VERSION.getKey(), "centos5");
properties.setProperty(Configuration.SHARED_RESOURCES_DIR.getKey(), "src/test/resources/");
}
@Test
@Ignore
public void testUpgrade() throws Exception {
//not all tests close database properly, ensure it is empty
try {
dropDatabase();
} catch (SQLException ignored) {
// it is ok if database not found
}
String targetVersion = getLastVersion();
injector = Guice.createInjector(new ControllerModule(properties));
LOG.info("Testing upgrade from version {} to {}", sourceVersion, targetVersion);
createSourceDatabase(sourceVersion);
performUpgrade(targetVersion);
testUpgradedSchema();
dropDatabase();
}
private void dropDatabase() throws ClassNotFoundException, SQLException {
Class.forName(Configuration.JDBC_IN_MEMORY_DRIVER);
try {
DriverManager.getConnection(DROP_DERBY_URL);
} catch (SQLNonTransientConnectionException ignored) {
LOG.info("Database dropped ", ignored); //error 08006 expected
}
}
private void testUpgradedSchema() throws Exception {
injector = Guice.createInjector(new ControllerModule(properties));
injector.getInstance(PersistService.class).start();
//TODO join() in AmbariServer.run() prevents proper start testing, figure out
//check dao selects
//TODO generify DAOs for basic methods? deal with caching config group daos in such case
ClusterDAO clusterDAO = injector.getInstance(ClusterDAO.class);
clusterDAO.findAll();
BlueprintDAO blueprintDAO = injector.getInstance(BlueprintDAO.class);
blueprintDAO.findAll();
ClusterServiceDAO clusterServiceDAO = injector.getInstance(ClusterServiceDAO.class);
clusterServiceDAO.findAll();
injector.getInstance(ClusterStateDAO.class).findAll();
injector.getInstance(ConfigGroupConfigMappingDAO.class).findAll();
injector.getInstance(ConfigGroupDAO.class).findAll();
injector.getInstance(ConfigGroupHostMappingDAO.class).findAll();
injector.getInstance(ExecutionCommandDAO.class).findAll();
injector.getInstance(HostComponentDesiredStateDAO.class).findAll();
injector.getInstance(HostComponentStateDAO.class).findAll();
injector.getInstance(HostConfigMappingDAO.class).findAll();
injector.getInstance(HostDAO.class).findAll();
injector.getInstance(HostRoleCommandDAO.class).findAll();
injector.getInstance(HostStateDAO.class).findAll();
injector.getInstance(KeyValueDAO.class).findAll();
injector.getInstance(MetainfoDAO.class).findAll();
RequestDAO requestDAO = injector.getInstance(RequestDAO.class);
requestDAO.findAll();
requestDAO.findAllResourceFilters();
injector.getInstance(RequestScheduleBatchRequestDAO.class).findAll();
injector.getInstance(RequestScheduleDAO.class).findAll();
injector.getInstance(RoleSuccessCriteriaDAO.class).findAll();
injector.getInstance(ServiceComponentDesiredStateDAO.class).findAll();
injector.getInstance(ServiceDesiredStateDAO.class).findAll();
injector.getInstance(StageDAO.class).findAll();
injector.getInstance(UserDAO.class).findAll();
injector.getInstance(ViewDAO.class).findAll();
injector.getInstance(ViewInstanceDAO.class).findAll();
//TODO extend checks if needed
injector.getInstance(PersistService.class).stop();
}
private void performUpgrade(String targetVersion) throws Exception {
Injector injector = Guice.createInjector(new SchemaUpgradeHelper.UpgradeHelperModule(properties) {
@Override
protected void configure() {
super.configure();
ViewRegistry viewRegistryMock = EasyMock.createNiceMock(ViewRegistry.class);
bind(ViewRegistry.class).toInstance(viewRegistryMock);
}
});
SchemaUpgradeHelper schemaUpgradeHelper = injector.getInstance(SchemaUpgradeHelper.class);
LOG.info("Upgrading schema to target version = " + targetVersion);
UpgradeCatalog targetUpgradeCatalog = AbstractUpgradeCatalog
.getUpgradeCatalog(targetVersion);
LOG.debug("Target upgrade catalog. " + targetUpgradeCatalog);
// Read source version from DB
String sourceVersion = schemaUpgradeHelper.readSourceVersion();
LOG.info("Upgrading schema from source version = " + sourceVersion);
List<UpgradeCatalog> upgradeCatalogs =
schemaUpgradeHelper.getUpgradePath(sourceVersion, targetVersion);
assertTrue("Final Upgrade Catalog should be run last",
!upgradeCatalogs.isEmpty() && upgradeCatalogs.get(upgradeCatalogs.size() - 1).isFinal());
try {
schemaUpgradeHelper.executeUpgrade(upgradeCatalogs);
} catch (Exception e) {
// In UpgradeCatalog210, a lot of the classes had host_name removed, but the catalog makes raw SQL queries from Ambari 2.0.0
// which still had that column, in order to populate the host_id. Therfore, ignore this exception type.
if (e.getMessage().contains("Column 'T.HOST_NAME' is either not in any table in the FROM list") || e.getMessage().contains("Column 'T.HOSTNAME' is either not in any table in the FROM list")) {
System.out.println("Ignoring on purpose, " + e.getMessage());
} else {
throw e;
}
}
schemaUpgradeHelper.startPersistenceService();
schemaUpgradeHelper.executePreDMLUpdates(upgradeCatalogs);
schemaUpgradeHelper.executeDMLUpdates(upgradeCatalogs, "test");
schemaUpgradeHelper.executeOnPostUpgrade(upgradeCatalogs);
LOG.info("Upgrade successful.");
schemaUpgradeHelper.stopPersistenceService();
}
private String getLastVersion() throws Exception {
Injector injector = Guice.createInjector(new SchemaUpgradeHelper.UpgradeHelperModule(properties));
Set<UpgradeCatalog> upgradeCatalogs = injector.getInstance(Key.get(new TypeLiteral<Set<UpgradeCatalog>>() {
}));
String maxVersion = "1.2";
for (UpgradeCatalog upgradeCatalog : upgradeCatalogs) {
String targetVersion = upgradeCatalog.getTargetVersion();
if (VersionUtils.compareVersions(maxVersion, targetVersion) < 0) {
maxVersion = targetVersion;
}
}
return maxVersion;
}
private void createSourceDatabase(String version) throws IOException, SQLException {
//create database
String fileName = String.format(DDL_PATTERN, version);
fileName = this.getClass().getClassLoader().getResource(fileName).getFile();
DBAccessor dbAccessor = injector.getInstance(DBAccessor.class);
dbAccessor.executeScript(fileName);
}
@Parameterized.Parameters
public static Collection<Object[]> data() {
Collection<Object[]> data = new ArrayList<>();
for (String s : VERSIONS) {
data.add(new Object[]{s});
}
return data;
}
}