You are viewing a plain text version of this content. The canonical link for it is here.
Posted to commits@ambari.apache.org by al...@apache.org on 2016/06/20 21:34:46 UTC
[4/6] ambari git commit: AMBARI-12885. Dynamic stack extensions -
install and upgrade support for custom services (Tim Thorpe via alejandro)
http://git-wip-us.apache.org/repos/asf/ambari/blob/300a7e21/ambari-server/src/main/java/org/apache/ambari/server/stack/ExtensionModule.java
----------------------------------------------------------------------
diff --git a/ambari-server/src/main/java/org/apache/ambari/server/stack/ExtensionModule.java b/ambari-server/src/main/java/org/apache/ambari/server/stack/ExtensionModule.java
new file mode 100644
index 0000000..e3f8d0d
--- /dev/null
+++ b/ambari-server/src/main/java/org/apache/ambari/server/stack/ExtensionModule.java
@@ -0,0 +1,540 @@
+/**
+ * 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.stack;
+
+import java.util.ArrayList;
+import java.util.Collection;
+import java.util.HashMap;
+import java.util.HashSet;
+import java.util.List;
+import java.util.Map;
+import java.util.Set;
+
+import org.apache.ambari.server.AmbariException;
+import org.apache.ambari.server.state.ConfigHelper;
+import org.apache.ambari.server.state.ExtensionInfo;
+import org.apache.ambari.server.state.PropertyDependencyInfo;
+import org.apache.ambari.server.state.PropertyInfo;
+import org.apache.ambari.server.state.RepositoryInfo;
+import org.apache.ambari.server.state.ServiceInfo;
+import org.apache.ambari.server.state.stack.ExtensionMetainfoXml;
+import org.apache.ambari.server.state.stack.RepositoryXml;
+import org.apache.ambari.server.state.stack.ServiceMetainfoXml;
+import org.slf4j.Logger;
+import org.slf4j.LoggerFactory;
+
+/**
+ * Extension module which provides all functionality related to parsing and fully
+ * resolving extensions from the extension definition.
+ *
+ * An extension version is like a stack version but it contains custom services. Linking an extension
+ * version to the current stack version allows the cluster to install the custom services contained in
+ * the extension version.
+ *
+ * <p>
+ * Each extension node is identified by name and version, contains service
+ * child nodes and may extend a single parent extension.
+ * </p>
+ *
+ * <p>
+ * Resolution of a extension is a depth first traversal up the inheritance chain where each extension node
+ * calls resolve on its parent before resolving itself. After the parent resolve call returns, all
+ * ancestors in the inheritance tree are fully resolved. The act of resolving the extension includes
+ * resolution of the services children of the extension as well as merging of other extension
+ * state with the fully resolved parent.
+ * </p>
+ *
+ * <p>
+ * Because a service may explicitly extend another service in a extension outside of the inheritance tree,
+ * service child node resolution involves a depth first resolution of the extension associated with the
+ * services explicit parent, if any. This follows the same steps defined above fore extension node
+ * resolution. After the services explicit parent is fully resolved, the services state is merged
+ * with it's parent.
+ * </p>
+ *
+ * <p>
+ * If a cycle in a extension definition is detected, an exception is thrown from the resolve call.
+ * </p>
+ *
+ */
+public class ExtensionModule extends BaseModule<ExtensionModule, ExtensionInfo> implements Validable {
+
+ /**
+ * Context which provides access to external functionality
+ */
+ private StackContext stackContext;
+
+ /**
+ * Map of child configuration modules keyed by configuration type
+ */
+ private Map<String, ConfigurationModule> configurationModules = new HashMap<String, ConfigurationModule>();
+
+ /**
+ * Map of child service modules keyed by service name
+ */
+ private Map<String, ServiceModule> serviceModules = new HashMap<String, ServiceModule>();
+
+ /**
+ * Corresponding ExtensionInfo instance
+ */
+ private ExtensionInfo extensionInfo;
+
+ /**
+ * Encapsulates IO operations on extension directory
+ */
+ private ExtensionDirectory extensionDirectory;
+
+ /**
+ * Extension id which is in the form extensionName:extensionVersion
+ */
+ private String id;
+
+ /**
+ * validity flag
+ */
+ protected boolean valid = true;
+
+ /**
+ * Logger
+ */
+ private final static Logger LOG = LoggerFactory.getLogger(ExtensionModule.class);
+
+ /**
+ * Constructor.
+ * @param extensionDirectory represents extension directory
+ * @param extensionContext general extension context
+ */
+ public ExtensionModule(ExtensionDirectory extensionDirectory, StackContext stackContext) {
+ this.extensionDirectory = extensionDirectory;
+ this.stackContext = stackContext;
+ this.extensionInfo = new ExtensionInfo();
+ populateExtensionInfo();
+ }
+
+ public Map<String, ServiceModule> getServiceModules() {
+ return serviceModules;
+ }
+
+ /**
+ * Fully resolve the extension. See extension resolution description in the class documentation.
+ * If the extension has a parent, this extension will be merged against its fully resolved parent
+ * if one is specified. Merging applies to all extension state including child service and
+ * configuration modules. Services may extend a service in another version in the
+ * same extension hierarchy or may explicitly extend a service in a different
+ * hierarchy.
+ *
+ * @param parentModule not used. Each extension determines its own parent since extensions don't
+ * have containing modules
+ * @param allStacks all stacks modules contained in the stack definition
+ * @param commonServices all common services
+ * @param extensions all extensions
+ *
+ * @throws AmbariException if an exception occurs during extension resolution
+ */
+ @Override
+ public void resolve(
+ ExtensionModule parentModule, Map<String, StackModule> allStacks, Map<String, ServiceModule> commonServices, Map<String, ExtensionModule> extensions)
+ throws AmbariException {
+ moduleState = ModuleState.VISITED;
+ checkExtensionName(allStacks);
+
+ String parentVersion = extensionInfo.getParentExtensionVersion();
+ mergeServicesWithExplicitParent(allStacks, commonServices, extensions);
+ // merge with parent version of same extension definition
+ if (parentVersion != null) {
+ mergeExtensionWithParent(parentVersion, allStacks, commonServices, extensions);
+ }
+ moduleState = ModuleState.RESOLVED;
+ }
+
+ @Override
+ public ExtensionInfo getModuleInfo() {
+ return extensionInfo;
+ }
+
+ @Override
+ public boolean isDeleted() {
+ return false;
+ }
+
+ @Override
+ public String getId() {
+ return id;
+ }
+
+ @Override
+ public void finalizeModule() {
+ finalizeChildModules(serviceModules.values());
+ finalizeChildModules(configurationModules.values());
+ }
+
+ /**
+ * Get the associated extension directory.
+ *
+ * @return associated extension directory
+ */
+ public ExtensionDirectory getExtensionDirectory() {
+ return extensionDirectory;
+ }
+
+ /**
+ * Merge the extension with its parent.
+ *
+ * @param allStacks all stacks in stack definition
+ * @param commonServices all common services specified in the stack definition
+ * @param parentVersion version of the extensions parent
+ *
+ * @throws AmbariException if an exception occurs merging with the parent
+ */
+ private void mergeExtensionWithParent(
+ String parentVersion, Map<String, StackModule> allStacks, Map<String, ServiceModule> commonServices, Map<String, ExtensionModule> extensions)
+ throws AmbariException {
+
+ String parentExtensionKey = extensionInfo.getName() + StackManager.PATH_DELIMITER + parentVersion;
+ ExtensionModule parentExtension = extensions.get(parentExtensionKey);
+
+ if (parentExtension == null) {
+ throw new AmbariException("Extension '" + extensionInfo.getName() + ":" + extensionInfo.getVersion() +
+ "' specifies a parent that doesn't exist");
+ }
+
+ resolveExtension(parentExtension, allStacks, commonServices, extensions);
+ /*mergeConfigurations(parentStack, allStacks, commonServices);
+ mergeRoleCommandOrder(parentStack);*/
+
+ /*if (extensionInfo.getStackHooksFolder() == null) {
+ extensionInfo.setStackHooksFolder(parentStack.getModuleInfo().getStackHooksFolder());
+ }
+
+ if (extensionInfo.getKerberosDescriptorFileLocation() == null) {
+ extensionInfo.setKerberosDescriptorFileLocation(parentStack.getModuleInfo().getKerberosDescriptorFileLocation());
+ }
+
+ if (extensionInfo.getWidgetsDescriptorFileLocation() == null) {
+ extensionInfo.setWidgetsDescriptorFileLocation(parentStack.getModuleInfo().getWidgetsDescriptorFileLocation());
+ }*/
+
+ mergeServicesWithParent(parentExtension, allStacks, commonServices, extensions);
+ }
+
+ /**
+ * Merge child services with parent extension.
+ *
+ * @param parentExtension parent extension module
+ * @param allStacks all stacks in stack definition
+ * @param commonServices all common services
+ * @param extensions all extensions
+ *
+ * @throws AmbariException if an exception occurs merging the child services with the parent extension
+ */
+ private void mergeServicesWithParent(
+ ExtensionModule parentExtension, Map<String, StackModule> allStacks, Map<String, ServiceModule> commonServices, Map<String, ExtensionModule> extensions)
+ throws AmbariException {
+ extensionInfo.getServices().clear();
+
+ LOG.info("***Merging extension services with parent: " + parentExtension.getId());
+
+ Collection<ServiceModule> mergedModules = mergeChildModules(
+ allStacks, commonServices, extensions, serviceModules, parentExtension.serviceModules);
+ for (ServiceModule module : mergedModules) {
+ serviceModules.put(module.getId(), module);
+ extensionInfo.getServices().add(module.getModuleInfo());
+ }
+ }
+
+ /**
+ * Merge services with their explicitly specified parent if one has been specified.
+ * @param allStacks all stacks in stack definition
+ * @param commonServices all common services specified in the stack definition
+ *
+ * @throws AmbariException if an exception occurs while merging child services with their explicit parents
+ */
+ private void mergeServicesWithExplicitParent(
+ Map<String, StackModule> allStacks, Map<String, ServiceModule> commonServices, Map<String, ExtensionModule> extensions) throws AmbariException {
+ for (ServiceModule service : serviceModules.values()) {
+ ServiceInfo serviceInfo = service.getModuleInfo();
+ String parent = serviceInfo.getParent();
+ if (parent != null) {
+ mergeServiceWithExplicitParent(service, parent, allStacks, commonServices, extensions);
+ }
+ }
+ }
+
+ /**
+ * Merge a service with its explicitly specified parent.
+ * @param service the service to merge
+ * @param parent the explicitly specified parent service
+ * @param allStacks all stacks specified in the stack definition
+ * @param commonServices all common services specified in the stack definition
+ *
+ * @throws AmbariException if an exception occurs merging a service with its explicit parent
+ */
+ private void mergeServiceWithExplicitParent(
+ ServiceModule service, String parent, Map<String, StackModule> allStacks,
+ Map<String, ServiceModule> commonServices, Map<String, ExtensionModule> extensions)
+ throws AmbariException {
+ if(isCommonServiceParent(parent)) {
+ LOG.info("merging with common service: " + service.getModuleInfo().getName());
+ mergeServiceWithCommonServiceParent(service, parent, allStacks, commonServices, extensions);
+ LOG.info("display name: " + service.getModuleInfo().getDisplayName());
+ } else {
+ throw new AmbariException("The service '" + service.getModuleInfo().getName() + "' in extension '" + extensionInfo.getName() + ":"
+ + extensionInfo.getVersion() + "' extends an invalid parent: '" + parent + "'");
+ }
+ }
+
+ /**
+ * @param allStacks all stacks specified in the stack definition
+ *
+ * @throws AmbariException if the extension name is the same as any of the stacks
+ */
+ private void checkExtensionName(Map<String, StackModule> allStacks)
+ throws AmbariException {
+
+ String name = extensionInfo.getName();
+ for (StackModule stack : allStacks.values()) {
+ String stackName = stack.getModuleInfo().getName();
+ if (name.equals(stackName)) {
+ throw new AmbariException("The extension '" + name + "' has a name which matches a stack name");
+ }
+ }
+ }
+
+ /**
+ * Check if parent is common service
+ * @param parent Parent string
+ * @return true: if parent is common service, false otherwise
+ */
+ private boolean isCommonServiceParent(String parent) {
+ return parent != null
+ && !parent.isEmpty()
+ && parent.split(StackManager.PATH_DELIMITER)[0].equalsIgnoreCase(StackManager.COMMON_SERVICES);
+ }
+
+ /**
+ * Merge a service with its explicitly specified common service as parent.
+ * Parent: common-services/<serviceName>/<serviceVersion>
+ * Common Services Lookup Key: <serviceName>/<serviceVersion>
+ * Example:
+ * Parent: common-services/HDFS/2.1.0.2.0
+ * Key: HDFS/2.1.0.2.0
+ *
+ * @param service the service to merge
+ * @param parent the explicitly specified common service as parent
+ * @param allStacks all stacks specified in the stack definition
+ * @param commonServices all common services specified in the stack definition
+ * @throws AmbariException
+ */
+ private void mergeServiceWithCommonServiceParent(
+ ServiceModule service, String parent, Map<String, StackModule> allStacks,
+ Map<String, ServiceModule> commonServices, Map<String, ExtensionModule> extensions)
+ throws AmbariException {
+ ServiceInfo serviceInfo = service.getModuleInfo();
+ String[] parentToks = parent.split(StackManager.PATH_DELIMITER);
+ if(parentToks.length != 3 || !parentToks[0].equalsIgnoreCase(StackManager.COMMON_SERVICES)) {
+ throw new AmbariException("The service '" + serviceInfo.getName() + "' in extension '" + extensionInfo.getName() + ":"
+ + extensionInfo.getVersion() + "' extends an invalid parent: '" + parent + "'");
+ }
+
+ String baseServiceKey = parentToks[1] + StackManager.PATH_DELIMITER + parentToks[2];
+ ServiceModule baseService = commonServices.get(baseServiceKey);
+ if (baseService == null) {
+ setValid(false);
+ extensionInfo.setValid(false);
+ String error = "The service '" + serviceInfo.getName() + "' in extension '" + extensionInfo.getName() + ":"
+ + extensionInfo.getVersion() + "' extends a non-existent service: '" + parent + "'";
+ addError(error);
+ extensionInfo.addError(error);
+ } else {
+ if (baseService.isValid()) {
+ service.resolveExplicit(baseService, allStacks, commonServices, extensions);
+ } else {
+ setValid(false);
+ extensionInfo.setValid(false);
+ addErrors(baseService.getErrors());
+ extensionInfo.addErrors(baseService.getErrors());
+ }
+ }
+ }
+
+ /**
+ * Populate the extension module and info from the extension definition.
+ */
+ private void populateExtensionInfo() {
+ extensionInfo.setName(extensionDirectory.getExtensionDirName());
+ extensionInfo.setVersion(extensionDirectory.getName());
+
+ id = String.format("%s:%s", extensionInfo.getName(), extensionInfo.getVersion());
+
+ LOG.debug("Adding new extension to known extensions"
+ + ", extensionName = " + extensionInfo.getName()
+ + ", extensionVersion = " + extensionInfo.getVersion());
+
+
+ //todo: give additional thought on handling missing metainfo.xml
+ ExtensionMetainfoXml emx = extensionDirectory.getMetaInfoFile();
+ if (emx != null) {
+ if (!emx.isValid()) {
+ extensionInfo.setValid(false);
+ extensionInfo.addErrors(emx.getErrors());
+ }
+ extensionInfo.setParentExtensionVersion(emx.getExtends());
+ extensionInfo.setStacks(emx.getStacks());
+ extensionInfo.setExtensions(emx.getExtensions());
+ }
+
+ try {
+ // Read the service for this extension
+ populateServices();
+ if (!extensionInfo.isValid()) {
+ setValid(false);
+ addErrors(extensionInfo.getErrors());
+ }
+
+ //todo: shouldn't blindly catch Exception, re-evaluate this.
+ } catch (Exception e) {
+ String error = "Exception caught while populating services for extension: " +
+ extensionInfo.getName() + "-" + extensionInfo.getVersion();
+ setValid(false);
+ extensionInfo.setValid(false);
+ addError(error);
+ extensionInfo.addError(error);
+ LOG.error(error);
+ }
+ }
+
+ /**
+ * Populate the child services.
+ */
+ private void populateServices()throws AmbariException {
+ for (ServiceDirectory serviceDir : extensionDirectory.getServiceDirectories()) {
+ populateService(serviceDir);
+ }
+ }
+
+ /**
+ * Populate a child service.
+ *
+ * @param serviceDirectory the child service directory
+ */
+ private void populateService(ServiceDirectory serviceDirectory) {
+ Collection<ServiceModule> serviceModules = new ArrayList<ServiceModule>();
+ // unfortunately, we allow multiple services to be specified in the same metainfo.xml,
+ // so we can't move the unmarshal logic into ServiceModule
+ ServiceMetainfoXml metaInfoXml = serviceDirectory.getMetaInfoFile();
+ if (!metaInfoXml.isValid()){
+ extensionInfo.setValid(metaInfoXml.isValid());
+ setValid(metaInfoXml.isValid());
+ extensionInfo.addErrors(metaInfoXml.getErrors());
+ addErrors(metaInfoXml.getErrors());
+ return;
+ }
+ List<ServiceInfo> serviceInfos = metaInfoXml.getServices();
+
+ for (ServiceInfo serviceInfo : serviceInfos) {
+ ServiceModule serviceModule = new ServiceModule(stackContext, serviceInfo, serviceDirectory);
+ serviceModules.add(serviceModule);
+ if (!serviceModule.isValid()){
+ extensionInfo.setValid(false);
+ setValid(false);
+ extensionInfo.addErrors(serviceModule.getErrors());
+ addErrors(serviceModule.getErrors());
+ }
+ }
+ addServices(serviceModules);
+ }
+
+ /**
+ * Resolve another extension module.
+ *
+ * @param parentExtension extension module to be resolved
+ * @param allStacks all stack modules in stack definition
+ * @param commonServices all common services specified in the stack definition
+ * @param extensions all extensions
+ * @throws AmbariException if unable to resolve the extension
+ */
+ private void resolveExtension(
+ ExtensionModule parentExtension, Map<String, StackModule> allStacks, Map<String, ServiceModule> commonServices, Map<String, ExtensionModule> extensions)
+ throws AmbariException {
+ if (parentExtension.getModuleState() == ModuleState.INIT) {
+ parentExtension.resolve(null, allStacks, commonServices, extensions);
+ } else if (parentExtension.getModuleState() == ModuleState.VISITED) {
+ //todo: provide more information to user about cycle
+ throw new AmbariException("Cycle detected while parsing extension definition");
+ }
+ if (!parentExtension.isValid() || (parentExtension.getModuleInfo() != null && !parentExtension.getModuleInfo().isValid())) {
+ setValid(parentExtension.isValid());
+ extensionInfo.setValid(parentExtension.extensionInfo.isValid());
+ addErrors(parentExtension.getErrors());
+ extensionInfo.addErrors(parentExtension.getErrors());
+ }
+ }
+
+ /**
+ * Add a child service module to the extension.
+ *
+ * @param service service module to add
+ */
+ private void addService(ServiceModule service) {
+ ServiceInfo serviceInfo = service.getModuleInfo();
+ Object previousValue = serviceModules.put(service.getId(), service);
+ if (previousValue == null) {
+ extensionInfo.getServices().add(serviceInfo);
+ }
+ }
+
+ /**
+ * Add child service modules to the extension.
+ *
+ * @param services collection of service modules to add
+ */
+ private void addServices(Collection<ServiceModule> services) {
+ for (ServiceModule service : services) {
+ addService(service);
+ }
+ }
+
+ @Override
+ public boolean isValid() {
+ return valid;
+ }
+
+ @Override
+ public void setValid(boolean valid) {
+ this.valid = valid;
+ }
+
+ private Set<String> errorSet = new HashSet<String>();
+
+ @Override
+ public Collection getErrors() {
+ return errorSet;
+ }
+
+ @Override
+ public void addError(String error) {
+ errorSet.add(error);
+ }
+
+ @Override
+ public void addErrors(Collection<String> errors) {
+ this.errorSet.addAll(errors);
+ }
+
+}
http://git-wip-us.apache.org/repos/asf/ambari/blob/300a7e21/ambari-server/src/main/java/org/apache/ambari/server/stack/ModuleFileUnmarshaller.java
----------------------------------------------------------------------
diff --git a/ambari-server/src/main/java/org/apache/ambari/server/stack/ModuleFileUnmarshaller.java b/ambari-server/src/main/java/org/apache/ambari/server/stack/ModuleFileUnmarshaller.java
index 9e2f997..7d47339 100644
--- a/ambari-server/src/main/java/org/apache/ambari/server/stack/ModuleFileUnmarshaller.java
+++ b/ambari-server/src/main/java/org/apache/ambari/server/stack/ModuleFileUnmarshaller.java
@@ -20,6 +20,7 @@ package org.apache.ambari.server.stack;
import org.apache.ambari.server.state.stack.ConfigUpgradePack;
import org.apache.ambari.server.state.stack.ConfigurationXml;
+import org.apache.ambari.server.state.stack.ExtensionMetainfoXml;
import org.apache.ambari.server.state.stack.RepositoryXml;
import org.apache.ambari.server.state.stack.ServiceMetainfoXml;
import org.apache.ambari.server.state.stack.StackMetainfoXml;
@@ -27,7 +28,9 @@ import org.apache.ambari.server.state.stack.UpgradePack;
import javax.xml.bind.JAXBContext;
import javax.xml.bind.JAXBException;
+import javax.xml.bind.Marshaller;
import javax.xml.bind.Unmarshaller;
+
import java.io.File;
import java.util.HashMap;
import java.util.Map;
@@ -72,6 +75,7 @@ class ModuleFileUnmarshaller {
jaxbContexts.put(UpgradePack.class, ctx);
jaxbContexts.put(ConfigUpgradePack.class, ctx);
jaxbContexts.put(ServiceMetainfoXml.class, JAXBContext.newInstance(ServiceMetainfoXml.class));
+ jaxbContexts.put(ExtensionMetainfoXml.class, JAXBContext.newInstance(ExtensionMetainfoXml.class));
} catch (JAXBException e) {
throw new RuntimeException (e);
}
http://git-wip-us.apache.org/repos/asf/ambari/blob/300a7e21/ambari-server/src/main/java/org/apache/ambari/server/stack/QuickLinksConfigurationModule.java
----------------------------------------------------------------------
diff --git a/ambari-server/src/main/java/org/apache/ambari/server/stack/QuickLinksConfigurationModule.java b/ambari-server/src/main/java/org/apache/ambari/server/stack/QuickLinksConfigurationModule.java
index 84da70e..ed8e8cf 100644
--- a/ambari-server/src/main/java/org/apache/ambari/server/stack/QuickLinksConfigurationModule.java
+++ b/ambari-server/src/main/java/org/apache/ambari/server/stack/QuickLinksConfigurationModule.java
@@ -83,7 +83,7 @@ public class QuickLinksConfigurationModule extends BaseModule<QuickLinksConfigur
}
@Override
- public void resolve(QuickLinksConfigurationModule parent, Map<String, StackModule> allStacks, Map<String, ServiceModule> commonServices) throws AmbariException {
+ public void resolve(QuickLinksConfigurationModule parent, Map<String, StackModule> allStacks, Map<String, ServiceModule> commonServices, Map<String, ExtensionModule> allExtensions) throws AmbariException {
QuickLinksConfigurationInfo parentModuleInfo = parent.getModuleInfo();
if (parent.getModuleInfo() != null && !moduleInfo.isDeleted()) {
http://git-wip-us.apache.org/repos/asf/ambari/blob/300a7e21/ambari-server/src/main/java/org/apache/ambari/server/stack/ServiceDirectory.java
----------------------------------------------------------------------
diff --git a/ambari-server/src/main/java/org/apache/ambari/server/stack/ServiceDirectory.java b/ambari-server/src/main/java/org/apache/ambari/server/stack/ServiceDirectory.java
index 9ed2c24..e938e68 100644
--- a/ambari-server/src/main/java/org/apache/ambari/server/stack/ServiceDirectory.java
+++ b/ambari-server/src/main/java/org/apache/ambari/server/stack/ServiceDirectory.java
@@ -22,11 +22,17 @@ import org.apache.ambari.server.AmbariException;
import org.apache.ambari.server.api.services.AmbariMetaInfo;
import org.apache.ambari.server.state.ServiceInfo;
import org.apache.ambari.server.state.stack.ServiceMetainfoXml;
+import org.apache.ambari.server.state.stack.StackRoleCommandOrder;
+import org.codehaus.jackson.map.ObjectMapper;
+import org.codehaus.jackson.type.TypeReference;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import javax.xml.bind.JAXBException;
+
import java.io.File;
+import java.io.IOException;
+import java.io.InputStream;
import java.util.HashMap;
import java.util.Map;
@@ -60,6 +66,16 @@ public abstract class ServiceDirectory extends StackDefinitionDirectory {
private File kerberosDescriptorFile;
/**
+ * RCO file
+ */
+ private File rcoFile;
+
+ /**
+ * role command order
+ */
+ private StackRoleCommandOrder roleCommandOrder;
+
+ /**
* widgets descriptor file
*/
private Map<String, File> widgetsDescriptorFileMap = new HashMap<String, File>();
@@ -119,33 +135,6 @@ public abstract class ServiceDirectory extends StackDefinitionDirectory {
public ServiceDirectory(String servicePath) throws AmbariException {
super(servicePath);
parsePath();
-
- File af = new File(directory.getAbsolutePath()
- + File.separator + AmbariMetaInfo.SERVICE_ALERT_FILE_NAME);
- alertsFile = af.exists() ? af : null;
-
- File kdf = new File(directory.getAbsolutePath()
- + File.separator + AmbariMetaInfo.KERBEROS_DESCRIPTOR_FILE_NAME);
- kerberosDescriptorFile = kdf.exists() ? kdf : null;
-
- if (metaInfoXml.getServices() != null) {
- for (ServiceInfo serviceInfo : metaInfoXml.getServices()) {
- File mf = new File(directory.getAbsolutePath()
- + File.separator + serviceInfo.getMetricsFileName());
- metricsFileMap.put(serviceInfo.getName(), mf.exists() ? mf : null);
-
- File wdf = new File(directory.getAbsolutePath()
- + File.separator + serviceInfo.getWidgetsFileName());
- widgetsDescriptorFileMap.put(serviceInfo.getName(), wdf.exists() ? wdf : null);
- }
- }
-
- File advFile = new File(directory.getAbsolutePath()
- + File.separator + AmbariMetaInfo.SERVICE_ADVISOR_FILE_NAME);
- advisorFile = advFile.exists() ? advFile : null;
-
- File themeFile = new File(directory.getAbsolutePath() + File.separator + AmbariMetaInfo.SERVICE_THEME_FILE_NAME);
- this.themeFile = themeFile.exists() ? themeFile : null;
}
/**
@@ -237,9 +226,54 @@ public abstract class ServiceDirectory extends StackDefinitionDirectory {
}
/**
+ * Obtain the object representation of the service role_command_order.json file
+ *
+ * @return object representation of the service role_command_order.json file
+ */
+ public StackRoleCommandOrder getRoleCommandOrder() {
+ return roleCommandOrder;
+ }
+
+ /**
* Parse the service directory.
*/
- protected abstract void parsePath() throws AmbariException;
+ protected void parsePath() throws AmbariException {
+ calculateDirectories();
+ parseMetaInfoFile();
+
+ File af = new File(directory, AmbariMetaInfo.SERVICE_ALERT_FILE_NAME);
+ alertsFile = af.exists() ? af : null;
+
+ File kdf = new File(directory, AmbariMetaInfo.KERBEROS_DESCRIPTOR_FILE_NAME);
+ kerberosDescriptorFile = kdf.exists() ? kdf : null;
+
+ File rco = new File(directory, AmbariMetaInfo.RCO_FILE_NAME);
+ if (rco.exists()) {
+ rcoFile = rco;
+ parseRoleCommandOrder();
+ }
+
+ if (metaInfoXml.getServices() != null) {
+ for (ServiceInfo serviceInfo : metaInfoXml.getServices()) {
+ File mf = new File(directory, serviceInfo.getMetricsFileName());
+ metricsFileMap.put(serviceInfo.getName(), mf.exists() ? mf : null);
+
+ File wdf = new File(directory, serviceInfo.getWidgetsFileName());
+ widgetsDescriptorFileMap.put(serviceInfo.getName(), wdf.exists() ? wdf : null);
+ }
+ }
+
+ File advFile = new File(directory, AmbariMetaInfo.SERVICE_ADVISOR_FILE_NAME);
+ advisorFile = advFile.exists() ? advFile : null;
+
+ File themeFile = new File(directory, AmbariMetaInfo.SERVICE_THEME_FILE_NAME);
+ this.themeFile = themeFile.exists() ? themeFile : null;
+ }
+
+ /**
+ * Calculate the service specific directories.
+ */
+ protected abstract void calculateDirectories();
/**
* Unmarshal the metainfo file into its object representation.
@@ -266,4 +300,28 @@ public abstract class ServiceDirectory extends StackDefinitionDirectory {
}
}
+ /**
+ * Parse role command order file
+ */
+ private void parseRoleCommandOrder() {
+ if (rcoFile == null)
+ return;
+
+ try {
+ ObjectMapper mapper = new ObjectMapper();
+ TypeReference<Map<String, Object>> rcoElementTypeReference = new TypeReference<Map<String, Object>>() {};
+ HashMap<String, Object> result = mapper.readValue(rcoFile, rcoElementTypeReference);
+ LOG.info("Role command order info was loaded from file: {}", rcoFile.getAbsolutePath());
+
+ roleCommandOrder = new StackRoleCommandOrder(result);
+
+ if (LOG.isDebugEnabled() && rcoFile != null) {
+ LOG.debug("Role Command Order for " + rcoFile.getAbsolutePath());
+ roleCommandOrder.printRoleCommandOrder(LOG);
+ }
+ } catch (IOException e) {
+ LOG.error(String.format("Can not read role command order info %s", rcoFile.getAbsolutePath()), e);
+ }
+ }
+
}
http://git-wip-us.apache.org/repos/asf/ambari/blob/300a7e21/ambari-server/src/main/java/org/apache/ambari/server/stack/ServiceModule.java
----------------------------------------------------------------------
diff --git a/ambari-server/src/main/java/org/apache/ambari/server/stack/ServiceModule.java b/ambari-server/src/main/java/org/apache/ambari/server/stack/ServiceModule.java
index 17a2a93..bc94104 100644
--- a/ambari-server/src/main/java/org/apache/ambari/server/stack/ServiceModule.java
+++ b/ambari-server/src/main/java/org/apache/ambari/server/stack/ServiceModule.java
@@ -36,6 +36,8 @@ import org.apache.ambari.server.state.ThemeInfo;
import org.apache.commons.lang.StringUtils;
import org.apache.commons.lang.builder.ToStringBuilder;
import org.apache.commons.lang.builder.ToStringStyle;
+import org.slf4j.Logger;
+import org.slf4j.LoggerFactory;
import javax.annotation.Nullable;
@@ -102,6 +104,11 @@ public class ServiceModule extends BaseModule<ServiceModule, ServiceInfo> implem
protected boolean valid = true;
/**
+ * Logger
+ */
+ private final static Logger LOG = LoggerFactory.getLogger(ServiceModule.class);
+
+ /**
* Constructor.
*
* @param stackContext stack context which provides module access to external functionality
@@ -131,6 +138,7 @@ public class ServiceModule extends BaseModule<ServiceModule, ServiceInfo> implem
serviceInfo.setAlertsFile(serviceDirectory.getAlertsFile());
serviceInfo.setKerberosDescriptorFile(serviceDirectory.getKerberosDescriptorFile());
serviceInfo.setWidgetsDescriptorFile(serviceDirectory.getWidgetsDescriptorFile(serviceInfo.getName()));
+ serviceInfo.setRoleCommandOrder(serviceDirectory.getRoleCommandOrder());
serviceInfo.setSchemaVersion(AmbariMetaInfo.SCHEMA_VERSION_2);
serviceInfo.setServicePackageFolder(serviceDirectory.getPackageDir());
serviceInfo.setServiceUpgradesFolder(serviceDirectory.getUpgradesDir());
@@ -152,25 +160,27 @@ public class ServiceModule extends BaseModule<ServiceModule, ServiceInfo> implem
@Override
public void resolve(
- ServiceModule parentModule, Map<String, StackModule> allStacks, Map<String, ServiceModule> commonServices)
+ ServiceModule parentModule, Map<String, StackModule> allStacks, Map<String, ServiceModule> commonServices, Map<String, ExtensionModule> extensions)
throws AmbariException {
- resolveInternal(parentModule, allStacks, commonServices, false);
+ resolveInternal(parentModule, allStacks, commonServices, extensions, false);
}
public void resolveExplicit(
- ServiceModule parentModule, Map<String, StackModule> allStacks, Map<String, ServiceModule> commonServices)
+ ServiceModule parentModule, Map<String, StackModule> allStacks, Map<String, ServiceModule> commonServices, Map<String, ExtensionModule> extensions)
throws AmbariException {
- resolveInternal(parentModule, allStacks, commonServices, true);
+ resolveInternal(parentModule, allStacks, commonServices, extensions, true);
}
public void resolveInternal(
ServiceModule parentModule, Map<String, StackModule> allStacks, Map<String, ServiceModule> commonServices,
- boolean resolveExplicit)
+ Map<String, ExtensionModule> extensions, boolean resolveExplicit)
throws AmbariException {
if (!serviceInfo.isValid() || !parentModule.isValid()) {
return;
}
+ LOG.info("Resolve service");
+
// If resolving against parent stack service module (stack inheritance), do not merge if an
// explicit parent is specified
if(!StringUtils.isBlank(serviceInfo.getParent()) && !resolveExplicit) {
@@ -182,6 +192,7 @@ public class ServiceModule extends BaseModule<ServiceModule, ServiceInfo> implem
if (serviceInfo.getComment() == null) {
serviceInfo.setComment(parent.getComment());
}
+ LOG.info("Display name service/parent: " + serviceInfo.getDisplayName() + "/" + parent.getDisplayName());
if (serviceInfo.getDisplayName() == null) {
serviceInfo.setDisplayName(parent.getDisplayName());
}
@@ -239,17 +250,19 @@ public class ServiceModule extends BaseModule<ServiceModule, ServiceInfo> implem
serviceInfo.setAdvisorName(parent.getAdvisorName());
}
+ if (serviceInfo.getRoleCommandOrder() == null) {
+ serviceInfo.setRoleCommandOrder(parent.getRoleCommandOrder());
+ }
+
mergeCustomCommands(parent.getCustomCommands(), serviceInfo.getCustomCommands());
mergeConfigDependencies(parent);
- mergeComponents(parentModule, allStacks, commonServices);
- mergeConfigurations(parentModule, allStacks, commonServices);
- mergeThemes(parentModule, allStacks, commonServices);
- mergeQuickLinksConfigurations(parentModule, allStacks, commonServices);
+ mergeComponents(parentModule, allStacks, commonServices, extensions);
+ mergeConfigurations(parentModule, allStacks, commonServices, extensions);
+ mergeThemes(parentModule, allStacks, commonServices, extensions);
+ mergeQuickLinksConfigurations(parentModule, allStacks, commonServices, extensions);
mergeExcludedConfigTypes(parent);
-
mergeServiceProperties(parent.getServicePropertyList());
-
}
/**
@@ -296,7 +309,7 @@ public class ServiceModule extends BaseModule<ServiceModule, ServiceInfo> implem
*
* @throws AmbariException
*/
- public void resolveCommonService(Map<String, StackModule> allStacks, Map<String, ServiceModule> commonServices)
+ public void resolveCommonService(Map<String, StackModule> allStacks, Map<String, ServiceModule> commonServices, Map<String, ExtensionModule> extensions)
throws AmbariException {
if(!isCommonService) {
throw new AmbariException("Not a common service");
@@ -314,12 +327,12 @@ public class ServiceModule extends BaseModule<ServiceModule, ServiceInfo> implem
ServiceModule baseService = commonServices.get(baseServiceKey);
ModuleState baseModuleState = baseService.getModuleState();
if (baseModuleState == ModuleState.INIT) {
- baseService.resolveCommonService(allStacks, commonServices);
+ baseService.resolveCommonService(allStacks, commonServices, extensions);
} else if (baseModuleState == ModuleState.VISITED) {
//todo: provide more information to user about cycle
throw new AmbariException("Cycle detected while parsing common service");
}
- resolveExplicit(baseService, allStacks, commonServices);
+ resolveExplicit(baseService, allStacks, commonServices, extensions);
} else {
throw new AmbariException("Common service cannot inherit from a non common service");
}
@@ -414,8 +427,8 @@ public class ServiceModule extends BaseModule<ServiceModule, ServiceInfo> implem
* Merge theme modules.
*/
private void mergeThemes(ServiceModule parent, Map<String, StackModule> allStacks,
- Map<String, ServiceModule> commonServices) throws AmbariException {
- Collection<ThemeModule> mergedModules = mergeChildModules(allStacks, commonServices, themeModules, parent.themeModules);
+ Map<String, ServiceModule> commonServices, Map<String, ExtensionModule> extensions) throws AmbariException {
+ Collection<ThemeModule> mergedModules = mergeChildModules(allStacks, commonServices, extensions, themeModules, parent.themeModules);
for (ThemeModule mergedModule : mergedModules) {
themeModules.put(mergedModule.getId(), mergedModule);
@@ -448,8 +461,8 @@ public class ServiceModule extends BaseModule<ServiceModule, ServiceInfo> implem
* Merge theme modules.
*/
private void mergeQuickLinksConfigurations(ServiceModule parent, Map<String, StackModule> allStacks,
- Map<String, ServiceModule> commonServices) throws AmbariException {
- Collection<QuickLinksConfigurationModule> mergedModules = mergeChildModules(allStacks, commonServices, quickLinksConfigurationModules, parent.quickLinksConfigurationModules);
+ Map<String, ServiceModule> commonServices, Map<String, ExtensionModule> extensions) throws AmbariException {
+ Collection<QuickLinksConfigurationModule> mergedModules = mergeChildModules(allStacks, commonServices, extensions, quickLinksConfigurationModules, parent.quickLinksConfigurationModules);
for (QuickLinksConfigurationModule mergedModule : mergedModules) {
quickLinksConfigurationModules.put(mergedModule.getId(), mergedModule);
@@ -514,13 +527,13 @@ public class ServiceModule extends BaseModule<ServiceModule, ServiceInfo> implem
* @param commonServices common service modules
*/
private void mergeConfigurations(
- ServiceModule parent, Map<String, StackModule> allStacks, Map<String, ServiceModule> commonServices)
+ ServiceModule parent, Map<String, StackModule> allStacks, Map<String, ServiceModule> commonServices, Map<String, ExtensionModule> extensions)
throws AmbariException {
serviceInfo.getProperties().clear();
serviceInfo.setAllConfigAttributes(new HashMap<String, Map<String, Map<String, String>>>());
Collection<ConfigurationModule> mergedModules = mergeChildModules(
- allStacks, commonServices, configurationModules, parent.configurationModules);
+ allStacks, commonServices, extensions, configurationModules, parent.configurationModules);
for (ConfigurationModule module : mergedModules) {
configurationModules.put(module.getId(), module);
@@ -540,11 +553,11 @@ public class ServiceModule extends BaseModule<ServiceModule, ServiceInfo> implem
* @param commonServices common service modules
*/
private void mergeComponents(
- ServiceModule parent, Map<String, StackModule> allStacks, Map<String, ServiceModule> commonServices)
+ ServiceModule parent, Map<String, StackModule> allStacks, Map<String, ServiceModule> commonServices, Map<String, ExtensionModule> extensions)
throws AmbariException {
serviceInfo.getComponents().clear();
Collection<ComponentModule> mergedModules = mergeChildModules(
- allStacks, commonServices, componentModules, parent.componentModules);
+ allStacks, commonServices, extensions, componentModules, parent.componentModules);
componentModules.clear();
for (ComponentModule module : mergedModules) {
componentModules.put(module.getId(), module);
http://git-wip-us.apache.org/repos/asf/ambari/blob/300a7e21/ambari-server/src/main/java/org/apache/ambari/server/stack/StackDefinitionModule.java
----------------------------------------------------------------------
diff --git a/ambari-server/src/main/java/org/apache/ambari/server/stack/StackDefinitionModule.java b/ambari-server/src/main/java/org/apache/ambari/server/stack/StackDefinitionModule.java
index b35afb2..0e834b8 100644
--- a/ambari-server/src/main/java/org/apache/ambari/server/stack/StackDefinitionModule.java
+++ b/ambari-server/src/main/java/org/apache/ambari/server/stack/StackDefinitionModule.java
@@ -37,10 +37,11 @@ public interface StackDefinitionModule <T, I> {
* @param parent the parent that this module will be merged with
* @param allStacks collection of all stack modules in the tree
* @param commonServices collection of all common service modules in the tree
+ * @param extensions collection of all extension modules in the tree
*
* @throws AmbariException if resolution fails
*/
- public void resolve(T parent, Map<String, StackModule> allStacks, Map<String, ServiceModule> commonServices) throws AmbariException;
+ public void resolve(T parent, Map<String, StackModule> allStacks, Map<String, ServiceModule> commonServices, Map<String, ExtensionModule> extensions) throws AmbariException;
/**
* Obtain the associated module information.
http://git-wip-us.apache.org/repos/asf/ambari/blob/300a7e21/ambari-server/src/main/java/org/apache/ambari/server/stack/StackDirectory.java
----------------------------------------------------------------------
diff --git a/ambari-server/src/main/java/org/apache/ambari/server/stack/StackDirectory.java b/ambari-server/src/main/java/org/apache/ambari/server/stack/StackDirectory.java
index ee9e383..bfba021 100644
--- a/ambari-server/src/main/java/org/apache/ambari/server/stack/StackDirectory.java
+++ b/ambari-server/src/main/java/org/apache/ambari/server/stack/StackDirectory.java
@@ -272,7 +272,6 @@ public class StackDirectory extends StackDefinitionDirectory {
*
* @return object representation of the stack role_command_order.json file
*/
-
public StackRoleCommandOrder getRoleCommandOrder() {
return roleCommandOrder;
}
@@ -509,7 +508,6 @@ public class StackDirectory extends StackDefinitionDirectory {
result = new HashMap<String, Object>();
}
roleCommandOrder = new StackRoleCommandOrder(result);
- parseRoleCommandOrdersForServices();
if (LOG.isDebugEnabled()) {
LOG.debug("Role Command Order for " + rcoFilePath);
roleCommandOrder.printRoleCommandOrder(LOG);
@@ -518,34 +516,4 @@ public class StackDirectory extends StackDefinitionDirectory {
LOG.error(String.format("Can not read role command order info %s", rcoFilePath), e);
}
}
-
- private void parseRoleCommandOrdersForServices() {
- if (rcoFilePath != null) {
- File stack = new File(rcoFilePath).getParentFile();
- File servicesDir = new File(stack, "services");
- File[] services = servicesDir.listFiles();
- for (File service : services) {
- if (service.isDirectory()) {
- File rcoFile = new File(service, ROLE_COMMAND_ORDER_FILE);
- if (rcoFile.exists())
- parseRoleCommandOrdersForService(rcoFile);
- }
- }
- }
- }
-
- private void parseRoleCommandOrdersForService(File rcoFile) {
- HashMap<String, Object> result = null;
- ObjectMapper mapper = new ObjectMapper();
- TypeReference<Map<String, Object>> rcoElementTypeReference = new TypeReference<Map<String, Object>>() {};
- try {
- result = mapper.readValue(rcoFile, rcoElementTypeReference);
- LOG.info("Role command order info was loaded from file: {}", rcoFile.getAbsolutePath());
- StackRoleCommandOrder serviceRoleCommandOrder = new StackRoleCommandOrder(result);
- roleCommandOrder.merge(serviceRoleCommandOrder, true);
- } catch (IOException e) {
- LOG.error(String.format("Can not read role command order info %s", rcoFile), e);
- }
- }
-
}
http://git-wip-us.apache.org/repos/asf/ambari/blob/300a7e21/ambari-server/src/main/java/org/apache/ambari/server/stack/StackManager.java
----------------------------------------------------------------------
diff --git a/ambari-server/src/main/java/org/apache/ambari/server/stack/StackManager.java b/ambari-server/src/main/java/org/apache/ambari/server/stack/StackManager.java
index c224b56..6c0d5e4 100644
--- a/ambari-server/src/main/java/org/apache/ambari/server/stack/StackManager.java
+++ b/ambari-server/src/main/java/org/apache/ambari/server/stack/StackManager.java
@@ -22,6 +22,7 @@ import java.io.File;
import java.util.Collection;
import java.util.HashMap;
import java.util.HashSet;
+import java.util.List;
import java.util.Map;
import javax.annotation.Nullable;
@@ -35,9 +36,14 @@ import org.apache.ambari.server.AmbariException;
import org.apache.ambari.server.api.services.AmbariMetaInfo;
import org.apache.ambari.server.configuration.Configuration;
import org.apache.ambari.server.metadata.ActionMetadata;
+import org.apache.ambari.server.orm.dao.ExtensionDAO;
+import org.apache.ambari.server.orm.dao.ExtensionLinkDAO;
import org.apache.ambari.server.orm.dao.MetainfoDAO;
import org.apache.ambari.server.orm.dao.StackDAO;
+import org.apache.ambari.server.orm.entities.ExtensionEntity;
+import org.apache.ambari.server.orm.entities.ExtensionLinkEntity;
import org.apache.ambari.server.orm.entities.StackEntity;
+import org.apache.ambari.server.state.ExtensionInfo;
import org.apache.ambari.server.state.ServiceInfo;
import org.apache.ambari.server.state.StackInfo;
import org.apache.ambari.server.state.stack.OsFamily;
@@ -72,10 +78,19 @@ public class StackManager {
public static final String COMMON_SERVICES = "common-services";
/**
+ * Prefix used for extension services parent path string
+ */
+ public static final String EXTENSIONS = "extensions";
+
+ public static final String METAINFO_FILE_NAME = "metainfo.xml";
+
+ /**
* Provides access to non-stack server functionality
*/
private StackContext stackContext;
+ private File stackRoot;
+
/**
* Logger
*/
@@ -87,12 +102,19 @@ public class StackManager {
private Map<String, StackInfo> stackMap = new HashMap<String, StackInfo>();
/**
+ * Map of extension id to extension info
+ */
+ private Map<String, ExtensionInfo> extensionMap = new HashMap<String, ExtensionInfo>();
+
+ /**
* Constructor. Initialize stack manager.
*
* @param stackRoot
* stack root directory
* @param commonServicesRoot
* common services root directory
+ * @param extensionRoot
+ * extensions root directory
* @param osFamily
* the OS family read from resources
* @param metaInfoDAO
@@ -101,6 +123,10 @@ public class StackManager {
* action meta data automatically injected
* @param stackDao
* stack DAO automatically injected
+ * @param extensionDao
+ * extension DAO automatically injected
+ * @param linkDao
+ * extension link DAO automatically injected
*
* @throws AmbariException
* if an exception occurs while processing the stacks
@@ -108,9 +134,10 @@ public class StackManager {
@Inject
public StackManager(@Assisted("stackRoot") File stackRoot,
@Assisted("commonServicesRoot") @Nullable File commonServicesRoot,
+ @Assisted("extensionRoot") @Nullable File extensionRoot,
@Assisted OsFamily osFamily, @Assisted boolean validate,
- MetainfoDAO metaInfoDAO,
- ActionMetadata actionMetadata, StackDAO stackDao)
+ MetainfoDAO metaInfoDAO, ActionMetadata actionMetadata, StackDAO stackDao,
+ ExtensionDAO extensionDao, ExtensionLinkDAO linkDao)
throws AmbariException {
LOG.info("Initializing the stack manager...");
@@ -118,22 +145,50 @@ public class StackManager {
if (validate) {
validateStackDirectory(stackRoot);
validateCommonServicesDirectory(commonServicesRoot);
+ validateExtensionDirectory(extensionRoot);
}
stackMap = new HashMap<String, StackInfo>();
stackContext = new StackContext(metaInfoDAO, actionMetadata, osFamily);
+ extensionMap = new HashMap<String, ExtensionInfo>();
Map<String, ServiceModule> commonServiceModules = parseCommonServicesDirectory(commonServicesRoot);
Map<String, StackModule> stackModules = parseStackDirectory(stackRoot);
+ LOG.info("About to parse extension directories");
+ Map<String, ExtensionModule> extensionModules = null;
+ extensionModules = parseExtensionDirectory(extensionRoot);
+
+ //Read the extension links from the DB
+ for (StackModule module : stackModules.values()) {
+ StackInfo stack = module.getModuleInfo();
+ List<ExtensionLinkEntity> entities = linkDao.findByStack(stack.getName(), stack.getVersion());
+ for (ExtensionLinkEntity entity : entities) {
+ String name = entity.getExtension().getExtensionName();
+ String version = entity.getExtension().getExtensionVersion();
+ String key = name + StackManager.PATH_DELIMITER + version;
+ ExtensionModule extensionModule = extensionModules.get(key);
+ if (extensionModule != null) {
+ LOG.info("Adding extension to stack/version: " + stack.getName() + "/" + stack.getVersion() +
+ " extension/version: " + name + "/" + version);
+ //Add the extension to the stack
+ module.getExtensionModules().put(key, extensionModule);
+ }
+ }
+ }
- fullyResolveCommonServices(stackModules, commonServiceModules);
- fullyResolveStacks(stackModules, commonServiceModules);
+ fullyResolveCommonServices(stackModules, commonServiceModules, extensionModules);
+ fullyResolveExtensions(stackModules, commonServiceModules, extensionModules);
+ fullyResolveStacks(stackModules, commonServiceModules, extensionModules);
+ populateDB(stackDao, extensionDao);
+ }
+
+ private void populateDB(StackDAO stackDao, ExtensionDAO extensionDao) throws AmbariException {
// for every stack read in, ensure that we have a database entry for it;
// don't put try/catch logic around this since a failure here will
// cause other things to break down the road
Collection<StackInfo> stacks = getStacks();
- for( StackInfo stack : stacks ){
+ for(StackInfo stack : stacks){
String stackName = stack.getName();
String stackVersion = stack.getVersion();
@@ -147,6 +202,25 @@ public class StackManager {
stackDao.create(stackEntity);
}
}
+
+ // for every extension read in, ensure that we have a database entry for it;
+ // don't put try/catch logic around this since a failure here will
+ // cause other things to break down the road
+ Collection<ExtensionInfo> extensions = getExtensions();
+ for(ExtensionInfo extension : extensions){
+ String extensionName = extension.getName();
+ String extensionVersion = extension.getVersion();
+
+ if (extensionDao.find(extensionName, extensionVersion) == null) {
+ LOG.info("Adding extension {}-{} to the database", extensionName, extensionVersion);
+
+ ExtensionEntity extensionEntity = new ExtensionEntity();
+ extensionEntity.setExtensionName(extensionName);
+ extensionEntity.setExtensionVersion(extensionVersion);
+
+ extensionDao.create(extensionEntity);
+ }
+ }
}
/**
@@ -188,6 +262,44 @@ public class StackManager {
}
/**
+ * Obtain the extension info specified by name and version.
+ *
+ * @param name name of the extension
+ * @param version version of the extension
+ * @return The extension corresponding to the specified name and version.
+ * If no matching stack exists, null is returned.
+ */
+ public ExtensionInfo getExtension(String name, String version) {
+ return extensionMap.get(name + StackManager.PATH_DELIMITER + version);
+ }
+
+ /**
+ * Obtain all extensions for the given name.
+ *
+ * @param name extension name
+ * @return A collection of all extensions with the given name.
+ * If no extensions match the specified name, an empty collection is returned.
+ */
+ public Collection<ExtensionInfo> getExtensions(String name) {
+ Collection<ExtensionInfo> extensions = new HashSet<ExtensionInfo>();
+ for (ExtensionInfo extension: extensionMap.values()) {
+ if (extension.getName().equals(name)) {
+ extensions.add(extension);
+ }
+ }
+ return extensions;
+ }
+
+ /**
+ * Obtain all extensions.
+ *
+ * @return collection of all extensions
+ */
+ public Collection<ExtensionInfo> getExtensions() {
+ return extensionMap.values();
+ }
+
+ /**
* Determine if all tasks which update stack repo urls have completed.
*
* @return true if all of the repo update tasks have completed; false otherwise
@@ -204,12 +316,12 @@ public class StackManager {
* @throws AmbariException if unable to resolve all stacks
*/
private void fullyResolveStacks(
- Map<String, StackModule> stackModules, Map<String, ServiceModule> commonServiceModules)
+ Map<String, StackModule> stackModules, Map<String, ServiceModule> commonServiceModules, Map<String, ExtensionModule> extensions)
throws AmbariException {
// Resolve all stacks without finalizing the stacks.
for (StackModule stack : stackModules.values()) {
if (stack.getModuleState() == ModuleState.INIT) {
- stack.resolve(null, stackModules, commonServiceModules);
+ stack.resolve(null, stackModules, commonServiceModules, extensions);
}
}
// Finalize the common services and stacks to remove sub-modules marked for deletion.
@@ -219,6 +331,9 @@ public class StackManager {
for(ServiceModule commonService : commonServiceModules.values()) {
commonService.finalizeModule();
}
+ for (ExtensionModule extension : extensions.values()) {
+ extension.finalizeModule();
+ }
for (StackModule stack : stackModules.values()) {
stack.finalizeModule();
}
@@ -234,11 +349,29 @@ public class StackManager {
* @throws AmbariException if unable to resolve all common services
*/
private void fullyResolveCommonServices(
- Map<String, StackModule> stackModules, Map<String, ServiceModule> commonServiceModules)
+ Map<String, StackModule> stackModules, Map<String, ServiceModule> commonServiceModules, Map<String, ExtensionModule> extensions)
throws AmbariException {
for(ServiceModule commonService : commonServiceModules.values()) {
if (commonService.getModuleState() == ModuleState.INIT) {
- commonService.resolveCommonService(stackModules, commonServiceModules);
+ commonService.resolveCommonService(stackModules, commonServiceModules, extensions);
+ }
+ }
+ }
+
+ /**
+ * Fully resolve extensions.
+ *
+ * @param extensionModules map of extension id which contains name and version to extension module.
+ * @param stackModules map of stack id which contains name and version to stack module.
+ * @param commonServiceModules map of common service id which contains name and version to common service module.
+ * @throws AmbariException if unable to resolve all extensions
+ */
+ private void fullyResolveExtensions(Map<String, StackModule> stackModules, Map<String, ServiceModule> commonServiceModules,
+ Map<String, ExtensionModule> extensionModules)
+ throws AmbariException {
+ for(ExtensionModule extensionModule : extensionModules.values()) {
+ if (extensionModule.getModuleState() == ModuleState.INIT) {
+ extensionModule.resolve(null, stackModules, commonServiceModules, extensionModules);
}
}
}
@@ -321,6 +454,34 @@ public class StackManager {
}
}
+
+
+ /**
+ * Validate that the specified extension root is a valid directory.
+ *
+ * @param extensionRoot the extension root directory to validate
+ * @throws AmbariException if the specified extension root directory is invalid
+ */
+ private void validateExtensionDirectory(File extensionRoot) throws AmbariException {
+ LOG.info("Validating extension directory {} ...", extensionRoot);
+
+ if (extensionRoot == null)
+ return;
+
+ String extensionRootAbsPath = extensionRoot.getAbsolutePath();
+ if (LOG.isDebugEnabled()) {
+ LOG.debug("Loading extension information"
+ + ", extensionRoot = " + extensionRootAbsPath);
+ }
+
+ //For backwards compatibility extension directory may not exist
+ if (extensionRoot.exists() && !extensionRoot.isDirectory()) {
+ throw new AmbariException("" + Configuration.METADATA_DIR_PATH
+ + " should be a directory"
+ + ", extensionRoot = " + extensionRootAbsPath);
+ }
+ }
+
/**
* Parse the specified common services root directory
*
@@ -399,4 +560,48 @@ public class StackManager {
}
return stackModules;
}
+
+ public void linkStackToExtension(StackInfo stack, ExtensionInfo extension) throws AmbariException {
+ }
+
+ public void unlinkStackAndExtension(StackInfo stack, ExtensionInfo extension) throws AmbariException {
+ }
+
+ /**
+ * Parse the specified extension root directory
+ *
+ * @param extensionRoot the extension root directory to parse
+ * @return map of extension id which contains name and version to extension module.
+ * @throws AmbariException if unable to parse all extensions
+ */
+ private Map<String, ExtensionModule> parseExtensionDirectory(File extensionRoot) throws AmbariException {
+ Map<String, ExtensionModule> extensionModules = new HashMap<String, ExtensionModule>();
+ if (extensionRoot == null || !extensionRoot.exists())
+ return extensionModules;
+
+ File[] extensionFiles = extensionRoot.listFiles(AmbariMetaInfo.FILENAME_FILTER);
+ for (File extensionNameFolder : extensionFiles) {
+ if (extensionNameFolder.isFile()) {
+ continue;
+ }
+ for (File extensionVersionFolder : extensionNameFolder.listFiles(AmbariMetaInfo.FILENAME_FILTER)) {
+ if (extensionVersionFolder.isFile()) {
+ continue;
+ }
+ String extensionName = extensionNameFolder.getName();
+ String extensionVersion = extensionVersionFolder.getName();
+
+ ExtensionModule extensionModule = new ExtensionModule(new ExtensionDirectory(extensionVersionFolder.getPath()), stackContext);
+ String extensionKey = extensionName + StackManager.PATH_DELIMITER + extensionVersion;
+ extensionModules.put(extensionKey, extensionModule);
+ extensionMap.put(extensionKey, extensionModule.getModuleInfo());
+ }
+ }
+
+ if (stackMap.isEmpty()) {
+ throw new AmbariException("Unable to find extension definitions under " +
+ "extensionRoot = " + extensionRoot.getAbsolutePath());
+ }
+ return extensionModules;
+ }
}
http://git-wip-us.apache.org/repos/asf/ambari/blob/300a7e21/ambari-server/src/main/java/org/apache/ambari/server/stack/StackManagerFactory.java
----------------------------------------------------------------------
diff --git a/ambari-server/src/main/java/org/apache/ambari/server/stack/StackManagerFactory.java b/ambari-server/src/main/java/org/apache/ambari/server/stack/StackManagerFactory.java
index 105b715..84e7e0b 100644
--- a/ambari-server/src/main/java/org/apache/ambari/server/stack/StackManagerFactory.java
+++ b/ambari-server/src/main/java/org/apache/ambari/server/stack/StackManagerFactory.java
@@ -38,11 +38,14 @@ public interface StackManagerFactory {
* @param commonServicesRoot
* the root of the common services from which other stack services
* are extended (not {@code null}).
+ * @param extensionRoot
+ * the root of the extensions (not {@code null}).
* @param osFamily
* the list of all parsed OS families (not {@code null}).
* @return a stack manager instance which contains all parsed stacks.
*/
StackManager create(@Assisted("stackRoot") File stackRoot,
@Nullable @Assisted("commonServicesRoot") File commonServicesRoot,
+ @Assisted("extensionRoot") @Nullable File extensionRoot,
OsFamily osFamily, boolean validate);
}
http://git-wip-us.apache.org/repos/asf/ambari/blob/300a7e21/ambari-server/src/main/java/org/apache/ambari/server/stack/StackModule.java
----------------------------------------------------------------------
diff --git a/ambari-server/src/main/java/org/apache/ambari/server/stack/StackModule.java b/ambari-server/src/main/java/org/apache/ambari/server/stack/StackModule.java
index d819a52..0606f2a 100644
--- a/ambari-server/src/main/java/org/apache/ambari/server/stack/StackModule.java
+++ b/ambari-server/src/main/java/org/apache/ambari/server/stack/StackModule.java
@@ -36,6 +36,7 @@ import org.apache.ambari.server.AmbariException;
import org.apache.ambari.server.api.services.AmbariMetaInfo;
import org.apache.ambari.server.stack.StackDefinitionDirectory;
import org.apache.ambari.server.state.ConfigHelper;
+import org.apache.ambari.server.state.ExtensionInfo;
import org.apache.ambari.server.state.PropertyDependencyInfo;
import org.apache.ambari.server.state.PropertyInfo;
import org.apache.ambari.server.state.RepositoryInfo;
@@ -107,6 +108,11 @@ public class StackModule extends BaseModule<StackModule, StackInfo> implements V
private Map<String, ServiceModule> serviceModules = new HashMap<String, ServiceModule>();
/**
+ * Map of linked extension modules keyed by extension name + version
+ */
+ private Map<String, ExtensionModule> extensionModules = new HashMap<String, ExtensionModule>();
+
+ /**
* Corresponding StackInfo instance
*/
private StackInfo stackInfo;
@@ -148,6 +154,14 @@ public class StackModule extends BaseModule<StackModule, StackInfo> implements V
populateStackInfo();
}
+ public Map<String, ServiceModule> getServiceModules() {
+ return serviceModules;
+ }
+
+ public Map<String, ExtensionModule> getExtensionModules() {
+ return extensionModules;
+ }
+
/**
* Fully resolve the stack. See stack resolution description in the class documentation.
* If the stack has a parent, this stack will be merged against its fully resolved parent
@@ -160,20 +174,34 @@ public class StackModule extends BaseModule<StackModule, StackInfo> implements V
* have containing modules
* @param allStacks all stacks modules contained in the stack definition
* @param commonServices all common services specified in the stack definition
+ * @param extensions all extension modules contained in the stack definition
*
* @throws AmbariException if an exception occurs during stack resolution
*/
@Override
public void resolve(
- StackModule parentModule, Map<String, StackModule> allStacks, Map<String, ServiceModule> commonServices)
+ StackModule parentModule, Map<String, StackModule> allStacks, Map<String, ServiceModule> commonServices, Map<String, ExtensionModule> extensions)
throws AmbariException {
moduleState = ModuleState.VISITED;
+ LOG.info("Resolve: " + stackInfo.getName() + ":" + stackInfo.getVersion());
String parentVersion = stackInfo.getParentStackVersion();
- mergeServicesWithExplicitParent(allStacks, commonServices);
+ mergeServicesWithExplicitParent(allStacks, commonServices, extensions);
+ addExtensionServices();
+
// merge with parent version of same stack definition
if (parentVersion != null) {
- mergeStackWithParent(parentVersion, allStacks, commonServices);
+ mergeStackWithParent(parentVersion, allStacks, commonServices, extensions);
}
+ for (ExtensionInfo extension : stackInfo.getExtensions()) {
+ String extensionKey = extension.getName() + StackManager.PATH_DELIMITER + extension.getVersion();
+ ExtensionModule extensionModule = extensions.get(extensionKey);
+ if (extensionModule == null) {
+ throw new AmbariException("Extension '" + stackInfo.getName() + ":" + stackInfo.getVersion() +
+ "' specifies an extension " + extensionKey + " that doesn't exist");
+ }
+ mergeStackWithExtension(extensionModule, allStacks, commonServices, extensions);
+ }
+
processUpgradePacks();
processRepositories();
processPropertyDependencies();
@@ -199,6 +227,12 @@ public class StackModule extends BaseModule<StackModule, StackInfo> implements V
public void finalizeModule() {
finalizeChildModules(serviceModules.values());
finalizeChildModules(configurationModules.values());
+
+ // This needs to be merged during the finalize to avoid the RCO from services being inherited by the children stacks
+ // The RCOs from a service should only be inherited through the service.
+ for (ServiceModule module : serviceModules.values()) {
+ mergeRoleCommandOrder(module);
+ }
}
/**
@@ -220,7 +254,7 @@ public class StackModule extends BaseModule<StackModule, StackInfo> implements V
* @throws AmbariException if an exception occurs merging with the parent
*/
private void mergeStackWithParent(
- String parentVersion, Map<String, StackModule> allStacks, Map<String, ServiceModule> commonServices)
+ String parentVersion, Map<String, StackModule> allStacks, Map<String, ServiceModule> commonServices, Map<String, ExtensionModule> extensions)
throws AmbariException {
String parentStackKey = stackInfo.getName() + StackManager.PATH_DELIMITER + parentVersion;
@@ -231,8 +265,8 @@ public class StackModule extends BaseModule<StackModule, StackInfo> implements V
"' specifies a parent that doesn't exist");
}
- resolveStack(parentStack, allStacks, commonServices);
- mergeConfigurations(parentStack, allStacks, commonServices);
+ resolveStack(parentStack, allStacks, commonServices, extensions);
+ mergeConfigurations(parentStack, allStacks, commonServices, extensions);
mergeRoleCommandOrder(parentStack);
if (stackInfo.getStackHooksFolder() == null) {
@@ -248,7 +282,22 @@ public class StackModule extends BaseModule<StackModule, StackInfo> implements V
stackInfo.setWidgetsDescriptorFileLocation(parentStack.getModuleInfo().getWidgetsDescriptorFileLocation());
}
- mergeServicesWithParent(parentStack, allStacks, commonServices);
+ mergeServicesWithParent(parentStack, allStacks, commonServices, extensions);
+ }
+
+ /**
+ * Merge the stack with one of its linked extensions.
+ *
+ * @param allStacks all stacks in stack definition
+ * @param commonServices all common services specified in the stack definition
+ * @param parentVersion version of the stacks parent
+ *
+ * @throws AmbariException if an exception occurs merging with the parent
+ */
+ private void mergeStackWithExtension(
+ ExtensionModule extension, Map<String, StackModule> allStacks, Map<String, ServiceModule> commonServices, Map<String, ExtensionModule> extensions)
+ throws AmbariException {
+
}
/**
@@ -261,11 +310,11 @@ public class StackModule extends BaseModule<StackModule, StackInfo> implements V
* @throws AmbariException if an exception occurs merging the child services with the parent stack
*/
private void mergeServicesWithParent(
- StackModule parentStack, Map<String, StackModule> allStacks, Map<String, ServiceModule> commonServices)
+ StackModule parentStack, Map<String, StackModule> allStacks, Map<String, ServiceModule> commonServices, Map<String, ExtensionModule> extensions)
throws AmbariException {
stackInfo.getServices().clear();
Collection<ServiceModule> mergedModules = mergeChildModules(
- allStacks, commonServices, serviceModules, parentStack.serviceModules);
+ allStacks, commonServices, extensions, serviceModules, parentStack.serviceModules);
for (ServiceModule module : mergedModules) {
serviceModules.put(module.getId(), module);
stackInfo.getServices().add(module.getModuleInfo());
@@ -280,12 +329,12 @@ public class StackModule extends BaseModule<StackModule, StackInfo> implements V
* @throws AmbariException if an exception occurs while merging child services with their explicit parents
*/
private void mergeServicesWithExplicitParent(
- Map<String, StackModule> allStacks, Map<String, ServiceModule> commonServices) throws AmbariException {
+ Map<String, StackModule> allStacks, Map<String, ServiceModule> commonServices, Map<String, ExtensionModule> extensions) throws AmbariException {
for (ServiceModule service : serviceModules.values()) {
ServiceInfo serviceInfo = service.getModuleInfo();
String parent = serviceInfo.getParent();
if (parent != null) {
- mergeServiceWithExplicitParent(service, parent, allStacks, commonServices);
+ mergeServiceWithExplicitParent(service, parent, allStacks, commonServices, extensions);
}
}
}
@@ -301,12 +350,16 @@ public class StackModule extends BaseModule<StackModule, StackInfo> implements V
*/
private void mergeServiceWithExplicitParent(
ServiceModule service, String parent, Map<String, StackModule> allStacks,
- Map<String, ServiceModule> commonServices)
+ Map<String, ServiceModule> commonServices, Map<String, ExtensionModule> extensions)
throws AmbariException {
+
+ LOG.info("mergeServiceWithExplicitParent" + parent);
if(isCommonServiceParent(parent)) {
- mergeServiceWithCommonServiceParent(service, parent, allStacks,commonServices);
+ mergeServiceWithCommonServiceParent(service, parent, allStacks, commonServices, extensions);
+ } else if(isExtensionServiceParent(parent)) {
+ mergeServiceWithExtensionServiceParent(service, parent, allStacks, commonServices, extensions);
} else {
- mergeServiceWithStackServiceParent(service, parent, allStacks, commonServices);
+ mergeServiceWithStackServiceParent(service, parent, allStacks, commonServices, extensions);
}
}
@@ -322,6 +375,25 @@ public class StackModule extends BaseModule<StackModule, StackInfo> implements V
}
/**
+ * Check if parent is extension service
+ * @param parent Parent string
+ * @return true: if parent is extension service, false otherwise
+ */
+ private boolean isExtensionServiceParent(String parent) {
+ return parent != null
+ && !parent.isEmpty()
+ && parent.split(StackManager.PATH_DELIMITER)[0].equalsIgnoreCase(StackManager.EXTENSIONS);
+ }
+
+ private void addExtensionServices() throws AmbariException {
+ for (ExtensionModule extension : extensionModules.values()) {
+ stackInfo.getExtensions().add(extension.getModuleInfo());
+ Collection<ServiceModule> services = extension.getServiceModules().values();
+ addServices(services);
+ }
+ }
+
+ /**
* Merge a service with its explicitly specified common service as parent.
* Parent: common-services/<serviceName>/<serviceVersion>
* Common Services Lookup Key: <serviceName>/<serviceVersion>
@@ -337,7 +409,7 @@ public class StackModule extends BaseModule<StackModule, StackInfo> implements V
*/
private void mergeServiceWithCommonServiceParent(
ServiceModule service, String parent, Map<String, StackModule> allStacks,
- Map<String, ServiceModule> commonServices)
+ Map<String, ServiceModule> commonServices, Map<String, ExtensionModule> extensions)
throws AmbariException {
ServiceInfo serviceInfo = service.getModuleInfo();
String[] parentToks = parent.split(StackManager.PATH_DELIMITER);
@@ -357,7 +429,7 @@ public class StackModule extends BaseModule<StackModule, StackInfo> implements V
stackInfo.addError(error);
} else {
if (baseService.isValid()) {
- service.resolveExplicit(baseService, allStacks, commonServices);
+ service.resolveExplicit(baseService, allStacks, commonServices, extensions);
} else {
setValid(false);
stackInfo.setValid(false);
@@ -368,6 +440,50 @@ public class StackModule extends BaseModule<StackModule, StackInfo> implements V
}
/**
+ * Merge a service with its explicitly specified extension service as parent.
+ * Parent: extensions/<extensionName>/<extensionVersion>/<serviceName>
+ * Example:
+ * Parent: extensions/EXT_TEST/1.0/CUSTOM_SERVICE
+ *
+ * @param service the service to merge
+ * @param parent the explicitly specified extension as parent
+ * @param allStacks all stacks specified in the stack definition
+ * @param commonServices all common services
+ * @param extensions all extensions
+ * @throws AmbariException
+ */
+ private void mergeServiceWithExtensionServiceParent(
+ ServiceModule service, String parent, Map<String, StackModule> allStacks,
+ Map<String, ServiceModule> commonServices, Map<String, ExtensionModule> extensions)
+ throws AmbariException {
+ ServiceInfo serviceInfo = service.getModuleInfo();
+ String[] parentToks = parent.split(StackManager.PATH_DELIMITER);
+ if(parentToks.length != 4 || !parentToks[0].equalsIgnoreCase(StackManager.EXTENSIONS)) {
+ throw new AmbariException("The service '" + serviceInfo.getName() + "' in stack '" + stackInfo.getName() + ":"
+ + stackInfo.getVersion() + "' extends an invalid parent: '" + parent + "'");
+ }
+
+ String extensionKey = parentToks[1] + StackManager.PATH_DELIMITER + parentToks[2];
+ ExtensionModule extension = extensions.get(extensionKey);
+
+ if (extension == null || !extension.isValid()) {
+ setValid(false);
+ addError("The service '" + serviceInfo.getName() + "' in stack '" + stackInfo.getName() + ":"
+ + stackInfo.getVersion() + "' extends a non-existent service: '" + parent + "'");
+ } else {
+ resolveExtension(extension, allStacks, commonServices, extensions);
+ ServiceModule parentService = extension.getServiceModules().get(parentToks[3]);
+ if (parentService == null || !parentService.isValid()) {
+ setValid(false);
+ addError("The service '" + serviceInfo.getName() + "' in stack '" + stackInfo.getName() + ":"
+ + stackInfo.getVersion() + "' extends a non-existent service: '" + parent + "'");
+ }
+ else
+ service.resolve(parentService, allStacks, commonServices, extensions);
+ }
+ }
+
+ /**
* Merge a service with its explicitly specified stack service as parent.
* Parent: <stackName>/<stackVersion>/<serviceName>
* Stack Lookup Key: <stackName>/<stackVersion>
@@ -378,16 +494,17 @@ public class StackModule extends BaseModule<StackModule, StackInfo> implements V
* @param service the service to merge
* @param parent the explicitly specified stack service as parent
* @param allStacks all stacks specified in the stack definition
- * @param commonServices all common services specified in the stack definition
+ * @param commonServices all common services
+ * @param extensions all extensions
* @throws AmbariException
*/
private void mergeServiceWithStackServiceParent(
ServiceModule service, String parent, Map<String, StackModule> allStacks,
- Map<String, ServiceModule> commonServices)
+ Map<String, ServiceModule> commonServices, Map<String, ExtensionModule> extensions)
throws AmbariException {
ServiceInfo serviceInfo = service.getModuleInfo();
String[] parentToks = parent.split(StackManager.PATH_DELIMITER);
- if(parentToks.length != 3 || parentToks[0].equalsIgnoreCase(StackManager.COMMON_SERVICES)) {
+ if(parentToks.length != 3 || parentToks[0].equalsIgnoreCase(StackManager.EXTENSIONS) || parentToks[0].equalsIgnoreCase(StackManager.COMMON_SERVICES)) {
throw new AmbariException("The service '" + serviceInfo.getName() + "' in stack '" + stackInfo.getName() + ":"
+ stackInfo.getVersion() + "' extends an invalid parent: '" + parent + "'");
}
@@ -399,14 +516,14 @@ public class StackModule extends BaseModule<StackModule, StackInfo> implements V
+ stackInfo.getVersion() + "' extends a service in a non-existent stack: '" + baseStackKey + "'");
}
- resolveStack(baseStack, allStacks, commonServices);
+ resolveStack(baseStack, allStacks, commonServices, extensions);
ServiceModule baseService = baseStack.serviceModules.get(parentToks[2]);
if (baseService == null) {
throw new AmbariException("The service '" + serviceInfo.getName() + "' in stack '" + stackInfo.getName() + ":"
+ stackInfo.getVersion() + "' extends a non-existent service: '" + parent + "'");
}
- service.resolveExplicit(baseService, allStacks, commonServices);
+ service.resolveExplicit(baseService, allStacks, commonServices, extensions);
}
/**
@@ -422,8 +539,7 @@ public class StackModule extends BaseModule<StackModule, StackInfo> implements V
+ ", stackName = " + stackInfo.getName()
+ ", stackVersion = " + stackInfo.getVersion());
-
- //odo: give additional thought on handling missing metainfo.xml
+ //todo: give additional thought on handling missing metainfo.xml
StackMetainfoXml smx = stackDirectory.getMetaInfoFile();
if (smx != null) {
if (!smx.isValid()) {
@@ -542,13 +658,13 @@ public class StackModule extends BaseModule<StackModule, StackInfo> implements V
* @param commonServices all common services specified in the stack definition
*/
private void mergeConfigurations(
- StackModule parent, Map<String,StackModule> allStacks, Map<String, ServiceModule> commonServices)
+ StackModule parent, Map<String,StackModule> allStacks, Map<String, ServiceModule> commonServices, Map<String, ExtensionModule> extensions)
throws AmbariException {
stackInfo.getProperties().clear();
stackInfo.setAllConfigAttributes(new HashMap<String, Map<String, Map<String, String>>>());
Collection<ConfigurationModule> mergedModules = mergeChildModules(
- allStacks, commonServices, configurationModules, parent.configurationModules);
+ allStacks, commonServices, extensions, configurationModules, parent.configurationModules);
for (ConfigurationModule module : mergedModules) {
configurationModules.put(module.getId(), module);
stackInfo.getProperties().addAll(module.getModuleInfo().getProperties());
@@ -565,10 +681,10 @@ public class StackModule extends BaseModule<StackModule, StackInfo> implements V
* @throws AmbariException if unable to resolve the stack
*/
private void resolveStack(
- StackModule stackToBeResolved, Map<String, StackModule> allStacks, Map<String, ServiceModule> commonServices)
+ StackModule stackToBeResolved, Map<String, StackModule> allStacks, Map<String, ServiceModule> commonServices, Map<String, ExtensionModule> extensions)
throws AmbariException {
if (stackToBeResolved.getModuleState() == ModuleState.INIT) {
- stackToBeResolved.resolve(null, allStacks, commonServices);
+ stackToBeResolved.resolve(null, allStacks, commonServices, extensions);
} else if (stackToBeResolved.getModuleState() == ModuleState.VISITED) {
//todo: provide more information to user about cycle
throw new AmbariException("Cycle detected while parsing stack definition");
@@ -582,6 +698,30 @@ public class StackModule extends BaseModule<StackModule, StackInfo> implements V
}
/**
+ * Resolve an extension module.
+ *
+ * @param extension extension module to be resolved
+ * @param allStacks all stack modules in stack definition
+ * @param commonServices all common services
+ * @param extensions all extensions
+ * @throws AmbariException if unable to resolve the stack
+ */
+ private void resolveExtension(
+ ExtensionModule extension, Map<String, StackModule> allStacks, Map<String, ServiceModule> commonServices, Map<String, ExtensionModule> extensions)
+ throws AmbariException {
+ if (extension.getModuleState() == ModuleState.INIT) {
+ extension.resolve(null, allStacks, commonServices, extensions);
+ } else if (extension.getModuleState() == ModuleState.VISITED) {
+ //todo: provide more information to user about cycle
+ throw new AmbariException("Cycle detected while parsing extension definition");
+ }
+ if (!extension.isValid() || (extension.getModuleInfo() != null && !extension.getModuleInfo().isValid())) {
+ setValid(false);
+ addError("Stack includes an invalid extension: " + extension.getModuleInfo().getName());
+ }
+ }
+
+ /**
* Add a child service module to the stack.
*
* @param service service module to add
@@ -943,6 +1083,23 @@ public class StackModule extends BaseModule<StackModule, StackInfo> implements V
stackInfo.getRoleCommandOrder().merge(parentStack.stackInfo.getRoleCommandOrder());
}
+ /**
+ * Merge role command order with the service
+ *
+ * @param service service
+ */
+ private void mergeRoleCommandOrder(ServiceModule service) {
+ if (service.getModuleInfo().getRoleCommandOrder() == null)
+ return;
+
+ stackInfo.getRoleCommandOrder().merge(service.getModuleInfo().getRoleCommandOrder(), true);
+ if (LOG.isDebugEnabled()) {
+ LOG.debug("Role Command Order for " + stackInfo.getName() + "-" + stackInfo.getVersion() +
+ " service " + service.getModuleInfo().getName());
+ stackInfo.getRoleCommandOrder().printRoleCommandOrder(LOG);
+ }
+ }
+
@Override
public boolean isValid() {
return valid;
http://git-wip-us.apache.org/repos/asf/ambari/blob/300a7e21/ambari-server/src/main/java/org/apache/ambari/server/stack/StackServiceDirectory.java
----------------------------------------------------------------------
diff --git a/ambari-server/src/main/java/org/apache/ambari/server/stack/StackServiceDirectory.java b/ambari-server/src/main/java/org/apache/ambari/server/stack/StackServiceDirectory.java
index 68c1dd6..7bcd08b 100644
--- a/ambari-server/src/main/java/org/apache/ambari/server/stack/StackServiceDirectory.java
+++ b/ambari-server/src/main/java/org/apache/ambari/server/stack/StackServiceDirectory.java
@@ -67,14 +67,13 @@ public class StackServiceDirectory extends ServiceDirectory {
@Override
/**
- * Parse stack service directory.
+ * Calculate the stack service directories.
* packageDir Format: stacks/<stackName>/<stackVersion>/services/<serviceName>/package
* Example:
* directory: "/var/lib/ambari-server/resources/stacks/HDP/2.0.6/services/HDFS"
* packageDir: "stacks/HDP/2.0.6/services/HDFS/package"
- * @throws AmbariException if unable to parse the service directory
*/
- protected void parsePath() throws AmbariException {
+ protected void calculateDirectories() {
File serviceDir = new File(getAbsolutePath());
File stackVersionDir = serviceDir.getParentFile().getParentFile();
File stackDir = stackVersionDir.getParentFile();
@@ -116,6 +115,5 @@ public class StackServiceDirectory extends ServiceDirectory {
LOG.debug("Service upgrades folder %s for service %s for stack %s does not exist.",
absUpgradesDir, serviceDir.getName(), stackId);
}
- parseMetaInfoFile();
}
}
http://git-wip-us.apache.org/repos/asf/ambari/blob/300a7e21/ambari-server/src/main/java/org/apache/ambari/server/stack/ThemeModule.java
----------------------------------------------------------------------
diff --git a/ambari-server/src/main/java/org/apache/ambari/server/stack/ThemeModule.java b/ambari-server/src/main/java/org/apache/ambari/server/stack/ThemeModule.java
index d8f50c6..30c1f1b 100644
--- a/ambari-server/src/main/java/org/apache/ambari/server/stack/ThemeModule.java
+++ b/ambari-server/src/main/java/org/apache/ambari/server/stack/ThemeModule.java
@@ -82,7 +82,8 @@ public class ThemeModule extends BaseModule<ThemeModule, ThemeInfo> implements V
}
@Override
- public void resolve(ThemeModule parent, Map<String, StackModule> allStacks, Map<String, ServiceModule> commonServices) throws AmbariException {
+ public void resolve(ThemeModule parent, Map<String, StackModule> allStacks,
+ Map<String, ServiceModule> commonServices, Map<String, ExtensionModule> extensions) throws AmbariException {
ThemeInfo parentModuleInfo = parent.getModuleInfo();
if (parent.getModuleInfo() != null && !moduleInfo.isDeleted()) {