AMBARI-22319. Allow the same config type to belong to multiple services 
(amagyar)


Project: http://git-wip-us.apache.org/repos/asf/ambari/repo
Commit: http://git-wip-us.apache.org/repos/asf/ambari/commit/193446d0
Tree: http://git-wip-us.apache.org/repos/asf/ambari/tree/193446d0
Diff: http://git-wip-us.apache.org/repos/asf/ambari/diff/193446d0

Branch: refs/heads/branch-feature-AMBARI-22008-isilon
Commit: 193446d05987e5358d5d24debad1973ace93ab7e
Parents: 45b6549
Author: Attila Magyar <amag...@hortonworks.com>
Authored: Mon Oct 30 11:34:27 2017 +0100
Committer: Attila Magyar <amag...@hortonworks.com>
Committed: Fri Jan 5 08:54:45 2018 +0100

----------------------------------------------------------------------
 .../AmbariManagementControllerImpl.java         |  8 +-
 .../server/controller/internal/Stack.java       | 14 ++++
 .../ambari/server/state/ConfigHelper.java       |  2 +-
 .../server/state/cluster/ClusterImpl.java       | 79 +++++++-------------
 .../ambari/server/topology/AmbariContext.java   |  7 +-
 .../server/state/cluster/ClusterTest.java       |  4 +
 .../server/topology/AmbariContextTest.java      |  5 +-
 7 files changed, 58 insertions(+), 61 deletions(-)
----------------------------------------------------------------------


http://git-wip-us.apache.org/repos/asf/ambari/blob/193446d0/ambari-server/src/main/java/org/apache/ambari/server/controller/AmbariManagementControllerImpl.java
----------------------------------------------------------------------
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 198b617..a2d8494 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
@@ -802,14 +802,8 @@ public class AmbariManagementControllerImpl implements 
AmbariManagementControlle
 
     // If the config type is for a service, then allow a user with 
SERVICE_MODIFY_CONFIGS to
     // update, else ensure the user has CLUSTER_MODIFY_CONFIGS
-    String service = null;
+    String service = cluster.getServiceByConfigType(configType);
 
-    try {
-      service = 
cluster.getServiceForConfigTypes(Collections.singleton(configType));
-    } catch (IllegalArgumentException e) {
-      // Ignore this since we may have hit a config type that spans multiple 
services. This may
-      // happen in unit test cases but should not happen with later versions 
of stacks.
-    }
 
     // Get the changes so that the user's intention can be determined. For 
example, maybe
     // the user wants to change the run-as user for a service or maybe the the 
cluster-wide

http://git-wip-us.apache.org/repos/asf/ambari/blob/193446d0/ambari-server/src/main/java/org/apache/ambari/server/controller/internal/Stack.java
----------------------------------------------------------------------
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 f8feef2..912d9be 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
@@ -18,10 +18,12 @@
 
 package org.apache.ambari.server.controller.internal;
 
+import java.util.ArrayList;
 import java.util.Collection;
 import java.util.Collections;
 import java.util.HashMap;
 import java.util.HashSet;
+import java.util.List;
 import java.util.Map;
 import java.util.Set;
 
@@ -494,6 +496,18 @@ public class Stack {
         "Specified configuration type is not associated with any service: " + 
config);
   }
 
+  public List<String> getServicesForConfigType(String config) {
+    List<String> serviceNames = new ArrayList<>();
+    for (Map.Entry<String, Map<String, Map<String, ConfigProperty>>> entry : 
serviceConfigurations.entrySet()) {
+      Map<String, Map<String, ConfigProperty>> typeMap = entry.getValue();
+      String serviceName = entry.getKey();
+      if (typeMap.containsKey(config) && 
!getExcludedConfigurationTypes(serviceName).contains(config)) {
+        serviceNames.add(serviceName);
+      }
+    }
+    return serviceNames;
+  }
+
   /**
    * Return the dependencies specified for the given component.
    *

http://git-wip-us.apache.org/repos/asf/ambari/blob/193446d0/ambari-server/src/main/java/org/apache/ambari/server/state/ConfigHelper.java
----------------------------------------------------------------------
diff --git 
a/ambari-server/src/main/java/org/apache/ambari/server/state/ConfigHelper.java 
b/ambari-server/src/main/java/org/apache/ambari/server/state/ConfigHelper.java
index 6813fc0..3b0ee0f 100644
--- 
a/ambari-server/src/main/java/org/apache/ambari/server/state/ConfigHelper.java
+++ 
b/ambari-server/src/main/java/org/apache/ambari/server/state/ConfigHelper.java
@@ -1123,7 +1123,7 @@ public class ConfigHelper {
 
       if (null != baseConfig) {
         try {
-          String service = 
cluster.getServiceForConfigTypes(Collections.singleton(type));
+          String service = cluster.getServiceByConfigType(type);
           if (!serviceMapped.containsKey(service)) {
             serviceMapped.put(service, new HashSet<>());
           }

http://git-wip-us.apache.org/repos/asf/ambari/blob/193446d0/ambari-server/src/main/java/org/apache/ambari/server/state/cluster/ClusterImpl.java
----------------------------------------------------------------------
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 2266c62..2f3afd6 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
@@ -18,6 +18,8 @@
 
 package org.apache.ambari.server.state.cluster;
 
+import static java.util.stream.Collectors.toList;
+
 import java.text.MessageFormat;
 import java.util.ArrayList;
 import java.util.Collection;
@@ -29,13 +31,13 @@ import java.util.Iterator;
 import java.util.List;
 import java.util.Map;
 import java.util.Map.Entry;
+import java.util.Objects;
 import java.util.Set;
 import java.util.concurrent.ConcurrentHashMap;
 import java.util.concurrent.ConcurrentMap;
 import java.util.concurrent.ConcurrentSkipListMap;
 import java.util.concurrent.CopyOnWriteArrayList;
 import java.util.concurrent.locks.ReadWriteLock;
-import java.util.stream.Collectors;
 
 import javax.annotation.Nullable;
 import javax.persistence.EntityManager;
@@ -1135,7 +1137,7 @@ public class ClusterImpl implements Cluster {
     return clusterDAO.getLatestConfigurationsWithTypes(clusterId, 
getDesiredStackVersion(), types)
       .stream()
       .map(clusterConfigEntity -> configFactory.createExisting(this, 
clusterConfigEntity))
-      .collect(Collectors.toList());
+      .collect(toList());
   }
 
   @Override
@@ -1581,37 +1583,35 @@ public class ClusterImpl implements Cluster {
 
   @Override
   public String getServiceForConfigTypes(Collection<String> configTypes) {
-    //debug
-    LOG.info("Looking for service for config types {}", configTypes);
-    String serviceName = null;
-    for (String configType : configTypes) {
-      for (Entry<String, String> entry : serviceConfigTypes.entries()) {
-        if (StringUtils.equals(entry.getValue(), configType)) {
-          if (serviceName != null) {
-            if (entry.getKey()!=null && !StringUtils.equals(serviceName, 
entry.getKey())) {
-              throw new IllegalArgumentException(String.format("Config type %s 
belongs to %s service, " +
-                "but also qualified for %s", configType, serviceName, 
entry.getKey()));
-            }
-          } else {
-            serviceName = entry.getKey();
-          }
-        }
-      }
+    List<String> serviceNames = configTypes.stream()
+      .map(this::getServiceByConfigType)
+      .filter(Objects::nonNull)
+      .collect(toList());
+    boolean allTheSame = new HashSet<>(serviceNames).size() <= 1;
+    if (!allTheSame) {
+      throw new IllegalArgumentException(String.format(
+        "Config types: %s should belong to a single installed service. But 
they belong to: %s", configTypes, serviceNames));
     }
-    LOG.info("Service {} returning", serviceName);
-    return serviceName;
+    return serviceNames.isEmpty() ? null : serviceNames.get(0);
+  }
+
+  public List<String> serviceNameByConfigType(String configType) {
+    return serviceConfigTypes.entries().stream()
+      .filter(entry -> StringUtils.equals(entry.getValue(), configType))
+      .map(entry -> entry.getKey())
+      .collect(toList());
   }
 
   @Override
   public String getServiceByConfigType(String configType) {
-    for (Entry<String, String> entry : serviceConfigTypes.entries()) {
-      String serviceName = entry.getKey();
-      String type = entry.getValue();
-      if (StringUtils.equals(type, configType)) {
-        return serviceName;
-      }
-    }
-    return null;
+    return serviceNameByConfigType(configType).stream()
+      .filter(this::isServiceInstalled)
+      .findFirst()
+      .orElse(null);
+  }
+
+  private boolean isServiceInstalled(String serviceName) {
+    return services.get(serviceName) != null;
   }
 
   @Override
@@ -1894,28 +1894,7 @@ public class ClusterImpl implements Cluster {
 
   @Transactional
   ServiceConfigVersionResponse applyConfigs(Set<Config> configs, String user, 
String serviceConfigVersionNote) {
-
-    String serviceName = null;
-    for (Config config : configs) {
-      for (Entry<String, String> entry : serviceConfigTypes.entries()) {
-        if (StringUtils.equals(entry.getValue(), config.getType())) {
-          if (serviceName == null) {
-            serviceName = entry.getKey();
-            break;
-          } else if (!serviceName.equals(entry.getKey())) {
-            String error = String.format("Updating configs for multiple 
services by a " +
-                "single API request isn't supported. Conflicting services %s 
and %s for %s",
-                                         serviceName, entry.getKey(), 
config.getType());
-            IllegalArgumentException exception = new 
IllegalArgumentException(error);
-            LOG.error(error + ", config version not created for {}", 
serviceName);
-            throw exception;
-          } else {
-            break;
-          }
-        }
-      }
-    }
-
+    String serviceName = 
getServiceForConfigTypes(configs.stream().map(Config::getType).collect(toList()));
     // update the selected flag for every config type
     ClusterEntity clusterEntity = getClusterEntity();
     Collection<ClusterConfigEntity> clusterConfigs = 
clusterEntity.getClusterConfigEntities();

http://git-wip-us.apache.org/repos/asf/ambari/blob/193446d0/ambari-server/src/main/java/org/apache/ambari/server/topology/AmbariContext.java
----------------------------------------------------------------------
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 933afa2..9e7eb17 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
@@ -732,7 +732,12 @@ 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.getServiceForConfigType(type);
+      List<String> services = stack.getServicesForConfigType(type);
+      String service = services.stream()
+        .filter(each -> topology.getBlueprint().getServices().contains(each))
+        .findFirst()
+        .orElseThrow(() -> new IllegalArgumentException("Specified 
configuration type is not associated with any service: " + type));
+
       Config config = configFactory.createReadOnly(type, groupName, 
entry.getValue(), null);
       //todo: attributes
       Map<String, Config> serviceConfigs = groupConfigs.get(service);

http://git-wip-us.apache.org/repos/asf/ambari/blob/193446d0/ambari-server/src/test/java/org/apache/ambari/server/state/cluster/ClusterTest.java
----------------------------------------------------------------------
diff --git 
a/ambari-server/src/test/java/org/apache/ambari/server/state/cluster/ClusterTest.java
 
b/ambari-server/src/test/java/org/apache/ambari/server/state/cluster/ClusterTest.java
index 4b09c6d..8b37f72 100644
--- 
a/ambari-server/src/test/java/org/apache/ambari/server/state/cluster/ClusterTest.java
+++ 
b/ambari-server/src/test/java/org/apache/ambari/server/state/cluster/ClusterTest.java
@@ -1203,6 +1203,7 @@ public class ClusterTest {
   @Test
   public void testServiceConfigVersions() throws Exception {
     createDefaultCluster();
+    c1.addService("HDFS", helper.getOrCreateRepositoryVersion(new 
StackId("HDP", "0.1"), "0.1"));
 
     Config config1 = configFactory.createNew(c1, "hdfs-site", "version1",
       new HashMap<String, String>() {{ put("a", "b"); }}, new HashMap<>());
@@ -1261,6 +1262,7 @@ public class ClusterTest {
   @Test
   public void testSingleServiceVersionForMultipleConfigs() throws Exception {
     createDefaultCluster();
+    c1.addService("HDFS", helper.getOrCreateRepositoryVersion(new 
StackId("HDP", "0.1"), "0.1"));
 
     Config config1 = configFactory.createNew(c1, "hdfs-site", "version1",
       new HashMap<String, String>() {{ put("a", "b"); }}, new HashMap<>());
@@ -1383,6 +1385,7 @@ public class ClusterTest {
   public void testAllServiceConfigVersionsWithConfigGroups() throws Exception {
     // Given
     createDefaultCluster();
+    c1.addService("HDFS", helper.getOrCreateRepositoryVersion(new 
StackId("HDP", "0.1"), "0.1"));
 
     Config hdfsSiteConfigV1 = configFactory.createNew(c1, "hdfs-site", 
"version1",
         ImmutableMap.of("p1", "v1"), ImmutableMap.of());
@@ -1442,6 +1445,7 @@ public class ClusterTest {
   public void testAllServiceConfigVersionsWithDeletedConfigGroups() throws 
Exception {
     // Given
     createDefaultCluster();
+    c1.addService("HDFS", helper.getOrCreateRepositoryVersion(new 
StackId("HDP", "0.1"), "0.1"));
 
     Config hdfsSiteConfigV1 = configFactory.createNew(c1, "hdfs-site", 
"version1",
         ImmutableMap.of("p1", "v1"), ImmutableMap.of());

http://git-wip-us.apache.org/repos/asf/ambari/blob/193446d0/ambari-server/src/test/java/org/apache/ambari/server/topology/AmbariContextTest.java
----------------------------------------------------------------------
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 0deeae9..5128c65 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
@@ -18,6 +18,7 @@
 
 package org.apache.ambari.server.topology;
 
+import static java.util.Collections.singletonList;
 import static org.easymock.EasyMock.anyObject;
 import static org.easymock.EasyMock.capture;
 import static org.easymock.EasyMock.createMock;
@@ -215,7 +216,7 @@ public class AmbariContextTest {
     
expect(repositoryVersion.getType()).andReturn(RepositoryType.STANDARD).atLeastOnce();
 
     
expect(repositoryVersionDAO.findByStack(EasyMock.anyObject(StackId.class))).andReturn(
-        Collections.singletonList(repositoryVersion)).atLeastOnce();
+        singletonList(repositoryVersion)).atLeastOnce();
     replay(repositoryVersionDAO, repositoryVersion);
 
     context.configFactory = configFactory;
@@ -240,7 +241,7 @@ public class AmbariContextTest {
     expect(stack.getVersion()).andReturn(STACK_VERSION).anyTimes();
 
     for (Map.Entry<String, String> entry : 
configTypeServiceMapping.entrySet()) {
-      
expect(stack.getServiceForConfigType(entry.getKey())).andReturn(entry.getValue()).anyTimes();
+      
expect(stack.getServicesForConfigType(entry.getKey())).andReturn(singletonList(entry.getValue())).anyTimes();
     }
 
     expect(controller.getClusters()).andReturn(clusters).anyTimes();

Reply via email to