http://git-wip-us.apache.org/repos/asf/incubator-brooklyn/blob/64c2b2e5/software/base/src/main/java/brooklyn/entity/brooklynnode/BrooklynNodeImpl.java ---------------------------------------------------------------------- diff --git a/software/base/src/main/java/brooklyn/entity/brooklynnode/BrooklynNodeImpl.java b/software/base/src/main/java/brooklyn/entity/brooklynnode/BrooklynNodeImpl.java deleted file mode 100644 index e09eab5..0000000 --- a/software/base/src/main/java/brooklyn/entity/brooklynnode/BrooklynNodeImpl.java +++ /dev/null @@ -1,524 +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; - -import java.net.URI; -import java.util.List; -import java.util.Map; -import java.util.concurrent.Callable; -import java.util.concurrent.atomic.AtomicReference; - -import javax.annotation.Nullable; - -import org.apache.brooklyn.api.effector.Effector; -import org.apache.brooklyn.api.entity.Entity; -import org.apache.brooklyn.api.mgmt.Task; -import org.apache.brooklyn.api.mgmt.TaskAdaptable; -import org.apache.brooklyn.api.mgmt.ha.ManagementNodeState; -import org.apache.brooklyn.config.ConfigKey; -import org.apache.brooklyn.core.config.render.RendererHints; -import org.apache.brooklyn.core.mgmt.BrooklynTaskTags; -import org.apache.brooklyn.effector.core.EffectorBody; -import org.apache.brooklyn.effector.core.Effectors; -import org.apache.brooklyn.entity.core.Attributes; -import org.apache.brooklyn.entity.core.Entities; -import org.apache.brooklyn.entity.lifecycle.Lifecycle; -import org.apache.brooklyn.entity.lifecycle.ServiceStateLogic; -import org.apache.brooklyn.entity.lifecycle.ServiceStateLogic.ServiceNotUpLogic; -import org.apache.brooklyn.entity.trait.Startable; -import org.apache.http.HttpStatus; -import org.slf4j.Logger; -import org.slf4j.LoggerFactory; - -import brooklyn.entity.basic.SoftwareProcess.StopSoftwareParameters.StopMode; -import brooklyn.entity.basic.SoftwareProcessImpl; -import brooklyn.entity.brooklynnode.EntityHttpClient.ResponseCodePredicates; -import brooklyn.entity.brooklynnode.effector.BrooklynNodeUpgradeEffectorBody; -import brooklyn.entity.brooklynnode.effector.SetHighAvailabilityModeEffectorBody; -import brooklyn.entity.brooklynnode.effector.SetHighAvailabilityPriorityEffectorBody; -import brooklyn.entity.software.MachineLifecycleEffectorTasks; - -import org.apache.brooklyn.location.access.BrooklynAccessUtils; -import org.apache.brooklyn.location.basic.Locations; -import org.apache.brooklyn.sensor.enricher.Enrichers; -import org.apache.brooklyn.sensor.feed.ConfigToAttributes; -import org.apache.brooklyn.sensor.feed.http.HttpFeed; -import org.apache.brooklyn.sensor.feed.http.HttpPollConfig; -import org.apache.brooklyn.sensor.feed.http.HttpValueFunctions; -import org.apache.brooklyn.sensor.feed.http.JsonFunctions; -import org.apache.brooklyn.util.collections.Jsonya; -import org.apache.brooklyn.util.collections.MutableMap; -import org.apache.brooklyn.util.core.config.ConfigBag; -import org.apache.brooklyn.util.core.http.HttpToolResponse; -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.exceptions.Exceptions; -import org.apache.brooklyn.util.exceptions.PropagatedRuntimeException; -import org.apache.brooklyn.util.guava.Functionals; -import org.apache.brooklyn.util.javalang.Enums; -import org.apache.brooklyn.util.javalang.JavaClassNames; -import org.apache.brooklyn.util.repeat.Repeater; -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.VisibleForTesting; -import com.google.common.base.Preconditions; -import com.google.common.base.Predicates; -import com.google.common.collect.ImmutableMap; -import com.google.common.net.HostAndPort; -import com.google.common.util.concurrent.Runnables; -import com.google.gson.Gson; - -public class BrooklynNodeImpl extends SoftwareProcessImpl implements BrooklynNode { - - private static final Logger log = LoggerFactory.getLogger(BrooklynNodeImpl.class); - - static { - RendererHints.register(WEB_CONSOLE_URI, RendererHints.namedActionWithUrl()); - } - - private static class UnmanageTask implements Runnable { - private Task<?> latchTask; - private Entity unmanageEntity; - - public UnmanageTask(@Nullable Task<?> latchTask, Entity unmanageEntity) { - this.latchTask = latchTask; - this.unmanageEntity = unmanageEntity; - } - - public void run() { - if (latchTask != null) { - latchTask.blockUntilEnded(); - } else { - log.debug("No latch task provided for UnmanageTask, falling back to fixed wait"); - Time.sleep(Duration.FIVE_SECONDS); - } - synchronized (this) { - Entities.unmanage(unmanageEntity); - } - } - } - - private HttpFeed httpFeed; - - public BrooklynNodeImpl() { - super(); - } - - public BrooklynNodeImpl(Entity parent) { - super(parent); - } - - @Override - public Class<?> getDriverInterface() { - return BrooklynNodeDriver.class; - } - - @Override - public void init() { - super.init(); - getMutableEntityType().addEffector(DeployBlueprintEffectorBody.DEPLOY_BLUEPRINT); - getMutableEntityType().addEffector(ShutdownEffectorBody.SHUTDOWN); - getMutableEntityType().addEffector(StopNodeButLeaveAppsEffectorBody.STOP_NODE_BUT_LEAVE_APPS); - getMutableEntityType().addEffector(StopNodeAndKillAppsEffectorBody.STOP_NODE_AND_KILL_APPS); - getMutableEntityType().addEffector(SetHighAvailabilityPriorityEffectorBody.SET_HIGH_AVAILABILITY_PRIORITY); - getMutableEntityType().addEffector(SetHighAvailabilityModeEffectorBody.SET_HIGH_AVAILABILITY_MODE); - getMutableEntityType().addEffector(BrooklynNodeUpgradeEffectorBody.UPGRADE); - } - - @Override - protected void preStart() { - ServiceNotUpLogic.clearNotUpIndicator(this, SHUTDOWN.getName()); - } - - @Override - protected void preStopConfirmCustom() { - super.preStopConfirmCustom(); - ConfigBag stopParameters = BrooklynTaskTags.getCurrentEffectorParameters(); - if (Boolean.TRUE.equals(getAttribute(BrooklynNode.WEB_CONSOLE_ACCESSIBLE)) && - stopParameters != null && !stopParameters.containsKey(ShutdownEffector.STOP_APPS_FIRST)) { - Preconditions.checkState(getChildren().isEmpty(), "Can't stop instance with running applications."); - } - } - - @Override - protected void preStop() { - super.preStop(); - if (MachineLifecycleEffectorTasks.canStop(getStopProcessModeParam(), this)) { - shutdownGracefully(); - } - } - - private StopMode getStopProcessModeParam() { - ConfigBag parameters = BrooklynTaskTags.getCurrentEffectorParameters(); - if (parameters != null) { - return parameters.get(StopSoftwareParameters.STOP_PROCESS_MODE); - } else { - return StopSoftwareParameters.STOP_PROCESS_MODE.getDefaultValue(); - } - } - - @Override - protected void preRestart() { - super.preRestart(); - //restart will kill the process, try to shut down before that - shutdownGracefully(); - DynamicTasks.queue("pre-restart", new Runnable() { public void run() { - //set by shutdown - clear it so the entity starts cleanly. Does the indicator bring any value at all? - ServiceNotUpLogic.clearNotUpIndicator(BrooklynNodeImpl.this, SHUTDOWN.getName()); - }}); - } - - private void shutdownGracefully() { - // Shutdown only if accessible: any of stop_* could have already been called. - // Don't check serviceUp=true because stop() will already have set serviceUp=false && expectedState=stopping - if (Boolean.TRUE.equals(getAttribute(BrooklynNode.WEB_CONSOLE_ACCESSIBLE))) { - queueShutdownTask(); - queueWaitExitTask(); - } else { - log.info("Skipping graceful shutdown call, because web-console not up for {}", this); - } - } - - private void queueWaitExitTask() { - //give time to the process to die gracefully after closing the shutdown call - DynamicTasks.queue(Tasks.builder().name("wait for graceful stop").body(new Runnable() { - @Override - public void run() { - DynamicTasks.markInessential(); - boolean cleanExit = Repeater.create() - .until(new Callable<Boolean>() { - @Override - public Boolean call() throws Exception { - return !getDriver().isRunning(); - } - }) - .backoffTo(Duration.ONE_SECOND) - .limitTimeTo(Duration.ONE_MINUTE) - .run(); - if (!cleanExit) { - log.warn("Tenant " + this + " didn't stop cleanly after shutdown. Timeout waiting for process exit."); - } - } - }).build()); - } - - @Override - protected void postStop() { - super.postStop(); - if (isMachineStopped()) { - // Don't unmanage in entity's task context as it will self-cancel the task. Wait for the stop effector to complete (and all parent entity tasks). - // If this is not enough (still getting Caused by: java.util.concurrent.CancellationException: null) then - // we could wait for BrooklynTaskTags.getTasksInEntityContext(ExecutionManager, this).isEmpty(); - Task<?> stopEffectorTask = BrooklynTaskTags.getClosestEffectorTask(Tasks.current(), Startable.STOP); - Task<?> topEntityTask = getTopEntityTask(stopEffectorTask); - getManagementContext().getExecutionManager().submit(new UnmanageTask(topEntityTask, this)); - } - } - - private Task<?> getTopEntityTask(Task<?> stopEffectorTask) { - Entity context = BrooklynTaskTags.getContextEntity(stopEffectorTask); - Task<?> topTask = stopEffectorTask; - while (true) { - Task<?> parentTask = topTask.getSubmittedByTask(); - Entity parentContext = BrooklynTaskTags.getContextEntity(parentTask); - if (parentTask == null || parentContext != context) { - return topTask; - } else { - topTask = parentTask; - } - } - } - - private boolean isMachineStopped() { - // Don't rely on effector parameters, check if there is still a machine running. - // If the entity was previously stopped with STOP_MACHINE_MODE=StopMode.NEVER - // and a second time with STOP_MACHINE_MODE=StopMode.IF_NOT_STOPPED, then the - // machine is still running, but there is no deterministic way to infer this from - // the parameters alone. - return Locations.findUniqueSshMachineLocation(this.getLocations()).isAbsent(); - } - - private void queueShutdownTask() { - ConfigBag stopParameters = BrooklynTaskTags.getCurrentEffectorParameters(); - ConfigBag shutdownParameters; - if (stopParameters != null) { - shutdownParameters = ConfigBag.newInstanceCopying(stopParameters); - } else { - shutdownParameters = ConfigBag.newInstance(); - } - shutdownParameters.putIfAbsent(ShutdownEffector.REQUEST_TIMEOUT, Duration.ONE_MINUTE); - shutdownParameters.putIfAbsent(ShutdownEffector.FORCE_SHUTDOWN_ON_ERROR, Boolean.TRUE); - TaskAdaptable<Void> shutdownTask = Effectors.invocation(this, SHUTDOWN, shutdownParameters); - //Mark inessential so that even if it fails the process stop task will run afterwards to clean up. - TaskTags.markInessential(shutdownTask); - DynamicTasks.queue(shutdownTask); - } - - public static class DeployBlueprintEffectorBody extends EffectorBody<String> implements DeployBlueprintEffector { - public static final Effector<String> DEPLOY_BLUEPRINT = Effectors.effector(BrooklynNode.DEPLOY_BLUEPRINT).impl(new DeployBlueprintEffectorBody()).build(); - - // TODO support YAML parsing - // TODO define a new type YamlMap for the config key which supports coercing from string and from map - @SuppressWarnings("unchecked") - public static Map<String,Object> asMap(ConfigBag parameters, ConfigKey<?> key) { - Object v = parameters.getStringKey(key.getName()); - if (v==null || (v instanceof String && Strings.isBlank((String)v))) - return null; - if (v instanceof Map) - return (Map<String, Object>) v; - - if (v instanceof String) { - // TODO ideally, parse YAML - return new Gson().fromJson((String)v, Map.class); - } - throw new IllegalArgumentException("Invalid "+JavaClassNames.simpleClassName(v)+" value for "+key+": "+v); - } - - @Override - public String call(ConfigBag parameters) { - if (log.isDebugEnabled()) - log.debug("Deploying blueprint to "+entity()+": "+parameters); - String plan = extractPlanYamlString(parameters); - return submitPlan(plan); - } - - protected String extractPlanYamlString(ConfigBag parameters) { - Object planRaw = parameters.getStringKey(BLUEPRINT_CAMP_PLAN.getName()); - if (planRaw instanceof String && Strings.isBlank((String)planRaw)) planRaw = null; - - String url = parameters.get(BLUEPRINT_TYPE); - if (url!=null && planRaw!=null) - throw new IllegalArgumentException("Cannot supply both plan and url"); - if (url==null && planRaw==null) - throw new IllegalArgumentException("Must supply plan or url"); - - Map<String, Object> config = asMap(parameters, BLUEPRINT_CONFIG); - - if (planRaw==null) { - planRaw = Jsonya.at("services").list().put("serviceType", url).putIfNotNull("brooklyn.config", config).getRootMap(); - } else { - if (config!=null) - throw new IllegalArgumentException("Cannot supply plan with config"); - } - - // planRaw might be a yaml string, or a map; if a map, convert to string - if (planRaw instanceof Map) - planRaw = Jsonya.of((Map<?,?>)planRaw).toString(); - if (!(planRaw instanceof String)) - throw new IllegalArgumentException("Invalid "+JavaClassNames.simpleClassName(planRaw)+" value for CAMP plan: "+planRaw); - - // now *all* the data is in planRaw; that is what will be submitted - return (String)planRaw; - } - - @VisibleForTesting - // Integration test for this in BrooklynNodeIntegrationTest in this project doesn't use this method, - // but a Unit test for this does, in DeployBlueprintTest -- but in the REST server project (since it runs against local) - public String submitPlan(final String plan) { - final MutableMap<String, String> headers = MutableMap.of(com.google.common.net.HttpHeaders.CONTENT_TYPE, "application/yaml"); - final AtomicReference<byte[]> response = new AtomicReference<byte[]>(); - Repeater.create() - .every(Duration.ONE_SECOND) - .backoffTo(Duration.FIVE_SECONDS) - .limitTimeTo(Duration.minutes(5)) - .repeat(Runnables.doNothing()) - .rethrowExceptionImmediately() - .until(new Callable<Boolean>() { - @Override - public Boolean call() { - HttpToolResponse result = ((BrooklynNode)entity()).http() - //will throw on non-{2xx, 403} response - .responseSuccess(Predicates.<Integer>or(ResponseCodePredicates.success(), Predicates.equalTo(HttpStatus.SC_FORBIDDEN))) - .post("/v1/applications", headers, plan.getBytes()); - if (result.getResponseCode() == HttpStatus.SC_FORBIDDEN) { - log.debug("Remote is not ready to accept requests, response is " + result.getResponseCode()); - return false; - } else { - byte[] content = result.getContent(); - response.set(content); - return true; - } - } - }) - .runRequiringTrue(); - return (String)new Gson().fromJson(new String(response.get()), Map.class).get("entityId"); - } - } - - public static class ShutdownEffectorBody extends EffectorBody<Void> implements ShutdownEffector { - public static final Effector<Void> SHUTDOWN = Effectors.effector(BrooklynNode.SHUTDOWN).impl(new ShutdownEffectorBody()).build(); - - @Override - public Void call(ConfigBag parameters) { - MutableMap<String, String> formParams = MutableMap.of(); - Lifecycle initialState = entity().getAttribute(Attributes.SERVICE_STATE_ACTUAL); - ServiceStateLogic.setExpectedState(entity(), Lifecycle.STOPPING); - for (ConfigKey<?> k: new ConfigKey<?>[] { STOP_APPS_FIRST, FORCE_SHUTDOWN_ON_ERROR, SHUTDOWN_TIMEOUT, REQUEST_TIMEOUT, DELAY_FOR_HTTP_RETURN }) - formParams.addIfNotNull(k.getName(), toNullableString(parameters.get(k))); - try { - log.debug("Shutting down "+entity()+" with "+formParams); - HttpToolResponse resp = ((BrooklynNode)entity()).http() - .post("/v1/server/shutdown", - ImmutableMap.of("Brooklyn-Allow-Non-Master-Access", "true"), - formParams); - if (resp.getResponseCode() != HttpStatus.SC_NO_CONTENT) { - throw new IllegalStateException("Response code "+resp.getResponseCode()); - } - } catch (Exception e) { - Exceptions.propagateIfFatal(e); - throw new PropagatedRuntimeException("Error shutting down remote node "+entity()+" (in state "+initialState+"): "+Exceptions.collapseText(e), e); - } - ServiceNotUpLogic.updateNotUpIndicator(entity(), SHUTDOWN.getName(), "Shutdown of remote node has completed successfuly"); - return null; - } - - private static String toNullableString(Object obj) { - if (obj == null) { - return null; - } else { - return obj.toString(); - } - } - - } - - public static class StopNodeButLeaveAppsEffectorBody extends EffectorBody<Void> implements StopNodeButLeaveAppsEffector { - public static final Effector<Void> STOP_NODE_BUT_LEAVE_APPS = Effectors.effector(BrooklynNode.STOP_NODE_BUT_LEAVE_APPS).impl(new StopNodeButLeaveAppsEffectorBody()).build(); - - @Override - public Void call(ConfigBag parameters) { - Duration timeout = parameters.get(TIMEOUT); - - ConfigBag stopParameters = ConfigBag.newInstanceCopying(parameters); - stopParameters.put(ShutdownEffector.STOP_APPS_FIRST, Boolean.FALSE); - stopParameters.putIfAbsent(ShutdownEffector.SHUTDOWN_TIMEOUT, timeout); - stopParameters.putIfAbsent(ShutdownEffector.REQUEST_TIMEOUT, timeout); - DynamicTasks.queue(Effectors.invocation(entity(), STOP, stopParameters)).asTask().getUnchecked(); - return null; - } - } - - public static class StopNodeAndKillAppsEffectorBody extends EffectorBody<Void> implements StopNodeAndKillAppsEffector { - public static final Effector<Void> STOP_NODE_AND_KILL_APPS = Effectors.effector(BrooklynNode.STOP_NODE_AND_KILL_APPS).impl(new StopNodeAndKillAppsEffectorBody()).build(); - - @Override - public Void call(ConfigBag parameters) { - Duration timeout = parameters.get(TIMEOUT); - - ConfigBag stopParameters = ConfigBag.newInstanceCopying(parameters); - stopParameters.put(ShutdownEffector.STOP_APPS_FIRST, Boolean.TRUE); - stopParameters.putIfAbsent(ShutdownEffector.SHUTDOWN_TIMEOUT, timeout); - stopParameters.putIfAbsent(ShutdownEffector.REQUEST_TIMEOUT, timeout); - DynamicTasks.queue(Effectors.invocation(entity(), STOP, stopParameters)).asTask().getUnchecked(); - return null; - } - } - - public List<String> getClasspath() { - List<String> classpath = getConfig(CLASSPATH); - if (classpath == null || classpath.isEmpty()) { - classpath = getManagementContext().getConfig().getConfig(CLASSPATH); - } - return classpath; - } - - protected List<String> getEnabledHttpProtocols() { - return getAttribute(ENABLED_HTTP_PROTOCOLS); - } - - protected boolean isHttpProtocolEnabled(String protocol) { - List<String> protocols = getAttribute(ENABLED_HTTP_PROTOCOLS); - for (String contender : protocols) { - if (protocol.equalsIgnoreCase(contender)) { - return true; - } - } - return false; - } - - @Override - protected void connectSensors() { - super.connectSensors(); - - // TODO what sensors should we poll? - ConfigToAttributes.apply(this); - - URI webConsoleUri; - if (isHttpProtocolEnabled("http")) { - int port = getConfig(PORT_MAPPER).apply(getAttribute(HTTP_PORT)); - HostAndPort accessible = BrooklynAccessUtils.getBrooklynAccessibleAddress(this, port); - webConsoleUri = URI.create(String.format("http://%s:%s", accessible.getHostText(), accessible.getPort())); - } else if (isHttpProtocolEnabled("https")) { - int port = getConfig(PORT_MAPPER).apply(getAttribute(HTTPS_PORT)); - HostAndPort accessible = BrooklynAccessUtils.getBrooklynAccessibleAddress(this, port); - webConsoleUri = URI.create(String.format("https://%s:%s", accessible.getHostText(), accessible.getPort())); - } else { - // web-console is not enabled - webConsoleUri = null; - } - setAttribute(WEB_CONSOLE_URI, webConsoleUri); - - if (webConsoleUri != null) { - httpFeed = HttpFeed.builder() - .entity(this) - .period(getConfig(POLL_PERIOD)) - .baseUri(webConsoleUri) - .credentialsIfNotNull(getConfig(MANAGEMENT_USER), getConfig(MANAGEMENT_PASSWORD)) - .poll(new HttpPollConfig<Boolean>(WEB_CONSOLE_ACCESSIBLE) - .suburl("/v1/server/healthy") - .onSuccess(Functionals.chain(HttpValueFunctions.jsonContents(), JsonFunctions.cast(Boolean.class))) - //if using an old distribution the path doesn't exist, but at least the instance is responding - .onFailure(HttpValueFunctions.responseCodeEquals(404)) - .setOnException(false)) - .poll(new HttpPollConfig<ManagementNodeState>(MANAGEMENT_NODE_STATE) - .suburl("/v1/server/ha/state") - .onSuccess(Functionals.chain(Functionals.chain(HttpValueFunctions.jsonContents(), JsonFunctions.cast(String.class)), Enums.fromStringFunction(ManagementNodeState.class))) - .setOnFailureOrException(null)) - // TODO sensors for load, size, etc - .build(); - - if (!Lifecycle.RUNNING.equals(getAttribute(SERVICE_STATE_ACTUAL))) { - // TODO when updating the map, if it would change from empty to empty on a successful run (see in nginx) - ServiceNotUpLogic.updateNotUpIndicator(this, WEB_CONSOLE_ACCESSIBLE, "No response from the web console yet"); - } - addEnricher(Enrichers.builder().updatingMap(Attributes.SERVICE_NOT_UP_INDICATORS) - .from(WEB_CONSOLE_ACCESSIBLE) - .computing(Functionals.ifNotEquals(true).value("URL where Brooklyn listens is not answering correctly") ) - .build()); - } else { - connectServiceUpIsRunning(); - } - } - - @Override - protected void disconnectSensors() { - super.disconnectSensors(); - disconnectServiceUpIsRunning(); - if (httpFeed != null) httpFeed.stop(); - } - - @Override - public EntityHttpClient http() { - return new EntityHttpClientImpl(this, BrooklynNode.WEB_CONSOLE_URI); - } - -}
http://git-wip-us.apache.org/repos/asf/incubator-brooklyn/blob/64c2b2e5/software/base/src/main/java/brooklyn/entity/brooklynnode/BrooklynNodeSshDriver.java ---------------------------------------------------------------------- diff --git a/software/base/src/main/java/brooklyn/entity/brooklynnode/BrooklynNodeSshDriver.java b/software/base/src/main/java/brooklyn/entity/brooklynnode/BrooklynNodeSshDriver.java deleted file mode 100644 index 13a099e..0000000 --- a/software/base/src/main/java/brooklyn/entity/brooklynnode/BrooklynNodeSshDriver.java +++ /dev/null @@ -1,395 +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; - -import static com.google.common.base.Preconditions.checkNotNull; -import static java.lang.String.format; - -import java.io.ByteArrayInputStream; -import java.io.File; -import java.io.InputStream; -import java.net.InetAddress; -import java.util.LinkedHashMap; -import java.util.List; -import java.util.Map; - -import brooklyn.entity.brooklynnode.BrooklynNode.ExistingFileBehaviour; -import brooklyn.entity.java.JavaSoftwareProcessSshDriver; -import brooklyn.entity.software.SshEffectorTasks; - -import org.apache.brooklyn.entity.core.Entities; -import org.apache.brooklyn.entity.drivers.downloads.DownloadSubstituters; -import org.apache.brooklyn.location.basic.SshMachineLocation; -import org.apache.brooklyn.util.collections.MutableMap; -import org.apache.brooklyn.util.core.file.ArchiveBuilder; -import org.apache.brooklyn.util.core.file.ArchiveUtils; -import org.apache.brooklyn.util.core.internal.ssh.SshTool; -import org.apache.brooklyn.util.core.task.DynamicTasks; -import org.apache.brooklyn.util.net.Networking; -import org.apache.brooklyn.util.net.Urls; -import org.apache.brooklyn.util.os.Os; -import org.apache.brooklyn.util.ssh.BashCommands; -import org.apache.brooklyn.util.text.Identifiers; -import org.apache.brooklyn.util.text.Strings; - -import com.google.common.base.Objects; -import com.google.common.collect.ImmutableMap; -import com.google.common.collect.Lists; - -public class BrooklynNodeSshDriver extends JavaSoftwareProcessSshDriver implements BrooklynNodeDriver { - - public BrooklynNodeSshDriver(BrooklynNodeImpl entity, SshMachineLocation machine) { - super(entity, machine); - } - - @Override - public BrooklynNodeImpl getEntity() { - return (BrooklynNodeImpl) super.getEntity(); - } - - public String getBrooklynHome() { - return getRunDir(); - } - - @Override - protected String getLogFileLocation() { - return Os.mergePathsUnix(getRunDir(), "console"); - } - - protected String getPidFile() { - return Os.mergePathsUnix(getRunDir(), "pid_java"); - } - - @Override - protected String getInstallLabelExtraSalt() { - String downloadUrl = entity.getConfig(BrooklynNode.DOWNLOAD_URL); - String uploadUrl = entity.getConfig(BrooklynNode.DISTRO_UPLOAD_URL); - if (Objects.equal(downloadUrl, BrooklynNode.DOWNLOAD_URL.getConfigKey().getDefaultValue()) && - Objects.equal(uploadUrl, BrooklynNode.DISTRO_UPLOAD_URL.getDefaultValue())) { - // if both are at the default value, then no salt - return null; - } - return Identifiers.makeIdFromHash(Objects.hashCode(downloadUrl, uploadUrl)); - } - - @Override - public void preInstall() { - resolver = Entities.newDownloader(this); - String subpath = entity.getConfig(BrooklynNode.SUBPATH_IN_ARCHIVE); - if (subpath==null) { - // assume the dir name is `basename-VERSION` where download link is `basename-VERSION-dist.tar.gz` - String uploadUrl = entity.getConfig(BrooklynNode.DISTRO_UPLOAD_URL); - String origDownloadName = uploadUrl; - if (origDownloadName==null) - origDownloadName = entity.getAttribute(BrooklynNode.DOWNLOAD_URL); - if (origDownloadName!=null) { - // BasicDownloadResolver makes it crazy hard to get the template-evaluated value of DOWNLOAD_URL - origDownloadName = DownloadSubstituters.substitute(origDownloadName, DownloadSubstituters.getBasicEntitySubstitutions(this)); - origDownloadName = Urls.decode(origDownloadName); - origDownloadName = Urls.getBasename(origDownloadName); - String downloadName = origDownloadName; - downloadName = Strings.removeFromEnd(downloadName, ".tar.gz"); - downloadName = Strings.removeFromEnd(downloadName, ".tgz"); - downloadName = Strings.removeFromEnd(downloadName, ".zip"); - if (!downloadName.equals(origDownloadName)) { - downloadName = Strings.removeFromEnd(downloadName, "-dist"); - subpath = downloadName; - } - } - } - if (subpath==null) subpath = format("brooklyn-dist-%s", getVersion()); - setExpandedInstallDir(Os.mergePaths(getInstallDir(), resolver.getUnpackedDirectoryName(subpath))); - } - - @Override - public void clearInstallDir() { - super.setInstallDir(null); - super.setExpandedInstallDir(null); - } - - @Override - public void install() { - String uploadUrl = entity.getConfig(BrooklynNode.DISTRO_UPLOAD_URL); - - // Need to explicitly give file, because for snapshot URLs you don't get a clean filename from the URL. - // This filename is used to generate the first URL to try: [BROOKLYN_VERSION_BELOW] - // file://$HOME/.brooklyn/repository/BrooklynNode/0.8.0-SNAPSHOT/brooklynnode-0.8.0-snapshot.tar.gz - // (DOWNLOAD_URL overrides this and has a default which comes from maven) - List<String> urls = resolver.getTargets(); - String saveAs = resolver.getFilename(); - - newScript("createInstallDir") - .body.append("mkdir -p "+getInstallDir()) - .failOnNonZeroResultCode() - .execute(); - - List<String> commands = Lists.newArrayList(); - // TODO use machine.installTo ... but that only works w a single location currently - if (uploadUrl != null) { - // Only upload if not already installed - boolean exists = newScript("checkIfInstalled") - .body.append("cd "+getInstallDir(), "test -f BROOKLYN") - .execute() == 0; - if (!exists) { - InputStream distroStream = resource.getResourceFromUrl(uploadUrl); - getMachine().copyTo(distroStream, getInstallDir()+"/"+saveAs); - } - } else { - commands.addAll(BashCommands.commandsToDownloadUrlsAs(urls, saveAs)); - } - commands.add(BashCommands.INSTALL_TAR); - commands.add("tar xzfv " + saveAs); - - newScript(INSTALLING). - failOnNonZeroResultCode(). - body.append(commands).execute(); - } - - @Override - public void customize() { - newScript(CUSTOMIZING) - .failOnNonZeroResultCode() - .body.append( - // workaround for AMP distribution placing everything in the root of this archive, but - // brooklyn distribution placing everything in a subdirectory: check to see if subdirectory - // with expected name exists; symlink to same directory if it doesn't - // FIXME remove when all downstream usages don't use this - format("[ -d %1$s ] || ln -s . %1$s", getExpandedInstallDir(), getExpandedInstallDir()), - - // previously we only copied bin,conf and set BROOKLYN_HOME to the install dir; - // but that does not play nicely if installing dists other than brooklyn - // (such as what is built by our artifact) - format("cp -R %s/* .", getExpandedInstallDir()), - "mkdir -p ./lib/") - .execute(); - - SshMachineLocation machine = getMachine(); - BrooklynNode entity = getEntity(); - - String brooklynGlobalPropertiesRemotePath = entity.getConfig(BrooklynNode.BROOKLYN_GLOBAL_PROPERTIES_REMOTE_PATH); - String brooklynGlobalPropertiesContents = entity.getConfig(BrooklynNode.BROOKLYN_GLOBAL_PROPERTIES_CONTENTS); - String brooklynGlobalPropertiesUri = entity.getConfig(BrooklynNode.BROOKLYN_GLOBAL_PROPERTIES_URI); - - String brooklynLocalPropertiesRemotePath = processTemplateContents(entity.getConfig(BrooklynNode.BROOKLYN_LOCAL_PROPERTIES_REMOTE_PATH)); - String brooklynLocalPropertiesContents = entity.getConfig(BrooklynNode.BROOKLYN_LOCAL_PROPERTIES_CONTENTS); - String brooklynLocalPropertiesUri = entity.getConfig(BrooklynNode.BROOKLYN_LOCAL_PROPERTIES_URI); - - String brooklynCatalogRemotePath = entity.getConfig(BrooklynNode.BROOKLYN_CATALOG_REMOTE_PATH); - String brooklynCatalogContents = entity.getConfig(BrooklynNode.BROOKLYN_CATALOG_CONTENTS); - String brooklynCatalogUri = entity.getConfig(BrooklynNode.BROOKLYN_CATALOG_URI); - - // Override the ~/.brooklyn/brooklyn.properties if required - if (brooklynGlobalPropertiesContents != null || brooklynGlobalPropertiesUri != null) { - ExistingFileBehaviour onExisting = entity.getConfig(BrooklynNode.ON_EXISTING_PROPERTIES_FILE); - Integer checkExists = DynamicTasks.queue(SshEffectorTasks.ssh("ls \""+brooklynGlobalPropertiesRemotePath+"\"").allowingNonZeroExitCode()).get(); - boolean doUpload = true; - if (checkExists==0) { - switch (onExisting) { - case USE_EXISTING: doUpload = false; break; - case OVERWRITE: break; - case DO_NOT_USE: - throw new IllegalStateException("Properties file "+brooklynGlobalPropertiesContents+" already exists and "+ - "even though it is not being used, content for it was supplied"); - case FAIL: - throw new IllegalStateException("Properties file "+brooklynGlobalPropertiesContents+" already exists and "+ - BrooklynNode.ON_EXISTING_PROPERTIES_FILE+" response is to fail"); - default: - throw new IllegalStateException("Properties file "+brooklynGlobalPropertiesContents+" already exists and "+ - BrooklynNode.ON_EXISTING_PROPERTIES_FILE+" response "+onExisting+" is unknown"); - } - } - if (onExisting==ExistingFileBehaviour.DO_NOT_USE) { - log.warn("Global properties supplied when told not to use them; no global properties exists, so it will be installed, but it will not be used."); - } - if (doUpload) - uploadFileContents(brooklynGlobalPropertiesContents, brooklynGlobalPropertiesUri, brooklynGlobalPropertiesRemotePath); - } - - // Upload a local-brooklyn.properties if required - if (brooklynLocalPropertiesContents != null || brooklynLocalPropertiesUri != null) { - uploadFileContents(brooklynLocalPropertiesContents, brooklynLocalPropertiesUri, brooklynLocalPropertiesRemotePath); - } - - // Override the ~/.brooklyn/catalog.xml if required - if (brooklynCatalogContents != null || brooklynCatalogUri != null) { - uploadFileContents(brooklynCatalogContents, brooklynCatalogUri, brooklynCatalogRemotePath); - } - - // Copy additional resources to the server - for (Map.Entry<String,String> entry : getEntity().getAttribute(BrooklynNode.COPY_TO_RUNDIR).entrySet()) { - Map<String, String> substitutions = ImmutableMap.of("RUN", getRunDir()); - String localResource = entry.getKey(); - String remotePath = entry.getValue(); - String resolvedRemotePath = remotePath; - for (Map.Entry<String,String> substitution : substitutions.entrySet()) { - String key = substitution.getKey(); - String val = substitution.getValue(); - resolvedRemotePath = resolvedRemotePath.replace("${"+key+"}", val).replace("$"+key, val); - } - machine.copyTo(MutableMap.of("permissions", "0600"), resource.getResourceFromUrl(localResource), resolvedRemotePath); - } - - for (String entry : getEntity().getClasspath()) { - // If a local folder, then create archive from contents first - if (Urls.isDirectory(entry)) { - File jarFile = ArchiveBuilder.jar().addDirContentsAt(new File(entry), "").create(); - entry = jarFile.getAbsolutePath(); - } - - // Determine filename - String destFile = entry.contains("?") ? entry.substring(0, entry.indexOf('?')) : entry; - destFile = destFile.substring(destFile.lastIndexOf('/') + 1); - - ArchiveUtils.deploy(MutableMap.<String, Object>of(), entry, machine, getRunDir(), Os.mergePaths(getRunDir(), "lib"), destFile); - } - - String cmd = entity.getConfig(BrooklynNode.EXTRA_CUSTOMIZATION_SCRIPT); - if (Strings.isNonBlank(cmd)) { - DynamicTasks.queueIfPossible( SshEffectorTasks.ssh(cmd).summary("Bespoke BrooklynNode customization script") - .requiringExitCodeZero() ) - .orSubmitAndBlock(getEntity()); - } - } - - @SuppressWarnings("deprecation") - @Override - public void launch() { - String app = getEntity().getAttribute(BrooklynNode.APP); - String locations = getEntity().getAttribute(BrooklynNode.LOCATIONS); - boolean hasLocalBrooklynProperties = getEntity().getConfig(BrooklynNode.BROOKLYN_LOCAL_PROPERTIES_CONTENTS) != null || getEntity().getConfig(BrooklynNode.BROOKLYN_LOCAL_PROPERTIES_URI) != null; - String localBrooklynPropertiesPath = processTemplateContents(getEntity().getConfig(BrooklynNode.BROOKLYN_LOCAL_PROPERTIES_REMOTE_PATH)); - InetAddress bindAddress = getEntity().getAttribute(BrooklynNode.WEB_CONSOLE_BIND_ADDRESS); - InetAddress publicAddress = getEntity().getAttribute(BrooklynNode.WEB_CONSOLE_PUBLIC_ADDRESS); - - String cmd = entity.getConfig(BrooklynNode.LAUNCH_COMMAND); - if (Strings.isBlank(cmd)) cmd = "./bin/brooklyn"; - cmd = "nohup " + cmd + " launch"; - if (app != null) { - cmd += " --app "+app; - } - if (locations != null) { - cmd += " --locations "+locations; - } - if (entity.getConfig(BrooklynNode.ON_EXISTING_PROPERTIES_FILE)==ExistingFileBehaviour.DO_NOT_USE) { - cmd += " --noGlobalBrooklynProperties"; - } - if (hasLocalBrooklynProperties) { - cmd += " --localBrooklynProperties "+localBrooklynPropertiesPath; - } - Integer webPort = null; - if (getEntity().isHttpProtocolEnabled("http")) { - webPort = getEntity().getAttribute(BrooklynNode.HTTP_PORT); - Networking.checkPortsValid(ImmutableMap.of("webPort", webPort)); - } else if (getEntity().isHttpProtocolEnabled("https")) { - webPort = getEntity().getAttribute(BrooklynNode.HTTPS_PORT); - Networking.checkPortsValid(ImmutableMap.of("webPort", webPort)); - } - if (webPort!=null) { - cmd += " --port "+webPort; - } else if (getEntity().getEnabledHttpProtocols().isEmpty()) { - // TODO sensors will probably not work in this mode - cmd += " --noConsole"; - } else { - throw new IllegalStateException("Unknown web protocol in "+BrooklynNode.ENABLED_HTTP_PROTOCOLS+" " - + "("+getEntity().getEnabledHttpProtocols()+"); expecting 'http' or 'https'"); - } - - if (bindAddress != null) { - cmd += " --bindAddress "+bindAddress.getHostAddress(); - } - if (publicAddress != null) { - cmd += " --publicAddress "+publicAddress.getHostName(); - } - if (getEntity().getAttribute(BrooklynNode.NO_WEB_CONSOLE_AUTHENTICATION)) { - cmd += " --noConsoleSecurity"; - } - if (Strings.isNonBlank(getEntity().getConfig(BrooklynNode.EXTRA_LAUNCH_PARAMETERS))) { - cmd += " "+getEntity().getConfig(BrooklynNode.EXTRA_LAUNCH_PARAMETERS); - } - cmd += format(" >> %s/console 2>&1 </dev/null &", getRunDir()); - - log.info("Starting brooklyn on {} using command {}", getMachine(), cmd); - - // relies on brooklyn script creating pid file - newScript(ImmutableMap.of("usePidFile", - entity.getConfig(BrooklynNode.LAUNCH_COMMAND_CREATES_PID_FILE) ? false : getPidFile()), - LAUNCHING). - body.append( - format("export BROOKLYN_CLASSPATH=%s", getRunDir()+"/lib/\"*\""), - format("export BROOKLYN_HOME=%s", getBrooklynHome()), - format(cmd) - ).failOnNonZeroResultCode().execute(); - } - - @Override - public boolean isRunning() { - Map<String,String> flags = ImmutableMap.of("usePidFile", getPidFile()); - int result = newScript(flags, CHECK_RUNNING).execute(); - return result == 0; - } - - @Override - public void stop() { - Map<String,String> flags = ImmutableMap.of("usePidFile", getPidFile()); - newScript(flags, STOPPING).execute(); - } - - @Override - public void kill() { - Map<String,String> flags = ImmutableMap.of("usePidFile", getPidFile()); - newScript(flags, KILLING).execute(); - } - - @Override - public Map<String, String> getShellEnvironment() { - Map<String, String> orig = super.getShellEnvironment(); - String origClasspath = orig.get("CLASSPATH"); - String newClasspath = (origClasspath == null ? "" : origClasspath+":") + - getRunDir()+"/conf/" + ":" + - getRunDir()+"/lib/\"*\""; - Map<String,String> results = new LinkedHashMap<String,String>(); - results.putAll(orig); - results.put("BROOKLYN_CLASSPATH", newClasspath); - results.put("BROOKLYN_HOME", getBrooklynHome()); - results.put("RUN", getRunDir()); - return results; - } - - private void uploadFileContents(String contents, String alternativeUri, String remotePath) { - checkNotNull(remotePath, "remotePath"); - SshMachineLocation machine = getMachine(); - String tempRemotePath = String.format("%s/upload.tmp", getRunDir()); - - if (contents == null && alternativeUri == null) { - throw new IllegalStateException("No contents supplied for file " + remotePath); - } - InputStream stream = contents != null - ? new ByteArrayInputStream(contents.getBytes()) - : resource.getResourceFromUrl(alternativeUri); - Map<String, String> flags = MutableMap.of(SshTool.PROP_PERMISSIONS.getName(), "0600"); - machine.copyTo(flags, stream, tempRemotePath); - newScript(CUSTOMIZING) - .failOnNonZeroResultCode() - .body.append( - format("mkdir -p %s", remotePath.subSequence(0, remotePath.lastIndexOf("/"))), - format("cp -p %s %s", tempRemotePath, remotePath), - format("rm -f %s", tempRemotePath)) - .execute(); - } -} http://git-wip-us.apache.org/repos/asf/incubator-brooklyn/blob/64c2b2e5/software/base/src/main/java/brooklyn/entity/brooklynnode/EntityHttpClient.java ---------------------------------------------------------------------- diff --git a/software/base/src/main/java/brooklyn/entity/brooklynnode/EntityHttpClient.java b/software/base/src/main/java/brooklyn/entity/brooklynnode/EntityHttpClient.java deleted file mode 100644 index deed605..0000000 --- a/software/base/src/main/java/brooklyn/entity/brooklynnode/EntityHttpClient.java +++ /dev/null @@ -1,93 +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; - -import java.util.Map; - -import org.apache.brooklyn.util.core.http.HttpTool; -import org.apache.brooklyn.util.core.http.HttpToolResponse; - -import com.google.common.base.Predicate; -import com.google.common.base.Predicates; -import com.google.common.collect.Range; - -/** - * Helpful methods for making HTTP requests to {@link BrooklynNode} entities. - */ -public interface EntityHttpClient { - public static class ResponseCodePredicates { - private static class ResponseCodeHealthyPredicate implements Predicate<Integer> { - @Override - public boolean apply(Integer input) { - return HttpTool.isStatusCodeHealthy(input); - } - } - public static Predicate<Integer> informational() {return Range.closed(100, 199);} - public static Predicate<Integer> success() {return new ResponseCodeHealthyPredicate();} - public static Predicate<Integer> redirect() {return Range.closed(300, 399);} - public static Predicate<Integer> clientError() {return Range.closed(400, 499);} - public static Predicate<Integer> serverError() {return Range.closed(500, 599);} - public static Predicate<Integer> invalid() {return Predicates.or(Range.atMost(99), Range.atLeast(600));} - } - - /** - * @return An HTTP client builder configured to access the {@link - * BrooklynNode#WEB_CONSOLE_URI web console URI} at the - * given entity, or null if the entity has no URI. - */ - public HttpTool.HttpClientBuilder getHttpClientForBrooklynNode(); - - /** - * Configure which response codes are treated as successful - * @param successPredicate A predicate which returns true is the response code is acceptable - * @return this - */ - public EntityHttpClient responseSuccess(Predicate<Integer> responseSuccess); - - /** - * Makes an HTTP GET to a Brooklyn node entity. - * @param path Relative path to resource on server, e.g v1/catalog - * @return The server's response - */ - public HttpToolResponse get(String path); - - /** - * Makes an HTTP POST to a Brooklyn node entity. - * @param path Relative path to resource on server, e.g v1/catalog - * @param body byte array of serialized JSON to attach to the request - * @return The server's response - */ - public HttpToolResponse post(String path, Map<String, String> headers, byte[] body); - - /** - * Makes an HTTP POST to a Brooklyn node entity. - * @param path Relative path to resource on server, e.g v1/catalog - * @param formParams The parameters to send in a x-www-form-urlencoded format - * @return The server's response - */ - public HttpToolResponse post(String path, Map<String, String> headers, Map<String, String> formParams); - - /** - * Makes an HTTP DELETE to a Brooklyn node entity. - * @param path Relative path to resource on server, e.g v1/catalog - * @return The server's response - */ - public HttpToolResponse delete(String path, Map<String, String> headers); - -} http://git-wip-us.apache.org/repos/asf/incubator-brooklyn/blob/64c2b2e5/software/base/src/main/java/brooklyn/entity/brooklynnode/EntityHttpClientImpl.java ---------------------------------------------------------------------- diff --git a/software/base/src/main/java/brooklyn/entity/brooklynnode/EntityHttpClientImpl.java b/software/base/src/main/java/brooklyn/entity/brooklynnode/EntityHttpClientImpl.java deleted file mode 100644 index f34ac15..0000000 --- a/software/base/src/main/java/brooklyn/entity/brooklynnode/EntityHttpClientImpl.java +++ /dev/null @@ -1,162 +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; - -import static com.google.common.base.Preconditions.checkNotNull; - -import java.net.URI; -import java.util.Map; - -import org.apache.brooklyn.api.entity.Entity; -import org.apache.brooklyn.api.sensor.AttributeSensor; -import org.apache.brooklyn.config.ConfigKey; -import org.apache.brooklyn.core.mgmt.BrooklynTaskTags; -import org.apache.brooklyn.util.collections.MutableMap; -import org.apache.brooklyn.util.core.http.HttpTool; -import org.apache.brooklyn.util.core.http.HttpToolResponse; -import org.apache.brooklyn.util.core.task.Tasks; -import org.apache.brooklyn.util.exceptions.Exceptions; -import org.apache.brooklyn.util.net.Urls; -import org.apache.brooklyn.util.stream.Streams; -import org.apache.http.auth.UsernamePasswordCredentials; -import org.apache.http.client.HttpClient; -import org.slf4j.Logger; -import org.slf4j.LoggerFactory; - -import com.google.common.base.Preconditions; -import com.google.common.base.Predicate; - -public class EntityHttpClientImpl implements EntityHttpClient { - private static final Logger LOG = LoggerFactory.getLogger(EntityHttpClientImpl.class); - - protected static interface HttpCall { - public HttpToolResponse call(HttpClient client, URI uri); - } - - protected Entity entity; - protected AttributeSensor<?> urlSensor; - protected ConfigKey<?> urlConfig; - protected Predicate<Integer> responseSuccess = ResponseCodePredicates.success(); - - protected EntityHttpClientImpl(Entity entity, AttributeSensor<?> urlSensor) { - this.entity = entity; - this.urlSensor = urlSensor; - } - - protected EntityHttpClientImpl(Entity entity, ConfigKey<?> urlConfig) { - this.entity = entity; - this.urlConfig = urlConfig; - } - - @Override - public HttpTool.HttpClientBuilder getHttpClientForBrooklynNode() { - String baseUrl = getEntityUrl(); - HttpTool.HttpClientBuilder builder = HttpTool.httpClientBuilder() - .trustAll() - .laxRedirect(true) - .uri(baseUrl); - if (entity.getConfig(BrooklynNode.MANAGEMENT_USER) != null) { - UsernamePasswordCredentials credentials = new UsernamePasswordCredentials( - entity.getConfig(BrooklynNode.MANAGEMENT_USER), - entity.getConfig(BrooklynNode.MANAGEMENT_PASSWORD)); - builder.credentials(credentials); - } - return builder; - } - - @Override - public EntityHttpClient responseSuccess(Predicate<Integer> responseSuccess) { - this.responseSuccess = checkNotNull(responseSuccess, "responseSuccess"); - return this; - } - - protected HttpToolResponse exec(String path, HttpCall httpCall) { - HttpClient client = Preconditions.checkNotNull(getHttpClientForBrooklynNode(), "No address info for "+entity) - .build(); - String baseUri = getEntityUrl(); - URI uri = URI.create(Urls.mergePaths(baseUri, path)); - - HttpToolResponse result; - try { - result = httpCall.call(client, uri); - } catch (Exception e) { - Exceptions.propagateIfFatal(e); - throw new IllegalStateException("Invalid response invoking " + uri + ": " + e, e); - } - Tasks.addTagDynamically(BrooklynTaskTags.tagForStream("http_response", Streams.byteArray(result.getContent()))); - if (!responseSuccess.apply(result.getResponseCode())) { - LOG.warn("Invalid response invoking {}: response code {}\n{}: {}", - new Object[]{uri, result.getResponseCode(), result, new String(result.getContent())}); - throw new IllegalStateException("Invalid response invoking " + uri + ": response code " + result.getResponseCode()); - } - return result; - } - - @Override - public HttpToolResponse get(String path) { - return exec(path, new HttpCall() { - @Override - public HttpToolResponse call(HttpClient client, URI uri) { - return HttpTool.httpGet(client, uri, MutableMap.<String, String>of()); - } - }); - } - - @Override - public HttpToolResponse post(String path, final Map<String, String> headers, final byte[] body) { - return exec(path, new HttpCall() { - @Override - public HttpToolResponse call(HttpClient client, URI uri) { - return HttpTool.httpPost(client, uri, headers, body); - } - }); - } - - @Override - public HttpToolResponse post(String path, final Map<String, String> headers, final Map<String, String> formParams) { - return exec(path, new HttpCall() { - @Override - public HttpToolResponse call(HttpClient client, URI uri) { - return HttpTool.httpPost(client, uri, headers, formParams); - } - }); - } - - protected String getEntityUrl() { - Preconditions.checkState(urlSensor == null ^ urlConfig == null, "Exactly one of urlSensor and urlConfig should be non-null for entity " + entity); - Object url = null; - if (urlSensor != null) { - url = entity.getAttribute(urlSensor); - } else if (urlConfig != null) { - url = entity.getConfig(urlConfig); - } - Preconditions.checkNotNull(url, "URL sensor " + urlSensor + " for entity " + entity + " is empty"); - return url.toString(); - } - - @Override - public HttpToolResponse delete(String path, final Map<String, String> headers) { - return exec(path, new HttpCall() { - @Override - public HttpToolResponse call(HttpClient client, URI uri) { - return HttpTool.httpDelete(client, uri, headers); - } - }); - } -} http://git-wip-us.apache.org/repos/asf/incubator-brooklyn/blob/64c2b2e5/software/base/src/main/java/brooklyn/entity/brooklynnode/LocalBrooklynNode.java ---------------------------------------------------------------------- diff --git a/software/base/src/main/java/brooklyn/entity/brooklynnode/LocalBrooklynNode.java b/software/base/src/main/java/brooklyn/entity/brooklynnode/LocalBrooklynNode.java deleted file mode 100644 index 41714de..0000000 --- a/software/base/src/main/java/brooklyn/entity/brooklynnode/LocalBrooklynNode.java +++ /dev/null @@ -1,37 +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; - -import org.apache.brooklyn.api.entity.ImplementedBy; - -/** - * A {@link BrooklynNode} entity that represents the local Brooklyn service. - * <p> - * Management username and password can be specified in the {@code brooklyn.properties} file, as - * either specific username and password (useful when the credentials are set with SHA-256 hashes - * or via LDAP) or a username with separately configured webconsole plaintext password. - * <pre> - * brooklyn.entity.brooklynnode.local.user=admin - * brooklyn.entity.brooklynnode.local.password=password - * brooklyn.webconsole.security.user.admin.password=password - * </pre> - */ -@ImplementedBy(LocalBrooklynNodeImpl.class) -public interface LocalBrooklynNode extends BrooklynNode { -} http://git-wip-us.apache.org/repos/asf/incubator-brooklyn/blob/64c2b2e5/software/base/src/main/java/brooklyn/entity/brooklynnode/LocalBrooklynNodeImpl.java ---------------------------------------------------------------------- diff --git a/software/base/src/main/java/brooklyn/entity/brooklynnode/LocalBrooklynNodeImpl.java b/software/base/src/main/java/brooklyn/entity/brooklynnode/LocalBrooklynNodeImpl.java deleted file mode 100644 index 1096ea5..0000000 --- a/software/base/src/main/java/brooklyn/entity/brooklynnode/LocalBrooklynNodeImpl.java +++ /dev/null @@ -1,46 +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; - -import org.apache.brooklyn.core.internal.BrooklynProperties; -import org.apache.brooklyn.util.text.Strings; - -public class LocalBrooklynNodeImpl extends BrooklynNodeImpl implements LocalBrooklynNode { - - private static final String LOCAL_BROOKLYN_NODE_KEY = "brooklyn.entity.brooklynnode.local.%s"; - private static final String BROOKLYN_WEBCONSOLE_PASSWORD_KEY = "brooklyn.webconsole.security.user.%s.password"; - - @Override - protected void connectSensors() { - // Override management username and password from brooklyn.properties - BrooklynProperties properties = (BrooklynProperties) getManagementContext().getConfig(); - String user = (String) properties.get(String.format(LOCAL_BROOKLYN_NODE_KEY, "user")); - String password = (String) properties.get(String.format(LOCAL_BROOKLYN_NODE_KEY, "password")); - if (Strings.isBlank(password)) { - if (Strings.isBlank(user)) user = "admin"; - password = (String) properties.get(String.format(BROOKLYN_WEBCONSOLE_PASSWORD_KEY, user)); - } - if (Strings.isNonBlank(user) && Strings.isNonBlank(password)) { - setConfig(MANAGEMENT_USER, user); - setConfig(MANAGEMENT_PASSWORD, password); - } - super.connectSensors(); - } - -} http://git-wip-us.apache.org/repos/asf/incubator-brooklyn/blob/64c2b2e5/software/base/src/main/java/brooklyn/entity/brooklynnode/RemoteEffectorBuilder.java ---------------------------------------------------------------------- diff --git a/software/base/src/main/java/brooklyn/entity/brooklynnode/RemoteEffectorBuilder.java b/software/base/src/main/java/brooklyn/entity/brooklynnode/RemoteEffectorBuilder.java deleted file mode 100644 index fedeff9..0000000 --- a/software/base/src/main/java/brooklyn/entity/brooklynnode/RemoteEffectorBuilder.java +++ /dev/null @@ -1,85 +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; - -import java.util.ArrayList; -import java.util.Collection; -import java.util.Map; - -import org.apache.brooklyn.api.effector.Effector; -import org.apache.brooklyn.effector.core.Effectors; -import org.apache.brooklyn.effector.core.Effectors.EffectorBuilder; -import org.apache.brooklyn.util.core.http.HttpToolResponse; - -import brooklyn.entity.brooklynnode.BrooklynEntityMirrorImpl.RemoteEffector; - -import com.google.common.base.Function; - -public class RemoteEffectorBuilder { - private static class ResultParser implements Function<HttpToolResponse, String> { - @Override - public String apply(HttpToolResponse input) { - return input.getContentAsString(); - } - } - - - public static Collection<Effector<String>> of(Collection<?> cfgEffectors) { - Collection<Effector<String>> effectors = new ArrayList<Effector<String>>(); - for (Object objEff : cfgEffectors) { - Map<?, ?> cfgEff = (Map<?, ?>)objEff; - String effName = (String)cfgEff.get("name"); - String description = (String)cfgEff.get("description"); - - EffectorBuilder<String> eff = Effectors.effector(String.class, effName); - Collection<?> params = (Collection<?>)cfgEff.get("parameters"); - - /* The *return type* should NOT be included in the signature here. - * It might be a type known only at the mirrored brooklyn node - * (in which case loading it here would fail); or possibly it could - * be a different version of the type here, in which case the signature - * would look valid here, but deserializing it would fail. - * - * Best to just pass the json representation back to the caller. - * (They won't be able to tell the difference between that and deserialize-then-serialize!) - */ - - if (description != null) { - eff.description(description); - } - - for (Object objParam : params) { - buildParam(eff, (Map<?, ?>)objParam); - } - - eff.impl(new RemoteEffector<String>(effName, new ResultParser())); - effectors.add(eff.build()); - } - return effectors; - } - - private static void buildParam(EffectorBuilder<String> eff, Map<?, ?> cfgParam) { - String name = (String)cfgParam.get("name"); - String description = (String)cfgParam.get("description"); - String defaultValue = (String)cfgParam.get("defaultValue"); - - eff.parameter(Object.class, name, description, defaultValue /*TypeCoercions.coerce(defaultValue, paramType)*/); - } - -} http://git-wip-us.apache.org/repos/asf/incubator-brooklyn/blob/64c2b2e5/software/base/src/main/java/brooklyn/entity/brooklynnode/effector/BrooklynClusterUpgradeEffectorBody.java ---------------------------------------------------------------------- diff --git a/software/base/src/main/java/brooklyn/entity/brooklynnode/effector/BrooklynClusterUpgradeEffectorBody.java b/software/base/src/main/java/brooklyn/entity/brooklynnode/effector/BrooklynClusterUpgradeEffectorBody.java deleted file mode 100644 index 26ecaca..0000000 --- a/software/base/src/main/java/brooklyn/entity/brooklynnode/effector/BrooklynClusterUpgradeEffectorBody.java +++ /dev/null @@ -1,207 +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.io.File; -import java.util.Collection; -import java.util.Collections; -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.EntitySpec; -import org.apache.brooklyn.api.entity.Group; -import org.apache.brooklyn.api.mgmt.TaskAdaptable; -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.Attributes; -import org.apache.brooklyn.entity.core.EntityPredicates; -import org.apache.brooklyn.entity.core.EntityTasks; -import org.apache.brooklyn.entity.group.DynamicCluster; -import org.apache.brooklyn.entity.lifecycle.Lifecycle; -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.core.task.Tasks; -import org.apache.brooklyn.util.net.Urls; -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.BrooklynCluster.UpgradeClusterEffector; -import brooklyn.entity.brooklynnode.BrooklynNode; -import brooklyn.entity.brooklynnode.BrooklynNode.SetHighAvailabilityModeEffector; - -import com.google.common.base.Preconditions; -import com.google.common.base.Predicates; -import com.google.common.collect.Collections2; -import com.google.common.collect.Iterables; - -public class BrooklynClusterUpgradeEffectorBody extends EffectorBody<Void> implements UpgradeClusterEffector { - - private static final Logger log = LoggerFactory.getLogger(BrooklynClusterUpgradeEffectorBody.class); - - public static final Effector<Void> UPGRADE_CLUSTER = Effectors.effector(UpgradeClusterEffector.UPGRADE_CLUSTER) - .impl(new BrooklynClusterUpgradeEffectorBody()).build(); - - private final AtomicBoolean upgradeInProgress = new AtomicBoolean(); - - @Override - public Void call(ConfigBag parameters) { - if (!upgradeInProgress.compareAndSet(false, true)) { - throw new IllegalStateException("An upgrade is already in progress."); - } - - EntitySpec<?> origMemberSpec = entity().getConfig(BrooklynCluster.MEMBER_SPEC); - Preconditions.checkNotNull(origMemberSpec, BrooklynCluster.MEMBER_SPEC.getName() + " is required for " + UpgradeClusterEffector.class.getName()); - - log.debug("Upgrading "+entity()+", changing "+BrooklynCluster.MEMBER_SPEC+" from "+origMemberSpec+" / "+origMemberSpec.getConfig()); - - boolean success = false; - try { - String newDownloadUrl = parameters.get(DOWNLOAD_URL); - - EntitySpec<?> newMemberSpec = EntitySpec.create(origMemberSpec); - - ConfigBag newConfig = ConfigBag.newInstance(); - newConfig.putIfNotNull(DOWNLOAD_URL, newDownloadUrl); - newConfig.put(BrooklynNode.DISTRO_UPLOAD_URL, inferUploadUrl(newDownloadUrl)); - newConfig.putAll(ConfigBag.newInstance(parameters.get(EXTRA_CONFIG)).getAllConfigAsConfigKeyMap()); - newMemberSpec.configure(newConfig.getAllConfigAsConfigKeyMap()); - - entity().setConfig(BrooklynCluster.MEMBER_SPEC, newMemberSpec); - - log.debug("Upgrading "+entity()+", new "+BrooklynCluster.MEMBER_SPEC+": "+newMemberSpec+" / "+newMemberSpec.getConfig()+" (adding: "+newConfig+")"); - - upgrade(parameters); - - success = true; - } finally { - if (!success) { - log.debug("Upgrading "+entity()+" failed, will rethrow after restoring "+BrooklynCluster.MEMBER_SPEC+" to: "+origMemberSpec); - entity().setConfig(BrooklynCluster.MEMBER_SPEC, origMemberSpec); - } - - upgradeInProgress.set(false); - } - return null; - } - - private String inferUploadUrl(String newDownloadUrl) { - if (newDownloadUrl==null) return null; - boolean isLocal = "file".equals(Urls.getProtocol(newDownloadUrl)) || new File(newDownloadUrl).exists(); - if (isLocal) { - return newDownloadUrl; - } else { - return null; - } - } - - protected void upgrade(ConfigBag parameters) { - //TODO currently this will fight with auto-scaler policies; they must be turned off for upgrade to work - - Group cluster = (Group)entity(); - Collection<Entity> initialMembers = cluster.getMembers(); - int initialClusterSize = initialMembers.size(); - - if (!BrooklynNodeUpgradeEffectorBody.isPersistenceModeEnabled(cluster)) { - // 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. " - + "Cluster upgrade will not succeed unless a custom launch script enables it.")) ); - } - - //TODO we'd like to disable these nodes as standby targets, ie in some 'hot standby but not available for failover' mode - //currently if failover happens to a new node, assumptions below may fail and the cluster may require manual repair - - //1. Initially create a single node to check if it will launch successfully - TaskAdaptable<Collection<Entity>> initialNodeTask = DynamicTasks.queue(newCreateNodesTask(1, "Creating first upgraded version node")); - - //2. If everything is OK with the first node launch the rest as well - @SuppressWarnings("unused") - TaskAdaptable<Collection<Entity>> remainingNodesTask = DynamicTasks.queue(newCreateNodesTask(initialClusterSize - 1, "Creating remaining upgraded version nodes ("+(initialClusterSize - 1)+")")); - - //3. Once we have all nodes running without errors switch master - DynamicTasks.queue(Effectors.invocation(cluster, BrooklynCluster.SELECT_MASTER, MutableMap.of(SelectMasterEffector.NEW_MASTER_ID, - Iterables.getOnlyElement(initialNodeTask.asTask().getUnchecked()).getId()))).asTask().getUnchecked(); - - //4. Stop the nodes which were running at the start of the upgrade call, but keep them around. - // Should we create a quarantine-like zone for old stopped version? - // For members that were created meanwhile - they will be using the new version already. If the new version - // isn't good then they will fail to start as well, forcing the policies to retry (and succeed once the - // URL is reverted). - - //any other nodes created via other means should also be using the new spec, so initialMembers will be all the old version nodes - DynamicTasks.queue(Effectors.invocation(BrooklynNode.STOP_NODE_BUT_LEAVE_APPS, Collections.emptyMap(), initialMembers)).asTask().getUnchecked(); - } - - private TaskAdaptable<Collection<Entity>> newCreateNodesTask(int size, String name) { - return Tasks.<Collection<Entity>>builder().name(name).body(new CreateNodesCallable(size)).build(); - } - - protected class CreateNodesCallable implements Callable<Collection<Entity>> { - private final int size; - public CreateNodesCallable(int size) { - this.size = size; - } - @Override - public Collection<Entity> call() throws Exception { - return createNodes(size); - } - } - - protected Collection<Entity> createNodes(int nodeCnt) { - DynamicCluster cluster = (DynamicCluster)entity(); - - //1. Create the nodes - Collection<Entity> newNodes = cluster.resizeByDelta(nodeCnt); - - //2. Wait for them to be RUNNING (or at least STARTING to have completed) - // (should already be the case, because above is synchronous and, we think, it will fail if start does not succeed) - DynamicTasks.queue(EntityTasks.requiringAttributeEventually(newNodes, Attributes.SERVICE_STATE_ACTUAL, - Predicates.not(Predicates.equalTo(Lifecycle.STARTING)), Duration.minutes(30))); - - //3. Set HOT_STANDBY in case it is not enabled on the command line ... - // TODO support via EntitySpec - DynamicTasks.queue(Effectors.invocation( - BrooklynNode.SET_HIGH_AVAILABILITY_MODE, - MutableMap.of(SetHighAvailabilityModeEffector.MODE, HighAvailabilityMode.HOT_STANDBY), - newNodes)).asTask().getUnchecked(); - //... and wait until all of the nodes change state - // TODO fail quicker if state changes to FAILED - DynamicTasks.queue(EntityTasks.requiringAttributeEventually(newNodes, BrooklynNode.MANAGEMENT_NODE_STATE, - Predicates.equalTo(ManagementNodeState.HOT_STANDBY), Duration.FIVE_MINUTES)); - - // TODO also check that the nodes created all report the original master, in case persistence changes it - - //5. Just in case check if all of the nodes are SERVICE_UP (which would rule out ON_FIRE as well) - Collection<Entity> failedNodes = Collections2.filter(newNodes, EntityPredicates.attributeEqualTo(BrooklynNode.SERVICE_UP, Boolean.FALSE)); - if (!failedNodes.isEmpty()) { - throw new IllegalStateException("Nodes " + failedNodes + " are not " + BrooklynNode.SERVICE_UP + " though successfully in " + ManagementNodeState.HOT_STANDBY); - } - return newNodes; - } - -}
