You are viewing a plain text version of this content. The canonical link for it is here.
Posted to commits@slider.apache.org by te...@apache.org on 2015/01/04 17:52:59 UTC

incubator-slider git commit: SLIDER-668 "slider package" CLI command to offer package management operations (Vasanth)

Repository: incubator-slider
Updated Branches:
  refs/heads/develop aa4299e38 -> 07a552630


SLIDER-668 "slider package" CLI command to offer package management operations (Vasanth)


Project: http://git-wip-us.apache.org/repos/asf/incubator-slider/repo
Commit: http://git-wip-us.apache.org/repos/asf/incubator-slider/commit/07a55263
Tree: http://git-wip-us.apache.org/repos/asf/incubator-slider/tree/07a55263
Diff: http://git-wip-us.apache.org/repos/asf/incubator-slider/diff/07a55263

Branch: refs/heads/develop
Commit: 07a55263065bf476d85780de8a700318cc292cbc
Parents: aa4299e
Author: tedyu <yu...@gmail.com>
Authored: Sun Jan 4 08:52:54 2015 -0800
Committer: tedyu <yu...@gmail.com>
Committed: Sun Jan 4 08:52:54 2015 -0800

----------------------------------------------------------------------
 .../org/apache/slider/client/SliderClient.java  | 158 ++++++++++-
 .../apache/slider/client/SliderClientAPI.java   |  12 +
 .../slider/common/params/ActionPackageArgs.java |  73 +++++
 .../apache/slider/common/params/Arguments.java  |   4 +
 .../apache/slider/common/params/ClientArgs.java |   8 +
 .../slider/common/params/SliderActions.java     |   1 +
 .../agent/actions/TestActionPackage.groovy      | 266 +++++++++++++++++++
 7 files changed, 521 insertions(+), 1 deletion(-)
----------------------------------------------------------------------


http://git-wip-us.apache.org/repos/asf/incubator-slider/blob/07a55263/slider-core/src/main/java/org/apache/slider/client/SliderClient.java
----------------------------------------------------------------------
diff --git a/slider-core/src/main/java/org/apache/slider/client/SliderClient.java b/slider-core/src/main/java/org/apache/slider/client/SliderClient.java
index 98bb36f..f7b558e 100644
--- a/slider-core/src/main/java/org/apache/slider/client/SliderClient.java
+++ b/slider-core/src/main/java/org/apache/slider/client/SliderClient.java
@@ -23,6 +23,7 @@ import com.google.common.base.Preconditions;
 
 import org.apache.commons.lang.StringUtils;
 import org.apache.hadoop.conf.Configuration;
+import org.apache.hadoop.fs.FileStatus;
 import org.apache.hadoop.fs.FileSystem;
 import org.apache.hadoop.fs.LocatedFileStatus;
 import org.apache.hadoop.fs.Path;
@@ -79,6 +80,7 @@ import org.apache.slider.common.params.ActionDiagnosticArgs;
 import org.apache.slider.common.params.ActionExistsArgs;
 import org.apache.slider.common.params.ActionInstallKeytabArgs;
 import org.apache.slider.common.params.ActionInstallPackageArgs;
+import org.apache.slider.common.params.ActionPackageArgs;
 import org.apache.slider.common.params.ActionAMSuicideArgs;
 import org.apache.slider.common.params.ActionCreateArgs;
 import org.apache.slider.common.params.ActionEchoArgs;
@@ -173,6 +175,7 @@ import java.util.Arrays;
 import java.util.Collection;
 import java.util.HashMap;
 import java.util.HashSet;
+import java.util.Iterator;
 import java.util.LinkedList;
 import java.util.List;
 import java.util.Locale;
@@ -376,7 +379,9 @@ public class SliderClient extends AbstractSliderLaunchedService implements RunSe
     int exitCode = EXIT_SUCCESS;
     String clusterName = serviceArgs.getClusterName();
     // actions
-    if (ACTION_INSTALL_PACKAGE.equals(action)) {
+    if (ACTION_PACKAGE.equals(action)) {
+      exitCode = actionPackage(serviceArgs.getActionPackageArgs());
+    } else if (ACTION_INSTALL_PACKAGE.equals(action)) {
       exitCode = actionInstallPkg(serviceArgs.getActionInstallPackageArgs());
     } else if (ACTION_INSTALL_KEYTAB.equals(action)) {
       exitCode = actionInstallKeytab(serviceArgs.getActionInstallKeytabArgs());
@@ -860,6 +865,157 @@ public class SliderClient extends AbstractSliderLaunchedService implements RunSe
   }
 
   @Override
+  public int actionPackage(ActionPackageArgs actionPackageInfo)
+      throws YarnException, IOException {
+    if (actionPackageInfo.install) {
+      return actionPackageInstall(actionPackageInfo);
+    }
+    if (actionPackageInfo.delete) {
+      return actionPackageDelete(actionPackageInfo);
+    }
+    if (actionPackageInfo.list) {
+      return actionPackageList();
+    }
+    if (actionPackageInfo.instances) {
+      return actionPackageInstances();
+    }
+    throw new BadCommandArgumentsException(
+        "Select valid package operation option");
+  }
+
+  private int actionPackageInstances() throws YarnException, IOException {
+    Map<String, Path> persistentInstances = sliderFileSystem
+        .listPersistentInstances();
+    if(persistentInstances.isEmpty()) {
+      log.info("No slider cluster specification available");
+      return EXIT_SUCCESS;
+    }
+    String pkgPathValue = sliderFileSystem
+        .buildPackageDirPath(StringUtils.EMPTY).toUri().getPath();
+    FileSystem fs = sliderFileSystem.getFileSystem();
+    Iterator<Map.Entry<String, Path>> instanceItr = persistentInstances
+        .entrySet().iterator();
+    log.info("List of application with its package name and path");
+    while(instanceItr.hasNext()) {
+      Map.Entry<String, Path> entry = instanceItr.next();
+      String clusterName = entry.getKey();
+      Path clusterPath = entry.getValue();
+      AggregateConf instanceDefinition = loadInstanceDefinitionUnresolved(
+          clusterName, clusterPath);
+      Path appDefPath = new Path(instanceDefinition.getAppConfOperations()
+          .getGlobalOptions().getMandatoryOption(AgentKeys.APP_DEF));
+      try {
+        if (appDefPath.toString().contains(pkgPathValue)
+            && fs.isFile(appDefPath)) {
+          String packageName = appDefPath.getParent().getName();
+          println("\t" + clusterName + "\t" + packageName + "\t"
+              + appDefPath.toString());
+        }
+      } catch(IOException e) {
+        if(log.isDebugEnabled()) {
+          log.debug(clusterName + " application definition path "
+              + appDefPath.toString() + " is not found.");
+        }
+      }
+    }
+    return EXIT_SUCCESS;
+  }
+
+  private int actionPackageList() throws IOException {
+    Path pkgPath = sliderFileSystem.buildPackageDirPath(StringUtils.EMPTY);
+    log.info("Package install path : " + pkgPath);
+    if (!sliderFileSystem.getFileSystem().isDirectory(pkgPath)) {
+      log.info("No package(s) installed");
+      return EXIT_SUCCESS;
+    }
+    FileStatus[] fileStatus = sliderFileSystem.getFileSystem().listStatus(
+        pkgPath);
+    boolean hasPackage = false;
+    StringBuilder sb = new StringBuilder();
+    sb.append("List of installed packages:\n");
+    for (FileStatus fstat : fileStatus) {
+      if (fstat.isDirectory()) {
+        sb.append("\t" + fstat.getPath().getName());
+        sb.append("\n");
+        hasPackage = true;
+      }
+    }
+    if (hasPackage) {
+      println(sb.toString());
+    } else {
+      log.info("No package(s) installed");
+    }
+    return EXIT_SUCCESS;
+  }
+
+  private int actionPackageInstall(ActionPackageArgs actionPackageArgs) throws
+      YarnException,
+      IOException {
+
+    Path srcFile = null;
+    if (StringUtils.isEmpty(actionPackageArgs.name)) {
+      throw new BadCommandArgumentsException(
+          "A valid application type name is required (e.g. HBASE).\n"
+              + CommonArgs.usage(serviceArgs, ACTION_PACKAGE));
+    }
+
+    if (StringUtils.isEmpty(actionPackageArgs.packageURI)) {
+      throw new BadCommandArgumentsException(
+          "A valid application package location required.");
+    } else {
+      File pkgFile = new File(actionPackageArgs.packageURI);
+      if (!pkgFile.exists() || pkgFile.isDirectory()) {
+        throw new BadCommandArgumentsException(
+            "Unable to access supplied pkg file at "
+                + pkgFile.getAbsolutePath());
+      } else {
+        srcFile = new Path(pkgFile.toURI());
+      }
+    }
+
+    Path pkgPath = sliderFileSystem.buildPackageDirPath(actionPackageArgs.name);
+    sliderFileSystem.getFileSystem().mkdirs(pkgPath);
+
+    Path fileInFs = new Path(pkgPath, srcFile.getName());
+    log.info("Installing package {} at {} and overwrite is {}.", srcFile,
+        fileInFs, actionPackageArgs.replacePkg);
+    if (sliderFileSystem.getFileSystem().exists(fileInFs)
+        && !actionPackageArgs.replacePkg) {
+      throw new BadCommandArgumentsException("Pkg exists at " +
+                                             fileInFs.toUri().toString() +
+                                             ". Use --replacepkg to overwrite.");
+    }
+
+    sliderFileSystem.getFileSystem().copyFromLocalFile(false,
+        actionPackageArgs.replacePkg, srcFile, fileInFs);
+    return EXIT_SUCCESS;
+  }
+
+  private int actionPackageDelete(ActionPackageArgs actionPackageArgs) throws
+      YarnException, IOException {
+    if (StringUtils.isEmpty(actionPackageArgs.name)) {
+      throw new BadCommandArgumentsException(
+          "A valid application type name is required (e.g. HBASE).\n"
+              + CommonArgs.usage(serviceArgs, ACTION_PACKAGE));
+    }
+
+    Path pkgPath = sliderFileSystem.buildPackageDirPath(actionPackageArgs.name);
+    if (!sliderFileSystem.getFileSystem().exists(pkgPath)) {
+      throw new BadCommandArgumentsException("Package does not exists at "
+          + pkgPath.toUri().toString());
+    }
+    log.info("Deleting package {} at {}.", actionPackageArgs.name, pkgPath);
+
+    if(sliderFileSystem.getFileSystem().delete(pkgPath, true)) {
+      log.info("Deleted package {} " + actionPackageArgs.name);
+      return EXIT_SUCCESS;
+    } else {
+      log.warn("Package deletion failed.");
+      return EXIT_NOT_FOUND;
+    }
+  }
+
+  @Override
   public int actionUpdate(String clustername,
       AbstractClusterBuildingActionArgs buildInfo) throws
       YarnException, IOException {

http://git-wip-us.apache.org/repos/asf/incubator-slider/blob/07a55263/slider-core/src/main/java/org/apache/slider/client/SliderClientAPI.java
----------------------------------------------------------------------
diff --git a/slider-core/src/main/java/org/apache/slider/client/SliderClientAPI.java b/slider-core/src/main/java/org/apache/slider/client/SliderClientAPI.java
index 9b0f10f..328ec46 100644
--- a/slider-core/src/main/java/org/apache/slider/client/SliderClientAPI.java
+++ b/slider-core/src/main/java/org/apache/slider/client/SliderClientAPI.java
@@ -33,6 +33,7 @@ import org.apache.slider.common.params.ActionFreezeArgs;
 import org.apache.slider.common.params.ActionInstallKeytabArgs;
 import org.apache.slider.common.params.ActionInstallPackageArgs;
 import org.apache.slider.common.params.ActionKeytabArgs;
+import org.apache.slider.common.params.ActionPackageArgs;
 import org.apache.slider.common.params.ActionKillContainerArgs;
 import org.apache.slider.common.params.ActionListArgs;
 import org.apache.slider.common.params.ActionRegistryArgs;
@@ -124,6 +125,17 @@ public interface SliderClientAPI extends Service {
       throws YarnException, IOException;
 
   /**
+   * Managing slider application package
+   *
+   * @param pkgInfo the arguments needed to upload, delete or list the package
+   * @throws YarnException Yarn problems
+   * @throws IOException other problems
+   * @throws BadCommandArgumentsException bad arguments.
+   */
+  int actionPackage(ActionPackageArgs pkgInfo)
+      throws YarnException, IOException;
+
+  /**
    * Update the cluster specification
    *
    * @param clustername cluster name

http://git-wip-us.apache.org/repos/asf/incubator-slider/blob/07a55263/slider-core/src/main/java/org/apache/slider/common/params/ActionPackageArgs.java
----------------------------------------------------------------------
diff --git a/slider-core/src/main/java/org/apache/slider/common/params/ActionPackageArgs.java b/slider-core/src/main/java/org/apache/slider/common/params/ActionPackageArgs.java
new file mode 100644
index 0000000..d5bd293
--- /dev/null
+++ b/slider-core/src/main/java/org/apache/slider/common/params/ActionPackageArgs.java
@@ -0,0 +1,73 @@
+/*
+ * 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.common.params;
+
+import com.beust.jcommander.Parameter;
+import com.beust.jcommander.Parameters;
+
+@Parameters(commandNames = {SliderActions.ACTION_PACKAGE},
+            commandDescription = SliderActions.DESCRIBE_ACTION_INSTALL_PACKAGE)
+
+public class ActionPackageArgs extends AbstractActionArgs {
+
+  @Override
+  public String getActionName() {
+    return SliderActions.ACTION_PACKAGE;
+  }
+
+  @Parameter(names = {ARG_PKGINSTALL},
+      description = "Install package operation")
+  public boolean install;
+
+  @Parameter(names = {ARG_PKGDELETE},
+      description = "Delete package operation")
+  public boolean delete;
+
+  @Parameter(names = {ARG_PKGLIST},
+      description = "List of package(s) installed")
+  public boolean list;
+
+  @Parameter(names = {ARG_PKGINSTANCES},
+      description = "Lists all application instances referring to package")
+  public boolean instances;
+
+  @Parameter(names = {ARG_PACKAGE},
+             description = "Path to app package on local disk")
+  public String packageURI;
+
+  @Parameter(names = {ARG_NAME},
+             description = "Package name")
+  public String name;
+
+  @Parameter(names = {ARG_REPLACE_PKG}, description = "Overwrite existing package")
+  public boolean replacePkg = false;
+
+  /**
+   * Get the min #of params expected
+   * @return the min number of params in the {@link #parameters} field
+   */
+  public int getMinParams() {
+    return 0;
+  }
+
+  @Override
+  public int getMaxParams() {
+    return 1;
+  }
+}

http://git-wip-us.apache.org/repos/asf/incubator-slider/blob/07a55263/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 002b77d..511d01e 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
@@ -80,6 +80,10 @@ public interface Arguments {
   String ARG_OVERWRITE = "--overwrite";
   String ARG_PACKAGE = "--package";
   String ARG_PATH = "--path";
+  String ARG_PKGDELETE = "--delete";
+  String ARG_PKGINSTALL = "--install";
+  String ARG_PKGINSTANCES = "--instances";
+  String ARG_PKGLIST = "--list";
   String ARG_PROVIDER = "--provider";
   String ARG_QUEUE = "--queue";
   String ARG_REPLACE_PKG = "--replacepkg";

http://git-wip-us.apache.org/repos/asf/incubator-slider/blob/07a55263/slider-core/src/main/java/org/apache/slider/common/params/ClientArgs.java
----------------------------------------------------------------------
diff --git a/slider-core/src/main/java/org/apache/slider/common/params/ClientArgs.java b/slider-core/src/main/java/org/apache/slider/common/params/ClientArgs.java
index a0f82e6..fc466a1 100644
--- a/slider-core/src/main/java/org/apache/slider/common/params/ClientArgs.java
+++ b/slider-core/src/main/java/org/apache/slider/common/params/ClientArgs.java
@@ -57,6 +57,7 @@ public class ClientArgs extends CommonArgs {
   private final ActionFreezeArgs actionFreezeArgs = new ActionFreezeArgs();
   private final ActionHelpArgs actionHelpArgs = new ActionHelpArgs();
   private final ActionInstallPackageArgs actionInstallPackageArgs = new ActionInstallPackageArgs();
+  private final ActionPackageArgs actionPackageArgs = new ActionPackageArgs();
   private final ActionInstallKeytabArgs actionInstallKeytabArgs = new ActionInstallKeytabArgs();
   private final ActionKeytabArgs actionKeytabArgs = new ActionKeytabArgs();
   private final ActionKillContainerArgs actionKillContainerArgs =
@@ -94,6 +95,7 @@ public class ClientArgs extends CommonArgs {
         actionFreezeArgs,
         actionHelpArgs,
         actionInstallPackageArgs,
+        actionPackageArgs,
         actionInstallKeytabArgs,
         actionKeytabArgs,
         actionKillContainerArgs,
@@ -142,6 +144,9 @@ public class ClientArgs extends CommonArgs {
   public ActionInstallPackageArgs getActionInstallPackageArgs() {
     return actionInstallPackageArgs; }
 
+  public ActionPackageArgs getActionPackageArgs() {
+    return actionPackageArgs; }
+
   public ActionInstallKeytabArgs getActionInstallKeytabArgs() {
     return actionInstallKeytabArgs; }
 
@@ -249,6 +254,9 @@ public class ClientArgs extends CommonArgs {
     } else if (SliderActions.ACTION_KEYTAB.equals(action)) {
       bindCoreAction(actionKeytabArgs);
 
+    } else if (SliderActions.ACTION_PACKAGE.equals(action)) {
+      bindCoreAction(actionPackageArgs);
+
     } else if (SliderActions.ACTION_INSTALL_KEYTAB.equals(action)) {
       bindCoreAction(actionInstallKeytabArgs);
 

http://git-wip-us.apache.org/repos/asf/incubator-slider/blob/07a55263/slider-core/src/main/java/org/apache/slider/common/params/SliderActions.java
----------------------------------------------------------------------
diff --git a/slider-core/src/main/java/org/apache/slider/common/params/SliderActions.java b/slider-core/src/main/java/org/apache/slider/common/params/SliderActions.java
index 3dee8ff..e58ad2d 100644
--- a/slider-core/src/main/java/org/apache/slider/common/params/SliderActions.java
+++ b/slider-core/src/main/java/org/apache/slider/common/params/SliderActions.java
@@ -46,6 +46,7 @@ public interface SliderActions {
   String ACTION_VERSION = "version";
   String ACTION_DIAGNOSTICS = "diagnostics";
   String ACTION_INSTALL_PACKAGE = "install-package";
+  String ACTION_PACKAGE = "package";
   String ACTION_INSTALL_KEYTAB = "install-keytab";
   String ACTION_KEYTAB = "keytab";
   String DESCRIBE_ACTION_AM_SUICIDE =

http://git-wip-us.apache.org/repos/asf/incubator-slider/blob/07a55263/slider-core/src/test/groovy/org/apache/slider/agent/actions/TestActionPackage.groovy
----------------------------------------------------------------------
diff --git a/slider-core/src/test/groovy/org/apache/slider/agent/actions/TestActionPackage.groovy b/slider-core/src/test/groovy/org/apache/slider/agent/actions/TestActionPackage.groovy
new file mode 100644
index 0000000..b0b832a
--- /dev/null
+++ b/slider-core/src/test/groovy/org/apache/slider/agent/actions/TestActionPackage.groovy
@@ -0,0 +1,266 @@
+/*
+ * 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.agent.actions
+
+import groovy.transform.CompileStatic
+import groovy.util.logging.Slf4j
+import org.apache.hadoop.yarn.conf.YarnConfiguration
+import org.apache.slider.agent.AgentMiniClusterTestBase
+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.main.ServiceLauncher
+import org.junit.Before
+import org.junit.Test
+
+/**
+ * Test case for Action package
+ */
+@CompileStatic
+@Slf4j
+
+class TestActionPackage extends AgentMiniClusterTestBase {
+
+  @Before
+  public void setup() {
+    super.setup()
+    createMiniCluster("", configuration, 1, false)
+  }
+
+  @Test
+  public void testPackageInstallFailsWithNoPackageName() throws Throwable {
+    try {
+      ServiceLauncher launcher = launchClientAgainstMiniMR(
+          //config includes RM binding info
+          new YarnConfiguration(miniCluster.config),
+          //varargs list of command line params
+          [
+              SliderActions.ACTION_PACKAGE,
+              Arguments.ARG_PKGINSTALL
+          ],
+      )
+      fail("expected an exception, got a status code " + launcher.serviceExitCode)
+    } catch (BadCommandArgumentsException e) {
+      assert e.message.contains("A valid application type name is required (e.g. HBASE)")
+    }
+  }
+
+  @Test
+  public void testPackageInstallFailsWithNoPackagePath() throws Throwable {
+    try {
+      ServiceLauncher launcher = launchClientAgainstMiniMR(
+          //config includes RM binding info
+          new YarnConfiguration(miniCluster.config),
+          //varargs list of command line params
+          [
+              SliderActions.ACTION_PACKAGE,
+              Arguments.ARG_PKGINSTALL,
+              Arguments.ARG_NAME, "hbase"
+          ],
+      )
+      fail("expected an exception, got a status code " + launcher.serviceExitCode)
+    } catch (BadCommandArgumentsException e) {
+      assert e.message.contains("A valid application package location required")
+    }
+  }
+
+  @Test
+  public void testPackageInstallFailsWithInvalidPackagePath() throws Throwable {
+    try {
+      ServiceLauncher launcher = launchClientAgainstMiniMR(
+          //config includes RM binding info
+          new YarnConfiguration(miniCluster.config),
+          //varargs list of command line params
+          [
+              SliderActions.ACTION_PACKAGE,
+              Arguments.ARG_PKGINSTALL,
+              Arguments.ARG_NAME, "hbase",
+              Arguments.ARG_PACKAGE, "src/test/resources/log4j.properties",
+          ],
+      )
+      launcher = launchClientAgainstMiniMR(
+          //config includes RM binding info
+          new YarnConfiguration(miniCluster.config),
+          //varargs list of command line params
+          [
+              SliderActions.ACTION_PACKAGE,
+              Arguments.ARG_PKGINSTALL,
+              Arguments.ARG_NAME, "hbase",
+              Arguments.ARG_PACKAGE, "src/test/resources/log4j.properties",
+          ],
+      )
+      fail("expected an exception, got a status code " + launcher.serviceExitCode)
+    } catch (BadCommandArgumentsException e) {
+      assert e.message.contains("Use --replacepkg to overwrite")
+    }
+  }
+
+  @Test
+  public void testPackageInstallFailsWithNeedingReplaceFlag() throws Throwable {
+    try {
+      ServiceLauncher launcher = launchClientAgainstMiniMR(
+          //config includes RM binding info
+          new YarnConfiguration(miniCluster.config),
+          //varargs list of command line params
+          [
+              SliderActions.ACTION_PACKAGE,
+              Arguments.ARG_PKGINSTALL,
+              Arguments.ARG_NAME, "hbase",
+              Arguments.ARG_PACKAGE, "unlikely_to_be_a_file_path",
+          ],
+      )
+      fail("expected an exception, got a status code " + launcher.serviceExitCode)
+    } catch (BadCommandArgumentsException e) {
+      assert e.message.contains("Unable to access supplied pkg file at")
+    }
+  }
+
+  @Test
+  public void testPackageInstallWithReplace() throws Throwable {
+    try {
+      ServiceLauncher launcher = launchClientAgainstMiniMR(
+          //config includes RM binding info
+          new YarnConfiguration(miniCluster.config),
+          //varargs list of command line params
+          [
+              SliderActions.ACTION_PACKAGE,
+              Arguments.ARG_PKGINSTALL,
+              Arguments.ARG_NAME, "hbase",
+              Arguments.ARG_PACKAGE, "src/test/resources/log4j.properties",
+          ],
+      )
+      launcher = launchClientAgainstMiniMR(
+          //config includes RM binding info
+          new YarnConfiguration(miniCluster.config),
+          //varargs list of command line params
+          [
+              SliderActions.ACTION_PACKAGE,
+              Arguments.ARG_PKGINSTALL,
+              Arguments.ARG_NAME, "hbase",
+              Arguments.ARG_PACKAGE, "src/test/resources/log4j.properties",
+              Arguments.ARG_REPLACE_PKG
+          ],
+      )
+    } catch (BadCommandArgumentsException e) {
+      log.info(e.message)
+    }
+  }
+
+  @Test
+  public void testPackageList() throws Throwable {
+    try {
+      ServiceLauncher launcher = launchClientAgainstMiniMR(
+          //config includes RM binding info
+          new YarnConfiguration(miniCluster.config),
+          //varargs list of command line params
+          [
+              SliderActions.ACTION_PACKAGE,
+              Arguments.ARG_PKGINSTALL,
+              Arguments.ARG_PKGLIST
+          ],
+      )
+    } catch (BadCommandArgumentsException e) {
+      log.info(e.message)
+    }
+  }
+
+  @Test
+  public void testPackageInstances() throws Throwable {
+    try {
+      ServiceLauncher launcher = launchClientAgainstMiniMR(
+          //config includes RM binding info
+          new YarnConfiguration(miniCluster.config),
+          //varargs list of command line params
+          [
+              SliderActions.ACTION_PACKAGE,
+              Arguments.ARG_PKGINSTALL,
+              Arguments.ARG_PKGINSTANCES
+          ],
+      )
+    } catch (BadCommandArgumentsException e) {
+      log.info(e.message)
+    }
+  }
+
+  @Test
+  public void testPackageDelete() throws Throwable {
+    try {
+      ServiceLauncher launcher = launchClientAgainstMiniMR(
+          //config includes RM binding info
+          new YarnConfiguration(miniCluster.config),
+          //varargs list of command line params
+          [
+              SliderActions.ACTION_PACKAGE,
+              Arguments.ARG_PKGINSTALL,
+              Arguments.ARG_NAME, "storm",
+              Arguments.ARG_PACKAGE, "src/test/resources/log4j.properties",
+          ],
+      )
+      launcher = launchClientAgainstMiniMR(
+          //config includes RM binding info
+          new YarnConfiguration(miniCluster.config),
+          //varargs list of command line params
+          [
+              SliderActions.ACTION_PACKAGE,
+              Arguments.ARG_PKGDELETE,
+              Arguments.ARG_NAME, "storm"
+          ],
+      )
+    } catch (BadCommandArgumentsException e) {
+      fail("Should not throw exception:" + e.getMessage())
+    }
+  }
+
+  @Test
+  public void testPackageDeleteFail() throws Throwable {
+    try {
+      ServiceLauncher launcher = launchClientAgainstMiniMR(
+          //config includes RM binding info
+          new YarnConfiguration(miniCluster.config),
+          //varargs list of command line params
+          [
+              SliderActions.ACTION_PACKAGE,
+              Arguments.ARG_PKGDELETE,
+              Arguments.ARG_NAME, "hbase1"
+          ],
+      )
+      fail("expected an exception, got a status code " + launcher.serviceExitCode)
+    } catch (BadCommandArgumentsException e) {
+      assert e.message.contains("Package does not exists")
+    }
+  }
+
+  @Test
+  public void testPackageDeleteFailsWithNoPackageName() throws Throwable {
+    try {
+      ServiceLauncher launcher = launchClientAgainstMiniMR(
+          //config includes RM binding info
+          new YarnConfiguration(miniCluster.config),
+          //varargs list of command line params
+          [
+              SliderActions.ACTION_PACKAGE,
+              Arguments.ARG_PKGDELETE
+          ],
+      )
+      fail("expected an exception, got a status code " + launcher.serviceExitCode)
+    } catch (BadCommandArgumentsException e) {
+      assert e.message.contains("A valid application type name is required (e.g. HBASE)")
+    }
+  }
+}