Convert SaltEntity to a SoftwareProcess entity
Project: http://git-wip-us.apache.org/repos/asf/brooklyn-library/repo Commit: http://git-wip-us.apache.org/repos/asf/brooklyn-library/commit/ac837bca Tree: http://git-wip-us.apache.org/repos/asf/brooklyn-library/tree/ac837bca Diff: http://git-wip-us.apache.org/repos/asf/brooklyn-library/diff/ac837bca Branch: refs/heads/master Commit: ac837bcaba844ffe203edb957630896a48094d54 Parents: 38a9f78 Author: Thomas Bouron <[email protected]> Authored: Tue Aug 29 17:07:30 2017 +0100 Committer: Thomas Bouron <[email protected]> Committed: Tue Aug 29 17:07:30 2017 +0100 ---------------------------------------------------------------------- .../brooklyn/entity/cm/salt/SaltEntity.java | 1 - .../entity/cm/salt/SaltEntityDriver.java | 3 +- .../brooklyn/entity/cm/salt/SaltEntityImpl.java | 86 ++++++ .../entity/cm/salt/SaltEntitySshDriver.java | 269 ++++++++++++++++++ .../brooklyn/entity/cm/salt/SaltHighstate.java | 144 ++++++++++ .../brooklyn/entity/cm/salt/SaltSshTasks.java | 237 ++++++++++++++++ .../brooklyn/entity/cm/salt/SaltUtils.java | 42 +++ .../entity/cm/salt/impl/SaltEntityImpl.java | 81 ------ .../cm/salt/impl/SaltEntitySshDriver.java | 58 ---- .../entity/cm/salt/impl/SaltHighstate.java | 144 ---------- .../salt/impl/SaltLifecycleEffectorTasks.java | 275 ------------------- .../entity/cm/salt/impl/SaltSshDriver.java | 28 -- .../entity/cm/salt/impl/SaltSshTasks.java | 237 ---------------- .../brooklyn/entity/cm/salt/impl/SaltUtils.java | 42 --- .../brooklyn/entity/cm/salt/HighstateTest.java | 1 - 15 files changed, 780 insertions(+), 868 deletions(-) ---------------------------------------------------------------------- http://git-wip-us.apache.org/repos/asf/brooklyn-library/blob/ac837bca/software/cm/salt/src/main/java/org/apache/brooklyn/entity/cm/salt/SaltEntity.java ---------------------------------------------------------------------- diff --git a/software/cm/salt/src/main/java/org/apache/brooklyn/entity/cm/salt/SaltEntity.java b/software/cm/salt/src/main/java/org/apache/brooklyn/entity/cm/salt/SaltEntity.java index 4428658..e308a20 100644 --- a/software/cm/salt/src/main/java/org/apache/brooklyn/entity/cm/salt/SaltEntity.java +++ b/software/cm/salt/src/main/java/org/apache/brooklyn/entity/cm/salt/SaltEntity.java @@ -23,7 +23,6 @@ import com.google.common.reflect.TypeToken; import org.apache.brooklyn.api.catalog.Catalog; import org.apache.brooklyn.api.entity.ImplementedBy; import org.apache.brooklyn.api.sensor.AttributeSensor; -import org.apache.brooklyn.entity.cm.salt.impl.SaltEntityImpl; import org.apache.brooklyn.config.ConfigKey; import org.apache.brooklyn.core.annotation.Effector; import org.apache.brooklyn.core.annotation.EffectorParam; http://git-wip-us.apache.org/repos/asf/brooklyn-library/blob/ac837bca/software/cm/salt/src/main/java/org/apache/brooklyn/entity/cm/salt/SaltEntityDriver.java ---------------------------------------------------------------------- diff --git a/software/cm/salt/src/main/java/org/apache/brooklyn/entity/cm/salt/SaltEntityDriver.java b/software/cm/salt/src/main/java/org/apache/brooklyn/entity/cm/salt/SaltEntityDriver.java index 19a88ed..a435991 100644 --- a/software/cm/salt/src/main/java/org/apache/brooklyn/entity/cm/salt/SaltEntityDriver.java +++ b/software/cm/salt/src/main/java/org/apache/brooklyn/entity/cm/salt/SaltEntityDriver.java @@ -19,7 +19,8 @@ package org.apache.brooklyn.entity.cm.salt; import org.apache.brooklyn.entity.software.base.SoftwareProcessDriver; +import org.apache.brooklyn.util.core.task.system.ProcessTaskWrapper; public interface SaltEntityDriver extends SoftwareProcessDriver { - + ProcessTaskWrapper<Integer> saltCall(String spec); } http://git-wip-us.apache.org/repos/asf/brooklyn-library/blob/ac837bca/software/cm/salt/src/main/java/org/apache/brooklyn/entity/cm/salt/SaltEntityImpl.java ---------------------------------------------------------------------- diff --git a/software/cm/salt/src/main/java/org/apache/brooklyn/entity/cm/salt/SaltEntityImpl.java b/software/cm/salt/src/main/java/org/apache/brooklyn/entity/cm/salt/SaltEntityImpl.java new file mode 100644 index 0000000..e93ebe7 --- /dev/null +++ b/software/cm/salt/src/main/java/org/apache/brooklyn/entity/cm/salt/SaltEntityImpl.java @@ -0,0 +1,86 @@ +/* + * 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.cm.salt; + +import java.util.Set; + +import org.apache.brooklyn.entity.software.base.SoftwareProcessImpl; +import org.apache.brooklyn.util.core.task.system.ProcessTaskWrapper; +import org.slf4j.Logger; +import org.slf4j.LoggerFactory; + +import com.google.common.annotations.Beta; + +@Beta +public class SaltEntityImpl extends SoftwareProcessImpl implements SaltEntity { + private static final Logger LOG = LoggerFactory.getLogger(SaltEntityImpl.class); + + @Override + public Class getDriverInterface() { + return SaltEntityDriver.class; + } + + @Override + public SaltEntityDriver getDriver() { + return (SaltEntityDriver) super.getDriver(); + } + + @Override + public void init() { + super.init(); + + final Set<? extends String> runList = getConfig(SaltConfig.START_STATES); + if (0 == runList.size()) { + throw new IllegalArgumentException("Must have configuration values for 'start_states' (" + + SaltConfig.START_STATES + ")"); + } + + LOG.debug("Run list size is {}", runList.size()); + for (String state : runList) { + LOG.debug("Runlist state: {} ", state); + } + + final Set<? extends String> formulas = getConfig(SaltConfig.SALT_FORMULAS); + LOG.debug("Formulas size: {}", formulas.size()); + for (String formula : formulas) { + LOG.debug("Formula configured: {}", formula); + } + + SaltConfig.SaltMode mode = getConfig(SaltConfig.SALT_MODE); + LOG.debug("Initialize SaltStack {} mode", mode.name()); + } + + @Override + public void populateServiceNotUpDiagnostics() { + // TODO: noop for now; + } + + @Override + public String saltCall(String spec) { + final ProcessTaskWrapper<Integer> command = getDriver().saltCall(spec); + + command.asTask().blockUntilEnded(); + + if (0 == command.getExitCode()) { + return command.getStdout(); + } else { + throw new RuntimeException("Command (" + spec + ") failed with stderr:\n" + command.getStderr() + "\n"); + } + } +} http://git-wip-us.apache.org/repos/asf/brooklyn-library/blob/ac837bca/software/cm/salt/src/main/java/org/apache/brooklyn/entity/cm/salt/SaltEntitySshDriver.java ---------------------------------------------------------------------- diff --git a/software/cm/salt/src/main/java/org/apache/brooklyn/entity/cm/salt/SaltEntitySshDriver.java b/software/cm/salt/src/main/java/org/apache/brooklyn/entity/cm/salt/SaltEntitySshDriver.java new file mode 100644 index 0000000..dac9756 --- /dev/null +++ b/software/cm/salt/src/main/java/org/apache/brooklyn/entity/cm/salt/SaltEntitySshDriver.java @@ -0,0 +1,269 @@ +/* + * 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.cm.salt; + +import static java.util.regex.Pattern.DOTALL; +import static java.util.regex.Pattern.MULTILINE; +import static org.apache.brooklyn.entity.software.base.SoftwareProcess.StopSoftwareParameters.StopMode.ALWAYS; +import static org.apache.brooklyn.entity.software.base.SoftwareProcess.StopSoftwareParameters.StopMode.NEVER; + +import java.util.Set; +import java.util.regex.Matcher; +import java.util.regex.Pattern; + +import org.apache.brooklyn.api.effector.Effector; +import org.apache.brooklyn.api.mgmt.TaskAdaptable; +import org.apache.brooklyn.camp.brooklyn.BrooklynCampConstants; +import org.apache.brooklyn.core.effector.Effectors; +import org.apache.brooklyn.core.entity.Entities; +import org.apache.brooklyn.core.entity.lifecycle.Lifecycle; +import org.apache.brooklyn.core.entity.lifecycle.ServiceStateLogic; +import org.apache.brooklyn.core.entity.trait.Startable; +import org.apache.brooklyn.entity.software.base.AbstractSoftwareProcessSshDriver; +import org.apache.brooklyn.entity.software.base.SoftwareProcess; +import org.apache.brooklyn.location.ssh.SshMachineLocation; +import org.apache.brooklyn.util.collections.MutableSet; +import org.apache.brooklyn.util.core.config.ConfigBag; +import org.apache.brooklyn.util.core.task.DynamicTasks; +import org.apache.brooklyn.util.core.task.TaskBuilder; +import org.apache.brooklyn.util.core.task.system.ProcessTaskFactory; +import org.apache.brooklyn.util.core.task.system.ProcessTaskWrapper; +import org.apache.brooklyn.util.text.Strings; +import org.slf4j.Logger; +import org.slf4j.LoggerFactory; + +import com.google.common.base.Preconditions; +import com.google.common.collect.ImmutableMap; + +// TODO: does this belong to the _.impl package? +public class SaltEntitySshDriver extends AbstractSoftwareProcessSshDriver implements SaltEntityDriver { + private static final Logger LOG = LoggerFactory.getLogger(SaltEntitySshDriver.class); + private static final Pattern FAILURES = Pattern.compile(".*^Failed:\\s+(\\d+)$.*", MULTILINE | DOTALL); + private static final String ZERO = "0"; + + public SaltEntitySshDriver(SaltEntityImpl entity, SshMachineLocation machine) { + super(entity, machine); + } + + @Override + public void install() { + SaltConfig.SaltMode mode = getEntity().config().get(SaltConfig.SALT_MODE); + Preconditions.checkNotNull(mode, "Required config " + SaltConfig.SALT_MODE + " not provided for entity: " + entity); + LOG.info("Starting salt in '{}' mode at '{}'", mode, getMachine().getDisplayName()); + + if (mode != SaltConfig.SaltMode.MASTERLESS) { + // TODO: implement MASTER and MINION + throw new IllegalStateException("Unknown salt mode: " + mode.name()); + } + + final Set<? extends String> startStates = getEntity().config().get(SaltConfig.START_STATES); + final Set<? extends String> formulas = getEntity().config().get(SaltConfig.SALT_FORMULAS); + final Set<? extends String> pillars = getEntity().config().get(SaltConfig.SALT_PILLARS); + final Set<? extends String> pillarUrls = getEntity().config().get(SaltConfig.SALT_PILLAR_URLS); + final String entityId = getEntity().config().get(BrooklynCampConstants.PLAN_ID); + + final ProcessTaskWrapper<Integer> installedAlready = queueAndBlock(SaltSshTasks.isSaltInstalled(false)); + + if (0 != installedAlready.getExitCode()) { + DynamicTasks.queue("install", new Runnable() { + @Override + public void run() { + DynamicTasks.queue( + SaltSshTasks.installSalt(false), + SaltSshTasks.installSaltUtilities(false), + SaltSshTasks.configureForMasterlessOperation(false), + SaltSshTasks.installTopFile(startStates, false)); + + if (Strings.isNonBlank(entityId)) { + DynamicTasks.queue(SaltSshTasks.setMinionId(entityId)); + } + installFormulas(formulas); + installPillars(pillars, pillarUrls); + } + }); + } + } + + @Override + public void customize() { + newScript(CUSTOMIZING).execute(); + } + + @Override + public void launch() { + String name = "apply top states"; + + final ProcessTaskWrapper<Integer> topStates = queueAndBlock(SaltSshTasks.applyTopStates(false).summary(name)); + + // Salt apply returns exit code 0 even upon failure so check the stdout. + final Matcher failCount = FAILURES.matcher(topStates.getStdout()); + if (!failCount.matches() || !ZERO.equals(failCount.group(1))) { + LOG.warn("Encountered error in applying Salt top states: {}", topStates.getStdout()); + throw new RuntimeException( + "Encountered error in applying Salt top states, see '" + name + "' in activities for details"); + } + } + + @Override + public void runPostLaunchCommand() { + super.runPostLaunchCommand(); + + final ProcessTaskWrapper<String> retrieveHighstate = SaltSshTasks.retrieveHighstate(); + final ProcessTaskWrapper<String> highstate = DynamicTasks.queue(retrieveHighstate).block(); + String stateDescription = highstate.get(); + + SaltHighstate.applyHighstate(stateDescription, getEntity()); + + getEntity().sensors().set(SoftwareProcess.SERVICE_UP, true); + } + + @Override + public boolean isRunning() { + return true; + } + + @Override + public void stop() { + final Set<? extends String> stopStates = getEntity().config().get(SaltConfig.STOP_STATES); + LOG.debug("Executing Salt stopProcessesAtMachine with states {}", stopStates); + if (stopStates.isEmpty()) { + stopBasedOnStartStates(); + } else { + applyStates(stopStates); + } + } + + @Override + public void restart() { + ServiceStateLogic.setExpectedState(getEntity(), Lifecycle.STOPPING); + + try { + final Set<? extends String> restartStates = getEntity().config().get(SaltConfig.RESTART_STATES); + LOG.debug("Executing Salt restart with states {}", restartStates); + if (restartStates.isEmpty()) { + restartBasedOnStartStates(); + } else { + applyStates(restartStates); + } + ServiceStateLogic.setExpectedState(getEntity(), Lifecycle.RUNNING); + } catch (Exception e) { + getEntity().sensors().set(ServiceStateLogic.SERVICE_NOT_UP_DIAGNOSTICS, + ImmutableMap.<String, Object>of("restart", e.getMessage())); + ServiceStateLogic.setExpectedState(getEntity(), Lifecycle.ON_FIRE); + } + } + + @Override + public ProcessTaskWrapper<Integer> saltCall(String spec) { + return DynamicTasks.queue(SaltSshTasks.saltCall(spec)); + } + + private ProcessTaskWrapper<Integer> queueAndBlock(ProcessTaskFactory<Integer> taskFactory) { + final ProcessTaskWrapper<Integer> queued = DynamicTasks.queue(taskFactory); + queued.asTask().blockUntilEnded(); + return queued; + } + + private void installFormulas(Set<? extends String> formulas) { + if (formulas.size() > 0) { + DynamicTasks.queue(SaltSshTasks.enableFileRoots(false)); + + final TaskBuilder<Object> formulaTasks = TaskBuilder.builder().displayName("install formulas"); + for (String url : formulas) { + formulaTasks.add(SaltSshTasks.installSaltFormula(url, false).newTask()); + } + DynamicTasks.queue(formulaTasks.build()); + } + } + + private void installPillars(Set<? extends String> pillars, Set<? extends String> pillarUrls) { + if (pillarUrls.size() > 0) { + final TaskBuilder<Object> pillarTasks = TaskBuilder.builder().displayName("install pillars"); + pillarTasks.add(SaltSshTasks.invokeSaltUtility("init_pillar_config", null, false) + .summary("init pillar config").newTask()); + for (String pillar : pillars) { + pillarTasks.add(SaltSshTasks.addPillarToTop(pillar, false).newTask()); + } + for (String url : pillarUrls) { + pillarTasks.add(SaltSshTasks.installSaltPillar(url, false).newTask()); + } + DynamicTasks.queue(pillarTasks.build()); + } + } + + private MutableSet<String> addSuffix(Set<? extends String> names, String suffix) { + final MutableSet<String> suffixed = MutableSet.of(); + for (String name : names) { + suffixed.add(name + suffix); + } + return suffixed; + } + + private void applyStates(Set<? extends String> states) { + for (String state : states) { + DynamicTasks.queue(SaltSshTasks.applyState(state, false).summary("apply state " + state)); + } + } + + private void stopBasedOnStartStates() { + final Set<? extends String> startStates = getEntity().config().get(SaltConfig.START_STATES); + final MutableSet<String> stopStates = addSuffix(startStates, ".stop"); + final ProcessTaskWrapper<Integer> checkStops = + queueAndBlock(SaltSshTasks.verifyStates(stopStates, false).summary("check stop states")); + if (0 != checkStops.getExitCode()) { + throw new RuntimeException("No stop_states configured and not all start_states have matching stop states"); + } else { + applyStates(stopStates); + } + } + + private void restartBasedOnStartStates() { + final Set<? extends String> startStates = getEntity().config().get(SaltConfig.START_STATES); + final MutableSet<String> restartStates = addSuffix(startStates, ".restart"); + final ProcessTaskWrapper<Integer> queued = + queueAndBlock(SaltSshTasks.findStates(restartStates, false).summary("check restart states")); + final String stdout = queued.getStdout(); + String[] foundStates = Strings.isNonBlank(stdout) ? stdout.split("\\n") : null; + + if (restartStates.size() > 0 && foundStates != null && (restartStates.size() == foundStates.length)) { + // each state X listed in start_states has a matching state of the form X.restart; we apply them. + LOG.debug("All start_states have matching restart states, applying these"); + applyStates(restartStates); + + } else if (foundStates != null && foundStates.length > 0) { + // only *some* of the states have a matching restart; we treat this as a fail + LOG.debug("Only some start_states have matching restart states, treating as restart failure") ; + throw new RuntimeException("unable to find restart state for all applied states"); + + } else { + // else we apply "stop" effector (with parameters to stop processes not machine) then "start" + // (and in that effector we'd fail if stop was not well-defined) + LOG.debug("No stop states available, invoking stop and start effectors"); + invokeEffector(Startable.STOP, ConfigBag.newInstance() + .configure(SoftwareProcess.StopSoftwareParameters.STOP_PROCESS_MODE, ALWAYS) + .configure(SoftwareProcess.StopSoftwareParameters.STOP_MACHINE_MODE, NEVER)); + invokeEffector(Startable.START, ConfigBag.EMPTY); + } + } + + private void invokeEffector(Effector<Void> effector, ConfigBag config) { + final TaskAdaptable<Void> stop = Entities.submit(getEntity(), Effectors.invocation(getEntity(), effector, config)); + stop.asTask().blockUntilEnded(); + } +} http://git-wip-us.apache.org/repos/asf/brooklyn-library/blob/ac837bca/software/cm/salt/src/main/java/org/apache/brooklyn/entity/cm/salt/SaltHighstate.java ---------------------------------------------------------------------- diff --git a/software/cm/salt/src/main/java/org/apache/brooklyn/entity/cm/salt/SaltHighstate.java b/software/cm/salt/src/main/java/org/apache/brooklyn/entity/cm/salt/SaltHighstate.java new file mode 100644 index 0000000..16a4224 --- /dev/null +++ b/software/cm/salt/src/main/java/org/apache/brooklyn/entity/cm/salt/SaltHighstate.java @@ -0,0 +1,144 @@ +/* + * 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.cm.salt; + +import com.google.common.reflect.TypeToken; + +import org.apache.brooklyn.api.entity.Entity; +import org.apache.brooklyn.api.sensor.AttributeSensor; +import org.apache.brooklyn.core.sensor.Sensors; +import org.apache.brooklyn.util.collections.MutableList; +import org.apache.brooklyn.util.collections.MutableMap; +import org.apache.brooklyn.util.text.Strings; +import org.apache.brooklyn.util.yaml.Yamls; +import org.slf4j.Logger; +import org.slf4j.LoggerFactory; + +import java.util.List; +import java.util.Map; + +/** + * Utility for setting a Salt highstate description on entity sensors. + */ +public class SaltHighstate { + + private static final Logger LOG = LoggerFactory.getLogger(SaltHighstate.class); + + public static final String HIGHSTATE_SENSOR_PREFIX = "salt.state"; + + @SuppressWarnings("serial") + public static TypeToken<Map<String, Object>> STATE_FUNCTION_TYPE = + new TypeToken<Map<String, Object>>() {}; + + private SaltHighstate() {} + + public static void applyHighstate(String contents, Entity entity) { + + final String adaptedYaml = adaptForSaltYamlTypes(contents); + LOG.debug("Parsing Salt highstate yaml:\n{}", adaptedYaml); + final Iterable<Object> objects = Yamls.parseAll(adaptedYaml); + + for (Object entry: objects) { + @SuppressWarnings("unchecked") + final Map<String, Object> scopeMap = Yamls.getAs(entry, Map.class); + applyStatesInScope(entity, scopeMap); + } + } + + private static void applyStatesInScope(Entity entity, Map<String, Object> scopeMap) { + for (String scope: scopeMap.keySet()) { + @SuppressWarnings("unchecked") + final Map<String, Object> stateMap = Yamls.getAs(scopeMap.get(scope), Map.class); + for (String id: stateMap.keySet()) { + applyStateSensor(id, stateMap.get(id), entity); + } + } + } + + + private static String adaptForSaltYamlTypes(String description) { + return description.replaceAll("!!python/unicode\\s+", ""); + } + + @SuppressWarnings("unchecked") + private static void applyStateSensor(String id, Object stateData, Entity entity) { + if (isSaltInternal(id)) { + return; + } + addStateSensor(id, entity); + try { + Map<String, List<Object>> stateInfo = (Map<String, List<Object>>)stateData; + for (String stateModule : stateInfo.keySet()) { + addStateModuleValue(id, entity, stateInfo, stateModule); + } + } catch (ClassCastException e) { + LOG.info("Unexpected structure for {} state, skipping ({})", id, e.getMessage()); + } + } + + @SuppressWarnings("unchecked") + private static void addStateModuleValue(String id, Entity entity, Map<String, List<Object>> stateInfo, + String stateModule) { + + if (isSaltInternal(stateModule)) { + return; + } + try { + final List<Object> stateEntries = stateInfo.get(stateModule); + String stateFunction = ""; + Map<String, Object> moduleSettings = MutableMap.of(); + for (Object entry : stateEntries) { + if (entry instanceof Map) { + moduleSettings.putAll((Map<String, Object>)entry); + } else { + stateFunction = entry.toString(); + } + } + + final String name = sensorName(id, stateModule, stateFunction); + final AttributeSensor<Map<String, Object>> newSensor = + Sensors.newSensor(STATE_FUNCTION_TYPE, HIGHSTATE_SENSOR_PREFIX + "." + name, name); + entity.sensors().set(newSensor, moduleSettings); + + LOG.debug("Sensor set for: {}", moduleSettings); + } catch (ClassCastException e) { + LOG.info("Unexpected structure for state module {}, skipping ({})", id + "." + stateModule, e.getMessage()); + } + } + + private static String sensorName(String... parts) { + return Strings.join(parts, "."); + } + + + private static void addStateSensor(String state, Entity entity) { + List<String> states = entity.sensors().get(SaltEntityImpl.STATES); + if (null == states || !states.contains(state)) { + if (null == states) { + states = MutableList.of(); + } + states.add(state); + entity.sensors().set(SaltEntityImpl.STATES, states); + } + } + + private static boolean isSaltInternal(String module) { + return module.startsWith("__"); + } +} http://git-wip-us.apache.org/repos/asf/brooklyn-library/blob/ac837bca/software/cm/salt/src/main/java/org/apache/brooklyn/entity/cm/salt/SaltSshTasks.java ---------------------------------------------------------------------- diff --git a/software/cm/salt/src/main/java/org/apache/brooklyn/entity/cm/salt/SaltSshTasks.java b/software/cm/salt/src/main/java/org/apache/brooklyn/entity/cm/salt/SaltSshTasks.java new file mode 100644 index 0000000..03daf8e --- /dev/null +++ b/software/cm/salt/src/main/java/org/apache/brooklyn/entity/cm/salt/SaltSshTasks.java @@ -0,0 +1,237 @@ +/* + * 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.cm.salt; + +import org.apache.brooklyn.api.mgmt.TaskAdaptable; +import org.apache.brooklyn.api.mgmt.TaskFactory; +import org.apache.brooklyn.core.effector.EffectorTasks; +import org.apache.brooklyn.core.effector.ssh.SshEffectorTasks; +import org.apache.brooklyn.core.effector.ssh.SshEffectorTasks.SshEffectorTaskFactory; +import org.apache.brooklyn.util.collections.MutableList; +import org.apache.brooklyn.util.core.ResourceUtils; +import org.apache.brooklyn.util.core.file.ArchiveTasks; +import org.apache.brooklyn.util.core.task.TaskBuilder; +import org.apache.brooklyn.util.core.task.Tasks; +import org.apache.brooklyn.util.core.task.ssh.SshPutTaskFactory; +import org.apache.brooklyn.util.core.task.system.ProcessTaskFactory; +import org.apache.brooklyn.util.core.task.system.ProcessTaskWrapper; +import org.apache.brooklyn.util.ssh.BashCommands; +import org.apache.brooklyn.util.text.Identifiers; +import org.apache.brooklyn.util.text.Strings; + +import java.util.List; +import java.util.Set; + +import static org.apache.brooklyn.core.effector.ssh.SshEffectorTasks.ssh; +import static org.apache.brooklyn.util.ssh.BashCommands.sudo; + + +public class SaltSshTasks { + + private static final String UTILITY_SCRIPT = "salt_utilities.sh"; + + private SaltSshTasks() { + // Utility class + } + + public static TaskFactory<?> installSalt(boolean force) { + // TODO: ignore force? + return sshCommands( + BashCommands.commandToDownloadUrlAs("https://bootstrap.saltstack.com", "install_salt.sh"), + sudo("sh install_salt.sh") + ) + .summary("install salt"); + } + + public static SshEffectorTaskFactory<Integer> isSaltInstalled(boolean force) { + return invokeSaltUtility("salt_installed", null, true).summary("check installed"); + } + + public static TaskFactory<?> configureForMasterlessOperation(boolean force) { + // TODO: ignore force? + return ssh(sudo("sed -i '/^#file_client/c file_client: local' /etc/salt/minion")) + .summary("configure masterless"); + } + + + public static TaskFactory<?> enableFileRoots(boolean force) { + return sshCommands( + "grep ^file_roots /etc/salt/minion || {", + "cat /etc/salt/minion > /tmp/minion.update", + "cat >> /tmp/minion.update << BROOKLYN_EOF", + "file_roots:", + " base:", + " - /srv/salt/", + "BROOKLYN_EOF", + sudo("mv /tmp/minion.update /etc/salt/minion"), + "}" + ) + .requiringExitCodeZero() + .summary("enable file_roots"); + } + + + public static TaskFactory<?> addPillarToTop(String pillar, boolean b) { + return invokeSaltUtility("add_pillar_to_top", pillar, false) + .summary("Add pillar " + pillar + " to top"); + } + + public static TaskFactory<?> installSaltPillar(final String pillarUrl, boolean force) { + return new TaskFactory<TaskAdaptable<?>>() { + @Override + public TaskAdaptable<?> newTask() { + TaskBuilder<Void> tb = Tasks.<Void>builder().displayName(pillarUrl); + String tempDownloadDir = "/tmp/download-" + Identifiers.makeRandomId(12); + + tb.add(ArchiveTasks.deploy(null,null,pillarUrl,EffectorTasks.findSshMachine(), + tempDownloadDir,false,null,null).newTask()); + + tb.add(ssh("cd " + tempDownloadDir + " ; " + sudo("mv * /srv/pillar")).newTask()); + + return tb.build(); + } + }; + } + + public static TaskFactory<?> installSaltFormula(final String formulaUrl, boolean force) { + return new TaskFactory<TaskAdaptable<?>>() { + @Override + public TaskAdaptable<?> newTask() { + TaskBuilder<Void> tb = Tasks.<Void>builder().displayName(formulaUrl); + + String tempDirectoryForUnpack = "/tmp/download-" + Identifiers.makeRandomId(12); + + tb.add(ArchiveTasks.deploy(null, null, formulaUrl, EffectorTasks.findSshMachine(), + tempDirectoryForUnpack, false, null, null).newTask()); + + // TODO move this into salt_utilities.sh + String installCmd = BashCommands.chain( + "cd "+tempDirectoryForUnpack, + "EXPANDED_DIR=`ls`", + BashCommands.requireTest("`ls | wc -w` -eq 1", + "The deployed archive "+ formulaUrl +" must contain exactly one directory"), + "sudo mkdir -p /srv/formula", + "sudo mv $EXPANDED_DIR /srv/formula/", + // sed command below relies on enableFileRoots behaviour of append file_roots to end of config file + "sudo sed -i \"$ a\\ - /srv/formula/$EXPANDED_DIR\" /etc/salt/minion", + "cd ..", + "rm -rf '"+tempDirectoryForUnpack+"'"); + tb.add(ssh(installCmd).summary("installing " + formulaUrl + " states to /srv/formula") + .requiringExitCodeZero().newTask()); + + return tb.build(); + } + }; + } + + public static TaskFactory<?> installTopFile(final Set<? extends String> runList, boolean force) { + // TODO: ignore force? + // TODO: move this into salt_utilities.sh + final MutableList.Builder<String> topBuilder = MutableList.<String>builder() + .add("cat > /tmp/top.sls << BROOKLYN_EOF") + .add("base:") + .add(" '*':"); + for (String stateName: runList) { + topBuilder.add(" - " + stateName); + } + topBuilder.add("BROOKLYN_EOF"); + List<String> createTempTopFile = topBuilder.build(); + + List<String> commands = MutableList.<String>builder() + .add(sudo("mkdir -p /srv/salt")) + .add(Strings.join(createTempTopFile, "\n")) + .add(sudo("mv /tmp/top.sls /srv/salt")) + .build(); + return ssh(commands).summary("create top.sls file"); + } + + public static ProcessTaskFactory<Integer> applyTopStates(boolean force) { + return saltCall("state.apply"); + } + + public static SshEffectorTaskFactory<Integer> applyState(String state, boolean force) { + return saltCall("state.apply " + state); + } + + public static SshEffectorTaskFactory<Integer> saltCall(String command) { + return ssh(sudo("salt-call --local " + command)).allowingNonZeroExitCode(); + } + + public static ProcessTaskWrapper<String> retrieveHighstate() { + return saltCall("state.show_highstate --out=yaml") + .summary("retrieve highstate") + .requiringZeroAndReturningStdout() + .newTask(); + } + + public static TaskFactory<?> installSaltUtilities(boolean force) { + return new TaskFactory<TaskAdaptable<?>>() { + @Override + public TaskAdaptable<?> newTask() { + final TaskBuilder<Void> builder = Tasks.<Void>builder() + .displayName("install salt utilities") + .add(installScript(UTILITY_SCRIPT, "install salt shell utils").newTask()) + .add(ssh(sudo("mv /tmp/" + UTILITY_SCRIPT + " /etc/salt")).newTask()); + return builder.build(); + } + }; + } + + private static SshPutTaskFactory installScript(String name, String description) { + return SshEffectorTasks.put("/tmp/" + name) + .contents(ResourceUtils.create().getResourceFromUrl("classpath:" + name)) + .summary(description); + } + + public static SshEffectorTaskFactory<Integer> verifyStates(Set<String> states, boolean force) { + return invokeSaltUtility("verify_states", Strings.join(states, " "), true); + } + + public static SshEffectorTaskFactory<Integer> findStates(Set<String> states, boolean force) { + return invokeSaltUtility("find_states", Strings.join(states, " "), true); + } + + // Simple invocation of a function from salt_utilities.sh, optionally allowing it to fail. + // Uses single quoted bash command, so args mustn't contain single quotes. + public static SshEffectorTaskFactory<Integer> invokeSaltUtility(String functionName, String args, boolean permitFailure) { + + final SshEffectorTaskFactory<Integer> taskFactory = + ssh(sudo("/bin/bash -c '. /etc/salt/salt_utilities.sh ; " + functionName + " " + args + "'")) + .summary(functionName); + + if (permitFailure) { + taskFactory.allowingNonZeroExitCode(); + } else { + taskFactory.requiringExitCodeZero(); + } + return taskFactory; + + } + + public static SshEffectorTaskFactory<Integer> sshCommands(String line, String... lines) { + final MutableList.Builder<String> builder = MutableList.<String>builder() + .add(line); + builder.addAll(lines); + return ssh(Strings.join(builder.build(), "\n")); + } + + public static SshEffectorTaskFactory<Integer> setMinionId(final String entityId) { + return invokeSaltUtility("set_minion_id", entityId, true); + } +} http://git-wip-us.apache.org/repos/asf/brooklyn-library/blob/ac837bca/software/cm/salt/src/main/java/org/apache/brooklyn/entity/cm/salt/SaltUtils.java ---------------------------------------------------------------------- diff --git a/software/cm/salt/src/main/java/org/apache/brooklyn/entity/cm/salt/SaltUtils.java b/software/cm/salt/src/main/java/org/apache/brooklyn/entity/cm/salt/SaltUtils.java new file mode 100644 index 0000000..fc18a86 --- /dev/null +++ b/software/cm/salt/src/main/java/org/apache/brooklyn/entity/cm/salt/SaltUtils.java @@ -0,0 +1,42 @@ +/* + * 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.cm.salt; + +import org.apache.brooklyn.api.location.LocationDefinition; +import org.apache.brooklyn.api.mgmt.ManagementContext; + +public class SaltUtils { + + private SaltUtils() { + // Utility class + } + + public static ManagementContext.PropertiesReloadListener propertiesReloadListener( + ManagementContext mc, LocationDefinition definition) { + + return new ManagementContext.PropertiesReloadListener() { + private static final long serialVersionUID = 1L; + @Override + public void reloaded() { + // TODO: implement properties reload logic + } + }; + } + +} http://git-wip-us.apache.org/repos/asf/brooklyn-library/blob/ac837bca/software/cm/salt/src/main/java/org/apache/brooklyn/entity/cm/salt/impl/SaltEntityImpl.java ---------------------------------------------------------------------- diff --git a/software/cm/salt/src/main/java/org/apache/brooklyn/entity/cm/salt/impl/SaltEntityImpl.java b/software/cm/salt/src/main/java/org/apache/brooklyn/entity/cm/salt/impl/SaltEntityImpl.java deleted file mode 100644 index bea2581..0000000 --- a/software/cm/salt/src/main/java/org/apache/brooklyn/entity/cm/salt/impl/SaltEntityImpl.java +++ /dev/null @@ -1,81 +0,0 @@ -/* - * 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.cm.salt.impl; - -import com.google.common.annotations.Beta; -import org.apache.brooklyn.entity.cm.salt.SaltConfig; -import org.apache.brooklyn.entity.cm.salt.SaltEntity; -import org.apache.brooklyn.entity.stock.EffectorStartableImpl; -import org.apache.brooklyn.util.core.task.DynamicTasks; -import org.apache.brooklyn.util.core.task.system.ProcessTaskWrapper; -import org.slf4j.Logger; -import org.slf4j.LoggerFactory; - -import java.util.Set; - -@Beta -public class SaltEntityImpl extends EffectorStartableImpl implements SaltEntity { - private static final Logger LOG = LoggerFactory.getLogger(SaltEntityImpl.class); - - public SaltEntityImpl() { - super(); - } - - @Override - public void init() { - super.init(); - - final Set<? extends String> runList = getConfig(SaltConfig.START_STATES); - if (0 == runList.size()) { - throw new IllegalArgumentException("Must have configuration values for 'start_states' (" - + SaltConfig.START_STATES + ")"); - } - - LOG.debug("Run list size is {}", runList.size()); - for (String state : runList) { - LOG.debug("Runlist state: {} ", state); - } - - final Set<? extends String> formulas = getConfig(SaltConfig.SALT_FORMULAS); - LOG.debug("Formulas size: {}", formulas.size()); - for (String formula : formulas) { - LOG.debug("Formula configured: {}", formula); - } - - SaltConfig.SaltMode mode = getConfig(SaltConfig.SALT_MODE); - LOG.debug("Initialize SaltStack {} mode", mode.name()); - new SaltLifecycleEffectorTasks().attachLifecycleEffectors(this); - } - - @Override - public void populateServiceNotUpDiagnostics() { - // TODO: noop for now; - } - - @Override - public String saltCall(String spec) { - final ProcessTaskWrapper<Integer> command = DynamicTasks.queue(SaltSshTasks.saltCall(spec)); - command.asTask().blockUntilEnded(); - if (0 == command.getExitCode()) { - return command.getStdout(); - } else { - throw new RuntimeException("Command (" + spec + ") failed with stderr:\n" + command.getStderr() + "\n"); - } - } -} http://git-wip-us.apache.org/repos/asf/brooklyn-library/blob/ac837bca/software/cm/salt/src/main/java/org/apache/brooklyn/entity/cm/salt/impl/SaltEntitySshDriver.java ---------------------------------------------------------------------- diff --git a/software/cm/salt/src/main/java/org/apache/brooklyn/entity/cm/salt/impl/SaltEntitySshDriver.java b/software/cm/salt/src/main/java/org/apache/brooklyn/entity/cm/salt/impl/SaltEntitySshDriver.java deleted file mode 100644 index 7531c27..0000000 --- a/software/cm/salt/src/main/java/org/apache/brooklyn/entity/cm/salt/impl/SaltEntitySshDriver.java +++ /dev/null @@ -1,58 +0,0 @@ -/* - * 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.cm.salt.impl; - -import org.apache.brooklyn.entity.cm.salt.SaltEntityDriver; -import org.apache.brooklyn.entity.software.base.AbstractSoftwareProcessSshDriver; -import org.apache.brooklyn.location.ssh.SshMachineLocation; - -// TODO: does this belong to the _.impl package? -public class SaltEntitySshDriver extends AbstractSoftwareProcessSshDriver implements SaltEntityDriver { - - public SaltEntitySshDriver(SaltEntityImpl entity, SshMachineLocation machine) { - super(entity, machine); - } - - @Override - public SaltEntityImpl getEntity() { - return (SaltEntityImpl) super.getEntity(); - } - - @Override - public void install() { - } - - @Override - public void customize() { - } - - @Override - public void launch() { - } - - @Override - public boolean isRunning() { - return true; - } - - @Override - public void stop() { - } - -} http://git-wip-us.apache.org/repos/asf/brooklyn-library/blob/ac837bca/software/cm/salt/src/main/java/org/apache/brooklyn/entity/cm/salt/impl/SaltHighstate.java ---------------------------------------------------------------------- diff --git a/software/cm/salt/src/main/java/org/apache/brooklyn/entity/cm/salt/impl/SaltHighstate.java b/software/cm/salt/src/main/java/org/apache/brooklyn/entity/cm/salt/impl/SaltHighstate.java deleted file mode 100644 index fdae93b..0000000 --- a/software/cm/salt/src/main/java/org/apache/brooklyn/entity/cm/salt/impl/SaltHighstate.java +++ /dev/null @@ -1,144 +0,0 @@ -/* - * 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.cm.salt.impl; - -import com.google.common.reflect.TypeToken; - -import org.apache.brooklyn.api.entity.Entity; -import org.apache.brooklyn.api.sensor.AttributeSensor; -import org.apache.brooklyn.core.sensor.Sensors; -import org.apache.brooklyn.util.collections.MutableList; -import org.apache.brooklyn.util.collections.MutableMap; -import org.apache.brooklyn.util.text.Strings; -import org.apache.brooklyn.util.yaml.Yamls; -import org.slf4j.Logger; -import org.slf4j.LoggerFactory; - -import java.util.List; -import java.util.Map; - -/** - * Utility for setting a Salt highstate description on entity sensors. - */ -public class SaltHighstate { - - private static final Logger LOG = LoggerFactory.getLogger(SaltHighstate.class); - - public static final String HIGHSTATE_SENSOR_PREFIX = "salt.state"; - - @SuppressWarnings("serial") - public static TypeToken<Map<String, Object>> STATE_FUNCTION_TYPE = - new TypeToken<Map<String, Object>>() {}; - - private SaltHighstate() {} - - public static void applyHighstate(String contents, Entity entity) { - - final String adaptedYaml = adaptForSaltYamlTypes(contents); - LOG.debug("Parsing Salt highstate yaml:\n{}", adaptedYaml); - final Iterable<Object> objects = Yamls.parseAll(adaptedYaml); - - for (Object entry: objects) { - @SuppressWarnings("unchecked") - final Map<String, Object> scopeMap = Yamls.getAs(entry, Map.class); - applyStatesInScope(entity, scopeMap); - } - } - - private static void applyStatesInScope(Entity entity, Map<String, Object> scopeMap) { - for (String scope: scopeMap.keySet()) { - @SuppressWarnings("unchecked") - final Map<String, Object> stateMap = Yamls.getAs(scopeMap.get(scope), Map.class); - for (String id: stateMap.keySet()) { - applyStateSensor(id, stateMap.get(id), entity); - } - } - } - - - private static String adaptForSaltYamlTypes(String description) { - return description.replaceAll("!!python/unicode\\s+", ""); - } - - @SuppressWarnings("unchecked") - private static void applyStateSensor(String id, Object stateData, Entity entity) { - if (isSaltInternal(id)) { - return; - } - addStateSensor(id, entity); - try { - Map<String, List<Object>> stateInfo = (Map<String, List<Object>>)stateData; - for (String stateModule : stateInfo.keySet()) { - addStateModuleValue(id, entity, stateInfo, stateModule); - } - } catch (ClassCastException e) { - LOG.info("Unexpected structure for {} state, skipping ({})", id, e.getMessage()); - } - } - - @SuppressWarnings("unchecked") - private static void addStateModuleValue(String id, Entity entity, Map<String, List<Object>> stateInfo, - String stateModule) { - - if (isSaltInternal(stateModule)) { - return; - } - try { - final List<Object> stateEntries = stateInfo.get(stateModule); - String stateFunction = ""; - Map<String, Object> moduleSettings = MutableMap.of(); - for (Object entry : stateEntries) { - if (entry instanceof Map) { - moduleSettings.putAll((Map<String, Object>)entry); - } else { - stateFunction = entry.toString(); - } - } - - final String name = sensorName(id, stateModule, stateFunction); - final AttributeSensor<Map<String, Object>> newSensor = - Sensors.newSensor(STATE_FUNCTION_TYPE, HIGHSTATE_SENSOR_PREFIX + "." + name, name); - entity.sensors().set(newSensor, moduleSettings); - - LOG.debug("Sensor set for: {}", moduleSettings); - } catch (ClassCastException e) { - LOG.info("Unexpected structure for state module {}, skipping ({})", id + "." + stateModule, e.getMessage()); - } - } - - private static String sensorName(String... parts) { - return Strings.join(parts, "."); - } - - - private static void addStateSensor(String state, Entity entity) { - List<String> states = entity.sensors().get(SaltEntityImpl.STATES); - if (null == states || !states.contains(state)) { - if (null == states) { - states = MutableList.of(); - } - states.add(state); - entity.sensors().set(SaltEntityImpl.STATES, states); - } - } - - private static boolean isSaltInternal(String module) { - return module.startsWith("__"); - } -} http://git-wip-us.apache.org/repos/asf/brooklyn-library/blob/ac837bca/software/cm/salt/src/main/java/org/apache/brooklyn/entity/cm/salt/impl/SaltLifecycleEffectorTasks.java ---------------------------------------------------------------------- diff --git a/software/cm/salt/src/main/java/org/apache/brooklyn/entity/cm/salt/impl/SaltLifecycleEffectorTasks.java b/software/cm/salt/src/main/java/org/apache/brooklyn/entity/cm/salt/impl/SaltLifecycleEffectorTasks.java deleted file mode 100644 index 2051ed6..0000000 --- a/software/cm/salt/src/main/java/org/apache/brooklyn/entity/cm/salt/impl/SaltLifecycleEffectorTasks.java +++ /dev/null @@ -1,275 +0,0 @@ -/* - * 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.cm.salt.impl; - -import static java.util.regex.Pattern.DOTALL; -import static java.util.regex.Pattern.MULTILINE; -import static org.apache.brooklyn.entity.software.base.SoftwareProcess.StopSoftwareParameters.StopMode.ALWAYS; -import static org.apache.brooklyn.entity.software.base.SoftwareProcess.StopSoftwareParameters.StopMode.NEVER; - -import java.util.Set; -import java.util.regex.Matcher; -import java.util.regex.Pattern; - -import org.apache.brooklyn.api.effector.Effector; -import org.apache.brooklyn.api.entity.Entity; -import org.apache.brooklyn.api.location.MachineLocation; -import org.apache.brooklyn.api.mgmt.TaskAdaptable; -import org.apache.brooklyn.camp.brooklyn.BrooklynCampConstants; -import org.apache.brooklyn.core.effector.Effectors; -import org.apache.brooklyn.core.entity.Entities; -import org.apache.brooklyn.core.entity.lifecycle.Lifecycle; -import org.apache.brooklyn.core.entity.lifecycle.ServiceStateLogic; -import org.apache.brooklyn.core.entity.trait.Startable; -import org.apache.brooklyn.entity.cm.salt.SaltConfig; -import org.apache.brooklyn.entity.software.base.SoftwareProcess; -import org.apache.brooklyn.entity.software.base.SoftwareProcess.StopSoftwareParameters; -import org.apache.brooklyn.entity.software.base.lifecycle.MachineLifecycleEffectorTasks; -import org.apache.brooklyn.util.collections.MutableSet; -import org.apache.brooklyn.util.core.config.ConfigBag; -import org.apache.brooklyn.util.core.task.DynamicTasks; -import org.apache.brooklyn.util.core.task.TaskBuilder; -import org.apache.brooklyn.util.core.task.system.ProcessTaskFactory; -import org.apache.brooklyn.util.core.task.system.ProcessTaskWrapper; -import org.apache.brooklyn.util.text.Strings; -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.collect.ImmutableMap; - -@Beta -public class SaltLifecycleEffectorTasks extends MachineLifecycleEffectorTasks implements SaltConfig { - private static final Logger LOG = LoggerFactory.getLogger(SaltLifecycleEffectorTasks.class); - - @Override - protected String startProcessesAtMachine(Supplier<MachineLocation> machineS) { - SaltMode mode = detectSaltMode(entity()); - final MachineLocation machine = machineS.get(); - LOG.info("Starting salt in '{}' mode at '{}'", mode, machine.getDisplayName()); - if (mode == SaltMode.MASTERLESS) { - startWithSshAsync(); - } else { - // TODO: implement MASTER and MINION - throw new IllegalStateException("Unknown salt mode: " + mode.name()); - } - return "salt tasks submitted (" + mode + ")"; - } - - - protected static SaltMode detectSaltMode(Entity entity) { - SaltMode mode = entity.getConfig(SaltConfig.SALT_MODE); - Preconditions.checkNotNull(mode, "Required config " + SaltConfig.SALT_MODE + " not provided for entity: " + entity); - return mode; - } - - protected void startWithSshAsync() { - - final Set<? extends String> startStates = entity().getConfig(SaltConfig.START_STATES); - final Set<? extends String> formulas = entity().getConfig(SaltConfig.SALT_FORMULAS); - final Set<? extends String> pillars = entity().getConfig(SaltConfig.SALT_PILLARS); - final Set<? extends String> pillarUrls = entity().getConfig(SaltConfig.SALT_PILLAR_URLS); - final String entityId = entity().getConfig(BrooklynCampConstants.PLAN_ID); - - final ProcessTaskWrapper<Integer> installedAlready = queueAndBlock(SaltSshTasks.isSaltInstalled(false)); - - if (0 != installedAlready.getExitCode()) { - DynamicTasks.queue("install", new Runnable() { - @Override - public void run() { - DynamicTasks.queue( - SaltSshTasks.installSalt(false), - SaltSshTasks.installSaltUtilities(false), - SaltSshTasks.configureForMasterlessOperation(false), - SaltSshTasks.installTopFile(startStates, false)); - - if (Strings.isNonBlank(entityId)) { - DynamicTasks.queue(SaltSshTasks.setMinionId(entityId)); - } - installFormulas(formulas); - installPillars(pillars, pillarUrls); - } - }); - } - - startSalt(); - - connectSensors(); - } - - - private static final Pattern FAILURES = Pattern.compile(".*^Failed:\\s+(\\d+)$.*", MULTILINE | DOTALL); - private static final String ZERO = "0"; - - private void startSalt() { - String name = "apply top states"; - final ProcessTaskWrapper<Integer> topStates = queueAndBlock(SaltSshTasks.applyTopStates(false).summary(name)); - - // Salt apply returns exit code 0 even upon failure so check the stdout. - final Matcher failCount = FAILURES.matcher(topStates.getStdout()); - if (!failCount.matches() || !ZERO.equals(failCount.group(1))) { - LOG.warn("Encountered error in applying Salt top states: {}", topStates.getStdout()); - throw new RuntimeException( - "Encountered error in applying Salt top states, see '" + name + "' in activities for details"); - } - } - - private void installFormulas(Set<? extends String> formulas) { - if (formulas.size() > 0) { - DynamicTasks.queue(SaltSshTasks.enableFileRoots(false)); - - final TaskBuilder<Object> formulaTasks = TaskBuilder.builder().displayName("install formulas"); - for (String url : formulas) { - formulaTasks.add(SaltSshTasks.installSaltFormula(url, false).newTask()); - } - DynamicTasks.queue(formulaTasks.build()); - } - } - - private void installPillars(Set<? extends String> pillars, Set<? extends String> pillarUrls) { - if (pillarUrls.size() > 0) { - final TaskBuilder<Object> pillarTasks = TaskBuilder.builder().displayName("install pillars"); - pillarTasks.add(SaltSshTasks.invokeSaltUtility("init_pillar_config", null, false) - .summary("init pillar config").newTask()); - for (String pillar : pillars) { - pillarTasks.add(SaltSshTasks.addPillarToTop(pillar, false).newTask()); - } - for (String url : pillarUrls) { - pillarTasks.add(SaltSshTasks.installSaltPillar(url, false).newTask()); - } - DynamicTasks.queue(pillarTasks.build()); - } - } - - private void connectSensors() { - final ProcessTaskWrapper<String> retrieveHighstate = SaltSshTasks.retrieveHighstate(); - final ProcessTaskWrapper<String> highstate = DynamicTasks.queue(retrieveHighstate).block(); - String stateDescription = highstate.get(); - - SaltHighstate.applyHighstate(stateDescription, entity()); - } - - @Override - protected void postStartCustom() { - // TODO: check for package installed? - entity().sensors().set(SoftwareProcess.SERVICE_UP, true); - super.postStartCustom(); - } - - - @Override - protected String stopProcessesAtMachine() { - final Set<? extends String> stopStates = entity().getConfig(SaltConfig.STOP_STATES); - LOG.debug("Executing Salt stopProcessesAtMachine with states {}", stopStates); - if (stopStates.isEmpty()) { - stopBasedOnStartStates(); - } else { - applyStates(stopStates); - } - return null; - } - - private void applyStates(Set<? extends String> states) { - for (String state : states) { - DynamicTasks.queue(SaltSshTasks.applyState(state, false).summary("apply state " + state)); - } - } - - private void stopBasedOnStartStates() { - final Set<? extends String> startStates = entity().getConfig(SaltConfig.START_STATES); - final MutableSet<String> stopStates = addSuffix(startStates, ".stop"); - final ProcessTaskWrapper<Integer> checkStops = - queueAndBlock(SaltSshTasks.verifyStates(stopStates, false).summary("check stop states")); - if (0 != checkStops.getExitCode()) { - throw new RuntimeException("No stop_states configured and not all start_states have matching stop states"); - } else { - applyStates(stopStates); - } - } - - @Override - public void restart(ConfigBag parameters) { - ServiceStateLogic.setExpectedState(entity(), Lifecycle.STOPPING); - - try { - final Set<? extends String> restartStates = entity().getConfig(SaltConfig.RESTART_STATES); - LOG.debug("Executing Salt restart with states {}", restartStates); - if (restartStates.isEmpty()) { - restartBasedOnStartStates(); - } else { - applyStates(restartStates); - } - ServiceStateLogic.setExpectedState(entity(), Lifecycle.RUNNING); - } catch (Exception e) { - entity().sensors().set(ServiceStateLogic.SERVICE_NOT_UP_DIAGNOSTICS, - ImmutableMap.<String, Object>of("restart", e.getMessage())); - ServiceStateLogic.setExpectedState(entity(), Lifecycle.ON_FIRE); - } - } - - private void restartBasedOnStartStates() { - final Set<? extends String> startStates = entity().getConfig(SaltConfig.START_STATES); - final MutableSet<String> restartStates = addSuffix(startStates, ".restart"); - final ProcessTaskWrapper<Integer> queued = - queueAndBlock(SaltSshTasks.findStates(restartStates, false).summary("check restart states")); - final String stdout = queued.getStdout(); - String[] foundStates = Strings.isNonBlank(stdout) ? stdout.split("\\n") : null; - - if (restartStates.size() > 0 && foundStates != null && (restartStates.size() == foundStates.length)) { - // each state X listed in start_states has a matching state of the form X.restart; we apply them. - LOG.debug("All start_states have matching restart states, applying these"); - applyStates(restartStates); - - } else if (foundStates != null && foundStates.length > 0) { - // only *some* of the states have a matching restart; we treat this as a fail - LOG.debug("Only some start_states have matching restart states, treating as restart failure") ; - throw new RuntimeException("unable to find restart state for all applied states"); - - } else { - // else we apply "stop" effector (with parameters to stop processes not machine) then "start" - // (and in that effector we'd fail if stop was not well-defined) - LOG.debug("No stop states available, invoking stop and start effectors"); - invokeEffector(Startable.STOP, ConfigBag.newInstance() - .configure(StopSoftwareParameters.STOP_PROCESS_MODE, ALWAYS) - .configure(StopSoftwareParameters.STOP_MACHINE_MODE, NEVER)); - invokeEffector(Startable.START, ConfigBag.EMPTY); - } - } - - private ProcessTaskWrapper<Integer> queueAndBlock(ProcessTaskFactory<Integer> taskFactory) { - final ProcessTaskWrapper<Integer> queued = DynamicTasks.queue(taskFactory); - queued.asTask().blockUntilEnded(); - return queued; - } - - private void invokeEffector(Effector<Void> effector, ConfigBag config) { - final TaskAdaptable<Void> stop = Entities.submit(entity(), Effectors.invocation(entity(), effector, config)); - stop.asTask().blockUntilEnded(); - } - - private MutableSet<String> addSuffix(Set<? extends String> names, String suffix) { - final MutableSet<String> suffixed = MutableSet.of(); - for (String name : names) { - suffixed.add(name + suffix); - } - return suffixed; - } -} http://git-wip-us.apache.org/repos/asf/brooklyn-library/blob/ac837bca/software/cm/salt/src/main/java/org/apache/brooklyn/entity/cm/salt/impl/SaltSshDriver.java ---------------------------------------------------------------------- diff --git a/software/cm/salt/src/main/java/org/apache/brooklyn/entity/cm/salt/impl/SaltSshDriver.java b/software/cm/salt/src/main/java/org/apache/brooklyn/entity/cm/salt/impl/SaltSshDriver.java deleted file mode 100644 index cd9cb38..0000000 --- a/software/cm/salt/src/main/java/org/apache/brooklyn/entity/cm/salt/impl/SaltSshDriver.java +++ /dev/null @@ -1,28 +0,0 @@ -/* - * 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.cm.salt.impl; - - -public class SaltSshDriver { - - private SaltSshDriver() { - // Utility class - } - -} http://git-wip-us.apache.org/repos/asf/brooklyn-library/blob/ac837bca/software/cm/salt/src/main/java/org/apache/brooklyn/entity/cm/salt/impl/SaltSshTasks.java ---------------------------------------------------------------------- diff --git a/software/cm/salt/src/main/java/org/apache/brooklyn/entity/cm/salt/impl/SaltSshTasks.java b/software/cm/salt/src/main/java/org/apache/brooklyn/entity/cm/salt/impl/SaltSshTasks.java deleted file mode 100644 index a8e1af6..0000000 --- a/software/cm/salt/src/main/java/org/apache/brooklyn/entity/cm/salt/impl/SaltSshTasks.java +++ /dev/null @@ -1,237 +0,0 @@ -/* - * 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.cm.salt.impl; - -import org.apache.brooklyn.api.mgmt.TaskAdaptable; -import org.apache.brooklyn.api.mgmt.TaskFactory; -import org.apache.brooklyn.core.effector.EffectorTasks; -import org.apache.brooklyn.core.effector.ssh.SshEffectorTasks; -import org.apache.brooklyn.core.effector.ssh.SshEffectorTasks.SshEffectorTaskFactory; -import org.apache.brooklyn.util.collections.MutableList; -import org.apache.brooklyn.util.core.ResourceUtils; -import org.apache.brooklyn.util.core.file.ArchiveTasks; -import org.apache.brooklyn.util.core.task.TaskBuilder; -import org.apache.brooklyn.util.core.task.Tasks; -import org.apache.brooklyn.util.core.task.ssh.SshPutTaskFactory; -import org.apache.brooklyn.util.core.task.system.ProcessTaskFactory; -import org.apache.brooklyn.util.core.task.system.ProcessTaskWrapper; -import org.apache.brooklyn.util.ssh.BashCommands; -import org.apache.brooklyn.util.text.Identifiers; -import org.apache.brooklyn.util.text.Strings; - -import java.util.List; -import java.util.Set; - -import static org.apache.brooklyn.core.effector.ssh.SshEffectorTasks.ssh; -import static org.apache.brooklyn.util.ssh.BashCommands.sudo; - - -public class SaltSshTasks { - - private static final String UTILITY_SCRIPT = "salt_utilities.sh"; - - private SaltSshTasks() { - // Utility class - } - - public static TaskFactory<?> installSalt(boolean force) { - // TODO: ignore force? - return sshCommands( - BashCommands.commandToDownloadUrlAs("https://bootstrap.saltstack.com", "install_salt.sh"), - sudo("sh install_salt.sh") - ) - .summary("install salt"); - } - - public static SshEffectorTaskFactory<Integer> isSaltInstalled(boolean force) { - return invokeSaltUtility("salt_installed", null, true).summary("check installed"); - } - - public static TaskFactory<?> configureForMasterlessOperation(boolean force) { - // TODO: ignore force? - return ssh(sudo("sed -i '/^#file_client/c file_client: local' /etc/salt/minion")) - .summary("configure masterless"); - } - - - public static TaskFactory<?> enableFileRoots(boolean force) { - return sshCommands( - "grep ^file_roots /etc/salt/minion || {", - "cat /etc/salt/minion > /tmp/minion.update", - "cat >> /tmp/minion.update << BROOKLYN_EOF", - "file_roots:", - " base:", - " - /srv/salt/", - "BROOKLYN_EOF", - sudo("mv /tmp/minion.update /etc/salt/minion"), - "}" - ) - .requiringExitCodeZero() - .summary("enable file_roots"); - } - - - public static TaskFactory<?> addPillarToTop(String pillar, boolean b) { - return invokeSaltUtility("add_pillar_to_top", pillar, false) - .summary("Add pillar " + pillar + " to top"); - } - - public static TaskFactory<?> installSaltPillar(final String pillarUrl, boolean force) { - return new TaskFactory<TaskAdaptable<?>>() { - @Override - public TaskAdaptable<?> newTask() { - TaskBuilder<Void> tb = Tasks.<Void>builder().displayName(pillarUrl); - String tempDownloadDir = "/tmp/download-" + Identifiers.makeRandomId(12); - - tb.add(ArchiveTasks.deploy(null,null,pillarUrl,EffectorTasks.findSshMachine(), - tempDownloadDir,false,null,null).newTask()); - - tb.add(ssh("cd " + tempDownloadDir + " ; " + sudo("mv * /srv/pillar")).newTask()); - - return tb.build(); - } - }; - } - - public static TaskFactory<?> installSaltFormula(final String formulaUrl, boolean force) { - return new TaskFactory<TaskAdaptable<?>>() { - @Override - public TaskAdaptable<?> newTask() { - TaskBuilder<Void> tb = Tasks.<Void>builder().displayName(formulaUrl); - - String tempDirectoryForUnpack = "/tmp/download-" + Identifiers.makeRandomId(12); - - tb.add(ArchiveTasks.deploy(null, null, formulaUrl, EffectorTasks.findSshMachine(), - tempDirectoryForUnpack, false, null, null).newTask()); - - // TODO move this into salt_utilities.sh - String installCmd = BashCommands.chain( - "cd "+tempDirectoryForUnpack, - "EXPANDED_DIR=`ls`", - BashCommands.requireTest("`ls | wc -w` -eq 1", - "The deployed archive "+ formulaUrl +" must contain exactly one directory"), - "sudo mkdir -p /srv/formula", - "sudo mv $EXPANDED_DIR /srv/formula/", - // sed command below relies on enableFileRoots behaviour of append file_roots to end of config file - "sudo sed -i \"$ a\\ - /srv/formula/$EXPANDED_DIR\" /etc/salt/minion", - "cd ..", - "rm -rf '"+tempDirectoryForUnpack+"'"); - tb.add(ssh(installCmd).summary("installing " + formulaUrl + " states to /srv/formula") - .requiringExitCodeZero().newTask()); - - return tb.build(); - } - }; - } - - public static TaskFactory<?> installTopFile(final Set<? extends String> runList, boolean force) { - // TODO: ignore force? - // TODO: move this into salt_utilities.sh - final MutableList.Builder<String> topBuilder = MutableList.<String>builder() - .add("cat > /tmp/top.sls << BROOKLYN_EOF") - .add("base:") - .add(" '*':"); - for (String stateName: runList) { - topBuilder.add(" - " + stateName); - } - topBuilder.add("BROOKLYN_EOF"); - List<String> createTempTopFile = topBuilder.build(); - - List<String> commands = MutableList.<String>builder() - .add(sudo("mkdir -p /srv/salt")) - .add(Strings.join(createTempTopFile, "\n")) - .add(sudo("mv /tmp/top.sls /srv/salt")) - .build(); - return ssh(commands).summary("create top.sls file"); - } - - public static ProcessTaskFactory<Integer> applyTopStates(boolean force) { - return saltCall("state.apply"); - } - - public static SshEffectorTaskFactory<Integer> applyState(String state, boolean force) { - return saltCall("state.apply " + state); - } - - public static SshEffectorTaskFactory<Integer> saltCall(String command) { - return ssh(sudo("salt-call --local " + command)).allowingNonZeroExitCode(); - } - - public static ProcessTaskWrapper<String> retrieveHighstate() { - return saltCall("state.show_highstate --out=yaml") - .summary("retrieve highstate") - .requiringZeroAndReturningStdout() - .newTask(); - } - - public static TaskFactory<?> installSaltUtilities(boolean force) { - return new TaskFactory<TaskAdaptable<?>>() { - @Override - public TaskAdaptable<?> newTask() { - final TaskBuilder<Void> builder = Tasks.<Void>builder() - .displayName("install salt utilities") - .add(installScript(UTILITY_SCRIPT, "install salt shell utils").newTask()) - .add(ssh(sudo("mv /tmp/" + UTILITY_SCRIPT + " /etc/salt")).newTask()); - return builder.build(); - } - }; - } - - private static SshPutTaskFactory installScript(String name, String description) { - return SshEffectorTasks.put("/tmp/" + name) - .contents(ResourceUtils.create().getResourceFromUrl("classpath:" + name)) - .summary(description); - } - - public static SshEffectorTaskFactory<Integer> verifyStates(Set<String> states, boolean force) { - return invokeSaltUtility("verify_states", Strings.join(states, " "), true); - } - - public static SshEffectorTaskFactory<Integer> findStates(Set<String> states, boolean force) { - return invokeSaltUtility("find_states", Strings.join(states, " "), true); - } - - // Simple invocation of a function from salt_utilities.sh, optionally allowing it to fail. - // Uses single quoted bash command, so args mustn't contain single quotes. - public static SshEffectorTaskFactory<Integer> invokeSaltUtility(String functionName, String args, boolean permitFailure) { - - final SshEffectorTaskFactory<Integer> taskFactory = - ssh(sudo("/bin/bash -c '. /etc/salt/salt_utilities.sh ; " + functionName + " " + args + "'")) - .summary(functionName); - - if (permitFailure) { - taskFactory.allowingNonZeroExitCode(); - } else { - taskFactory.requiringExitCodeZero(); - } - return taskFactory; - - } - - public static SshEffectorTaskFactory<Integer> sshCommands(String line, String... lines) { - final MutableList.Builder<String> builder = MutableList.<String>builder() - .add(line); - builder.addAll(lines); - return ssh(Strings.join(builder.build(), "\n")); - } - - public static SshEffectorTaskFactory<Integer> setMinionId(final String entityId) { - return invokeSaltUtility("set_minion_id", entityId, true); - } -} http://git-wip-us.apache.org/repos/asf/brooklyn-library/blob/ac837bca/software/cm/salt/src/main/java/org/apache/brooklyn/entity/cm/salt/impl/SaltUtils.java ---------------------------------------------------------------------- diff --git a/software/cm/salt/src/main/java/org/apache/brooklyn/entity/cm/salt/impl/SaltUtils.java b/software/cm/salt/src/main/java/org/apache/brooklyn/entity/cm/salt/impl/SaltUtils.java deleted file mode 100644 index b11c06e..0000000 --- a/software/cm/salt/src/main/java/org/apache/brooklyn/entity/cm/salt/impl/SaltUtils.java +++ /dev/null @@ -1,42 +0,0 @@ -/* - * 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.cm.salt.impl; - -import org.apache.brooklyn.api.location.LocationDefinition; -import org.apache.brooklyn.api.mgmt.ManagementContext; - -public class SaltUtils { - - private SaltUtils() { - // Utility class - } - - public static ManagementContext.PropertiesReloadListener propertiesReloadListener( - ManagementContext mc, LocationDefinition definition) { - - return new ManagementContext.PropertiesReloadListener() { - private static final long serialVersionUID = 1L; - @Override - public void reloaded() { - // TODO: implement properties reload logic - } - }; - } - -} http://git-wip-us.apache.org/repos/asf/brooklyn-library/blob/ac837bca/software/cm/salt/src/test/java/org/apache/brooklyn/entity/cm/salt/HighstateTest.java ---------------------------------------------------------------------- diff --git a/software/cm/salt/src/test/java/org/apache/brooklyn/entity/cm/salt/HighstateTest.java b/software/cm/salt/src/test/java/org/apache/brooklyn/entity/cm/salt/HighstateTest.java index 50205e0..e5d130a 100644 --- a/software/cm/salt/src/test/java/org/apache/brooklyn/entity/cm/salt/HighstateTest.java +++ b/software/cm/salt/src/test/java/org/apache/brooklyn/entity/cm/salt/HighstateTest.java @@ -20,7 +20,6 @@ package org.apache.brooklyn.entity.cm.salt; import com.google.common.collect.ImmutableSet; import org.apache.brooklyn.api.entity.EntitySpec; -import org.apache.brooklyn.entity.cm.salt.impl.SaltHighstate; import org.apache.brooklyn.core.entity.Entities; import org.apache.brooklyn.core.sensor.Sensors; import org.apache.brooklyn.core.test.entity.TestApplication;
