@Test public void testAddChild() throws Exception { try { // to test in GUI: // services: [ { type: org.apache.brooklyn.entity.stock.BasicEntity }] Response response = client() .path(entityEndpoint + "/children") .query("timeout", "10s") .post( javax.ws.rs.client.Entity.entity( "services: [ { type: " + TestEntity.class.getName() + " }]", "application/yaml")); HttpAsserts.assertHealthyStatusCode(response.getStatus()); Assert.assertEquals(entity.getChildren().size(), 1); Entity child = Iterables.getOnlyElement(entity.getChildren()); Assert.assertTrue(Entities.isManaged(child)); TaskSummary task = response.readEntity(TaskSummary.class); Assert.assertEquals(task.getResult(), MutableList.of(child.getId())); } finally { // restore it for other tests Collection<Entity> children = entity.getChildren(); if (!children.isEmpty()) Entities.unmanage(Iterables.getOnlyElement(children)); } }
/** De-register our {@link MesosLocation} and its children. */ @Override public void stop() { disconnectSensors(); sensors().set(SERVICE_UP, Boolean.FALSE); deleteLocation(); Duration timeout = config().get(SHUTDOWN_TIMEOUT); // Find all applications and stop, blocking for up to five minutes until ended try { Iterable<Entity> entities = Iterables.filter( getManagementContext().getEntityManager().getEntities(), Predicates.and( MesosUtils.sameCluster(this), Predicates.not(EntityPredicates.applicationIdEqualTo(getApplicationId())))); Set<Application> applications = ImmutableSet.copyOf( Iterables.transform( entities, new Function<Entity, Application>() { @Override public Application apply(Entity input) { return input.getApplication(); } })); LOG.debug("Stopping applications: {}", Iterables.toString(applications)); Entities.invokeEffectorList(this, applications, Startable.STOP).get(timeout); } catch (Exception e) { LOG.warn("Error stopping applications", e); } // Stop all framework tasks in parallel try { Group frameworks = sensors().get(MESOS_FRAMEWORKS); LOG.debug("Stopping framework tasks in: {}", Iterables.toString(frameworks.getMembers())); Entities.invokeEffectorList(this, frameworks.getMembers(), Startable.STOP).get(timeout); } catch (Exception e) { LOG.warn("Error stopping frameworks", e); } // Stop anything else left over // TODO Stop slave entities try { super.stop(); } catch (Exception e) { LOG.warn("Error stopping children", e); } }
// Test reproduces functionality used in Monterey, for Venue entity being told of requestActor @Test public void testSubscribeToJmxNotificationAndEmitCorrespondingNotificationSensor() throws Exception { TestApplication app2 = new TestApplicationImpl(); final EntityWithEmitter entity = new EntityWithEmitter(app2); Entities.startManagement(app2); try { app2.start(ImmutableList.of(new SimulatedLocation())); final List<SensorEvent<String>> received = Lists.newArrayList(); app2.subscriptions() .subscribe( null, EntityWithEmitter.MY_NOTIF, new SensorEventListener<String>() { public void onEvent(SensorEvent<String> event) { received.add(event); } }); final StandardEmitterMBean mbean = jmxService.registerMBean(ImmutableList.of("one"), objectName); final AtomicInteger sequence = new AtomicInteger(0); jmxHelper.connect(TIMEOUT_MS); jmxHelper.addNotificationListener( jmxObjectName, new NotificationListener() { public void handleNotification(Notification notif, Object callback) { if (notif.getType().equals("one")) { entity.sensors().emit(EntityWithEmitter.MY_NOTIF, (String) notif.getUserData()); } } }); Asserts.succeedsEventually( ImmutableMap.of("timeout", TIMEOUT_MS), new Runnable() { public void run() { sendNotification(mbean, "one", sequence.getAndIncrement(), "abc"); assertTrue(received.size() > 0, "received size should be bigger than 0"); assertEquals(received.get(0).getValue(), "abc"); } }); } finally { Entities.destroyAll(app2.getManagementContext()); } }
/*TODO RENAME Method. It could be represent that the service is bound and the service operation is called*/ private void manageService(Entity rawEntity) { CloudFoundryService cloudFoundryService; if (rawEntity instanceof CloudFoundryService) { cloudFoundryService = (CloudFoundryService) rawEntity; String serviceName = cloudFoundryService.getConfig(CloudFoundryService.SERVICE_INSTANCE_NAME); if (!Strings.isEmpty(serviceName)) { Entities.waitForServiceUp( cloudFoundryService, cloudFoundryService.getConfig(BrooklynConfigKeys.START_TIMEOUT)); bindingServiceToEntity(serviceName); setCredentialsOnService(cloudFoundryService); cloudFoundryService.operation(getEntity()); } else { log.error( "Trying to get service instance name from {}, but getting null", cloudFoundryService); } } else { log.error( "The service entity {} is not available from the application {}", new Object[] {rawEntity, getEntity()}); throw new NoSuchElementException( "No entity matching id " + rawEntity.getId() + " in Management Context " + getEntity().getManagementContext() + " during entity service binding " + getEntity().getId()); } }
@Override public void initApp() { StringConfigMap config = getManagementContext().getConfig(); GeoscalingDnsService geoDns = addChild( EntitySpec.create(GeoscalingDnsService.class) .displayName("GeoScaling DNS") .configure( "username", checkNotNull(config.getFirst("brooklyn.geoscaling.username"), "username")) .configure( "password", checkNotNull(config.getFirst("brooklyn.geoscaling.password"), "password")) .configure( "primaryDomainName", checkNotNull( config.getFirst("brooklyn.geoscaling.primaryDomain"), "primaryDomain")) .configure("smartSubdomainName", "brooklyn")); DynamicRegionsFabric webFabric = addChild( EntitySpec.create(DynamicRegionsFabric.class) .displayName("Web Fabric") .configure(DynamicRegionsFabric.FACTORY, new ElasticJavaWebAppService.Factory()) // specify the WAR file to use .configure( JavaWebAppService.ROOT_WAR, Entities.getRequiredUrlConfig(this, WAR_PATH))); // tell GeoDNS what to monitor geoDns.setTargetEntityProvider(webFabric); }
@AfterMethod(alwaysRun = true) public void tearDown() throws Exception { List<Exception> exceptions = Lists.newArrayList(); try { if (machines != null) { exceptions.addAll(releaseMachineSafely(machines)); machines.clear(); } } finally { try { if (managementContext != null) Entities.destroyAll(managementContext); } catch (Exception e) { LOG.warn("Error destroying management context", e); exceptions.add(e); } } // TODO Debate about whether to: // - use destroyAllCatching (i.e. not propagating exception) // Benefit is that other tests in class will subsequently be run, rather than bailing out. // - propagate exceptions from tearDown // Benefit is that we don't hide errors; release(...) etc should not be throwing exceptions. if (exceptions.size() > 0) { throw new CompoundRuntimeException("Error in tearDown of " + getClass(), exceptions); } }
@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 release(DockerContainerLocation machine) { lock.readLock().lock(); try { LOG.info("Releasing {}", machine); DynamicCluster cluster = dockerHost.getDockerContainerCluster(); DockerContainer container = machine.getOwner(); if (cluster.removeMember(container)) { LOG.info("Docker Host {}: member {} released", dockerHost.getDockerHostName(), machine); } else { LOG.warn( "Docker Host {}: member {} not found for release", dockerHost.getDockerHostName(), machine); } // Now close and unmange the container try { machine.close(); container.stop(); } catch (Exception e) { LOG.warn("Error stopping container: " + container, e); Exceptions.propagateIfFatal(e); } finally { Entities.unmanage(container); } } finally { lock.readLock().unlock(); } }
@Override public void doStart(Collection<? extends Location> locs) { List<Location> locations = MutableList.of(); sensors().set(SERVICE_UP, Boolean.FALSE); ServiceStateLogic.setExpectedState(this, Lifecycle.STARTING); LOG.info("Creating new MesosLocation"); createLocation(MutableMap.<String, Object>of()); // Start frameworks try { Group frameworks = sensors().get(MESOS_FRAMEWORKS); Entities.invokeEffectorList( this, frameworks.getMembers(), Startable.START, ImmutableMap.of("locations", locations)) .getUnchecked(); } catch (Exception e) { LOG.warn("Error starting frameworks", e); ServiceStateLogic.setExpectedState(this, Lifecycle.ON_FIRE); Exceptions.propagate(e); } super.doStart(locations); connectSensors(); ServiceStateLogic.setExpectedState(this, Lifecycle.RUNNING); sensors().set(SERVICE_UP, Boolean.TRUE); }
@AfterMethod(alwaysRun = true) public void tearDown() throws Exception { if (feed != null) feed.stop(); if (jmxHelper != null) jmxHelper.disconnect(); if (jmxService != null) jmxService.shutdown(); if (app != null) Entities.destroyAll(app.getManagementContext()); feed = null; }
protected WinRmExecuteHelper newScript(Map<String, ?> flags, String phase) { if (!Entities.isManaged(getEntity())) throw new IllegalStateException( getEntity() + " is no longer managed; cannot create script to run here (" + phase + ")"); WinRmExecuteHelper s = new WinRmExecuteHelper(this, phase + " " + elvis(entity, this)); return s; }
@Override public void init() { super.init(); getMutableEntityType() .addEffector( EXECUTE_SCRIPT, new EffectorBody<String>() { @Override public String call(ConfigBag parameters) { return executeScript((String) parameters.getStringKey("commands")); } }); Entities.checkRequiredUrl(this, getCassandraConfigTemplateUrl()); Entities.getRequiredUrlConfig(this, CASSANDRA_RACKDC_CONFIG_TEMPLATE_URL); connectEnrichers(); }
@Override public String getRpcAddress() { String sensorName = getConfig(RPC_ADDRESS_SENSOR); if (Strings.isNonBlank(sensorName)) return Entities.submit( this, DependentConfiguration.attributeWhenReady(this, Sensors.newStringSensor(sensorName))) .getUnchecked(); return "0.0.0.0"; }
protected void runStopProcessAndRestart(Effector<?> restartEffector, Map<String, ?> args) throws Exception { LocalhostMachineProvisioningLocation loc = app.newLocalhostProvisioningLocation(); SoftwareProcess entity = app.createAndManageChild(newEntitySpec()); // Start the app app.start(ImmutableList.of(loc)); EntityTestUtils.assertAttributeEqualsEventually(entity, SoftwareProcess.SERVICE_UP, true); EntityTestUtils.assertAttributeEqualsEventually(app, SoftwareProcess.SERVICE_UP, true); // Stop the process Entities.invokeEffector( app, entity, SoftwareProcess.STOP, ImmutableMap.of( StopSoftwareParameters.STOP_MACHINE_MODE.getName(), StopSoftwareParameters.StopMode.NEVER)) .get(); EntityTestUtils.assertAttributeEqualsEventually(entity, SoftwareProcess.SERVICE_UP, false); EntityTestUtils.assertAttributeEqualsEventually( entity, SoftwareProcess.SERVICE_STATE_ACTUAL, Lifecycle.STOPPED); EntityTestUtils.assertAttributeEqualsEventually( entity, SoftwareProcess.SERVICE_PROCESS_IS_RUNNING, false); EntityTestUtils.assertAttributeEventually( entity, ServiceStateLogic.SERVICE_NOT_UP_INDICATORS, CollectionFunctionals.<String>mapSizeEquals(1)); // Restart the process Entities.invokeEffector(app, entity, restartEffector, args).get(); EntityTestUtils.assertAttributeEqualsEventually(entity, SoftwareProcess.SERVICE_UP, true); EntityTestUtils.assertAttributeEqualsEventually( entity, SoftwareProcess.SERVICE_STATE_ACTUAL, Lifecycle.RUNNING); EntityTestUtils.assertAttributeEqualsEventually( entity, SoftwareProcess.SERVICE_PROCESS_IS_RUNNING, true); EntityTestUtils.assertAttributeEqualsEventually( entity, ServiceStateLogic.SERVICE_NOT_UP_INDICATORS, ImmutableMap.<String, Object>of()); EntityTestUtils.assertAttributeEqualsEventually(app, SoftwareProcess.SERVICE_UP, true); EntityTestUtils.assertAttributeEqualsEventually( app, SoftwareProcess.SERVICE_STATE_ACTUAL, Lifecycle.RUNNING); }
@Override public String getBroadcastAddress() { String sensorName = getConfig(BROADCAST_ADDRESS_SENSOR); if (Strings.isNonBlank(sensorName)) return Entities.submit( this, DependentConfiguration.attributeWhenReady(this, Sensors.newStringSensor(sensorName))) .getUnchecked(); String snitchName = getConfig(CassandraNode.ENDPOINT_SNITCH_NAME); if (snitchName.equals("Ec2MultiRegionSnitch") || snitchName.contains("MultiCloudSnitch")) { // http://www.datastax.com/documentation/cassandra/2.0/mobile/cassandra/architecture/architectureSnitchEC2MultiRegion_c.html // describes that the listen_address is set to the private IP, and the broadcast_address is // set to the public IP. return getAttribute(CassandraNode.ADDRESS); } else if (!getDriver().isClustered()) { return getListenAddress(); } else { // In other situations, prefer the hostname, so other regions can see it // *Unless* hostname resolves at the target to a local-only interface which is different to // ADDRESS // (workaround for issue deploying to localhost) String hostname = getAttribute(CassandraNode.HOSTNAME); try { String resolvedAddress = getDriver().getResolvedAddress(hostname); if (resolvedAddress == null) { log.debug( "Cassandra using broadcast address " + getListenAddress() + " for " + this + " because hostname " + hostname + " could not be resolved at remote machine"); return getListenAddress(); } if (resolvedAddress.equals("127.0.0.1")) { log.debug( "Cassandra using broadcast address " + getListenAddress() + " for " + this + " because hostname " + hostname + " resolves to 127.0.0.1"); return getListenAddress(); } return hostname; } catch (Exception e) { Exceptions.propagateIfFatal(e); log.warn("Error resolving hostname " + hostname + " for " + this + ": " + e, e); return hostname; } } }
@Override public void removeHost(DockerHost host) { SdnAgent agent = host.sensors().get(SdnAgent.SDN_AGENT); if (agent == null) { LOG.warn("{} cannot find calico service: {}", this, host); return; } agent.stop(); getAgents().removeMember(agent); Entities.unmanage(agent); if (LOG.isDebugEnabled()) LOG.debug("{} removed calico plugin {}", this, agent); }
@Override public String getListenAddress() { String sensorName = getConfig(LISTEN_ADDRESS_SENSOR); if (Strings.isNonBlank(sensorName)) return Entities.submit( this, DependentConfiguration.attributeWhenReady(this, Sensors.newStringSensor(sensorName))) .getUnchecked(); String subnetAddress = getAttribute(CassandraNode.SUBNET_ADDRESS); return Strings.isNonBlank(subnetAddress) ? subnetAddress : getAttribute(CassandraNode.ADDRESS); }
@Test public void testEntityCreatingItsEnricherDoesNotReCreateItUnlessUniqueTagDifferent() throws Exception { TestEntity e1 = origApp.createAndManageChild( EntitySpec.create(TestEntity.class, MyTestEntityWithEnricher.class)); Collection<Enricher> e1e = e1.getEnrichers(); log.info("enrichers1: " + e1e); Entities.dumpInfo(e1); assertEquals(e1e.size(), 5); newApp = (TestApplication) rebind(); Entity e2 = Iterables.getOnlyElement( Entities.descendants(newApp, EntityPredicates.idEqualTo(e1.getId()))); Collection<Enricher> e2e = e2.getEnrichers(); log.info("enrichers2: " + e2e); Entities.dumpInfo(e2); assertEquals(e2e.size(), e1e.size() + 1); }
/** Test that a node starts and sets SERVICE_UP correctly. */ @Test(groups = "Integration") public void canStartupAndShutdown() { solr = app.createAndManageChild(EntitySpec.create(SolrServer.class)); app.start(ImmutableList.of(testLocation)); EntityAsserts.assertAttributeEqualsEventually(solr, Startable.SERVICE_UP, true); Entities.dumpInfo(app); solr.stop(); EntityAsserts.assertAttributeEqualsEventually(solr, Startable.SERVICE_UP, false); }
@Override public void install() { Maybe<Object> url = ((EntityInternal) getEntity()).config().getRaw(SoftwareProcess.DOWNLOAD_URL); if (url.isPresentAndNonNull()) { DownloadResolver resolver = Entities.newDownloader(this); List<String> urls = resolver.getTargets(); downloadedFilename = resolver.getFilename(); List<String> commands = new LinkedList<String>(); commands.addAll(BashCommands.commandsToDownloadUrlsAs(urls, downloadedFilename)); commands.addAll(ArchiveUtils.installCommands(downloadedFilename)); int result = newScript(ImmutableMap.of(INSTALL_INCOMPLETE, true), INSTALLING) .failOnNonZeroResultCode(false) .body .append(commands) .execute(); if (result != 0) { // could not install at remote machine; try resolving URL here and copying across for (String urlI : urls) { result = ArchiveUtils.install( getMachine(), urlI, Urls.mergePaths(getInstallDir(), downloadedFilename)); if (result == 0) break; } if (result != 0) throw new IllegalStateException("Error installing archive: " + downloadedFilename); } } // If downloadUrl did partial install (see INSTALL_INCOMPLETE above) then always execute install // so mark it as completed. String installCommand = getEntity().getConfig(VanillaSoftwareProcess.INSTALL_COMMAND); if (url.isPresentAndNonNull() && Strings.isBlank(installCommand)) installCommand = "# mark as complete"; if (Strings.isNonBlank(installCommand)) { newScript(INSTALLING) .failOnNonZeroResultCode() .environmentVariablesReset(getShellEnvironment()) .body .append(installCommand) .execute(); } }
public static void main(String[] argv) { List<String> args = Lists.newArrayList(argv); String port = CommandLineUtil.getCommandLineOption(args, "--port", "8081+"); String locations = CommandLineUtil.getCommandLineOption( args, "--locations", Joiner.on(",").join(DEFAULT_LOCATIONS)); BrooklynLauncher launcher = BrooklynLauncher.newInstance() .application( EntitySpec.create(StartableApplication.class, GlobalWebFabricExample.class) .displayName("Brooklyn Global Web Fabric Example")) .webconsolePort(port) .locations(Arrays.asList(locations)) .start(); Entities.dumpInfo(launcher.getApplications()); }
@AfterMethod(alwaysRun = true) public void tearDown() { if (app != null) Entities.destroyAll(app.getManagementContext()); app = null; }
@AfterMethod(alwaysRun = true) public void tearDown() throws Exception { if (app != null) Entities.destroyAllCatching(app.getManagementContext()); }
protected void doStop(ConfigBag parameters, Callable<StopMachineDetails<Integer>> stopTask) { preStopConfirmCustom(); log.info("Stopping {} in {}", entity(), entity().getLocations()); StopMode stopMachineMode = getStopMachineMode(parameters); StopMode stopProcessMode = parameters.get(StopSoftwareParameters.STOP_PROCESS_MODE); DynamicTasks.queue("pre-stop", new PreStopCustomTask()); // BROOKLYN-263: // With this change the stop effector will wait for Location to provision so it can terminate // the machine, if a provisioning request is in-progress. // // The ProvisionMachineTask stores transient internal state in PROVISIONING_TASK_STATE and // PROVISIONED_MACHINE: it records when the provisioning is running and when done; and it // records the final machine. We record the machine in the internal sensor (rather than // just relying on getLocations) because the latter is set much later in the start() // process. // // This code is a big improvement (previously there was a several-minute window in some // clouds where a call to stop() would leave the machine running). // // However, there are still races. If the start() code has not yet reached the call to // location.obtain() then we won't wait, and the start() call won't know to abort. It's // fiddly to get that right, because we need to cope with restart() - so we mustn't leave // any state behind that will interfere with subsequent sequential calls to start(). // There is some attempt to handle it by ProvisionMachineTask checking if the expectedState // is stopping/stopped. Maybe<MachineLocation> machine = Machines.findUniqueMachineLocation(entity().getLocations()); ProvisioningTaskState provisioningState = entity().sensors().get(AttributesInternal.INTERNAL_PROVISIONING_TASK_STATE); if (machine.isAbsent() && provisioningState == ProvisioningTaskState.RUNNING) { Duration maxWait = entity().config().get(STOP_WAIT_PROVISIONING_TIMEOUT); log.info( "When stopping {}, waiting for up to {} for the machine to finish provisioning, before terminating it", entity(), maxWait); boolean success = Repeater.create("Wait for a machine to appear") .until( new Callable<Boolean>() { @Override public Boolean call() throws Exception { ProvisioningTaskState state = entity() .sensors() .get(AttributesInternal.INTERNAL_PROVISIONING_TASK_STATE); return (state != ProvisioningTaskState.RUNNING); } }) .backoffTo(Duration.FIVE_SECONDS) .limitTimeTo(maxWait) .run(); if (!success) { log.warn( "When stopping {}, timed out after {} waiting for the machine to finish provisioning - machine may we left running", entity(), maxWait); } machine = Maybe.ofDisallowingNull(entity().sensors().get(INTERNAL_PROVISIONED_MACHINE)); } entity().sensors().remove(AttributesInternal.INTERNAL_PROVISIONING_TASK_STATE); entity().sensors().remove(INTERNAL_PROVISIONED_MACHINE); Task<List<?>> stoppingProcess = null; if (canStop(stopProcessMode, entity())) { stoppingProcess = Tasks.parallel( "stopping", Tasks.create("stopping (process)", new StopProcessesAtMachineTask()), Tasks.create("stopping (feeds)", new StopFeedsAtMachineTask())); DynamicTasks.queue(stoppingProcess); } Task<StopMachineDetails<Integer>> stoppingMachine = null; if (canStop(stopMachineMode, machine.isAbsent())) { // Release this machine (even if error trying to stop process - we rethrow that after) Map<String, Object> stopMachineFlags = MutableMap.of(); if (Entitlements.getEntitlementContext() != null) { stopMachineFlags.put( "tags", MutableSet.of( BrooklynTaskTags.tagForEntitlement(Entitlements.getEntitlementContext()))); } Task<StopMachineDetails<Integer>> stopMachineTask = Tasks.<StopMachineDetails<Integer>>builder() .displayName("stopping (machine)") .body(stopTask) .flags(stopMachineFlags) .build(); stoppingMachine = DynamicTasks.queue(stopMachineTask); DynamicTasks.drain(entity().getConfig(STOP_PROCESS_TIMEOUT), false); // shutdown the machine if stopping process fails or takes too long synchronized (stoppingMachine) { // task also used as mutex by DST when it submits it; ensure it only submits once! if (!stoppingMachine.isSubmitted()) { // force the stoppingMachine task to run by submitting it here StringBuilder msg = new StringBuilder("Submitting machine stop early in background for ") .append(entity()); if (stoppingProcess == null) { msg.append(". Process stop skipped, pre-stop not finished?"); } else { msg.append(" because process stop has ") .append((stoppingProcess.isDone() ? "finished abnormally" : "not finished")); } log.warn(msg.toString()); Entities.submit(entity(), stoppingMachine); } } } try { // This maintains previous behaviour of silently squashing any errors on the stoppingProcess // task if the // stoppingMachine exits with a nonzero value boolean checkStopProcesses = (stoppingProcess != null && (stoppingMachine == null || stoppingMachine.get().value == 0)); if (checkStopProcesses) { // TODO we should test for destruction above, not merely successful "stop", as things like // localhost and ssh won't be destroyed DynamicTasks.waitForLast(); if (machine.isPresent()) { // throw early errors *only if* there is a machine and we have not destroyed it stoppingProcess.get(); } } } catch (Throwable e) { ServiceStateLogic.setExpectedState(entity(), Lifecycle.ON_FIRE); Exceptions.propagate(e); } entity().sensors().set(SoftwareProcess.SERVICE_UP, false); ServiceStateLogic.setExpectedState(entity(), Lifecycle.STOPPED); DynamicTasks.queue("post-stop", new PostStopCustomTask()); if (log.isDebugEnabled()) log.debug("Stopped software process entity " + entity()); }
@AfterMethod(alwaysRun = true) public void tearDown() throws Exception { if (managementContext != null) Entities.destroyAll(managementContext); }
@Override public DockerContainerLocation obtain(Map<?, ?> flags) throws NoMachinesAvailableException { lock.readLock().lock(); try { // Lookup entity from context or flags Object context = flags.get(LocationConfigKeys.CALLER_CONTEXT.getName()); if (context == null || !(context instanceof Entity)) { throw new IllegalStateException("Invalid location context: " + context); } Entity entity = (Entity) context; // Flag to configure adding SSHable layer boolean useSsh = entity.config().get(DockerContainer.DOCKER_USE_SSH) && dockerHost.config().get(DockerContainer.DOCKER_USE_SSH); // Configure the entity LOG.info("Configuring entity {} via subnet {}", entity, dockerHost.getSubnetTier()); entity .config() .set( SubnetTier.PORT_FORWARDING_MANAGER, dockerHost.getSubnetTier().getPortForwardManager()); entity.config().set(SubnetTier.PORT_FORWARDER, portForwarder); if (getOwner().config().get(SdnAttributes.SDN_ENABLE)) { SdnAgent agent = getOwner().sensors().get(SdnAgent.SDN_AGENT); if (agent == null) { throw new IllegalStateException("SDN agent entity on " + getOwner() + " is null"); } Map<String, Cidr> networks = agent.sensors().get(SdnAgent.SDN_PROVIDER).sensors().get(SdnProvider.SUBNETS); entity.config().set(SubnetTier.SUBNET_CIDR, networks.get(entity.getApplicationId())); } else { entity.config().set(SubnetTier.SUBNET_CIDR, Cidr.UNIVERSAL); } configureEnrichers(entity); // Add the entity Dockerfile if configured String dockerfile = entity.config().get(DockerAttributes.DOCKERFILE_URL); String entrypoint = entity.config().get(DockerAttributes.DOCKERFILE_ENTRYPOINT_URL); String contextArchive = entity.config().get(DockerAttributes.DOCKERFILE_CONTEXT_URL); String imageId = entity.config().get(DockerAttributes.DOCKER_IMAGE_ID); Optional<String> baseImage = Optional.fromNullable(entity.config().get(DockerAttributes.DOCKER_IMAGE_NAME)); String imageTag = Optional.fromNullable(entity.config().get(DockerAttributes.DOCKER_IMAGE_TAG)) .or("latest"); // TODO incorporate more info final String imageName = DockerUtils.imageName(entity, dockerfile); // Lookup image ID or build new image from Dockerfile LOG.info("ImageName for entity {}: {}", entity, imageName); if (dockerHost.getImageNamed(imageName, imageTag).isPresent()) { // Wait until committed before continuing - Brooklyn may be midway through its creation. waitForImage(imageName); // Look up imageId again imageId = dockerHost.getImageNamed(imageName, imageTag).get(); LOG.info("Found image {} for entity: {}", imageName, imageId); // Skip install phase entity.config().set(SoftwareProcess.SKIP_INSTALLATION, true); } else if (baseImage.isPresent()) { if (useSsh) { // Create an SSHable image from the one configured imageId = dockerHost.layerSshableImageOn(baseImage.get(), imageTag); LOG.info("Created SSHable image from {}: {}", baseImage.get(), imageId); } else { dockerHost.runDockerCommand(String.format("pull %s:%s", baseImage.get(), imageTag)); imageId = dockerHost.getImageNamed(baseImage.get(), imageTag).get(); } entity.config().set(SoftwareProcess.SKIP_INSTALLATION, true); } else { // Otherwise Clocker is going to make an image for the entity once it is installed. insertCallback(entity, SoftwareProcess.POST_INSTALL_COMMAND, DockerCallbacks.commit()); if (Strings.isNonBlank(dockerfile)) { if (imageId != null) { LOG.warn( "Ignoring container imageId {} as dockerfile URL is set: {}", imageId, dockerfile); } Map<String, Object> substitutions = getExtraTemplateSubstitutions(imageName, entity); imageId = dockerHost.buildImage( dockerfile, entrypoint, contextArchive, imageName, useSsh, substitutions); } if (Strings.isBlank(imageId)) { imageId = getOwner().sensors().get(DockerHost.DOCKER_IMAGE_ID); } // Tag the image name and create its latch images.putIfAbsent(imageName, new CountDownLatch(1)); dockerHost.runDockerCommand(String.format("tag -f %s %s:latest", imageId, imageName)); } // Look up hardware ID String hardwareId = entity.config().get(DockerAttributes.DOCKER_HARDWARE_ID); if (Strings.isEmpty(hardwareId)) { hardwareId = getOwner().config().get(DockerAttributes.DOCKER_HARDWARE_ID); } // Create new Docker container in the host cluster LOG.info( "Starting container with imageId {} and hardwareId {} at {}", new Object[] {imageId, hardwareId, machine}); Map<Object, Object> containerFlags = MutableMap.builder() .putAll(flags) .put("useSsh", useSsh) .put("entity", entity) .putIfNotNull("imageId", imageId) .putIfNotNull("hardwareId", hardwareId) .build(); DynamicCluster cluster = dockerHost.getDockerContainerCluster(); Entity added = cluster.addNode(machine, containerFlags); if (added == null) { throw new NoMachinesAvailableException( String.format("Failed to create container at %s", dockerHost.getDockerHostName())); } else { Entities.invokeEffector( (EntityLocal) entity, added, Startable.START, MutableMap.of("locations", ImmutableList.of(machine))) .getUnchecked(); } DockerContainer dockerContainer = (DockerContainer) added; // Save the container attributes dockerContainer.sensors().set(DockerContainer.IMAGE_ID, imageId); dockerContainer.sensors().set(DockerContainer.IMAGE_NAME, imageName); dockerContainer.sensors().set(DockerContainer.HARDWARE_ID, hardwareId); // record SDN application network details if (getOwner().config().get(SdnAttributes.SDN_ENABLE)) { SdnAgent agent = getOwner().sensors().get(SdnAgent.SDN_AGENT); Cidr applicationCidr = agent.sensors().get(SdnAgent.SDN_PROVIDER).getSubnetCidr(entity.getApplicationId()); entity.sensors().set(SdnProvider.APPLICATION_CIDR, applicationCidr); dockerContainer.sensors().set(SdnProvider.APPLICATION_CIDR, applicationCidr); } return dockerContainer.getDynamicLocation(); } finally { lock.readLock().unlock(); } }
public List<String> scanSlaves(JsonArray slaves) throws UnknownHostException { List<String> slaveIds = MutableList.<String>of(); for (int i = 0; i < slaves.size(); i++) { JsonObject slave = slaves.get(i).getAsJsonObject(); boolean active = slave.get("active").getAsBoolean(); String id = slave.get("id").getAsString(); String hostname = slave.get("hostname").getAsString(); Double registered = slave.get("registered_time").getAsDouble(); Group group = sensors().get(MESOS_SLAVES); Optional<Entity> entity = Iterables.tryFind( group.getMembers(), Predicates.compose( Predicates.equalTo(id), EntityFunctions.attribute(MesosSlave.MESOS_SLAVE_ID))); if (entity.isPresent()) { Entity found = entity.get(); found.sensors().set(MesosSlave.SLAVE_ACTIVE, active); if (!active) { Lifecycle state = found.sensors().get(Attributes.SERVICE_STATE_ACTUAL); if (Lifecycle.ON_FIRE.equals(state) || Lifecycle.STARTING.equals(state)) { continue; } else if (Lifecycle.STOPPING.equals(state) || Lifecycle.STOPPED.equals(state)) { group.removeMember(found); group.removeChild(found); Entities.unmanage(found); } else { ServiceStateLogic.setExpectedState(found, Lifecycle.STOPPING); } } } else if (active) { LocationSpec<SshMachineLocation> spec = LocationSpec.create(SshMachineLocation.class) .configure(SshMachineLocation.SSH_HOST, hostname) .configure("address", InetAddress.getByName(hostname)) .displayName(hostname); if (config().get(MESOS_SLAVE_ACCESSIBLE)) { spec.configure(CloudLocationConfig.WAIT_FOR_SSHABLE, "true") .configure(SshMachineLocation.DETECT_MACHINE_DETAILS, true) .configure(SshMachineLocation.SSH_PORT, config().get(MesosSlave.SLAVE_SSH_PORT)) .configure(LocationConfigKeys.USER, config().get(MesosSlave.SLAVE_SSH_USER)) .configure(LocationConfigKeys.PASSWORD, config().get(MesosSlave.SLAVE_SSH_PASSWORD)) .configure(SshTool.PROP_PASSWORD, config().get(MesosSlave.SLAVE_SSH_PASSWORD)) .configure(SshTool.PROP_PORT, config().get(MesosSlave.SLAVE_SSH_PORT)) .configure( LocationConfigKeys.PRIVATE_KEY_DATA, config().get(MesosSlave.SLAVE_SSH_PRIVATE_KEY_DATA)) .configure( LocationConfigKeys.PRIVATE_KEY_FILE, config().get(MesosSlave.SLAVE_SSH_PRIVATE_KEY_FILE)); } else { spec.configure(CloudLocationConfig.WAIT_FOR_SSHABLE, "false") .configure(SshMachineLocation.DETECT_MACHINE_DETAILS, false); } SshMachineLocation machine = getManagementContext().getLocationManager().createLocation(spec); // Setup port forwarding MarathonPortForwarder portForwarder = new MarathonPortForwarder(); portForwarder.setManagementContext(getManagementContext()); EntitySpec<MesosSlave> slaveSpec = EntitySpec.create(MesosSlave.class) .configure(MesosSlave.MESOS_SLAVE_ID, id) .configure(MesosSlave.REGISTERED_AT, registered.longValue()) .configure(MesosSlave.MESOS_CLUSTER, this) .displayName("Mesos Slave (" + hostname + ")"); MesosSlave added = sensors().get(MESOS_SLAVES).addMemberChild(slaveSpec); added.sensors().set(MesosSlave.SLAVE_ACTIVE, active); added.sensors().set(MesosSlave.HOSTNAME, hostname); added.sensors().set(MesosSlave.ADDRESS, hostname); added.start(ImmutableList.of(machine)); portForwarder.init(hostname, this); // Setup subnet tier SubnetTier subnetTier = added.addChild( EntitySpec.create(SubnetTier.class) .configure(SubnetTier.PORT_FORWARDER, portForwarder) .configure(SubnetTier.SUBNET_CIDR, Cidr.UNIVERSAL)); Entities.start(subnetTier, ImmutableList.of(machine)); added.sensors().set(MesosSlave.SUBNET_TIER, subnetTier); } if (active) slaveIds.add(id); } return slaveIds; }
@Override public void waitForEntityStart() { Entities.waitForServiceUp(this); }
@SuppressWarnings({"rawtypes", "unchecked"}) @Override public V call() { T value = source.getAttribute(sensor); // return immediately if either the ready predicate or the abort conditions hold if (ready(value)) return postProcess(value); final List<Exception> abortionExceptions = Lists.newCopyOnWriteArrayList(); long start = System.currentTimeMillis(); for (AttributeAndSensorCondition abortCondition : abortSensorConditions) { Object abortValue = abortCondition.source.getAttribute(abortCondition.sensor); if (abortCondition.predicate.apply(abortValue)) { abortionExceptions.add( new Exception( "Abort due to " + abortCondition.source + " -> " + abortCondition.sensor)); } } if (abortionExceptions.size() > 0) { throw new CompoundRuntimeException( "Aborted waiting for ready from " + source + " " + sensor, abortionExceptions); } TaskInternal<?> current = (TaskInternal<?>) Tasks.current(); if (current == null) throw new IllegalStateException("Should only be invoked in a running task"); Entity entity = BrooklynTaskTags.getTargetOrContextEntity(current); if (entity == null) throw new IllegalStateException( "Should only be invoked in a running task with an entity tag; " + current + " has no entity tag (" + current.getStatusDetail(false) + ")"); final LinkedList<T> publishedValues = new LinkedList<T>(); final Semaphore semaphore = new Semaphore(0); // could use Exchanger SubscriptionHandle subscription = null; List<SubscriptionHandle> abortSubscriptions = Lists.newArrayList(); try { subscription = entity .subscriptions() .subscribe( source, sensor, new SensorEventListener<T>() { @Override public void onEvent(SensorEvent<T> event) { synchronized (publishedValues) { publishedValues.add(event.getValue()); } semaphore.release(); } }); for (final AttributeAndSensorCondition abortCondition : abortSensorConditions) { abortSubscriptions.add( entity .subscriptions() .subscribe( abortCondition.source, abortCondition.sensor, new SensorEventListener<Object>() { @Override public void onEvent(SensorEvent<Object> event) { if (abortCondition.predicate.apply(event.getValue())) { abortionExceptions.add( new Exception( "Abort due to " + abortCondition.source + " -> " + abortCondition.sensor)); semaphore.release(); } } })); Object abortValue = abortCondition.source.getAttribute(abortCondition.sensor); if (abortCondition.predicate.apply(abortValue)) { abortionExceptions.add( new Exception( "Abort due to " + abortCondition.source + " -> " + abortCondition.sensor)); } } if (abortionExceptions.size() > 0) { throw new CompoundRuntimeException( "Aborted waiting for ready from " + source + " " + sensor, abortionExceptions); } CountdownTimer timer = timeout != null ? timeout.countdownTimer() : null; Duration maxPeriod = ValueResolver.PRETTY_QUICK_WAIT; Duration nextPeriod = ValueResolver.REAL_QUICK_PERIOD; while (true) { // check the source on initial run (could be done outside the loop) // and also (optionally) on each iteration in case it is more recent value = source.getAttribute(sensor); if (ready(value)) break; if (timer != null) { if (timer.getDurationRemaining().isShorterThan(nextPeriod)) { nextPeriod = timer.getDurationRemaining(); } if (timer.isExpired()) { if (onTimeout.isPresent()) return onTimeout.get(); throw new RuntimeTimeoutException("Unsatisfied after " + Duration.sinceUtc(start)); } } String prevBlockingDetails = current.setBlockingDetails(blockingDetails); try { if (semaphore.tryAcquire(nextPeriod.toMilliseconds(), TimeUnit.MILLISECONDS)) { // immediately release so we are available for the next check semaphore.release(); // if other permits have been made available (e.g. multiple notifications) drain them // all as no point running multiple times semaphore.drainPermits(); } } finally { current.setBlockingDetails(prevBlockingDetails); } // check any subscribed values which have come in first while (true) { synchronized (publishedValues) { if (publishedValues.isEmpty()) break; value = publishedValues.pop(); } if (ready(value)) break; } // if unmanaged then ignore the other abort conditions if (!ignoreUnmanaged && Entities.isNoLongerManaged(entity)) { if (onUnmanaged.isPresent()) return onUnmanaged.get(); throw new NotManagedException(entity); } if (abortionExceptions.size() > 0) { throw new CompoundRuntimeException( "Aborted waiting for ready from " + source + " " + sensor, abortionExceptions); } nextPeriod = nextPeriod.times(2).upperBound(maxPeriod); } if (LOG.isDebugEnabled()) LOG.debug("Attribute-ready for {} in entity {}", sensor, source); return postProcess(value); } catch (InterruptedException e) { throw Exceptions.propagate(e); } finally { if (subscription != null) { entity.subscriptions().unsubscribe(subscription); } for (SubscriptionHandle handle : abortSubscriptions) { entity.subscriptions().unsubscribe(handle); } } }