SLIDER 40. AgentProviderService should read metainfo and get the script path from the metainfo file

git-svn-id: https://svn.apache.org/repos/asf/incubator/slider/trunk@1593469 13f79535-47bb-0310-9956-ffa450edef68
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 10e1a1c..1f0f8cf 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
@@ -45,8 +45,10 @@
 import org.apache.slider.providers.ProviderCore;
 import org.apache.slider.providers.ProviderRole;
 import org.apache.slider.providers.ProviderUtils;
+import org.apache.slider.providers.agent.application.metadata.Component;
 import org.apache.slider.providers.agent.application.metadata.Metainfo;
 import org.apache.slider.providers.agent.application.metadata.MetainfoParser;
+import org.apache.slider.providers.agent.application.metadata.Service;
 import org.apache.slider.server.appmaster.state.StateAccessForProviders;
 import org.apache.slider.server.appmaster.web.rest.agent.AgentCommandType;
 import org.apache.slider.server.appmaster.web.rest.agent.AgentRestOperations;
@@ -94,6 +96,7 @@
   private Map<String, ComponentInstanceState> componentStatuses = new HashMap<String, ComponentInstanceState>();
   private Map<String, List<String>> roleHostMapping = new HashMap<String, List<String>>();
   private AtomicInteger taskId = new AtomicInteger(0);
+  private Metainfo metainfo = null;
 
   public AgentProviderService() {
     super("AgentProviderService");
@@ -138,6 +141,18 @@
       IOException,
       SliderException {
 
+    String appDef = instanceDefinition.getAppConfOperations().
+        getGlobalOptions().getMandatoryOption(AgentKeys.APP_DEF);
+
+    // No need to synchronize as there is low chance of multiple simultaneous reads
+    if (metainfo == null) {
+      metainfo = getApplicationMetainfo(fileSystem, appDef);
+      if(metainfo == null) {
+        log.error("metainfo.xml is unavailable or malformed at {}.", appDef);
+        throw new SliderException("metainfo.xml is required in app package.");
+      }
+    }
+
     this.instanceDefinition = instanceDefinition;
     log.info("Build launch context for Agent");
     log.debug(instanceDefinition.toString());
@@ -171,8 +186,6 @@
     }
 
     log.info("Using {} for agent.", scriptPath);
-    String appDef = instanceDefinition.getAppConfOperations().
-        getGlobalOptions().getMandatoryOption(AgentKeys.APP_DEF);
     LocalResource appDefRes = fileSystem.createAmResource(
         fileSystem.getFileSystem().resolvePath(new Path(appDef)),
         LocalResourceType.ARCHIVE);
@@ -217,10 +230,20 @@
                               getClusterInfoPropertyValue(OptionKeys.APPLICATION_NAME)));
   }
 
-  private Metainfo getApplicationMetainfo(SliderFileSystem fileSystem,
-                                      String appDef) throws IOException {
+  protected Metainfo getMetainfo() {
+    return this.metainfo;
+  }
+
+  protected Metainfo getApplicationMetainfo(SliderFileSystem fileSystem,
+                                            String appDef) throws IOException {
+    log.info("Reading metainfo at {}", appDef);
     InputStream metainfoStream = SliderUtils.getApplicationResourceInputStream(
         fileSystem.getFileSystem(), new Path(appDef), "metainfo.xml");
+    if(metainfoStream == null) {
+      log.error("metainfo.xml is unavailable at {}.", appDef);
+      throw new IOException("metainfo.xml is required in app package.");
+    }
+
     Metainfo metainfo = new MetainfoParser().parse(metainfoStream);
 
     return metainfo;
@@ -345,9 +368,12 @@
     String roleName = getRoleName(label);
     String containerId = getContainerId(label);
     StateAccessForProviders accessor = getStateAccessor();
-    String scriptPath;
-    scriptPath = accessor.getInstanceDefinitionSnapshot().
-        getAppConfOperations().getComponentOpt(roleName, AgentKeys.COMPONENT_SCRIPT, null);
+    String scriptPath = null;
+
+    scriptPath = getScriptPathFromMetainfo(roleName);
+
+    //scriptPath = accessor.getInstanceDefinitionSnapshot().
+    //    getAppConfOperations().getComponentOpt(roleName, AgentKeys.COMPONENT_SCRIPT, null);
     if (scriptPath == null) {
       log.error("role.script is unavailable for " + roleName + ". Commands will not be sent.");
       return response;
@@ -396,6 +422,22 @@
     return response;
   }
 
+  protected String getScriptPathFromMetainfo(String roleName) {
+    String scriptPath = null;
+    List<Service> services = getMetainfo().getServices();
+    if (services.size() != 1) {
+      log.error("Malformed app definition: Expect only one service in the metainfo.xml");
+    }
+    Service service = services.get(0);
+    for (Component component : service.getComponents()) {
+      if (component.getName().equals(roleName)) {
+        scriptPath = component.getCommandScript().getScript();
+        break;
+      }
+    }
+    return scriptPath;
+  }
+
   private String getRoleName(String label) {
     return label.substring(label.indexOf(LABEL_MAKER) + LABEL_MAKER.length());
   }
diff --git a/slider-core/src/main/java/org/apache/slider/providers/agent/application/metadata/CommandScript.java b/slider-core/src/main/java/org/apache/slider/providers/agent/application/metadata/CommandScript.java
index 74f0257..612322f 100644
--- a/slider-core/src/main/java/org/apache/slider/providers/agent/application/metadata/CommandScript.java
+++ b/slider-core/src/main/java/org/apache/slider/providers/agent/application/metadata/CommandScript.java
@@ -24,6 +24,10 @@
   String scriptType;
   long timeout;
 
+  public CommandScript() {
+
+  }
+
   public String getScript() {
     return script;
   }
@@ -48,7 +52,14 @@
     this.timeout = timeout;
   }
 
-  public CommandScript() {
-
+  @Override
+  public String toString() {
+    final StringBuilder sb =
+        new StringBuilder("{");
+    sb.append(",\n\"script\": ").append(script);
+    sb.append(",\n\"scriptType\": ").append(scriptType);
+    sb.append(",\n\"timeout\" :").append(timeout);
+    sb.append('}');
+    return sb.toString();
   }
 }
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 391a4ee..d34a223 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
@@ -50,4 +50,15 @@
   public void addCommandScript(CommandScript commandScript) {
     this.commandScript = commandScript;
   }
+
+  @Override
+  public String toString() {
+    final StringBuilder sb =
+        new StringBuilder("{");
+    sb.append(",\n\"name\": ").append(name);
+    sb.append(",\n\"category\": ").append(category);
+    sb.append(",\n\"commandScript\" :").append(commandScript);
+    sb.append('}');
+    return sb.toString();
+  }
 }
diff --git a/slider-core/src/main/java/org/apache/slider/providers/agent/application/metadata/Service.java b/slider-core/src/main/java/org/apache/slider/providers/agent/application/metadata/Service.java
index de738dd..2067bee 100644
--- a/slider-core/src/main/java/org/apache/slider/providers/agent/application/metadata/Service.java
+++ b/slider-core/src/main/java/org/apache/slider/providers/agent/application/metadata/Service.java
@@ -82,4 +82,20 @@
   public List<OSSpecific> getOSSpecifics() {
     return osSpecifics;
   }
+
+  @Override
+  public String toString() {
+    final StringBuilder sb =
+        new StringBuilder("{");
+    sb.append(",\n\"name\": ").append(name);
+    sb.append(",\n\"comment\": ").append(comment);
+    sb.append(",\n\"version\" :").append(version);
+    sb.append(",\n\"components\" : {");
+    for (Component component : components) {
+      sb.append("\n").append(component);
+    }
+    sb.append("\n},");
+    sb.append('}');
+    return sb.toString();
+  }
 }
diff --git a/slider-core/src/test/app_packages/test_command_log/appConfig.json b/slider-core/src/test/app_packages/test_command_log/appConfig.json
index 02ee4d4..0bace19 100644
--- a/slider-core/src/test/app_packages/test_command_log/appConfig.json
+++ b/slider-core/src/test/app_packages/test_command_log/appConfig.json
@@ -4,7 +4,7 @@
     },
     "global": {
         "agent.conf": "/slider/agent/conf/agent.ini",
-        "application.def": "/slider/cmd_log_app_pkg.tar",
+        "application.def": "/slider/cmd_log_app_pkg.zip",
         "config_types": "cl-site",
         "java_home": "/usr/jdk64/jdk1.7.0_45",
         "package_list": "files/command_log_10.tar",
@@ -19,7 +19,6 @@
     },
     "components": {
         "COMMAND_LOGGER": {
-            "role.script": "scripts/cl.py"
         },
         "slider-appmaster": {
             "jvm.heapsize": "256M"
diff --git a/slider-core/src/test/app_packages/test_command_log/cmd_log_app_pkg.tar b/slider-core/src/test/app_packages/test_command_log/cmd_log_app_pkg.tar
deleted file mode 100644
index 9dd165e..0000000
--- a/slider-core/src/test/app_packages/test_command_log/cmd_log_app_pkg.tar
+++ /dev/null
Binary files differ
diff --git a/slider-core/src/test/app_packages/test_command_log/cmd_log_app_pkg.zip b/slider-core/src/test/app_packages/test_command_log/cmd_log_app_pkg.zip
new file mode 100644
index 0000000..e44907a
--- /dev/null
+++ b/slider-core/src/test/app_packages/test_command_log/cmd_log_app_pkg.zip
Binary files differ
diff --git a/slider-core/src/test/groovy/org/apache/slider/providers/agent/TestAgentAMManagementWS.groovy b/slider-core/src/test/groovy/org/apache/slider/providers/agent/TestAgentAMManagementWS.groovy
index 48cb2dd..de20e10 100644
--- a/slider-core/src/test/groovy/org/apache/slider/providers/agent/TestAgentAMManagementWS.groovy
+++ b/slider-core/src/test/groovy/org/apache/slider/providers/agent/TestAgentAMManagementWS.groovy
@@ -58,7 +58,7 @@
         false)
     Map<String, Integer> roles = [:]
     File slider_core = new File(new File(".").absoluteFile, "src/test/python");
-    String app_def = "appdef_1.tar"
+    String app_def = "appdef_1.zip"
     File app_def_path = new File(slider_core, app_def)
     String agt_ver = "version"
     File agt_ver_path = new File(slider_core, agt_ver)
diff --git a/slider-core/src/test/groovy/org/apache/slider/providers/agent/TestAgentEcho.groovy b/slider-core/src/test/groovy/org/apache/slider/providers/agent/TestAgentEcho.groovy
index 908a5ff..1072ebe 100644
--- a/slider-core/src/test/groovy/org/apache/slider/providers/agent/TestAgentEcho.groovy
+++ b/slider-core/src/test/groovy/org/apache/slider/providers/agent/TestAgentEcho.groovy
@@ -57,7 +57,7 @@
     File slider_core = new File(new File(".").absoluteFile, "src/test/python");
     String echo_py = "echo.py"
     File echo_py_path = new File(slider_core, echo_py)
-    String app_def = "appdef_1.tar"
+    String app_def = "appdef_1.zip"
     File app_def_path = new File(slider_core, app_def)
     String agt_ver = "version"
     File agt_ver_path = new File(slider_core, agt_ver)
diff --git a/slider-core/src/test/groovy/org/apache/slider/providers/agent/TestBuildBasicAgent.groovy b/slider-core/src/test/groovy/org/apache/slider/providers/agent/TestBuildBasicAgent.groovy
index f1c1493..2b2ccaa 100644
--- a/slider-core/src/test/groovy/org/apache/slider/providers/agent/TestBuildBasicAgent.groovy
+++ b/slider-core/src/test/groovy/org/apache/slider/providers/agent/TestBuildBasicAgent.groovy
@@ -46,7 +46,7 @@
 
   private static class TestResources {
     static File slider_core = new File(new File(".").absoluteFile, "src/test/python");
-    static String app_def = "appdef_1.tar"
+    static String app_def = "appdef_1.zip"
     static File app_def_path = new File(slider_core, app_def)
     static String agt_conf = "agent.ini"
     static File agt_conf_path = new File(slider_core, agt_conf)
diff --git a/slider-core/src/test/groovy/org/apache/slider/registry/curator/TestRegistryRestResources.groovy b/slider-core/src/test/groovy/org/apache/slider/registry/curator/TestRegistryRestResources.groovy
index 804080f..4dba0bd 100644
--- a/slider-core/src/test/groovy/org/apache/slider/registry/curator/TestRegistryRestResources.groovy
+++ b/slider-core/src/test/groovy/org/apache/slider/registry/curator/TestRegistryRestResources.groovy
@@ -75,7 +75,7 @@
         false)
     Map<String, Integer> roles = [:]
     File slider_core = new File(new File(".").absoluteFile, "src/test/python");
-    String app_def = "appdef_1.tar"
+    String app_def = "appdef_1.zip"
     File app_def_path = new File(slider_core, app_def)
     String agt_ver = "version"
     File agt_ver_path = new File(slider_core, agt_ver)
diff --git a/slider-core/src/test/java/org/apache/slider/providers/agent/AgentProviderServiceTest.java b/slider-core/src/test/java/org/apache/slider/providers/agent/AgentProviderServiceTest.java
index 862fea6..0a6a4b9 100644
--- a/slider-core/src/test/java/org/apache/slider/providers/agent/AgentProviderServiceTest.java
+++ b/slider-core/src/test/java/org/apache/slider/providers/agent/AgentProviderServiceTest.java
@@ -36,6 +36,10 @@
 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.providers.agent.application.metadata.Component;
+import org.apache.slider.providers.agent.application.metadata.Metainfo;
+import org.apache.slider.providers.agent.application.metadata.MetainfoParser;
+import org.apache.slider.providers.agent.application.metadata.Service;
 import org.apache.slider.server.appmaster.model.mock.MockContainerId;
 import org.apache.slider.server.appmaster.model.mock.MockFileSystem;
 import org.apache.slider.server.appmaster.model.mock.MockNodeId;
@@ -50,8 +54,11 @@
 import org.slf4j.Logger;
 import org.slf4j.LoggerFactory;
 
+import java.io.ByteArrayInputStream;
 import java.io.IOException;
+import java.io.InputStream;
 import java.util.HashMap;
+import java.util.List;
 import java.util.Map;
 
 import static org.easymock.EasyMock.anyObject;
@@ -59,6 +66,7 @@
 import static org.easymock.EasyMock.expect;
 import static org.easymock.EasyMock.replay;
 import static org.mockito.Matchers.any;
+import static org.mockito.Matchers.anyString;
 import static org.mockito.Mockito.doNothing;
 import static org.mockito.Mockito.doReturn;
 import static org.mockito.Mockito.eq;
@@ -87,6 +95,7 @@
     Container container = createNiceMock(Container.class);
     String role = "HBASE_MASTER";
     SliderFileSystem sliderFileSystem = createNiceMock(SliderFileSystem.class);
+    ContainerLauncher launcher = createNiceMock(ContainerLauncher.class);
     Path generatedConfPath = new Path(".", "test");
     MapOperations resourceComponent = new MapOperations();
     MapOperations appComponent = new MapOperations();
@@ -95,7 +104,7 @@
     expect(sliderFileSystem.getFileSystem())
         .andReturn(new FilterFileSystem(mockFs)).anyTimes();
     expect(sliderFileSystem.createAmResource(anyObject(Path.class),
-                                           anyObject(LocalResourceType.class)))
+                                             anyObject(LocalResourceType.class)))
         .andReturn(createNiceMock(LocalResource.class)).anyTimes();
     expect(container.getId()).andReturn(new MockContainerId(1)).anyTimes();
     expect(container.getNodeId()).andReturn(new MockNodeId("localhost")).anyTimes();
@@ -103,6 +112,8 @@
 
     AgentProviderService mockAps = Mockito.spy(aps);
     doReturn(access).when(mockAps).getStateAccessor();
+    doReturn("scripts/hbase_master.py").when(mockAps).getScriptPathFromMetainfo(anyString());
+    doReturn(new Metainfo()).when(mockAps).getApplicationMetainfo(any(SliderFileSystem.class), anyString());
 
     try {
       doNothing().when(mockAps).addInstallCommand(
@@ -128,11 +139,11 @@
     replay(access, ctx, container, sliderFileSystem);
 
     try {
-      mockAps.buildContainerLaunchContext(createNiceMock(ContainerLauncher.class),
-          instanceDefinition,
+      mockAps.buildContainerLaunchContext(launcher,
+                                          instanceDefinition,
                                           container,
                                           role,
-          sliderFileSystem,
+                                          sliderFileSystem,
                                           generatedConfPath,
                                           resourceComponent,
                                           appComponent,
@@ -163,11 +174,100 @@
     aps.setRoleHostMapping("FIRST_ROLE", "FIRST_HOST");
     aps.setRoleHostMapping("SECOND_ROLE", "SECOND_HOST");
     aps.setRoleHostMapping("SECOND_ROLE", "THIRD_HOST");
-    Map<String,String> tokens = new HashMap<String, String>();
+    Map<String, String> tokens = new HashMap<String, String>();
     aps.addRoleRelatedTokens(tokens);
     TestCase.assertEquals(2, tokens.size());
     TestCase.assertEquals("FIRST_HOST", tokens.get("${FIRST_ROLE_HOST}"));
     TestCase.assertEquals("SECOND_HOST,THIRD_HOST", tokens.get("${SECOND_ROLE_HOST}"));
     aps.close();
   }
+
+  @Test
+  public void testMetainfoParsing() throws Exception {
+    String metainfo_1_str = "<metainfo>\n"
+                            + "  <schemaVersion>2.0</schemaVersion>\n"
+                            + "  <services>\n"
+                            + "    <service>\n"
+                            + "      <name>HBASE</name>\n"
+                            + "      <comment>\n"
+                            + "        Apache HBase\n"
+                            + "      </comment>\n"
+                            + "      <version>0.96.0.2.1.1</version>\n"
+                            + "      <type>YARN-APP</type>\n"
+                            + "      <minHadoopVersion>2.1.0</minHadoopVersion>\n"
+                            + "      <components>\n"
+                            + "        <component>\n"
+                            + "          <name>HBASE_MASTER</name>\n"
+                            + "          <category>MASTER</category>\n"
+                            + "          <minInstanceCount>1</minInstanceCount>\n"
+                            + "          <maxInstanceCount>2</maxInstanceCount>\n"
+                            + "          <commandScript>\n"
+                            + "            <script>scripts/hbase_master.py</script>\n"
+                            + "            <scriptType>PYTHON</scriptType>\n"
+                            + "            <timeout>600</timeout>\n"
+                            + "          </commandScript>\n"
+                            + "        </component>\n"
+                            + "        <component>\n"
+                            + "          <name>HBASE_REGIONSERVER</name>\n"
+                            + "          <category>SLAVE</category>\n"
+                            + "          <minInstanceCount>1</minInstanceCount>\n"
+                            + "          <commandScript>\n"
+                            + "            <script>scripts/hbase_regionserver.py</script>\n"
+                            + "            <scriptType>PYTHON</scriptType>\n"
+                            + "          </commandScript>\n"
+                            + "        </component>\n"
+                            + "      </components>\n"
+                            + "      <osSpecifics>\n"
+                            + "        <osSpecific>\n"
+                            + "          <osType>any</osType>\n"
+                            + "          <packages>\n"
+                            + "            <package>\n"
+                            + "              <type>tarball</type>\n"
+                            + "              <name>files/hbase-0.96.1-hadoop2-bin.tar.gz</name>\n"
+                            + "            </package>\n"
+                            + "          </packages>\n"
+                            + "        </osSpecific>\n"
+                            + "      </osSpecifics>\n"
+                            + "    </service>\n"
+                            + "  </services>\n"
+                            + "</metainfo>";
+
+    InputStream metainfo_1 = new ByteArrayInputStream(metainfo_1_str.getBytes());
+    Metainfo metainfo = new MetainfoParser().parse(metainfo_1);
+    assert metainfo.getServices().size() == 1;
+    Service service = metainfo.getServices().get(0);
+    log.info("Service: " + service.toString());
+    assert service.getName().equals("HBASE");
+    assert service.getComponents().size() == 2;
+    List<Component> components = service.getComponents();
+    for (Component component : components) {
+      if (component.getName().equals("HBASE_MASTER")) {
+        assert component.getCommandScript().getScript().equals("scripts/hbase_master.py");
+        assert component.getCategory().equals("MASTER");
+      }
+      if (component.getName().equals("HBASE_REGIONSERVER")) {
+        assert component.getCommandScript().getScript().equals("scripts/hbase_regionserver.py");
+        assert component.getCategory().equals("SLAVE");
+      }
+    }
+
+    AgentProviderService aps = new AgentProviderService();
+    AgentProviderService mockAps = Mockito.spy(aps);
+    doReturn(metainfo).when(mockAps).getMetainfo();
+    String scriptPath = mockAps.getScriptPathFromMetainfo("HBASE_MASTER");
+    assert scriptPath.equals("scripts/hbase_master.py");
+
+    String metainfo_1_str_bad = "<metainfo>\n"
+                                + "  <schemaVersion>2.0</schemaVersion>\n"
+                                + "  <services>\n"
+                                + "    <service>\n"
+                                + "      <name>HBASE</name>\n"
+                                + "      <comment>\n"
+                                + "        Apache HBase\n"
+                                + "      </comment>\n";
+
+    metainfo_1 = new ByteArrayInputStream(metainfo_1_str_bad.getBytes());
+    metainfo = new MetainfoParser().parse(metainfo_1);
+    assert metainfo == null;
+  }
 }
diff --git a/slider-core/src/test/python/appdef_1.tar b/slider-core/src/test/python/appdef_1.tar
deleted file mode 100644
index 9340ea3..0000000
--- a/slider-core/src/test/python/appdef_1.tar
+++ /dev/null
Binary files differ
diff --git a/slider-core/src/test/python/appdef_1.zip b/slider-core/src/test/python/appdef_1.zip
new file mode 100644
index 0000000..6ee6af4
--- /dev/null
+++ b/slider-core/src/test/python/appdef_1.zip
Binary files differ
diff --git a/slider-core/src/test/python/metainfo.xml b/slider-core/src/test/python/metainfo.xml
new file mode 100644
index 0000000..beefd9c
--- /dev/null
+++ b/slider-core/src/test/python/metainfo.xml
@@ -0,0 +1,57 @@
+<?xml version="1.0"?>
+<!--
+   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.
+-->
+<metainfo>
+  <schemaVersion>2.0</schemaVersion>
+  <services>
+    <service>
+      <name>ECHO</name>
+      <comment>
+        Echo
+      </comment>
+      <version>0.1</version>
+      <type>YARN-APP</type>
+      <minHadoopVersion>2.1.0</minHadoopVersion>
+      <components>
+        <component>
+          <name>echo</name>
+          <category>MASTER</category>
+          <minInstanceCount>1</minInstanceCount>
+          <maxInstanceCount>2</maxInstanceCount>
+          <commandScript>
+            <script>echo.py</script>
+            <scriptType>PYTHON</scriptType>
+            <timeout>600</timeout>
+          </commandScript>
+        </component>
+      </components>
+      <osSpecifics>
+        <osSpecific>
+          <osType>any</osType>
+          <packages>
+            <package>
+              <type>tarball</type>
+              <name>files/echo.tar.gz</name>
+            </package>
+          </packages>
+        </osSpecific>
+      </osSpecifics>
+
+    </service>
+  </services>
+</metainfo>
+
diff --git a/slider-funtest/src/test/groovy/org/apache/slider/funtest/lifecycle/AgentCommandTestBase.groovy b/slider-funtest/src/test/groovy/org/apache/slider/funtest/lifecycle/AgentCommandTestBase.groovy
index 1f22d3a..daf091c 100644
--- a/slider-funtest/src/test/groovy/org/apache/slider/funtest/lifecycle/AgentCommandTestBase.groovy
+++ b/slider-funtest/src/test/groovy/org/apache/slider/funtest/lifecycle/AgentCommandTestBase.groovy
@@ -38,7 +38,7 @@
   
   protected static String APP_RESOURCE = "../slider-core/src/test/app_packages/test_command_log/resources.json"
   protected static String APP_TEMPLATE = "../slider-core/src/test/app_packages/test_command_log/appConfig.json"
-  protected static String APP_PKG = "../slider-core/src/test/app_packages/test_command_log/cmd_log_app_pkg.tar"
+  protected static String APP_PKG = "../slider-core/src/test/app_packages/test_command_log/cmd_log_app_pkg.zip"
   protected static String AGENT_CONF = "../slider-agent/conf/agent.ini"
   protected static final File LOCAL_SLIDER_AGENT_TARGZ
   protected static final File LOCAL_APP_PKZ
@@ -69,8 +69,8 @@
 
     // Upload the app pkg
     assume(LOCAL_APP_PKZ.exists(), "App pkg not found at $LOCAL_APP_PKZ")
-    appPkgPath = new Path(clusterFS.homeDirectory, "/slider/cmd_log_app_pkg.tar")
-    Path localAppPkg = new Path(LOCAL_SLIDER_AGENT_TARGZ.toURI());
+    appPkgPath = new Path(clusterFS.homeDirectory, "/slider/cmd_log_app_pkg.zip")
+    Path localAppPkg = new Path(LOCAL_APP_PKZ.toURI());
     clusterFS.copyFromLocalFile(false, true, localAppPkg, appPkgPath)
 
     // Upload the agent.ini