This is an automated email from the ASF dual-hosted git repository.

vrozov pushed a commit to branch master
in repository https://gitbox.apache.org/repos/asf/apex-core.git


The following commit(s) were added to refs/heads/master by this push:
     new 10d22df  APEXCORE-626 shutdown-app in cli supports app-name as argument
10d22df is described below

commit 10d22dff4ba4648e5a7aad4bc9ed31b26d6fd855
Author: Florian Schmidt <[email protected]>
AuthorDate: Mon Jul 24 15:41:47 2017 -0700

    APEXCORE-626 shutdown-app in cli supports app-name as argument
    
    The command shutdown-app now supports not only the app-id but also the
    app name as an argument.
    In case of multiple appIds or appNames the shutdown-app command follows
    a "best effort" approach, meaning that it tries to shutdown each app
    individually and prints an error message if the shutdown of an app
    fails, but continues to shutdown all the others.
    
    This commit adds the described functionality, documentation to the cli
    help messages and a test to ensure correct best effort behaviour.
---
 .../java/com/datatorrent/stram/cli/ApexCli.java    | 106 +++++++++------
 .../stram/cli/ApexCliShutdownCommandTest.java      | 142 +++++++++++++++++++++
 2 files changed, 211 insertions(+), 37 deletions(-)

diff --git a/engine/src/main/java/com/datatorrent/stram/cli/ApexCli.java 
b/engine/src/main/java/com/datatorrent/stram/cli/ApexCli.java
index 1717ace..2451b10 100644
--- a/engine/src/main/java/com/datatorrent/stram/cli/ApexCli.java
+++ b/engine/src/main/java/com/datatorrent/stram/cli/ApexCli.java
@@ -34,11 +34,13 @@ import java.net.URLEncoder;
 import java.security.PrivilegedExceptionAction;
 import java.text.SimpleDateFormat;
 import java.util.ArrayList;
+import java.util.Arrays;
 import java.util.Collections;
 import java.util.Comparator;
 import java.util.Enumeration;
 import java.util.HashMap;
 import java.util.Iterator;
+import java.util.LinkedHashMap;
 import java.util.LinkedList;
 import java.util.List;
 import java.util.Map;
@@ -632,9 +634,9 @@ public class ApexCli
         new Arg[]{new 
FileArg("jar-file/json-file/properties-file/app-package-file-path/app-package-file-uri"),
 new Arg("matching-app-name")},
         "Launch an app", LAUNCH_OPTIONS.options));
     globalCommands.put("shutdown-app", new CommandSpec(new 
ShutdownAppCommand(),
-        new Arg[]{new Arg("app-id")},
-        new Arg[]{new VarArg("app-id")},
-        "Shutdown an app"));
+        new Arg[]{new Arg("app-id/app-name")},
+        new Arg[]{new VarArg("app-id/app-name")},
+        "Shutdown application(s) by id or name"));
     globalCommands.put("list-apps", new CommandSpec(new ListAppsCommand(),
         null,
         new Arg[]{new Arg("pattern")},
@@ -738,7 +740,7 @@ public class ApexCli
         "Kill a container"));
     connectedCommands.put("shutdown-app", new CommandSpec(new 
ShutdownAppCommand(),
         null,
-        new Arg[]{new VarArg("app-id")},
+        new Arg[]{new VarArg("app-id/app-name")},
         "Shutdown an app"));
     connectedCommands.put("kill-app", new CommandSpec(new KillAppCommand(),
         null,
@@ -1462,7 +1464,7 @@ public class ApexCli
     return s.substring(i);
   }
 
-  private void processLine(String line, final ConsoleReader reader, boolean 
expandMacroAlias)
+  protected void processLine(String line, final ConsoleReader reader, boolean 
expandMacroAlias)
   {
     try {
       // clear interrupt flag
@@ -2182,49 +2184,85 @@ public class ApexCli
 
   }
 
+  private ApplicationReport findApplicationReportFromAppNameOrId(String 
appNameOrId)
+  {
+    ApplicationReport app = getApplication(appNameOrId);
+    if (app == null) {
+      app = getApplicationByName(appNameOrId);
+    }
+    return app;
+  }
+
   private class ShutdownAppCommand implements Command
   {
     @Override
     public void execute(String[] args, ConsoleReader reader) throws Exception
     {
-      ApplicationReport[] apps;
+      Map<String, ApplicationReport> appIdReports = new LinkedHashMap<>();
+
       if (args.length == 1) {
         if (currentApp == null) {
           throw new CliException("No application selected");
         } else {
-          apps = new ApplicationReport[]{currentApp};
+          appIdReports.put(currentApp.getApplicationId().toString(), 
currentApp);
         }
       } else {
-        apps = new ApplicationReport[args.length - 1];
-        for (int i = 1; i < args.length; i++) {
-          apps[i - 1] = getApplication(args[i]);
-          if (apps[i - 1] == null) {
-            throw new CliException("Streaming application with id " + args[i] 
+ " is not found.");
-          }
+        String[] appNamesOrIds = Arrays.copyOfRange(args, 1, args.length);
+
+        for (String appNameOrId : appNamesOrIds) {
+          ApplicationReport ap = 
findApplicationReportFromAppNameOrId(appNameOrId);
+          appIdReports.put(appNameOrId, ap);
         }
       }
 
-      for (ApplicationReport app : apps) {
-        try {
-          JSONObject response = getResource(new 
StramAgent.StramUriSpec().path(StramWebServices.PATH_SHUTDOWN), app, new 
WebServicesClient.WebServicesHandler<JSONObject>()
-          {
-            @Override
-            public JSONObject process(WebResource.Builder webResource, 
Class<JSONObject> clazz)
-            {
-              return 
webResource.accept(MediaType.APPLICATION_JSON).post(clazz, new JSONObject());
-            }
+      for (Map.Entry<String, ApplicationReport> entry : 
appIdReports.entrySet()) {
+        String appNameOrId = entry.getKey();
+        ApplicationReport app = entry.getValue();
 
-          });
-          if (consolePresent) {
-            System.out.println("Shutdown requested: " + response);
+        shutdownApp(appNameOrId, app);
+      }
+    }
+
+    private void shutdownApp(String appNameOrId, ApplicationReport app)
+    {
+      if (app == null) {
+        String errMessage = "Failed to request shutdown for app %s: 
Application with id or name %s not found%n";
+        System.err.printf(errMessage, appNameOrId, appNameOrId);
+        return;
+      }
+
+      try {
+        JSONObject response = sendShutdownRequest(app);
+        if (consolePresent) {
+          System.out.printf("Shutdown of app %s requested: %s%n", 
app.getApplicationId().toString(), response);
+        }
+      } catch (Exception e) {
+        String errMessage = "Failed to request shutdown for app %s: %s%n";
+        System.err.printf(errMessage, app.getApplicationId().toString(), 
e.getMessage());
+      } finally {
+        if (currentApp != null) {
+          if (app.getApplicationId().equals(currentApp.getApplicationId())) {
+            currentApp = null;
           }
-          currentApp = null;
-        } catch (Exception e) {
-          throw new CliException("Failed to request shutdown for appid " + 
app.getApplicationId().toString(), e);
         }
       }
     }
+  }
+
+  protected JSONObject sendShutdownRequest(ApplicationReport app)
+  {
+    StramAgent.StramUriSpec uriSpec = new 
StramAgent.StramUriSpec().path(StramWebServices.PATH_SHUTDOWN);
+
+    WebServicesClient.WebServicesHandler<JSONObject> handler = new 
WebServicesClient.WebServicesHandler<JSONObject>()
+    {
+      @Override
+      public JSONObject process(WebResource.Builder webResource, 
Class<JSONObject> clazz)
+      {
+        return webResource.accept(MediaType.APPLICATION_JSON).post(clazz, new 
JSONObject());
+      }
+    };
 
+    return getResource(uriSpec, app, handler);
   }
 
   private class ListAppsCommand implements Command
@@ -2328,17 +2366,11 @@ public class ApexCli
       int i = 0;
       try {
         while (++i < args.length) {
-          app = getApplication(args[i]);
+          app = findApplicationReportFromAppNameOrId(args[i]);
           if (app == null) {
-
-            /*
-             * try once again with application name type.
-             */
-            app = getApplicationByName(args[i]);
-            if (app == null) {
-              throw new CliException("Streaming application with id or name " 
+ args[i] + " is not found.");
-            }
+            throw new CliException("Streaming application with id or name " + 
args[i] + " is not found.");
           }
+
           yarnClient.killApplication(app.getApplicationId());
           if (app == currentApp) {
             currentApp = null;
diff --git 
a/engine/src/test/java/com/datatorrent/stram/cli/ApexCliShutdownCommandTest.java
 
b/engine/src/test/java/com/datatorrent/stram/cli/ApexCliShutdownCommandTest.java
new file mode 100644
index 0000000..3a68a3e
--- /dev/null
+++ 
b/engine/src/test/java/com/datatorrent/stram/cli/ApexCliShutdownCommandTest.java
@@ -0,0 +1,142 @@
+/**
+ * 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 com.datatorrent.stram.cli;
+
+import java.io.ByteArrayOutputStream;
+import java.io.PrintStream;
+import java.util.ArrayList;
+import java.util.List;
+import java.util.Set;
+
+import org.codehaus.jettison.json.JSONObject;
+import org.junit.Assert;
+import org.junit.Rule;
+import org.junit.Test;
+import org.mockito.Mockito;
+import org.powermock.core.classloader.annotations.PowerMockIgnore;
+import org.powermock.core.classloader.annotations.PrepareForTest;
+import org.powermock.modules.junit4.rule.PowerMockRule;
+import org.powermock.reflect.Whitebox;
+
+import org.apache.hadoop.yarn.api.records.ApplicationId;
+import org.apache.hadoop.yarn.api.records.ApplicationReport;
+import org.apache.hadoop.yarn.api.records.FinalApplicationStatus;
+import org.apache.hadoop.yarn.api.records.YarnApplicationState;
+import org.apache.hadoop.yarn.client.api.YarnClient;
+
+import com.datatorrent.stram.client.StramAgent;
+import com.datatorrent.stram.util.WebServicesClient;
+
+import jline.console.ConsoleReader;
+
+import static org.powermock.api.mockito.PowerMockito.mock;
+import static org.powermock.api.mockito.PowerMockito.when;
+import static 
org.powermock.api.support.membermodification.MemberMatcher.constructor;
+import static 
org.powermock.api.support.membermodification.MemberModifier.suppress;
+
+/**
+ *
+ */
+@PowerMockIgnore({
+    "javax.net.ssl.*",
+    "org.apache.log4j.*"
+    })
+@PrepareForTest({YarnClient.class, ApexCli.class, StramAgent.class})
+public class ApexCliShutdownCommandTest
+{
+
+  @Rule
+  public PowerMockRule powerMockRule = new PowerMockRule();
+
+  private ApplicationReport mockRunningApplicationReport(String appId, String 
appName)
+  {
+    ApplicationReport app = mock(ApplicationReport.class);
+    ApplicationId applicationId = mock(ApplicationId.class);
+
+    when(applicationId.toString()).thenReturn(appId);
+    when(app.getApplicationId()).thenReturn(applicationId);
+
+    when(app.getName()).thenReturn(appName);
+
+    
when(app.getYarnApplicationState()).thenReturn(YarnApplicationState.RUNNING);
+    
when(app.getFinalApplicationStatus()).thenReturn(FinalApplicationStatus.UNDEFINED);
+
+    when(app.getTrackingUrl()).thenReturn("http://example.com";);
+
+    return app;
+  }
+
+  @Test
+  public void shutdownAppCommandUsesBestEffortApproach() throws Exception
+  {
+    // Given a cli and two running apps
+    ApexCli cliUnderTest = new ApexCli();
+    StramAgent stramAgent = mock(StramAgent.class);
+    YarnClient yarnClient = mock(YarnClient.class);
+
+    Whitebox.setInternalState(cliUnderTest, "stramAgent", stramAgent);
+    Whitebox.setInternalState(cliUnderTest, "yarnClient", yarnClient);
+    Whitebox.setInternalState(cliUnderTest, "consolePresent", true);
+
+    suppress(constructor(WebServicesClient.class, new Class[0]));
+
+    List<ApplicationReport> runningApplications = new ArrayList<>();
+
+    ApplicationReport app1 = mockRunningApplicationReport("application-id-1", 
"app1");
+    ApplicationReport app2 = mockRunningApplicationReport("application-id-2", 
"app2");
+
+    runningApplications.add(app1);
+    runningApplications.add(app2);
+
+    
when(yarnClient.getApplications(Mockito.any(Set.class))).thenReturn(runningApplications);
+    when(stramAgent.issueStramWebRequest(
+        Mockito.any(WebServicesClient.class),
+        Mockito.anyString(),
+        Mockito.any(StramAgent.StramUriSpec.class),
+        Mockito.any(WebServicesClient.WebServicesHandler.class)))
+        .thenReturn(new JSONObject());
+
+    final ByteArrayOutputStream stdOut = new ByteArrayOutputStream();
+    final ByteArrayOutputStream stdErr = new ByteArrayOutputStream();
+
+    PrintStream beforeOut = System.out;
+    PrintStream beforeErr = System.err;
+
+    System.setOut(new PrintStream(stdOut, true));
+    System.setErr(new PrintStream(stdErr, true));
+
+    // When processing the shutdown command for two valid and one invalid 
appNames
+    String shutdownAppsCommand = "shutdown-app app1 notExisting app2";
+    cliUnderTest.processLine(shutdownAppsCommand, new ConsoleReader(), true);
+
+    // Then the output contains two success and one error messages
+    Assert.assertEquals(
+        "Shutdown of app application-id-1 requested: {}\nShutdown of app 
application-id-2 requested: {}\n",
+        stdOut.toString()
+    );
+
+    Assert.assertEquals(
+        "Failed to request shutdown for app notExisting: Application with id 
or name notExisting not found\n",
+        stdErr.toString()
+    );
+
+    System.setOut(beforeOut);
+    System.setErr(beforeErr);
+  }
+}

-- 
To stop receiving notification emails like this one, please contact
['"[email protected]" <[email protected]>'].

Reply via email to