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

Reply via email to