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]>'].