@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))); }
public static final Optional<String> httpPost( Entity framework, String subUrl, String dataUrl, Map<String, Object> substitutions) { String targetUrl = Urls.mergePaths(framework.sensors().get(MesosFramework.FRAMEWORK_URL), subUrl); String templateContents = ResourceUtils.create().getResourceAsString(dataUrl); String processedJson = TemplateProcessor.processTemplateContents(templateContents, substitutions); LOG.debug("Posting JSON to {}: {}", targetUrl, processedJson); URI postUri = URI.create(targetUrl); HttpToolResponse response = HttpTool.httpPost( MesosUtils.buildClient(framework), postUri, MutableMap.of( HttpHeaders.CONTENT_TYPE, "application/json", HttpHeaders.ACCEPT, "application/json"), processedJson.getBytes()); LOG.debug("Response: " + response.getContentAsString()); if (!HttpTool.isStatusCodeHealthy(response.getResponseCode())) { LOG.warn( "Invalid response code {}: {}", response.getResponseCode(), response.getReasonPhrase()); return Optional.absent(); } else { LOG.debug("Successfull call to {}: {}", targetUrl); return Optional.of(response.getContentAsString()); } }
@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 final Optional<String> httpDelete(Entity framework, String subUrl) { String targetUrl = Urls.mergePaths(framework.sensors().get(MesosFramework.FRAMEWORK_URL), subUrl); LOG.debug("Deleting {}", targetUrl); URI deleteUri = URI.create(targetUrl); HttpToolResponse response = HttpTool.httpDelete( buildClient(framework), deleteUri, MutableMap.of(HttpHeaders.ACCEPT, "application/json")); LOG.debug("Response: " + response.getContentAsString()); if (!HttpTool.isStatusCodeHealthy(response.getResponseCode())) { LOG.warn( "Invalid response code {}: {}", response.getResponseCode(), response.getReasonPhrase()); return Optional.absent(); } else { LOG.debug("Successfull call to {}: {}", targetUrl); return Optional.of(response.getContentAsString()); } }
@SuppressWarnings({"unchecked", "deprecation"}) protected void startWithChefSoloAsync() { String baseDir = MachineLifecycleEffectorTasks.resolveOnBoxDir( entity(), Machines.findUniqueMachineLocation(entity().getLocations(), SshMachineLocation.class) .get()); String installDir = Urls.mergePaths(baseDir, "installs/chef"); @SuppressWarnings("rawtypes") Map<String, String> cookbooks = (Map) ConfigBag.newInstance(entity().getConfig(CHEF_COOKBOOK_URLS)) .putIfAbsent(entity().getConfig(CHEF_COOKBOOKS)) .getAllConfig(); if (cookbooks.isEmpty()) log.warn("No cookbook_urls set for " + entity() + "; launch will likely fail subsequently"); DynamicTasks.queue( ChefSoloTasks.installChef(installDir, false), ChefSoloTasks.installCookbooks(installDir, cookbooks, false)); // TODO chef for and run a prestart recipe if necessary // TODO open ports String primary = getPrimaryCookbook(); // put all config under brooklyn/cookbook/config Navigator<MutableMap<Object, Object>> attrs = Jsonya.newInstancePrimitive().at("brooklyn"); if (Strings.isNonBlank(primary)) attrs.at(primary); attrs.at("config"); attrs.put(entity().getAllConfigBag().getAllConfig()); // and put launch attrs at root try { attrs .root() .put( (Map<?, ?>) Tasks.resolveDeepValue( entity().getConfig(CHEF_LAUNCH_ATTRIBUTES), Object.class, entity().getExecutionContext())); } catch (Exception e) { Exceptions.propagate(e); } Collection<? extends String> runList = entity().getConfig(CHEF_LAUNCH_RUN_LIST); if (runList == null) runList = entity().getConfig(CHEF_RUN_LIST); if (runList == null) { if (Strings.isNonBlank(primary)) runList = ImmutableList.of(primary + "::" + "start"); else throw new IllegalStateException( "Require a primary cookbook or a run_list to effect " + "start" + " on " + entity()); } String runDir = Urls.mergePaths( baseDir, "apps/" + entity().getApplicationId() + "/chef/entities/" + entity().getEntityType().getSimpleName() + "_" + entity().getId()); DynamicTasks.queue( ChefSoloTasks.buildChefFile( runDir, installDir, "launch", runList, (Map<String, Object>) attrs.root().get())); DynamicTasks.queue( ChefSoloTasks.runChef(runDir, "launch", entity().getConfig(CHEF_RUN_CONVERGE_TWICE))); }
public String getJmxSslTrustStoreFilePath() { return Urls.mergePaths(jmxSupport.getRunDir(), "jmx-truststore"); }
public String getJmxSslKeyStoreFilePath() { return Urls.mergePaths(jmxSupport.getRunDir(), "jmx-keystore"); }
@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/dropins/") .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 (Object entry : getEntity().getClasspath()) { String filename = null; String url = null; if (entry instanceof String) { url = (String) entry; } else { if (entry instanceof Map) { url = (String) ((Map) entry).get("url"); filename = (String) ((Map) entry).get("filename"); } } checkNotNull(url, "url"); // If a local folder, then create archive from contents first if (Urls.isDirectory(url)) { File jarFile = ArchiveBuilder.jar().addDirContentsAt(new File(url), "").create(); url = jarFile.getAbsolutePath(); } if (filename == null) { // Determine filename filename = getFilename(url); } ArchiveUtils.deploy( MutableMap.<String, Object>of(), url, machine, getRunDir(), Os.mergePaths(getRunDir(), "lib", "dropins"), filename); } 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()); } }
/* * TODO this is much messier than we would like because postgres runs as user postgres, * meaning the dirs must be RW by that user, and accessible (thus all parent paths), * which may rule out putting it in a location used by the default user. * Two irritating things: * * currently we sometimes make up a different onbox base dir; * * currently we put files to /tmp for staging * Could investigate if it really needs to run as user postgres; * could also see whether default user can be added to group postgres, * and the run dir (and all parents) made accessible to group postgres. */ @Override public void install() { String version = getEntity().getConfig(SoftwareProcess.SUGGESTED_VERSION); String majorMinorVersion = version.substring(0, version.lastIndexOf("-")); String shortVersion = majorMinorVersion.replace(".", ""); String altTarget = "/opt/brooklyn/postgres/"; String altInstallDir = Urls.mergePaths(altTarget, "install/" + majorMinorVersion); Iterable<String> pgctlLocations = ImmutableList.of( altInstallDir + "/bin", "/usr/lib/postgresql/" + majorMinorVersion + "/bin/", "/opt/local/lib/postgresql" + shortVersion + "/bin/", "/usr/pgsql-" + majorMinorVersion + "/bin", "/usr/local/bin/", "/usr/bin/", "/bin/"); DynamicTasks.queueIfPossible( SshTasks.dontRequireTtyForSudo( getMachine(), // sudo is absolutely required here, in customize we set user to postgres OnFailingTask.FAIL)) .orSubmitAndBlock(); DynamicTasks.waitForLast(); // Check whether we can find a usable pg_ctl, and if not install one MutableList<String> findOrInstall = MutableList.<String>of() .append("which pg_ctl") .appendAll( Iterables.transform(pgctlLocations, StringFunctions.formatter("test -x %s/pg_ctl"))) .append( installPackage( ImmutableMap.of( "yum", "postgresql" + shortVersion + " postgresql" + shortVersion + "-server", "apt", "postgresql-" + majorMinorVersion, "port", "postgresql" + shortVersion + " postgresql" + shortVersion + "-server"), null)) // due to impl of installPackage, it will not come to the line below I don't think .append( warn( format( "WARNING: failed to find or install postgresql %s binaries", majorMinorVersion))); // Link to correct binaries folder (different versions of pg_ctl and psql don't always play well // together) MutableList<String> linkFromHere = MutableList.<String>of() .append( ifExecutableElse1( "pg_ctl", chainGroup( "PG_EXECUTABLE=`which pg_ctl`", "PG_DIR=`dirname $PG_EXECUTABLE`", "echo 'found pg_ctl in '$PG_DIR' on path so linking PG bin/ to that dir'", "ln -s $PG_DIR bin"))) .appendAll( Iterables.transform( pgctlLocations, givenDirIfFileExistsInItLinkToDir("pg_ctl", "bin"))) .append( fail( format( "WARNING: failed to find postgresql %s binaries for pg_ctl, may already have another version installed; aborting", majorMinorVersion), 9)); newScript(INSTALLING) .body .append( dontRequireTtyForSudo(), ifExecutableElse0("yum", getYumRepository(version, majorMinorVersion, shortVersion)), ifExecutableElse0("apt-get", getAptRepository()), "rm -f bin", // if left over from previous incomplete/failed install (not sure why that // keeps happening!) alternativesGroup(findOrInstall), alternativesGroup(linkFromHere)) .failOnNonZeroResultCode() .queue(); // check that the proposed install dir is one that user postgres can access // TODO if command above fails then the following `getUnchecked` blocks forever if (DynamicTasks.queue( SshEffectorTasks.ssh(sudoAsUser("postgres", "ls " + getInstallDir())) .allowingNonZeroExitCode() .summary("check postgres user can access install dir")) .asTask() .getUnchecked() != 0) { log.info( "Postgres install dir " + getInstallDir() + " for " + getEntity() + " is not accessible to user 'postgres'; " + "using " + altInstallDir + " instead"); String newRunDir = Urls.mergePaths( altTarget, "apps", getEntity().getApplication().getId(), getEntity().getId()); if (DynamicTasks.queue( SshEffectorTasks.ssh("ls " + altInstallDir + "/pg_ctl") .allowingNonZeroExitCode() .summary("check whether " + altInstallDir + " is set up")) .asTask() .getUnchecked() == 0) { // alt target already exists with binary; nothing to do for install } else { DynamicTasks.queue( SshEffectorTasks.ssh( "mkdir -p " + altInstallDir, "rm -rf '" + altInstallDir + "'", "mv " + getInstallDir() + " " + altInstallDir, "rm -rf '" + getInstallDir() + "'", "ln -s " + altInstallDir + " " + getInstallDir(), "mkdir -p " + newRunDir, "chown -R postgres:postgres " + altTarget) .runAsRoot() .requiringExitCodeZero() .summary("move install dir from user to postgres owned space")); } DynamicTasks.waitForLast(); setInstallDir(altInstallDir); setRunDir(newRunDir); } }