You are viewing a plain text version of this content. The canonical link for it is here.
Posted to commits@ambari.apache.org by st...@apache.org on 2017/01/19 21:54:52 UTC
ambari git commit: AMBARI-19597. Blueprint installation should accept
quick link profile. (Balazs Bence Sari via stoader
Repository: ambari
Updated Branches:
refs/heads/branch-2.5 491854239 -> 99bb98840
AMBARI-19597. Blueprint installation should accept quick link profile. (Balazs Bence Sari via stoader
Project: http://git-wip-us.apache.org/repos/asf/ambari/repo
Commit: http://git-wip-us.apache.org/repos/asf/ambari/commit/99bb9884
Tree: http://git-wip-us.apache.org/repos/asf/ambari/tree/99bb9884
Diff: http://git-wip-us.apache.org/repos/asf/ambari/diff/99bb9884
Branch: refs/heads/branch-2.5
Commit: 99bb988406184aa12c4d3d0b12e39b83025b7385
Parents: 4918542
Author: Balazs Bence Sari <bs...@hortonworks.com>
Authored: Thu Jan 19 22:11:14 2017 +0100
Committer: Toader, Sebastian <st...@hortonworks.com>
Committed: Thu Jan 19 22:54:39 2017 +0100
----------------------------------------------------------------------
.../AmbariManagementControllerImpl.java | 10 +-
.../internal/ClusterResourceProvider.java | 17 +-
.../internal/ProvisionClusterRequest.java | 36 +++
.../quicklinksprofile/AcceptAllFilter.java | 5 +
.../state/quicklinksprofile/Component.java | 3 +
.../server/state/quicklinksprofile/Filter.java | 7 +-
.../quicklinksprofile/QuickLinksProfile.java | 9 +
.../QuickLinksProfileBuilder.java | 128 ++++++++++
.../QuickLinksProfileEvaluationException.java | 4 +
.../QuickLinksProfileParser.java | 6 +-
.../server/state/quicklinksprofile/Service.java | 3 +
.../ambari/server/topology/TopologyManager.java | 38 +++
.../internal/ProvisionClusterRequestTest.java | 112 +++++++--
.../QuickLinkArtifactResourceProviderTest.java | 1 -
.../QuickLinksProfileBuilderTest.java | 234 +++++++++++++++++++
.../server/topology/TopologyManagerTest.java | 138 ++++++++---
16 files changed, 681 insertions(+), 70 deletions(-)
----------------------------------------------------------------------
http://git-wip-us.apache.org/repos/asf/ambari/blob/99bb9884/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 fc241f5..6feccb4 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
@@ -184,6 +184,7 @@ import org.apache.ambari.server.state.State;
import org.apache.ambari.server.state.configgroup.ConfigGroupFactory;
import org.apache.ambari.server.state.quicklinksprofile.QuickLinkVisibilityController;
import org.apache.ambari.server.state.quicklinksprofile.QuickLinkVisibilityControllerFactory;
+import org.apache.ambari.server.state.quicklinksprofile.QuickLinksProfile;
import org.apache.ambari.server.state.repository.VersionDefinitionXml;
import org.apache.ambari.server.state.scheduler.RequestExecutionFactory;
import org.apache.ambari.server.state.stack.RepositoryXml;
@@ -238,12 +239,6 @@ public class AmbariManagementControllerImpl implements AmbariManagementControlle
public static final String SKIP_INSTALL_FOR_COMPONENTS = "skipInstallForComponents";
public static final String DONT_SKIP_INSTALL_FOR_COMPONENTS = "dontSkipInstallForComponents";
- /**
- * The name of the ambari setting that stores the quicklinks profile.
- * See {@link org.apache.ambari.server.state.quicklinksprofile.QuickLinksProfile}
- */
- public static final String SETTING_QUICKLINKS_PROFILE = "QuickLinksProfile";
-
private final Clusters clusters;
private final ActionManager actionManager;
@@ -504,7 +499,6 @@ public class AmbariManagementControllerImpl implements AmbariManagementControlle
c.createClusterVersion(stackId, versionEntity.getVersion(), getAuthName(), RepositoryVersionState.INIT);
}
}
-
}
@Override
@@ -5517,7 +5511,7 @@ public class AmbariManagementControllerImpl implements AmbariManagementControlle
@Override
public QuickLinkVisibilityController getQuicklinkVisibilityController() {
- SettingEntity entity = settingDAO.findByName(SETTING_QUICKLINKS_PROFILE);
+ SettingEntity entity = settingDAO.findByName(QuickLinksProfile.SETTING_NAME_QUICKLINKS_PROFILE);
String quickLinkProfileJson = null != entity ? entity.getContent() : null;
return QuickLinkVisibilityControllerFactory.get(quickLinkProfileJson);
}
http://git-wip-us.apache.org/repos/asf/ambari/blob/99bb9884/ambari-server/src/main/java/org/apache/ambari/server/controller/internal/ClusterResourceProvider.java
----------------------------------------------------------------------
diff --git a/ambari-server/src/main/java/org/apache/ambari/server/controller/internal/ClusterResourceProvider.java b/ambari-server/src/main/java/org/apache/ambari/server/controller/internal/ClusterResourceProvider.java
index cb30f2d..613ab3f 100644
--- a/ambari-server/src/main/java/org/apache/ambari/server/controller/internal/ClusterResourceProvider.java
+++ b/ambari-server/src/main/java/org/apache/ambari/server/controller/internal/ClusterResourceProvider.java
@@ -82,6 +82,7 @@ public class ClusterResourceProvider extends AbstractControllerResourceProvider
public static final String BLUEPRINT_PROPERTY_ID = PropertyHelper.getPropertyId(null, "blueprint");
public static final String SECURITY_PROPERTY_ID = PropertyHelper.getPropertyId(null, "security");
public static final String CREDENTIALS_PROPERTY_ID = PropertyHelper.getPropertyId(null, "credentials");
+ public static final String QUICKLINKS_PROFILE_PROPERTY_ID = PropertyHelper.getPropertyId(null, "quicklinks_profile");
public static final String SESSION_ATTRIBUTES_PROPERTY_ID = "session_attributes";
public static final String CLUSTER_REPO_VERSION = "Clusters/repository_version";
@@ -152,6 +153,7 @@ public class ClusterResourceProvider extends AbstractControllerResourceProvider
propertyIds.add(SECURITY_PROPERTY_ID);
propertyIds.add(CREDENTIALS_PROPERTY_ID);
propertyIds.add(CLUSTER_REPO_VERSION);
+ propertyIds.add(QUICKLINKS_PROFILE_PROPERTY_ID);
}
@@ -356,13 +358,13 @@ public class ClusterResourceProvider extends AbstractControllerResourceProvider
for (Map<String, Object> propertyMap : getPropertyMaps(predicate)) {
final ClusterRequest clusterRequest = getRequest(propertyMap);
- modifyResources(new Command<Void>() {
- @Override
- public Void invoke() throws AmbariException {
- getManagementController().deleteCluster(clusterRequest);
- return null;
- }
- });
+ modifyResources(new Command<Void>() {
+ @Override
+ public Void invoke() throws AmbariException {
+ getManagementController().deleteCluster(clusterRequest);
+ return null;
+ }
+ });
}
notifyDelete(Resource.Type.Cluster, predicate);
return getRequestStatus(null);
@@ -436,6 +438,7 @@ public class ClusterResourceProvider extends AbstractControllerResourceProvider
return cr;
}
+
/**
* Get the map of session attributes from the given property map.
*
http://git-wip-us.apache.org/repos/asf/ambari/blob/99bb9884/ambari-server/src/main/java/org/apache/ambari/server/controller/internal/ProvisionClusterRequest.java
----------------------------------------------------------------------
diff --git a/ambari-server/src/main/java/org/apache/ambari/server/controller/internal/ProvisionClusterRequest.java b/ambari-server/src/main/java/org/apache/ambari/server/controller/internal/ProvisionClusterRequest.java
index a35da86..2c8d09a 100644
--- a/ambari-server/src/main/java/org/apache/ambari/server/controller/internal/ProvisionClusterRequest.java
+++ b/ambari-server/src/main/java/org/apache/ambari/server/controller/internal/ProvisionClusterRequest.java
@@ -27,6 +27,8 @@ import java.util.Set;
import org.apache.ambari.server.api.predicate.InvalidQueryException;
import org.apache.ambari.server.security.encryption.CredentialStoreType;
import org.apache.ambari.server.stack.NoSuchStackException;
+import org.apache.ambari.server.state.quicklinksprofile.QuickLinksProfileBuilder;
+import org.apache.ambari.server.state.quicklinksprofile.QuickLinksProfileEvaluationException;
import org.apache.ambari.server.topology.ConfigRecommendationStrategy;
import org.apache.ambari.server.topology.Configuration;
import org.apache.ambari.server.topology.ConfigurationFactory;
@@ -105,6 +107,17 @@ public class ProvisionClusterRequest extends BaseClusterRequest {
*/
public static final String REPO_VERSION_PROPERTY = "repository_version";
+ /**
+ * The global quick link filters property
+ */
+ public static final String QUICKLINKS_PROFILE_FILTERS_PROPERTY = "quicklinks_profile/filters";
+
+ /**
+ * The service and component level quick link filters property
+ */
+ public static final String QUICKLINKS_PROFILE_SERVICES_PROPERTY = "quicklinks_profile/services";
+
+
/**
* configuration factory
@@ -130,6 +143,8 @@ public class ProvisionClusterRequest extends BaseClusterRequest {
private String repoVersion;
+ private final String quickLinksProfileJson;
+
private final static Logger LOG = LoggerFactory.getLogger(ProvisionClusterRequest.class);
/**
@@ -173,8 +188,23 @@ public class ProvisionClusterRequest extends BaseClusterRequest {
this.configRecommendationStrategy = parseConfigRecommendationStrategy(properties);
setProvisionAction(parseProvisionAction(properties));
+
+ try {
+ this.quickLinksProfileJson = processQuickLinksProfile(properties);
+ }
+ catch (QuickLinksProfileEvaluationException ex) {
+ throw new InvalidTopologyTemplateException("Invalid quick links profile", ex);
+ }
+ }
+
+ private String processQuickLinksProfile(Map<String, Object> properties) throws QuickLinksProfileEvaluationException {
+ Object globalFilters = properties.get(QUICKLINKS_PROFILE_FILTERS_PROPERTY);
+ Object serviceFilters = properties.get(QUICKLINKS_PROFILE_SERVICES_PROPERTY);
+ return (null != globalFilters || null != serviceFilters) ?
+ new QuickLinksProfileBuilder().buildQuickLinksProfile(globalFilters, serviceFilters) : null;
}
+
private Map<String, Credential> parseCredentials(Map<String, Object> properties) throws
InvalidTopologyTemplateException {
HashMap<String, Credential> credentialHashMap = new HashMap<>();
@@ -439,4 +469,10 @@ public class ProvisionClusterRequest extends BaseClusterRequest {
return repoVersion;
}
+ /**
+ * @return the quick links profile in Json string format
+ */
+ public String getQuickLinksProfileJson() {
+ return quickLinksProfileJson;
+ }
}
http://git-wip-us.apache.org/repos/asf/ambari/blob/99bb9884/ambari-server/src/main/java/org/apache/ambari/server/state/quicklinksprofile/AcceptAllFilter.java
----------------------------------------------------------------------
diff --git a/ambari-server/src/main/java/org/apache/ambari/server/state/quicklinksprofile/AcceptAllFilter.java b/ambari-server/src/main/java/org/apache/ambari/server/state/quicklinksprofile/AcceptAllFilter.java
index d784a22..069ae3f 100644
--- a/ambari-server/src/main/java/org/apache/ambari/server/state/quicklinksprofile/AcceptAllFilter.java
+++ b/ambari-server/src/main/java/org/apache/ambari/server/state/quicklinksprofile/AcceptAllFilter.java
@@ -43,4 +43,9 @@ public class AcceptAllFilter extends Filter {
public int hashCode() {
return java.util.Objects.hash(isVisible());
}
+
+ @Override
+ public String toString() {
+ return getClass().getSimpleName() + " (visible=" + isVisible() + ")";
+ }
}
http://git-wip-us.apache.org/repos/asf/ambari/blob/99bb9884/ambari-server/src/main/java/org/apache/ambari/server/state/quicklinksprofile/Component.java
----------------------------------------------------------------------
diff --git a/ambari-server/src/main/java/org/apache/ambari/server/state/quicklinksprofile/Component.java b/ambari-server/src/main/java/org/apache/ambari/server/state/quicklinksprofile/Component.java
index a1267df..729e5d4 100644
--- a/ambari-server/src/main/java/org/apache/ambari/server/state/quicklinksprofile/Component.java
+++ b/ambari-server/src/main/java/org/apache/ambari/server/state/quicklinksprofile/Component.java
@@ -24,6 +24,8 @@ import org.codehaus.jackson.annotate.JsonIgnoreProperties;
import org.codehaus.jackson.annotate.JsonProperty;
import org.codehaus.jackson.map.annotate.JsonSerialize;
+import com.google.common.base.Preconditions;
+
/**
* Class to represent component-level filter definitions
*/
@@ -37,6 +39,7 @@ public class Component {
private List<Filter> filters;
static Component create(String name, List<Filter> filters) {
+ Preconditions.checkNotNull(name, "Component name must not be null");
Component component = new Component();
component.setName(name);
component.setFilters(filters);
http://git-wip-us.apache.org/repos/asf/ambari/blob/99bb9884/ambari-server/src/main/java/org/apache/ambari/server/state/quicklinksprofile/Filter.java
----------------------------------------------------------------------
diff --git a/ambari-server/src/main/java/org/apache/ambari/server/state/quicklinksprofile/Filter.java b/ambari-server/src/main/java/org/apache/ambari/server/state/quicklinksprofile/Filter.java
index c551830..a62fee0 100644
--- a/ambari-server/src/main/java/org/apache/ambari/server/state/quicklinksprofile/Filter.java
+++ b/ambari-server/src/main/java/org/apache/ambari/server/state/quicklinksprofile/Filter.java
@@ -23,6 +23,8 @@ import org.codehaus.jackson.annotate.JsonIgnoreProperties;
import org.codehaus.jackson.annotate.JsonProperty;
import org.codehaus.jackson.map.annotate.JsonSerialize;
+import com.google.common.base.Preconditions;
+
@JsonSerialize(include= JsonSerialize.Inclusion.NON_NULL)
@JsonIgnoreProperties(ignoreUnknown = true)
/**
@@ -33,8 +35,9 @@ import org.codehaus.jackson.map.annotate.JsonSerialize;
* </ul>
*/
public abstract class Filter {
+ static final String VISIBLE = "visible";
- @JsonProperty("visible")
+ @JsonProperty(VISIBLE)
private boolean visible;
/**
@@ -63,6 +66,7 @@ public abstract class Filter {
}
static LinkNameFilter linkNameFilter(String linkName, boolean visible) {
+ Preconditions.checkNotNull(linkName, "Link name must not be null");
LinkNameFilter linkNameFilter = new LinkNameFilter();
linkNameFilter.setLinkName(linkName);
linkNameFilter.setVisible(visible);
@@ -70,6 +74,7 @@ public abstract class Filter {
}
static LinkAttributeFilter linkAttributeFilter(String linkAttribute, boolean visible) {
+ Preconditions.checkNotNull(linkAttribute, "Attribute name must not be null");
LinkAttributeFilter linkAttributeFilter = new LinkAttributeFilter();
linkAttributeFilter.setLinkAttribute(linkAttribute);
linkAttributeFilter.setVisible(visible);
http://git-wip-us.apache.org/repos/asf/ambari/blob/99bb9884/ambari-server/src/main/java/org/apache/ambari/server/state/quicklinksprofile/QuickLinksProfile.java
----------------------------------------------------------------------
diff --git a/ambari-server/src/main/java/org/apache/ambari/server/state/quicklinksprofile/QuickLinksProfile.java b/ambari-server/src/main/java/org/apache/ambari/server/state/quicklinksprofile/QuickLinksProfile.java
index c9ac6b4..7d480e9 100644
--- a/ambari-server/src/main/java/org/apache/ambari/server/state/quicklinksprofile/QuickLinksProfile.java
+++ b/ambari-server/src/main/java/org/apache/ambari/server/state/quicklinksprofile/QuickLinksProfile.java
@@ -41,6 +41,15 @@ import org.codehaus.jackson.map.annotate.JsonSerialize;
@JsonIgnoreProperties(ignoreUnknown = true)
public class QuickLinksProfile {
+ /**
+ * The name of the Ambari setting that stores the quick links profile
+ */
+ public static final String SETTING_NAME_QUICKLINKS_PROFILE = "QuickLinksProfile";
+ /**
+ * The type of the Ambari setting that stores the quick links profile
+ */
+ public static final String SETTING_TYPE_AMBARI_SERVER = "ambari-server";
+
@JsonProperty("filters")
private List<Filter> filters;
http://git-wip-us.apache.org/repos/asf/ambari/blob/99bb9884/ambari-server/src/main/java/org/apache/ambari/server/state/quicklinksprofile/QuickLinksProfileBuilder.java
----------------------------------------------------------------------
diff --git a/ambari-server/src/main/java/org/apache/ambari/server/state/quicklinksprofile/QuickLinksProfileBuilder.java b/ambari-server/src/main/java/org/apache/ambari/server/state/quicklinksprofile/QuickLinksProfileBuilder.java
new file mode 100644
index 0000000..fca1155
--- /dev/null
+++ b/ambari-server/src/main/java/org/apache/ambari/server/state/quicklinksprofile/QuickLinksProfileBuilder.java
@@ -0,0 +1,128 @@
+/*
+ * 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.quicklinksprofile;
+
+import static org.apache.ambari.server.state.quicklinksprofile.Filter.VISIBLE;
+import static org.apache.ambari.server.state.quicklinksprofile.LinkAttributeFilter.LINK_ATTRIBUTE;
+import static org.apache.ambari.server.state.quicklinksprofile.LinkNameFilter.LINK_NAME;
+
+import java.util.ArrayList;
+import java.util.Collection;
+import java.util.List;
+import java.util.Map;
+
+import javax.annotation.Nullable;
+
+import com.google.common.collect.ImmutableList;
+
+/**
+ * Class to create a {@link QuickLinksProfile} based on data received in a request
+ */
+public class QuickLinksProfileBuilder {
+
+ public static final String NAME = "name";
+ public static final String COMPONENTS = "components";
+ public static final String FILTERS = "filters";
+
+ /**
+ *
+ * @param globalFiltersRaw The data in the request belonging to the "quicklinks_profile/filters" key.
+ * @param serviceFiltersRaw The data in the request belonging to "quicklinks_profile/services"
+ * @return The quicklinks profile as Json encoded string.
+ * @throws QuickLinksProfileEvaluationException when the received data defines an invalid profile. This can be of various
+ * reasons: the received data is has invalid structure, critical data is missing or there are contradicting filter
+ * rules in the profile
+ */
+ public String buildQuickLinksProfile(@Nullable Object globalFiltersRaw, @Nullable Object serviceFiltersRaw) throws QuickLinksProfileEvaluationException {
+ try {
+ List<Filter> globalFilters = buildQuickLinkFilters(globalFiltersRaw);
+ List<Service> services = buildServices(serviceFiltersRaw);
+ QuickLinksProfile profile = QuickLinksProfile.create(globalFilters, services);
+ // sanity check: this should throw QuickLinksProfileEvaluationException if the profile is invalid
+ new DefaultQuickLinkVisibilityController(profile);
+ return new QuickLinksProfileParser().encode(profile);
+ }
+ catch (QuickLinksProfileEvaluationException ex) {
+ throw ex;
+ }
+ catch (Exception ex) {
+ throw new QuickLinksProfileEvaluationException("Error interpreting quicklinks profile data", ex);
+ }
+ }
+
+ List<Service> buildServices(@Nullable Object servicesRaw) {
+ if (null == servicesRaw) {
+ return ImmutableList.of();
+ }
+ List<Service> services = new ArrayList<>();
+ for (Map<String, Object> serviceAsMap: (Collection<Map<String, Object>>)servicesRaw) {
+ String serviceName = (String)serviceAsMap.get(NAME);
+ Object componentsRaw = serviceAsMap.get(COMPONENTS);
+ Object filtersRaw = serviceAsMap.get(FILTERS);
+ services.add(Service.create(serviceName,
+ buildQuickLinkFilters(filtersRaw),
+ buildComponents(componentsRaw)));
+ }
+ return services;
+ }
+
+ List<Component> buildComponents(@Nullable Object componentsRaw) {
+ if (null == componentsRaw) {
+ return ImmutableList.of();
+ }
+ List<Component> components = new ArrayList<>();
+ for (Map<String, Object> componentAsMap: (Collection<Map<String, Object>>)componentsRaw) {
+ String componentName = (String)componentAsMap.get(NAME);
+ Object filtersRaw = componentAsMap.get(FILTERS);
+ components.add(Component.create(componentName,
+ buildQuickLinkFilters(filtersRaw)));
+ }
+ return components; }
+
+
+ List<Filter> buildQuickLinkFilters(@Nullable Object filtersRaw) throws ClassCastException, IllegalArgumentException {
+ if (null == filtersRaw) {
+ return ImmutableList.of();
+ }
+ List<Filter> filters = new ArrayList<>();
+ for (Map<String, String> filterAsMap: (Collection<Map<String, String>>)filtersRaw) {
+ String linkName = filterAsMap.get(LINK_NAME);
+ String attributeName = filterAsMap.get(LINK_ATTRIBUTE);
+ boolean visible = Boolean.parseBoolean(filterAsMap.get(VISIBLE));
+
+ if (null != linkName && null != attributeName) {
+ throw new IllegalArgumentException(
+ String.format("%s link_name: %s, link_attribute: %s",
+ QuickLinksFilterDeserializer.PARSE_ERROR_MESSAGE, linkName, attributeName));
+ }
+ else if (null != linkName) {
+ filters.add(Filter.linkNameFilter(linkName, visible));
+ }
+ else if (null != attributeName) {
+ filters.add(Filter.linkAttributeFilter(attributeName, visible));
+ }
+ else {
+ filters.add(Filter.acceptAllFilter(visible));
+ }
+ }
+ return filters;
+ }
+
+
+}
http://git-wip-us.apache.org/repos/asf/ambari/blob/99bb9884/ambari-server/src/main/java/org/apache/ambari/server/state/quicklinksprofile/QuickLinksProfileEvaluationException.java
----------------------------------------------------------------------
diff --git a/ambari-server/src/main/java/org/apache/ambari/server/state/quicklinksprofile/QuickLinksProfileEvaluationException.java b/ambari-server/src/main/java/org/apache/ambari/server/state/quicklinksprofile/QuickLinksProfileEvaluationException.java
index 26819e1..1ef8a59 100644
--- a/ambari-server/src/main/java/org/apache/ambari/server/state/quicklinksprofile/QuickLinksProfileEvaluationException.java
+++ b/ambari-server/src/main/java/org/apache/ambari/server/state/quicklinksprofile/QuickLinksProfileEvaluationException.java
@@ -28,4 +28,8 @@ public class QuickLinksProfileEvaluationException extends Exception {
super(message);
}
+ public QuickLinksProfileEvaluationException(String message, Throwable cause) {
+ super(message, cause);
+ }
+
}
http://git-wip-us.apache.org/repos/asf/ambari/blob/99bb9884/ambari-server/src/main/java/org/apache/ambari/server/state/quicklinksprofile/QuickLinksProfileParser.java
----------------------------------------------------------------------
diff --git a/ambari-server/src/main/java/org/apache/ambari/server/state/quicklinksprofile/QuickLinksProfileParser.java b/ambari-server/src/main/java/org/apache/ambari/server/state/quicklinksprofile/QuickLinksProfileParser.java
index a3ae677..150b7d4 100644
--- a/ambari-server/src/main/java/org/apache/ambari/server/state/quicklinksprofile/QuickLinksProfileParser.java
+++ b/ambari-server/src/main/java/org/apache/ambari/server/state/quicklinksprofile/QuickLinksProfileParser.java
@@ -55,13 +55,17 @@ public class QuickLinksProfileParser {
public QuickLinksProfile parse(URL url) throws IOException {
return parse(Resources.toByteArray(url));
}
+
+ public String encode(QuickLinksProfile profile) throws IOException {
+ return mapper.writeValueAsString(profile);
+ }
}
/**
* Custom deserializer is needed to handle filter polymorphism.
*/
class QuickLinksFilterDeserializer extends StdDeserializer<Filter> {
- private static final String PARSE_ERROR_MESSAGE =
+ static final String PARSE_ERROR_MESSAGE =
"A filter is not allowed to declare both link_name and link_attribute at the same time.";
QuickLinksFilterDeserializer() {
http://git-wip-us.apache.org/repos/asf/ambari/blob/99bb9884/ambari-server/src/main/java/org/apache/ambari/server/state/quicklinksprofile/Service.java
----------------------------------------------------------------------
diff --git a/ambari-server/src/main/java/org/apache/ambari/server/state/quicklinksprofile/Service.java b/ambari-server/src/main/java/org/apache/ambari/server/state/quicklinksprofile/Service.java
index 7724852..07cce29 100644
--- a/ambari-server/src/main/java/org/apache/ambari/server/state/quicklinksprofile/Service.java
+++ b/ambari-server/src/main/java/org/apache/ambari/server/state/quicklinksprofile/Service.java
@@ -24,6 +24,8 @@ import org.codehaus.jackson.annotate.JsonIgnoreProperties;
import org.codehaus.jackson.annotate.JsonProperty;
import org.codehaus.jackson.map.annotate.JsonSerialize;
+import com.google.common.base.Preconditions;
+
/**
* Class to represent component-level filter definitions
*/
@@ -40,6 +42,7 @@ public class Service {
private List<Filter> filters;
static Service create(String name, List<Filter> filters, List<Component> components) {
+ Preconditions.checkNotNull(name, "Service name must not be null");
Service service = new Service();
service.setName(name);
service.setFilters(filters);
http://git-wip-us.apache.org/repos/asf/ambari/blob/99bb9884/ambari-server/src/main/java/org/apache/ambari/server/topology/TopologyManager.java
----------------------------------------------------------------------
diff --git a/ambari-server/src/main/java/org/apache/ambari/server/topology/TopologyManager.java b/ambari-server/src/main/java/org/apache/ambari/server/topology/TopologyManager.java
index 7db07a0..3103c34 100644
--- a/ambari-server/src/main/java/org/apache/ambari/server/topology/TopologyManager.java
+++ b/ambari-server/src/main/java/org/apache/ambari/server/topology/TopologyManager.java
@@ -63,10 +63,14 @@ import org.apache.ambari.server.events.HostsRemovedEvent;
import org.apache.ambari.server.events.RequestFinishedEvent;
import org.apache.ambari.server.events.publishers.AmbariEventPublisher;
import org.apache.ambari.server.orm.dao.HostRoleCommandStatusSummaryDTO;
+import org.apache.ambari.server.orm.dao.SettingDAO;
+import org.apache.ambari.server.orm.entities.SettingEntity;
import org.apache.ambari.server.orm.entities.StageEntity;
+import org.apache.ambari.server.security.authorization.AuthorizationHelper;
import org.apache.ambari.server.state.Host;
import org.apache.ambari.server.state.SecurityType;
import org.apache.ambari.server.state.host.HostImpl;
+import org.apache.ambari.server.state.quicklinksprofile.QuickLinksProfile;
import org.apache.ambari.server.utils.RetryHelper;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
@@ -118,6 +122,9 @@ public class TopologyManager {
@Inject
private AmbariEventPublisher ambariEventPublisher;
+ @Inject
+ private SettingDAO settingDAO;
+
/**
* A boolean not cached thread-local (volatile) to prevent double-checked
* locking on the synchronized keyword.
@@ -232,6 +239,11 @@ public class TopologyManager {
public RequestStatusResponse provisionCluster(final ProvisionClusterRequest request) throws InvalidTopologyException, AmbariException {
ensureInitialized();
+
+ if (null != request.getQuickLinksProfileJson()) {
+ saveOrUpdateQuickLinksProfile(request.getQuickLinksProfileJson());
+ }
+
ClusterTopology topology = new ClusterTopologyImpl(ambariContext, request);
final String clusterName = request.getClusterName();
final String repoVersion = request.getRepositoryVersion();
@@ -302,6 +314,32 @@ public class TopologyManager {
return getRequestStatus(logicalRequest.getRequestId());
}
+ /**
+ * Saves the quick links profile to the DB as an Ambari setting. Creates a new setting entity or updates the existing
+ * one.
+ * @param quickLinksProfileJson the quicklinks profile in Json format
+ */
+ void saveOrUpdateQuickLinksProfile(String quickLinksProfileJson) {
+ SettingEntity settingEntity = settingDAO.findByName(QuickLinksProfile.SETTING_NAME_QUICKLINKS_PROFILE);
+ // create new
+ if (null == settingEntity) {
+ settingEntity = new SettingEntity();
+ settingEntity.setName(QuickLinksProfile.SETTING_NAME_QUICKLINKS_PROFILE);
+ settingEntity.setSettingType(QuickLinksProfile.SETTING_TYPE_AMBARI_SERVER);
+ settingEntity.setContent(quickLinksProfileJson);
+ settingEntity.setUpdatedBy(AuthorizationHelper.getAuthenticatedName());
+ settingEntity.setUpdateTimestamp(System.currentTimeMillis());
+ settingDAO.create(settingEntity);
+ }
+ // update existing
+ else {
+ settingEntity.setContent(quickLinksProfileJson);
+ settingEntity.setUpdatedBy(AuthorizationHelper.getAuthenticatedName());
+ settingEntity.setUpdateTimestamp(System.currentTimeMillis());
+ settingDAO.merge(settingEntity);
+ }
+ }
+
private void submitCredential(String clusterName, Credential credential) {
ResourceProvider provider =
http://git-wip-us.apache.org/repos/asf/ambari/blob/99bb9884/ambari-server/src/test/java/org/apache/ambari/server/controller/internal/ProvisionClusterRequestTest.java
----------------------------------------------------------------------
diff --git a/ambari-server/src/test/java/org/apache/ambari/server/controller/internal/ProvisionClusterRequestTest.java b/ambari-server/src/test/java/org/apache/ambari/server/controller/internal/ProvisionClusterRequestTest.java
index 0610d10..b114edc 100644
--- a/ambari-server/src/test/java/org/apache/ambari/server/controller/internal/ProvisionClusterRequestTest.java
+++ b/ambari-server/src/test/java/org/apache/ambari/server/controller/internal/ProvisionClusterRequestTest.java
@@ -18,7 +18,32 @@
package org.apache.ambari.server.controller.internal;
+import static org.easymock.EasyMock.createMock;
+import static org.easymock.EasyMock.createNiceMock;
+import static org.easymock.EasyMock.expect;
+import static org.easymock.EasyMock.verify;
+import static org.junit.Assert.assertEquals;
+import static org.junit.Assert.assertNotNull;
+import static org.junit.Assert.assertNull;
+import static org.junit.Assert.assertSame;
+import static org.junit.Assert.assertTrue;
+import static org.powermock.api.easymock.PowerMock.createStrictMock;
+import static org.powermock.api.easymock.PowerMock.replay;
+import static org.powermock.api.easymock.PowerMock.reset;
+
+import java.lang.reflect.Field;
+import java.util.ArrayList;
+import java.util.Collection;
+import java.util.Collections;
+import java.util.HashMap;
+import java.util.HashSet;
+import java.util.LinkedHashMap;
+import java.util.List;
+import java.util.Map;
+import java.util.Set;
+
import org.apache.ambari.server.controller.spi.ResourceProvider;
+import org.apache.ambari.server.state.quicklinksprofile.QuickLinksProfileBuilderTest;
import org.apache.ambari.server.topology.Blueprint;
import org.apache.ambari.server.topology.BlueprintFactory;
import org.apache.ambari.server.topology.Configuration;
@@ -31,29 +56,7 @@ import org.junit.After;
import org.junit.Before;
import org.junit.Test;
-import java.lang.reflect.Field;
-import java.util.ArrayList;
-import java.util.Collection;
-import java.util.Collections;
-import java.util.HashMap;
-import java.util.HashSet;
-import java.util.LinkedHashMap;
-import java.util.List;
-import java.util.Map;
-import java.util.Set;
-
-import static org.easymock.EasyMock.createMock;
-import static org.easymock.EasyMock.createNiceMock;
-import static org.easymock.EasyMock.expect;
-import static org.easymock.EasyMock.verify;
-import static org.junit.Assert.assertEquals;
-import static org.junit.Assert.assertNotNull;
-import static org.junit.Assert.assertNull;
-import static org.junit.Assert.assertSame;
-import static org.junit.Assert.assertTrue;
-import static org.powermock.api.easymock.PowerMock.createStrictMock;
-import static org.powermock.api.easymock.PowerMock.replay;
-import static org.powermock.api.easymock.PowerMock.reset;
+import com.google.common.collect.Sets;
/**
* Unit tests for ProvisionClusterRequest.
@@ -428,6 +431,69 @@ public class ProvisionClusterRequestTest {
new ProvisionClusterRequest(properties, null);
}
+ @Test
+ public void testQuickLinksProfile_NoDataInRequest() throws Exception {
+ Map<String, Object> properties = createBlueprintRequestProperties(CLUSTER_NAME, BLUEPRINT_NAME);
+ ProvisionClusterRequest request = new ProvisionClusterRequest(properties, null);
+ assertNull("No quick links profile is expected", request.getQuickLinksProfileJson());
+ }
+
+ @Test
+ public void testQuickLinksProfile_OnlyGlobalFilterDataInRequest() throws Exception {
+ Map<String, Object> properties = createBlueprintRequestProperties(CLUSTER_NAME, BLUEPRINT_NAME);
+
+ properties.put(ProvisionClusterRequest.QUICKLINKS_PROFILE_FILTERS_PROPERTY,
+ Sets.newHashSet(QuickLinksProfileBuilderTest.filter(null, null, true)));
+
+ ProvisionClusterRequest request = new ProvisionClusterRequest(properties, null);
+ assertEquals("Quick links profile doesn't match expected",
+ "{\"filters\":[{\"visible\":true}],\"services\":[]}",
+ request.getQuickLinksProfileJson());
+ }
+
+ @Test
+ public void testQuickLinksProfile_OnlyServiceFilterDataInRequest() throws Exception {
+ Map<String, Object> properties = createBlueprintRequestProperties(CLUSTER_NAME, BLUEPRINT_NAME);
+
+ Map<String, String> filter = QuickLinksProfileBuilderTest.filter(null, null, true);
+ Map<String, Object> hdfs = QuickLinksProfileBuilderTest.service("HDFS", null, Sets.newHashSet(filter));
+ Set<Map<String, Object>> services = Sets.newHashSet(hdfs);
+ properties.put(ProvisionClusterRequest.QUICKLINKS_PROFILE_SERVICES_PROPERTY, services);
+
+ ProvisionClusterRequest request = new ProvisionClusterRequest(properties, null);
+ assertEquals("Quick links profile doesn't match expected",
+ "{\"filters\":[],\"services\":[{\"name\":\"HDFS\",\"components\":[],\"filters\":[{\"visible\":true}]}]}",
+ request.getQuickLinksProfileJson());
+ }
+
+ @Test
+ public void testQuickLinksProfile_BothGlobalAndServiceLevelFilters() throws Exception {
+ Map<String, Object> properties = createBlueprintRequestProperties(CLUSTER_NAME, BLUEPRINT_NAME);
+
+ properties.put(ProvisionClusterRequest.QUICKLINKS_PROFILE_FILTERS_PROPERTY,
+ Sets.newHashSet(QuickLinksProfileBuilderTest.filter(null, null, true)));
+
+ Map<String, String> filter = QuickLinksProfileBuilderTest.filter(null, null, true);
+ Map<String, Object> hdfs = QuickLinksProfileBuilderTest.service("HDFS", null, Sets.newHashSet(filter));
+ Set<Map<String, Object>> services = Sets.newHashSet(hdfs);
+ properties.put(ProvisionClusterRequest.QUICKLINKS_PROFILE_SERVICES_PROPERTY, services);
+
+ ProvisionClusterRequest request = new ProvisionClusterRequest(properties, null);
+ System.out.println(request.getQuickLinksProfileJson());
+ assertEquals("Quick links profile doesn't match expected",
+ "{\"filters\":[{\"visible\":true}],\"services\":[{\"name\":\"HDFS\",\"components\":[],\"filters\":[{\"visible\":true}]}]}",
+ request.getQuickLinksProfileJson());
+ }
+
+ @Test(expected = InvalidTopologyTemplateException.class)
+ public void testQuickLinksProfile_InvalidRequestData() throws Exception {
+ Map<String, Object> properties = createBlueprintRequestProperties(CLUSTER_NAME, BLUEPRINT_NAME);
+
+ properties.put(ProvisionClusterRequest.QUICKLINKS_PROFILE_SERVICES_PROPERTY, "Hello World!");
+
+ ProvisionClusterRequest request = new ProvisionClusterRequest(properties, null);
+ }
+
public static Map<String, Object> createBlueprintRequestProperties(String clusterName, String blueprintName) {
Map<String, Object> properties = new LinkedHashMap<String, Object>();
http://git-wip-us.apache.org/repos/asf/ambari/blob/99bb9884/ambari-server/src/test/java/org/apache/ambari/server/controller/internal/QuickLinkArtifactResourceProviderTest.java
----------------------------------------------------------------------
diff --git a/ambari-server/src/test/java/org/apache/ambari/server/controller/internal/QuickLinkArtifactResourceProviderTest.java b/ambari-server/src/test/java/org/apache/ambari/server/controller/internal/QuickLinkArtifactResourceProviderTest.java
index 8c723c9..689fbe4 100644
--- a/ambari-server/src/test/java/org/apache/ambari/server/controller/internal/QuickLinkArtifactResourceProviderTest.java
+++ b/ambari-server/src/test/java/org/apache/ambari/server/controller/internal/QuickLinkArtifactResourceProviderTest.java
@@ -192,7 +192,6 @@ public class QuickLinkArtifactResourceProviderTest {
expect(service.getName()).andReturn("YARN").anyTimes();
binder.bind(AmbariManagementController.class).toInstance(amc);
replay(amc, metaInfo, stack, service);
-
}
}
}
http://git-wip-us.apache.org/repos/asf/ambari/blob/99bb9884/ambari-server/src/test/java/org/apache/ambari/server/state/quicklinksprofile/QuickLinksProfileBuilderTest.java
----------------------------------------------------------------------
diff --git a/ambari-server/src/test/java/org/apache/ambari/server/state/quicklinksprofile/QuickLinksProfileBuilderTest.java b/ambari-server/src/test/java/org/apache/ambari/server/state/quicklinksprofile/QuickLinksProfileBuilderTest.java
new file mode 100644
index 0000000..1cc3fd3
--- /dev/null
+++ b/ambari-server/src/test/java/org/apache/ambari/server/state/quicklinksprofile/QuickLinksProfileBuilderTest.java
@@ -0,0 +1,234 @@
+/*
+ * 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.quicklinksprofile;
+
+import static com.google.common.collect.Sets.newHashSet;
+import static org.apache.ambari.server.state.quicklinksprofile.QuickLinksProfileBuilder.COMPONENTS;
+import static org.apache.ambari.server.state.quicklinksprofile.QuickLinksProfileBuilder.FILTERS;
+import static org.apache.ambari.server.state.quicklinksprofile.QuickLinksProfileBuilder.NAME;
+
+import java.util.HashMap;
+import java.util.List;
+import java.util.Map;
+import java.util.Set;
+
+import javax.annotation.Nonnull;
+import javax.annotation.Nullable;
+
+import org.junit.Test;
+
+import com.google.common.collect.Sets;
+
+public class QuickLinksProfileBuilderTest {
+
+
+ @Test
+ public void testBuildProfileOnlyGlobalFilters() throws Exception {
+ Set<Map<String, String>> filters = newHashSet(
+ filter("namenode_ui", null, true),
+ filter(null, "sso", true),
+ filter(null, null, false)
+ );
+
+ String profileJson = new QuickLinksProfileBuilder().buildQuickLinksProfile(filters, null);
+
+ //verify
+ QuickLinksProfile profile = new QuickLinksProfileParser().parse(profileJson.getBytes());
+ assertFilterExists(profile, null, null, Filter.linkNameFilter("namenode_ui", true));
+ assertFilterExists(profile, null, null, Filter.linkAttributeFilter("sso", true));
+ assertFilterExists(profile, null, null, Filter.acceptAllFilter(false));
+ }
+
+ @Test
+ public void testBuildProfileOnlyServiceFilters() throws Exception {
+ Map<String, Object> nameNode = component("NAMENODE",
+ newHashSet(filter("namenode_ui", null, false)));
+
+ Map<String, Object> hdfs = service("HDFS",
+ newHashSet(nameNode),
+ newHashSet(filter(null, "sso", true)));
+
+ Set<Map<String, Object>> services = Sets.newHashSet(hdfs);
+
+ String profileJson = new QuickLinksProfileBuilder().buildQuickLinksProfile(null, services);
+
+ //verify
+ QuickLinksProfile profile = new QuickLinksProfileParser().parse(profileJson.getBytes());
+ assertFilterExists(profile, "HDFS", "NAMENODE", Filter.linkNameFilter("namenode_ui", false));
+ assertFilterExists(profile, "HDFS", null, Filter.linkAttributeFilter("sso", true));
+ }
+
+ @Test
+ public void testBuildProfileBothGlobalAndServiceFilters() throws Exception {
+ Set<Map<String, String>> globalFilters = newHashSet( filter(null, null, false) );
+
+ Map<String, Object> nameNode = component("NAMENODE",
+ newHashSet(filter("namenode_ui", null, false)));
+
+ Map<String, Object> hdfs = service("HDFS",
+ newHashSet(nameNode),
+ newHashSet(filter(null, "sso", true)));
+
+ Set<Map<String, Object>> services = Sets.newHashSet(hdfs);
+
+ String profileJson = new QuickLinksProfileBuilder().buildQuickLinksProfile(globalFilters, services);
+
+ // verify
+ QuickLinksProfile profile = new QuickLinksProfileParser().parse(profileJson.getBytes());
+ assertFilterExists(profile, null, null, Filter.acceptAllFilter(false));
+ assertFilterExists(profile, "HDFS", "NAMENODE", Filter.linkNameFilter("namenode_ui", false));
+ assertFilterExists(profile, "HDFS", null, Filter.linkAttributeFilter("sso", true));
+ }
+
+ @Test(expected = QuickLinksProfileEvaluationException.class)
+ public void testBuildProfileBadInputStructure() throws Exception {
+ new QuickLinksProfileBuilder().buildQuickLinksProfile("Hello", "World");
+ }
+
+ @Test(expected = QuickLinksProfileEvaluationException.class)
+ public void testBuildProfileMissingDataServiceName() throws Exception {
+ Map<String, Object> nameNode = component("NAMENODE",
+ newHashSet(filter("namenode_ui", null, false)));
+
+ Map<String, Object> hdfs = service(null, // intentionally omitting service name
+ newHashSet(nameNode),
+ newHashSet(filter(null, "sso", true)));
+
+ Set<Map<String, Object>> services = Sets.newHashSet(hdfs);
+
+ new QuickLinksProfileBuilder().buildQuickLinksProfile(null, services);
+ }
+
+ @Test(expected = QuickLinksProfileEvaluationException.class)
+ public void testBuildProfileMissingDataComponentName() throws Exception {
+ Map<String, Object> nameNode = component(null, // intentionally omitting component name
+ newHashSet(filter("namenode_ui", null, false)));
+
+ Map<String, Object> hdfs = service("HDFS",
+ newHashSet(nameNode),
+ newHashSet(filter(null, "sso", true)));
+
+ Set<Map<String, Object>> services = Sets.newHashSet(hdfs);
+
+ new QuickLinksProfileBuilder().buildQuickLinksProfile(null, services);
+ }
+
+ @Test(expected = QuickLinksProfileEvaluationException.class)
+ public void testBuildProfileInvalidProfileDefiniton() throws Exception {
+ // Contradicting rules in the profile
+ Set<Map<String, String>> filters = newHashSet(
+ filter(null, "sso", true),
+ filter(null, "sso", false)
+ );
+
+ String profileJson = new QuickLinksProfileBuilder().buildQuickLinksProfile(filters, null);
+ }
+
+ /**
+ * Verifies that the filter specified by the arguments exists in the received {@link QuickLinksProfile}
+ * @param profile the {@link QuickLinksProfile} to examine
+ * @param serviceName the service name where the filter is defined. {@code null} means the searched filter is a global
+ * filter
+ * @param componentName the component name where the filter is defined. Only makes sense when serviceName is defined
+ * too. {@code null} means the searched filter is a service level filter, not component level one.
+ * filter
+ * @param filter the {@link Filter} to look for.
+ */
+ private static void assertFilterExists(@Nonnull QuickLinksProfile profile,
+ @Nullable String serviceName,
+ @Nullable String componentName,
+ @Nonnull Filter filter) {
+ // looking for a global filter
+ if (null == serviceName) {
+ if (!profile.getFilters().contains(filter)) {
+ throw new AssertionError("Expected global filter not found: " + filter);
+ }
+ }
+ // looking for a filter defined on service or component level
+ else {
+ Service service = findService(profile.getServices(), serviceName);
+ // looking for a filter defined on service level
+ if (null == componentName) {
+ if (!service.getFilters().contains(filter)) {
+ throw new AssertionError(String.format("Expected filter not found. Service: %s, Filter: %s",
+ serviceName, filter));
+ }
+ }
+ // looking for a filter defined on component level
+ else {
+ Component component = findComponent(service.getComponents(), componentName);
+ if (!component.getFilters().contains(filter)) {
+ throw new AssertionError(String.format("Expected filter not found. Service: %s, Component: %s, Filter: %s",
+ serviceName, componentName, filter));
+ }
+ }
+ }
+ }
+
+ private static Component findComponent(List<Component> components, String componentName) {
+ for (Component component: components) {
+ if (component.getName().equals(componentName)) {
+ return component;
+ }
+ }
+ throw new AssertionError("Expected component not found: " + componentName);
+ }
+
+ private static Service findService(List<Service> services, String serviceName) {
+ for (Service service: services) {
+ if (service.getName().equals(serviceName)) {
+ return service;
+ }
+ }
+ throw new AssertionError("Expected service not found: " + serviceName);
+ }
+
+ public static Map<String, String> filter(@Nullable String linkName, @Nullable String attributeName, boolean visible) {
+ Map<String, String> map = new HashMap<>(3);
+ if (null != linkName) {
+ map.put(LinkNameFilter.LINK_NAME, linkName);
+ }
+ if (null != attributeName) {
+ map.put(LinkAttributeFilter.LINK_ATTRIBUTE, attributeName);
+ }
+ map.put(Filter.VISIBLE, Boolean.toString(visible));
+ return map;
+ }
+
+ public static Map<String, Object> component(String componentName, Set<Map<String, String>> filters) {
+ Map<String, Object> map = new HashMap<>();
+ map.put(NAME, componentName);
+ map.put(FILTERS, filters);
+ return map;
+ }
+
+ public static Map<String, Object> service(String serviceName, Set<Map<String, Object>> components,
+ Set<Map<String, String>> filters) {
+ Map<String, Object> map = new HashMap<>();
+ map.put(NAME, serviceName);
+ if (null != components) {
+ map.put(COMPONENTS, components);
+ }
+ if (null != filters) {
+ map.put(FILTERS, filters);
+ }
+ return map;
+ }
+
+}
\ No newline at end of file
http://git-wip-us.apache.org/repos/asf/ambari/blob/99bb9884/ambari-server/src/test/java/org/apache/ambari/server/topology/TopologyManagerTest.java
----------------------------------------------------------------------
diff --git a/ambari-server/src/test/java/org/apache/ambari/server/topology/TopologyManagerTest.java b/ambari-server/src/test/java/org/apache/ambari/server/topology/TopologyManagerTest.java
index b45c585..cf92911 100644
--- a/ambari-server/src/test/java/org/apache/ambari/server/topology/TopologyManagerTest.java
+++ b/ambari-server/src/test/java/org/apache/ambari/server/topology/TopologyManagerTest.java
@@ -18,7 +18,33 @@
package org.apache.ambari.server.topology;
-import junit.framework.Assert;
+import static org.easymock.EasyMock.anyObject;
+import static org.easymock.EasyMock.capture;
+import static org.easymock.EasyMock.eq;
+import static org.easymock.EasyMock.expect;
+import static org.easymock.EasyMock.expectLastCall;
+import static org.easymock.EasyMock.isA;
+import static org.easymock.EasyMock.isNull;
+import static org.easymock.EasyMock.newCapture;
+import static org.easymock.EasyMock.replay;
+import static org.easymock.EasyMock.reset;
+import static org.easymock.EasyMock.verify;
+import static org.powermock.api.easymock.PowerMock.mockStatic;
+
+import java.lang.reflect.Field;
+import java.util.ArrayList;
+import java.util.Arrays;
+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.TreeMap;
+import java.util.concurrent.ExecutorService;
+import java.util.concurrent.Future;
+
+import org.apache.ambari.server.AmbariException;
import org.apache.ambari.server.actionmanager.HostRoleStatus;
import org.apache.ambari.server.AmbariException;
import org.apache.ambari.server.controller.ClusterRequest;
@@ -33,9 +59,13 @@ import org.apache.ambari.server.controller.spi.ClusterController;
import org.apache.ambari.server.controller.spi.Resource;
import org.apache.ambari.server.controller.spi.ResourceProvider;
import org.apache.ambari.server.events.RequestFinishedEvent;
+import org.apache.ambari.server.orm.dao.SettingDAO;
+import org.apache.ambari.server.orm.entities.SettingEntity;
+import org.apache.ambari.server.security.authorization.AuthorizationHelper;
import org.apache.ambari.server.security.encryption.CredentialStoreService;
import org.apache.ambari.server.stack.NoSuchStackException;
import org.apache.ambari.server.state.SecurityType;
+import org.apache.ambari.server.state.quicklinksprofile.QuickLinksProfile;
import org.easymock.Capture;
import org.easymock.EasyMock;
import org.easymock.EasyMockRule;
@@ -44,38 +74,20 @@ import org.easymock.Mock;
import org.easymock.MockType;
import org.easymock.TestSubject;
import org.junit.After;
+import org.junit.Assert;
import org.junit.Before;
import org.junit.Rule;
import org.junit.Test;
-
-import java.lang.reflect.Field;
-import java.util.ArrayList;
-import java.util.Arrays;
-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.TreeMap;
-import java.util.concurrent.ExecutorService;
-import java.util.concurrent.Future;
-
-import static org.easymock.EasyMock.anyObject;
-import static org.easymock.EasyMock.capture;
-import static org.easymock.EasyMock.eq;
-import static org.easymock.EasyMock.expect;
-import static org.easymock.EasyMock.expectLastCall;
-import static org.easymock.EasyMock.isA;
-import static org.easymock.EasyMock.isNull;
-import static org.easymock.EasyMock.newCapture;
-import static org.easymock.EasyMock.replay;
-import static org.easymock.EasyMock.reset;
-import static org.easymock.EasyMock.verify;
+import org.junit.runner.RunWith;
+import org.powermock.api.easymock.PowerMock;
+import org.powermock.core.classloader.annotations.PrepareForTest;
+import org.powermock.modules.junit4.PowerMockRunner;
/**
* TopologyManager unit tests
*/
+@RunWith(PowerMockRunner.class)
+@PrepareForTest( { TopologyManager.class })
public class TopologyManagerTest {
private static final String CLUSTER_NAME = "test-cluster";
@@ -83,6 +95,10 @@ public class TopologyManagerTest {
private static final String BLUEPRINT_NAME = "test-bp";
private static final String STACK_NAME = "test-stack";
private static final String STACK_VERSION = "test-stack-version";
+ private static final String SAMPLE_QUICKLINKS_PROFILE_1 = "{\"filters\":[{\"visible\":true}],\"services\":[]}";
+ private static final String SAMPLE_QUICKLINKS_PROFILE_2 =
+ "{\"filters\":[],\"services\":[{\"name\":\"HDFS\",\"components\":[],\"filters\":[{\"visible\":true}]}]}";
+
@Rule
public EasyMockRule mocks = new EasyMockRule(this);
@@ -132,6 +148,8 @@ public class TopologyManagerTest {
private ClusterController clusterController;
@Mock(type = MockType.STRICT)
private ResourceProvider resourceProvider;
+ @Mock(type = MockType.STRICT)
+ private SettingDAO settingDAO;
@Mock(type = MockType.STRICT)
private Future mockFuture;
@@ -344,13 +362,15 @@ public class TopologyManagerTest {
@After
public void tearDown() {
+ PowerMock.verify(System.class);
verify(blueprint, stack, request, group1, group2, ambariContext, logicalRequestFactory,
logicalRequest, configurationRequest, configurationRequest2, configurationRequest3,
- requestStatusResponse, executor, persistedState, mockFuture);
+ requestStatusResponse, executor, persistedState, mockFuture, settingDAO);
+ PowerMock.reset(System.class);
reset(blueprint, stack, request, group1, group2, ambariContext, logicalRequestFactory,
logicalRequest, configurationRequest, configurationRequest2, configurationRequest3,
- requestStatusResponse, executor, persistedState, mockFuture);
+ requestStatusResponse, executor, persistedState, mockFuture, settingDAO);
}
@Test
@@ -480,7 +500,7 @@ public class TopologyManagerTest {
replay(blueprint, stack, request, group1, group2, ambariContext, logicalRequestFactory,
configurationRequest, configurationRequest2, configurationRequest3, executor,
persistedState, securityConfigurationFactory, credentialStoreService, clusterController, resourceProvider,
- mockFuture, requestStatusResponse, logicalRequest);
+ mockFuture, requestStatusResponse, logicalRequest, settingDAO);
}
@Test(expected = InvalidTopologyException.class)
@@ -503,4 +523,64 @@ public class TopologyManagerTest {
topologyManager.scaleHosts(new ScaleClusterRequest(propertySet));
Assert.fail("InvalidTopologyException should have been thrown");
}
+
+ @Test
+ public void testProvisionCluster_QuickLinkProfileIsSavedTheFirstTime() throws Exception {
+ expect(persistedState.getAllRequests()).andReturn(Collections.<ClusterTopology,
+ List<LogicalRequest>>emptyMap()).anyTimes();
+
+ // request has a quicklinks profile
+ expect(request.getQuickLinksProfileJson()).andReturn(SAMPLE_QUICKLINKS_PROFILE_1).anyTimes();
+
+ // this means no quicklinks profile exists before calling provisionCluster()
+ expect(settingDAO.findByName(QuickLinksProfile.SETTING_NAME_QUICKLINKS_PROFILE)).andReturn(null);
+
+ // expect that settingsDao saves the quick links profile with the right content
+ final long timeStamp = System.currentTimeMillis();
+ mockStatic(System.class);
+ expect(System.currentTimeMillis()).andReturn(timeStamp);
+ PowerMock.replay(System.class);
+ final SettingEntity quickLinksProfile = createQuickLinksSettingEntity(SAMPLE_QUICKLINKS_PROFILE_1, timeStamp);
+ settingDAO.create(eq(quickLinksProfile));
+
+ replayAll();
+
+ topologyManager.provisionCluster(request);
+ }
+
+ @Test
+ public void testProvisionCluster_ExistingQuickLinkProfileIsOverwritten() throws Exception {
+ expect(persistedState.getAllRequests()).andReturn(Collections.<ClusterTopology,
+ List<LogicalRequest>>emptyMap()).anyTimes();
+
+ // request has a quicklinks profile
+ expect(request.getQuickLinksProfileJson()).andReturn(SAMPLE_QUICKLINKS_PROFILE_2).anyTimes();
+
+ // existing quick links profile returned by dao
+ final long timeStamp1 = System.currentTimeMillis();
+ SettingEntity originalProfile = createQuickLinksSettingEntity(SAMPLE_QUICKLINKS_PROFILE_1, timeStamp1);
+ expect(settingDAO.findByName(QuickLinksProfile.SETTING_NAME_QUICKLINKS_PROFILE)).andReturn(originalProfile);
+
+ // expect that settingsDao overwrites the quick links profile with the new content
+ mockStatic(System.class);
+ final long timeStamp2 = timeStamp1 + 100;
+ expect(System.currentTimeMillis()).andReturn(timeStamp2);
+ PowerMock.replay(System.class);
+ final SettingEntity newProfile = createQuickLinksSettingEntity(SAMPLE_QUICKLINKS_PROFILE_2, timeStamp2);
+ expect(settingDAO.merge(newProfile)).andReturn(newProfile);
+
+ replayAll();
+
+ topologyManager.provisionCluster(request);
+ }
+
+ private SettingEntity createQuickLinksSettingEntity(String content, long timeStamp) {
+ SettingEntity settingEntity = new SettingEntity();
+ settingEntity.setName(QuickLinksProfile.SETTING_NAME_QUICKLINKS_PROFILE);
+ settingEntity.setSettingType(QuickLinksProfile.SETTING_TYPE_AMBARI_SERVER);
+ settingEntity.setContent(content);
+ settingEntity.setUpdatedBy(AuthorizationHelper.getAuthenticatedName());
+ settingEntity.setUpdateTimestamp(timeStamp);
+ return settingEntity;
+ }
}