brooklyn-commits mailing list archives

Site index · List index
Message view « Date » · « Thread »
Top « Date » · « Thread »
From s...@apache.org
Subject [1/2] brooklyn-server git commit: Use SIGTERM in stop script inside AbstractSoftwareProcessSshDriver
Date Wed, 04 Jan 2017 14:06:55 GMT
Repository: brooklyn-server
Updated Branches:
  refs/heads/master ab7f7399f -> a74cc061c


Use SIGTERM in stop script inside AbstractSoftwareProcessSshDriver

- fixes BROOKLYN-418


Project: http://git-wip-us.apache.org/repos/asf/brooklyn-server/repo
Commit: http://git-wip-us.apache.org/repos/asf/brooklyn-server/commit/6946a4a0
Tree: http://git-wip-us.apache.org/repos/asf/brooklyn-server/tree/6946a4a0
Diff: http://git-wip-us.apache.org/repos/asf/brooklyn-server/diff/6946a4a0

Branch: refs/heads/master
Commit: 6946a4a0465928c7c1fd9f440825a19560967e45
Parents: b629f1b
Author: Valentin Aitken <bostko@gmail.com>
Authored: Tue Dec 20 18:56:29 2016 +0200
Committer: Valentin Aitken <bostko@gmail.com>
Committed: Tue Jan 3 19:06:15 2017 +0200

----------------------------------------------------------------------
 .../base/AbstractSoftwareProcessSshDriver.java  |  36 ++--
 .../lifecycle/ScriptHelperIntegrationTest.java  | 182 +++++++++++++++++++
 2 files changed, 204 insertions(+), 14 deletions(-)
----------------------------------------------------------------------


http://git-wip-us.apache.org/repos/asf/brooklyn-server/blob/6946a4a0/software/base/src/main/java/org/apache/brooklyn/entity/software/base/AbstractSoftwareProcessSshDriver.java
----------------------------------------------------------------------
diff --git a/software/base/src/main/java/org/apache/brooklyn/entity/software/base/AbstractSoftwareProcessSshDriver.java
b/software/base/src/main/java/org/apache/brooklyn/entity/software/base/AbstractSoftwareProcessSshDriver.java
index 53ccbec..c325afc 100644
--- a/software/base/src/main/java/org/apache/brooklyn/entity/software/base/AbstractSoftwareProcessSshDriver.java
+++ b/software/base/src/main/java/org/apache/brooklyn/entity/software/base/AbstractSoftwareProcessSshDriver.java
@@ -27,6 +27,7 @@ import java.util.List;
 import java.util.Map;
 import java.util.Set;
 
+import com.google.common.base.Joiner;
 import org.apache.brooklyn.api.entity.EntityLocal;
 import org.apache.brooklyn.api.entity.drivers.downloads.DownloadResolver;
 import org.apache.brooklyn.config.ConfigKey;
@@ -613,22 +614,29 @@ public abstract class AbstractSoftwareProcessSshDriver extends AbstractSoftwareP
                 // no pid, not running; 1 is not running
                 s.requireResultCode(Predicates.or(Predicates.equalTo(0), Predicates.equalTo(1)));
             } else if (STOPPING.equals(phase)) {
+                String stopCommand = Joiner.on('\n').join("PID=$(cat "+pidFile + ")",
+                        "test -n \"$PID\" || exit 0",
+                        "SIGTERM_USED=\"\"",
+                        "for i in $(seq 1 16); do",
+                        "  if ps -p $PID > /dev/null ; then",
+                        "     kill $PID",
+                        "     echo Attempted to stop PID $PID by sending SIGTERM.",
+                        "  else",
+                        "     echo Process $PID stopped successfully.",
+                        "     SIGTERM_USED=\"true\"",
+                        "     break",
+                        "  fi",
+                        "  sleep 1",
+                        "done",
+                        "if test -z $SIGTERM_USED; then",
+                        "  kill -9 $PID",
+                        "  echo Sent SIGKILL to $PID",
+                        "fi",
+                        "rm -f " + pidFile);
                 if (processOwner != null) {
-                    s.body.append(
-                            "export PID=$(" + BashCommands.sudoAsUser(processOwner, "cat
"+pidFile) + ")",
-                            "test -n \"$PID\" || exit 0",
-                            BashCommands.sudoAsUser(processOwner, "kill $PID"),
-                            BashCommands.sudoAsUser(processOwner, "kill -9 $PID"),
-                            BashCommands.sudoAsUser(processOwner, "rm -f "+pidFile)
-                    );
+                    s.body.append(BashCommands.sudoAsUser(processOwner, stopCommand));
                 } else {
-                    s.body.append(
-                            "export PID=$(cat "+pidFile+")",
-                            "test -n \"$PID\" || exit 0",
-                            "kill $PID",
-                            "kill -9 $PID",
-                            "rm -f "+pidFile
-                    );
+                    s.body.append(stopCommand);
                 }
             } else if (KILLING.equals(phase)) {
                 if (processOwner != null) {

http://git-wip-us.apache.org/repos/asf/brooklyn-server/blob/6946a4a0/software/base/src/test/java/org/apache/brooklyn/entity/software/base/lifecycle/ScriptHelperIntegrationTest.java
----------------------------------------------------------------------
diff --git a/software/base/src/test/java/org/apache/brooklyn/entity/software/base/lifecycle/ScriptHelperIntegrationTest.java
b/software/base/src/test/java/org/apache/brooklyn/entity/software/base/lifecycle/ScriptHelperIntegrationTest.java
new file mode 100644
index 0000000..0566f39
--- /dev/null
+++ b/software/base/src/test/java/org/apache/brooklyn/entity/software/base/lifecycle/ScriptHelperIntegrationTest.java
@@ -0,0 +1,182 @@
+/*
+ * 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.brooklyn.entity.software.base.lifecycle;
+
+import com.google.common.base.Joiner;
+import com.google.common.collect.ImmutableList;
+import com.google.common.collect.ImmutableMap;
+import com.google.common.collect.Iterables;
+import org.apache.brooklyn.api.entity.EntityLocal;
+import org.apache.brooklyn.api.entity.EntitySpec;
+import org.apache.brooklyn.api.location.Location;
+import org.apache.brooklyn.core.test.BrooklynAppLiveTestSupport;
+import org.apache.brooklyn.entity.software.base.AbstractSoftwareProcessSshDriver;
+import org.apache.brooklyn.entity.software.base.SoftwareProcessDriver;
+import org.apache.brooklyn.entity.software.base.VanillaSoftwareProcess;
+import org.apache.brooklyn.entity.software.base.VanillaSoftwareProcessImpl;
+import org.apache.brooklyn.location.ssh.SshMachineLocation;
+import org.apache.brooklyn.util.collections.MutableMap;
+import org.apache.brooklyn.util.core.task.system.internal.ExecWithLoggingHelpers;
+import org.apache.brooklyn.util.stream.ReaderInputStream;
+import org.apache.brooklyn.util.text.Strings;
+import org.apache.commons.io.output.ByteArrayOutputStream;
+import org.apache.commons.lang3.StringUtils;
+import org.slf4j.Logger;
+import org.slf4j.LoggerFactory;
+import org.testng.annotations.BeforeMethod;
+import org.testng.annotations.Test;
+
+import java.io.StringReader;
+
+import static java.lang.String.format;
+import static org.testng.Assert.*;
+
+public class ScriptHelperIntegrationTest extends BrooklynAppLiveTestSupport
+{
+    private static final Logger log = LoggerFactory.getLogger(ScriptHelperIntegrationTest.class);
+    
+    private Location loc;
+
+    @BeforeMethod(alwaysRun=true)
+    @SuppressWarnings("unchecked")
+    @Override
+    public void setUp() throws Exception {
+        super.setUp();
+        loc = app.getManagementContext().getLocationRegistry().getLocationManaged("localhost");
+    }
+
+    @Test(groups = "Integration")
+    public void testStopCommandWaitsToStopWithSigTerm() {
+        StopCommandSoftwareProcess entity = app.createAndManageChild(EntitySpec.create(StopCommandSoftwareProcess.class,
StopCommandSoftwareProcessImpl.class));
+        entity.start(ImmutableList.of(loc));
+        VanillaSoftwareProcessSshDriver driver = (VanillaSoftwareProcessSshDriver) entity.getDriver();
+        String launchContents = Joiner.on('\n')
+                .join("#!/usr/bin/env bash",
+                        "function trap_handler_command {",
+                        "  echo stopping...",
+                        "  exit 13",
+                        "}",
+                        "trap \"trap_handler_command\" SIGTERM",
+                        "while true; do",
+                        "  sleep 1",
+                        "done");
+        driver.copyResource(ImmutableMap.of(), new ReaderInputStream(new StringReader(launchContents),
"UTF-8"), "launch.sh", true);
+        driver.executeLaunchCommand("nohup bash launch.sh > /dev/null &");
+        ScriptHelper stopCommandScriptHelper = driver.stopCommandScriptHelper();
+        stopCommandScriptHelper.execute();
+        assertEquals(StringUtils.countMatches(stopCommandScriptHelper.getResultStdout(),
"Attempted to stop PID"), 1, "SIGTERM should be tried one time");
+        assertFalse(stopCommandScriptHelper.getResultStdout().contains("stopped with SIGKILL"),
"SIGKILL should not be sent after SIGTERM fails.");
+
+        SshMachineLocation machineLocation = (SshMachineLocation) Iterables.getFirst(entity.getLocations(),
null);
+        int checkPidFileExitCode = machineLocation.execCommands("Check for pid file", ImmutableList.of("ls
" + driver.getRunDir() + '/' + VanillaSoftwareProcessSshDriver.PID_FILENAME));
+        assertEquals(checkPidFileExitCode, 2, "pid file should be deleted.");
+    }
+
+
+    @Test(groups = "Integration")
+    public void testStopWithSigtermIsKilledWithSigKill() {
+        StopCommandSoftwareProcess entity = app.createAndManageChild(EntitySpec.create(StopCommandSoftwareProcess.class,
StopCommandSoftwareProcessImpl.class));
+        entity.start(ImmutableList.of(loc));
+        VanillaSoftwareProcessSshDriver driver = (VanillaSoftwareProcessSshDriver) entity.getDriver();
+        String launchContents = Joiner.on('\n')
+                .join("#!/usr/bin/env bash",
+                        "function trap_handler_command {",
+                        "  while true; do",
+                        "    echo \"Do nothing.\"",
+                        "    sleep 1",
+                        "  done",
+                        "}",
+                        "trap \"trap_handler_command\" SIGTERM",
+                        "while true; do",
+                        "  sleep 1",
+                        "done");
+        driver.copyResource(ImmutableMap.of(), new ReaderInputStream(new StringReader(launchContents),
"UTF-8"), "launch.sh", true);
+        driver.executeLaunchCommand("nohup bash launch.sh > /dev/null &");
+        ByteArrayOutputStream stdOut = new ByteArrayOutputStream(15);
+        SshMachineLocation machineLocation = (SshMachineLocation) Iterables.getFirst(entity.getLocations(),
null);
+        machineLocation.execCommands(
+                ImmutableMap.<String, Object>of(ExecWithLoggingHelpers.STDOUT.getName(),
stdOut),
+                "check process is stopped",
+                ImmutableList.of("cat "+driver.getRunDir() + '/' + VanillaSoftwareProcessSshDriver.PID_FILENAME),
+                MutableMap.<String,Object>of());
+        int launchedProcessPid = Integer.parseInt(Strings.trimEnd(new String(stdOut.toByteArray())));
+        log.info(format("Pid of launched long running process %d.", launchedProcessPid));
+        ScriptHelper stopCommandScriptHelper = driver.stopCommandScriptHelper();
+        stopCommandScriptHelper.execute();
+        assertEquals(StringUtils.countMatches(stopCommandScriptHelper.getResultStdout(),
"Attempted to stop PID"), 16, "SIGTERM should be tried one time");
+        assertEquals(StringUtils.countMatches(stopCommandScriptHelper.getResultStdout(),
"Sent SIGKILL to " + launchedProcessPid), 1, "SIGKILL should be sent after SIGTERM fails.");
+
+        int processCheckExitCode = machineLocation.execCommands("check whether process is
still running", ImmutableList.of("ps -p " + launchedProcessPid + " > /dev/null"));
+        assertNotEquals(processCheckExitCode, 0);
+        int checkPidFileExitCode = machineLocation.execCommands("Check for pid file", ImmutableList.of("ls
" + driver.getRunDir() + '/' + VanillaSoftwareProcessSshDriver.PID_FILENAME));
+        assertEquals(checkPidFileExitCode, 2, "pid file should be deleted.");
+    }
+
+    public interface StopCommandSoftwareProcess extends VanillaSoftwareProcess {
+        SoftwareProcessDriver getDriver();
+    }
+    public static class StopCommandSoftwareProcessImpl extends VanillaSoftwareProcessImpl
implements StopCommandSoftwareProcess {
+        @Override public Class<?> getDriverInterface() {
+            return VanillaSoftwareProcessSshDriver.class;
+        }
+    }
+
+    public static class VanillaSoftwareProcessSshDriver extends AbstractSoftwareProcessSshDriver
{
+        public VanillaSoftwareProcessSshDriver(EntityLocal entity, SshMachineLocation machine)
{
+            super(entity, machine);
+        }
+
+        public int executeLaunchCommand(String command) {
+            return newScript(MutableMap.of(USE_PID_FILE, true), LAUNCHING)
+                    .updateTaskAndFailOnNonZeroResultCode()
+                    .body.append(command)
+                    .gatherOutput()
+                    .execute();
+        }
+
+        public ScriptHelper stopCommandScriptHelper() {
+            return newScript(MutableMap.of(USE_PID_FILE, true), STOPPING).gatherOutput();
+        }
+
+        @Override
+        public boolean isRunning() {
+            return true;
+        }
+
+        @Override
+        public void stop() {
+            ScriptHelperIntegrationTest.log.debug(getClass().getName() + " stop");
+        }
+
+        @Override
+        public void install() {
+            ScriptHelperIntegrationTest.log.debug(getClass().getName() + " install");
+        }
+
+        @Override
+        public void customize() {
+            ScriptHelperIntegrationTest.log.debug(getClass().getName() + " customize");
+        }
+
+        @Override
+        public void launch() {
+            ScriptHelperIntegrationTest.log.info(getClass().getName() + " launch");
+        }
+    }
+}


Mime
View raw message