@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)));
 }
Ejemplo n.º 2
0
 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();
    }
  }
Ejemplo n.º 4
0
 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);
    }
  }