http://git-wip-us.apache.org/repos/asf/incubator-slider/blob/31e05853/slider-core/src/main/java/org/apache/slider/common/params/ActionRegistryArgs.java ---------------------------------------------------------------------- diff --git a/slider-core/src/main/java/org/apache/slider/common/params/ActionRegistryArgs.java b/slider-core/src/main/java/org/apache/slider/common/params/ActionRegistryArgs.java index deae4eb..1e4aba5 100644 --- a/slider-core/src/main/java/org/apache/slider/common/params/ActionRegistryArgs.java +++ b/slider-core/src/main/java/org/apache/slider/common/params/ActionRegistryArgs.java @@ -47,8 +47,10 @@ public class ActionRegistryArgs extends AbstractActionArgs { + " (" + Arguments.ARG_LIST + "|" + Arguments.ARG_LISTCONF + "|" + + Arguments.ARG_LISTEXP + "|" + Arguments.ARG_LISTFILES + "|" - + Arguments.ARG_GETCONF + "> " + + Arguments.ARG_GETCONF + "|" + + Arguments.ARG_GETEXP + "> " + Arguments.ARG_NAME + " <name> " + " )" + "[" + Arguments.ARG_VERBOSE + "] " @@ -56,6 +58,8 @@ public class ActionRegistryArgs extends AbstractActionArgs { + "[" + Arguments.ARG_OUTPUT + " <filename> ] " + "[" + Arguments.ARG_SERVICETYPE + " <servicetype> ] " + "[" + Arguments.ARG_FORMAT + " <xml|json|properties>] " + + System.getProperty("line.separator") + + "Arguments.ARG_GETEXP only supports " + Arguments.ARG_FORMAT + " json" ; public ActionRegistryArgs() { } @@ -90,7 +94,14 @@ public class ActionRegistryArgs extends AbstractActionArgs { description = "get configuration") public String getConf; - @Parameter(names = {ARG_LISTFILES}, + @Parameter(names = {ARG_LISTEXP}, + description = "list exports") + public boolean listExports; + @Parameter(names = {ARG_GETEXP}, + description = "get export") + public String getExport; + + @Parameter(names = {ARG_LISTFILES}, description = "list files") public String listFiles; @@ -135,8 +146,8 @@ public class ActionRegistryArgs extends AbstractActionArgs { super.validate(); //verify that at most one of the operations is set - int gets = s(getConf) + s(getFiles); - int lists = s(list) + s(listConf) + s(listFiles); + int gets = s(getConf) + s(getFiles) + s(getExport); + int lists = s(list) + s(listConf) + s(listFiles) + s(listExports); int set = lists + gets; if (set > 1) { throw new UsageException(USAGE);
http://git-wip-us.apache.org/repos/asf/incubator-slider/blob/31e05853/slider-core/src/main/java/org/apache/slider/common/params/Arguments.java ---------------------------------------------------------------------- diff --git a/slider-core/src/main/java/org/apache/slider/common/params/Arguments.java b/slider-core/src/main/java/org/apache/slider/common/params/Arguments.java index ff064c8..2c536d9 100644 --- a/slider-core/src/main/java/org/apache/slider/common/params/Arguments.java +++ b/slider-core/src/main/java/org/apache/slider/common/params/Arguments.java @@ -52,6 +52,7 @@ public interface Arguments { String ARG_FORMAT = "--format"; String ARG_FORCE = "--force"; String ARG_GETCONF = "--getconf"; + String ARG_GETEXP = "--getexp"; String ARG_GETFILES = "--getfiles"; String ARG_HELP = "--help"; String ARG_ID = "--id"; @@ -60,6 +61,7 @@ public interface Arguments { String ARG_LEVEL = "--level"; String ARG_LIST = "--list"; String ARG_LISTCONF = "--listconf"; + String ARG_LISTEXP = "--listexp"; String ARG_LISTFILES = "--listfiles"; String ARG_LIVE = "--live"; String ARG_MANAGER = "--manager"; http://git-wip-us.apache.org/repos/asf/incubator-slider/blob/31e05853/slider-core/src/main/java/org/apache/slider/core/registry/docstore/ExportEntry.java ---------------------------------------------------------------------- diff --git a/slider-core/src/main/java/org/apache/slider/core/registry/docstore/ExportEntry.java b/slider-core/src/main/java/org/apache/slider/core/registry/docstore/ExportEntry.java new file mode 100644 index 0000000..4bcf6c1 --- /dev/null +++ b/slider-core/src/main/java/org/apache/slider/core/registry/docstore/ExportEntry.java @@ -0,0 +1,120 @@ +/* + * 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.slider.core.registry.docstore; + +import org.codehaus.jackson.annotate.JsonIgnoreProperties; +import org.codehaus.jackson.map.annotate.JsonSerialize; + +/** + * JSON-serializable description of a published key-val configuration. + * + * The values themselves are not serialized in the external view; they have to be served up by the far end + */ +@JsonIgnoreProperties(ignoreUnknown = true) +@JsonSerialize(include = JsonSerialize.Inclusion.NON_NULL) +public class ExportEntry { + + /** + * The value of the export + */ + private String value; + /** + * The container id of the container that is responsible for the export + */ + private String containerId; + /** + * Tag associated with the container - its usually an identifier different than container id + * that allows a soft serial id to all containers of a component - e.g. 1, 2, 3, ... + */ + private String tag; + /** + * An export can be at the level of a component or an application + */ + private String level; + /** + * The time when the export was updated + */ + private String updatedTime; + /** + * The time when the export expires + */ + private String validUntil; + + public ExportEntry() { + } + + public String getValue() { + return value; + } + + public void setValue(String value) { + this.value = value; + } + + public String getContainerId() { + return containerId; + } + + public void setContainerId(String containerId) { + this.containerId = containerId; + } + + public String getTag() { + return tag; + } + + public void setTag(String tag) { + this.tag = tag; + } + + public String getLevel() { + return level; + } + + public void setLevel(String level) { + this.level = level; + } + public String getUpdatedTime() { + return updatedTime; + } + + public void setUpdatedTime(String updatedTime) { + this.updatedTime = updatedTime; + } + + public String getValidUntil() { + return validUntil; + } + + public void setValidUntil(String validUntil) { + this.validUntil = validUntil; + } + + @Override + public String toString() { + return new StringBuilder("ExportEntry{"). + append("value='").append(value).append("',"). + append("containerId='").append(containerId).append("',"). + append("tag='").append(tag).append("',"). + append("level='").append(level).append("'"). + append("updatedTime='").append(updatedTime).append("'"). + append("validUntil='").append(validUntil).append("'"). + append(" }").toString(); + } +} http://git-wip-us.apache.org/repos/asf/incubator-slider/blob/31e05853/slider-core/src/main/java/org/apache/slider/core/registry/docstore/PublishedExports.java ---------------------------------------------------------------------- diff --git a/slider-core/src/main/java/org/apache/slider/core/registry/docstore/PublishedExports.java b/slider-core/src/main/java/org/apache/slider/core/registry/docstore/PublishedExports.java new file mode 100644 index 0000000..d6d285c --- /dev/null +++ b/slider-core/src/main/java/org/apache/slider/core/registry/docstore/PublishedExports.java @@ -0,0 +1,141 @@ +/* + * 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.slider.core.registry.docstore; + +import org.apache.commons.collections.IteratorUtils; +import org.codehaus.jackson.annotate.JsonIgnoreProperties; +import org.codehaus.jackson.map.ObjectMapper; +import org.codehaus.jackson.map.annotate.JsonSerialize; + +import java.io.IOException; +import java.util.ArrayList; +import java.util.Collection; +import java.util.Date; +import java.util.HashMap; +import java.util.Iterator; +import java.util.List; +import java.util.Map; + +/** + * JSON-serializable description of a published key-val configuration. + * + * The values themselves are not serialized in the external view; they have to be served up by the far end + */ +@JsonIgnoreProperties(ignoreUnknown = true) +@JsonSerialize(include = JsonSerialize.Inclusion.NON_NULL) +public class PublishedExports { + + public String description; + public long updated; + public String updatedTime; + public Map<String, List<ExportEntry>> entries = new HashMap<String, List<ExportEntry>>(); + + public PublishedExports() { + } + + /** + * build an empty published configuration + * + * @param description configuration description + */ + public PublishedExports(String description) { + this.description = description; + } + + /** + * Build a configuration from the entries + * + * @param description configuration description + * @param entries entries to put + */ + public PublishedExports(String description, + Iterable<Map.Entry<String, List<ExportEntry>>> entries) { + this.description = description; + putValues(entries); + } + + /** + * Is the configuration empty. This means either that it has not been given any values, or it is stripped down copy + * set down over the wire. + * + * @return + */ + public boolean isEmpty() { + return entries.isEmpty(); + } + + public long getUpdated() { + return updated; + } + + public void setUpdated(long updated) { + this.updated = updated; + this.updatedTime = new Date(updated).toString(); + } + + /** + * Set the values from an iterable (this includes a Hadoop Configuration and Java properties object). Any existing + * value set is discarded + * + * @param entries entries to put + */ + public void putValues(Iterable<Map.Entry<String, List<ExportEntry>>> entries) { + this.entries = new HashMap<String, List<ExportEntry>>(); + for (Map.Entry<String, List<ExportEntry>> entry : entries) { + this.entries.put(entry.getKey(), entry.getValue()); + } + } + + /** + * Return the values as json string + * + * @return + * + * @throws IOException + */ + public String asJson() throws IOException { + ObjectMapper mapper = new ObjectMapper(); + String json = mapper.writeValueAsString(entries); + return json; + } + + /** + * This makes a copy without the nested content -so is suitable for returning as part of the list of a parent's + * values + * + * @return the copy + */ + public PublishedExports shallowCopy() { + PublishedExports that = new PublishedExports(); + that.description = this.description; + that.updated = this.updated; + that.updatedTime = this.updatedTime; + return that; + } + + @Override + public String toString() { + final StringBuilder sb = + new StringBuilder("PublishedConfiguration{"); + sb.append("description='").append(description).append('\''); + sb.append(" entries = ").append(entries.size()); + sb.append('}'); + return sb.toString(); + } +} http://git-wip-us.apache.org/repos/asf/incubator-slider/blob/31e05853/slider-core/src/main/java/org/apache/slider/core/registry/docstore/PublishedExportsOutputter.java ---------------------------------------------------------------------- diff --git a/slider-core/src/main/java/org/apache/slider/core/registry/docstore/PublishedExportsOutputter.java b/slider-core/src/main/java/org/apache/slider/core/registry/docstore/PublishedExportsOutputter.java new file mode 100644 index 0000000..b21e717 --- /dev/null +++ b/slider-core/src/main/java/org/apache/slider/core/registry/docstore/PublishedExportsOutputter.java @@ -0,0 +1,104 @@ +/* + * 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.slider.core.registry.docstore; + +import com.google.common.base.Charsets; +import com.google.common.base.Preconditions; +import org.apache.commons.io.FileUtils; +import org.apache.commons.io.IOUtils; + +import java.io.File; +import java.io.FileOutputStream; +import java.io.IOException; +import java.io.OutputStream; + +/** Output a published configuration */ +public abstract class PublishedExportsOutputter { + + protected final PublishedExports exports; + + protected PublishedExportsOutputter(PublishedExports exports) { + this.exports = exports; + } + + /** + * Create an outputter for the chosen format + * + * @param format format enumeration + * @param exports owning config + * @return the outputter + */ + + public static PublishedExportsOutputter createOutputter(ConfigFormat format, + PublishedExports exports) { + Preconditions.checkNotNull(exports); + switch (format) { + case JSON: + return new JsonOutputter(exports); + default: + throw new RuntimeException("Unsupported format :" + format); + } + } + + public void save(File dest) throws IOException { + FileOutputStream out = null; + try { + out = new FileOutputStream(dest); + save(out); + out.close(); + } finally { + org.apache.hadoop.io.IOUtils.closeStream(out); + } + } + + /** + * Save the content. The default saves the asString() value to the output stream + * + * @param out output stream + * @throws IOException + */ + public void save(OutputStream out) throws IOException { + IOUtils.write(asString(), out, Charsets.UTF_8); + } + + /** + * Convert to a string + * + * @return + * @throws IOException + */ + public abstract String asString() throws IOException; + + public static class JsonOutputter extends PublishedExportsOutputter { + + public JsonOutputter(PublishedExports exports) { + super(exports); + } + + @Override + public void save(File dest) throws IOException { + FileUtils.writeStringToFile(dest, asString(), Charsets.UTF_8); + } + + @Override + public String asString() throws IOException { + return exports.asJson(); + } + } +} http://git-wip-us.apache.org/repos/asf/incubator-slider/blob/31e05853/slider-core/src/main/java/org/apache/slider/core/registry/docstore/PublishedExportsSet.java ---------------------------------------------------------------------- diff --git a/slider-core/src/main/java/org/apache/slider/core/registry/docstore/PublishedExportsSet.java b/slider-core/src/main/java/org/apache/slider/core/registry/docstore/PublishedExportsSet.java new file mode 100644 index 0000000..cdd35de --- /dev/null +++ b/slider-core/src/main/java/org/apache/slider/core/registry/docstore/PublishedExportsSet.java @@ -0,0 +1,100 @@ +/* + * 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.slider.core.registry.docstore; + +import org.apache.slider.server.appmaster.web.rest.RestPaths; +import org.apache.slider.server.services.utility.PatternValidator; +import org.codehaus.jackson.annotate.JsonIgnoreProperties; +import org.codehaus.jackson.map.annotate.JsonSerialize; + +import java.util.HashMap; +import java.util.Locale; +import java.util.Map; +import java.util.Set; +import java.util.TreeSet; + +/** + * Represents a set of configurations for an application, component, etc. + * Json serialisable; accessors are synchronized + */ +@JsonIgnoreProperties(ignoreUnknown = true) +@JsonSerialize(include = JsonSerialize.Inclusion.NON_NULL) +public class PublishedExportsSet { + + private static final PatternValidator validator = new PatternValidator( + RestPaths.PUBLISHED_CONFIGURATION_REGEXP); + + public Map<String, PublishedExports> exports = + new HashMap<String, PublishedExports>(); + + public PublishedExportsSet() { + } + + /** + * Put a name -it will be converted to lower case before insertion. + * Any existing entry will be overwritten (that includes an entry + * with a different case in the original name) + * @param name name of entry + * @param export published export + * @throws IllegalArgumentException if not a valid name + */ + public void put(String name, PublishedExports export) { + String name1 = name.toLowerCase(Locale.ENGLISH); + validateName(name1); + exports.put(name1, export); + } + + /** + * Validate the name -restricting it to the set defined in + * {@link RestPaths#PUBLISHED_CONFIGURATION_REGEXP} + * @param name name to validate + * @throws IllegalArgumentException if not a valid name + */ + public static void validateName(String name) { + validator.validate(name); + + } + + public PublishedExports get(String name) { + return exports.get(name); + } + + public boolean contains(String name) { + return exports.containsKey(name); + } + + public int size() { + return exports.size(); + } + + public Set<String> keys() { + TreeSet<String> keys = new TreeSet<String>(); + keys.addAll(exports.keySet()); + return keys; + } + + public PublishedExportsSet shallowCopy() { + PublishedExportsSet that = new PublishedExportsSet(); + for (Map.Entry<String, PublishedExports> entry : + exports.entrySet()) { + that.put(entry.getKey(), entry.getValue().shallowCopy()); + } + return that; + } +} http://git-wip-us.apache.org/repos/asf/incubator-slider/blob/31e05853/slider-core/src/main/java/org/apache/slider/core/registry/info/CustomRegistryConstants.java ---------------------------------------------------------------------- diff --git a/slider-core/src/main/java/org/apache/slider/core/registry/info/CustomRegistryConstants.java b/slider-core/src/main/java/org/apache/slider/core/registry/info/CustomRegistryConstants.java index 65c122f..67b9feb 100644 --- a/slider-core/src/main/java/org/apache/slider/core/registry/info/CustomRegistryConstants.java +++ b/slider-core/src/main/java/org/apache/slider/core/registry/info/CustomRegistryConstants.java @@ -35,6 +35,9 @@ public class CustomRegistryConstants { public static final String PUBLISHER_CONFIGURATIONS_API = "org.apache.slider.publisher.configurations"; + public static final String PUBLISHER_EXPORTS_API = + "org.apache.slider.publisher.exports"; + public static final String PUBLISHER_DOCUMENTS_API = "org.apache.slider.publisher.documents"; http://git-wip-us.apache.org/repos/asf/incubator-slider/blob/31e05853/slider-core/src/main/java/org/apache/slider/core/registry/retrieve/RegistryRetriever.java ---------------------------------------------------------------------- diff --git a/slider-core/src/main/java/org/apache/slider/core/registry/retrieve/RegistryRetriever.java b/slider-core/src/main/java/org/apache/slider/core/registry/retrieve/RegistryRetriever.java index 101efb2..a91f515 100644 --- a/slider-core/src/main/java/org/apache/slider/core/registry/retrieve/RegistryRetriever.java +++ b/slider-core/src/main/java/org/apache/slider/core/registry/retrieve/RegistryRetriever.java @@ -33,6 +33,8 @@ import org.apache.slider.common.tools.SliderUtils; import org.apache.slider.core.exceptions.ExceptionConverter; import org.apache.slider.core.registry.docstore.PublishedConfigSet; import org.apache.slider.core.registry.docstore.PublishedConfiguration; +import org.apache.slider.core.registry.docstore.PublishedExports; +import org.apache.slider.core.registry.docstore.PublishedExportsSet; import org.apache.slider.core.registry.info.CustomRegistryConstants; import org.slf4j.Logger; import org.slf4j.LoggerFactory; @@ -52,6 +54,8 @@ public class RegistryRetriever { private final String externalConfigurationURL; private final String internalConfigurationURL; + private final String externalExportsURL; + private final String internalExportsURL; private static final Client jerseyClient; static { @@ -63,9 +67,12 @@ public class RegistryRetriever { jerseyClient.setFollowRedirects(true); } - public RegistryRetriever(String externalConfigurationURL, String internalConfigurationURL) { - this.externalConfigurationURL = externalConfigurationURL; - this.internalConfigurationURL = internalConfigurationURL; + public RegistryRetriever(String externalConfigurationURL, String internalConfigurationURL, + String externalExportsURL, String internalExportsURL) { + this.externalConfigurationURL = externalConfigurationURL; + this.internalConfigurationURL = internalConfigurationURL; + this.externalExportsURL = externalExportsURL; + this.internalExportsURL = internalExportsURL; } /** @@ -99,6 +106,29 @@ public class RegistryRetriever { } } externalConfigurationURL = url; + + internal = record.getInternalEndpoint( + CustomRegistryConstants.PUBLISHER_EXPORTS_API); + url = null; + if (internal != null) { + List<String> addresses = RegistryTypeUtils.retrieveAddressesUriType( + internal); + if (addresses != null && !addresses.isEmpty()) { + url = addresses.get(0); + } + } + internalExportsURL = url; + external = record.getExternalEndpoint( + CustomRegistryConstants.PUBLISHER_EXPORTS_API); + url = null; + if (external != null) { + List<String> addresses = + RegistryTypeUtils.retrieveAddressesUriType(external); + if (addresses != null && !addresses.isEmpty()) { + url = addresses.get(0); + } + } + externalExportsURL = url; } /** @@ -138,6 +168,33 @@ public class RegistryRetriever { return confURL; } + protected String getExportURL(boolean external) throws FileNotFoundException { + String confURL = external ? externalExportsURL: internalExportsURL; + if (Strings.isStringEmpty(confURL)) { + throw new FileNotFoundException("No configuration URL"); + } + return confURL; + } + + /** + * Get the configurations of the registry + * @param external flag to indicate that it is the external entries to fetch + * @return the configuration sets + */ + public PublishedExportsSet getExports(boolean external) throws + FileNotFoundException, IOException { + + String exportsUrl = getExportURL(external); + try { + WebResource webResource = jsonResource(exportsUrl); + log.debug("GET {}", exportsUrl); + PublishedExportsSet exportSet = webResource.get(PublishedExportsSet.class); + return exportSet; + } catch (UniformInterfaceException e) { + throw ExceptionConverter.convertJerseyException(exportsUrl, e); + } + } + private WebResource resource(String url) { WebResource resource = jerseyClient.resource(url); return resource; @@ -174,7 +231,33 @@ public class RegistryRetriever { throw ExceptionConverter.convertJerseyException(confURL, e); } } - + + /** + * Get a complete export, with all values + * @param exportSet + * @param name name of the configuration + * @param external flag to indicate that it is an external configuration + * @return the retrieved config + * @throws IOException IO problems + */ + public PublishedExports retrieveExports(PublishedExportsSet exportSet, + String name, + boolean external) throws IOException { + if (!exportSet.contains(name)) { + throw new FileNotFoundException("Unknown export " + name); + } + String exportsURL = getExportURL(external); + exportsURL = SliderUtils.appendToURL(exportsURL, name); + try { + WebResource webResource = jsonResource(exportsURL); + PublishedExports publishedExports = + webResource.get(PublishedExports.class); + return publishedExports; + } catch (UniformInterfaceException e) { + throw ExceptionConverter.convertJerseyException(exportsURL, e); + } + } + @Override public String toString() { return super.toString() http://git-wip-us.apache.org/repos/asf/incubator-slider/blob/31e05853/slider-core/src/main/java/org/apache/slider/providers/agent/AgentProviderService.java ---------------------------------------------------------------------- diff --git a/slider-core/src/main/java/org/apache/slider/providers/agent/AgentProviderService.java b/slider-core/src/main/java/org/apache/slider/providers/agent/AgentProviderService.java index 44777c3..8b9b257 100644 --- a/slider-core/src/main/java/org/apache/slider/providers/agent/AgentProviderService.java +++ b/slider-core/src/main/java/org/apache/slider/providers/agent/AgentProviderService.java @@ -52,7 +52,9 @@ import org.apache.slider.core.exceptions.NoSuchNodeException; import org.apache.slider.core.exceptions.SliderException; import org.apache.slider.core.launch.CommandLineBuilder; import org.apache.slider.core.launch.ContainerLauncher; +import org.apache.slider.core.registry.docstore.ExportEntry; import org.apache.slider.core.registry.docstore.PublishedConfiguration; +import org.apache.slider.core.registry.docstore.PublishedExports; import org.apache.slider.core.registry.info.CustomRegistryConstants; import org.apache.slider.providers.AbstractProviderService; import org.apache.slider.providers.ProviderCore; @@ -95,10 +97,13 @@ import java.net.URI; import java.net.URISyntaxException; import java.net.URL; import java.util.ArrayList; +import java.util.Arrays; import java.util.Collection; import java.util.Collections; +import java.util.Date; import java.util.HashMap; import java.util.HashSet; +import java.util.Iterator; import java.util.LinkedHashMap; import java.util.List; import java.util.Locale; @@ -106,6 +111,7 @@ import java.util.Map; import java.util.Set; import java.util.TreeMap; import java.util.concurrent.ConcurrentHashMap; +import java.util.concurrent.CopyOnWriteArrayList; import java.util.concurrent.TimeUnit; import java.util.concurrent.atomic.AtomicInteger; @@ -127,10 +133,15 @@ public class AgentProviderService extends AbstractProviderService implements private static final String CONTAINER_ID = "container_id"; private static final String GLOBAL_CONFIG_TAG = "global"; private static final String LOG_FOLDERS_TAG = "LogFolders"; + private static final String HOST_FOLDER_FORMAT = "%s:%s"; + private static final String CONTAINER_LOGS_TAG = "container_log_dirs"; + private static final String CONTAINER_PWDS_TAG = "container_work_dirs"; + private static final String COMPONENT_TAG = "component"; + private static final String APPLICATION_TAG = "application"; private static final String COMPONENT_DATA_TAG = "ComponentInstanceData"; private static final String SHARED_PORT_TAG = "SHARED"; private static final String PER_CONTAINER_TAG = "{PER_CONTAINER}"; - private static final int MAX_LOG_ENTRIES = 20; + private static final int MAX_LOG_ENTRIES = 40; private static final int DEFAULT_HEARTBEAT_MONITOR_INTERVAL = 60 * 1000; private final Object syncLock = new Object(); @@ -149,16 +160,25 @@ public class AgentProviderService extends AbstractProviderService implements new ConcurrentHashMap<String, ComponentInstanceState>(); private final Map<String, Map<String, String>> componentInstanceData = new ConcurrentHashMap<String, Map<String, String>>(); - private final Map<String, Map<String, String>> exportGroups = - new ConcurrentHashMap<String, Map<String, String>>(); + private final Map<String, Map<String, List<ExportEntry>>> exportGroups = + new ConcurrentHashMap<String, Map<String, List<ExportEntry>>>(); private final Map<String, Map<String, String>> allocatedPorts = new ConcurrentHashMap<String, Map<String, String>>(); - private final Map<String, String> workFolders = - Collections.synchronizedMap(new LinkedHashMap<String, String>(MAX_LOG_ENTRIES, 0.75f, false) { + + private final Map<String, ExportEntry> logFolderExports = + Collections.synchronizedMap(new LinkedHashMap<String, ExportEntry>(MAX_LOG_ENTRIES, 0.75f, false) { + protected boolean removeEldestEntry(Map.Entry eldest) { + return size() > MAX_LOG_ENTRIES; + } + }); + private final Map<String, ExportEntry> workFolderExports = + Collections.synchronizedMap(new LinkedHashMap<String, ExportEntry>(MAX_LOG_ENTRIES, 0.75f, false) { protected boolean removeEldestEntry(Map.Entry eldest) { return size() > MAX_LOG_ENTRIES; } }); + private final Map<String, Set<String>> containerExportsMap = + new HashMap<String, Set<String>>(); /** * Create an instance of AgentProviderService @@ -491,7 +511,7 @@ public class AgentProviderService extends AbstractProviderService implements Map<String, String> folders = registration.getLogFolders(); if (folders != null && !folders.isEmpty()) { - publishLogFolderPaths(folders, containerId, roleName, hostFqdn); + publishFolderPaths(folders, containerId, roleName, hostFqdn); } } else { response.setResponseStatus(RegistrationStatus.FAILED); @@ -558,7 +578,7 @@ public class AgentProviderService extends AbstractProviderService implements log.info("Component operation. Status: {}", result); if (command == Command.INSTALL && report.getFolders() != null && report.getFolders().size() > 0) { - publishLogFolderPaths(report.getFolders(), containerId, roleName, heartBeat.getFqdn()); + publishFolderPaths(report.getFolders(), containerId, roleName, heartBeat.getFqdn()); } } @@ -643,6 +663,7 @@ public class AgentProviderService extends AbstractProviderService implements // component specific publishes processAndPublishComponentSpecificData(ports, containerId, fqdn, roleName); + processAndPublishComponentSpecificExports(ports, containerId, fqdn, roleName); // and update registration entries if (instance != null) { @@ -692,7 +713,7 @@ public class AgentProviderService extends AbstractProviderService implements throw new IOException(e); } } - + @Override public void notifyContainerCompleted(ContainerId containerId) { if (containerId != null) { @@ -714,6 +735,25 @@ public class AgentProviderService extends AbstractProviderService implements } } } + + synchronized (this.containerExportsMap) { + Set<String> containerExportSets = containerExportsMap.get(containerIdStr); + if (containerExportSets != null) { + for (String containerExportStr : containerExportSets) { + String[] parts = containerExportStr.split(":"); + Map<String, List<ExportEntry>> exportGroup = getCurrentExports(parts[0]); + List<ExportEntry> exports = exportGroup.get(parts[1]); + List<ExportEntry> exportToRemove = new ArrayList<ExportEntry>(); + for (ExportEntry export : exports) { + if (containerIdStr.equals(export.getContainerId())) { + exportToRemove.add(export); + } + } + exports.removeAll(exportToRemove); + } + containerExportsMap.remove(containerIdStr); + } + } } } @@ -749,6 +789,16 @@ public class AgentProviderService extends AbstractProviderService implements } @VisibleForTesting + protected Map<String, ExportEntry> getLogFolderExports() { + return logFolderExports; + } + + @VisibleForTesting + protected Map<String, ExportEntry> getWorkFolderExports() { + return workFolderExports; + } + + @VisibleForTesting protected Metainfo getMetainfo() { return this.metainfo; } @@ -896,15 +946,59 @@ public class AgentProviderService extends AbstractProviderService implements * @param hostFqdn * @param roleName */ - protected void publishLogFolderPaths( + protected void publishFolderPaths( Map<String, String> folders, String containerId, String roleName, String hostFqdn) { - for (Map.Entry<String, String> entry: folders.entrySet()) { - workFolders.put(String.format("%s->%s->%s->%s", roleName, hostFqdn, entry.getKey(), containerId), - entry.getValue()); + Date now = new Date(); + for (Map.Entry<String, String> entry : folders.entrySet()) { + ExportEntry exportEntry = new ExportEntry(); + exportEntry.setValue(String.format(HOST_FOLDER_FORMAT, hostFqdn, entry.getValue())); + exportEntry.setContainerId(containerId); + exportEntry.setLevel(COMPONENT_TAG); + exportEntry.setTag(roleName); + exportEntry.setUpdatedTime(now.toString()); + if (entry.getKey().equals("AGENT_LOG_ROOT")) { + synchronized (logFolderExports) { + getLogFolderExports().put(containerId, exportEntry); + } + } else { + synchronized (workFolderExports) { + getWorkFolderExports().put(containerId, exportEntry); + } + } + log.info("Updating log and pwd folders for container {}", containerId); } - publishApplicationInstanceData(LOG_FOLDERS_TAG, LOG_FOLDERS_TAG, - (new HashMap<String, String>(this.workFolders)).entrySet()); + PublishedExports exports = new PublishedExports(CONTAINER_LOGS_TAG); + exports.setUpdated(now.getTime()); + synchronized (logFolderExports) { + updateExportsFromList(exports, getLogFolderExports()); + } + getAmState().getPublishedExportsSet().put(CONTAINER_LOGS_TAG, exports); + + exports = new PublishedExports(CONTAINER_PWDS_TAG); + exports.setUpdated(now.getTime()); + synchronized (workFolderExports) { + updateExportsFromList(exports, getWorkFolderExports()); + } + getAmState().getPublishedExportsSet().put(CONTAINER_PWDS_TAG, exports); + } + + /** + * Update the export data from the map + * @param exports + * @param folderExports + */ + private void updateExportsFromList(PublishedExports exports, Map<String, ExportEntry> folderExports) { + Map<String, List<ExportEntry>> perComponentList = new HashMap<String, List<ExportEntry>>(); + for(Map.Entry<String, ExportEntry> logEntry : folderExports.entrySet()) + { + String componentName = logEntry.getValue().getTag(); + if(!perComponentList.containsKey(componentName)) { + perComponentList.put(componentName, new ArrayList<ExportEntry>()); + } + perComponentList.get(componentName).add(logEntry.getValue()); + } + exports.putValues(perComponentList.entrySet()); } @@ -949,13 +1043,12 @@ public class AgentProviderService extends AbstractProviderService implements } } - List<ExportGroup> exportGroups = application.getExportGroups(); - boolean hasExportGroups = exportGroups != null && !exportGroups.isEmpty(); + List<ExportGroup> appExportGroups = application.getExportGroups(); + boolean hasExportGroups = appExportGroups != null && !appExportGroups.isEmpty(); Set<String> appExports = new HashSet(); String appExportsStr = getApplicationComponent(roleName).getAppExports(); - boolean hasNoAppExports = appExportsStr == null || appExportsStr.isEmpty(); - if (!hasNoAppExports) { + if (SliderUtils.isSet(appExportsStr)) { for (String appExport : appExportsStr.split(",")) { if (appExport.trim().length() > 0) { appExports.add(appExport.trim()); @@ -983,11 +1076,12 @@ public class AgentProviderService extends AbstractProviderService implements } Set<String> modifiedGroups = new HashSet<String>(); - for (ExportGroup exportGroup : exportGroups) { + for (ExportGroup exportGroup : appExportGroups) { List<Export> exports = exportGroup.getExports(); if (exports != null && !exports.isEmpty()) { String exportGroupName = exportGroup.getName(); - Map<String, String> map = getCurrentExports(exportGroupName); + ConcurrentHashMap<String, List<ExportEntry>> map = + (ConcurrentHashMap<String, List<ExportEntry>>)getCurrentExports(exportGroupName); for (Export export : exports) { if (canBeExported(exportGroupName, export.getName(), appExports)) { String value = export.getValue(); @@ -997,7 +1091,12 @@ public class AgentProviderService extends AbstractProviderService implements value = value.replace(token, replaceTokens.get(token)); } } - map.put(export.getName(), value); + ExportEntry entry = new ExportEntry(); + entry.setLevel(APPLICATION_TAG); + entry.setValue(value); + entry.setUpdatedTime(new Date().toString()); + // over-write, app exports are singletons + map.put(export.getName(), new ArrayList(Arrays.asList(entry))); log.info("Preparing to publish. Key {} and Value {}", export.getName(), value); } } @@ -1019,11 +1118,11 @@ public class AgentProviderService extends AbstractProviderService implements return appExports.contains(String.format("%s-%s", exportGroupName, name)); } - protected Map<String, String> getCurrentExports(String groupName) { + protected Map<String, List<ExportEntry>> getCurrentExports(String groupName) { if (!this.exportGroups.containsKey(groupName)) { synchronized (this.exportGroups) { if (!this.exportGroups.containsKey(groupName)) { - this.exportGroups.put(groupName, new ConcurrentHashMap<String, String>()); + this.exportGroups.put(groupName, new ConcurrentHashMap<String, List<ExportEntry>>()); } } } @@ -1032,10 +1131,24 @@ public class AgentProviderService extends AbstractProviderService implements } private void publishModifiedExportGroups(Set<String> modifiedGroups) { - synchronized (this.exportGroups) { - for (String groupName : modifiedGroups) { - publishApplicationInstanceData(groupName, groupName, this.exportGroups.get(groupName).entrySet()); + for (String groupName : modifiedGroups) { + Map<String, List<ExportEntry>> entries = this.exportGroups.get(groupName); + + // Publish in old format for the time being + Map<String, String> simpleEntries = new HashMap<String, String>(); + for (Map.Entry<String, List<ExportEntry>> entry : entries.entrySet()) { + List<ExportEntry> exports = entry.getValue(); + if(exports != null && exports.size() > 0) { + // there is no support for multiple exports per name - so extract only the first one + simpleEntries.put(entry.getKey(), entry.getValue().get(0).getValue()); + } } + publishApplicationInstanceData(groupName, groupName, simpleEntries.entrySet()); + + PublishedExports exports = new PublishedExports(groupName); + exports.setUpdated(new Date().getTime()); + exports.putValues(entries.entrySet()); + getAmState().getPublishedExportsSet().put(groupName, exports); } } @@ -1090,14 +1203,102 @@ public class AgentProviderService extends AbstractProviderService implements } } + /** Publish component instance specific data if the component demands it */ + protected void processAndPublishComponentSpecificExports(Map<String, String> ports, + String containerId, + String hostFqdn, + String roleName) { + String portVarFormat = "${site.%s}"; + String hostNamePattern = "${" + roleName + "_HOST}"; + + List<ExportGroup> appExportGroups = getMetainfo().getApplication().getExportGroups(); + Component component = getMetainfo().getApplicationComponent(roleName); + if (component != null && SliderUtils.isSet(component.getCompExports()) + && appExportGroups != null && appExportGroups.size() > 0) { + + Set<String> compExports = new HashSet(); + String compExportsStr = component.getCompExports(); + for (String appExport : compExportsStr.split(",")) { + if (appExport.trim().length() > 0) { + compExports.add(appExport.trim()); + } + } + + Date now = new Date(); + Set<String> modifiedGroups = new HashSet<String>(); + for (ExportGroup exportGroup : appExportGroups) { + List<Export> exports = exportGroup.getExports(); + if (exports != null && !exports.isEmpty()) { + String exportGroupName = exportGroup.getName(); + ConcurrentHashMap<String, List<ExportEntry>> map = + (ConcurrentHashMap<String, List<ExportEntry>>) getCurrentExports(exportGroupName); + for (Export export : exports) { + if (canBeExported(exportGroupName, export.getName(), compExports)) { + log.info("Attempting to publish {} of group {} for component type {}", + export.getName(), exportGroupName, roleName); + String templateToExport = export.getValue(); + for (String portName : ports.keySet()) { + boolean publishData = false; + String portValPattern = String.format(portVarFormat, portName); + if (templateToExport.contains(portValPattern)) { + templateToExport = templateToExport.replace(portValPattern, ports.get(portName)); + publishData = true; + } + if (templateToExport.contains(hostNamePattern)) { + templateToExport = templateToExport.replace(hostNamePattern, hostFqdn); + publishData = true; + } + if (publishData) { + ExportEntry entryToAdd = new ExportEntry(); + entryToAdd.setLevel(COMPONENT_TAG); + entryToAdd.setValue(templateToExport); + entryToAdd.setUpdatedTime(now.toString()); + entryToAdd.setContainerId(containerId); + + List<ExportEntry> existingList = + map.putIfAbsent(export.getName(), new CopyOnWriteArrayList(Arrays.asList(entryToAdd))); + + // in-place edit, no lock needed + if (existingList != null) { + boolean updatedInPlace = false; + for (ExportEntry entry : existingList) { + if (containerId.equalsIgnoreCase(entry.getContainerId())) { + entryToAdd.setValue(templateToExport); + entryToAdd.setUpdatedTime(now.toString()); + updatedInPlace = true; + } + } + if (!updatedInPlace) { + existingList.add(entryToAdd); + } + } + + log.info("Publishing {} for name {} and container {}", + templateToExport, export.getName(), containerId); + modifiedGroups.add(exportGroupName); + synchronized (containerExportsMap) { + if (!containerExportsMap.containsKey(containerId)) { + containerExportsMap.put(containerId, new HashSet<String>()); + } + Set<String> containerExportMaps = containerExportsMap.get(containerId); + containerExportMaps.add(String.format("%s:%s", exportGroupName, export.getName())); + } + } + } + } + } + } + } + publishModifiedExportGroups(modifiedGroups); + } + } + private void publishComponentInstanceData() { Map<String, String> dataToPublish = new HashMap<String, String>(); - synchronized (this.componentInstanceData) { - for (String container : getComponentInstanceData().keySet()) { - for (String prop : getComponentInstanceData().get(container).keySet()) { - dataToPublish.put( - container + "." + prop, getComponentInstanceData().get(container).get(prop)); - } + for (String container : getComponentInstanceData().keySet()) { + for (String prop : getComponentInstanceData().get(container).keySet()) { + dataToPublish.put( + container + "." + prop, getComponentInstanceData().get(container).get(prop)); } } publishApplicationInstanceData(COMPONENT_DATA_TAG, COMPONENT_DATA_TAG, dataToPublish.entrySet()); @@ -1580,5 +1781,4 @@ public class AgentProviderService extends AbstractProviderService implements ""); } } - } http://git-wip-us.apache.org/repos/asf/incubator-slider/blob/31e05853/slider-core/src/main/java/org/apache/slider/providers/agent/application/metadata/Component.java ---------------------------------------------------------------------- diff --git a/slider-core/src/main/java/org/apache/slider/providers/agent/application/metadata/Component.java b/slider-core/src/main/java/org/apache/slider/providers/agent/application/metadata/Component.java index 9f3dd0f..418868c 100644 --- a/slider-core/src/main/java/org/apache/slider/providers/agent/application/metadata/Component.java +++ b/slider-core/src/main/java/org/apache/slider/providers/agent/application/metadata/Component.java @@ -34,6 +34,7 @@ public class Component { String maxInstanceCount; String autoStartOnFailure; String appExports; + String compExports; CommandScript commandScript; List<ComponentExport> componentExports; @@ -82,6 +83,14 @@ public class Component { this.appExports = appExports; } + public String getCompExports() { + return compExports; + } + + public void setCompExports(String compExports) { + this.compExports = compExports; + } + public String getMinInstanceCount() { return minInstanceCount; } http://git-wip-us.apache.org/repos/asf/incubator-slider/blob/31e05853/slider-core/src/main/java/org/apache/slider/providers/agent/application/metadata/MetainfoParser.java ---------------------------------------------------------------------- diff --git a/slider-core/src/main/java/org/apache/slider/providers/agent/application/metadata/MetainfoParser.java b/slider-core/src/main/java/org/apache/slider/providers/agent/application/metadata/MetainfoParser.java index c92c265..1d8403f 100644 --- a/slider-core/src/main/java/org/apache/slider/providers/agent/application/metadata/MetainfoParser.java +++ b/slider-core/src/main/java/org/apache/slider/providers/agent/application/metadata/MetainfoParser.java @@ -61,6 +61,7 @@ public class MetainfoParser { digester.addBeanPropertySetter("*/component/maxInstanceCount"); digester.addBeanPropertySetter("*/component/autoStartOnFailure"); digester.addBeanPropertySetter("*/component/appExports"); + digester.addBeanPropertySetter("*/component/compExports"); digester.addObjectCreate("*/componentExport", ComponentExport.class); digester.addBeanPropertySetter("*/componentExport/name"); digester.addBeanPropertySetter("*/componentExport/value"); http://git-wip-us.apache.org/repos/asf/incubator-slider/blob/31e05853/slider-core/src/main/java/org/apache/slider/providers/slideram/SliderAMProviderService.java ---------------------------------------------------------------------- diff --git a/slider-core/src/main/java/org/apache/slider/providers/slideram/SliderAMProviderService.java b/slider-core/src/main/java/org/apache/slider/providers/slideram/SliderAMProviderService.java index 601c3f9..afe6428 100644 --- a/slider-core/src/main/java/org/apache/slider/providers/slideram/SliderAMProviderService.java +++ b/slider-core/src/main/java/org/apache/slider/providers/slideram/SliderAMProviderService.java @@ -146,6 +146,8 @@ public class SliderAMProviderService extends AbstractProviderService implements String configurationsURL = SliderUtils.appendToURL( publisherURL.toExternalForm(), RestPaths.SLIDER_CONFIGSET); + String exportsURL = SliderUtils.appendToURL( + publisherURL.toExternalForm(), RestPaths.SLIDER_EXPORTS); serviceRecord.addExternalEndpoint( RegistryTypeUtils.webEndpoint( @@ -166,6 +168,10 @@ public class SliderAMProviderService extends AbstractProviderService implements RegistryTypeUtils.restEndpoint( CustomRegistryConstants.PUBLISHER_CONFIGURATIONS_API, new URI(configurationsURL))); + serviceRecord.addExternalEndpoint( + RegistryTypeUtils.restEndpoint( + CustomRegistryConstants.PUBLISHER_EXPORTS_API, + new URI(exportsURL))); } catch (URISyntaxException e) { throw new IOException(e); http://git-wip-us.apache.org/repos/asf/incubator-slider/blob/31e05853/slider-core/src/main/java/org/apache/slider/server/appmaster/state/ProviderAppState.java ---------------------------------------------------------------------- diff --git a/slider-core/src/main/java/org/apache/slider/server/appmaster/state/ProviderAppState.java b/slider-core/src/main/java/org/apache/slider/server/appmaster/state/ProviderAppState.java index a0871ae..9c5da12 100644 --- a/slider-core/src/main/java/org/apache/slider/server/appmaster/state/ProviderAppState.java +++ b/slider-core/src/main/java/org/apache/slider/server/appmaster/state/ProviderAppState.java @@ -26,6 +26,7 @@ import org.apache.slider.core.conf.AggregateConf; import org.apache.slider.core.conf.ConfTreeOperations; import org.apache.slider.core.exceptions.NoSuchNodeException; import org.apache.slider.core.registry.docstore.PublishedConfigSet; +import org.apache.slider.core.registry.docstore.PublishedExportsSet; import org.apache.slider.server.appmaster.web.rest.RestPaths; import org.apache.slider.server.services.utility.PatternValidator; @@ -40,6 +41,7 @@ public class ProviderAppState implements StateAccessForProviders { private final Map<String, PublishedConfigSet> publishedConfigSets = new ConcurrentHashMap<String, PublishedConfigSet>(5); + private final PublishedExportsSet publishedExportsSets = new PublishedExportsSet(); private static final PatternValidator validator = new PatternValidator( RestPaths.PUBLISHED_CONFIGURATION_SET_REGEXP); private String applicationName; @@ -66,6 +68,11 @@ public class ProviderAppState implements StateAccessForProviders { } @Override + public PublishedExportsSet getPublishedExportsSet() { + return publishedExportsSets; + } + + @Override public PublishedConfigSet getPublishedConfigSet(String name) { return publishedConfigSets.get(name); } http://git-wip-us.apache.org/repos/asf/incubator-slider/blob/31e05853/slider-core/src/main/java/org/apache/slider/server/appmaster/state/StateAccessForProviders.java ---------------------------------------------------------------------- diff --git a/slider-core/src/main/java/org/apache/slider/server/appmaster/state/StateAccessForProviders.java b/slider-core/src/main/java/org/apache/slider/server/appmaster/state/StateAccessForProviders.java index 1714f75..b907b06 100644 --- a/slider-core/src/main/java/org/apache/slider/server/appmaster/state/StateAccessForProviders.java +++ b/slider-core/src/main/java/org/apache/slider/server/appmaster/state/StateAccessForProviders.java @@ -26,6 +26,7 @@ import org.apache.slider.core.conf.AggregateConf; import org.apache.slider.core.conf.ConfTreeOperations; import org.apache.slider.core.exceptions.NoSuchNodeException; import org.apache.slider.core.registry.docstore.PublishedConfigSet; +import org.apache.slider.core.registry.docstore.PublishedExportsSet; import java.util.Collection; import java.util.List; @@ -51,6 +52,12 @@ public interface StateAccessForProviders { PublishedConfigSet getPublishedSliderConfigurations(); /** + * Get the published exports set + * @return + */ + PublishedExportsSet getPublishedExportsSet(); + + /** * Get a named published config set * @param name name to look up * @return the instance or null http://git-wip-us.apache.org/repos/asf/incubator-slider/blob/31e05853/slider-core/src/main/java/org/apache/slider/server/appmaster/web/rest/RestPaths.java ---------------------------------------------------------------------- diff --git a/slider-core/src/main/java/org/apache/slider/server/appmaster/web/rest/RestPaths.java b/slider-core/src/main/java/org/apache/slider/server/appmaster/web/rest/RestPaths.java index 93601ad..94f1e4c 100644 --- a/slider-core/src/main/java/org/apache/slider/server/appmaster/web/rest/RestPaths.java +++ b/slider-core/src/main/java/org/apache/slider/server/appmaster/web/rest/RestPaths.java @@ -61,6 +61,7 @@ public class RestPaths { = "[a-z0-9][a-z0-9_.\\+-]*"; public static final String SLIDER_CONFIGSET = "slider"; + public static final String SLIDER_EXPORTS = "exports"; public static final String SLIDER_CLASSPATH = "classpath"; } http://git-wip-us.apache.org/repos/asf/incubator-slider/blob/31e05853/slider-core/src/main/java/org/apache/slider/server/appmaster/web/rest/publisher/PublisherResource.java ---------------------------------------------------------------------- diff --git a/slider-core/src/main/java/org/apache/slider/server/appmaster/web/rest/publisher/PublisherResource.java b/slider-core/src/main/java/org/apache/slider/server/appmaster/web/rest/publisher/PublisherResource.java index 5d8b657..e47bbb9 100644 --- a/slider-core/src/main/java/org/apache/slider/server/appmaster/web/rest/publisher/PublisherResource.java +++ b/slider-core/src/main/java/org/apache/slider/server/appmaster/web/rest/publisher/PublisherResource.java @@ -23,6 +23,8 @@ import org.apache.slider.core.registry.docstore.ConfigFormat; import org.apache.slider.core.registry.docstore.PublishedConfigSet; import org.apache.slider.core.registry.docstore.PublishedConfiguration; import org.apache.slider.core.registry.docstore.PublishedConfigurationOutputter; +import org.apache.slider.core.registry.docstore.PublishedExports; +import org.apache.slider.core.registry.docstore.PublishedExportsSet; import org.apache.slider.core.registry.docstore.UriMap; import org.apache.slider.server.appmaster.state.StateAccessForProviders; import org.apache.slider.server.appmaster.web.WebAppApi; @@ -56,7 +58,10 @@ public class PublisherResource { protected static final Logger log = LoggerFactory.getLogger(PublisherResource.class); private final WebAppApi slider; - public static final String SET_NAME = + public static final String EXPORTS_NAME = "exports"; + public static final String EXPORTS_RESOURCES_PATH = "/" + EXPORTS_NAME; + public static final String EXPORT_RESOURCE_PATH = EXPORTS_RESOURCES_PATH + "/{exportname}" ; + public static final String SET_NAME = "{setname: " + PUBLISHED_CONFIGURATION_SET_REGEXP + "}"; private static final String CONFIG = SET_NAME + "/{config: " + PUBLISHED_CONFIGURATION_REGEXP + "}"; @@ -101,7 +106,9 @@ public class PublisherResource { UriMap uriMap = new UriMap(); for (String name : appState.listConfigSets()) { uriMap.put(name, baseURL + name); + log.info("Tick tack {} and {}", name, baseURL); } + uriMap.put(EXPORTS_NAME, baseURL + EXPORTS_NAME); return uriMap; } @@ -114,6 +121,26 @@ public class PublisherResource { } @GET + @Path(EXPORTS_RESOURCES_PATH) + @Produces({MediaType.APPLICATION_JSON}) + public PublishedExportsSet gePublishedExports() { + + PublishedExportsSet set = appState.getPublishedExportsSet(); + return set.shallowCopy(); + } + + @GET + @Path(EXPORT_RESOURCE_PATH) + @Produces({MediaType.APPLICATION_JSON}) + public PublishedExports getAMExports2(@PathParam("exportname") String exportname, + @Context UriInfo uriInfo, + @Context HttpServletResponse res) { + init(res, uriInfo); + PublishedExportsSet set = appState.getPublishedExportsSet(); + return set.get(exportname); + } + + @GET @Path("/"+ SET_NAME) @Produces({MediaType.APPLICATION_JSON}) public PublishedConfigSet getPublishedConfiguration( @@ -129,7 +156,7 @@ public class PublisherResource { } private void logRequest(UriInfo uriInfo) { - log.debug(uriInfo.getRequestUri().toString()); + log.info(uriInfo.getRequestUri().toString()); } @GET http://git-wip-us.apache.org/repos/asf/incubator-slider/blob/31e05853/slider-core/src/test/groovy/org/apache/slider/client/TestClientBadArgs.groovy ---------------------------------------------------------------------- diff --git a/slider-core/src/test/groovy/org/apache/slider/client/TestClientBadArgs.groovy b/slider-core/src/test/groovy/org/apache/slider/client/TestClientBadArgs.groovy index d1f8a8f..7d596d6 100644 --- a/slider-core/src/test/groovy/org/apache/slider/client/TestClientBadArgs.groovy +++ b/slider-core/src/test/groovy/org/apache/slider/client/TestClientBadArgs.groovy @@ -24,8 +24,10 @@ import org.apache.hadoop.conf.Configuration import org.apache.slider.common.params.ActionRegistryArgs import org.apache.slider.common.params.Arguments import org.apache.slider.common.params.SliderActions +import org.apache.slider.core.exceptions.BadCommandArgumentsException import org.apache.slider.core.exceptions.ErrorStrings import org.apache.slider.core.exceptions.UsageException +import org.apache.slider.core.main.ServiceLauncher import org.apache.slider.core.main.ServiceLauncherBaseTest import org.junit.Test @@ -88,4 +90,45 @@ class TestClientBadArgs extends ServiceLauncherBaseTest { log.info(exception.toString()) } + @Test + public void testRegistryExportBadUsage1() throws Throwable { + def exception = launchExpectingException(SliderClient, + new Configuration(), + "Expected a value after parameter --getexp", + [SliderActions.ACTION_REGISTRY, + Arguments.ARG_NAME, + "cl1", + Arguments.ARG_GETEXP]) + assert exception instanceof BadCommandArgumentsException + log.info(exception.toString()) + } + + @Test + public void testRegistryExportBadUsage2() throws Throwable { + def exception = launchExpectingException(SliderClient, + new Configuration(), + "Expected a value after parameter --getexp", + [SliderActions.ACTION_REGISTRY, + Arguments.ARG_NAME, + "cl1", + Arguments.ARG_LISTEXP, + Arguments.ARG_GETEXP]) + assert exception instanceof BadCommandArgumentsException + log.info(exception.toString()) + } + + @Test + public void testRegistryExportBadUsage3() throws Throwable { + def exception = launchExpectingException(SliderClient, + new Configuration(), + "Usage: registry", + [SliderActions.ACTION_REGISTRY, + Arguments.ARG_NAME, + "cl1", + Arguments.ARG_LISTEXP, + Arguments.ARG_GETEXP, + "export1"]) + assert exception instanceof UsageException + log.info(exception.toString()) + } } http://git-wip-us.apache.org/repos/asf/incubator-slider/blob/31e05853/slider-core/src/test/java/org/apache/slider/providers/agent/TestAgentProviderService.java ---------------------------------------------------------------------- diff --git a/slider-core/src/test/java/org/apache/slider/providers/agent/TestAgentProviderService.java b/slider-core/src/test/java/org/apache/slider/providers/agent/TestAgentProviderService.java index 7eff45a..a20e7a9 100644 --- a/slider-core/src/test/java/org/apache/slider/providers/agent/TestAgentProviderService.java +++ b/slider-core/src/test/java/org/apache/slider/providers/agent/TestAgentProviderService.java @@ -25,6 +25,7 @@ import org.apache.hadoop.fs.Path; import org.apache.hadoop.registry.client.api.RegistryConstants; import org.apache.hadoop.registry.client.api.RegistryOperations; import org.apache.hadoop.registry.client.types.ServiceRecord; +import org.apache.hadoop.yarn.api.records.ApplicationAttemptId; import org.apache.hadoop.yarn.api.records.Container; import org.apache.hadoop.yarn.api.records.ContainerId; import org.apache.hadoop.yarn.api.records.ContainerLaunchContext; @@ -45,6 +46,9 @@ import org.apache.slider.core.conf.ConfTreeOperations; import org.apache.slider.core.conf.MapOperations; import org.apache.slider.core.exceptions.SliderException; import org.apache.slider.core.launch.ContainerLauncher; +import org.apache.slider.core.registry.docstore.ExportEntry; +import org.apache.slider.core.registry.docstore.PublishedExports; +import org.apache.slider.core.registry.docstore.PublishedExportsSet; import org.apache.slider.providers.ProviderRole; import org.apache.slider.providers.agent.application.metadata.Application; import org.apache.slider.providers.agent.application.metadata.CommandOrder; @@ -94,6 +98,7 @@ import java.util.HashMap; import java.util.List; import java.util.Map; import java.util.Set; +import java.util.concurrent.ConcurrentHashMap; import static org.easymock.EasyMock.anyObject; import static org.easymock.EasyMock.createNiceMock; @@ -136,6 +141,10 @@ public class TestAgentProviderService { + " <name>Master_Status</name>\n" + " <value>http://${HBASE_MASTER_HOST}:${site.hbase-site.hbase.master.info.port}/master-status</value>\n" + " </export>\n" + + " <export>\n" + + " <name>Comp_Endpoint</name>\n" + + " <value>http://${HBASE_REGIONSERVER_HOST}:${site.global.listen_port}</value>\n" + + " </export>\n" + " </exports>\n" + " </exportGroup>\n" + " </exportGroups>\n" @@ -165,6 +174,7 @@ public class TestAgentProviderService { + " <publishConfig>true</publishConfig>\n" + " <autoStartOnFailure>true</autoStartOnFailure>\n" + " <appExports>QuickLinks-JMX_Endpoint,QuickLinks-Master_Status</appExports>\n" + + " <compExports>QuickLinks-Comp_Endpoint</compExports>\n" + " <minInstanceCount>1</minInstanceCount>\n" + " <maxInstanceCount>2</maxInstanceCount>\n" + " <commandScript>\n" @@ -182,6 +192,7 @@ public class TestAgentProviderService { + " <script>scripts/hbase_regionserver.py</script>\n" + " <scriptType>PYTHON</scriptType>\n" + " </commandScript>\n" + + " <compExports>QuickLinks-Comp_Endpoint</compExports>\n" + " <componentExports>\n" + " <componentExport>\n" + " <name>PropertyA</name>\n" @@ -322,10 +333,10 @@ public class TestAgentProviderService { anyMap() ); - doNothing().when(mockAps).publishLogFolderPaths(anyMap(), - anyString(), - anyString(), - anyString() + doNothing().when(mockAps).publishFolderPaths(anyMap(), + anyString(), + anyString(), + anyString() ); expect(access.isApplicationLive()).andReturn(true).anyTimes(); ClusterDescription desc = new ClusterDescription(); @@ -378,7 +389,7 @@ public class TestAgentProviderService { anyMap() ); - Mockito.verify(mockAps, Mockito.times(1)).publishLogFolderPaths( + Mockito.verify(mockAps, Mockito.times(1)).publishFolderPaths( anyMap(), anyString(), anyString(), @@ -694,6 +705,92 @@ public class TestAgentProviderService { } } + + @Test + public void testComponentSpecificPublishes2() throws Exception { + InputStream metainfo_1 = new ByteArrayInputStream(metainfo_1_str.getBytes()); + Metainfo metainfo = new MetainfoParser().parse(metainfo_1); + AgentProviderService aps = createAgentProviderService(new Configuration()); + AgentProviderService mockAps = Mockito.spy(aps); + doNothing().when(mockAps).publishApplicationInstanceData(anyString(), anyString(), anyCollection()); + doReturn(metainfo).when(mockAps).getMetainfo(); + StateAccessForProviders access = createNiceMock(StateAccessForProviders.class); + doReturn(access).when(mockAps).getAmState(); + PublishedExportsSet pubExpSet = new PublishedExportsSet(); + expect(access.getPublishedExportsSet()).andReturn(pubExpSet).anyTimes(); + replay(access); + + Map<String, String> ports = new HashMap<String, String>(); + ports.put("global.listen_port", "10010"); + mockAps.processAndPublishComponentSpecificExports(ports, + "mockcontainer_1", + "host1", + "HBASE_REGIONSERVER"); + ArgumentCaptor<Collection> entriesCaptor = ArgumentCaptor. + forClass(Collection.class); + ArgumentCaptor<String> publishNameCaptor = ArgumentCaptor. + forClass(String.class); + Mockito.verify(mockAps, Mockito.times(1)).publishApplicationInstanceData( + anyString(), + publishNameCaptor.capture(), + entriesCaptor.capture()); + + PublishedExports pubExports = pubExpSet.get("QuickLinks".toLowerCase()); + Assert.assertEquals(1, pubExports.entries.size()); + Assert.assertEquals("QuickLinks", pubExports.description); + List<ExportEntry> expEntries = pubExports.entries.get("Comp_Endpoint"); + Assert.assertEquals(1, expEntries.size()); + Assert.assertEquals("mockcontainer_1", expEntries.get(0).getContainerId()); + Assert.assertEquals("component", expEntries.get(0).getLevel()); + Assert.assertEquals(null, expEntries.get(0).getTag()); + Assert.assertEquals("http://host1:10010", expEntries.get(0).getValue()); + Assert.assertNotNull(expEntries.get(0).getUpdatedTime()); + Assert.assertNull(expEntries.get(0).getValidUntil()); + + assert entriesCaptor.getAllValues().size() == 1; + for (Collection coll : entriesCaptor.getAllValues()) { + Set<Map.Entry<String, String>> entrySet = (Set<Map.Entry<String, String>>) coll; + for (Map.Entry entry : entrySet) { + log.info("{}:{}", entry.getKey(), entry.getValue().toString()); + if (entry.getKey().equals("Comp_Endpoint")) { + assert entry.getValue().toString().equals("http://host1:10010"); + } + } + } + assert publishNameCaptor.getAllValues().size() == 1; + for (String coll : publishNameCaptor.getAllValues()) { + assert coll.equals("QuickLinks"); + } + + mockAps.notifyContainerCompleted(new MockContainerId(1)); + pubExports = pubExpSet.get("QuickLinks".toLowerCase()); + Assert.assertEquals(1, pubExports.entries.size()); + Assert.assertEquals("QuickLinks", pubExports.description); + expEntries = pubExports.entries.get("Comp_Endpoint"); + Assert.assertEquals(0, expEntries.size()); + + mockAps.notifyContainerCompleted(new MockContainerId(1)); + mockAps.notifyContainerCompleted(new MockContainerId(2)); + + mockAps.processAndPublishComponentSpecificExports(ports, + "mockcontainer_1", + "host1", + "HBASE_REGIONSERVER"); + mockAps.processAndPublishComponentSpecificExports(ports, + "mockcontainer_2", + "host1", + "HBASE_REGIONSERVER"); + pubExports = pubExpSet.get("QuickLinks".toLowerCase()); + Assert.assertEquals(1, pubExports.entries.size()); + Assert.assertEquals("QuickLinks", pubExports.description); + expEntries = pubExports.entries.get("Comp_Endpoint"); + Assert.assertEquals(2, expEntries.size()); + + mockAps.notifyContainerCompleted(new MockContainerId(2)); + expEntries = pubExports.entries.get("Comp_Endpoint"); + Assert.assertEquals(1, expEntries.size()); + } + @Test public void testProcessConfig() throws Exception { InputStream metainfo_1 = new ByteArrayInputStream(metainfo_1_str.getBytes()); @@ -728,6 +825,11 @@ public class TestAgentProviderService { doNothing().when(mockAps).publishApplicationInstanceData(anyString(), anyString(), anyCollection()); doReturn(metainfo).when(mockAps).getMetainfo(); doReturn(roleClusterNodeMap).when(mockAps).getRoleClusterNodeMapping(); + StateAccessForProviders access = createNiceMock(StateAccessForProviders.class); + doReturn(access).when(mockAps).getAmState(); + PublishedExportsSet pubExpSet = new PublishedExportsSet(); + expect(access.getPublishedExportsSet()).andReturn(pubExpSet).anyTimes(); + replay(access); mockAps.publishConfigAndExportGroups(hb, componentStatus, "HBASE_MASTER"); Assert.assertTrue(componentStatus.getConfigReported()); @@ -748,15 +850,30 @@ public class TestAgentProviderService { } } - Map<String, String> exports = mockAps.getCurrentExports("QuickLinks"); + Map<String, List<ExportEntry>> exports = mockAps.getCurrentExports("QuickLinks"); Assert.assertEquals(2, exports.size()); - Assert.assertEquals(exports.get("JMX_Endpoint"), "http://HOST1:60012/jmx"); + Assert.assertEquals(exports.get("JMX_Endpoint").get(0).getValue(), "http://HOST1:60012/jmx"); mockAps.publishConfigAndExportGroups(hb, componentStatus, "HBASE_REST"); Mockito.verify(mockAps, Mockito.times(3)).publishApplicationInstanceData( anyString(), anyString(), entriesCaptor.capture()); + PublishedExports pubExports = pubExpSet.get("QuickLinks".toLowerCase()); + Assert.assertEquals(2, pubExports.entries.size()); + Assert.assertEquals("QuickLinks", pubExports.description); + List<ExportEntry> expEntries = pubExports.entries.get("JMX_Endpoint"); + Assert.assertEquals(1, expEntries.size()); + Assert.assertEquals(null, expEntries.get(0).getContainerId()); + Assert.assertEquals("application", expEntries.get(0).getLevel()); + Assert.assertEquals(null, expEntries.get(0).getTag()); + Assert.assertEquals("http://HOST1:60012/jmx", expEntries.get(0).getValue()); + Assert.assertNull(expEntries.get(0).getValidUntil()); + + expEntries = pubExports.entries.get("Master_Status"); + Assert.assertEquals(1, expEntries.size()); + expEntries = pubExports.entries.get("JMX_Endpoint"); + Assert.assertEquals("http://HOST1:60012/jmx", expEntries.get(0).getValue()); } @Test @@ -781,6 +898,7 @@ public class TestAgentProviderService { Assert.assertEquals(component.getCategory(), "MASTER"); Assert.assertEquals(component.getComponentExports().size(), 0); Assert.assertEquals(component.getAppExports(), "QuickLinks-JMX_Endpoint,QuickLinks-Master_Status"); + Assert.assertEquals(component.getCompExports(), "QuickLinks-Comp_Endpoint"); found++; } if (component.getName().equals("HBASE_REGIONSERVER")) { @@ -807,7 +925,7 @@ public class TestAgentProviderService { List<ExportGroup> egs = application.getExportGroups(); ExportGroup eg = egs.get(0); Assert.assertEquals(eg.getName(), "QuickLinks"); - Assert.assertEquals(eg.getExports().size(), 2); + Assert.assertEquals(eg.getExports().size(), 3); found = 0; for (Export export : eg.getExports()) { @@ -975,15 +1093,18 @@ public class TestAgentProviderService { anyString(), anyString(), any(HeartBeatResponse.class)); - doNothing().when(mockAps).publishApplicationInstanceData( + doNothing().when(mockAps).publishFolderPaths( + anyMap(), anyString(), anyString(), - anyCollection()); + anyString()); doReturn(conf).when(mockAps).getConfig(); } catch (SliderException e) { } + PublishedExportsSet pubExpSet = new PublishedExportsSet(); expect(access.isApplicationLive()).andReturn(true).anyTimes(); + expect(access.getPublishedExportsSet()).andReturn(pubExpSet).anyTimes(); ClusterDescription desc = new ClusterDescription(); desc.setOption(OptionKeys.ZOOKEEPER_QUORUM, "host1:2181"); desc.setInfo(OptionKeys.APPLICATION_NAME, "HBASE"); @@ -996,6 +1117,7 @@ public class TestAgentProviderService { treeOps.set(OptionKeys.APPLICATION_NAME, "HBASE"); expect(access.getInstanceDefinitionSnapshot()).andReturn(aggConf).anyTimes(); expect(access.getInternalsSnapshot()).andReturn(treeOps).anyTimes(); + doNothing().when(mockAps).publishApplicationInstanceData(anyString(), anyString(), anyCollection()); replay(access, ctx, container, sliderFileSystem, mockFs); // build two containers @@ -1094,6 +1216,7 @@ public class TestAgentProviderService { hb = new HeartBeat(); hb.setResponseId(2); hb.setHostname("mockcontainer_1___HBASE_MASTER"); + hb.setFqdn("host1"); cr = new CommandReport(); cr.setRole("HBASE_MASTER"); cr.setRoleCommand("INSTALL"); @@ -1160,10 +1283,11 @@ public class TestAgentProviderService { log.warn(he.getMessage()); } - Mockito.verify(mockAps, Mockito.times(1)).publishApplicationInstanceData( + Mockito.verify(mockAps, Mockito.times(1)).publishFolderPaths( + anyMap(), anyString(), anyString(), - anyCollection()); + anyString()); } protected AgentProviderService createAgentProviderService(Configuration conf) throws @@ -1193,6 +1317,44 @@ public class TestAgentProviderService { } @Test + public void testPublishFolderPaths() throws IOException { + AgentProviderService aps = createAgentProviderService(new Configuration()); + StateAccessForProviders access = createNiceMock(StateAccessForProviders.class); + AgentProviderService mockAps = Mockito.spy(aps); + doReturn(access).when(mockAps).getAmState(); + PublishedExportsSet pubExpSet = new PublishedExportsSet(); + expect(access.getPublishedExportsSet()).andReturn(pubExpSet).anyTimes(); + replay(access); + + Map<String, String> folders = new HashMap<String, String>(); + folders.put("AGENT_LOG_ROOT", "aFolder"); + folders.put("AGENT_WORK_ROOT", "folderB"); + mockAps.publishFolderPaths(folders, "cid", "role", "fqdn"); + + PublishedExports exports = pubExpSet.get("container_log_dirs"); + Assert.assertEquals(1, exports.entries.size()); + List<ExportEntry> expEntries = exports.entries.get("role"); + Assert.assertEquals(1, expEntries.size()); + Assert.assertEquals("cid", expEntries.get(0).getContainerId()); + Assert.assertEquals("component", expEntries.get(0).getLevel()); + Assert.assertEquals("role", expEntries.get(0).getTag()); + Assert.assertEquals("fqdn:aFolder", expEntries.get(0).getValue()); + Assert.assertNull(expEntries.get(0).getValidUntil()); + Assert.assertEquals(null, expEntries.get(0).getValidUntil()); + + exports = pubExpSet.get("container_work_dirs"); + Assert.assertEquals(1, exports.entries.size()); + expEntries = exports.entries.get("role"); + Assert.assertEquals(1, expEntries.size()); + Assert.assertEquals("cid", expEntries.get(0).getContainerId()); + Assert.assertEquals("component", expEntries.get(0).getLevel()); + Assert.assertEquals("role", expEntries.get(0).getTag()); + Assert.assertEquals("fqdn:folderB", expEntries.get(0).getValue()); + Assert.assertNull(expEntries.get(0).getValidUntil()); + Assert.assertEquals(null, expEntries.get(0).getValidUntil()); + } + + @Test public void testNotifyContainerCompleted() throws IOException { AgentProviderService aps = createAgentProviderService(new Configuration()); AgentProviderService mockAps = Mockito.spy(aps); http://git-wip-us.apache.org/repos/asf/incubator-slider/blob/31e05853/slider-funtest/src/main/groovy/org/apache/slider/funtest/framework/AgentCommandTestBase.groovy ---------------------------------------------------------------------- diff --git a/slider-funtest/src/main/groovy/org/apache/slider/funtest/framework/AgentCommandTestBase.groovy b/slider-funtest/src/main/groovy/org/apache/slider/funtest/framework/AgentCommandTestBase.groovy index d5448bf..db9fa6d 100644 --- a/slider-funtest/src/main/groovy/org/apache/slider/funtest/framework/AgentCommandTestBase.groovy +++ b/slider-funtest/src/main/groovy/org/apache/slider/funtest/framework/AgentCommandTestBase.groovy @@ -164,6 +164,33 @@ implements FuntestProperties, Arguments, SliderExitCodes, SliderActions { return null; } + public static boolean containsString(SliderShell shell, String lookThisUp, int n = 1) { + int count = 0 + for (String str in shell.out) { + int subCount = countString(str, lookThisUp) + count = count + subCount + if (count == n) { + return true; + } + } + + return false; + } + + public static int countString(String str, String search) { + int count = 0 + if (SliderUtils.isUnset(str) || SliderUtils.isUnset(search)) { + return count + } + + int index = str.indexOf(search, 0) + while (index > 0) { + index = str.indexOf(search, index + 1) + ++count + } + return count + } + public static String findLineEntryValue(SliderShell shell, String[] locaters) { String line = findLineEntry(shell, locaters); http://git-wip-us.apache.org/repos/asf/incubator-slider/blob/31e05853/slider-funtest/src/test/groovy/org/apache/slider/funtest/lifecycle/AppsThroughAgentIT.groovy ---------------------------------------------------------------------- diff --git a/slider-funtest/src/test/groovy/org/apache/slider/funtest/lifecycle/AppsThroughAgentIT.groovy b/slider-funtest/src/test/groovy/org/apache/slider/funtest/lifecycle/AppsThroughAgentIT.groovy index 234275a..0796355 100644 --- a/slider-funtest/src/test/groovy/org/apache/slider/funtest/lifecycle/AppsThroughAgentIT.groovy +++ b/slider-funtest/src/test/groovy/org/apache/slider/funtest/lifecycle/AppsThroughAgentIT.groovy @@ -75,6 +75,46 @@ implements FuntestProperties, Arguments, SliderExitCodes, SliderActions { assertComponentCount(COMMAND_LOGGER, 2, shell) assertSuccess(shell) + + // get log folders + shell = slider(EXIT_SUCCESS, + [ + ACTION_REGISTRY, + ARG_NAME, + APPLICATION_NAME, + ARG_LISTEXP]) + if(!containsString(shell, "container_log_dirs") || + !containsString(shell, "container_work_dirs")) { + logShell(shell) + assert fail("Should list default exports container_log_dirs or container_work_dirs") + } + + // get log folders + shell = slider(EXIT_SUCCESS, + [ + ACTION_REGISTRY, + ARG_NAME, + APPLICATION_NAME, + ARG_GETEXP, + "container_log_dirs"]) + if(!containsString(shell, "\"tag\":\"COMMAND_LOGGER\",\"level\":\"component\"", 2)) { + logShell(shell) + assert fail("Should list 2 entries for log folders") + } + + // get log folders + shell = slider(EXIT_SUCCESS, + [ + ACTION_REGISTRY, + ARG_NAME, + APPLICATION_NAME, + ARG_GETEXP, + "container_work_dirs"]) + if(!containsString(shell, "\"tag\":\"COMMAND_LOGGER\",\"level\":\"component\"", 2)) { + logShell(shell) + assert fail("Should list 2 entries for work folder") + } + assert isApplicationInState("RUNNING", APPLICATION_NAME), 'App is not running.' } }
