/**
   * deploys a bundle
   *
   * @param input bundle distribution ZIP file
   * @param group to be deployed to (a BundleDestination is created on top of given group), group
   *     must be compatible and it's resources must support bundle deployment
   * @param config input configuration for bundle (for passing input-parameter values)
   * @param destinationName - name for new destination being created
   * @param baseDirName - baseDir for deployment - this must match to resourceType contained in
   *     given group
   * @param deployDir - directory to deploy to - relative path based on baseDir
   * @return bundleDeployment where deployment has finished (either failed or success)
   * @throws Exception
   */
  public BundleDeployment deployBundle(
      File input,
      ResourceGroup group,
      Configuration config,
      String destinationName,
      String baseDirName,
      String deployDir)
      throws Exception {
    BundleVersion version = createBundleVersion(input);
    BundleDestination destination =
        bundleManager.createBundleDestination(
            client.getSubject(),
            version.getBundle().getId(),
            destinationName,
            "",
            baseDirName,
            deployDir,
            group.getId());

    BundleDeployment deployment =
        bundleManager.createBundleDeployment(
            client.getSubject(), version.getId(), destination.getId(), "", config);
    deployment =
        bundleManager.scheduleBundleDeployment(client.getSubject(), deployment.getId(), false);
    return waitForBundleDeployment(deployment);
  }
 /**
  * waits until given BundleDeployment is not in PENDING or IN_PROGRESS state, then returns
  * BundleDeployment instance, which is going to be either SUCCESS or FAILURE
  *
  * @param deployment
  * @return
  */
 private BundleDeployment waitForBundleDeployment(BundleDeployment deployment) {
   while (deployment.getStatus().equals(BundleDeploymentStatus.IN_PROGRESS)
       || deployment.getStatus().equals(BundleDeploymentStatus.PENDING)) {
     try {
       Thread.currentThread().join(3 * 1000);
     } catch (InterruptedException e) {
       e.printStackTrace();
     }
     BundleDeploymentCriteria criteria = new BundleDeploymentCriteria();
     criteria.addFilterId(deployment.getId());
     deployment =
         bundleManager.findBundleDeploymentsByCriteria(client.getSubject(), criteria).get(0);
   }
   return deployment;
 }
  @Override
  public void execute(JobExecutionContext context) throws JobExecutionException {
    BundleManagerLocal bundleManager = LookupUtil.getBundleManager();
    SubjectManagerLocal subjectManager = LookupUtil.getSubjectManager();

    Subject overlord = subjectManager.getOverlord();

    PageList<BundleDeployment> deployments =
        bundleManager.findBundleDeploymentsByCriteria(overlord, getCriteriaFromContext(context));

    if (deployments.size() > 0) {
      BundleDeployment bundleDeployment = deployments.get(0);
      SchedulerLocal scheduler = LookupUtil.getSchedulerBean();
      JobDetail jobDetail = context.getJobDetail();

      BundleDeploymentStatus bundleDeploymentStatus =
          bundleManager.determineBundleDeploymentStatus(bundleDeployment.getId());
      if (bundleDeploymentStatus.isTerminal()) {
        // delete this job, we've assigned a final status
        try {
          context.setResult(bundleDeploymentStatus); // Return status to possible listeners
          scheduler.deleteJob(jobDetail.getName(), jobDetail.getGroup());
        } catch (SchedulerException e) {
          throw new JobExecutionException(
              "Could not delete the bundle deployment completion check job for "
                  + bundleDeployment
                  + ".",
              e);
        }
      } else {
        // try again in 10s
        try {
          Trigger trigger = QuartzUtil.getFireOnceOffsetTrigger(jobDetail, 10000L);
          // just need a trigger name unique for this job
          trigger.setName(String.valueOf(System.currentTimeMillis()));
          scheduler.scheduleJob(trigger);
        } catch (SchedulerException e) {
          throw new JobExecutionException(
              "Could not schedule the bundle deployment completion check job for "
                  + bundleDeployment
                  + ".",
              e);
        }
      }
    }
  }
  public ControlResults invoke(String operation, Configuration params) {
    ControlResults results = new ControlResults();

    try {
      Bundle bundle = getBundle();
      BundleVersion bundleVersion = getBundleVersion(bundle);

      Configuration bundleConfig = new Configuration();
      File clusterDir = new File(params.getSimpleValue("clusterDirectory"));
      int numNodes = Integer.parseInt(params.getSimpleValue("numberOfNodes"));
      int replicationFactor = Integer.parseInt(params.getSimpleValue("replicationFactor", "1"));
      String hostname = params.getSimpleValue("host");

      Resource platform = getPlatform(hostname);
      ResourceGroup group = getPlatformGroup(platform, hostname);

      Set<String> ipAddresses = calculateLocalIPAddresses(numNodes);

      for (int i = 0; i < numNodes; ++i) {
        Set<String> seeds = getSeeds(ipAddresses, i + 1);
        int jmxPort = 7200 + i;

        Configuration deploymentConfig = new Configuration();
        deploymentConfig.put(new PropertySimple("cluster.name", "rhqdev"));
        deploymentConfig.put(new PropertySimple("cluster.dir", clusterDir.getAbsolutePath()));
        deploymentConfig.put(new PropertySimple("auto.bootstrap", "false"));
        deploymentConfig.put(new PropertySimple("data.dir", "data"));
        deploymentConfig.put(new PropertySimple("commitlog.dir", "commit_log"));
        deploymentConfig.put(new PropertySimple("log.dir", "logs"));
        deploymentConfig.put(new PropertySimple("saved.caches.dir", "saved_caches"));
        deploymentConfig.put(new PropertySimple("hostname", getLocalIPAddress(i + 1)));
        deploymentConfig.put(new PropertySimple("seeds", collectionToString(seeds)));
        deploymentConfig.put(new PropertySimple("jmx.port", Integer.toString(jmxPort)));
        deploymentConfig.put(new PropertySimple("initial.token", generateToken(i, numNodes)));
        deploymentConfig.put(new PropertySimple("install.schema", i == 0));
        deploymentConfig.put(new PropertySimple("replication.factor", replicationFactor));

        String destinationName = "cassandra-node[" + i + "]-deployment";
        String deployDir = new File(clusterDir, "node" + i).getAbsolutePath();

        BundleDestination bundleDestination =
            getBundleDestination(bundleVersion, destinationName, group, deployDir);

        BundleDeployment bundleDeployment =
            bundleManager.createBundleDeployment(
                overlord,
                bundleVersion.getId(),
                bundleDestination.getId(),
                destinationName,
                deploymentConfig);

        bundleManager.scheduleBundleDeployment(overlord, bundleDeployment.getId(), true);
      }

      return new ControlResults();
    } catch (ResourceNotFoundException e) {
      results.setError(e.getMessage());
      return results;

    } catch (Exception e) {
      results.setError(e);
      return results;
    }
  }
  private void upgrade(boolean clean) throws Exception {
    testAntBundleInitialInstall(); // install a bundle first
    cleanPluginDirs(); // clean everything but the dest dir - we want to upgrade the destination
    prepareBeforeTestMethod(); // prepare for our new test

    // deploy upgrade and test it
    ResourceType resourceType =
        new ResourceType("testSimpleBundle2Type", "plugin", ResourceCategory.SERVER, null);
    BundleType bundleType = new BundleType("testSimpleBundle2BType", resourceType);
    Repo repo = new Repo("test-bundle-two");
    PackageType packageType = new PackageType("test-bundle-two", resourceType);
    Bundle bundle = new Bundle("test-bundle-two", bundleType, repo, packageType);
    BundleVersion bundleVersion =
        new BundleVersion(
            "test-bundle-two", "3.0", bundle, getRecipeFromFile("test-bundle-three.xml"));
    BundleDestination destination =
        new BundleDestination(
            bundle,
            "testSimpleBundle2Dest",
            new ResourceGroup("testSimpleBundle2Group"),
            DEST_BASE_DIR_NAME,
            this.destDir.getAbsolutePath());

    Configuration config = new Configuration();
    String customPropName = "custom.prop";
    String customPropValue = "DEF";
    String onePropName = "one.prop";
    String onePropValue = "one-one-one";
    String threePropName = "three.prop";
    String threePropValue = "333";
    config.put(new PropertySimple(customPropName, customPropValue));
    config.put(new PropertySimple(onePropName, onePropValue));
    config.put(new PropertySimple(threePropName, threePropValue));

    BundleDeployment deployment = new BundleDeployment();
    deployment.setId(456);
    deployment.setName("test bundle 3 deployment name - upgrades test bundle 2");
    deployment.setBundleVersion(bundleVersion);
    deployment.setConfiguration(config);
    deployment.setDestination(destination);

    // copy the test archive file to the bundle files dir
    FileUtil.copyFile(
        new File("src/test/resources/test-bundle-three-archive.zip"),
        new File(this.bundleFilesDir, "test-bundle-three-archive.zip"));

    // create test.properties file in the bundle files dir
    File file1 = new File(this.bundleFilesDir, "test.properties");
    Properties props = new Properties();
    props.setProperty(customPropName, "@@" + customPropName + "@@");
    FileOutputStream outputStream = new FileOutputStream(file1);
    props.store(outputStream, "test.properties comment");
    outputStream.close();

    // create some additional files - note: recipe says to ignore "ignore/**"
    File ignoreDir = new File(this.destDir, "ignore");
    File extraDir = new File(this.destDir, "extra");
    ignoreDir.mkdirs();
    extraDir.mkdirs();
    File ignoredFile = new File(ignoreDir, "ignore-file.txt");
    File extraFile = new File(extraDir, "extra-file.txt");
    FileUtil.writeFile(new ByteArrayInputStream("ignore".getBytes()), ignoredFile);
    FileUtil.writeFile(new ByteArrayInputStream("extra".getBytes()), extraFile);

    BundleDeployRequest request = new BundleDeployRequest();
    request.setBundleFilesLocation(this.bundleFilesDir);
    request.setResourceDeployment(new BundleResourceDeployment(deployment, null));
    request.setBundleManagerProvider(new MockBundleManagerProvider());
    request.setAbsoluteDestinationDirectory(this.destDir);
    request.setCleanDeployment(clean);

    BundleDeployResult results = plugin.deployBundle(request);

    assertResultsSuccess(results);

    // test that the prop was replaced in raw file test.properties
    Properties realizedProps = new Properties();
    loadProperties(
        realizedProps, new FileInputStream(new File(this.destDir, "config/test.properties")));
    assert customPropValue.equals(realizedProps.getProperty(customPropName))
        : "didn't replace prop";

    // test that the archive was extracted properly. These are the files in the archive or removed
    // from original:
    // REMOVED: zero-file.txt
    // one/one-file.txt (content: "@@one.prop@@") <-- recipe says this is to be replaced
    // two/two-file.txt (content: "@@two.prop@@") <-- recipe does not say to replace this
    // three/three-file.txt (content: "@@three.prop@@") <-- recipe says this is to be replaced
    File zeroFile = new File(this.destDir, "zero-file.txt");
    File oneFile = new File(this.destDir, "one/one-file.txt");
    File twoFile = new File(this.destDir, "two/two-file.txt");
    File threeFile = new File(this.destDir, "three/three-file.txt");
    assert !zeroFile.exists() : "zero file should have been removed during upgrade";
    assert oneFile.exists() : "one file missing";
    assert twoFile.exists() : "two file missing";
    assert threeFile.exists() : "three file missing";
    if (clean) {
      assert !ignoredFile.exists()
          : "ignored file should have been deleted due to clean deployment request";
      assert !extraFile.exists()
          : "extra file should have been deleted due to clean deployment request";
    } else {
      assert ignoredFile.exists() : "ignored file wasn't ignored, it was deleted";
      assert !extraFile.exists() : "extra file ignored, but it should have been deleted/backed up";
    }
    assert readFile(oneFile).startsWith(onePropValue);
    assert readFile(twoFile).startsWith("@@two.prop@@");
    assert readFile(threeFile).startsWith(threePropValue);

    DeploymentsMetadata metadata = new DeploymentsMetadata(this.destDir);
    DeploymentProperties deploymentProps = metadata.getDeploymentProperties(deployment.getId());
    assert deploymentProps.getDeploymentId() == deployment.getId();
    assert deploymentProps.getBundleName().equals(bundle.getName());
    assert deploymentProps.getBundleVersion().equals(bundleVersion.getVersion());
    assert deploymentProps.getManageRootDir() == true;

    DeploymentProperties currentProps = metadata.getCurrentDeploymentProperties();
    assert deploymentProps.equals(currentProps);

    // check the backup directory - note, clean flag is irrelevent when determining what should be
    // backed up
    File backupDir = metadata.getDeploymentBackupDirectory(deployment.getId());
    File extraBackupFile =
        new File(backupDir, extraDir.getName() + File.separatorChar + extraFile.getName());
    File ignoredBackupFile =
        new File(backupDir, ignoreDir.getName() + File.separatorChar + ignoredFile.getName());
    assert !ignoredBackupFile.exists() : "ignored file was backed up but it should not have been";
    assert extraBackupFile.exists() : "extra file was not backed up";
    assert "extra".equals(new String(StreamUtil.slurp(new FileInputStream(extraBackupFile))))
        : "bad backup of extra";

    DeploymentProperties previousProps = metadata.getPreviousDeploymentProperties(456);
    assert previousProps != null : "There should be previous deployment metadata";
    assert previousProps.getDeploymentId() == 123
        : "bad previous deployment metadata"; // testAntBundleInitialInstall used 123
    assert previousProps.getBundleName().equals(deploymentProps.getBundleName());
    assert previousProps
        .getBundleVersion()
        .equals("2.5"); // testAntBundleInitialInstall deployed version 2.5
    assert previousProps.getManageRootDir() == true;
  }
  /**
   * Test deployment of an RHQ bundle recipe where the deploy directory is not to be fully managed.
   */
  @Test(enabled = true)
  public void testAntBundleNoManageRootDir() throws Exception {
    ResourceType resourceType =
        new ResourceType("testNoManageRootDirBundle", "plugin", ResourceCategory.SERVER, null);
    BundleType bundleType = new BundleType("testNoManageRootDirBundle", resourceType);
    Repo repo = new Repo("testNoManageRootDirBundle");
    PackageType packageType = new PackageType("testNoManageRootDirBundle", resourceType);
    Bundle bundle = new Bundle("testNoManageRootDirBundle", bundleType, repo, packageType);
    BundleVersion bundleVersion =
        new BundleVersion(
            "testNoManageRootDirBundle",
            "1.0",
            bundle,
            getRecipeFromFile("test-bundle-no-manage-root-dir.xml"));
    BundleDestination destination =
        new BundleDestination(
            bundle,
            "testNoManageRootDirBundle",
            new ResourceGroup("testNoManageRootDirBundle"),
            DEST_BASE_DIR_NAME,
            this.destDir.getAbsolutePath());
    Configuration config = new Configuration();

    BundleDeployment deployment = new BundleDeployment();
    deployment.setName("test bundle deployment name");
    deployment.setBundleVersion(bundleVersion);
    deployment.setConfiguration(config);
    deployment.setDestination(destination);

    // create bundle test files
    File file0 = new File(this.bundleFilesDir, "zero.properties");
    Properties props = new Properties();
    props.setProperty("zero", "0");
    FileOutputStream outputStream = new FileOutputStream(file0);
    props.store(outputStream, "zero file");
    outputStream.close();

    File file1 = new File(this.bundleFilesDir, "one.properties");
    props.clear();
    props.setProperty("one", "1");
    outputStream = new FileOutputStream(file1);
    props.store(outputStream, "one file");
    outputStream.close();

    File file2 = new File(this.bundleFilesDir, "two.properties");
    props.clear();
    props.setProperty("two", "2");
    outputStream = new FileOutputStream(file2);
    props.store(outputStream, "two file");
    outputStream.close();

    // create some external test files that don't belong to the bundle but are in the dest dir
    // (which is not fully managed by the bundle)
    this.destDir.mkdirs();
    File external1 = new File(this.destDir, "external1.properties");
    props.clear();
    props.setProperty("external1", "1");
    outputStream = new FileOutputStream(external1);
    props.store(outputStream, "external1 file");
    outputStream.close();

    File external2 = new File(this.destDir, "extdir/external2.properties");
    external2.getParentFile().mkdirs();
    props.clear();
    props.setProperty("external2", "2");
    outputStream = new FileOutputStream(external2);
    props.store(outputStream, "external2 file");
    outputStream.close();

    // deploy the bundle
    BundleDeployRequest request = new BundleDeployRequest();
    request.setBundleFilesLocation(this.bundleFilesDir);
    request.setResourceDeployment(new BundleResourceDeployment(deployment, null));
    request.setBundleManagerProvider(new MockBundleManagerProvider());
    request.setAbsoluteDestinationDirectory(this.destDir);

    BundleDeployResult results = plugin.deployBundle(request);

    assertResultsSuccess(results);

    // test that files were deployed in the proper place
    props.clear();
    loadProperties(props, new FileInputStream(new File(this.destDir, "zero.properties")));
    assert "0".equals(props.getProperty("zero")) : "did not deploy bundle correctly 0";
    loadProperties(props, new FileInputStream(new File(this.destDir, "subdir1/one.properties")));
    assert "1".equals(props.getProperty("one")) : "did not deploy bundle correctly 1";
    loadProperties(props, new FileInputStream(new File(this.destDir, "subdir2/two.properties")));
    assert "2".equals(props.getProperty("two")) : "did not deploy bundle correctly 2";

    DeploymentsMetadata metadata = new DeploymentsMetadata(this.destDir);
    assert metadata.isManaged() == true : "missing metadata directory";
    assert metadata.getCurrentDeploymentProperties().getManageRootDir() == false
        : "should not be managing root dir";

    // make sure our unmanaged files/directories weren't removed
    props.clear();
    loadProperties(props, new FileInputStream(new File(this.destDir, "external1.properties")));
    assert "1".equals(props.getProperty("external1"))
        : "bundle deployment removed our unmanaged file 1";
    loadProperties(
        props, new FileInputStream(new File(this.destDir, "extdir/external2.properties")));
    assert "2".equals(props.getProperty("external2"))
        : "bundle deployment removed our unmanaged file 2";

    // now purge the bundle - this should only purge those files that were laid down by the bundle
    // plus the metadata directory
    BundlePurgeRequest purgeRequest = new BundlePurgeRequest();
    purgeRequest.setLiveResourceDeployment(new BundleResourceDeployment(deployment, null));
    purgeRequest.setBundleManagerProvider(new MockBundleManagerProvider());
    purgeRequest.setAbsoluteDestinationDirectory(this.destDir);

    BundlePurgeResult purgeResults = plugin.purgeBundle(purgeRequest);
    assertResultsSuccess(purgeResults);

    // make sure our bundle files have been completely purged; the metadata directory should have
    // been purged too
    assert new File(this.destDir, "zero.properties").exists() == false;
    assert new File(this.destDir, "subdir1/one.properties").exists() == false;
    assert new File(this.destDir, "subdir2/two.properties").exists() == false;
    assert new File(this.destDir, "subdir1").exists() == false;
    assert new File(this.destDir, "subdir2").exists() == false;
    assert this.destDir.exists() == true
        : "deploy dir should still exist, we were told not to fully manage it";

    metadata = new DeploymentsMetadata(this.destDir);
    assert metadata.getMetadataDirectory().exists() == false
        : "metadata directory should not exist";

    // make sure our external, unmanaged files still exist - the purge should not have deleted these
    props.clear();
    loadProperties(props, new FileInputStream(new File(this.destDir, "external1.properties")));
    assert "1".equals(props.getProperty("external1")) : "bundle purge removed our unmanaged file 1";
    loadProperties(
        props, new FileInputStream(new File(this.destDir, "extdir/external2.properties")));
    assert "2".equals(props.getProperty("external2")) : "bundle purge removed our unmanaged file 2";
  }
  /** Test deployment of an RHQ bundle recipe. */
  @Test(enabled = true)
  public void testAntBundle() throws Exception {
    ResourceType resourceType =
        new ResourceType("testSimpleBundle", "plugin", ResourceCategory.SERVER, null);
    BundleType bundleType = new BundleType("testSimpleBundle", resourceType);
    Repo repo = new Repo("testSimpleBundle");
    PackageType packageType = new PackageType("testSimpleBundle", resourceType);
    Bundle bundle = new Bundle("testSimpleBundle", bundleType, repo, packageType);
    BundleVersion bundleVersion =
        new BundleVersion("testSimpleBundle", "1.0", bundle, getRecipeFromFile("test-bundle.xml"));
    BundleDestination destination =
        new BundleDestination(
            bundle,
            "testSimpleBundle",
            new ResourceGroup("testSimpleBundle"),
            DEST_BASE_DIR_NAME,
            this.destDir.getAbsolutePath());

    Configuration config = new Configuration();
    String realPropValue = "ABC123";
    config.put(new PropertySimple("custom.prop1", realPropValue));

    BundleDeployment deployment = new BundleDeployment();
    deployment.setName("test bundle deployment name");
    deployment.setBundleVersion(bundleVersion);
    deployment.setConfiguration(config);
    deployment.setDestination(destination);

    // create test file
    File file1 = new File(this.bundleFilesDir, "test.properties");
    Properties props = new Properties();
    props.setProperty("custom.prop1", "@@custom.prop1@@");
    FileOutputStream outputStream = new FileOutputStream(file1);
    props.store(outputStream, "replace");
    outputStream.close();

    // create noreplace test file
    File noreplacefile = new File(this.bundleFilesDir, "noreplace.properties");
    outputStream = new FileOutputStream(noreplacefile);
    props.store(outputStream, "noreplace");
    outputStream.close();

    // create foo test file
    File foofile = new File(this.bundleFilesDir, "foo.properties");
    outputStream = new FileOutputStream(foofile);
    props.store(outputStream, "foo");
    outputStream.close();

    BundleDeployRequest request = new BundleDeployRequest();
    request.setBundleFilesLocation(this.bundleFilesDir);
    request.setResourceDeployment(new BundleResourceDeployment(deployment, null));
    request.setBundleManagerProvider(new MockBundleManagerProvider());
    request.setAbsoluteDestinationDirectory(this.destDir);

    BundleDeployResult results = plugin.deployBundle(request);

    assertResultsSuccess(results);

    // test that the prop was replaced in test.properties
    Properties realizedProps = new Properties();
    loadProperties(
        realizedProps, new FileInputStream(new File(this.destDir, "config/test.properties")));
    assert realPropValue.equals(realizedProps.getProperty("custom.prop1")) : "didn't replace prop";

    // test that the prop was not replaced in noreplace.properties
    Properties notrealizedProps = new Properties();
    loadProperties(
        notrealizedProps,
        new FileInputStream(new File(this.destDir, "config/noreplace.properties")));
    assert "@@custom.prop1@@".equals(notrealizedProps.getProperty("custom.prop1"))
        : "replaced prop when it shouldn't";
  }
  /** Test deployment of an RHQ bundle recipe with archive file and raw file */
  @Test(enabled = true)
  public void testAntBundleInitialInstall() throws Exception {
    ResourceType resourceType =
        new ResourceType("testSimpleBundle2Type", "plugin", ResourceCategory.SERVER, null);
    BundleType bundleType = new BundleType("testSimpleBundle2BType", resourceType);
    Repo repo = new Repo("test-bundle-two");
    PackageType packageType = new PackageType("test-bundle-two", resourceType);
    Bundle bundle = new Bundle("test-bundle-two", bundleType, repo, packageType);
    BundleVersion bundleVersion =
        new BundleVersion(
            "test-bundle-two", "2.5", bundle, getRecipeFromFile("test-bundle-two.xml"));
    BundleDestination destination =
        new BundleDestination(
            bundle,
            "testSimpleBundle2Dest",
            new ResourceGroup("testSimpleBundle2Group"),
            DEST_BASE_DIR_NAME,
            this.destDir.getAbsolutePath());

    Configuration config = new Configuration();
    String customPropName = "custom.prop";
    String customPropValue = "ABC";
    String onePropName = "one.prop";
    String onePropValue = "111";
    config.put(new PropertySimple(customPropName, customPropValue));
    config.put(new PropertySimple(onePropName, onePropValue));

    BundleDeployment deployment = new BundleDeployment();
    deployment.setId(123);
    deployment.setName("test bundle 2 deployment name");
    deployment.setBundleVersion(bundleVersion);
    deployment.setConfiguration(config);
    deployment.setDestination(destination);

    // copy the test archive file to the bundle files dir
    FileUtil.copyFile(
        new File("src/test/resources/test-bundle-two-archive.zip"),
        new File(this.bundleFilesDir, "test-bundle-two-archive.zip"));

    // create test.properties file in the bundle files dir
    File file1 = new File(this.bundleFilesDir, "test.properties");
    Properties props = new Properties();
    props.setProperty(customPropName, "@@" + customPropName + "@@");
    FileOutputStream outputStream = new FileOutputStream(file1);
    props.store(outputStream, "test.properties comment");
    outputStream.close();

    BundleDeployRequest request = new BundleDeployRequest();
    request.setBundleFilesLocation(this.bundleFilesDir);
    request.setResourceDeployment(new BundleResourceDeployment(deployment, null));
    request.setBundleManagerProvider(new MockBundleManagerProvider());
    request.setAbsoluteDestinationDirectory(this.destDir);

    BundleDeployResult results = plugin.deployBundle(request);

    assertResultsSuccess(results);

    // test that the prop was replaced in raw file test.properties
    Properties realizedProps = new Properties();
    loadProperties(
        realizedProps, new FileInputStream(new File(this.destDir, "config/test.properties")));
    assert customPropValue.equals(realizedProps.getProperty(customPropName))
        : "didn't replace prop";

    // test that the archive was extracted properly. These are the files in the archive:
    // zero-file.txt (content: "zero")
    // one/one-file.txt (content: "@@one.prop@@") <-- recipe says this is to be replaced
    // two/two-file.txt (content: "@@two.prop@@") <-- recipe does not say to replace this
    File zeroFile = new File(this.destDir, "zero-file.txt");
    File oneFile = new File(this.destDir, "one/one-file.txt");
    File twoFile = new File(this.destDir, "two/two-file.txt");
    assert zeroFile.exists() : "zero file missing";
    assert oneFile.exists() : "one file missing";
    assert twoFile.exists() : "two file missing";
    assert readFile(zeroFile).startsWith("zero");
    assert readFile(oneFile).startsWith(onePropValue);
    assert readFile(twoFile).startsWith("@@two.prop@@");

    DeploymentsMetadata metadata = new DeploymentsMetadata(this.destDir);
    DeploymentProperties deploymentProps = metadata.getDeploymentProperties(deployment.getId());
    assert deploymentProps.getDeploymentId() == deployment.getId();
    assert deploymentProps.getBundleName().equals(bundle.getName());
    assert deploymentProps.getBundleVersion().equals(bundleVersion.getVersion());
    assert deploymentProps.getManageRootDir() == true;
    DeploymentProperties currentProps = metadata.getCurrentDeploymentProperties();
    assert deploymentProps.equals(currentProps);
    DeploymentProperties previousProps =
        metadata.getPreviousDeploymentProperties(deployment.getId());
    assert previousProps == null : "There should not be any previous deployment metadata";
  }
  @Test(enabled = true)
  public void testAntBundleRevert() throws Exception {
    // install then upgrade a bundle first
    testAntBundleUpgrade();
    cleanPluginDirs(); // clean everything but the dest dir - we want to keep the metadata
    prepareBeforeTestMethod(); // prepare for our new test

    // we installed version 2.5 then upgraded to 3.0
    // now we want to revert back to 2.5
    ResourceType resourceType =
        new ResourceType("testSimpleBundle2Type", "plugin", ResourceCategory.SERVER, null);
    BundleType bundleType = new BundleType("testSimpleBundle2BType", resourceType);
    Repo repo = new Repo("test-bundle-two");
    PackageType packageType = new PackageType("test-bundle-two", resourceType);
    Bundle bundle = new Bundle("test-bundle-two", bundleType, repo, packageType);
    BundleVersion bundleVersion =
        new BundleVersion(
            "test-bundle-two", "2.5", bundle, getRecipeFromFile("test-bundle-two.xml"));
    BundleDestination destination =
        new BundleDestination(
            bundle,
            "testSimpleBundle2Dest",
            new ResourceGroup("testSimpleBundle2Group"),
            DEST_BASE_DIR_NAME,
            this.destDir.getAbsolutePath());

    Configuration config = new Configuration();
    String customPropName = "custom.prop";
    String customPropValue = "ABC-revert";
    String onePropName = "one.prop";
    String onePropValue = "111-revert";
    config.put(new PropertySimple(customPropName, customPropValue));
    config.put(new PropertySimple(onePropName, onePropValue));

    BundleDeployment deployment = new BundleDeployment();
    deployment.setId(789);
    deployment.setName("test bundle 2 deployment name - REVERT");
    deployment.setBundleVersion(bundleVersion);
    deployment.setConfiguration(config);
    deployment.setDestination(destination);

    // copy the test archive file to the bundle files dir
    FileUtil.copyFile(
        new File("src/test/resources/test-bundle-two-archive.zip"),
        new File(this.bundleFilesDir, "test-bundle-two-archive.zip"));

    // create test.properties file in the bundle files dir
    File file1 = new File(this.bundleFilesDir, "test.properties");
    Properties props = new Properties();
    props.setProperty(customPropName, "@@" + customPropName + "@@");
    FileOutputStream outputStream = new FileOutputStream(file1);
    props.store(outputStream, "test.properties comment");
    outputStream.close();

    BundleDeployRequest request = new BundleDeployRequest();
    request.setBundleFilesLocation(this.bundleFilesDir);
    request.setResourceDeployment(new BundleResourceDeployment(deployment, null));
    request.setBundleManagerProvider(new MockBundleManagerProvider());
    request.setAbsoluteDestinationDirectory(this.destDir);
    request.setRevert(true);

    BundleDeployResult results = plugin.deployBundle(request);

    assertResultsSuccess(results);

    // test that the prop was replaced in raw file test.properties
    Properties realizedProps = new Properties();
    loadProperties(
        realizedProps, new FileInputStream(new File(this.destDir, "config/test.properties")));
    assert customPropValue.equals(realizedProps.getProperty(customPropName))
        : "didn't replace prop";

    // test that the archive was extracted properly. These are the files in the archive:
    // zero-file.txt (content: "zero")
    // one/one-file.txt (content: "@@one.prop@@") <-- recipe says this is to be replaced
    // two/two-file.txt (content: "@@two.prop@@") <-- recipe does not say to replace this
    // REMOVED: three/three-file.txt <-- this existed in the upgrade, but not the original
    // ----- the following was backed up and should be reverted
    // extra/extra-file.txt

    File zeroFile = new File(this.destDir, "zero-file.txt");
    File oneFile = new File(this.destDir, "one/one-file.txt");
    File twoFile = new File(this.destDir, "two/two-file.txt");
    File threeFile = new File(this.destDir, "three/three-file.txt");
    assert zeroFile.exists() : "zero file should have been restored during revert";
    assert oneFile.exists() : "one file missing";
    assert twoFile.exists() : "two file missing";
    assert !threeFile.exists() : "three file should have been deleted during revert";

    assert readFile(zeroFile).startsWith("zero") : "bad restore of zero file";
    assert readFile(oneFile).startsWith(onePropValue);
    assert readFile(twoFile).startsWith("@@two.prop@@");

    // make sure the revert restored the backed up files
    File extraFile = new File(this.destDir, "extra/extra-file.txt");
    assert extraFile.exists()
        : "extra file should have been restored due to revert deployment request";
    assert readFile(extraFile).startsWith("extra") : "bad restore of extra file";

    DeploymentsMetadata metadata = new DeploymentsMetadata(this.destDir);
    DeploymentProperties deploymentProps = metadata.getDeploymentProperties(deployment.getId());
    assert deploymentProps.getDeploymentId() == deployment.getId();
    assert deploymentProps.getBundleName().equals(bundle.getName());
    assert deploymentProps.getBundleVersion().equals(bundleVersion.getVersion());
    assert deploymentProps.getManageRootDir() == true;

    DeploymentProperties currentProps = metadata.getCurrentDeploymentProperties();
    assert deploymentProps.equals(currentProps);

    // check the backup directory - note, clean flag is irrelevent when determining what should be
    // backed up
    File backupDir = metadata.getDeploymentBackupDirectory(deployment.getId());
    File ignoredBackupFile = new File(backupDir, "ignore/ignore-file.txt");
    assert ignoredBackupFile.isFile() : "old recipe didn't ignore these, should be backed up";

    DeploymentProperties previousProps = metadata.getPreviousDeploymentProperties(789);
    assert previousProps != null : "There should be previous deployment metadata";
    assert previousProps.getDeploymentId() == 456
        : "bad previous deployment metadata"; // testAntBundleUpgrade used 456
    assert previousProps.getBundleName().equals(deploymentProps.getBundleName());
    assert previousProps
        .getBundleVersion()
        .equals("3.0"); // testAntBundleUpgrade deployed version 3.0
  }
Beispiel #10
0
  /**
   * Downloads the bundle's files into the bundle plugin's tmp directory and returns that tmp
   * directory.
   *
   * @param resourceDeployment access to deployment information, including what bundle files need to
   *     be downloaded
   * @param downloadDir location where the bundle files should be downloaded
   * @return map of the package versions to their files that were downloaded
   * @throws Exception
   */
  private Map<PackageVersion, File> downloadBundleFiles(
      BundleResourceDeployment resourceDeployment, File downloadDir) throws Exception {

    BundleDeployment bundleDeployment = resourceDeployment.getBundleDeployment();
    BundleVersion bundleVersion = bundleDeployment.getBundleVersion();

    Map<PackageVersion, File> packageVersionFiles = new HashMap<PackageVersion, File>();
    List<PackageVersion> packageVersions = getAllBundleVersionPackageVersions(bundleVersion);
    for (PackageVersion packageVersion : packageVersions) {
      File packageFile = new File(downloadDir, packageVersion.getFileName());

      try {
        verifyHash(packageVersion, packageFile);
      } catch (Exception e) {

        // file either doesn't exist or it hash doesn't match, download a new copy
        packageFile.getParentFile().mkdirs();
        FileOutputStream fos = new FileOutputStream(packageFile);
        try {
          auditDeployment(
              resourceDeployment,
              AUDIT_FILE_DOWNLOAD_STARTED,
              packageVersion.getDisplayName(),
              "Downloading [" + packageVersion + "]");

          long size = getFileContent(packageVersion, fos);

          if (packageVersion.getFileSize() != null
              && size != packageVersion.getFileSize().longValue()) {
            String message =
                "Downloaded bundle file ["
                    + packageVersion
                    + "] but its size was ["
                    + size
                    + "] when it was expected to be ["
                    + packageVersion.getFileSize()
                    + "].";
            log.warn(message);
            auditDeployment(
                resourceDeployment,
                AUDIT_FILE_DOWNLOAD_ENDED,
                packageVersion.getDisplayName(),
                null,
                BundleResourceDeploymentHistory.Status.WARN,
                message,
                null);
          } else {
            auditDeployment(
                resourceDeployment,
                AUDIT_FILE_DOWNLOAD_ENDED,
                packageVersion.getDisplayName(),
                "Download complete for [" + packageVersion + "]");
          }
        } catch (Exception e2) {
          String message = "Failed to downloaded bundle file [" + packageVersion + "] " + e2;
          log.warn(message);
          auditDeployment(
              resourceDeployment,
              AUDIT_FILE_DOWNLOAD_ENDED,
              packageVersion.getDisplayName(),
              null,
              BundleResourceDeploymentHistory.Status.FAILURE,
              message,
              null);
        } finally {
          fos.close();
        }

        // now try to verify it again, if this throws an exception, that is very bad and we need to
        // abort
        verifyHash(packageVersion, packageFile);
      }

      packageVersionFiles.put(packageVersion, packageFile);
    }

    return packageVersionFiles;
  }
Beispiel #11
0
  @Override
  public BundlePurgeResponse purge(BundlePurgeRequest request) {
    final BundlePurgeResponse response = new BundlePurgeResponse();

    try {
      final BundleResourceDeployment resourceDeployment = request.getLiveBundleResourceDeployment();
      final BundleDeployment bundleDeployment = resourceDeployment.getBundleDeployment();

      // find the resource that will purge the bundle
      InventoryManager im = getInventoryManager();
      BundleType bundleType = bundleDeployment.getBundleVersion().getBundle().getBundleType();
      ResourceType resourceType = bundleType.getResourceType();
      Set<Resource> resources = im.getResourcesWithType(resourceType);
      if (resources.isEmpty()) {
        throw new Exception("No bundle plugin supports bundle type [" + bundleType + "]");
      }
      final int bundleHandlerResourceId = resources.iterator().next().getId();
      final ResourceContainer resourceContainer = im.getResourceContainer(bundleHandlerResourceId);
      if (null == resourceContainer.getResourceContext()) {
        throw new Exception(
            "No bundle plugin resource available to handle purge for bundle type ["
                + bundleType
                + "]. Ensure the bundle plugin is deployed and its resource is imported into inventory.");
      }

      // purge the bundle utilizing the bundle facet object
      String deploymentMessage =
          "Deployment ["
              + bundleDeployment
              + "] to be purged via ["
              + resourceDeployment.getResource()
              + "]";
      auditDeployment(
          resourceDeployment, AUDIT_PURGE_STARTED, bundleDeployment.getName(), deploymentMessage);

      File absoluteDestDir = getAbsoluteDestinationDir(request.getLiveBundleResourceDeployment());

      org.rhq.core.pluginapi.bundle.BundlePurgeRequest purgeRequest =
          new org.rhq.core.pluginapi.bundle.BundlePurgeRequest();
      purgeRequest.setBundleManagerProvider(this);
      purgeRequest.setLiveResourceDeployment(resourceDeployment);
      purgeRequest.setAbsoluteDestinationDirectory(absoluteDestDir);

      // get the bundle facet object that will process the bundle and call it to start the purge
      int facetMethodTimeout =
          30 * 60
              * 1000; // 30 minutes should be enough time for the bundle plugin to purge everything
      BundleFacet bundlePluginComponent =
          getBundleFacet(bundleHandlerResourceId, facetMethodTimeout);
      BundlePurgeResult result = bundlePluginComponent.purgeBundle(purgeRequest);
      if (result.isSuccess()) {
        auditDeployment(
            resourceDeployment, AUDIT_PURGE_ENDED, bundleDeployment.getName(), deploymentMessage);
      } else {
        response.setErrorMessage(result.getErrorMessage());
        auditDeployment(
            resourceDeployment,
            AUDIT_PURGE_ENDED,
            bundleDeployment.getName(),
            null,
            Status.FAILURE,
            "Failed: " + deploymentMessage,
            result.getErrorMessage());
      }
    } catch (Throwable t) {
      log.error("Failed to purge bundle: " + request, t);
      response.setErrorMessage(t);
    }

    return response;
  }
Beispiel #12
0
  @Override
  public BundleScheduleResponse schedule(final BundleScheduleRequest request) {
    final BundleScheduleResponse response = new BundleScheduleResponse();

    try {
      final BundleResourceDeployment resourceDeployment = request.getBundleResourceDeployment();
      final BundleDeployment bundleDeployment = resourceDeployment.getBundleDeployment();

      // find the resource that will handle the bundle processing
      InventoryManager im = getInventoryManager();
      BundleType bundleType = bundleDeployment.getBundleVersion().getBundle().getBundleType();
      ResourceType resourceType = bundleType.getResourceType();
      Set<Resource> resources = im.getResourcesWithType(resourceType);
      if (resources.isEmpty()) {
        throw new Exception("No bundle plugin supports bundle type [" + bundleType + "]");
      }
      final int bundleHandlerResourceId = resources.iterator().next().getId();
      final ResourceContainer resourceContainer = im.getResourceContainer(bundleHandlerResourceId);
      if (null == resourceContainer.getResourceContext()) {
        throw new Exception(
            "No bundle plugin resource available to handle deployment for bundle type ["
                + bundleType
                + "]. Ensure the bundle plugin is deployed and its resource is imported into inventory.");
      }

      auditDeployment(
          resourceDeployment,
          AUDIT_DEPLOYMENT_SCHEDULED,
          bundleDeployment.getName(),
          "Scheduled deployment time: " + request.getRequestedDeployTimeAsString());

      Runnable deployerRunnable =
          new Runnable() {
            public void run() {
              try {
                // pull down the bundle files that the plugin will need in order to process the
                // bundle
                File pluginTmpDir = resourceContainer.getResourceContext().getTemporaryDirectory();
                File bundleFilesDir =
                    new File(
                        pluginTmpDir,
                        "bundle-versions/" + bundleDeployment.getBundleVersion().getId());
                bundleFilesDir.mkdirs();

                // clean up any old downloads we may have retrieved before. This helps clean out
                // our temp directory so we don't unnecessarily fill up our file system with
                // obsolete files
                removeOldDownloadedBundleFiles(bundleFilesDir);

                // now download the bundle files we need for the current deployment
                Map<PackageVersion, File> downloadedFiles =
                    downloadBundleFiles(resourceDeployment, bundleFilesDir);

                // deploy the bundle utilizing the bundle facet object
                String deploymentMessage =
                    "Deployment ["
                        + bundleDeployment
                        + "] to ["
                        + resourceDeployment.getResource()
                        + "]";
                auditDeployment(
                    resourceDeployment,
                    AUDIT_DEPLOYMENT_STARTED,
                    bundleDeployment.getName(),
                    deploymentMessage);

                File absoluteDestDir =
                    getAbsoluteDestinationDir(request.getBundleResourceDeployment());

                BundleDeployRequest deployRequest = new BundleDeployRequest();
                deployRequest.setBundleManagerProvider(BundleManager.this);
                deployRequest.setResourceDeployment(resourceDeployment);
                deployRequest.setBundleFilesLocation(bundleFilesDir);
                deployRequest.setPackageVersionFiles(downloadedFiles);
                deployRequest.setCleanDeployment(request.isCleanDeployment());
                deployRequest.setRevert(request.isRevert());
                deployRequest.setAbsoluteDestinationDirectory(absoluteDestDir);

                // get the bundle facet object that will process the bundle and call it to start the
                // deployment
                int facetMethodTimeout =
                    4 * 60 * 60 * 1000; // 4 hours is given to the bundle plugin to do its thing
                BundleFacet bundlePluginComponent =
                    getBundleFacet(bundleHandlerResourceId, facetMethodTimeout);
                BundleDeployResult result = bundlePluginComponent.deployBundle(deployRequest);
                if (result.isSuccess()) {
                  completeDeployment(
                      resourceDeployment, BundleDeploymentStatus.SUCCESS, deploymentMessage);
                } else {
                  completeDeployment(
                      resourceDeployment, BundleDeploymentStatus.FAILURE, result.getErrorMessage());
                }
              } catch (InterruptedException ie) {
                log.error("Failed to complete bundle deployment due to interrupt", ie);
                completeDeployment(
                    resourceDeployment, BundleDeploymentStatus.FAILURE, "Deployment interrupted");
              } catch (Throwable t) {
                log.error("Failed to complete bundle deployment", t);
                completeDeployment(
                    resourceDeployment,
                    BundleDeploymentStatus.FAILURE,
                    "Deployment failed: " + ThrowableUtil.getAllMessages(t));
              }
            }
          };

      this.deployerThreadPool.execute(deployerRunnable);
    } catch (Throwable t) {
      log.error("Failed to schedule bundle request: " + request, t);
      response.setErrorMessage(t);
    }

    return response;
  }