http://git-wip-us.apache.org/repos/asf/incubator-brooklyn/blob/64c2b2e5/software/base/src/main/java/brooklyn/entity/brooklynnode/effector/BrooklynNodeUpgradeEffectorBody.java ---------------------------------------------------------------------- diff --git a/software/base/src/main/java/brooklyn/entity/brooklynnode/effector/BrooklynNodeUpgradeEffectorBody.java b/software/base/src/main/java/brooklyn/entity/brooklynnode/effector/BrooklynNodeUpgradeEffectorBody.java deleted file mode 100644 index 9483eee..0000000 --- a/software/base/src/main/java/brooklyn/entity/brooklynnode/effector/BrooklynNodeUpgradeEffectorBody.java +++ /dev/null @@ -1,230 +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 brooklyn.entity.brooklynnode.effector; - -import java.util.Map; - -import org.apache.brooklyn.api.effector.Effector; -import org.apache.brooklyn.api.entity.Entity; -import org.apache.brooklyn.api.entity.EntitySpec; -import org.apache.brooklyn.api.entity.drivers.DriverDependentEntity; -import org.apache.brooklyn.api.mgmt.ha.HighAvailabilityMode; -import org.apache.brooklyn.api.mgmt.ha.ManagementNodeState; -import org.apache.brooklyn.config.ConfigKey; -import org.apache.brooklyn.core.config.ConfigKeys; -import org.apache.brooklyn.core.config.MapConfigKey; -import org.apache.brooklyn.effector.core.EffectorBody; -import org.apache.brooklyn.effector.core.Effectors; -import org.apache.brooklyn.entity.core.Entities; -import org.apache.brooklyn.entity.core.EntityInternal; -import org.apache.brooklyn.entity.core.EntityTasks; -import org.apache.brooklyn.util.core.config.ConfigBag; -import org.apache.brooklyn.util.core.task.DynamicTasks; -import org.apache.brooklyn.util.core.task.Tasks; -import org.apache.brooklyn.util.net.Urls; -import org.apache.brooklyn.util.text.Identifiers; -import org.apache.brooklyn.util.text.Strings; -import org.apache.brooklyn.util.time.Duration; -import org.slf4j.Logger; -import org.slf4j.LoggerFactory; - -import brooklyn.entity.basic.SoftwareProcess; -import brooklyn.entity.basic.SoftwareProcess.StopSoftwareParameters; -import brooklyn.entity.basic.SoftwareProcess.StopSoftwareParameters.StopMode; -import brooklyn.entity.brooklynnode.BrooklynCluster; -import brooklyn.entity.brooklynnode.BrooklynNode; -import brooklyn.entity.brooklynnode.BrooklynNodeDriver; -import brooklyn.entity.software.SshEffectorTasks; - -import com.google.common.annotations.Beta; -import com.google.common.base.Predicates; -import com.google.common.collect.ImmutableMap; -import com.google.common.reflect.TypeToken; - -@SuppressWarnings("serial") -/** Upgrades a brooklyn node in-place on the box, - * by creating a child brooklyn node and ensuring it can rebind in HOT_STANDBY - * <p> - * Requires the target node to have persistence enabled. - */ -public class BrooklynNodeUpgradeEffectorBody extends EffectorBody<Void> { - - private static final Logger log = LoggerFactory.getLogger(BrooklynNodeUpgradeEffectorBody.class); - - public static final ConfigKey<String> DOWNLOAD_URL = BrooklynNode.DOWNLOAD_URL.getConfigKey(); - public static final ConfigKey<Boolean> DO_DRY_RUN_FIRST = ConfigKeys.newBooleanConfigKey( - "doDryRunFirst", "Test rebinding with a temporary instance before stopping the entity for upgrade.", true); - public static final ConfigKey<Map<String,Object>> EXTRA_CONFIG = MapConfigKey.builder(new TypeToken<Map<String,Object>>() {}) - .name("extraConfig") - .description("Additional new config to set on the BrooklynNode as part of upgrading") - .build(); - - public static final Effector<Void> UPGRADE = Effectors.effector(Void.class, "upgrade") - .description("Changes the Brooklyn build used to run this node, " - + "by spawning a dry-run node then copying the installed files across. " - + "This node must be running for persistence for in-place upgrading to work.") - .parameter(BrooklynNode.SUGGESTED_VERSION) - .parameter(DOWNLOAD_URL) - .parameter(DO_DRY_RUN_FIRST) - .parameter(EXTRA_CONFIG) - .impl(new BrooklynNodeUpgradeEffectorBody()).build(); - - @Override - public Void call(ConfigBag parametersO) { - if (!isPersistenceModeEnabled(entity())) { - // would could try a `forcePersistNow`, but that's sloppy; - // for now, require HA/persistence for upgrading - DynamicTasks.queue( Tasks.warning("Check persistence", - new IllegalStateException("Persistence does not appear to be enabled at this cluster. " - + "In-place node upgrade will not succeed unless a custom launch script enables it.")) ); - } - - final ConfigBag parameters = ConfigBag.newInstanceCopying(parametersO); - - /* - * all parameters are passed to children, apart from EXTRA_CONFIG - * whose value (as a map) is so passed; it provides an easy way to set extra config in the gui. - * (IOW a key-value mapping can be passed either inside EXTRA_CONFIG or as a sibling to EXTRA_CONFIG) - */ - if (parameters.containsKey(EXTRA_CONFIG)) { - Map<String, Object> extra = parameters.get(EXTRA_CONFIG); - parameters.remove(EXTRA_CONFIG); - parameters.putAll(extra); - } - log.debug(this+" upgrading, using "+parameters); - - final String bkName; - boolean doDryRunFirst = parameters.get(DO_DRY_RUN_FIRST); - if(doDryRunFirst) { - bkName = dryRunUpdate(parameters); - } else { - bkName = "direct-"+Identifiers.makeRandomId(4); - } - - // Stop running instance - DynamicTasks.queue(Tasks.builder().name("shutdown node") - .add(Effectors.invocation(entity(), BrooklynNode.STOP_NODE_BUT_LEAVE_APPS, ImmutableMap.of(StopSoftwareParameters.STOP_MACHINE_MODE, StopMode.NEVER))) - .build()); - - // backup old files - DynamicTasks.queue(Tasks.builder().name("backup old version").body(new Runnable() { - @Override - public void run() { - String runDir = entity().getAttribute(SoftwareProcess.RUN_DIR); - String bkDir = Urls.mergePaths(runDir, "..", Urls.getBasename(runDir)+"-backups", bkName); - log.debug(this+" storing backup of previous version in "+bkDir); - DynamicTasks.queue(SshEffectorTasks.ssh( - "cd "+runDir, - "mkdir -p "+bkDir, - "mv * "+bkDir - // By removing the run dir of the entity we force it to go through - // the customize step again on start and re-generate local-brooklyn.properties. - ).summary("move files")); - } - }).build()); - - // Reconfigure entity - DynamicTasks.queue(Tasks.builder().name("reconfigure").body(new Runnable() { - @Override - public void run() { - DynamicTasks.waitForLast(); - ((EntityInternal)entity()).setAttribute(SoftwareProcess.INSTALL_DIR, (String)null); - entity().setConfig(SoftwareProcess.INSTALL_UNIQUE_LABEL, (String)null); - entity().getConfigMap().addToLocalBag(parameters.getAllConfig()); - entity().setAttribute(BrooklynNode.DOWNLOAD_URL, entity().getConfig(DOWNLOAD_URL)); - - // Setting SUGGESTED_VERSION will result in an new empty INSTALL_FOLDER, but clear it - // just in case the user specified already installed version. - ((BrooklynNodeDriver)((DriverDependentEntity<?>)entity()).getDriver()).clearInstallDir(); - } - }).build()); - - // Start this entity, running the new version. - // This will download and install the new dist (if not already done by the dry run node). - DynamicTasks.queue(Effectors.invocation(entity(), BrooklynNode.START, ConfigBag.EMPTY)); - - return null; - } - - private String dryRunUpdate(ConfigBag parameters) { - // TODO require entity() node state master or hot standby AND require persistence enabled, or a new 'force_attempt_upgrade' parameter to be applied - // TODO could have a 'skip_dry_run_upgrade' parameter - // TODO could support 'dry_run_only' parameter, with optional resumption tasks (eg new dynamic effector) - - // 1 add new brooklyn version entity as child (so uses same machine), with same config apart from things in parameters - final Entity dryRunChild = entity().addChild(createDryRunSpec() - .displayName("Upgraded Version Dry-Run Node") - // install dir and label are recomputed because they are not inherited, and download_url will normally be different - .configure(parameters.getAllConfig())); - - //force this to start as hot-standby - // TODO alternatively could use REST API as in BrooklynClusterUpgradeEffectorBody - String launchParameters = dryRunChild.getConfig(BrooklynNode.EXTRA_LAUNCH_PARAMETERS); - if (Strings.isBlank(launchParameters)) launchParameters = ""; - else launchParameters += " "; - launchParameters += "--highAvailability "+HighAvailabilityMode.HOT_STANDBY; - ((EntityInternal)dryRunChild).setConfig(BrooklynNode.EXTRA_LAUNCH_PARAMETERS, launchParameters); - - Entities.manage(dryRunChild); - final String dryRunNodeUid = dryRunChild.getId(); - ((EntityInternal)dryRunChild).setDisplayName("Dry-Run Upgraded Brooklyn Node ("+dryRunNodeUid+")"); - - DynamicTasks.queue(Effectors.invocation(dryRunChild, BrooklynNode.START, ConfigBag.EMPTY)); - - // 2 confirm hot standby status - DynamicTasks.queue(EntityTasks.requiringAttributeEventually(dryRunChild, BrooklynNode.MANAGEMENT_NODE_STATE, - Predicates.equalTo(ManagementNodeState.HOT_STANDBY), Duration.FIVE_MINUTES)); - - // 3 stop new version - DynamicTasks.queue(Tasks.builder().name("shutdown transient node") - .add(Effectors.invocation(dryRunChild, BrooklynNode.STOP_NODE_BUT_LEAVE_APPS, ImmutableMap.of(StopSoftwareParameters.STOP_MACHINE_MODE, StopMode.NEVER))) - .build()); - - DynamicTasks.queue(Tasks.<Void>builder().name("remove transient node").body( - new Runnable() { - @Override - public void run() { - Entities.unmanage(dryRunChild); - } - } - ).build()); - - return dryRunChild.getId(); - } - - protected EntitySpec<? extends BrooklynNode> createDryRunSpec() { - return EntitySpec.create(BrooklynNode.class); - } - - @Beta - static boolean isPersistenceModeEnabled(Entity entity) { - // TODO when there are PERSIST* options in BrooklynNode, look at them here! - // or, even better, make a REST call to check persistence - String params = null; - if (entity instanceof BrooklynCluster) { - EntitySpec<?> spec = entity.getConfig(BrooklynCluster.MEMBER_SPEC); - params = Strings.toString( spec.getConfig().get(BrooklynNode.EXTRA_LAUNCH_PARAMETERS) ); - } - if (params==null) params = entity.getConfig(BrooklynNode.EXTRA_LAUNCH_PARAMETERS); - if (params==null) return false; - if (params.indexOf("persist")==0) return false; - return true; - } - -}
http://git-wip-us.apache.org/repos/asf/incubator-brooklyn/blob/64c2b2e5/software/base/src/main/java/brooklyn/entity/brooklynnode/effector/SelectMasterEffectorBody.java ---------------------------------------------------------------------- diff --git a/software/base/src/main/java/brooklyn/entity/brooklynnode/effector/SelectMasterEffectorBody.java b/software/base/src/main/java/brooklyn/entity/brooklynnode/effector/SelectMasterEffectorBody.java deleted file mode 100644 index 92b46b2..0000000 --- a/software/base/src/main/java/brooklyn/entity/brooklynnode/effector/SelectMasterEffectorBody.java +++ /dev/null @@ -1,175 +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 brooklyn.entity.brooklynnode.effector; - -import java.util.NoSuchElementException; -import java.util.concurrent.Callable; -import java.util.concurrent.atomic.AtomicBoolean; - -import org.apache.brooklyn.api.effector.Effector; -import org.apache.brooklyn.api.entity.Entity; -import org.apache.brooklyn.api.entity.Group; -import org.apache.brooklyn.api.mgmt.ha.HighAvailabilityMode; -import org.apache.brooklyn.api.mgmt.ha.ManagementNodeState; -import org.apache.brooklyn.effector.core.EffectorBody; -import org.apache.brooklyn.effector.core.Effectors; -import org.apache.brooklyn.entity.core.EntityPredicates; -import org.apache.brooklyn.util.collections.MutableMap; -import org.apache.brooklyn.util.core.config.ConfigBag; -import org.apache.brooklyn.util.core.task.DynamicTasks; -import org.apache.brooklyn.util.repeat.Repeater; -import org.apache.brooklyn.util.time.Duration; -import org.slf4j.Logger; -import org.slf4j.LoggerFactory; - -import brooklyn.entity.brooklynnode.BrooklynCluster; -import brooklyn.entity.brooklynnode.BrooklynCluster.SelectMasterEffector; -import brooklyn.entity.brooklynnode.BrooklynNode; -import brooklyn.entity.brooklynnode.BrooklynNode.SetHighAvailabilityModeEffector; -import brooklyn.entity.brooklynnode.BrooklynNode.SetHighAvailabilityPriorityEffector; - -import com.google.common.base.Preconditions; -import com.google.common.base.Objects; -import com.google.common.collect.Iterables; - -public class SelectMasterEffectorBody extends EffectorBody<Void> implements SelectMasterEffector { - public static final Effector<Void> SELECT_MASTER = Effectors.effector(SelectMasterEffector.SELECT_MASTER).impl(new SelectMasterEffectorBody()).build(); - - private static final Logger LOG = LoggerFactory.getLogger(SelectMasterEffectorBody.class); - - private static final int HA_STANDBY_PRIORITY = 0; - private static final int HA_MASTER_PRIORITY = 1; - - private AtomicBoolean selectMasterInProgress = new AtomicBoolean(); - - @Override - public Void call(ConfigBag parameters) { - if (!selectMasterInProgress.compareAndSet(false, true)) { - throw new IllegalStateException("A master change is already in progress."); - } - - try { - selectMaster(parameters); - } finally { - selectMasterInProgress.set(false); - } - return null; - } - - private void selectMaster(ConfigBag parameters) { - String newMasterId = parameters.get(NEW_MASTER_ID); - Preconditions.checkNotNull(newMasterId, NEW_MASTER_ID.getName() + " parameter is required"); - - final Entity oldMaster = entity().getAttribute(BrooklynCluster.MASTER_NODE); - if (oldMaster != null && oldMaster.getId().equals(newMasterId)) { - LOG.info(newMasterId + " is already the current master, no change needed."); - return; - } - - final Entity newMaster = getMember(newMasterId); - - //1. Increase the priority of the node we wish to become master - toggleNodePriority(newMaster, HA_MASTER_PRIORITY); - - //2. Demote the existing master so a new election takes place - try { - // this allows the finally block to run even on failure - DynamicTasks.swallowChildrenFailures(); - - if (oldMaster != null) { - demoteOldMaster(oldMaster, HighAvailabilityMode.HOT_STANDBY); - } - - waitMasterHandover(oldMaster, newMaster); - } finally { - //3. Revert the priority of the node once it has become master - toggleNodePriority(newMaster, HA_STANDBY_PRIORITY); - } - - checkMasterSelected(newMaster); - } - - private void waitMasterHandover(final Entity oldMaster, final Entity newMaster) { - boolean masterChanged = Repeater.create() - .backoff(Repeater.DEFAULT_REAL_QUICK_PERIOD, 1.5, Duration.FIVE_SECONDS) - .limitTimeTo(Duration.ONE_MINUTE) - .until(new Callable<Boolean>() { - @Override - public Boolean call() throws Exception { - Entity master = getMasterNode(); - return !Objects.equal(master, oldMaster) && master != null; - } - }) - .run(); - if (!masterChanged) { - LOG.warn("Timeout waiting for node to become master: " + newMaster + "."); - } - } - - private void demoteOldMaster(Entity oldMaster, HighAvailabilityMode mode) { - ManagementNodeState oldState = DynamicTasks.queue( - Effectors.invocation( - oldMaster, - BrooklynNode.SET_HIGH_AVAILABILITY_MODE, - MutableMap.of(SetHighAvailabilityModeEffector.MODE, mode)) - ).asTask().getUnchecked(); - - if (oldState != ManagementNodeState.MASTER) { - LOG.warn("The previous HA state on node " + oldMaster.getId() + " was " + oldState + - ", while the expected value is " + ManagementNodeState.MASTER + "."); - } - } - - private void toggleNodePriority(Entity node, int newPriority) { - Integer oldPriority = DynamicTasks.queue( - Effectors.invocation( - node, - BrooklynNode.SET_HIGH_AVAILABILITY_PRIORITY, - MutableMap.of(SetHighAvailabilityPriorityEffector.PRIORITY, newPriority)) - ).asTask().getUnchecked(); - - Integer expectedPriority = (newPriority == HA_MASTER_PRIORITY ? HA_STANDBY_PRIORITY : HA_MASTER_PRIORITY); - if (oldPriority != expectedPriority) { - LOG.warn("The previous HA priority on node " + node.getId() + " was " + oldPriority + - ", while the expected value is " + expectedPriority + " (while setting priority " + - newPriority + ")."); - } - } - - private void checkMasterSelected(Entity newMaster) { - Entity actualMaster = getMasterNode(); - if (actualMaster != newMaster) { - throw new IllegalStateException("Expected node " + newMaster + " to be master, but found that " + - "master is " + actualMaster + " instead."); - } - } - - private Entity getMember(String memberId) { - Group cluster = (Group)entity(); - try { - return Iterables.find(cluster.getMembers(), EntityPredicates.idEqualTo(memberId)); - } catch (NoSuchElementException e) { - throw new IllegalStateException(memberId + " is not an ID of brooklyn node in this cluster"); - } - } - - private Entity getMasterNode() { - return entity().getAttribute(BrooklynCluster.MASTER_NODE); - } -} http://git-wip-us.apache.org/repos/asf/incubator-brooklyn/blob/64c2b2e5/software/base/src/main/java/brooklyn/entity/brooklynnode/effector/SetHighAvailabilityModeEffectorBody.java ---------------------------------------------------------------------- diff --git a/software/base/src/main/java/brooklyn/entity/brooklynnode/effector/SetHighAvailabilityModeEffectorBody.java b/software/base/src/main/java/brooklyn/entity/brooklynnode/effector/SetHighAvailabilityModeEffectorBody.java deleted file mode 100644 index 00d9a7a..0000000 --- a/software/base/src/main/java/brooklyn/entity/brooklynnode/effector/SetHighAvailabilityModeEffectorBody.java +++ /dev/null @@ -1,64 +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 brooklyn.entity.brooklynnode.effector; - -import org.apache.brooklyn.api.effector.Effector; -import org.apache.brooklyn.api.mgmt.ha.HighAvailabilityMode; -import org.apache.brooklyn.api.mgmt.ha.ManagementNodeState; -import org.apache.brooklyn.effector.core.EffectorBody; -import org.apache.brooklyn.effector.core.Effectors; -import org.apache.brooklyn.sensor.feed.http.HttpValueFunctions; -import org.apache.brooklyn.sensor.feed.http.JsonFunctions; -import org.apache.brooklyn.util.core.config.ConfigBag; -import org.apache.brooklyn.util.core.http.HttpToolResponse; -import org.apache.brooklyn.util.guava.Functionals; -import org.apache.brooklyn.util.javalang.Enums; -import org.apache.http.HttpStatus; - -import brooklyn.entity.brooklynnode.BrooklynNode; -import brooklyn.entity.brooklynnode.BrooklynNode.SetHighAvailabilityModeEffector; -import brooklyn.entity.brooklynnode.EntityHttpClient; - -import com.google.common.base.Preconditions; -import com.google.common.base.Function; -import com.google.common.collect.ImmutableMap; - -public class SetHighAvailabilityModeEffectorBody extends EffectorBody<ManagementNodeState> implements SetHighAvailabilityModeEffector { - public static final Effector<ManagementNodeState> SET_HIGH_AVAILABILITY_MODE = Effectors.effector(SetHighAvailabilityModeEffector.SET_HIGH_AVAILABILITY_MODE).impl(new SetHighAvailabilityModeEffectorBody()).build(); - - @Override - public ManagementNodeState call(ConfigBag parameters) { - HighAvailabilityMode mode = parameters.get(MODE); - Preconditions.checkNotNull(mode, MODE.getName() + " parameter is required"); - - EntityHttpClient httpClient = ((BrooklynNode)entity()).http(); - HttpToolResponse resp = httpClient.post("/v1/server/ha/state", - ImmutableMap.of("Brooklyn-Allow-Non-Master-Access", "true"), - ImmutableMap.of("mode", mode.toString())); - - if (resp.getResponseCode() == HttpStatus.SC_OK) { - Function<HttpToolResponse, ManagementNodeState> parseRespone = Functionals.chain( - Functionals.chain(HttpValueFunctions.jsonContents(), JsonFunctions.cast(String.class)), - Enums.fromStringFunction(ManagementNodeState.class)); - return parseRespone.apply(resp); - } else { - throw new IllegalStateException("Unexpected response code: " + resp.getResponseCode() + "\n" + resp.getContentAsString()); - } - } -} http://git-wip-us.apache.org/repos/asf/incubator-brooklyn/blob/64c2b2e5/software/base/src/main/java/brooklyn/entity/brooklynnode/effector/SetHighAvailabilityPriorityEffectorBody.java ---------------------------------------------------------------------- diff --git a/software/base/src/main/java/brooklyn/entity/brooklynnode/effector/SetHighAvailabilityPriorityEffectorBody.java b/software/base/src/main/java/brooklyn/entity/brooklynnode/effector/SetHighAvailabilityPriorityEffectorBody.java deleted file mode 100644 index 73639e5..0000000 --- a/software/base/src/main/java/brooklyn/entity/brooklynnode/effector/SetHighAvailabilityPriorityEffectorBody.java +++ /dev/null @@ -1,55 +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 brooklyn.entity.brooklynnode.effector; - -import org.apache.brooklyn.api.effector.Effector; -import org.apache.brooklyn.effector.core.EffectorBody; -import org.apache.brooklyn.effector.core.Effectors; -import org.apache.brooklyn.util.core.config.ConfigBag; -import org.apache.brooklyn.util.core.http.HttpToolResponse; -import org.apache.http.HttpStatus; - -import brooklyn.entity.brooklynnode.BrooklynNode; -import brooklyn.entity.brooklynnode.BrooklynNode.SetHighAvailabilityPriorityEffector; -import brooklyn.entity.brooklynnode.EntityHttpClient; - -import com.google.common.base.Preconditions; -import com.google.common.collect.ImmutableMap; - -public class SetHighAvailabilityPriorityEffectorBody extends EffectorBody<Integer> implements SetHighAvailabilityPriorityEffector { - public static final Effector<Integer> SET_HIGH_AVAILABILITY_PRIORITY = Effectors.effector(SetHighAvailabilityPriorityEffector.SET_HIGH_AVAILABILITY_PRIORITY).impl(new SetHighAvailabilityPriorityEffectorBody()).build(); - - @Override - public Integer call(ConfigBag parameters) { - Integer priority = parameters.get(PRIORITY); - Preconditions.checkNotNull(priority, PRIORITY.getName() + " parameter is required"); - - EntityHttpClient httpClient = ((BrooklynNode)entity()).http(); - HttpToolResponse resp = httpClient.post("/v1/server/ha/priority", - ImmutableMap.of("Brooklyn-Allow-Non-Master-Access", "true"), - ImmutableMap.of("priority", priority.toString())); - - if (resp.getResponseCode() == HttpStatus.SC_OK) { - return Integer.valueOf(resp.getContentAsString()); - } else { - throw new IllegalStateException("Unexpected response code: " + resp.getResponseCode() + "\n" + resp.getContentAsString()); - } - } - -} http://git-wip-us.apache.org/repos/asf/incubator-brooklyn/blob/64c2b2e5/software/base/src/main/java/brooklyn/entity/chef/ChefAttributeFeed.java ---------------------------------------------------------------------- diff --git a/software/base/src/main/java/brooklyn/entity/chef/ChefAttributeFeed.java b/software/base/src/main/java/brooklyn/entity/chef/ChefAttributeFeed.java deleted file mode 100644 index 00c5c20..0000000 --- a/software/base/src/main/java/brooklyn/entity/chef/ChefAttributeFeed.java +++ /dev/null @@ -1,410 +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 brooklyn.entity.chef; - -import static com.google.common.base.Preconditions.checkArgument; -import static com.google.common.base.Preconditions.checkNotNull; - -import java.util.Arrays; -import java.util.List; -import java.util.Map; -import java.util.Set; -import java.util.concurrent.Callable; -import java.util.concurrent.TimeUnit; - -import org.apache.brooklyn.api.internal.EntityLocal; -import org.apache.brooklyn.api.mgmt.ExecutionContext; -import org.apache.brooklyn.api.sensor.AttributeSensor; -import org.apache.brooklyn.config.ConfigKey; -import org.apache.brooklyn.core.config.ConfigKeys; -import org.apache.brooklyn.entity.core.EntityInternal; -import org.apache.brooklyn.sensor.feed.AbstractFeed; -import org.apache.brooklyn.sensor.feed.PollHandler; -import org.apache.brooklyn.sensor.feed.Poller; -import org.apache.brooklyn.sensor.feed.ssh.SshPollValue; -import org.apache.brooklyn.util.core.flags.TypeCoercions; -import org.apache.brooklyn.util.core.task.system.ProcessTaskWrapper; -import org.apache.brooklyn.util.time.Duration; -import org.slf4j.Logger; -import org.slf4j.LoggerFactory; - -import com.google.common.base.Joiner; -import com.google.common.base.Preconditions; -import com.google.common.base.Splitter; -import com.google.common.base.Strings; -import com.google.common.base.Throwables; -import com.google.common.collect.ImmutableList; -import com.google.common.collect.Iterables; -import com.google.common.collect.Maps; -import com.google.common.collect.Sets; -import com.google.common.reflect.TypeToken; -import com.google.gson.Gson; -import com.google.gson.JsonElement; -import com.google.gson.JsonObject; - -/** - * A sensor feed that retrieves attributes from Chef server and converts selected attributes to sensors. - * - * <p>To use this feed, you must provide the entity, the name of the node as it is known to Chef, and a collection of attribute - * sensors. The attribute sensors must follow the naming convention of starting with the string <tt>chef.attribute.</tt> - * followed by a period-separated path through the Chef attribute hierarchy. For example, an attribute sensor named - * <tt>chef.attribute.sql_server.instance_name</tt> would cause the feed to search for a Chef attribute called - * <tt>sql_server</tt>, and within that an attribute <tt>instance_name</tt>, and set the sensor to the value of this - * attribute.</p> - * - * <p>This feed uses the <tt>knife</tt> tool to query all the attributes on a named node. It then iterates over the configured - * list of attribute sensors, using the sensor name to locate an equivalent Chef attribute. The sensor is then set to the value - * of the Chef attribute.</p> - * - * <p>Example:</p> - * - * {@code - * @Override - * protected void connectSensors() { - * nodeAttributesFeed = ChefAttributeFeed.newFeed(this, nodeName, new AttributeSensor[]{ - * SqlServerNode.CHEF_ATTRIBUTE_NODE_NAME, - * SqlServerNode.CHEF_ATTRIBUTE_SQL_SERVER_INSTANCE_NAME, - * SqlServerNode.CHEF_ATTRIBUTE_SQL_SERVER_PORT, - * SqlServerNode.CHEF_ATTRIBUTE_SQL_SERVER_SA_PASSWORD - * }); - * } - * } - * - * @since 0.6.0 - * @author richardcloudsoft - */ -public class ChefAttributeFeed extends AbstractFeed { - - private static final Logger log = LoggerFactory.getLogger(ChefAttributeFeed.class); - - /** - * Prefix for attribute sensor names. - */ - public static final String CHEF_ATTRIBUTE_PREFIX = "chef.attribute."; - - @SuppressWarnings("serial") - public static final ConfigKey<Set<ChefAttributePollConfig<?>>> POLLS = ConfigKeys.newConfigKey( - new TypeToken<Set<ChefAttributePollConfig<?>>>() {}, - "polls"); - - public static final ConfigKey<String> NODE_NAME = ConfigKeys.newStringConfigKey("nodeName"); - - public static Builder builder() { - return new Builder(); - } - - @SuppressWarnings("rawtypes") - public static class Builder { - private EntityLocal entity; - private boolean onlyIfServiceUp = false; - private String nodeName; - private Set<ChefAttributePollConfig> polls = Sets.newLinkedHashSet(); - private Duration period = Duration.of(30, TimeUnit.SECONDS); - private String uniqueTag; - private volatile boolean built; - - public Builder entity(EntityLocal val) { - this.entity = checkNotNull(val, "entity"); - return this; - } - public Builder onlyIfServiceUp() { return onlyIfServiceUp(true); } - public Builder onlyIfServiceUp(boolean onlyIfServiceUp) { - this.onlyIfServiceUp = onlyIfServiceUp; - return this; - } - public Builder nodeName(String nodeName) { - this.nodeName = checkNotNull(nodeName, "nodeName"); - return this; - } - public Builder addSensor(ChefAttributePollConfig config) { - polls.add(config); - return this; - } - @SuppressWarnings("unchecked") - public Builder addSensor(String chefAttributePath, AttributeSensor sensor) { - return addSensor(new ChefAttributePollConfig(sensor).chefAttributePath(chefAttributePath)); - } - public Builder addSensors(Map<String, AttributeSensor> sensors) { - for (Map.Entry<String, AttributeSensor> entry : sensors.entrySet()) { - addSensor(entry.getKey(), entry.getValue()); - } - return this; - } - public Builder addSensors(AttributeSensor[] sensors) { - return addSensors(Arrays.asList(checkNotNull(sensors, "sensors"))); - } - public Builder addSensors(Iterable<AttributeSensor> sensors) { - for(AttributeSensor sensor : checkNotNull(sensors, "sensors")) { - checkNotNull(sensor, "sensors collection contains a null value"); - checkArgument(sensor.getName().startsWith(CHEF_ATTRIBUTE_PREFIX), "sensor name must be prefixed "+CHEF_ATTRIBUTE_PREFIX+" for autodetection to work"); - addSensor(sensor.getName().substring(CHEF_ATTRIBUTE_PREFIX.length()), sensor); - } - return this; - } - public Builder period(Duration period) { - this.period = period; - return this; - } - public Builder period(long millis) { - return period(Duration.of(millis, TimeUnit.MILLISECONDS)); - } - public Builder period(long val, TimeUnit units) { - return period(Duration.of(val, units)); - } - public Builder uniqueTag(String uniqueTag) { - this.uniqueTag = uniqueTag; - return this; - } - public ChefAttributeFeed build() { - built = true; - ChefAttributeFeed result = new ChefAttributeFeed(this); - result.setEntity(checkNotNull(entity, "entity")); - result.start(); - return result; - } - @Override - protected void finalize() { - if (!built) log.warn("SshFeed.Builder created, but build() never called"); - } - } - - private KnifeTaskFactory<String> knifeTaskFactory; - - /** - * For rebind; do not call directly; use builder - */ - public ChefAttributeFeed() { - } - - protected ChefAttributeFeed(Builder builder) { - setConfig(ONLY_IF_SERVICE_UP, builder.onlyIfServiceUp); - setConfig(NODE_NAME, checkNotNull(builder.nodeName, "builder.nodeName")); - - Set<ChefAttributePollConfig<?>> polls = Sets.newLinkedHashSet(); - for (ChefAttributePollConfig<?> config : builder.polls) { - if (!config.isEnabled()) continue; - @SuppressWarnings({ "unchecked", "rawtypes" }) - ChefAttributePollConfig<?> configCopy = new ChefAttributePollConfig(config); - if (configCopy.getPeriod() < 0) configCopy.period(builder.period); - polls.add(configCopy); - } - setConfig(POLLS, polls); - initUniqueTag(builder.uniqueTag, polls); - } - - @Override - protected void preStart() { - final String nodeName = getConfig(NODE_NAME); - final Set<ChefAttributePollConfig<?>> polls = getConfig(POLLS); - - long minPeriod = Integer.MAX_VALUE; - for (ChefAttributePollConfig<?> config : polls) { - minPeriod = Math.min(minPeriod, config.getPeriod()); - } - - knifeTaskFactory = new KnifeNodeAttributeQueryTaskFactory(nodeName); - - final Callable<SshPollValue> getAttributesFromKnife = new Callable<SshPollValue>() { - public SshPollValue call() throws Exception { - ProcessTaskWrapper<String> taskWrapper = knifeTaskFactory.newTask(); - final ExecutionContext executionContext = ((EntityInternal) entity).getManagementSupport().getExecutionContext(); - log.debug("START: Running knife to query attributes of Chef node {}", nodeName); - executionContext.submit(taskWrapper); - taskWrapper.block(); - log.debug("DONE: Running knife to query attributes of Chef node {}", nodeName); - return new SshPollValue(null, taskWrapper.getExitCode(), taskWrapper.getStdout(), taskWrapper.getStderr()); - } - }; - - getPoller().scheduleAtFixedRate( - new CallInEntityExecutionContext<SshPollValue>(entity, getAttributesFromKnife), - new SendChefAttributesToSensors(entity, polls), - minPeriod); - } - - @SuppressWarnings("unchecked") - protected Poller<SshPollValue> getPoller() { - return (Poller<SshPollValue>) super.getPoller(); - } - - /** - * An implementation of {@link KnifeTaskFactory} that queries for the attributes of a node. - */ - private static class KnifeNodeAttributeQueryTaskFactory extends KnifeTaskFactory<String> { - private final String nodeName; - - public KnifeNodeAttributeQueryTaskFactory(String nodeName) { - super("retrieve attributes of node " + nodeName); - this.nodeName = nodeName; - } - - @Override - protected List<String> initialKnifeParameters() { - return ImmutableList.of("node", "show", "-l", nodeName, "--format", "json"); - } - } - - /** - * A {@link Callable} that wraps another {@link Callable}, where the inner {@link Callable} is executed in the context of a - * specific entity. - * - * @param <T> The type of the {@link Callable}. - */ - private static class CallInEntityExecutionContext<T> implements Callable<T> { - - private final Callable<T> job; - private EntityLocal entity; - - private CallInEntityExecutionContext(EntityLocal entity, Callable<T> job) { - this.job = job; - this.entity = entity; - } - - @Override - public T call() throws Exception { - final ExecutionContext executionContext = ((EntityInternal) entity).getManagementSupport().getExecutionContext(); - return executionContext.submit(Maps.newHashMap(), job).get(); - } - } - - /** - * A poll handler that takes the result of the <tt>knife</tt> invocation and sets the appropriate sensors. - */ - private static class SendChefAttributesToSensors implements PollHandler<SshPollValue> { - private static final Iterable<String> PREFIXES = ImmutableList.of("", "automatic", "force_override", "override", "normal", "force_default", "default"); - private static final Splitter SPLITTER = Splitter.on('.'); - - private final EntityLocal entity; - private final Map<String, AttributeSensor<?>> chefAttributeSensors; - - public SendChefAttributesToSensors(EntityLocal entity, Set<ChefAttributePollConfig<?>> polls) { - this.entity = entity; - chefAttributeSensors = Maps.newLinkedHashMap(); - for (ChefAttributePollConfig<?> config : polls) { - chefAttributeSensors.put(config.getChefAttributePath(), config.getSensor()); - } - } - - @Override - public boolean checkSuccess(SshPollValue val) { - if (val.getExitStatus() != 0) return false; - String stderr = val.getStderr(); - if (stderr == null || stderr.length() != 0) return false; - String out = val.getStdout(); - if (out == null || out.length() == 0) return false; - if (!out.contains("{")) return false; - return true; - } - - @SuppressWarnings({ "unchecked", "rawtypes" }) - @Override - public void onSuccess(SshPollValue val) { - String stdout = val.getStdout(); - int jsonStarts = stdout.indexOf('{'); - if (jsonStarts > 0) - stdout = stdout.substring(jsonStarts); - JsonElement jsonElement = new Gson().fromJson(stdout, JsonElement.class); - - for (Map.Entry<String, AttributeSensor<?>> attribute : chefAttributeSensors.entrySet()) { - String chefAttributeName = attribute.getKey(); - AttributeSensor<?> sensor = attribute.getValue(); - log.trace("Finding value for attribute sensor " + sensor.getName()); - - Iterable<String> path = SPLITTER.split(chefAttributeName); - JsonElement elementForSensor = null; - for(String prefix : PREFIXES) { - Iterable<String> prefixedPath = !Strings.isNullOrEmpty(prefix) - ? Iterables.concat(ImmutableList.of(prefix), path) - : path; - try { - elementForSensor = getElementByPath(jsonElement.getAsJsonObject(), prefixedPath); - } catch(IllegalArgumentException e) { - log.error("Entity {}: bad Chef attribute {} for sensor {}: {}", new Object[]{ - entity.getDisplayName(), - Joiner.on('.').join(prefixedPath), - sensor.getName(), - e.getMessage()}); - throw Throwables.propagate(e); - } - if (elementForSensor != null) { - log.debug("Entity {}: apply Chef attribute {} to sensor {} with value {}", new Object[]{ - entity.getDisplayName(), - Joiner.on('.').join(prefixedPath), - sensor.getName(), - elementForSensor.getAsString()}); - break; - } - } - if (elementForSensor != null) { - entity.setAttribute((AttributeSensor)sensor, TypeCoercions.coerce(elementForSensor.getAsString(), sensor.getTypeToken())); - } else { - log.debug("Entity {}: no Chef attribute matching {}; setting sensor {} to null", new Object[]{ - entity.getDisplayName(), - chefAttributeName, - sensor.getName()}); - entity.setAttribute(sensor, null); - } - } - } - - private JsonElement getElementByPath(JsonElement element, Iterable<String> path) { - if (Iterables.isEmpty(path)) { - return element; - } else { - String head = Iterables.getFirst(path, null); - Preconditions.checkArgument(!Strings.isNullOrEmpty(head), "path must not contain empty or null elements"); - Iterable<String> tail = Iterables.skip(path, 1); - JsonElement child = ((JsonObject) element).get(head); - return child != null - ? getElementByPath(child, tail) - : null; - } - } - - @Override - public void onFailure(SshPollValue val) { - log.error("Chef attribute query did not respond as expected. exitcode={} stdout={} stderr={}", new Object[]{val.getExitStatus(), val.getStdout(), val.getStderr()}); - for (AttributeSensor<?> attribute : chefAttributeSensors.values()) { - if (!attribute.getName().startsWith(CHEF_ATTRIBUTE_PREFIX)) - continue; - entity.setAttribute(attribute, null); - } - } - - @Override - public void onException(Exception exception) { - log.error("Detected exception while retrieving Chef attributes from entity " + entity.getDisplayName(), exception); - for (AttributeSensor<?> attribute : chefAttributeSensors.values()) { - if (!attribute.getName().startsWith(CHEF_ATTRIBUTE_PREFIX)) - continue; - entity.setAttribute(attribute, null); - } - } - - @Override - public String toString() { - return super.toString()+"["+getDescription()+"]"; - } - - @Override - public String getDescription() { - return ""+chefAttributeSensors; - } - } -} http://git-wip-us.apache.org/repos/asf/incubator-brooklyn/blob/64c2b2e5/software/base/src/main/java/brooklyn/entity/chef/ChefAttributePollConfig.java ---------------------------------------------------------------------- diff --git a/software/base/src/main/java/brooklyn/entity/chef/ChefAttributePollConfig.java b/software/base/src/main/java/brooklyn/entity/chef/ChefAttributePollConfig.java deleted file mode 100644 index f2c90d2..0000000 --- a/software/base/src/main/java/brooklyn/entity/chef/ChefAttributePollConfig.java +++ /dev/null @@ -1,53 +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 brooklyn.entity.chef; - -import org.apache.brooklyn.api.sensor.AttributeSensor; -import org.apache.brooklyn.sensor.feed.PollConfig; - -import com.google.common.base.Function; -import com.google.common.base.Functions; - -public class ChefAttributePollConfig<T> extends PollConfig<Object, T, ChefAttributePollConfig<T>>{ - - private String chefAttributePath; - - @SuppressWarnings({ "unchecked", "rawtypes" }) - public ChefAttributePollConfig(AttributeSensor<T> sensor) { - super(sensor); - onSuccess((Function)Functions.identity()); - } - - public ChefAttributePollConfig(ChefAttributePollConfig<T> other) { - super(other); - this.chefAttributePath = other.chefAttributePath; - } - - public String getChefAttributePath() { - return chefAttributePath; - } - - public ChefAttributePollConfig<T> chefAttributePath(String val) { - this.chefAttributePath = val; return this; - } - - @Override protected String toStringBaseName() { return "chef"; } - @Override protected String toStringPollSource() { return chefAttributePath; } - -} http://git-wip-us.apache.org/repos/asf/incubator-brooklyn/blob/64c2b2e5/software/base/src/main/java/brooklyn/entity/chef/ChefBashCommands.java ---------------------------------------------------------------------- diff --git a/software/base/src/main/java/brooklyn/entity/chef/ChefBashCommands.java b/software/base/src/main/java/brooklyn/entity/chef/ChefBashCommands.java deleted file mode 100644 index 1f0577d..0000000 --- a/software/base/src/main/java/brooklyn/entity/chef/ChefBashCommands.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 brooklyn.entity.chef; - -import static org.apache.brooklyn.util.ssh.BashCommands.INSTALL_CURL; -import static org.apache.brooklyn.util.ssh.BashCommands.INSTALL_TAR; -import static org.apache.brooklyn.util.ssh.BashCommands.INSTALL_UNZIP; -import static org.apache.brooklyn.util.ssh.BashCommands.downloadToStdout; -import static org.apache.brooklyn.util.ssh.BashCommands.sudo; - -import org.apache.brooklyn.util.ssh.BashCommands; - -import com.google.common.annotations.Beta; - -/** BASH commands useful for setting up Chef */ -@Beta -public class ChefBashCommands { - - public static final String INSTALL_FROM_OPSCODE = - BashCommands.chain( - INSTALL_CURL, - INSTALL_TAR, - INSTALL_UNZIP, - "( "+downloadToStdout("https://www.opscode.com/chef/install.sh") + " | " + sudo("bash")+" )"); - -} http://git-wip-us.apache.org/repos/asf/incubator-brooklyn/blob/64c2b2e5/software/base/src/main/java/brooklyn/entity/chef/ChefConfig.java ---------------------------------------------------------------------- diff --git a/software/base/src/main/java/brooklyn/entity/chef/ChefConfig.java b/software/base/src/main/java/brooklyn/entity/chef/ChefConfig.java deleted file mode 100644 index 2483157..0000000 --- a/software/base/src/main/java/brooklyn/entity/chef/ChefConfig.java +++ /dev/null @@ -1,98 +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 brooklyn.entity.chef; - -import org.apache.brooklyn.config.ConfigKey; -import org.apache.brooklyn.core.config.ConfigKeys; -import org.apache.brooklyn.core.config.MapConfigKey; -import org.apache.brooklyn.core.config.SetConfigKey; -import org.apache.brooklyn.util.core.flags.SetFromFlag; - -import com.google.common.annotations.Beta; - -/** {@link ConfigKey}s used to configure the chef driver */ -@Beta -public interface ChefConfig { - - public static final ConfigKey<String> CHEF_COOKBOOK_PRIMARY_NAME = ConfigKeys.newStringConfigKey("brooklyn.chef.cookbook.primary.name", - "Namespace to use for passing data to Chef and for finding effectors"); - - @Deprecated /** @deprecatd since 0.7.0 use #CHEF_COOKBOOK_URLS */ - @SetFromFlag("cookbooks") - public static final MapConfigKey<String> CHEF_COOKBOOKS = new MapConfigKey<String>(String.class, "brooklyn.chef.cookbooksUrls"); - - @SetFromFlag("cookbook_urls") - public static final MapConfigKey<String> CHEF_COOKBOOK_URLS = new MapConfigKey<String>(String.class, "brooklyn.chef.cookbooksUrls"); - - @SetFromFlag("converge_twice") - public static final ConfigKey<Boolean> CHEF_RUN_CONVERGE_TWICE = ConfigKeys.newBooleanConfigKey("brooklyn.chef.converge.twice", - "Whether to run converge commands twice if the first one fails; needed in some contexts, e.g. when switching between chef-server and chef-solo mode", false); - - @Deprecated /** @deprecated since 0.7.0 use #CHEF_LAUNCH_RUN_LIST */ - public static final SetConfigKey<String> CHEF_RUN_LIST = new SetConfigKey<String>(String.class, "brooklyn.chef.runList"); - - /** typically set from spec, to customize the launch part of the start effector */ - @SetFromFlag("launch_run_list") - public static final SetConfigKey<String> CHEF_LAUNCH_RUN_LIST = new SetConfigKey<String>(String.class, "brooklyn.chef.launch.runList"); - /** typically set from spec, to customize the launch part of the start effector */ - @SetFromFlag("launch_attributes") - public static final MapConfigKey<Object> CHEF_LAUNCH_ATTRIBUTES = new MapConfigKey<Object>(Object.class, "brooklyn.chef.launch.attributes"); - - public static enum ChefModes { - /** Force use of Chef Solo */ - SOLO, - /** Force use of Knife; knife must be installed, and either - * {@link ChefConfig#KNIFE_EXECUTABLE} and {@link ChefConfig#KNIFE_CONFIG_FILE} must be set - * or knife on the path with valid global config set up */ - KNIFE, - // TODO server via API - /** Tries {@link #KNIFE} if valid, else {@link #SOLO} */ - AUTODETECT - }; - - @SetFromFlag("chef_mode") - public static final ConfigKey<ChefModes> CHEF_MODE = ConfigKeys.newConfigKey(ChefModes.class, "brooklyn.chef.mode", - "Whether Chef should run in solo mode, knife mode, or auto-detect", ChefModes.AUTODETECT); - - // TODO server-url for server via API mode - - public static final ConfigKey<String> KNIFE_SETUP_COMMANDS = ConfigKeys.newStringConfigKey("brooklyn.chef.knife.setupCommands", - "An optional set of commands to run on localhost before invoking knife; useful if using ruby version manager for example"); - public static final ConfigKey<String> KNIFE_EXECUTABLE = ConfigKeys.newStringConfigKey("brooklyn.chef.knife.executableFile", - "Knife command to run on the Brooklyn machine, including full path; defaults to scanning the path"); - public static final ConfigKey<String> KNIFE_CONFIG_FILE = ConfigKeys.newStringConfigKey("brooklyn.chef.knife.configFile", - "Knife config file (typically knife.rb) to use, including full path; defaults to knife default/global config"); - - @SetFromFlag("chef_node_name") - public static final ConfigKey<String> CHEF_NODE_NAME = ConfigKeys.newStringConfigKey("brooklyn.chef.node.nodeName", - "Node name to register with the chef server for this entity, if using Chef server and a specific node name is desired; " - + "if supplied ,this must be unique across the nodes Chef Server manages; if not supplied, one will be created if needed"); - - // for providing some simple (ssh-based) lifecycle operations and checks - @SetFromFlag("pid_file") - public static final ConfigKey<String> PID_FILE = ConfigKeys.newStringConfigKey("brooklyn.chef.lifecycle.pidFile", - "Path to PID file on remote machine, for use in checking running and stopping; may contain wildcards"); - @SetFromFlag("service_name") - public static final ConfigKey<String> SERVICE_NAME = ConfigKeys.newStringConfigKey("brooklyn.chef.lifecycle.serviceName", - "Name of OS service this will run as, for use in checking running and stopping"); - @SetFromFlag("windows_service_name") - public static final ConfigKey<String> WINDOWS_SERVICE_NAME = ConfigKeys.newStringConfigKey("brooklyn.chef.lifecycle.windowsServiceName", - "Name of OS service this will run as on Windows, if different there, for use in checking running and stopping"); - -} http://git-wip-us.apache.org/repos/asf/incubator-brooklyn/blob/64c2b2e5/software/base/src/main/java/brooklyn/entity/chef/ChefConfigs.java ---------------------------------------------------------------------- diff --git a/software/base/src/main/java/brooklyn/entity/chef/ChefConfigs.java b/software/base/src/main/java/brooklyn/entity/chef/ChefConfigs.java deleted file mode 100644 index 3ff7490..0000000 --- a/software/base/src/main/java/brooklyn/entity/chef/ChefConfigs.java +++ /dev/null @@ -1,102 +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 brooklyn.entity.chef; - -import java.util.Map; - -import org.apache.brooklyn.api.entity.Entity; -import org.apache.brooklyn.api.entity.EntitySpec; -import org.apache.brooklyn.config.ConfigKey; -import org.apache.brooklyn.core.config.MapConfigKey.MapModifications; -import org.apache.brooklyn.core.config.SetConfigKey.SetModifications; -import org.apache.brooklyn.entity.core.EntityInternal; -import org.apache.brooklyn.util.git.GithubUrls; - -import com.google.common.annotations.Beta; -import com.google.common.base.Preconditions; - -/** Conveniences for configuring brooklyn Chef entities - * @since 0.6.0 */ -@Beta -public class ChefConfigs { - - public static void addToLaunchRunList(EntitySpec<?> entity, String ...recipes) { - for (String recipe: recipes) - entity.configure(ChefConfig.CHEF_LAUNCH_RUN_LIST, SetModifications.addItem(recipe)); - } - - public static void addToLaunchRunList(EntityInternal entity, String ...recipes) { - for (String recipe: recipes) - entity.setConfig(ChefConfig.CHEF_LAUNCH_RUN_LIST, SetModifications.addItem(recipe)); - } - - public static void addToCookbooksFromGithub(EntitySpec<?> entity, String ...cookbookNames) { - for (String cookbookName: cookbookNames) - addToCookbooksFromGithub(entity, cookbookName, getGithubOpscodeRepo(cookbookName)); - } - - public static void addToCookbooksFromGithub(EntityInternal entity, String ...cookbookNames) { - for (String cookbookName: cookbookNames) - addToCookbooksFromGithub(entity, cookbookName, getGithubOpscodeRepo(cookbookName)); - } - - public static String getGithubOpscodeRepo(String cookbookName) { - return getGithubOpscodeRepo(cookbookName, "master"); - } - public static String getGithubOpscodeRepo(String cookbookName, String tag) { - return GithubUrls.tgz("opscode-cookbooks", cookbookName, tag); - } - - public static void addToCookbooksFromGithub(EntitySpec<?> entity, String cookbookName, String cookbookUrl) { - entity.configure(ChefConfig.CHEF_COOKBOOK_URLS.subKey(cookbookName), cookbookUrl); - } - - public static void addToCookbooksFromGithub(EntityInternal entity, String cookbookName, String cookbookUrl) { - entity.setConfig(ChefConfig.CHEF_COOKBOOK_URLS.subKey(cookbookName), cookbookUrl); - } - - @SuppressWarnings({ "unchecked", "rawtypes" }) - public static void addLaunchAttributes(EntitySpec<?> entity, Map<? extends Object,? extends Object> attributesMap) { - entity.configure(ChefConfig.CHEF_LAUNCH_ATTRIBUTES, MapModifications.add((Map)attributesMap)); - } - - @SuppressWarnings({ "unchecked", "rawtypes" }) - public static void addLaunchAttributes(EntityInternal entity, Map<? extends Object,? extends Object> attributesMap) { - entity.setConfig(ChefConfig.CHEF_LAUNCH_ATTRIBUTES, MapModifications.add((Map)attributesMap)); - } - - /** replaces the attributes underneath the rootAttribute parameter with the given value; - * see {@link #addLaunchAttributesMap(EntitySpec, Map)} for richer functionality */ - public static void setLaunchAttribute(EntitySpec<?> entity, String rootAttribute, Object value) { - entity.configure(ChefConfig.CHEF_LAUNCH_ATTRIBUTES.subKey(rootAttribute), value); - } - - /** replaces the attributes underneath the rootAttribute parameter with the given value; - * see {@link #addLaunchAttributesMap(EntitySpec, Map)} for richer functionality */ - public static void setLaunchAttribute(EntityInternal entity, String rootAttribute, Object value) { - entity.setConfig(ChefConfig.CHEF_LAUNCH_ATTRIBUTES.subKey(rootAttribute), value); - } - - public static <T> T getRequiredConfig(Entity entity, ConfigKey<T> key) { - return Preconditions.checkNotNull( - Preconditions.checkNotNull(entity, "Entity must be supplied").getConfig(key), - "Key "+key+" is required on "+entity); - } - -} http://git-wip-us.apache.org/repos/asf/incubator-brooklyn/blob/64c2b2e5/software/base/src/main/java/brooklyn/entity/chef/ChefEntity.java ---------------------------------------------------------------------- diff --git a/software/base/src/main/java/brooklyn/entity/chef/ChefEntity.java b/software/base/src/main/java/brooklyn/entity/chef/ChefEntity.java deleted file mode 100644 index 911eacc..0000000 --- a/software/base/src/main/java/brooklyn/entity/chef/ChefEntity.java +++ /dev/null @@ -1,27 +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 brooklyn.entity.chef; - -import org.apache.brooklyn.api.entity.ImplementedBy; - -import brooklyn.entity.basic.SoftwareProcess; - -@ImplementedBy(ChefEntityImpl.class) -public interface ChefEntity extends SoftwareProcess, ChefConfig { -} http://git-wip-us.apache.org/repos/asf/incubator-brooklyn/blob/64c2b2e5/software/base/src/main/java/brooklyn/entity/chef/ChefEntityImpl.java ---------------------------------------------------------------------- diff --git a/software/base/src/main/java/brooklyn/entity/chef/ChefEntityImpl.java b/software/base/src/main/java/brooklyn/entity/chef/ChefEntityImpl.java deleted file mode 100644 index d69640a..0000000 --- a/software/base/src/main/java/brooklyn/entity/chef/ChefEntityImpl.java +++ /dev/null @@ -1,38 +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 brooklyn.entity.chef; - -import org.apache.brooklyn.entity.stock.EffectorStartableImpl; -import org.apache.brooklyn.util.text.Strings; - -public class ChefEntityImpl extends EffectorStartableImpl implements ChefEntity { - - public void init() { - String primaryName = getConfig(CHEF_COOKBOOK_PRIMARY_NAME); - if (!Strings.isBlank(primaryName)) setDefaultDisplayName(primaryName+" (chef)"); - - super.init(); - new ChefLifecycleEffectorTasks().attachLifecycleEffectors(this); - } - - @Override - public void populateServiceNotUpDiagnostics() { - // TODO no-op currently; should check ssh'able etc - } -} http://git-wip-us.apache.org/repos/asf/incubator-brooklyn/blob/64c2b2e5/software/base/src/main/java/brooklyn/entity/chef/ChefLifecycleEffectorTasks.java ---------------------------------------------------------------------- diff --git a/software/base/src/main/java/brooklyn/entity/chef/ChefLifecycleEffectorTasks.java b/software/base/src/main/java/brooklyn/entity/chef/ChefLifecycleEffectorTasks.java deleted file mode 100644 index ad28d87..0000000 --- a/software/base/src/main/java/brooklyn/entity/chef/ChefLifecycleEffectorTasks.java +++ /dev/null @@ -1,362 +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 brooklyn.entity.chef; - -import java.util.Collection; -import java.util.Map; - -import org.apache.brooklyn.api.entity.Entity; -import org.apache.brooklyn.api.location.MachineLocation; -import org.apache.brooklyn.entity.core.Attributes; -import org.apache.brooklyn.entity.lifecycle.Lifecycle; -import org.slf4j.Logger; -import org.slf4j.LoggerFactory; - -import brooklyn.entity.basic.SoftwareProcess; -import brooklyn.entity.software.MachineLifecycleEffectorTasks; -import brooklyn.entity.software.SshEffectorTasks; - -import org.apache.brooklyn.location.basic.Machines; -import org.apache.brooklyn.util.collections.Jsonya; -import org.apache.brooklyn.util.collections.MutableMap; -import org.apache.brooklyn.util.collections.Jsonya.Navigator; -import org.apache.brooklyn.util.core.config.ConfigBag; -import org.apache.brooklyn.util.core.task.DynamicTasks; -import org.apache.brooklyn.util.core.task.TaskTags; -import org.apache.brooklyn.util.core.task.Tasks; -import org.apache.brooklyn.util.core.task.system.ProcessTaskWrapper; -import org.apache.brooklyn.util.exceptions.Exceptions; -import org.apache.brooklyn.util.net.Urls; -import org.apache.brooklyn.util.ssh.BashCommands; -import org.apache.brooklyn.util.text.Strings; -import org.apache.brooklyn.util.time.Duration; -import org.apache.brooklyn.util.time.Time; - -import com.google.common.annotations.Beta; -import com.google.common.base.Preconditions; -import com.google.common.base.Supplier; -import com.google.common.collect.ImmutableList; - -/** - * Creates effectors to start, restart, and stop processes using Chef. - * <p> - * Instances of this should use the {@link ChefConfig} config attributes to configure startup, - * and invoke {@link #usePidFile(String)} or {@link #useService(String)} to determine check-running and stop behaviour. - * Alternatively this can be subclassed and {@link #postStartCustom()} and {@link #stopProcessesAtMachine()} overridden. - * - * @since 0.6.0 - **/ -@Beta -public class ChefLifecycleEffectorTasks extends MachineLifecycleEffectorTasks implements ChefConfig { - - private static final Logger log = LoggerFactory.getLogger(ChefLifecycleEffectorTasks.class); - - protected String _pidFile, _serviceName, _windowsServiceName; - - public ChefLifecycleEffectorTasks() { - } - - public ChefLifecycleEffectorTasks usePidFile(String pidFile) { - this._pidFile = pidFile; - return this; - } - public ChefLifecycleEffectorTasks useService(String serviceName) { - this._serviceName = serviceName; - return this; - } - public ChefLifecycleEffectorTasks useWindowsService(String serviceName) { - this._windowsServiceName = serviceName; - return this; - } - - public String getPidFile() { - if (_pidFile!=null) return _pidFile; - return _pidFile = entity().getConfig(ChefConfig.PID_FILE); - } - - public String getServiceName() { - if (_serviceName!=null) return _serviceName; - return _serviceName = entity().getConfig(ChefConfig.SERVICE_NAME); - } - - protected String getNodeName() { - // (node name is needed so we can node delete it) - - // TODO would be better if CHEF_NODE_NAME were a freemarker template, could access entity.id, or hostname, etc, - // in addition to supporting hard-coded node names (which is all we support so far). - - String nodeName = entity().getConfig(ChefConfig.CHEF_NODE_NAME); - if (Strings.isNonBlank(nodeName)) return Strings.makeValidFilename(nodeName); - // node name is taken from ID of this entity, if not specified - return entity().getId(); - } - - public String getWindowsServiceName() { - if (_windowsServiceName!=null) return _windowsServiceName; - return _windowsServiceName = entity().getConfig(ChefConfig.WINDOWS_SERVICE_NAME); - } - - @Override - public void attachLifecycleEffectors(Entity entity) { - if (getPidFile()==null && getServiceName()==null && getClass().equals(ChefLifecycleEffectorTasks.class)) { - // warn on incorrect usage - log.warn("Uses of "+getClass()+" must define a PID file or a service name (or subclass and override {start,stop} methods as per javadoc) " + - "in order for check-running and stop to work"); - } - - super.attachLifecycleEffectors(entity); - } - - public static ChefModes detectChefMode(Entity entity) { - ChefModes mode = entity.getConfig(ChefConfig.CHEF_MODE); - if (mode == ChefModes.AUTODETECT) { - // TODO server via API - ProcessTaskWrapper<Boolean> installCheck = DynamicTasks.queue( - ChefServerTasks.isKnifeInstalled()); - mode = installCheck.get() ? ChefModes.KNIFE : ChefModes.SOLO; - log.debug("Using Chef in "+mode+" mode due to autodetect exit code "+installCheck.getExitCode()); - } - Preconditions.checkNotNull(mode, "Non-null "+ChefConfig.CHEF_MODE+" required for "+entity); - return mode; - } - - @Override - protected String startProcessesAtMachine(Supplier<MachineLocation> machineS) { - ChefModes mode = detectChefMode(entity()); - switch (mode) { - case KNIFE: - startWithKnifeAsync(); - break; - - case SOLO: - startWithChefSoloAsync(); - break; - - default: - throw new IllegalStateException("Unknown Chef mode "+mode+" when starting processes for "+entity()); - } - - return "chef start tasks submitted ("+mode+")"; - } - - protected String getPrimaryCookbook() { - return entity().getConfig(CHEF_COOKBOOK_PRIMARY_NAME); - } - - @SuppressWarnings({ "unchecked", "deprecation" }) - protected void startWithChefSoloAsync() { - String baseDir = MachineLifecycleEffectorTasks.resolveOnBoxDir(entity(), Machines.findUniqueSshMachineLocation(entity().getLocations()).get()); - String installDir = Urls.mergePaths(baseDir, "installs/chef"); - - @SuppressWarnings("rawtypes") - Map<String, String> cookbooks = (Map) - ConfigBag.newInstance( entity().getConfig(CHEF_COOKBOOK_URLS) ) - .putIfAbsent( entity().getConfig(CHEF_COOKBOOKS) ) - .getAllConfig(); - if (cookbooks.isEmpty()) - log.warn("No cookbook_urls set for "+entity()+"; launch will likely fail subsequently"); - DynamicTasks.queue( - ChefSoloTasks.installChef(installDir, false), - ChefSoloTasks.installCookbooks(installDir, cookbooks, false)); - - // TODO chef for and run a prestart recipe if necessary - // TODO open ports - - String primary = getPrimaryCookbook(); - - // put all config under brooklyn/cookbook/config - Navigator<MutableMap<Object, Object>> attrs = Jsonya.newInstancePrimitive().at("brooklyn"); - if (Strings.isNonBlank(primary)) attrs.at(primary); - attrs.at("config"); - attrs.put( entity().getAllConfigBag().getAllConfig() ); - // and put launch attrs at root - try { - attrs.root().put((Map<?,?>)Tasks.resolveDeepValue(entity().getConfig(CHEF_LAUNCH_ATTRIBUTES), Object.class, entity().getExecutionContext())); - } catch (Exception e) { Exceptions.propagate(e); } - - Collection<? extends String> runList = entity().getConfig(CHEF_LAUNCH_RUN_LIST); - if (runList==null) runList = entity().getConfig(CHEF_RUN_LIST); - if (runList==null) { - if (Strings.isNonBlank(primary)) runList = ImmutableList.of(primary+"::"+"start"); - else throw new IllegalStateException("Require a primary cookbook or a run_list to effect "+"start"+" on "+entity()); - } - - String runDir = Urls.mergePaths(baseDir, - "apps/"+entity().getApplicationId()+"/chef/entities/"+entity().getEntityType().getSimpleName()+"_"+entity().getId()); - - DynamicTasks.queue(ChefSoloTasks.buildChefFile(runDir, installDir, "launch", - runList, (Map<String, Object>) attrs.root().get())); - - DynamicTasks.queue(ChefSoloTasks.runChef(runDir, "launch", entity().getConfig(CHEF_RUN_CONVERGE_TWICE))); - } - - @SuppressWarnings({ "unchecked", "rawtypes", "deprecation" }) - protected void startWithKnifeAsync() { - // TODO prestart, ports (as above); also, note, some aspects of this are untested as we need a chef server - - String primary = getPrimaryCookbook(); - - // put all config under brooklyn/cookbook/config - Navigator<MutableMap<Object, Object>> attrs = Jsonya.newInstancePrimitive().at("brooklyn"); - if (Strings.isNonBlank(primary)) attrs.at(primary); - attrs.at("config"); - attrs.put( entity().getAllConfigBag().getAllConfig() ); - // and put launch attrs at root - try { - attrs.root().put((Map<?,?>)Tasks.resolveDeepValue(entity().getConfig(CHEF_LAUNCH_ATTRIBUTES), Object.class, entity().getExecutionContext())); - } catch (Exception e) { Exceptions.propagate(e); } - - Collection<? extends String> runList = entity().getConfig(CHEF_LAUNCH_RUN_LIST); - if (runList==null) runList = entity().getConfig(CHEF_RUN_LIST); - if (runList==null) { - if (Strings.isNonBlank(primary)) runList = ImmutableList.of(primary+"::"+"start"); - else throw new IllegalStateException("Require a primary cookbook or a run_list to effect "+"start"+" on "+entity()); - } - - DynamicTasks.queue( - ChefServerTasks.knifeConvergeTask() - .knifeNodeName(getNodeName()) - .knifeRunList(Strings.join(runList, ",")) - .knifeAddAttributes((Map<? extends Object, ? extends Object>)(Map) attrs.root().get()) - .knifeRunTwice(entity().getConfig(CHEF_RUN_CONVERGE_TWICE)) ); - } - - protected void postStartCustom() { - boolean result = false; - result |= tryCheckStartPid(); - result |= tryCheckStartService(); - result |= tryCheckStartWindowsService(); - if (!result) { - log.warn("No way to check whether "+entity()+" is running; assuming yes"); - } - entity().setAttribute(SoftwareProcess.SERVICE_UP, true); - } - - protected boolean tryCheckStartPid() { - if (getPidFile()==null) return false; - - // if it's still up after 5s assume we are good (default behaviour) - Time.sleep(Duration.FIVE_SECONDS); - if (!DynamicTasks.queue(SshEffectorTasks.isPidFromFileRunning(getPidFile()).runAsRoot()).get()) { - throw new IllegalStateException("The process for "+entity()+" appears not to be running (pid file "+getPidFile()+")"); - } - - // and set the PID - entity().setAttribute(Attributes.PID, - Integer.parseInt(DynamicTasks.queue(SshEffectorTasks.ssh("cat "+getPidFile()).runAsRoot()).block().getStdout().trim())); - return true; - } - - protected boolean tryCheckStartService() { - if (getServiceName()==null) return false; - - // if it's still up after 5s assume we are good (default behaviour) - Time.sleep(Duration.FIVE_SECONDS); - if (!((Integer)0).equals(DynamicTasks.queue(SshEffectorTasks.ssh("/etc/init.d/"+getServiceName()+" status").runAsRoot()).get())) { - throw new IllegalStateException("The process for "+entity()+" appears not to be running (service "+getServiceName()+")"); - } - - return true; - } - - protected boolean tryCheckStartWindowsService() { - if (getWindowsServiceName()==null) return false; - - // if it's still up after 5s assume we are good (default behaviour) - Time.sleep(Duration.FIVE_SECONDS); - if (!((Integer)0).equals(DynamicTasks.queue(SshEffectorTasks.ssh("sc query \""+getWindowsServiceName()+"\" | find \"RUNNING\"").runAsCommand()).get())) { - throw new IllegalStateException("The process for "+entity()+" appears not to be running (windowsService "+getWindowsServiceName()+")"); - } - - return true; - } - - @Override - protected String stopProcessesAtMachine() { - boolean result = false; - result |= tryStopService(); - result |= tryStopWindowsService(); - result |= tryStopPid(); - if (!result) { - throw new IllegalStateException("The process for "+entity()+" could not be stopped (no impl!)"); - } - return "stopped"; - } - - @Override - protected StopMachineDetails<Integer> stopAnyProvisionedMachines() { - if (detectChefMode(entity())==ChefModes.KNIFE) { - DynamicTasks.queue( - // if this task fails show it as failed but don't block subsequent routines - // (ie allow us to actually decommission the machine) - // TODO args could be a List<String> config key ? - TaskTags.markInessential( - new KnifeTaskFactory<String>("delete node and client registration at chef server") - .add("knife node delete "+getNodeName()+" -y") - .add("knife client delete "+getNodeName()+" -y") - .requiringZeroAndReturningStdout() - .newTask() )); - } - - return super.stopAnyProvisionedMachines(); - } - - protected boolean tryStopService() { - if (getServiceName()==null) return false; - int result = DynamicTasks.queue(SshEffectorTasks.ssh("/etc/init.d/"+getServiceName()+" stop").runAsRoot()).get(); - if (0==result) return true; - if (entity().getAttribute(Attributes.SERVICE_STATE_ACTUAL)!=Lifecycle.RUNNING) - return true; - - throw new IllegalStateException("The process for "+entity()+" appears could not be stopped (exit code "+result+" to service stop)"); - } - - protected boolean tryStopWindowsService() { - if (getWindowsServiceName()==null) return false; - int result = DynamicTasks.queue(SshEffectorTasks.ssh("sc query \""+getWindowsServiceName()+"\"").runAsCommand()).get(); - if (0==result) return true; - if (entity().getAttribute(Attributes.SERVICE_STATE_ACTUAL)!=Lifecycle.RUNNING) - return true; - - throw new IllegalStateException("The process for "+entity()+" appears could not be stopped (exit code "+result+" to service stop)"); - } - - protected boolean tryStopPid() { - Integer pid = entity().getAttribute(Attributes.PID); - if (pid==null) { - if (entity().getAttribute(Attributes.SERVICE_STATE_ACTUAL)==Lifecycle.RUNNING && getPidFile()==null) - log.warn("No PID recorded for "+entity()+" when running, with PID file "+getPidFile()+"; skipping kill in "+Tasks.current()); - else - if (log.isDebugEnabled()) - log.debug("No PID recorded for "+entity()+"; skipping ("+entity().getAttribute(Attributes.SERVICE_STATE_ACTUAL)+" / "+getPidFile()+")"); - return false; - } - - // allow non-zero exit as process may have already been killed - DynamicTasks.queue(SshEffectorTasks.ssh( - "kill "+pid, "sleep 5", BashCommands.ok("kill -9 "+pid)).allowingNonZeroExitCode().runAsRoot()).block(); - - if (DynamicTasks.queue(SshEffectorTasks.isPidRunning(pid).runAsRoot()).get()) { - throw new IllegalStateException("Process for "+entity()+" in "+pid+" still running after kill"); - } - entity().setAttribute(Attributes.PID, null); - return true; - } - -} http://git-wip-us.apache.org/repos/asf/incubator-brooklyn/blob/64c2b2e5/software/base/src/main/java/brooklyn/entity/chef/ChefServerTasks.java ---------------------------------------------------------------------- diff --git a/software/base/src/main/java/brooklyn/entity/chef/ChefServerTasks.java b/software/base/src/main/java/brooklyn/entity/chef/ChefServerTasks.java deleted file mode 100644 index 14ab230..0000000 --- a/software/base/src/main/java/brooklyn/entity/chef/ChefServerTasks.java +++ /dev/null @@ -1,97 +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 brooklyn.entity.chef; - - -import java.io.File; -import java.io.IOException; -import java.nio.charset.Charset; -import java.security.KeyPair; - -import org.apache.brooklyn.location.basic.SshMachineLocation; -import org.apache.brooklyn.util.core.crypto.SecureKeys; - -import com.google.common.base.Throwables; -import com.google.common.io.Files; - -public class ChefServerTasks { - - private static File chefKeyDir; - - private synchronized static File getExtractedKeysDir() { - if (chefKeyDir==null) { - chefKeyDir = Files.createTempDir(); - chefKeyDir.deleteOnExit(); - } - return chefKeyDir; - } - - /** extract key to a temp file, but one per machine, scheduled for deletion afterwards; - * we extract the key because chef has no way to accept passphrases at present */ - synchronized static File extractKeyFile(SshMachineLocation machine) { - File f = new File(getExtractedKeysDir(), machine.getAddress().getHostName()+".pem"); - if (f.exists()) return f; - KeyPair data = machine.findKeyPair(); - if (data==null) return null; - try { - f.deleteOnExit(); - Files.write(SecureKeys.stringPem(data), f, Charset.defaultCharset()); - return f; - } catch (IOException e) { - throw Throwables.propagate(e); - } - } - - public static KnifeTaskFactory<Boolean> isKnifeInstalled() { - return new KnifeTaskFactory<Boolean>("knife install check") - .knifeAddParameters("node list") - .notThrowingOnCommonKnifeErrors() - .returningIsExitCodeZero(); - } - - /** plain knife converge task - run list must be set, other arguments are optional */ - public static KnifeConvergeTaskFactory<String> knifeConvergeTask() { - return new KnifeConvergeTaskFactory<String>("knife converge") - .requiringZeroAndReturningStdout(); - } - /** knife converge task configured for this run list (and sudo) */ - public static KnifeConvergeTaskFactory<String> knifeConvergeRunList(String runList) { - return knifeConvergeTask() - .knifeRunList(runList) - .knifeSudo(true); - } - - /** knife converge task configured for this run list on windows (ssh) */ - public static KnifeConvergeTaskFactory<String> knifeConvergeRunListWindowsSsh(String runList) { - return knifeConvergeTask() - .knifeRunList(runList) - .knifeSudo(false) - .knifeAddExtraBootstrapParameters("windows ssh"); - } - - /** knife converge task configured for this run list on windows (winrm) */ - public static KnifeConvergeTaskFactory<String> knifeConvergeRunListWindowsWinrm(String runList) { - return knifeConvergeTask() - .knifeRunList(runList) - .knifeSudo(false) - .knifeAddExtraBootstrapParameters("windows winrm") - .knifePortUseKnifeDefault(); - } - -}
