Skip to content

Commit

Permalink
feat(installer): BDM comparison (#2747)
Browse files Browse the repository at this point in the history
Add a new service method `isDeployed(byte[] bdmArchive)` to compare a
given bdm archive to install with the current deployed bdm.

To perform the comparison, we uses the generated server jar.
This jar contains the generated classes + the bom.xml file.

Before generating the server jar, we set the productVersion in the
bom.xml stored to match the current platform version. This way we ensure
that the result of the generation + schema update is equivalent for a
given xml model.

BDM comparison is used at application install/update. If the bdm are
equivalent when updating, the tenant is not paused and no bdm update is
performed. This should limit the platform downtime (especially in
cluster) when updating an SCA without updating the bonita version.

To also ensure that the bdm server jar can be compared with another
equivalent version, the utility method to create jar file in IOUtils now
forces the JarEntry creation/lastUpdate date and time.

To be able to generate a bdm server jar to do the comparison, a
`disableRuntimeClassesValidation` flag has been added to the
CodeGenerator. The flag is true by default.

The application version and its semver implementation are now part of the
ApplicationArchive.

Closes DEV-552
---------

Co-authored-by: Bonita CI <[email protected]>
Co-authored-by: Adrien Kantcheff <[email protected]>
  • Loading branch information
3 people authored Oct 6, 2023
1 parent 9470dd9 commit 447aa9c
Show file tree
Hide file tree
Showing 30 changed files with 633 additions and 400 deletions.
Original file line number Diff line number Diff line change
Expand Up @@ -17,7 +17,6 @@
import static org.assertj.core.api.Assertions.assertThatExceptionOfType;

import java.io.File;
import java.util.Optional;

import org.bonitasoft.engine.CommonAPIIT;
import org.bonitasoft.engine.api.impl.application.installer.ApplicationArchive;
Expand Down Expand Up @@ -77,7 +76,7 @@ public void custom_application_should_be_deployed_entirely() throws Exception {
// when:
try (var applicationAsStream = ApplicationInstallerIT.class.getResourceAsStream("/customer-application.zip")) {
var applicationArchive = applicationArchiveReader.read(applicationAsStream);
applicationInstallerImpl.install(applicationArchive, "1.0.0");
applicationInstallerImpl.install(applicationArchive);
}

// then:
Expand Down Expand Up @@ -120,9 +119,9 @@ public void custom_application_should_be_installed_with_configuration() throws E
try (var applicationAsStream = ApplicationInstallerIT.class
.getResourceAsStream("/simple-app-1.0.0-SNAPSHOT-local.zip")) {
var applicationArchive = applicationArchiveReader.read(applicationAsStream);
applicationArchive.setConfigurationFile(Optional.of(new File(ApplicationInstallerIT.class
.getResource("/simple-app-1.0.0-SNAPSHOT-local.bconf").getFile())));
applicationInstallerImpl.install(applicationArchive, "1.0.0-SNAPSHOT");
applicationArchive.setConfigurationFile(new File(ApplicationInstallerIT.class
.getResource("/simple-app-1.0.0-SNAPSHOT-local.bconf").getFile()));
applicationInstallerImpl.install(applicationArchive);
}

final long processDefinitionId = getProcessAPI().getProcessDefinitionId("Pool", "1.0");
Expand Down Expand Up @@ -150,7 +149,7 @@ public void empty_custom_application_should_throw_an_exception() throws Exceptio

// then:
assertThatExceptionOfType(ApplicationInstallationException.class)
.isThrownBy(() -> applicationInstaller.install(applicationArchive, "1.0.0"))
.isThrownBy(() -> applicationInstaller.install(applicationArchive))
.withMessage("The Application Archive contains no valid artifact to install");
}
}
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -84,7 +84,7 @@ private void initFirstInstall() throws Exception {
final InputStream applicationAsStream = this.getClass().getResourceAsStream("/customer-application.zip");

// when:
applicationInstaller.install(applicationArchiveReader.read(applicationAsStream), "1.0.0");
applicationInstaller.install(applicationArchiveReader.read(applicationAsStream));

// then:

Expand Down Expand Up @@ -120,7 +120,7 @@ public void empty_custom_application_should_throw_an_exception() throws Exceptio

// then:
assertThatExceptionOfType(ApplicationInstallationException.class)
.isThrownBy(() -> applicationInstaller.update(applicationArchive, "1.0.1"))
.isThrownBy(() -> applicationInstaller.update(applicationArchive))
.withMessage("The Application Archive contains no valid artifact to install");
}

Expand All @@ -141,7 +141,7 @@ public void process_update_custom_application_with_same_installed_version() thro
try (var applicationAsStream = this.getClass().getResourceAsStream("/customer-application.zip")) {
// Avoid test failure due to instant update.
Awaitility.await().timeout(Duration.of(1, ChronoUnit.SECONDS));
applicationInstaller.update(applicationArchiveReader.read(applicationAsStream), "1.0.0");
applicationInstaller.update(applicationArchiveReader.read(applicationAsStream));
}
// then:
TenantResource updatedBdm = getTenantAdministrationAPI().getBusinessDataModelResource();
Expand All @@ -152,8 +152,9 @@ public void process_update_custom_application_with_same_installed_version() thro
final ProcessDeploymentInfo deploymentInfoAfterUpdate = getProcessAPI()
.getProcessDeploymentInfo(processDefinitionId);

// check that bdm resource has NOT been updated (same content)
assertThat(updatedBdm.getLastUpdateDate()).isEqualTo(bdm.getLastUpdateDate());
// check that resources has been updated
assertThat(updatedBdm.getLastUpdateDate().toEpochSecond() > bdm.getLastUpdateDate().toEpochSecond()).isTrue();
assertThat(updatedApplication.getLastUpdateDate().after(application.getLastUpdateDate())).isTrue();
assertThat(
updatedProcessStarterAPI.getLastModificationDate().after(processStarterAPI.getLastModificationDate()))
Expand Down Expand Up @@ -181,7 +182,7 @@ public void process_update_custom_application_with_new_version()

// when:
final InputStream applicationAsStream = this.getClass().getResourceAsStream("/customer-application-v2.zip");
applicationInstaller.update(applicationArchiveReader.read(applicationAsStream), "1.0.1");
applicationInstaller.update(applicationArchiveReader.read(applicationAsStream));

// then:
TenantResource updatedBdm = getTenantAdministrationAPI().getBusinessDataModelResource();
Expand All @@ -193,7 +194,7 @@ public void process_update_custom_application_with_new_version()
.getProcessDeploymentInfo(processDefinitionId);

// check that resources has been updated
assertThat(updatedBdm.getLastUpdateDate().toEpochSecond() > bdm.getLastUpdateDate().toEpochSecond()).isTrue();
assertThat(updatedBdm.getLastUpdateDate()).isAfter(bdm.getLastUpdateDate());
// check installed apps
assertThat(updatedApplication.getLastUpdateDate().after(application.getLastUpdateDate())).isTrue();
// fetch application menus
Expand All @@ -219,6 +220,6 @@ public void process_update_custom_application_with_new_version()

// CallHealthCheck Process must not be updated
assertThat(deploymentInfoAfterUpdate.getLastUpdateDate().after(deploymentInfo.getLastUpdateDate())).isTrue();
assertThat(deploymentInfoAfterUpdate.getActivationState().name()).isEqualTo("DISABLED");
assertThat(deploymentInfoAfterUpdate.getActivationState()).isEqualTo(ActivationState.DISABLED);
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -20,10 +20,8 @@
import java.util.List;
import java.util.Optional;

import lombok.AllArgsConstructor;
import lombok.Data;
import lombok.NoArgsConstructor;
import lombok.Singular;
import com.vdurmont.semver4j.Semver;
import lombok.*;
import lombok.extern.slf4j.Slf4j;

@Slf4j
Expand All @@ -32,6 +30,7 @@
@AllArgsConstructor
public class ApplicationArchive implements AutoCloseable {

private String fileName;
private File organization;
private File bdm;
private List<File> processes = new ArrayList<>();
Expand All @@ -41,11 +40,15 @@ public class ApplicationArchive implements AutoCloseable {
private List<File> themes = new ArrayList<>();
private List<File> applications = new ArrayList<>();
private List<File> applicationIcons = new ArrayList<>();

private List<File> ignoredFiles = new ArrayList<>();

@Singular
private Optional<File> configurationFile = Optional.empty();
private File configurationFile;

private String version;

@Setter(AccessLevel.NONE)
@Getter(AccessLevel.NONE)
private Semver semver;

public ApplicationArchive addPage(File page) {
pages.add(page);
Expand Down Expand Up @@ -92,6 +95,35 @@ public ApplicationArchive setBdm(File bdm) {
return this;
}

public Optional<File> getConfigurationFile() {
return Optional.ofNullable(configurationFile);
}

public void setVersion(String version) {
this.version = version;
if (version != null) {
this.semver = new Semver(version, Semver.SemverType.LOOSE);
}
}

public boolean hasVersionGreaterThan(String version) {
if (semver == null) {
return false;
}
return semver.isGreaterThan(version);
}

public boolean hasVersionEquivalentTo(String version) {
if (semver == null) {
return false;
}
return semver.isEquivalentTo(version);
}

public boolean hasVersion() {
return semver != null;
}

/**
* @return <code>true</code> if the application archive has no artifact
*/
Expand Down Expand Up @@ -133,4 +165,5 @@ protected void deletePhysicalFilesFromList(List<File> list) throws IOException {
Files.deleteIfExists(f.toPath());
}
}

}
Loading

0 comments on commit 447aa9c

Please sign in to comment.