Repository: ambari Updated Branches: refs/heads/branch-2.6 3279bef52 -> 295125730
AMBARI-22431 Able to add config type if EU/RU of the same stack (minor version upgrade) (dili) Project: http://git-wip-us.apache.org/repos/asf/ambari/repo Commit: http://git-wip-us.apache.org/repos/asf/ambari/commit/29512573 Tree: http://git-wip-us.apache.org/repos/asf/ambari/tree/29512573 Diff: http://git-wip-us.apache.org/repos/asf/ambari/diff/29512573 Branch: refs/heads/branch-2.6 Commit: 29512573090948e29d38bb0d259f72b0a5e42e99 Parents: 3279bef Author: Di Li <d...@apache.org> Authored: Thu Nov 16 12:22:35 2017 -0500 Committer: Di Li <d...@apache.org> Committed: Thu Nov 16 12:22:35 2017 -0500 ---------------------------------------------------------------------- .../internal/UpgradeResourceProvider.java | 36 ++ .../upgrades/CreateAndConfigureAction.java | 164 +++++++++ .../ambari/server/state/stack/UpgradePack.java | 3 + .../state/stack/upgrade/ClusterGrouping.java | 2 + .../stack/upgrade/CreateAndConfigureTask.java | 57 +++ .../ambari/server/state/stack/upgrade/Task.java | 6 +- .../src/main/resources/upgrade-pack.xsd | 9 +- .../upgrades/CreateAndConfigureActionTest.java | 357 +++++++++++++++++++ 8 files changed, 632 insertions(+), 2 deletions(-) ---------------------------------------------------------------------- http://git-wip-us.apache.org/repos/asf/ambari/blob/29512573/ambari-server/src/main/java/org/apache/ambari/server/controller/internal/UpgradeResourceProvider.java ---------------------------------------------------------------------- diff --git a/ambari-server/src/main/java/org/apache/ambari/server/controller/internal/UpgradeResourceProvider.java b/ambari-server/src/main/java/org/apache/ambari/server/controller/internal/UpgradeResourceProvider.java index 66f5bf9..b6846f7 100644 --- a/ambari-server/src/main/java/org/apache/ambari/server/controller/internal/UpgradeResourceProvider.java +++ b/ambari-server/src/main/java/org/apache/ambari/server/controller/internal/UpgradeResourceProvider.java @@ -92,6 +92,7 @@ import org.apache.ambari.server.state.UpgradeHelper.UpgradeGroupHolder; import org.apache.ambari.server.state.stack.ConfigUpgradePack; import org.apache.ambari.server.state.stack.UpgradePack; import org.apache.ambari.server.state.stack.upgrade.ConfigureTask; +import org.apache.ambari.server.state.stack.upgrade.CreateAndConfigureTask; import org.apache.ambari.server.state.stack.upgrade.Direction; import org.apache.ambari.server.state.stack.upgrade.ManualTask; import org.apache.ambari.server.state.stack.upgrade.ServerSideActionTask; @@ -1336,6 +1337,41 @@ public class UpgradeResourceProvider extends AbstractControllerResourceProvider break; } + case CREATE_AND_CONFIGURE: { + CreateAndConfigureTask ct = (CreateAndConfigureTask) task; + + // !!! would prefer to do this in the sequence generator, but there's too many + // places to miss + if (context.getOrchestrationType().isRevertable() && !ct.supportsPatch) { + process = false; + } + + Map<String, String> configurationChanges = + ct.getConfigurationChanges(cluster, configUpgradePack); + + // add all configuration changes to the command params + commandParams.putAll(configurationChanges); + + // extract the config type to build the summary + String configType = configurationChanges.get(CreateAndConfigureTask.PARAMETER_CONFIG_TYPE); + if (null != configType) { + itemDetail = String.format("Updating configuration %s", configType); + } else { + itemDetail = "Skipping Configuration Task " + + StringUtils.defaultString(ct.id, "(missing id)"); + } + + entity.setText(itemDetail); + + String configureTaskSummary = ct.getSummary(configUpgradePack); + if (null != configureTaskSummary) { + stageText = configureTaskSummary; + } else { + stageText = itemDetail; + } + + break; + } default: break; } http://git-wip-us.apache.org/repos/asf/ambari/blob/29512573/ambari-server/src/main/java/org/apache/ambari/server/serveraction/upgrades/CreateAndConfigureAction.java ---------------------------------------------------------------------- diff --git a/ambari-server/src/main/java/org/apache/ambari/server/serveraction/upgrades/CreateAndConfigureAction.java b/ambari-server/src/main/java/org/apache/ambari/server/serveraction/upgrades/CreateAndConfigureAction.java new file mode 100644 index 0000000..e60938a --- /dev/null +++ b/ambari-server/src/main/java/org/apache/ambari/server/serveraction/upgrades/CreateAndConfigureAction.java @@ -0,0 +1,164 @@ +/* + * 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.serveraction.upgrades; + +import java.util.Iterator; +import java.util.Map; +import java.util.concurrent.ConcurrentMap; + +import org.apache.ambari.server.AmbariException; +import org.apache.ambari.server.actionmanager.HostRoleStatus; +import org.apache.ambari.server.agent.CommandReport; +import org.apache.ambari.server.controller.AmbariManagementController; +import org.apache.ambari.server.controller.ConfigurationRequest; +import org.apache.ambari.server.orm.entities.RepositoryVersionEntity; +import org.apache.ambari.server.state.Cluster; +import org.apache.ambari.server.state.Clusters; +import org.apache.ambari.server.state.ConfigHelper; +import org.apache.ambari.server.state.DesiredConfig; +import org.apache.ambari.server.state.StackId; +import org.apache.ambari.server.state.UpgradeContext; +import org.apache.ambari.server.state.stack.upgrade.CreateAndConfigureTask; +import org.apache.ambari.server.state.stack.upgrade.Direction; +import org.apache.commons.lang.StringUtils; +import org.slf4j.Logger; +import org.slf4j.LoggerFactory; + +import com.google.inject.Inject; + +/** + * The {@link CreateAndConfigureAction} is used to alter a configuration property during + * an upgrade. It also creates the config type if it does not exist as a desired config for the cluster. + * It will only produce a new configuration if an actual change is + * occuring. For some configure tasks, the value is already at the desired + * property or the conditions of the task are not met. In these cases, a new + * configuration will not be created. This task can perform any of the following + * actions in a single declaration: + * <ul> + * <li>Copy a configuration to a new property key, optionally setting a default + * if the original property did not exist</li> + * <li>Copy a configuration to a new property key from one configuration type to + * another, optionally setting a default if the original property did not exist</li> + * <li>Rename a configuration, optionally setting a default if the original + * property did not exist</li> + * <li>Delete a configuration property</li> + * <li>Set a configuration property</li> + * <li>Conditionally set a configuration property based on another configuration + * property value</li> + * </ul> + */ +public class CreateAndConfigureAction extends ConfigureAction { + + private static final Logger LOG = LoggerFactory.getLogger(CreateAndConfigureAction.class); + + /** + * Used to lookup the cluster. + */ + @Inject + private Clusters m_clusters; + + /** + * Used to update the configuration properties. + */ + @Inject + private AmbariManagementController m_controller; + + /** + * Used to assist in the creation of a {@link ConfigurationRequest} to update + * configuration values. + */ + @Inject + private ConfigHelper m_configHelper; + + + @Override + public CommandReport execute( + ConcurrentMap<String, Object> requestSharedDataContext) + throws AmbariException, InterruptedException { + + LOG.info("Create and Configure..."); + + Map<String,String> commandParameters = getCommandParameters(); + if( null == commandParameters || commandParameters.isEmpty() ){ + return createCommandReport(0, HostRoleStatus.FAILED, "{}", "", + "Unable to change configuration values without command parameters"); + } + + String clusterName = commandParameters.get("clusterName"); + Cluster cluster = m_clusters.getCluster(clusterName); + UpgradeContext upgradeContext = getUpgradeContext(cluster); + + Direction direction = upgradeContext.getDirection(); + if (direction == Direction.DOWNGRADE) { + return createCommandReport(0, HostRoleStatus.COMPLETED, "{}", "", "Skip changing configuration values for downgrade"); + } + + String configType = commandParameters.get(CreateAndConfigureTask.PARAMETER_CONFIG_TYPE); + String serviceName = cluster.getServiceByConfigType(configType); + + if (StringUtils.isBlank(serviceName)) { + serviceName = commandParameters.get(CreateAndConfigureTask.PARAMETER_ASSOCIATED_SERVICE); + } + + RepositoryVersionEntity sourceRepoVersion = upgradeContext.getSourceRepositoryVersion(serviceName); + RepositoryVersionEntity targetRepoVersion = upgradeContext.getTargetRepositoryVersion(serviceName); + StackId sourceStackId = sourceRepoVersion.getStackId(); + StackId targetStackId = targetRepoVersion.getStackId(); + + if (!sourceStackId.equals(targetStackId)){ + return createCommandReport(0, HostRoleStatus.FAILED, "{}", "", + "Unable to change configuration values across stacks. Use regular config task type instead."); + } + + Map<String, DesiredConfig> desiredConfigs = cluster.getDesiredConfigs(); + DesiredConfig desiredConfig = desiredConfigs.get(configType); + if (desiredConfig == null) { + LOG.info(String.format("Could not find desired config type with name %s. Create it with default values.", configType)); + + // populate a map with default configurations from the new stack + Map<String, Map<String, String>> newServiceDefaultConfigsByType = m_configHelper.getDefaultProperties( + targetStackId, serviceName); + + if (!newServiceDefaultConfigsByType.containsKey(configType)){ + String error = String.format("%s in %s does not contain configuration type %s", serviceName, targetStackId.getStackId(), configType); + LOG.error(error); + return createCommandReport(0, HostRoleStatus.FAILED, "{}", "", error); + } + + Map<String, String> defaultConfigsForType = newServiceDefaultConfigsByType.get(configType); + // Remove any property for the new config type whose value is NULL + Iterator<Map.Entry<String, String>> iter = defaultConfigsForType.entrySet().iterator(); + while (iter.hasNext()) { + Map.Entry<String, String> entry = iter.next(); + if (entry.getValue() == null) { + iter.remove(); + } + } + + String serviceVersionNote = String.format("%s %s %s", direction.getText(true), + direction.getPreposition(), upgradeContext.getRepositoryVersion().getVersion()); + + m_configHelper.createConfigType(cluster, targetStackId, + m_controller, + configType, defaultConfigsForType, + m_controller.getAuthName(), serviceVersionNote); + } + + return super.execute(requestSharedDataContext); + } +} \ No newline at end of file http://git-wip-us.apache.org/repos/asf/ambari/blob/29512573/ambari-server/src/main/java/org/apache/ambari/server/state/stack/UpgradePack.java ---------------------------------------------------------------------- diff --git a/ambari-server/src/main/java/org/apache/ambari/server/state/stack/UpgradePack.java b/ambari-server/src/main/java/org/apache/ambari/server/state/stack/UpgradePack.java index 56f13ab..43c7a55 100644 --- a/ambari-server/src/main/java/org/apache/ambari/server/state/stack/UpgradePack.java +++ b/ambari-server/src/main/java/org/apache/ambari/server/state/stack/UpgradePack.java @@ -38,6 +38,7 @@ import javax.xml.bind.annotation.XmlValue; import org.apache.ambari.server.api.services.AmbariMetaInfo; import org.apache.ambari.server.state.stack.upgrade.ClusterGrouping; import org.apache.ambari.server.state.stack.upgrade.ConfigureTask; +import org.apache.ambari.server.state.stack.upgrade.CreateAndConfigureTask; import org.apache.ambari.server.state.stack.upgrade.Direction; import org.apache.ambari.server.state.stack.upgrade.Grouping; import org.apache.ambari.server.state.stack.upgrade.ServiceCheckGrouping; @@ -594,6 +595,8 @@ public class UpgradePack { for (Task task : tasks) { if (Task.Type.CONFIGURE == task.getType()) { ((ConfigureTask) task).associatedService = service; + } else if (Task.Type.CREATE_AND_CONFIGURE == task.getType()) { + ((CreateAndConfigureTask) task).associatedService = service; } } } http://git-wip-us.apache.org/repos/asf/ambari/blob/29512573/ambari-server/src/main/java/org/apache/ambari/server/state/stack/upgrade/ClusterGrouping.java ---------------------------------------------------------------------- diff --git a/ambari-server/src/main/java/org/apache/ambari/server/state/stack/upgrade/ClusterGrouping.java b/ambari-server/src/main/java/org/apache/ambari/server/state/stack/upgrade/ClusterGrouping.java index 63d0993..c1a05c0 100644 --- a/ambari-server/src/main/java/org/apache/ambari/server/state/stack/upgrade/ClusterGrouping.java +++ b/ambari-server/src/main/java/org/apache/ambari/server/state/stack/upgrade/ClusterGrouping.java @@ -135,6 +135,8 @@ public class ClusterGrouping extends Grouping { void afterUnmarshal(Unmarshaller unmarshaller, Object parent) { if (task.getType().equals(Task.Type.CONFIGURE) && StringUtils.isNotEmpty(service)) { ((ConfigureTask) task).associatedService = service; + } else if (task.getType().equals(Task.Type.CREATE_AND_CONFIGURE) && StringUtils.isNotEmpty(service)) { + ((CreateAndConfigureTask) task).associatedService = service; } } http://git-wip-us.apache.org/repos/asf/ambari/blob/29512573/ambari-server/src/main/java/org/apache/ambari/server/state/stack/upgrade/CreateAndConfigureTask.java ---------------------------------------------------------------------- diff --git a/ambari-server/src/main/java/org/apache/ambari/server/state/stack/upgrade/CreateAndConfigureTask.java b/ambari-server/src/main/java/org/apache/ambari/server/state/stack/upgrade/CreateAndConfigureTask.java new file mode 100644 index 0000000..d89840f --- /dev/null +++ b/ambari-server/src/main/java/org/apache/ambari/server/state/stack/upgrade/CreateAndConfigureTask.java @@ -0,0 +1,57 @@ +/** + * 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.state.stack.upgrade; + + +import javax.xml.bind.annotation.XmlAccessType; +import javax.xml.bind.annotation.XmlAccessorType; +import javax.xml.bind.annotation.XmlRootElement; +import javax.xml.bind.annotation.XmlType; + +import org.apache.ambari.server.serveraction.upgrades.CreateAndConfigureAction; + +/** + * The {@link CreateAndConfigureTask} represents a two step change where the create is for creating a config type if it does not exist + * followed by the configuration change. + * This task contains id of change. Change definitions are located in a separate file (config + * upgrade pack). IDs of change definitions share the same namespace within all stacks. + * + * + * <p/> + * + * <pre> + * {@code + * <task xsi:type="create_and_configure" id="hdp_2_3_0_0-UpdateHiveConfig"/> + * } + * </pre> + * + */ +@XmlRootElement +@XmlAccessorType(XmlAccessType.FIELD) +@XmlType(name="create_and_configure") +public class CreateAndConfigureTask extends ConfigureTask { + + public static final String actionVerb = "CreateAndConfiguring"; + + /** + * Constructor. + */ + public CreateAndConfigureTask() { + implClass = CreateAndConfigureAction.class.getName(); + } +} \ No newline at end of file http://git-wip-us.apache.org/repos/asf/ambari/blob/29512573/ambari-server/src/main/java/org/apache/ambari/server/state/stack/upgrade/Task.java ---------------------------------------------------------------------- diff --git a/ambari-server/src/main/java/org/apache/ambari/server/state/stack/upgrade/Task.java b/ambari-server/src/main/java/org/apache/ambari/server/state/stack/upgrade/Task.java index 6ab2fd2..2167b7b 100644 --- a/ambari-server/src/main/java/org/apache/ambari/server/state/stack/upgrade/Task.java +++ b/ambari-server/src/main/java/org/apache/ambari/server/state/stack/upgrade/Task.java @@ -25,7 +25,7 @@ import javax.xml.bind.annotation.XmlSeeAlso; /** * Base class to identify the items that could possibly occur during an upgrade */ -@XmlSeeAlso(value={ExecuteTask.class, ConfigureTask.class, ManualTask.class, RestartTask.class, StartTask.class, StopTask.class, ServerActionTask.class, ConfigureFunction.class}) +@XmlSeeAlso(value={ExecuteTask.class, CreateAndConfigureTask.class, ConfigureTask.class, ManualTask.class, RestartTask.class, StartTask.class, StopTask.class, ServerActionTask.class, ConfigureFunction.class}) public abstract class Task { /** @@ -96,6 +96,10 @@ public abstract class Task { */ CONFIGURE, /** + * Task that create a config type if it does not, and alters a configuration. + */ + CREATE_AND_CONFIGURE, + /** * Task that sets up the configuration for subsequent task */ CONFIGURE_FUNCTION, http://git-wip-us.apache.org/repos/asf/ambari/blob/29512573/ambari-server/src/main/resources/upgrade-pack.xsd ---------------------------------------------------------------------- diff --git a/ambari-server/src/main/resources/upgrade-pack.xsd b/ambari-server/src/main/resources/upgrade-pack.xsd index 21606bd..249725e 100644 --- a/ambari-server/src/main/resources/upgrade-pack.xsd +++ b/ambari-server/src/main/resources/upgrade-pack.xsd @@ -87,7 +87,7 @@ </xs:extension> </xs:complexContent> </xs:complexType> - + <xs:complexType name="security"> <xs:complexContent> <xs:extension base="abstract-condition-type"> @@ -336,6 +336,13 @@ </xs:complexContent> </xs:complexType> + <xs:complexType name="create_and_configure"> + <xs:complexContent> + <xs:extension base="configure"> + </xs:extension> + </xs:complexContent> + </xs:complexType> + <xs:complexType name="configure_function"> <xs:complexContent> <xs:extension base="abstract-task-type"> http://git-wip-us.apache.org/repos/asf/ambari/blob/29512573/ambari-server/src/test/java/org/apache/ambari/server/serveraction/upgrades/CreateAndConfigureActionTest.java ---------------------------------------------------------------------- diff --git a/ambari-server/src/test/java/org/apache/ambari/server/serveraction/upgrades/CreateAndConfigureActionTest.java b/ambari-server/src/test/java/org/apache/ambari/server/serveraction/upgrades/CreateAndConfigureActionTest.java new file mode 100644 index 0000000..43b5bd0 --- /dev/null +++ b/ambari-server/src/test/java/org/apache/ambari/server/serveraction/upgrades/CreateAndConfigureActionTest.java @@ -0,0 +1,357 @@ +/* + * 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.serveraction.upgrades; + +import static org.junit.Assert.assertEquals; +import static org.junit.Assert.assertFalse; +import static org.junit.Assert.assertNotNull; + +import java.util.ArrayList; +import java.util.Collections; +import java.util.HashMap; +import java.util.List; +import java.util.Map; + +import javax.persistence.EntityManager; + +import org.apache.ambari.server.AmbariException; +import org.apache.ambari.server.H2DatabaseCleaner; +import org.apache.ambari.server.ServiceComponentNotFoundException; +import org.apache.ambari.server.ServiceNotFoundException; +import org.apache.ambari.server.actionmanager.ExecutionCommandWrapper; +import org.apache.ambari.server.actionmanager.HostRoleCommand; +import org.apache.ambari.server.actionmanager.HostRoleCommandFactory; +import org.apache.ambari.server.agent.CommandReport; +import org.apache.ambari.server.agent.ExecutionCommand; +import org.apache.ambari.server.orm.GuiceJpaInitializer; +import org.apache.ambari.server.orm.InMemoryDefaultTestModule; +import org.apache.ambari.server.orm.OrmTestHelper; +import org.apache.ambari.server.orm.dao.RequestDAO; +import org.apache.ambari.server.orm.dao.UpgradeDAO; +import org.apache.ambari.server.orm.entities.RepositoryVersionEntity; +import org.apache.ambari.server.orm.entities.RequestEntity; +import org.apache.ambari.server.orm.entities.UpgradeEntity; +import org.apache.ambari.server.orm.entities.UpgradeHistoryEntity; +import org.apache.ambari.server.serveraction.ServerAction; +import org.apache.ambari.server.state.Cluster; +import org.apache.ambari.server.state.Clusters; +import org.apache.ambari.server.state.Config; +import org.apache.ambari.server.state.ConfigFactory; +import org.apache.ambari.server.state.ConfigHelper; +import org.apache.ambari.server.state.Host; +import org.apache.ambari.server.state.Service; +import org.apache.ambari.server.state.ServiceComponent; +import org.apache.ambari.server.state.ServiceComponentFactory; +import org.apache.ambari.server.state.ServiceComponentHost; +import org.apache.ambari.server.state.ServiceComponentHostFactory; +import org.apache.ambari.server.state.ServiceFactory; +import org.apache.ambari.server.state.StackId; +import org.apache.ambari.server.state.State; +import org.apache.ambari.server.state.stack.upgrade.ConfigUpgradeChangeDefinition.ConfigurationKeyValue; +import org.apache.ambari.server.state.stack.upgrade.CreateAndConfigureTask; +import org.apache.ambari.server.state.stack.upgrade.UpgradeType; +import org.apache.commons.lang.StringUtils; +import org.junit.After; +import org.junit.Assert; +import org.junit.Before; +import org.junit.Test; + +import com.google.gson.Gson; +import com.google.inject.Guice; +import com.google.inject.Inject; +import com.google.inject.Injector; + +/** + * Tests upgrade-related server side actions + */ +public class CreateAndConfigureActionTest { + + @Inject + private Injector m_injector; + + @Inject + private OrmTestHelper m_helper; + + @Inject + private HostRoleCommandFactory hostRoleCommandFactory; + + @Inject + private ServiceFactory serviceFactory; + + @Inject + private ConfigHelper m_configHelper; + + @Inject + private Clusters clusters; + + @Inject + private ConfigFactory configFactory; + + @Inject + private CreateAndConfigureAction action; + + @Inject + private RequestDAO requestDAO; + + @Inject + private UpgradeDAO upgradeDAO; + + @Inject + private ServiceComponentFactory serviceComponentFactory; + + @Inject + private ServiceComponentHostFactory serviceComponentHostFactory; + + private RepositoryVersionEntity repoVersion2110; + private RepositoryVersionEntity repoVersion2111; + private RepositoryVersionEntity repoVersion2200; + + private final Map<String, Map<String, String>> NO_ATTRIBUTES = new HashMap<>(); + + @Before + public void setup() throws Exception { + m_injector = Guice.createInjector(new InMemoryDefaultTestModule()); + m_injector.getInstance(GuiceJpaInitializer.class); + m_injector.injectMembers(this); + + repoVersion2110 = m_helper.getOrCreateRepositoryVersion(new StackId("HDP-2.1.1"), "2.1.1.0-1234"); + repoVersion2111 = m_helper.getOrCreateRepositoryVersion(new StackId("HDP-2.1.1"), "2.1.1.1-5678"); + repoVersion2200 = m_helper.getOrCreateRepositoryVersion(new StackId("HDP-2.2.0"), "2.2.0.0-1234"); + + makeUpgradeCluster(); + } + + @After + public void teardown() throws Exception { + H2DatabaseCleaner.clearDatabase(m_injector.getProvider(EntityManager.class).get()); + } + + + /** + * Tests that a new configuration is created when upgrading across stack when + * there is no existing configuration with the correct target stack. + * + * @throws Exception + */ + @Test + public void testNewConfigCreatedWhenUpgradingWithoutChaningStack() throws Exception { + Cluster c = clusters.getCluster("c1"); + assertEquals(1, c.getConfigsByType("zoo.cfg").size()); + + Map<String, String> properties = new HashMap<String, String>() { + { + put("initLimit", "10"); + } + }; + + Config config = createConfig(c, "zoo.cfg", "version2", properties); + + c.addDesiredConfig("user", Collections.singleton(config)); + assertEquals(2, c.getConfigsByType("zoo.cfg").size()); + + List<ConfigurationKeyValue> configurations = new ArrayList<>(); + ConfigurationKeyValue keyValue = new ConfigurationKeyValue(); + configurations.add(keyValue); + keyValue.key = "initLimit"; + keyValue.value = "11"; + c.setCurrentStackVersion(repoVersion2110.getStackId()); + c.setDesiredStackVersion(repoVersion2111.getStackId()); + + createUpgrade(c, repoVersion2111); + + Map<String, String> commandParams = new HashMap<>(); + commandParams.put("clusterName", "c1"); + commandParams.put(CreateAndConfigureTask.PARAMETER_CONFIG_TYPE, "zoo.cfg"); + commandParams.put(CreateAndConfigureTask.PARAMETER_KEY_VALUE_PAIRS, new Gson().toJson(configurations)); + + ExecutionCommand executionCommand = getExecutionCommand(commandParams); + HostRoleCommand hostRoleCommand = hostRoleCommandFactory.create(null, null, + null, null); + + hostRoleCommand.setExecutionCommandWrapper(new ExecutionCommandWrapper( + executionCommand)); + + action.setExecutionCommand(executionCommand); + action.setHostRoleCommand(hostRoleCommand); + + CommandReport report = action.execute(null); + assertNotNull(report); + + assertEquals(3, c.getConfigsByType("zoo.cfg").size()); + + config = c.getDesiredConfigByType("zoo.cfg"); + assertNotNull(config); + assertFalse(StringUtils.equals("version2", config.getTag())); + assertEquals("11", config.getProperties().get("initLimit")); + } + + /** + * Creates a cluster using {@link #repoVersion2110} with ZooKeeper installed. + * + * @throws Exception + */ + private void makeUpgradeCluster() throws Exception { + String clusterName = "c1"; + String hostName = "h1"; + + clusters.addCluster(clusterName, repoVersion2110.getStackId()); + + Cluster c = clusters.getCluster(clusterName); + + // add a host component + clusters.addHost(hostName); + Host host = clusters.getHost(hostName); + Map<String, String> hostAttributes = new HashMap<>(); + hostAttributes.put("os_family", "redhat"); + hostAttributes.put("os_release_version", "6"); + host.setHostAttributes(hostAttributes); + + clusters.mapHostToCluster(hostName, clusterName); + + // !!! very important, otherwise the loops that walk the list of installed + // service properties will not run! + Service zk = installService(c, "ZOOKEEPER", repoVersion2110); + addServiceComponent(c, zk, "ZOOKEEPER_SERVER"); + addServiceComponent(c, zk, "ZOOKEEPER_CLIENT"); + createNewServiceComponentHost(c, "ZOOKEEPER", "ZOOKEEPER_SERVER", hostName); + createNewServiceComponentHost(c, "ZOOKEEPER", "ZOOKEEPER_CLIENT", hostName); + + Map<String, String> properties = new HashMap<String, String>() { + { + put("initLimit", "10"); + } + }; + + Config config = createConfig(c, "zoo.cfg", "version1", properties); + + c.addDesiredConfig("user", Collections.singleton(config)); + + // verify that our configs are there + String tickTime = m_configHelper.getPropertyValueFromStackDefinitions(c, "zoo.cfg", "tickTime"); + assertNotNull(tickTime); + } + + /** + * Installs a service in the cluster. + * + * @param cluster + * @param serviceName + * @return + * @throws AmbariException + */ + private Service installService(Cluster cluster, String serviceName, + RepositoryVersionEntity repositoryVersion) throws AmbariException { + Service service = null; + + try { + service = cluster.getService(serviceName); + } catch (ServiceNotFoundException e) { + service = serviceFactory.createNew(cluster, serviceName, repositoryVersion); + cluster.addService(service); + } + + return service; + } + + private ServiceComponent addServiceComponent(Cluster cluster, Service service, + String componentName) throws AmbariException { + ServiceComponent serviceComponent = null; + try { + serviceComponent = service.getServiceComponent(componentName); + } catch (ServiceComponentNotFoundException e) { + serviceComponent = serviceComponentFactory.createNew(service, componentName); + service.addServiceComponent(serviceComponent); + serviceComponent.setDesiredState(State.INSTALLED); + } + + return serviceComponent; + } + + private ServiceComponentHost createNewServiceComponentHost(Cluster cluster, String serviceName, + String svcComponent, String hostName) throws AmbariException { + Assert.assertNotNull(cluster.getConfigGroups()); + Service s = cluster.getService(serviceName); + ServiceComponent sc = addServiceComponent(cluster, s, svcComponent); + + ServiceComponentHost sch = serviceComponentHostFactory.createNew(sc, hostName); + + sc.addServiceComponentHost(sch); + sch.setDesiredState(State.INSTALLED); + sch.setState(State.INSTALLED); + return sch; + } + + /** + * Creates an upgrade and associates it with the cluster. + */ + private UpgradeEntity createUpgrade(Cluster cluster, RepositoryVersionEntity repositoryVersion) + throws Exception { + + // create some entities for the finalize action to work with for patch + // history + RequestEntity requestEntity = new RequestEntity(); + requestEntity.setClusterId(cluster.getClusterId()); + requestEntity.setRequestId(1L); + requestEntity.setStartTime(System.currentTimeMillis()); + requestEntity.setCreateTime(System.currentTimeMillis()); + requestDAO.create(requestEntity); + + UpgradeEntity upgradeEntity = new UpgradeEntity(); + upgradeEntity.setId(1L); + upgradeEntity.setClusterId(cluster.getClusterId()); + upgradeEntity.setRequestEntity(requestEntity); + upgradeEntity.setUpgradePackage(""); + upgradeEntity.setRepositoryVersion(repositoryVersion); + upgradeEntity.setUpgradeType(UpgradeType.NON_ROLLING); + + Map<String, Service> services = cluster.getServices(); + for (String serviceName : services.keySet()) { + Service service = services.get(serviceName); + Map<String, ServiceComponent> components = service.getServiceComponents(); + for (String componentName : components.keySet()) { + UpgradeHistoryEntity history = new UpgradeHistoryEntity(); + history.setUpgrade(upgradeEntity); + history.setServiceName(serviceName); + history.setComponentName(componentName); + history.setFromRepositoryVersion(service.getDesiredRepositoryVersion()); + history.setTargetRepositoryVersion(repositoryVersion); + upgradeEntity.addHistory(history); + } + } + + upgradeDAO.create(upgradeEntity); + cluster.setUpgradeEntity(upgradeEntity); + return upgradeEntity; + } + + private ExecutionCommand getExecutionCommand(Map<String, String> commandParams) { + ExecutionCommand executionCommand = new ExecutionCommand(); + executionCommand.setClusterName("c1"); + executionCommand.setCommandParams(commandParams); + executionCommand.setRoleParams(new HashMap<String, String>()); + executionCommand.getRoleParams().put(ServerAction.ACTION_USER_NAME, "username"); + + return executionCommand; + } + + private Config createConfig(Cluster cluster, String type, String tag, + Map<String, String> properties) { + return configFactory.createNew(cluster, type, tag, properties, + NO_ATTRIBUTES); + } +} \ No newline at end of file