This is an automated email from the ASF dual-hosted git repository.

benyoka pushed a commit to branch branch-feature-AMBARI-14714
in repository https://gitbox.apache.org/repos/asf/ambari.git


The following commit(s) were added to refs/heads/branch-feature-AMBARI-14714 by 
this push:
     new deac5ab  [AMBARI-24657] Fix exception in blueprint deployment with 
host group configs (#2355)
deac5ab is described below

commit deac5ab3030109681f5fe80401de45363e40a7c0
Author: benyoka <[email protected]>
AuthorDate: Mon Sep 24 22:09:15 2018 +0200

    [AMBARI-24657] Fix exception in blueprint deployment with host group 
configs (#2355)
    
    * AMBARI-24657 WIP (benyoka)
    
    * AMBARI-24657 Blueprint install with host group configs (benyoka)
    
    * AMBARI-24657 fixe review comment (benyoka)
    
    * AMBARI-24657 fixed unit tests (benyoka)
---
 .../controller/AmbariManagementControllerImpl.java | 10 ++-
 .../server/controller/internal/CompositeStack.java | 18 ++----
 .../ambari/server/controller/internal/Stack.java   |  9 +++
 .../controller/internal/StackDefinition.java       |  6 ++
 .../org/apache/ambari/server/state/Cluster.java    | 17 ++++--
 .../ambari/server/state/cluster/ClusterImpl.java   | 43 ++++---------
 .../server/state/configgroup/ConfigGroupImpl.java  | 18 ++++--
 .../ambari/server/topology/AmbariContext.java      | 48 ++++++---------
 .../ambari/server/utils/CollectionUtils.java       | 71 ++++++++++++++++++++++
 .../controller/internal/CompositeStackTest.java    | 19 ++++++
 .../server/controller/internal/StackTest.java      | 54 +++++++++++++++-
 .../ambari/server/state/ConfigGroupTest.java       | 11 +++-
 .../ambari/server/topology/AmbariContextTest.java  |  4 +-
 13 files changed, 242 insertions(+), 86 deletions(-)

diff --git 
a/ambari-server/src/main/java/org/apache/ambari/server/controller/AmbariManagementControllerImpl.java
 
b/ambari-server/src/main/java/org/apache/ambari/server/controller/AmbariManagementControllerImpl.java
index 2b3bff2..ba6f902 100644
--- 
a/ambari-server/src/main/java/org/apache/ambari/server/controller/AmbariManagementControllerImpl.java
+++ 
b/ambari-server/src/main/java/org/apache/ambari/server/controller/AmbariManagementControllerImpl.java
@@ -69,6 +69,7 @@ import java.util.LinkedList;
 import java.util.List;
 import java.util.Map;
 import java.util.Map.Entry;
+import java.util.Optional;
 import java.util.Set;
 import java.util.SortedMap;
 import java.util.TreeMap;
@@ -1160,7 +1161,10 @@ public class AmbariManagementControllerImpl implements 
AmbariManagementControlle
 
     Config config = configFactory.createNew(stackId, cluster, type, 
versionTag, properties,
         propertiesAttributes, serviceId);
-
+    // TODO: the constructor of ConfigImpl adds itself to the cluster so 
calling this method
+    // should not be necessary. It causes some confusion with service instance 
level configs
+    // (where a serviceId is present). The result of the extra addConfig() 
call adds these configs
+    // to the cluster level configs too adding confusion.
     cluster.addConfig(config);
     return config;
   }
@@ -1626,7 +1630,7 @@ public class AmbariManagementControllerImpl implements 
AmbariManagementControlle
       Config config = null;
       //TODO : Remove after getting rid of cluster configurations
       if (request.getServiceId() != null) {
-        config = cluster.getConfigByServiceId(request.getType(), 
request.getVersionTag(), request.getServiceId());
+        config = cluster.getConfig(request.getType(), request.getVersionTag(), 
Optional.of(request.getServiceId()));
         if (null != config) {
           response = new ConfigurationResponse(
                   cluster.getClusterName(), config, request.getServiceId(), 
request.getServiceGroupId());
@@ -1911,7 +1915,7 @@ public class AmbariManagementControllerImpl implements 
AmbariManagementControlle
             }
           }
           note = cr.getServiceConfigVersionNote();
-          Config config = cluster.getConfig(configType, cr.getVersionTag());
+          Config config = cluster.getConfig(configType, cr.getVersionTag(), 
Optional.ofNullable(cr.getServiceId()));
           if (null != config) {
             configs.add(config);
           }
diff --git 
a/ambari-server/src/main/java/org/apache/ambari/server/controller/internal/CompositeStack.java
 
b/ambari-server/src/main/java/org/apache/ambari/server/controller/internal/CompositeStack.java
index 6c5e6ec..fa3d14c 100644
--- 
a/ambari-server/src/main/java/org/apache/ambari/server/controller/internal/CompositeStack.java
+++ 
b/ambari-server/src/main/java/org/apache/ambari/server/controller/internal/CompositeStack.java
@@ -243,18 +243,12 @@ public class CompositeStack implements StackDefinition {
 
   @Override
   public Stream<String> getServicesForConfigType(String config) {
-    if (ConfigHelper.CLUSTER_ENV.equals(config)) { // for backwards 
compatibility
-      return Stream.empty();
-    }
-    return stacks.stream()
-      .map(m -> {
-        try {
-          return m.getServiceForConfigType(config);
-        } catch (IllegalArgumentException e) {
-          return null;
-        }
-      })
-      .filter(Objects::nonNull);
+    return stacks.stream().flatMap(s -> s.getServicesForConfigType(config));
+  }
+
+  @Override
+  public Stream<Pair<StackId, String>> getStackServicesForConfigType(String 
config) {
+    return stacks.stream().flatMap(m -> 
m.getStackServicesForConfigType(config));
   }
 
   @Override
diff --git 
a/ambari-server/src/main/java/org/apache/ambari/server/controller/internal/Stack.java
 
b/ambari-server/src/main/java/org/apache/ambari/server/controller/internal/Stack.java
index 28d5ce0..e42dea7 100644
--- 
a/ambari-server/src/main/java/org/apache/ambari/server/controller/internal/Stack.java
+++ 
b/ambari-server/src/main/java/org/apache/ambari/server/controller/internal/Stack.java
@@ -431,6 +431,9 @@ public class Stack implements StackDefinition {
 
   @Override
   public Stream<String> getServicesForConfigType(String config) {
+    if (ConfigHelper.CLUSTER_ENV.equals(config)) { // for backwards 
compatibility
+      return Stream.empty();
+    }
     return serviceConfigurations.entrySet().stream()
       .filter(e -> e.getValue().containsKey(config))
       .filter(e -> !getExcludedConfigurationTypes(e.getKey()).contains(config))
@@ -438,6 +441,12 @@ public class Stack implements StackDefinition {
   }
 
   @Override
+  public Stream<Pair<StackId, String>> getStackServicesForConfigType(String 
config) {
+    return getServicesForConfigType(config).map(service -> 
Pair.of(getStackId(), service));
+  }
+
+
+  @Override
   public Collection<DependencyInfo> getDependenciesForComponent(String 
component) {
     return dependencies.containsKey(component) ? dependencies.get(component) :
         Collections.emptySet();
diff --git 
a/ambari-server/src/main/java/org/apache/ambari/server/controller/internal/StackDefinition.java
 
b/ambari-server/src/main/java/org/apache/ambari/server/controller/internal/StackDefinition.java
index eb9a0ef..8f9029b 100644
--- 
a/ambari-server/src/main/java/org/apache/ambari/server/controller/internal/StackDefinition.java
+++ 
b/ambari-server/src/main/java/org/apache/ambari/server/controller/internal/StackDefinition.java
@@ -229,6 +229,12 @@ public interface StackDefinition {
   Stream<String> getServicesForConfigType(String config);
 
   /**
+   * @return stream of service names along with the id of their defining stack 
which correspond
+   * to the specified configuration type name
+   */
+  Stream<Pair<StackId, String>> getStackServicesForConfigType(String config);
+
+  /**
    * Return the dependencies specified for the given component.
    *
    * @param component  component to get dependency information for
diff --git 
a/ambari-server/src/main/java/org/apache/ambari/server/state/Cluster.java 
b/ambari-server/src/main/java/org/apache/ambari/server/state/Cluster.java
index ca44b39..a04e5d6 100644
--- a/ambari-server/src/main/java/org/apache/ambari/server/state/Cluster.java
+++ b/ambari-server/src/main/java/org/apache/ambari/server/state/Cluster.java
@@ -23,8 +23,11 @@ import static java.util.stream.Collectors.toSet;
 import java.util.Collection;
 import java.util.List;
 import java.util.Map;
+import java.util.Optional;
 import java.util.Set;
 
+import javax.annotation.Nonnull;
+
 import org.apache.ambari.server.AmbariException;
 import org.apache.ambari.server.ClusterSettingNotFoundException;
 import org.apache.ambari.server.ServiceGroupNotFoundException;
@@ -415,6 +418,8 @@ public interface Cluster {
   Map<PropertyInfo.PropertyType, Set<String>> getConfigPropertiesTypes(String 
configType);
 
   /**
+   * @deprecated {@link #getConfig(String, String, Optional)} should be 
preferred
+   *
    * Gets the specific config that matches the specified type and tag.  This 
not
    * necessarily a DESIRED configuration that applies to a cluster.
    *
@@ -423,19 +428,23 @@ public interface Cluster {
    * @return a {@link Config} object, or <code>null</code> if the specific type
    * and version have not been set.
    */
-  Config getConfig(String configType, String versionTag);
+  @Deprecated
+  Config getConfig(@Nonnull String configType, @Nonnull String versionTag);
 
   /**
-   * Gets the specific config that matches the specified type and tag.  This 
not
+   * Gets the specific config that matches the specified type and tag. This not
    * necessarily a DESIRED configuration that applies to a cluster.
    *
+   * If {@code serviceId} is present, the config will be first looked up in 
service instance level configs, when absent,
+   * cluster level configs will be searched.
+   *
    * @param configType the config type to find
    * @param versionTag the config version tag to find
-   * @param serviceId the service for the config
+   * @param serviceId The optional serviceid. When present, the config will be 
looked up from service leve configs
    * @return a {@link Config} object, or <code>null</code> if the specific type
    * and version have not been set.
    */
-  Config getConfigByServiceId(String configType, String versionTag, Long 
serviceId);
+  Config getConfig(@Nonnull String configType, @Nonnull String versionTag, 
@Nonnull Optional<Long> serviceId);
 
   /**
    * Get latest (including inactive ones) configurations with any of the given 
types.
diff --git 
a/ambari-server/src/main/java/org/apache/ambari/server/state/cluster/ClusterImpl.java
 
b/ambari-server/src/main/java/org/apache/ambari/server/state/cluster/ClusterImpl.java
index d45b38d..2619e68 100644
--- 
a/ambari-server/src/main/java/org/apache/ambari/server/state/cluster/ClusterImpl.java
+++ 
b/ambari-server/src/main/java/org/apache/ambari/server/state/cluster/ClusterImpl.java
@@ -19,6 +19,7 @@
 package org.apache.ambari.server.state.cluster;
 
 import static java.util.stream.Collectors.toList;
+import static 
org.apache.ambari.server.utils.CollectionUtils.emptyConcurrentMap;
 
 import java.text.MessageFormat;
 import java.util.ArrayList;
@@ -32,6 +33,7 @@ import java.util.List;
 import java.util.Map;
 import java.util.Map.Entry;
 import java.util.Objects;
+import java.util.Optional;
 import java.util.Set;
 import java.util.concurrent.ConcurrentHashMap;
 import java.util.concurrent.ConcurrentMap;
@@ -42,6 +44,7 @@ import java.util.concurrent.locks.ReadWriteLock;
 import java.util.function.Function;
 import java.util.stream.Collectors;
 
+import javax.annotation.Nonnull;
 import javax.annotation.Nullable;
 import javax.persistence.EntityManager;
 import javax.persistence.RollbackException;
@@ -163,6 +166,7 @@ import org.slf4j.Logger;
 import org.slf4j.LoggerFactory;
 
 import com.google.common.base.Functions;
+import com.google.common.base.Preconditions;
 import com.google.common.base.Predicate;
 import com.google.common.collect.ImmutableList;
 import com.google.common.collect.ImmutableMap;
@@ -1498,37 +1502,19 @@ public class ClusterImpl implements Cluster {
   }
 
   @Override
-  public Config getConfig(String configType, String versionTag) {
-    clusterGlobalLock.readLock().lock();
-    try {
-      if (!allConfigs.containsKey(configType)
-        || !allConfigs.get(configType).containsKey(versionTag)) {
-        return null;
-      }
-      return allConfigs.get(configType).get(versionTag);
-    } finally {
-      clusterGlobalLock.readLock().unlock();
-    }
+  public Config getConfig(String configType, @Nonnull String versionTag) {
+    return getConfig(configType, versionTag, Optional.empty());
   }
 
   @Override
-  public Config getConfigByServiceId(String configType, String versionTag, 
Long serviceId) {
+  public Config getConfig(@Nonnull String configType, @Nonnull String 
versionTag, @Nonnull Optional<Long> serviceId) {
     clusterGlobalLock.readLock().lock();
     try {
-      if (!serviceConfigs.containsKey(serviceId)) {
-        return null;
-      }
-      else {
-        ConcurrentMap<String, ConcurrentMap<String, Config>> allServiceConfigs 
= serviceConfigs.get(serviceId);
-        if (!allServiceConfigs.containsKey(configType)
-                || !allServiceConfigs.get(configType).containsKey(versionTag)) 
{
-          return null;
-        }
-        return allServiceConfigs.get(configType).get(versionTag);
-      }
-    }
-    finally {
-        clusterGlobalLock.readLock().unlock();
+      ConcurrentMap<String, ConcurrentMap<String, Config>> configs =
+        serviceId.isPresent() ? serviceConfigs.getOrDefault(serviceId.get(), 
emptyConcurrentMap()) : allConfigs;
+      return configs.getOrDefault(configType, 
emptyConcurrentMap()).get(versionTag);
+    } finally {
+      clusterGlobalLock.readLock().unlock();
     }
   }
 
@@ -1610,10 +1596,7 @@ public class ClusterImpl implements Cluster {
 
   @Override
   public void addConfig(Config config) {
-    if (config.getType() == null || config.getType().isEmpty()) {
-      throw new IllegalArgumentException("Config type cannot be empty");
-    }
-
+    Preconditions.checkArgument(config.getType() != null && 
!config.getType().isEmpty(), "Config type cannot be empty");
     clusterGlobalLock.writeLock().lock();
     try {
       if (!allConfigs.containsKey(config.getType())) {
diff --git 
a/ambari-server/src/main/java/org/apache/ambari/server/state/configgroup/ConfigGroupImpl.java
 
b/ambari-server/src/main/java/org/apache/ambari/server/state/configgroup/ConfigGroupImpl.java
index e488086..c6c3fed 100644
--- 
a/ambari-server/src/main/java/org/apache/ambari/server/state/configgroup/ConfigGroupImpl.java
+++ 
b/ambari-server/src/main/java/org/apache/ambari/server/state/configgroup/ConfigGroupImpl.java
@@ -23,6 +23,7 @@ import java.util.HashMap;
 import java.util.HashSet;
 import java.util.Map;
 import java.util.Map.Entry;
+import java.util.Optional;
 import java.util.Set;
 import java.util.concurrent.ConcurrentHashMap;
 import java.util.concurrent.ConcurrentMap;
@@ -175,8 +176,10 @@ public class ConfigGroupImpl implements ConfigGroup {
 
     // Populate configs
     for (ConfigGroupConfigMappingEntity configMappingEntity : 
configGroupEntity.getConfigGroupConfigMappingEntities()) {
-      Config config = cluster.getConfig(configMappingEntity.getConfigType(),
-        configMappingEntity.getVersionTag());
+      Config config = cluster.getConfig(
+        configMappingEntity.getConfigType(),
+        configMappingEntity.getVersionTag(),
+        Optional.ofNullable(serviceId));
 
       if (config != null) {
         m_configurations.put(config.getType(), config);
@@ -436,9 +439,16 @@ public class ConfigGroupImpl implements ConfigGroup {
         if (clusterConfigEntity == null) {
           Service service = cluster.getService(serviceId);
 
-          //TODO check the serviceid = null for the right use case
           config = configFactory.createNew(service.getStackId(), cluster, 
config.getType(),
-              config.getTag(), config.getProperties(), 
config.getPropertiesAttributes(), null);
+              config.getTag(), config.getProperties(), 
config.getPropertiesAttributes(), serviceId);
+          // TODO: the following line should not be here, adding only 
temporarily for compatibility
+          // ClusterImpl keeps cluster level configs and service instance 
level configs (those associated with
+          // a serviceId) separately. Most of current Ambari code only uses 
cluster level configs. (TODO: fix it!)
+          // When a service instance level config is created using 
AmbariManagementController.createConfig() it will
+          // be added to the cluster both as service level and cluster level 
configs. To keep consistency with
+          // this behavior service level configs created here will too be 
added as cluster level configs until
+          // this thing gets fixed.
+          cluster.addConfig(config);
 
           entry.setValue(config);
 
diff --git 
a/ambari-server/src/main/java/org/apache/ambari/server/topology/AmbariContext.java
 
b/ambari-server/src/main/java/org/apache/ambari/server/topology/AmbariContext.java
index 4cb3333..730a3c5 100644
--- 
a/ambari-server/src/main/java/org/apache/ambari/server/topology/AmbariContext.java
+++ 
b/ambari-server/src/main/java/org/apache/ambari/server/topology/AmbariContext.java
@@ -25,6 +25,7 @@ import java.util.Collection;
 import java.util.Collections;
 import java.util.Comparator;
 import java.util.HashMap;
+import java.util.List;
 import java.util.Map;
 import java.util.Set;
 import java.util.SortedSet;
@@ -32,9 +33,9 @@ import java.util.TreeSet;
 import java.util.concurrent.Callable;
 import java.util.concurrent.atomic.AtomicLong;
 import java.util.concurrent.locks.Lock;
+import java.util.stream.Collectors;
 import java.util.stream.Stream;
 
-import javax.annotation.Nullable;
 import javax.inject.Inject;
 
 import org.apache.ambari.server.AmbariException;
@@ -92,9 +93,11 @@ import org.apache.ambari.server.state.StackId;
 import org.apache.ambari.server.state.State;
 import org.apache.ambari.server.state.configgroup.ConfigGroup;
 import org.apache.ambari.server.utils.RetryHelper;
+import org.apache.commons.lang3.tuple.Pair;
 import org.slf4j.Logger;
 import org.slf4j.LoggerFactory;
 
+import com.google.common.base.Preconditions;
 import com.google.common.collect.ImmutableSet;
 import com.google.common.collect.Iterables;
 import com.google.common.collect.Sets;
@@ -146,7 +149,6 @@ public class AmbariContext {
 
   //todo: task id's.  Use existing mechanism for getting next task id sequence
   private final static AtomicLong nextTaskId = new AtomicLong(10000);
-  static final String DEFAULT_SERVICE_GROUP_NAME = "default_service_group"; // 
exposed for test
 
   private static ClusterController clusterController;
   private static HostRoleCommandFactory hostRoleCommandFactory;
@@ -160,7 +162,6 @@ public class AmbariContext {
 
   private final static Logger LOG = 
LoggerFactory.getLogger(AmbariContext.class);
 
-
   /**
    * When config groups are created using Blueprints these are created when
    * hosts join a hostgroup and are added to the corresponding config group.
@@ -696,7 +697,8 @@ public class AmbariContext {
    * and the hosts associated with the host group are assigned to the config 
group.
    */
   private void createConfigGroupsAndRegisterHost(ClusterTopology topology, 
String groupName) throws AmbariException {
-    Map<String, Map<String, Config>> groupConfigs = new HashMap<>();
+    // (StackId, Service) -> Config Type -> Config
+    Map<Pair<StackId, String>, Map<String, Config>> groupConfigs = new 
HashMap<>();
     StackDefinition stack = topology.getStack();
 
     // get the host-group config with cluster creation template overrides
@@ -710,26 +712,21 @@ public class AmbariContext {
     // iterate over topo host group configs which were defined in
     for (Map.Entry<String, Map<String, String>> entry : 
userProvidedGroupProperties.entrySet()) {
       String type = entry.getKey();
-      String service = stack.getServicesForConfigType(type)
-        .filter(each -> topology.getServiceTypes().contains(each))
-        .findFirst()
-        // TODO check if this is required at all (might be handled by the 
"orphan" removal)
-        // TODO move this validation earlier
-        .orElseThrow(() -> new IllegalArgumentException("Specified 
configuration type is not associated with any service in the blueprint: " + 
type));
-
-      Config config = configFactory.createReadOnly(type, groupName, 
entry.getValue(), null);
-      //todo: attributes
-      Map<String, Config> serviceConfigs = groupConfigs.get(service);
-      if (serviceConfigs == null) {
-        serviceConfigs = new HashMap<>();
-        groupConfigs.put(service, serviceConfigs);
-      }
-      serviceConfigs.put(type, config);
+      List<Pair<StackId, String>> stackServices = 
stack.getStackServicesForConfigType(type).
+        filter(each -> 
topology.getServiceTypes().contains(each.getValue())).collect(Collectors.toList());
+      Preconditions.checkArgument(!stackServices.isEmpty(), "Specified 
configuration type is not associated with any service in the blueprint: " + 
type);
+      stackServices.forEach( stackService -> {
+        Config config = configFactory.createReadOnly(type, groupName, 
entry.getValue(), null);
+        //todo: attributes
+        groupConfigs.computeIfAbsent(stackService, __ -> new 
HashMap<>()).put(type, config);
+      });
     }
 
     String bpName = topology.getBlueprintName();
-    for (Map.Entry<String, Map<String, Config>> entry : 
groupConfigs.entrySet()) {
-      String service = entry.getKey();
+    for (Map.Entry<Pair<StackId, String>, Map<String, Config>> entry : 
groupConfigs.entrySet()) {
+      Pair<StackId, String> stackService = entry.getKey();
+      StackId stackId = entry.getKey().getLeft();
+      String service = entry.getKey().getRight();
       Map<String, Config> serviceConfigs = entry.getValue();
       String absoluteGroupName = getConfigurationGroupName(bpName, groupName);
       Collection<String> groupHosts;
@@ -747,15 +744,10 @@ public class AmbariContext {
       }
 
       final Map<String, Host> clusterHosts = 
getController().getClusters().getHostsForCluster(clusterName);
-      Iterable<String> filteredGroupHosts = Iterables.filter(groupHosts, new 
com.google.common.base.Predicate<String>() {
-        @Override
-        public boolean apply(@Nullable String groupHost) {
-          return clusterHosts.containsKey(groupHost);
-        }
-      });
+      Iterable<String> filteredGroupHosts = Iterables.filter(groupHosts, 
groupHost -> clusterHosts.containsKey(groupHost));
 
       ConfigGroupRequest request = new ConfigGroupRequest(null, clusterName,
-        absoluteGroupName, service, DEFAULT_SERVICE_GROUP_NAME, service, "Host 
Group Configuration",
+        absoluteGroupName, service, stackId.getStackName(), service, "Host 
Group Configuration",
         Sets.newHashSet(filteredGroupHosts), serviceConfigs);
 
       // get the config group provider and create config group resource
diff --git 
a/ambari-server/src/main/java/org/apache/ambari/server/utils/CollectionUtils.java
 
b/ambari-server/src/main/java/org/apache/ambari/server/utils/CollectionUtils.java
new file mode 100644
index 0000000..d5e7828
--- /dev/null
+++ 
b/ambari-server/src/main/java/org/apache/ambari/server/utils/CollectionUtils.java
@@ -0,0 +1,71 @@
+/*
+ * 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.utils;
+
+import java.util.AbstractMap;
+import java.util.Collections;
+import java.util.Set;
+import java.util.concurrent.ConcurrentMap;
+
+/**
+ * Utilities for collections
+ */
+public class CollectionUtils {
+
+  /**
+   * Returns a singleton instance of an empty immutable {@link ConcurrentMap}
+   * @param <K> key type
+   * @param <V> value type
+   * @return a singleton instance of an empty immutable {@link ConcurrentMap}
+   */
+  public static <K, V> ConcurrentMap<K, V> emptyConcurrentMap() {
+    return (ConcurrentMap<K, V>)EmptyConcurrentMap.INSTANCE;
+  }
+
+
+  private static class EmptyConcurrentMap<K, V> extends AbstractMap<K, V> 
implements ConcurrentMap<K, V> {
+    @SuppressWarnings({"rawtypes"})
+    private static final EmptyConcurrentMap INSTANCE = new 
EmptyConcurrentMap();
+
+    @Override
+    public Set<Entry<K, V>> entrySet() {
+      return Collections.emptySet();
+    }
+
+    @Override
+    public boolean remove(Object key, Object value) {
+      throw new UnsupportedOperationException();
+    }
+
+    @Override
+    public boolean replace(K key, V oldValue, V newValue) {
+      throw new UnsupportedOperationException();
+    }
+
+    @Override
+    public V replace(K key, V newValue) {
+      throw new UnsupportedOperationException();
+    }
+
+    @Override
+    public V putIfAbsent(K key, V value) {
+      throw new UnsupportedOperationException();
+    }
+  }
+}
diff --git 
a/ambari-server/src/test/java/org/apache/ambari/server/controller/internal/CompositeStackTest.java
 
b/ambari-server/src/test/java/org/apache/ambari/server/controller/internal/CompositeStackTest.java
index 0561bb4..9ca6cf7 100644
--- 
a/ambari-server/src/test/java/org/apache/ambari/server/controller/internal/CompositeStackTest.java
+++ 
b/ambari-server/src/test/java/org/apache/ambari/server/controller/internal/CompositeStackTest.java
@@ -29,9 +29,12 @@ import java.util.Set;
 import org.apache.ambari.server.stack.StackManager;
 import org.apache.ambari.server.stack.StackManagerTest;
 import org.apache.ambari.server.state.StackId;
+import org.apache.commons.lang3.tuple.Pair;
 import org.junit.BeforeClass;
 import org.junit.Test;
 
+import com.google.common.collect.ImmutableSet;
+
 public class CompositeStackTest {
 
   private static Set<Stack> elements;
@@ -66,6 +69,22 @@ public class CompositeStackTest {
   }
 
   @Test
+  public void getServicesByConfigType() {
+    assertEquals(
+      ImmutableSet.of("HDFS"),
+      composite.getServicesForConfigType("hdfs-site").collect(toSet()));
+  }
+
+  @Test
+  public void getStackServicesByConfigType() {
+    assertEquals(
+      ImmutableSet.of(
+        Pair.of(new StackId("HDP", "0.1"), "HDFS"),
+        Pair.of(new StackId("OTHER", "1.0"), "HDFS")),
+      composite.getStackServicesForConfigType("hdfs-site").collect(toSet()));
+  }
+
+  @Test
   public void getComponents() {
     Set<String> components = new HashSet<>(composite.getComponents());
     for (Stack stack : elements) {
diff --git 
a/ambari-server/src/test/java/org/apache/ambari/server/controller/internal/StackTest.java
 
b/ambari-server/src/test/java/org/apache/ambari/server/controller/internal/StackTest.java
index e6794b8..f9a50f3 100644
--- 
a/ambari-server/src/test/java/org/apache/ambari/server/controller/internal/StackTest.java
+++ 
b/ambari-server/src/test/java/org/apache/ambari/server/controller/internal/StackTest.java
@@ -28,7 +28,9 @@ import static org.junit.Assert.assertSame;
 import java.util.Collection;
 import java.util.Collections;
 import java.util.HashSet;
+import java.util.List;
 import java.util.Set;
+import java.util.stream.Collectors;
 import java.util.stream.Stream;
 
 import org.apache.ambari.server.controller.StackLevelConfigurationResponse;
@@ -45,6 +47,7 @@ import org.easymock.EasyMockSupport;
 import org.junit.Before;
 import org.junit.Test;
 
+import com.google.common.collect.ImmutableList;
 import com.google.common.collect.ImmutableSet;
 
 /**
@@ -57,6 +60,7 @@ public class StackTest {
   private static final String STACK_CONFIG_FILE = STACK_CONFIG_TYPE + ".xml";
   private static final String SERVICE_CONFIG_TYPE = "test-site";
   private static final String SERVICE_CONFIG_FILE = SERVICE_CONFIG_TYPE + 
".xml";
+  public static final String SERVICE_NAME = "some service";
 
   private StackInfo stackInfo;
   private ServiceInfo serviceInfo;
@@ -73,7 +77,7 @@ public class StackTest {
     stackInfo.setVersion(STACK_ID.getStackVersion());
 
     serviceInfo = new ServiceInfo();
-    serviceInfo.setName("some service");
+    serviceInfo.setName(SERVICE_NAME);
     stackInfo.getServices().add(serviceInfo);
 
     componentInfo = new ComponentInfo();
@@ -136,6 +140,54 @@ public class StackTest {
   }
 
   @Test
+  public void getServicesForConfigType() throws Exception {
+    // GIVEN
+    Stack stack = new Stack(stackInfo);
+
+    // WHEN
+    List<String> services = 
stack.getServicesForConfigType(SERVICE_CONFIG_TYPE).collect(Collectors.toList());
+
+    // THEN
+    assertEquals(ImmutableList.of(SERVICE_NAME), services);
+  }
+
+  @Test
+  public void getServicesForConfigType_ClusterEnv() throws Exception {
+    // GIVEN
+    Stack stack = new Stack(stackInfo);
+
+    // WHEN
+    List<String> services = 
stack.getServicesForConfigType(STACK_CONFIG_TYPE).collect(Collectors.toList());
+
+    // THEN
+    assertEquals(ImmutableList.of(), services);
+  }
+
+  @Test
+  public void getStackServicesForConfigType() throws Exception {
+    // GIVEN
+    Stack stack = new Stack(stackInfo);
+
+    // WHEN
+    List<Pair<StackId, String>> stackServices = 
stack.getStackServicesForConfigType(SERVICE_CONFIG_TYPE).collect(Collectors.toList());
+
+    // THEN
+    assertEquals(ImmutableList.of(Pair.of(STACK_ID, SERVICE_NAME)), 
stackServices);
+  }
+
+  @Test
+  public void getStackServicesForConfigType_ClusterEnv() throws Exception {
+    // GIVEN
+    Stack stack = new Stack(stackInfo);
+
+    // WHEN
+    List<String> stackServices = 
stack.getServicesForConfigType(STACK_CONFIG_TYPE).collect(Collectors.toList());
+
+    // THEN
+    assertEquals(ImmutableList.of(), stackServices);
+  }
+
+  @Test
   public void getServicesForOwnStackId() throws Exception {
     // GIVEN
     Stack stack = new Stack(stackInfo);
diff --git 
a/ambari-server/src/test/java/org/apache/ambari/server/state/ConfigGroupTest.java
 
b/ambari-server/src/test/java/org/apache/ambari/server/state/ConfigGroupTest.java
index 3eb7ab6..0b2e9b8 100644
--- 
a/ambari-server/src/test/java/org/apache/ambari/server/state/ConfigGroupTest.java
+++ 
b/ambari-server/src/test/java/org/apache/ambari/server/state/ConfigGroupTest.java
@@ -32,6 +32,7 @@ import org.apache.ambari.server.orm.dao.ClusterServiceDAO;
 import org.apache.ambari.server.orm.dao.ConfigGroupDAO;
 import org.apache.ambari.server.orm.dao.ConfigGroupHostMappingDAO;
 import org.apache.ambari.server.orm.dao.ServiceGroupDAO;
+import org.apache.ambari.server.orm.entities.ClusterConfigEntity;
 import org.apache.ambari.server.orm.entities.ClusterEntity;
 import org.apache.ambari.server.orm.entities.ClusterServiceEntity;
 import org.apache.ambari.server.orm.entities.ConfigGroupConfigMappingEntity;
@@ -63,6 +64,7 @@ public class ConfigGroupTest {
   private ClusterDAO clusterDAO;
   private ClusterServiceDAO clusterServiceDAO;
   private ServiceGroupDAO serviceGroupDAO;
+  private StackId stackId;
 
   @Before
   public void setup() throws Exception {
@@ -78,7 +80,7 @@ public class ConfigGroupTest {
     clusterDAO = injector.getInstance(ClusterDAO.class);
     serviceGroupDAO = injector.getInstance(ServiceGroupDAO.class);
 
-    StackId stackId = new StackId("HDP-0.1");
+    stackId = new StackId("HDP-0.1");
     OrmTestHelper helper = injector.getInstance(OrmTestHelper.class);
     helper.createMpack(stackId);
 
@@ -107,7 +109,7 @@ public class ConfigGroupTest {
     Map<String, String> attributes = new HashMap<>();
     attributes.put("a", "true");
     propertiesAttributes.put("final", attributes);
-    Config config = configFactory.createNew(cluster, "hdfs-site", 
"testversion", properties, propertiesAttributes);
+    Config config = configFactory.createNew(stackId, cluster, "hdfs-site", 
"testversion", properties, propertiesAttributes, 1L);
 
     Host host = clusters.getHost("h1");
 
@@ -130,6 +132,7 @@ public class ConfigGroupTest {
     clusterServiceEntity.setServiceGroupEntity(serviceGroupEntity);
     clusterServiceEntity.setServiceName("HDFS");
     clusterServiceEntity.setServiceType("HDFS");
+    clusterServiceEntity.setServiceId(1L);
     clusterServiceDAO.create(clusterServiceEntity);
 
     ConfigGroup configGroup = configGroupFactory.createNew(cluster, 1L, 
clusterServiceEntity.getServiceId(), "cg-test", "HDFS", "New HDFS configs for 
h1", configs, hosts);
@@ -151,7 +154,9 @@ public class ConfigGroupTest {
     Assert.assertNotNull(configMappingEntity);
     Assert.assertEquals("hdfs-site", configMappingEntity.getConfigType());
     Assert.assertEquals("testversion", configMappingEntity.getVersionTag());
-    Assert.assertNotNull(configMappingEntity.getClusterConfigEntity());
+    ClusterConfigEntity clusterConfigEntity = 
configMappingEntity.getClusterConfigEntity();
+    Assert.assertNotNull(clusterConfigEntity);
+    Assert.assertEquals(Long.valueOf(1L), clusterConfigEntity.getServiceId());
     Assert.assertTrue(configMappingEntity
       .getClusterConfigEntity().getData().contains("a"));
     Assert.assertEquals("{\"final\":{\"a\":\"true\"}}", configMappingEntity
diff --git 
a/ambari-server/src/test/java/org/apache/ambari/server/topology/AmbariContextTest.java
 
b/ambari-server/src/test/java/org/apache/ambari/server/topology/AmbariContextTest.java
index 3ea53fc..0f8ea19 100644
--- 
a/ambari-server/src/test/java/org/apache/ambari/server/topology/AmbariContextTest.java
+++ 
b/ambari-server/src/test/java/org/apache/ambari/server/topology/AmbariContextTest.java
@@ -269,7 +269,9 @@ public class AmbariContextTest {
     expect(stack.getVersion()).andReturn(STACK_VERSION).anyTimes();
 
     for (Map.Entry<String, String> entry : 
configTypeServiceMapping.entrySet()) {
-      
expect(stack.getServicesForConfigType(entry.getKey())).andReturn(Stream.of(entry.getValue())).anyTimes();
+      expect(stack.getStackServicesForConfigType(entry.getKey()))
+        .andReturn( Stream.of(Pair.of(STACK_ID, entry.getValue())) )
+        .anyTimes();
     }
 
     expect(controller.getClusters()).andReturn(clusters).anyTimes();

Reply via email to