Repository: incubator-brooklyn Updated Branches: refs/heads/master 946b5dcf7 -> 05b95f377
Support softwareProcess config open/stop iptables And also dontRequireTtyForSudo Project: http://git-wip-us.apache.org/repos/asf/incubator-brooklyn/repo Commit: http://git-wip-us.apache.org/repos/asf/incubator-brooklyn/commit/90f18811 Tree: http://git-wip-us.apache.org/repos/asf/incubator-brooklyn/tree/90f18811 Diff: http://git-wip-us.apache.org/repos/asf/incubator-brooklyn/diff/90f18811 Branch: refs/heads/master Commit: 90f18811328752fa99dad4af9f71a53135942f32 Parents: 42d6091 Author: Aled Sage <[email protected]> Authored: Sat Jul 25 15:00:57 2015 +0100 Committer: Aled Sage <[email protected]> Committed: Mon Jul 27 13:50:00 2015 +0100 ---------------------------------------------------------------------- .../brooklyn/entity/basic/SoftwareProcess.java | 16 ++ .../entity/software/MachineInitTasks.java | 172 +++++++++++++++++++ .../software/MachineLifecycleEffectorTasks.java | 41 ++++- .../webapp/tomcat/TomcatServerEc2LiveTest.java | 1 + 4 files changed, 222 insertions(+), 8 deletions(-) ---------------------------------------------------------------------- http://git-wip-us.apache.org/repos/asf/incubator-brooklyn/blob/90f18811/software/base/src/main/java/brooklyn/entity/basic/SoftwareProcess.java ---------------------------------------------------------------------- diff --git a/software/base/src/main/java/brooklyn/entity/basic/SoftwareProcess.java b/software/base/src/main/java/brooklyn/entity/basic/SoftwareProcess.java index 1de84de..1570355 100644 --- a/software/base/src/main/java/brooklyn/entity/basic/SoftwareProcess.java +++ b/software/base/src/main/java/brooklyn/entity/basic/SoftwareProcess.java @@ -122,6 +122,22 @@ public interface SoftwareProcess extends Entity, Startable { @Deprecated ConfigKey<String> SUGGESTED_RUN_DIR = BrooklynConfigKeys.SUGGESTED_RUN_DIR; + public static final ConfigKey<Boolean> OPEN_IPTABLES = ConfigKeys.newBooleanConfigKey("openIptables", + "Whether to open the INBOUND_PORTS via iptables rules; " + + "if true then ssh in to run iptables commands, as part of machine provisioning", false); + + public static final ConfigKey<Boolean> STOP_IPTABLES = ConfigKeys.newBooleanConfigKey("stopIptables", + "Whether to stop iptables entirely; " + + "if true then ssh in to stop the iptables service, as part of machine provisioning", false); + + public static final ConfigKey<Boolean> DONT_REQUIRE_TTY_FOR_SUDO = ConfigKeys.newBooleanConfigKey("dontRequireTtyForSudo", + "Whether to explicitly set /etc/sudoers, so don't need tty (will leave unchanged if 'false'); " + + "some machines require a tty for sudo; brooklyn by default does not use a tty " + + "(so that it can get separate error+stdout streams); you can enable a tty as an " + + "option to every ssh command, or you can do it once and " + + "modify the machine so that a tty is not subsequently required.", + false); + /** * Files to be copied to the server before pre-install. * <p> http://git-wip-us.apache.org/repos/asf/incubator-brooklyn/blob/90f18811/software/base/src/main/java/brooklyn/entity/software/MachineInitTasks.java ---------------------------------------------------------------------- diff --git a/software/base/src/main/java/brooklyn/entity/software/MachineInitTasks.java b/software/base/src/main/java/brooklyn/entity/software/MachineInitTasks.java new file mode 100644 index 0000000..dfcb61d --- /dev/null +++ b/software/base/src/main/java/brooklyn/entity/software/MachineInitTasks.java @@ -0,0 +1,172 @@ +/* + * 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 brooklyn.entity.software; + +import java.util.List; +import java.util.concurrent.Callable; + +import org.slf4j.Logger; +import org.slf4j.LoggerFactory; + +import com.google.common.annotations.Beta; +import com.google.common.collect.ImmutableList; +import com.google.common.collect.Iterables; +import com.google.common.collect.Lists; + +import brooklyn.entity.basic.BrooklynTaskTags; +import brooklyn.entity.basic.EntityInternal; +import brooklyn.location.basic.SshMachineLocation; +import brooklyn.management.Task; +import brooklyn.util.net.Protocol; +import brooklyn.util.ssh.BashCommands; +import brooklyn.util.ssh.IptablesCommands; +import brooklyn.util.ssh.IptablesCommands.Chain; +import brooklyn.util.ssh.IptablesCommands.Policy; +import brooklyn.util.task.DynamicTasks; +import brooklyn.util.task.Tasks; +import brooklyn.util.task.ssh.SshTasks; +import brooklyn.util.text.Strings; + +/** + * + */ +@Beta +public class MachineInitTasks { + + // TODO Move somewhere so code can also be called by JcloudsLocation! + + private static final Logger log = LoggerFactory.getLogger(MachineInitTasks.class); + + protected EntityInternal entity() { + return (EntityInternal) BrooklynTaskTags.getTargetOrContextEntity(Tasks.current()); + } + + /** + * Returns a queued {@link Task} which opens the given ports in iptables on the given machine. + */ + public Task<Void> openIptablesAsync(final Iterable<Integer> inboundPorts, final SshMachineLocation machine) { + return DynamicTasks.queue("open iptables "+toTruncatedString(inboundPorts, 6), new Callable<Void>() { + public Void call() { + openIptablesImpl(inboundPorts, machine); + return null; + } + }); + } + + /** + * Returns a queued {@link Task} which stops iptables on the given machine. + */ + public Task<Void> stopIptablesAsync(final SshMachineLocation machine) { + return DynamicTasks.queue("stop iptables", new Callable<Void>() { + public Void call() { + stopIptablesImpl(machine); + return null; + } + }); + } + + /** + * See docs in {@link BashCommands#dontRequireTtyForSudo()} + */ + public Task<Boolean> dontRequireTtyForSudoAsync(final SshMachineLocation machine) { + return SshTasks.dontRequireTtyForSudo(machine, true).newTask().asTask(); + } + + protected void openIptablesImpl(Iterable<Integer> inboundPorts, SshMachineLocation machine) { + if (inboundPorts == null || Iterables.isEmpty(inboundPorts)) { + log.info("No ports to open in iptables (no inbound ports) for {} at {}", machine, this); + } else { + log.info("Opening ports in iptables for {} at {}", entity(), machine); + + List<String> iptablesRules = Lists.newArrayList(); + + if (isLocationFirewalldEnabled(machine)) { + for (Integer port : inboundPorts) { + iptablesRules.add(IptablesCommands.addFirewalldRule(Chain.INPUT, Protocol.TCP, port, Policy.ACCEPT)); + } + } else { + iptablesRules = createIptablesRulesForNetworkInterface(inboundPorts); + iptablesRules.add(IptablesCommands.saveIptablesRules()); + } + List<String> batch = Lists.newArrayList(); + // Some entities, such as Riak (erlang based) have a huge range of ports, which leads to a script that + // is too large to run (fails with a broken pipe). Batch the rules into batches of 50 + for (String rule : iptablesRules) { + batch.add(rule); + if (batch.size() == 50) { + machine.execCommands("Inserting iptables rules, 50 command batch", batch); + batch.clear(); + } + } + if (batch.size() > 0) { + machine.execCommands("Inserting iptables rules", batch); + } + machine.execCommands("List iptables rules", ImmutableList.of(IptablesCommands.listIptablesRule())); + } + } + + protected void stopIptablesImpl(SshMachineLocation machine) { + log.info("Stopping iptables for {} at {}", entity(), machine); + + List<String> cmds = ImmutableList.<String>of(); + if (isLocationFirewalldEnabled(machine)) { + cmds = ImmutableList.of(IptablesCommands.firewalldServiceStop(), IptablesCommands.firewalldServiceStatus()); + } else { + cmds = ImmutableList.of(IptablesCommands.iptablesServiceStop(), IptablesCommands.iptablesServiceStatus()); + } + machine.execCommands("Stopping iptables", cmds); + } + + private List<String> createIptablesRulesForNetworkInterface(Iterable<Integer> ports) { + List<String> iptablesRules = Lists.newArrayList(); + for (Integer port : ports) { + iptablesRules.add(IptablesCommands.insertIptablesRule(Chain.INPUT, Protocol.TCP, port, Policy.ACCEPT)); + } + return iptablesRules; + } + + public boolean isLocationFirewalldEnabled(SshMachineLocation location) { + int result = location.execCommands("checking if firewalld is active", + ImmutableList.of(IptablesCommands.firewalldServiceIsActive())); + if (result == 0) { + return true; + } + + return false; + } + + protected String toTruncatedString(Iterable<?> vals, int maxShown) { + StringBuilder result = new StringBuilder("["); + int shown = 0; + for (Object val : (vals == null ? ImmutableList.of() : vals)) { + if (shown != 0) { + result.append(", "); + } + if (shown < maxShown) { + result.append(Strings.toString(val)); + shown++; + } else { + result.append("..."); + break; + } + } + result.append("]"); + return result.toString(); + } +} http://git-wip-us.apache.org/repos/asf/incubator-brooklyn/blob/90f18811/software/base/src/main/java/brooklyn/entity/software/MachineLifecycleEffectorTasks.java ---------------------------------------------------------------------- diff --git a/software/base/src/main/java/brooklyn/entity/software/MachineLifecycleEffectorTasks.java b/software/base/src/main/java/brooklyn/entity/software/MachineLifecycleEffectorTasks.java index 2ba42f7..ec3ae1c 100644 --- a/software/base/src/main/java/brooklyn/entity/software/MachineLifecycleEffectorTasks.java +++ b/software/base/src/main/java/brooklyn/entity/software/MachineLifecycleEffectorTasks.java @@ -25,11 +25,19 @@ import java.util.Collections; import java.util.List; import java.util.Map; import java.util.concurrent.Callable; + import javax.annotation.Nullable; import org.slf4j.Logger; import org.slf4j.LoggerFactory; +import com.google.common.annotations.Beta; +import com.google.common.base.Preconditions; +import com.google.common.base.Supplier; +import com.google.common.base.Suppliers; +import com.google.common.collect.ImmutableList; +import com.google.common.collect.Iterables; + import brooklyn.config.ConfigKey; import brooklyn.entity.Effector; import brooklyn.entity.Entity; @@ -62,6 +70,7 @@ import brooklyn.location.basic.LocalhostMachineProvisioningLocation; import brooklyn.location.basic.Locations; import brooklyn.location.basic.Machines; import brooklyn.location.basic.SshMachineLocation; +import brooklyn.location.cloud.CloudLocationConfig; import brooklyn.management.Task; import brooklyn.management.TaskFactory; import brooklyn.util.collections.MutableMap; @@ -77,13 +86,6 @@ import brooklyn.util.task.system.ProcessTaskWrapper; import brooklyn.util.text.Strings; import brooklyn.util.time.Duration; -import com.google.common.annotations.Beta; -import com.google.common.base.Preconditions; -import com.google.common.base.Supplier; -import com.google.common.base.Suppliers; -import com.google.common.collect.ImmutableList; -import com.google.common.collect.Iterables; - /** * Default skeleton for start/stop/restart tasks on machines. * <p> @@ -116,6 +118,8 @@ public abstract class MachineLifecycleEffectorTasks { public static final ConfigKey<Duration> STOP_PROCESS_TIMEOUT = ConfigKeys.newConfigKey(Duration.class, "process.stop.timeout", "How long to wait for the processes to be stopped; use null to mean forever", Duration.TWO_MINUTES); + protected final MachineInitTasks machineInitTasks = new MachineInitTasks(); + /** Attaches lifecycle effectors (start, restart, stop) to the given entity post-creation. */ public void attachLifecycleEffectors(Entity entity) { ((EntityInternal) entity).getMutableEntityType().addEffector(newStartEffector()); @@ -344,6 +348,28 @@ public abstract class MachineLifecycleEffectorTasks { entity().setAttribute(Attributes.SSH_ADDRESS, sshAddress); } + if (Boolean.TRUE.equals(entity().getConfig(SoftwareProcess.OPEN_IPTABLES))) { + if (machine instanceof SshMachineLocation) { + Iterable<Integer> inboundPorts = (Iterable<Integer>) machine.config().get(CloudLocationConfig.INBOUND_PORTS); + machineInitTasks.openIptablesAsync(inboundPorts, (SshMachineLocation)machine); + } else { + log.warn("Ignoring flag OPEN_IPTABLES on non-ssh location {}", machine); + } + } + if (Boolean.TRUE.equals(entity().getConfig(SoftwareProcess.STOP_IPTABLES))) { + if (machine instanceof SshMachineLocation) { + machineInitTasks.stopIptablesAsync((SshMachineLocation)machine); + } else { + log.warn("Ignoring flag STOP_IPTABLES on non-ssh location {}", machine); + } + } + if (Boolean.TRUE.equals(entity().getConfig(SoftwareProcess.DONT_REQUIRE_TTY_FOR_SUDO))) { + if (machine instanceof SshMachineLocation) { + machineInitTasks.dontRequireTtyForSudoAsync((SshMachineLocation)machine); + } else { + log.warn("Ignoring flag DONT_REQUIRE_TTY_FOR_SUDO on non-ssh location {}", machine); + } + } resolveOnBoxDir(entity(), machine); preStartCustom(machine); }}); @@ -761,5 +787,4 @@ public abstract class MachineLifecycleEffectorTasks { } return new StopMachineDetails<Integer>("Decommissioned "+machine, 1); } - } http://git-wip-us.apache.org/repos/asf/incubator-brooklyn/blob/90f18811/software/webapp/src/test/java/brooklyn/entity/webapp/tomcat/TomcatServerEc2LiveTest.java ---------------------------------------------------------------------- diff --git a/software/webapp/src/test/java/brooklyn/entity/webapp/tomcat/TomcatServerEc2LiveTest.java b/software/webapp/src/test/java/brooklyn/entity/webapp/tomcat/TomcatServerEc2LiveTest.java index ec7f0a1..e0775b7 100644 --- a/software/webapp/src/test/java/brooklyn/entity/webapp/tomcat/TomcatServerEc2LiveTest.java +++ b/software/webapp/src/test/java/brooklyn/entity/webapp/tomcat/TomcatServerEc2LiveTest.java @@ -47,6 +47,7 @@ public class TomcatServerEc2LiveTest extends AbstractEc2LiveTest { @Override protected void doTest(Location loc) throws Exception { final TomcatServer server = app.createAndManageChild(EntitySpec.create(TomcatServer.class) + .configure(TomcatServer.OPEN_IPTABLES, true) .configure("war", getTestWar())); app.start(ImmutableList.of(loc));
