create and use install/run dirs on windows
diff --git a/camp/camp-brooklyn/src/test/java/org/apache/brooklyn/camp/brooklyn/WindowsYamlLiveTest.java b/camp/camp-brooklyn/src/test/java/org/apache/brooklyn/camp/brooklyn/WindowsYamlLiveTest.java
index 6e1a0a1..974f9f6 100644
--- a/camp/camp-brooklyn/src/test/java/org/apache/brooklyn/camp/brooklyn/WindowsYamlLiveTest.java
+++ b/camp/camp-brooklyn/src/test/java/org/apache/brooklyn/camp/brooklyn/WindowsYamlLiveTest.java
@@ -38,6 +38,7 @@
import org.apache.brooklyn.location.winrm.WinRmMachineLocation;
import org.apache.brooklyn.test.Asserts;
import org.apache.brooklyn.util.core.task.TaskPredicates;
+import org.apache.brooklyn.util.text.StringEscapes.BashStringEscapes;
import org.apache.brooklyn.util.text.StringEscapes.JavaStringEscapes;
import org.apache.brooklyn.util.text.StringPredicates;
import org.apache.brooklyn.util.text.Strings;
@@ -151,13 +152,13 @@
public void testPowershellMinimalist() throws Exception {
Map<String, String> cmds = ImmutableMap.<String, String>builder()
.put("myarg", "myval")
- .put("launch.powershell.command", "\"& c:\\\\exit0.ps1\"")
- .put("checkRunning.powershell.command", "\"& c:\\\\exit0.bat\"")
+ .put("launch.powershell.command", JavaStringEscapes.wrapJavaString("& \"$Env:INSTALL_DIR\\exit0.ps1\""))
+ .put("checkRunning.powershell.command", JavaStringEscapes.wrapJavaString("& \"$Env:INSTALL_DIR\\exit0.bat\""))
.build();
Map<String, List<String>> stdouts = ImmutableMap.of();
- runWindowsApp(cmds, stdouts, null);
+ runWindowsApp(cmds, stdouts, true, null);
}
@Test(groups="Live")
@@ -182,7 +183,7 @@
.put("winrm: pre-launch-command.*", ImmutableList.of("myval"))
.build();
- runWindowsApp(cmds, stdouts, null);
+ runWindowsApp(cmds, stdouts, false, null);
}
@Test(groups="Live")
@@ -207,7 +208,7 @@
.put("winrm: pre-launch-command.*", ImmutableList.of("myval"))
.build();
- runWindowsApp(cmds, stdouts, null);
+ runWindowsApp(cmds, stdouts, false, null);
}
@Test(groups="Live")
@@ -227,7 +228,7 @@
Map<String, List<String>> stdouts = ImmutableMap.of();
- runWindowsApp(cmds, stdouts, "winrm: pre-install-command.*");
+ runWindowsApp(cmds, stdouts, false, "winrm: pre-install-command.*");
}
// FIXME Failing to match the expected exception, but looks fine! Needs more investigation.
@@ -248,7 +249,7 @@
Map<String, List<String>> stdouts = ImmutableMap.of();
- runWindowsApp(cmds, stdouts, "winrm: is-running-command.*");
+ runWindowsApp(cmds, stdouts, false, "winrm: is-running-command.*");
}
// FIXME Needs more work to get the stop's task that failed, so can assert got the right error message
@@ -269,29 +270,29 @@
Map<String, List<String>> stdouts = ImmutableMap.of();
- runWindowsApp(cmds, stdouts, "winrm: stop-command.*");
+ runWindowsApp(cmds, stdouts, false, "winrm: stop-command.*");
}
- protected void runWindowsApp(Map<String, String> commands, Map<String, List<String>> stdouts, String taskRegexFailed) throws Exception {
+ protected void runWindowsApp(Map<String, String> commands, Map<String, List<String>> stdouts, boolean useInstallDir, String taskRegexFailed) throws Exception {
String cmdFailed = (taskRegexFailed == null) ? null : TASK_REGEX_TO_COMMAND.get(taskRegexFailed);
List<String> yaml = Lists.newArrayList();
yaml.addAll(yamlLocation);
+ String prefix = useInstallDir ? "" : "c:\\";
yaml.addAll(ImmutableList.of(
"services:",
"- type: org.apache.brooklyn.entity.software.base.VanillaWindowsProcess",
" brooklyn.config:",
- " onbox.base.dir.skipResolution: true",
" templates.preinstall:",
" classpath://org/apache/brooklyn/camp/brooklyn/echoFreemarkerMyarg.bat: c:\\echoFreemarkerMyarg.bat",
" classpath://org/apache/brooklyn/camp/brooklyn/echoFreemarkerMyarg.ps1: c:\\echoFreemarkerMyarg.ps1",
" files.preinstall:",
- " classpath://org/apache/brooklyn/camp/brooklyn/echoArg.bat: c:\\echoArg.bat",
- " classpath://org/apache/brooklyn/camp/brooklyn/echoMyArg.ps1: c:\\echoMyArg.ps1",
- " classpath://org/apache/brooklyn/camp/brooklyn/exit0.bat: c:\\exit0.bat",
- " classpath://org/apache/brooklyn/camp/brooklyn/exit1.bat: c:\\exit1.bat",
- " classpath://org/apache/brooklyn/camp/brooklyn/exit0.ps1: c:\\exit0.ps1",
- " classpath://org/apache/brooklyn/camp/brooklyn/exit1.ps1: c:\\exit1.ps1",
+ " classpath://org/apache/brooklyn/camp/brooklyn/echoArg.bat: "+prefix+"echoArg.bat",
+ " classpath://org/apache/brooklyn/camp/brooklyn/echoMyArg.ps1: "+prefix+"echoMyArg.ps1",
+ " classpath://org/apache/brooklyn/camp/brooklyn/exit0.bat: "+prefix+"exit0.bat",
+ " classpath://org/apache/brooklyn/camp/brooklyn/exit1.bat: "+prefix+"exit1.bat",
+ " classpath://org/apache/brooklyn/camp/brooklyn/exit0.ps1: "+prefix+"exit0.ps1",
+ " classpath://org/apache/brooklyn/camp/brooklyn/exit1.ps1: "+prefix+"exit1.ps1",
""));
for (Map.Entry<String, String> entry : commands.entrySet()) {
@@ -363,7 +364,7 @@
private void assertPhaseStreamEquals(Entity entity, String phase, String stream, Predicate<String> check) {
Optional<Task<?>> t = findTaskOrSubTask(entity, TaskPredicates.displayNameSatisfies(StringPredicates.startsWith("winrm: "+phase)));
- Asserts.assertThat(BrooklynTaskTags.stream(t.get(), stream).getStreamContentsAbbreviated().trim(), check);
+ Asserts.assertThat(BrooklynTaskTags.stream(t.get(), stream).streamContents.get().trim(), check);
}
@Override
diff --git a/core/src/main/java/org/apache/brooklyn/location/ssh/CanResolveOnBoxDir.java b/core/src/main/java/org/apache/brooklyn/location/ssh/CanResolveOnBoxDir.java
new file mode 100644
index 0000000..a53a319
--- /dev/null
+++ b/core/src/main/java/org/apache/brooklyn/location/ssh/CanResolveOnBoxDir.java
@@ -0,0 +1,27 @@
+/*
+ * 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.location.ssh;
+
+import org.apache.brooklyn.api.entity.Entity;
+
+public interface CanResolveOnBoxDir {
+
+ String resolveOnBoxDirFor(Entity entity, String unresolvedPath);
+
+}
diff --git a/core/src/main/java/org/apache/brooklyn/location/ssh/SshMachineLocation.java b/core/src/main/java/org/apache/brooklyn/location/ssh/SshMachineLocation.java
index 909cc2b..ada35d0 100644
--- a/core/src/main/java/org/apache/brooklyn/location/ssh/SshMachineLocation.java
+++ b/core/src/main/java/org/apache/brooklyn/location/ssh/SshMachineLocation.java
@@ -44,6 +44,7 @@
import javax.annotation.Nonnull;
import javax.annotation.Nullable;
+import org.apache.brooklyn.api.entity.Entity;
import org.apache.brooklyn.api.location.MachineDetails;
import org.apache.brooklyn.api.location.MachineLocation;
import org.apache.brooklyn.api.location.PortRange;
@@ -57,6 +58,7 @@
import org.apache.brooklyn.core.config.ConfigUtils;
import org.apache.brooklyn.core.config.MapConfigKey;
import org.apache.brooklyn.core.config.Sanitizer;
+import org.apache.brooklyn.core.effector.ssh.SshEffectorTasks;
import org.apache.brooklyn.core.entity.BrooklynConfigKeys;
import org.apache.brooklyn.core.location.AbstractMachineLocation;
import org.apache.brooklyn.core.location.BasicMachineDetails;
@@ -80,8 +82,10 @@
import org.apache.brooklyn.util.core.internal.ssh.SshTool;
import org.apache.brooklyn.util.core.internal.ssh.sshj.SshjTool;
import org.apache.brooklyn.util.core.mutex.WithMutexes;
+import org.apache.brooklyn.util.core.task.DynamicTasks;
import org.apache.brooklyn.util.core.task.ScheduledTask;
import org.apache.brooklyn.util.core.task.Tasks;
+import org.apache.brooklyn.util.core.task.system.ProcessTaskWrapper;
import org.apache.brooklyn.util.core.task.system.internal.ExecWithLoggingHelpers;
import org.apache.brooklyn.util.exceptions.Exceptions;
import org.apache.brooklyn.util.guava.KeyTransformingLoadingCache.KeyTransformingSameTypeLoadingCache;
@@ -128,7 +132,7 @@
* Additionally there are routines to copyTo, copyFrom; and installTo (which tries a curl, and falls back to copyTo
* in event the source is accessible by the caller only).
*/
-public class SshMachineLocation extends AbstractMachineLocation implements MachineLocation, PortSupplier, WithMutexes, Closeable {
+public class SshMachineLocation extends AbstractMachineLocation implements MachineLocation, PortSupplier, WithMutexes, Closeable, CanResolveOnBoxDir {
private static final Logger LOG = LoggerFactory.getLogger(SshMachineLocation.class);
private static final Logger logSsh = LoggerFactory.getLogger(BrooklynLogging.SSH_IO);
@@ -1040,4 +1044,21 @@
return mutexes().hasMutex(mutexId);
}
+ @Override
+ public String resolveOnBoxDirFor(Entity entity, String unresolvedPath) {
+ ProcessTaskWrapper<Integer> baseTask = SshEffectorTasks.ssh(
+ BashCommands.alternatives("mkdir -p \"${BASE_DIR}\"",
+ BashCommands.chain(
+ BashCommands.sudo("mkdir -p \"${BASE_DIR}\""),
+ BashCommands.sudo("chown "+getUser()+" \"${BASE_DIR}\""))),
+ "cd ~",
+ "cd ${BASE_DIR}",
+ "echo BASE_DIR_RESULT':'`pwd`:BASE_DIR_RESULT")
+ .environmentVariable("BASE_DIR", unresolvedPath)
+ .requiringExitCodeZero()
+ .summary("initializing on-box base dir "+unresolvedPath).newTask();
+ DynamicTasks.queueIfPossible(baseTask).orSubmitAsync(entity);
+ return Strings.getFragmentBetween(baseTask.block().getStdout(), "BASE_DIR_RESULT:", ":BASE_DIR_RESULT");
+ }
+
}
diff --git a/software/base/src/main/java/org/apache/brooklyn/entity/software/base/AbstractSoftwareProcessDriver.java b/software/base/src/main/java/org/apache/brooklyn/entity/software/base/AbstractSoftwareProcessDriver.java
index b7748df..f945aeb 100644
--- a/software/base/src/main/java/org/apache/brooklyn/entity/software/base/AbstractSoftwareProcessDriver.java
+++ b/software/base/src/main/java/org/apache/brooklyn/entity/software/base/AbstractSoftwareProcessDriver.java
@@ -47,6 +47,7 @@
import org.apache.brooklyn.core.entity.EntityInternal;
import org.apache.brooklyn.core.entity.lifecycle.Lifecycle;
import org.apache.brooklyn.core.entity.lifecycle.ServiceStateLogic;
+import org.apache.brooklyn.core.feed.ConfigToAttributes;
import org.apache.brooklyn.entity.software.base.lifecycle.MachineLifecycleEffectorTasks;
import org.apache.brooklyn.entity.software.base.lifecycle.MachineLifecycleEffectorTasks.CloseableLatch;
import org.apache.brooklyn.util.collections.MutableMap;
@@ -58,6 +59,7 @@
import org.apache.brooklyn.util.exceptions.Exceptions;
import org.apache.brooklyn.util.os.Os;
import org.apache.brooklyn.util.stream.ReaderInputStream;
+import org.apache.brooklyn.util.text.Strings;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
@@ -77,6 +79,13 @@
protected final ResourceUtils resource;
protected final Location location;
+ // we cache these for efficiency and in case the entity becomes unmanaged
+ private volatile String installDir;
+ private volatile String runDir;
+ private volatile String expandedInstallDir;
+
+ private final Object installDirSetupMutex = new Object();
+
public AbstractSoftwareProcessDriver(EntityLocal entity, Location location) {
this.entity = checkNotNull(entity, "entity");
this.location = checkNotNull(location, "location");
@@ -441,6 +450,10 @@
}
}
+ protected String mergePaths(String ...s) {
+ return Os.mergePathsUnix(s);
+ }
+
private void applyFnToResourcesAppendToList(
Map<String, String> resources, final Function<SourceAndDestination, Task<?>> function,
String destinationParentDir, final List<TaskAdaptable<?>> tasks) {
@@ -448,7 +461,7 @@
for (Map.Entry<String, String> entry : resources.entrySet()) {
final String source = checkNotNull(entry.getKey(), "Missing source for resource");
String target = checkNotNull(entry.getValue(), "Missing destination for resource");
- final String destination = Os.isAbsolutish(target) ? target : Os.mergePathsUnix(destinationParentDir, target);
+ final String destination = Os.isAbsolutish(target) ? target : mergePaths(destinationParentDir, target);
// if source is a directory then copy all files underneath.
// e.g. /tmp/a/{b,c/d}, source = /tmp/a, destination = dir/a/b and dir/a/c/d.
@@ -463,7 +476,7 @@
public FileVisitResult visitFile(Path file, BasicFileAttributes attrs) throws IOException {
if (attrs.isRegularFile()) {
Path relativePath = file.subpath(startElements, file.getNameCount());
- tasks.add(function.apply(new SourceAndDestination(file.toString(), Os.mergePathsUnix(destination, relativePath.toString()))));
+ tasks.add(function.apply(new SourceAndDestination(file.toString(), mergePaths(destination, relativePath.toString()))));
}
return FileVisitResult.CONTINUE;
}
@@ -562,18 +575,19 @@
}
/**
- * @param template URI of file to template and copy, e.g. file://.., http://.., classpath://..
+ * @param templateUrl URI of file to template and copy, e.g. file://.., http://.., classpath://..
* @param target Destination on server.
* @param extraSubstitutions Extra substitutions for the templater to use, for example
* "foo" -> "bar", and in a template ${foo}.
* @return The exit code of the SSH command run.
*/
- public int copyTemplate(String template, String target, boolean createParent, Map<String, ?> extraSubstitutions) {
- String data = processTemplate(template, extraSubstitutions);
+ public int copyTemplate(String templateUrl, String target, boolean createParent, Map<String, ?> extraSubstitutions) {
+ log.debug("Processing template "+templateUrl+" and copying to "+target+" on "+getLocation()+" for "+getEntity());
+ String data = processTemplate(templateUrl, extraSubstitutions);
return copyResource(MutableMap.<Object,Object>of(), new StringReader(data), target, createParent);
}
- public abstract int copyResource(Map<Object,Object> sshFlags, String source, String target, boolean createParentDir);
+ public abstract int copyResource(Map<Object,Object> sshFlags, String sourceUrl, String target, boolean createParentDir);
public abstract int copyResource(Map<Object,Object> sshFlags, InputStream source, String target, boolean createParentDir);
@@ -595,8 +609,8 @@
return copyResource(MutableMap.of(), resource, target);
}
- public int copyResource(String resource, String target, boolean createParentDir) {
- return copyResource(MutableMap.of(), resource, target, createParentDir);
+ public int copyResource(String resourceUrl, String target, boolean createParentDir) {
+ return copyResource(MutableMap.of(), resourceUrl, target, createParentDir);
}
@SuppressWarnings({ "rawtypes", "unchecked" })
@@ -677,6 +691,95 @@
return envSerializer.serialize(env);
}
- public abstract String getRunDir();
- public abstract String getInstallDir();
+
+ protected void setInstallDir(String installDir) {
+ this.installDir = installDir;
+ entity.sensors().set(SoftwareProcess.INSTALL_DIR, installDir);
+ }
+
+ public String getInstallDir() {
+ if (installDir != null) return installDir;
+
+ String existingVal = getEntity().getAttribute(SoftwareProcess.INSTALL_DIR);
+ if (Strings.isNonBlank(existingVal)) { // e.g. on rebind
+ installDir = existingVal;
+ return installDir;
+ }
+
+ synchronized (installDirSetupMutex) {
+ // previously we looked at sensor value, but we shouldn't as it might have been converted from the config key value
+ // *before* we computed the install label, or that label may have changed since previous install; now force a recompute
+ setInstallLabel();
+
+ // set it null first so that we force a recompute
+ setInstallDir(null);
+ setInstallDir(Os.tidyPath(ConfigToAttributes.apply(getEntity(), SoftwareProcess.INSTALL_DIR)));
+ return installDir;
+ }
+ }
+
+ protected void setInstallLabel() {
+ if (((EntityInternal)getEntity()).config().getLocalRaw(SoftwareProcess.INSTALL_UNIQUE_LABEL).isPresentAndNonNull()) return;
+ getEntity().config().set(SoftwareProcess.INSTALL_UNIQUE_LABEL,
+ getEntity().getEntityType().getSimpleName()+
+ (Strings.isNonBlank(getVersion()) ? "_"+getVersion() : "")+
+ (Strings.isNonBlank(getInstallLabelExtraSalt()) ? "_"+getInstallLabelExtraSalt() : "") );
+ }
+
+ /** allows subclasses to return extra salt (ie unique hash)
+ * for cases where install dirs need to be distinct e.g. based on extra plugins being placed in the install dir;
+ * {@link #setInstallLabel()} uses entity-type simple name and version already
+ * <p>
+ * this salt should not be too long and must not contain invalid path chars.
+ * a hash code of other relevant info is not a bad choice.
+ **/
+ protected String getInstallLabelExtraSalt() {
+ return null;
+ }
+
+ protected void setRunDir(String runDir) {
+ this.runDir = runDir;
+ entity.sensors().set(SoftwareProcess.RUN_DIR, runDir);
+ }
+
+ public String getRunDir() {
+ if (runDir != null) return runDir;
+
+ String existingVal = getEntity().getAttribute(SoftwareProcess.RUN_DIR);
+ if (Strings.isNonBlank(existingVal)) { // e.g. on rebind
+ runDir = existingVal;
+ return runDir;
+ }
+
+ setRunDir(Os.tidyPath(ConfigToAttributes.apply(getEntity(), SoftwareProcess.RUN_DIR)));
+ return runDir;
+ }
+
+ public void setExpandedInstallDir(String val) {
+ String oldVal = getEntity().getAttribute(SoftwareProcess.EXPANDED_INSTALL_DIR);
+ if (Strings.isNonBlank(oldVal) && !oldVal.equals(val)) {
+ log.info("Resetting expandedInstallDir (to "+val+" from "+oldVal+") for "+getEntity());
+ }
+
+ expandedInstallDir = val;
+ getEntity().sensors().set(SoftwareProcess.EXPANDED_INSTALL_DIR, val);
+ }
+
+ public String getExpandedInstallDir() {
+ if (expandedInstallDir != null) return expandedInstallDir;
+
+ String existingVal = getEntity().getAttribute(SoftwareProcess.EXPANDED_INSTALL_DIR);
+ if (Strings.isNonBlank(existingVal)) { // e.g. on rebind
+ expandedInstallDir = existingVal;
+ return expandedInstallDir;
+ }
+
+ String untidiedVal = ConfigToAttributes.apply(getEntity(), SoftwareProcess.EXPANDED_INSTALL_DIR);
+ if (Strings.isNonBlank(untidiedVal)) {
+ setExpandedInstallDir(Os.tidyPath(untidiedVal));
+ return expandedInstallDir;
+ } else {
+ throw new IllegalStateException("expandedInstallDir is null; most likely install was not called for "+getEntity());
+ }
+ }
}
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 9ff370c..376577b 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
@@ -81,13 +81,6 @@
public static final Logger log = LoggerFactory.getLogger(AbstractSoftwareProcessSshDriver.class);
public static final Logger logSsh = LoggerFactory.getLogger(BrooklynLogging.SSH_IO);
- // we cache these for efficiency and in case the entity becomes unmanaged
- private volatile String installDir;
- private volatile String runDir;
- private volatile String expandedInstallDir;
-
- private final Object installDirSetupMutex = new Object();
-
protected volatile DownloadResolver resolver;
@Override
@@ -130,99 +123,6 @@
return (SshMachineLocation) super.getLocation();
}
- protected void setInstallDir(String installDir) {
- this.installDir = installDir;
- entity.sensors().set(SoftwareProcess.INSTALL_DIR, installDir);
- }
-
- @Override
- public String getInstallDir() {
- if (installDir != null) return installDir;
-
- String existingVal = getEntity().getAttribute(SoftwareProcess.INSTALL_DIR);
- if (Strings.isNonBlank(existingVal)) { // e.g. on rebind
- installDir = existingVal;
- return installDir;
- }
-
- synchronized (installDirSetupMutex) {
- // previously we looked at sensor value, but we shouldn't as it might have been converted from the config key value
- // *before* we computed the install label, or that label may have changed since previous install; now force a recompute
- setInstallLabel();
-
- // set it null first so that we force a recompute
- setInstallDir(null);
- setInstallDir(Os.tidyPath(ConfigToAttributes.apply(getEntity(), SoftwareProcess.INSTALL_DIR)));
- return installDir;
- }
- }
-
- protected void setInstallLabel() {
- if (((EntityInternal)getEntity()).config().getLocalRaw(SoftwareProcess.INSTALL_UNIQUE_LABEL).isPresentAndNonNull()) return;
- getEntity().config().set(SoftwareProcess.INSTALL_UNIQUE_LABEL,
- getEntity().getEntityType().getSimpleName()+
- (Strings.isNonBlank(getVersion()) ? "_"+getVersion() : "")+
- (Strings.isNonBlank(getInstallLabelExtraSalt()) ? "_"+getInstallLabelExtraSalt() : "") );
- }
-
- /** allows subclasses to return extra salt (ie unique hash)
- * for cases where install dirs need to be distinct e.g. based on extra plugins being placed in the install dir;
- * {@link #setInstallLabel()} uses entity-type simple name and version already
- * <p>
- * this salt should not be too long and must not contain invalid path chars.
- * a hash code of other relevant info is not a bad choice.
- **/
- protected String getInstallLabelExtraSalt() {
- return null;
- }
-
- protected void setRunDir(String runDir) {
- this.runDir = runDir;
- entity.sensors().set(SoftwareProcess.RUN_DIR, runDir);
- }
-
- @Override
- public String getRunDir() {
- if (runDir != null) return runDir;
-
- String existingVal = getEntity().getAttribute(SoftwareProcess.RUN_DIR);
- if (Strings.isNonBlank(existingVal)) { // e.g. on rebind
- runDir = existingVal;
- return runDir;
- }
-
- setRunDir(Os.tidyPath(ConfigToAttributes.apply(getEntity(), SoftwareProcess.RUN_DIR)));
- return runDir;
- }
-
- public void setExpandedInstallDir(String val) {
- String oldVal = getEntity().getAttribute(SoftwareProcess.EXPANDED_INSTALL_DIR);
- if (Strings.isNonBlank(oldVal) && !oldVal.equals(val)) {
- log.info("Resetting expandedInstallDir (to "+val+" from "+oldVal+") for "+getEntity());
- }
-
- expandedInstallDir = val;
- getEntity().sensors().set(SoftwareProcess.EXPANDED_INSTALL_DIR, val);
- }
-
- public String getExpandedInstallDir() {
- if (expandedInstallDir != null) return expandedInstallDir;
-
- String existingVal = getEntity().getAttribute(SoftwareProcess.EXPANDED_INSTALL_DIR);
- if (Strings.isNonBlank(existingVal)) { // e.g. on rebind
- expandedInstallDir = existingVal;
- return expandedInstallDir;
- }
-
- String untidiedVal = ConfigToAttributes.apply(getEntity(), SoftwareProcess.EXPANDED_INSTALL_DIR);
- if (Strings.isNonBlank(untidiedVal)) {
- setExpandedInstallDir(Os.tidyPath(untidiedVal));
- return expandedInstallDir;
- } else {
- throw new IllegalStateException("expandedInstallDir is null; most likely install was not called for "+getEntity());
- }
- }
-
public SshMachineLocation getMachine() { return getLocation(); }
public String getHostname() { return entity.getAttribute(Attributes.HOSTNAME); }
public String getAddress() { return entity.getAttribute(Attributes.ADDRESS); }
diff --git a/software/base/src/main/java/org/apache/brooklyn/entity/software/base/AbstractSoftwareProcessWinRmDriver.java b/software/base/src/main/java/org/apache/brooklyn/entity/software/base/AbstractSoftwareProcessWinRmDriver.java
index 0303313..eca10a1 100644
--- a/software/base/src/main/java/org/apache/brooklyn/entity/software/base/AbstractSoftwareProcessWinRmDriver.java
+++ b/software/base/src/main/java/org/apache/brooklyn/entity/software/base/AbstractSoftwareProcessWinRmDriver.java
@@ -58,7 +58,6 @@
import com.google.common.collect.ImmutableList;
import com.google.common.collect.ImmutableMap;
-import com.google.common.collect.Maps;
public abstract class AbstractSoftwareProcessWinRmDriver extends AbstractSoftwareProcessDriver implements NativeWindowsScriptRunner {
private static final Logger LOG = LoggerFactory.getLogger(AbstractSoftwareProcessWinRmDriver.class);
@@ -74,6 +73,10 @@
entity.sensors().set(WINDOWS_PASSWORD, location.config().get(WinRmMachineLocation.PASSWORD));
}
+ protected String mergePaths(String ...s) {
+ return super.mergePaths(s).replaceAll("/", "\\\\");
+ }
+
protected WinRmExecuteHelper newScript(String command, String psCommand, String phase, String taskNamePrefix) {
return newScript(command, psCommand, phase, taskNamePrefix, null);
}
@@ -284,28 +287,17 @@
}
@Override
- public String getRunDir() {
- // TODO: This needs to be tidied, and read from the appropriate flags (if set)
- return "$HOME\\brooklyn-managed-processes\\apps\\" + entity.getApplicationId() + "\\entities\\" + getEntityVersionLabel()+"_"+entity.getId();
- }
-
- @Override
- public String getInstallDir() {
- // TODO: This needs to be tidied, and read from the appropriate flags (if set)
- return "$HOME\\brooklyn-managed-processes\\installs\\" + entity.getApplicationId() + "\\" + getEntityVersionLabel()+"_"+entity.getId();
- }
-
- @Override
- public int copyResource(Map<Object, Object> sshFlags, String source, String target, boolean createParentDir) {
+ public int copyResource(Map<Object, Object> sshFlags, String sourceUrl, String target, boolean createParentDir) {
if (createParentDir) {
createDirectory(getDirectory(target), "Creating resource directory");
}
InputStream stream = null;
try {
- Tasks.setBlockingDetails("retrieving resource "+source+" for copying across");
- stream = resource.getResourceFromUrl(source);
- Tasks.setBlockingDetails("copying resource "+source+" to server");
+ Tasks.setBlockingDetails("retrieving resource "+sourceUrl+" for copying across");
+ stream = resource.getResourceFromUrl(sourceUrl);
+ Tasks.setBlockingDetails("copying resource "+sourceUrl+" to server");
+ LOG.debug("Copying "+sourceUrl+" to "+target+" on "+getLocation()+" for "+getEntity());
return copyTo(stream, target);
} catch (Exception e) {
throw Exceptions.propagate(e);
diff --git a/software/base/src/main/java/org/apache/brooklyn/entity/software/base/VanillaWindowsProcess.java b/software/base/src/main/java/org/apache/brooklyn/entity/software/base/VanillaWindowsProcess.java
index e772f24..7879d58 100644
--- a/software/base/src/main/java/org/apache/brooklyn/entity/software/base/VanillaWindowsProcess.java
+++ b/software/base/src/main/java/org/apache/brooklyn/entity/software/base/VanillaWindowsProcess.java
@@ -30,12 +30,53 @@
import org.apache.brooklyn.config.ConfigInheritance;
import org.apache.brooklyn.config.ConfigKey;
import org.apache.brooklyn.core.config.ConfigKeys;
+import org.apache.brooklyn.core.entity.BrooklynConfigKeys;
+import org.apache.brooklyn.core.sensor.AttributeSensorAndConfigKey;
import org.apache.brooklyn.core.sensor.Sensors;
+import org.apache.brooklyn.core.sensor.TemplatedStringAttributeSensorAndConfigKey;
+import org.apache.brooklyn.util.core.flags.SetFromFlag;
import org.apache.brooklyn.util.time.Duration;
@Catalog(name="Vanilla Windows Process", description="A basic Windows entity configured with scripts, e.g. for launch, check-running and stop")
@ImplementedBy(VanillaWindowsProcessImpl.class)
public interface VanillaWindowsProcess extends AbstractVanillaProcess {
+
+ @SetFromFlag("installDir")
+ AttributeSensorAndConfigKey<String,String> INSTALL_DIR = new TemplatedStringAttributeSensorAndConfigKey(
+ "install.dir",
+ "Directory in which this software will be installed (if downloading/unpacking artifacts explicitly); uses FreeMarker templating format",
+ "${" +
+ "config['"+BrooklynConfigKeys.ONBOX_BASE_DIR.getName()+"']!" +
+ "config['"+BrooklynConfigKeys.BROOKLYN_DATA_DIR.getName()+"']!" +
+ "'ERROR-ONBOX_BASE_DIR-not-set'" +
+ "}" +
+ "\\" +
+ "installs\\" +
+ // the var?? tests if it exists, passing value to ?string(if_present,if_absent)
+ // the ! provides a default value afterwards, which is never used, but is required for parsing
+ // when the config key is not available;
+ // thus the below prefers the install.unique_label, but falls back to simple name
+ // plus a version identifier *if* the version is explicitly set
+ "${(config['install.unique_label']??)?string(config['install.unique_label']!'X'," +
+ "(entity.entityType.simpleName)+" +
+ "((config['install.version']??)?string('_'+(config['install.version']!'X'),''))" +
+ ")}");
+
+ @SetFromFlag("runDir")
+ AttributeSensorAndConfigKey<String,String> RUN_DIR = new TemplatedStringAttributeSensorAndConfigKey(
+ "run.dir",
+ "Directory from which this software to be run; uses FreeMarker templating format",
+ "${" +
+ "config['"+BrooklynConfigKeys.ONBOX_BASE_DIR.getName()+"']!" +
+ "config['"+BrooklynConfigKeys.BROOKLYN_DATA_DIR.getName()+"']!" +
+ "'ERROR-ONBOX_BASE_DIR-not-set'" +
+ "}" +
+ "\\" +
+ "apps\\${entity.applicationId}\\" +
+ "entities\\${entity.entityType.simpleName}_" +
+ "${entity.id}");
+
+
// 3389 is RDP; 5985 is WinRM (3389 isn't used by Brooklyn, but useful for the end-user subsequently)
ConfigKey<Collection<Integer>> REQUIRED_OPEN_LOGIN_PORTS = ConfigKeys.newConfigKeyWithDefault(
SoftwareProcess.REQUIRED_OPEN_LOGIN_PORTS,
diff --git a/software/base/src/main/java/org/apache/brooklyn/entity/software/base/lifecycle/MachineLifecycleEffectorTasks.java b/software/base/src/main/java/org/apache/brooklyn/entity/software/base/lifecycle/MachineLifecycleEffectorTasks.java
index 26c9ac4..721c3a0 100644
--- a/software/base/src/main/java/org/apache/brooklyn/entity/software/base/lifecycle/MachineLifecycleEffectorTasks.java
+++ b/software/base/src/main/java/org/apache/brooklyn/entity/software/base/lifecycle/MachineLifecycleEffectorTasks.java
@@ -44,7 +44,6 @@
import org.apache.brooklyn.core.config.Sanitizer;
import org.apache.brooklyn.core.effector.EffectorBody;
import org.apache.brooklyn.core.effector.Effectors;
-import org.apache.brooklyn.core.effector.ssh.SshEffectorTasks;
import org.apache.brooklyn.core.entity.Attributes;
import org.apache.brooklyn.core.entity.BrooklynConfigKeys;
import org.apache.brooklyn.core.entity.Entities;
@@ -74,6 +73,7 @@
import org.apache.brooklyn.entity.software.base.SoftwareProcess.StopSoftwareParameters.StopMode;
import org.apache.brooklyn.entity.stock.EffectorStartableImpl.StartParameters;
import org.apache.brooklyn.location.localhost.LocalhostMachineProvisioningLocation;
+import org.apache.brooklyn.location.ssh.CanResolveOnBoxDir;
import org.apache.brooklyn.location.ssh.SshMachineLocation;
import org.apache.brooklyn.util.collections.MutableMap;
import org.apache.brooklyn.util.collections.MutableSet;
@@ -81,14 +81,11 @@
import org.apache.brooklyn.util.core.task.DynamicTasks;
import org.apache.brooklyn.util.core.task.Tasks;
import org.apache.brooklyn.util.core.task.ValueResolverIterator;
-import org.apache.brooklyn.util.core.task.system.ProcessTaskWrapper;
import org.apache.brooklyn.util.exceptions.Exceptions;
import org.apache.brooklyn.util.guava.Maybe;
import org.apache.brooklyn.util.net.UserAndHostAndPort;
import org.apache.brooklyn.util.os.Os;
import org.apache.brooklyn.util.repeat.Repeater;
-import org.apache.brooklyn.util.ssh.BashCommands;
-import org.apache.brooklyn.util.text.Strings;
import org.apache.brooklyn.util.time.Duration;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
@@ -561,6 +558,7 @@
if (base==null) base = machine.getConfig(BrooklynConfigKeys.BROOKLYN_DATA_DIR);
if (base==null) base = entity.getManagementContext().getConfig().getConfig(BrooklynConfigKeys.BROOKLYN_DATA_DIR);
if (base==null) base = "~/brooklyn-managed-processes";
+
if (base.equals("~")) base=".";
if (base.startsWith("~/")) base = "."+base.substring(1);
@@ -569,21 +567,8 @@
if (log.isDebugEnabled()) log.debug("Skipping on-box base dir resolution for "+entity+" at "+machine);
if (!Os.isAbsolutish(base)) base = "~/"+base;
resolvedBase = Os.tidyPath(base);
- } else if (machine instanceof SshMachineLocation) {
- SshMachineLocation ms = (SshMachineLocation)machine;
- ProcessTaskWrapper<Integer> baseTask = SshEffectorTasks.ssh(
- BashCommands.alternatives("mkdir -p \"${BASE_DIR}\"",
- BashCommands.chain(
- BashCommands.sudo("mkdir -p \"${BASE_DIR}\""),
- BashCommands.sudo("chown "+ms.getUser()+" \"${BASE_DIR}\""))),
- "cd ~",
- "cd ${BASE_DIR}",
- "echo BASE_DIR_RESULT':'`pwd`:BASE_DIR_RESULT")
- .environmentVariable("BASE_DIR", base)
- .requiringExitCodeZero()
- .summary("initializing on-box base dir "+base).newTask();
- DynamicTasks.queueIfPossible(baseTask).orSubmitAsync(entity);
- resolvedBase = Strings.getFragmentBetween(baseTask.block().getStdout(), "BASE_DIR_RESULT:", ":BASE_DIR_RESULT");
+ } else if (machine instanceof CanResolveOnBoxDir) {
+ resolvedBase = ((CanResolveOnBoxDir)machine).resolveOnBoxDirFor(entity, base);
}
if (resolvedBase==null) {
if (!Os.isAbsolutish(base)) base = "~/"+base;
diff --git a/software/winrm/src/main/java/org/apache/brooklyn/location/winrm/WinRmMachineLocation.java b/software/winrm/src/main/java/org/apache/brooklyn/location/winrm/WinRmMachineLocation.java
index 9471096..0fd93b7 100644
--- a/software/winrm/src/main/java/org/apache/brooklyn/location/winrm/WinRmMachineLocation.java
+++ b/software/winrm/src/main/java/org/apache/brooklyn/location/winrm/WinRmMachineLocation.java
@@ -32,6 +32,7 @@
import javax.annotation.Nullable;
+import org.apache.brooklyn.api.entity.Entity;
import org.apache.brooklyn.api.location.MachineDetails;
import org.apache.brooklyn.api.location.MachineLocation;
import org.apache.brooklyn.api.location.OsDetails;
@@ -40,19 +41,24 @@
import org.apache.brooklyn.core.config.ConfigKeys;
import org.apache.brooklyn.core.config.ConfigUtils;
import org.apache.brooklyn.core.config.Sanitizer;
+import org.apache.brooklyn.core.effector.ssh.SshEffectorTasks;
import org.apache.brooklyn.core.entity.BrooklynConfigKeys;
import org.apache.brooklyn.core.location.AbstractMachineLocation;
import org.apache.brooklyn.core.location.access.PortForwardManager;
import org.apache.brooklyn.core.location.access.PortForwardManagerLocationResolver;
import org.apache.brooklyn.core.mgmt.ManagementContextInjectable;
+import org.apache.brooklyn.location.ssh.CanResolveOnBoxDir;
import org.apache.brooklyn.util.core.ClassLoaderUtils;
import org.apache.brooklyn.util.core.config.ConfigBag;
import org.apache.brooklyn.util.core.internal.ssh.SshTool;
import org.apache.brooklyn.util.core.internal.winrm.WinRmTool;
import org.apache.brooklyn.util.core.internal.winrm.WinRmToolResponse;
import org.apache.brooklyn.util.core.internal.winrm.winrm4j.Winrm4jTool;
+import org.apache.brooklyn.util.core.task.DynamicTasks;
+import org.apache.brooklyn.util.core.task.system.ProcessTaskWrapper;
import org.apache.brooklyn.util.exceptions.Exceptions;
import org.apache.brooklyn.util.guava.Maybe;
+import org.apache.brooklyn.util.ssh.BashCommands;
import org.apache.brooklyn.util.stream.Streams;
import org.apache.brooklyn.util.text.Strings;
import org.apache.commons.codec.binary.Base64;
@@ -71,7 +77,7 @@
import com.google.common.net.HostAndPort;
import com.google.common.reflect.TypeToken;
-public class WinRmMachineLocation extends AbstractMachineLocation implements MachineLocation {
+public class WinRmMachineLocation extends AbstractMachineLocation implements MachineLocation, CanResolveOnBoxDir {
private static final Logger LOG = LoggerFactory.getLogger(WinRmMachineLocation.class);
@@ -492,4 +498,13 @@
// ));
}
+ @Override
+ public String resolveOnBoxDirFor(Entity entity, String unresolvedPath) {
+ // TODO this is simplistic, writes at c:\ for HOME
+ if (unresolvedPath.startsWith("./") || unresolvedPath.startsWith("~/")) {
+ unresolvedPath = "C:\\"+unresolvedPath.substring(2);
+ }
+ return unresolvedPath.replaceAll("/", "\\");
+ }
+
}
diff --git a/software/winrm/src/main/java/org/apache/brooklyn/util/core/internal/winrm/winrm4j/Winrm4jTool.java b/software/winrm/src/main/java/org/apache/brooklyn/util/core/internal/winrm/winrm4j/Winrm4jTool.java
index 2ea8318..b7ff665 100644
--- a/software/winrm/src/main/java/org/apache/brooklyn/util/core/internal/winrm/winrm4j/Winrm4jTool.java
+++ b/software/winrm/src/main/java/org/apache/brooklyn/util/core/internal/winrm/winrm4j/Winrm4jTool.java
@@ -144,7 +144,12 @@
byte[] inputData = new byte[chunkSize];
int bytesRead;
int expectedFileSize = 0;
+ int i=0;
while ((bytesRead = source.read(inputData)) > 0) {
+ i++;
+
+ LOG.debug("Copying chunk "+i+" to "+destination+" on "+host);
+
byte[] chunk;
if (bytesRead == chunkSize) {
chunk = inputData;
@@ -156,7 +161,8 @@
" -value ([System.Convert]::FromBase64String(\"" + new String(Base64.encodeBase64(chunk)) + "\"))}"));
expectedFileSize += bytesRead;
}
-
+ LOG.debug("Finished copying to "+destination+" on "+host);
+
return new org.apache.brooklyn.util.core.internal.winrm.WinRmToolResponse("", "", 0);
} catch (java.io.IOException e) {
throw propagate(e, "Failed copying to server at "+destination);