/* * Copyright 2017-2019 the original author or authors. * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. * You may obtain a copy of the License at * * https://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. * See the License for the specific language governing permissions and * limitations under the License. */ package org.springframework.cloud.skipper.server.service; import java.io.IOException; import java.util.List; import org.junit.After; import org.junit.Test; import org.slf4j.Logger; import org.slf4j.LoggerFactory; import org.springframework.beans.factory.annotation.Autowired; import org.springframework.cloud.deployer.spi.app.AppInstanceStatus; import org.springframework.cloud.deployer.spi.app.AppStatus; import org.springframework.cloud.deployer.spi.app.DeploymentState; import org.springframework.cloud.skipper.PackageDeleteException; import org.springframework.cloud.skipper.ReleaseNotFoundException; import org.springframework.cloud.skipper.SkipperException; import org.springframework.cloud.skipper.domain.ConfigValues; import org.springframework.cloud.skipper.domain.Info; import org.springframework.cloud.skipper.domain.InstallProperties; import org.springframework.cloud.skipper.domain.InstallRequest; import org.springframework.cloud.skipper.domain.LogInfo; import org.springframework.cloud.skipper.domain.PackageIdentifier; import org.springframework.cloud.skipper.domain.PackageMetadata; import org.springframework.cloud.skipper.domain.Release; import org.springframework.cloud.skipper.domain.Repository; import org.springframework.cloud.skipper.domain.ScaleRequest; import org.springframework.cloud.skipper.domain.StatusCode; import org.springframework.cloud.skipper.domain.UpgradeProperties; import org.springframework.cloud.skipper.domain.UpgradeRequest; import org.springframework.cloud.skipper.server.AbstractIntegrationTest; import org.springframework.cloud.skipper.server.deployer.DefaultReleaseManager; import org.springframework.cloud.skipper.server.repository.jpa.AppDeployerDataRepository; import org.springframework.cloud.skipper.server.repository.jpa.PackageMetadataRepository; import org.springframework.cloud.skipper.server.repository.jpa.RepositoryRepository; import org.springframework.test.context.ActiveProfiles; import static junit.framework.TestCase.fail; import static org.assertj.core.api.Assertions.assertThat; import static org.assertj.core.api.Assertions.assertThatThrownBy; /** * Tests ReleaseService methods. * @author Mark Pollack * @author Ilayaperumal Gopinathan * @author Glenn Renfro * @author Christian Tzolov */ @ActiveProfiles({"repo-test", "local"}) public class ReleaseServiceTests extends AbstractIntegrationTest { private final Logger logger = LoggerFactory.getLogger(ReleaseServiceTests.class); @Autowired private PackageMetadataRepository packageMetadataRepository; @Autowired private AppDeployerDataRepository appDeployerDataRepository; @Autowired private RepositoryRepository repositoryRepository; @After public void afterTests() { Repository repo = this.repositoryRepository.findByName("test"); repo.setLocal(false); this.repositoryRepository.save(repo); } @Test public void testBadArguments() { assertThatThrownBy(() -> releaseService.install(123L, new InstallProperties())) .isInstanceOf(SkipperException.class) .hasMessageContaining("can not be found"); assertThatThrownBy(() -> releaseService.install(123L, null)) .isInstanceOf(IllegalArgumentException.class) .hasMessageContaining("Deploy properties can not be null"); assertThatThrownBy(() -> releaseService.install((Long) null, new InstallProperties())) .isInstanceOf(IllegalArgumentException.class) .hasMessageContaining("Package id can not be null"); assertThatThrownBy(() -> releaseService.delete(null)) .isInstanceOf(IllegalArgumentException.class); } @Test public void testInstallAndUpdatePackageNotFound() throws InterruptedException { String releaseName = "logrelease"; InstallRequest installRequest = new InstallRequest(); installRequest.setInstallProperties(createInstallProperties(releaseName)); PackageIdentifier packageIdentifier = new PackageIdentifier(); packageIdentifier.setPackageName("log"); packageIdentifier.setPackageVersion("1.0.0"); installRequest.setPackageIdentifier(packageIdentifier); Release release = install(installRequest); installRequest.setPackageIdentifier(packageIdentifier); assertThat(release).isNotNull(); assertThat(release.getPkg().getMetadata().getVersion()).isEqualTo("1.0.0"); Info info = this.releaseService.status(releaseName); assertThat(info).isNotNull(); UpgradeProperties upgradeProperties = new UpgradeProperties(); upgradeProperties.setReleaseName(releaseName); UpgradeRequest upgradeRequest = new UpgradeRequest(); upgradeRequest.setUpgradeProperties(upgradeProperties); packageIdentifier = new PackageIdentifier(); String packageName = "random"; String packageVersion = "1.0.0"; packageIdentifier.setPackageName(packageName); packageIdentifier.setPackageVersion(packageVersion); upgradeRequest.setPackageIdentifier(packageIdentifier); try { upgrade(upgradeRequest); fail("Expected to throw SkipperException"); } catch (SkipperException e) { assertThat(e.getMessage()).isEqualTo(String.format("Can not find package '%s', version '%s'", packageName, packageVersion)); } delete(release.getName()); } @Test public void testStatus() throws InterruptedException, IOException { String releaseName = "logrelease"; InstallRequest installRequest = new InstallRequest(); installRequest.setInstallProperties(createInstallProperties(releaseName)); PackageIdentifier packageIdentifier = new PackageIdentifier(); packageIdentifier.setPackageName("log"); packageIdentifier.setPackageVersion("1.0.0"); installRequest.setPackageIdentifier(packageIdentifier); Release release = install(installRequest); installRequest.setPackageIdentifier(packageIdentifier); assertThat(release).isNotNull(); assertThat(release.getPkg().getMetadata().getVersion()).isEqualTo("1.0.0"); Info info = this.releaseService.status(releaseName); assertThat(info).isNotNull(); List<AppStatus> appStatuses = info.getStatus().getAppStatusList(); assertThat(appStatuses).isNotNull(); assertThat(appStatuses.size()).isEqualTo(1); AppStatus appStatus = appStatuses.iterator().next(); assertThat(appStatus.getDeploymentId()).isEqualTo("logrelease.log-v1"); assertThat(appStatus.getState()).isEqualTo(DeploymentState.deployed); assertThat(appStatus.getInstances().size()).isEqualTo(1); AppInstanceStatus appInstanceState = appStatus.getInstances().values().iterator().next(); assertThat(appInstanceState.getAttributes().get(DefaultReleaseManager.SKIPPER_RELEASE_NAME_ATTRIBUTE)).isEqualTo("logrelease"); assertThat(appInstanceState.getAttributes().get(DefaultReleaseManager.SKIPPER_RELEASE_VERSION_ATTRIBUTE)).isEqualTo("1"); assertThat(appInstanceState.getAttributes().get(DefaultReleaseManager.SKIPPER_APPLICATION_NAME_ATTRIBUTE)).isEqualTo("log"); } @Test public void testLogs() throws InterruptedException { String releaseName = "myapp-release"; InstallRequest installRequest = new InstallRequest(); installRequest.setInstallProperties(createInstallProperties(releaseName)); PackageIdentifier packageIdentifier = new PackageIdentifier(); packageIdentifier.setPackageName("log"); packageIdentifier.setPackageVersion("1.0.0"); installRequest.setPackageIdentifier(packageIdentifier); Release release = install(installRequest); installRequest.setPackageIdentifier(packageIdentifier); assertThat(release).isNotNull(); assertThat(release.getPkg().getMetadata().getVersion()).isEqualTo("1.0.0"); LogInfo logContent = this.releaseService.getLog(releaseName); assertThat(logContent).isNotNull(); } @Test public void testLogsByNonExistingRelease() { try { this.releaseService.getLog("invalid"); fail(); } catch (ReleaseNotFoundException e) { assertThat(e.getMessage()).isEqualTo("Release with the name [invalid] doesn't exist"); } } @Test public void testScaleByNonExistingRelease() { try { this.releaseService.scale("invalid", new ScaleRequest()); fail(); } catch (ReleaseNotFoundException e) { assertThat(e.getMessage()).isEqualTo("Release with the name [invalid] doesn't exist"); } } @Test public void testInstallByLatestPackage() throws InterruptedException { InstallRequest installRequest = new InstallRequest(); installRequest.setInstallProperties(createInstallProperties("latestPackage")); PackageIdentifier packageIdentifier = new PackageIdentifier(); packageIdentifier.setPackageName("log"); installRequest.setPackageIdentifier(packageIdentifier); Release release = install(installRequest); assertThat(release).isNotNull(); assertThat(release.getPkg().getMetadata().getVersion()).isEqualTo("2.0.0"); delete(release.getName()); } @Test(expected = ReleaseNotFoundException.class) public void testStatusReleaseDoesNotExist() { releaseService.status("notexist"); } @Test public void testPackageNotFound() { boolean exceptionFired = false; try { this.packageMetadataRepository.findByNameAndOptionalVersionRequired("random", "1.2.4"); } catch (SkipperException se) { assertThat(se.getMessage()).isEqualTo("Can not find package 'random', version '1.2.4'"); exceptionFired = true; } assertThat(exceptionFired).isTrue(); } @Test public void testInstallPackageNotFound() { InstallRequest installRequest = new InstallRequest(); installRequest.setInstallProperties(createInstallProperties("latestPackage")); PackageIdentifier packageIdentifier = new PackageIdentifier(); packageIdentifier.setPackageName("random"); installRequest.setPackageIdentifier(packageIdentifier); try { releaseService.install(installRequest); fail("SkipperException is expected for non existing package"); } catch (Exception se) { assertThat(se.getMessage()).isEqualTo("Can not find a package named 'random'"); } } @Test public void testLatestPackageByName() { String packageName = "log"; PackageMetadata packageMetadata = this.packageMetadataRepository.findFirstByNameOrderByVersionDesc(packageName); PackageMetadata latestPackageMetadata = this.packageMetadataRepository .findByNameAndOptionalVersionRequired(packageName, null); assertThat(packageMetadata).isEqualTo(latestPackageMetadata); } @Test public void testInstallReleaseThatIsNotDeleted() throws InterruptedException { String releaseName = "installDeployedRelease"; InstallRequest installRequest = new InstallRequest(); installRequest.setInstallProperties(createInstallProperties(releaseName)); PackageIdentifier packageIdentifier = new PackageIdentifier(); packageIdentifier.setPackageName("log"); packageIdentifier.setPackageVersion("1.0.0"); installRequest.setPackageIdentifier(packageIdentifier); Release release = install(installRequest); assertThat(release).isNotNull(); // Now let's install it a second time. try { install(installRequest); fail("Expected to fail when installing already deployed release."); } catch (SkipperException e) { assertThat(e.getMessage()).isEqualTo("Release with the name [" + releaseName + "] already exists " + "and it is not deleted."); } } @Test public void testInstallDeletedRelease() throws InterruptedException { String releaseName = "deletedRelease"; InstallRequest installRequest = new InstallRequest(); installRequest.setInstallProperties(createInstallProperties(releaseName)); PackageIdentifier packageIdentifier = new PackageIdentifier(); packageIdentifier.setPackageName("log"); packageIdentifier.setPackageVersion("1.0.0"); installRequest.setPackageIdentifier(packageIdentifier); // Install Release release = install(installRequest); assertThat(release).isNotNull(); // Delete delete(releaseName); // Install again Release release2 = install(installRequest); assertThat(release2.getVersion()).isEqualTo(2); } @Test public void testDeletedReleaseWithPackage() throws InterruptedException { // Make the test repo Local Repository repo = this.repositoryRepository.findByName("test"); repo.setLocal(true); this.repositoryRepository.save(repo); String releaseName = "deletedRelease"; InstallRequest installRequest = new InstallRequest(); installRequest.setInstallProperties(createInstallProperties(releaseName)); PackageIdentifier packageIdentifier = new PackageIdentifier(); packageIdentifier.setPackageName("log"); packageIdentifier.setPackageVersion("1.0.0"); installRequest.setPackageIdentifier(packageIdentifier); List<PackageMetadata> releasePackage = this.packageMetadataRepository.findByNameAndVersionOrderByApiVersionDesc( packageIdentifier.getPackageName(), packageIdentifier.getPackageVersion()); assertThat(releasePackage).isNotNull(); assertThat(releasePackage.size()).isEqualTo(1); assertThat(this.packageMetadataRepository.findByName(packageIdentifier.getPackageName()).size()).isEqualTo(3); // Install Release release = install(installRequest); assertThat(release).isNotNull(); // Delete delete(releaseName, true); assertThat(this.packageMetadataRepository.findByName(packageIdentifier.getPackageName()).size()).isEqualTo(0); } @Test public void testDeletedReleaseWithPackageNonLocalRepo() throws InterruptedException { // Make the test repo Non-local Repository repo = this.repositoryRepository.findByName("test"); repo.setLocal(false); this.repositoryRepository.save(repo); String releaseName = "deletedRelease"; InstallRequest installRequest = new InstallRequest(); installRequest.setInstallProperties(createInstallProperties(releaseName)); PackageIdentifier packageIdentifier = new PackageIdentifier(); packageIdentifier.setPackageName("log"); packageIdentifier.setPackageVersion("1.0.0"); installRequest.setPackageIdentifier(packageIdentifier); assertThat(this.packageMetadataRepository.findByName(packageIdentifier.getPackageName()).size()).isEqualTo(3); // Install Release release = install(installRequest); assertThat(release).isNotNull(); assertReleaseStatus(releaseName, StatusCode.DEPLOYED); // Delete attempt try { delete(releaseName, true); fail("Packages from non-local repositories can't be deleted"); } catch (SkipperException se) { } assertReleaseStatus(releaseName, StatusCode.DEPLOYED); assertThat(this.packageMetadataRepository.findByName(packageIdentifier.getPackageName()).size()).isEqualTo(3); } @Test public void testInstallDeleteOfdMultipleReleasesFromSingePackage() throws InterruptedException { Repository repo = this.repositoryRepository.findByName("test"); repo.setLocal(true); this.repositoryRepository.save(repo); boolean DELETE_RELEASE_PACKAGE = true; String RELEASE_ONE = "RELEASE_ONE"; String RELEASE_TWO = "RELEASE_TWO"; String RELEASE_THREE = "RELEASE_THREE"; // 3 versions of package "log" exists PackageIdentifier logPackageIdentifier = new PackageIdentifier(); logPackageIdentifier.setPackageName("log"); logPackageIdentifier.setPackageVersion("1.0.0"); List<PackageMetadata> releasePackage = this.packageMetadataRepository.findByNameAndVersionOrderByApiVersionDesc( logPackageIdentifier.getPackageName(), logPackageIdentifier.getPackageVersion()); assertThat(releasePackage).isNotNull(); assertThat(releasePackage.size()).isEqualTo(1); assertThat(this.packageMetadataRepository.findByName(logPackageIdentifier.getPackageName()).size()).isEqualTo(3); // Install 2 releases (RELEASE_ONE, RELEASE_TWO) from the same "log" package install(RELEASE_ONE, logPackageIdentifier); install(RELEASE_TWO, logPackageIdentifier); assertReleaseStatus(RELEASE_ONE, StatusCode.DEPLOYED); assertReleaseStatus(RELEASE_TWO, StatusCode.DEPLOYED); // Attempt to delete release one together with its package try { delete(RELEASE_ONE, DELETE_RELEASE_PACKAGE); fail("Attempt to delete a package with other deployed releases should fail"); } catch (PackageDeleteException se) { assertThat(se.getMessage()).isEqualTo("Can not delete Package Metadata [log:1.0.0] in Repository [test]. " + "Not all releases of this package have the status DELETED. Active Releases [RELEASE_TWO]"); } // Verify that neither the releases nor the package have been deleted assertReleaseStatus(RELEASE_ONE, StatusCode.DEPLOYED); assertReleaseStatus(RELEASE_TWO, StatusCode.DEPLOYED); assertThat(this.packageMetadataRepository.findByName(logPackageIdentifier.getPackageName()).size()).isEqualTo(3); // Install a third release (RELEASE_THREE) from the same package (log) install(RELEASE_THREE, logPackageIdentifier); assertReleaseStatus(RELEASE_THREE, StatusCode.DEPLOYED); // Attempt to delete release one together with its package try { delete(RELEASE_ONE, DELETE_RELEASE_PACKAGE); fail("Attempt to delete a package with other deployed releases must fail."); } catch (PackageDeleteException se) { assertThat(se.getMessage()).isEqualTo("Can not delete Package Metadata [log:1.0.0] in Repository [test]. " + "Not all releases of this package have the status DELETED. Active Releases [RELEASE_THREE,RELEASE_TWO]"); } // Verify that nothing has been deleted assertReleaseStatus(RELEASE_ONE, StatusCode.DEPLOYED); assertReleaseStatus(RELEASE_TWO, StatusCode.DEPLOYED); assertReleaseStatus(RELEASE_THREE, StatusCode.DEPLOYED); assertThat(this.packageMetadataRepository.findByName(logPackageIdentifier.getPackageName()).size()).isEqualTo(3); // Delete releases two and three without without deleting their package. delete(RELEASE_TWO, !DELETE_RELEASE_PACKAGE); delete(RELEASE_THREE, !DELETE_RELEASE_PACKAGE); // Release One is still deployed assertReleaseStatus(RELEASE_ONE, StatusCode.DEPLOYED); // Releases Two and Three were undeployed assertReleaseStatus(RELEASE_TWO, StatusCode.DELETED); assertReleaseStatus(RELEASE_THREE, StatusCode.DELETED); // Package "log" still has 3 registered versions assertThat(this.packageMetadataRepository.findByName(logPackageIdentifier.getPackageName()).size()).isEqualTo(3); // Attempt to delete release one together with its package delete(RELEASE_ONE, DELETE_RELEASE_PACKAGE); // Successful deletion of release and its package. assertReleaseStatus(RELEASE_ONE, StatusCode.DELETED); assertThat(this.packageMetadataRepository.findByName(logPackageIdentifier.getPackageName()).size()).isEqualTo(0); } private Release install(String releaseName, PackageIdentifier packageIdentifier) throws InterruptedException { InstallRequest installRequest = new InstallRequest(); installRequest.setPackageIdentifier(packageIdentifier); installRequest.setInstallProperties(createInstallProperties(releaseName)); Release release = install(installRequest); assertThat(release).isNotNull(); return release; } private void assertReleaseStatus(String releaseName, StatusCode expectedStatusCode) { assertThat(this.releaseRepository.findByNameIgnoreCaseContaining(releaseName).size()).isEqualTo(1); assertThat(this.releaseRepository.findByNameIgnoreCaseContaining(releaseName).iterator().next() .getInfo().getStatus().getStatusCode()).isEqualTo(expectedStatusCode); } @Test public void testRollbackDeletedRelease() throws InterruptedException { String releaseName = "rollbackDeletedRelease"; InstallRequest installRequest = new InstallRequest(); InstallProperties installProperties = createInstallProperties(releaseName); ConfigValues installConfig = new ConfigValues(); installConfig.setRaw("log:\n version: 1.2.0.RC1\ntime:\n version: 1.2.0.RC1\n"); installProperties.setConfigValues(installConfig); installRequest.setInstallProperties(installProperties); PackageIdentifier packageIdentifier = new PackageIdentifier(); packageIdentifier.setPackageName("log"); packageIdentifier.setPackageVersion("1.0.0"); installRequest.setPackageIdentifier(packageIdentifier); // Install logger.info("Installing log 1.0.0 package"); Release release = install(installRequest); assertThat(release).isNotNull(); assertThat(release.getVersion()).isEqualTo(1); this.appDeployerDataRepository.findByReleaseNameAndReleaseVersionRequired(releaseName, 1); // Upgrade UpgradeProperties upgradeProperties = new UpgradeProperties(); upgradeProperties.setReleaseName(releaseName); ConfigValues upgradeConfig = new ConfigValues(); upgradeConfig.setRaw("log:\n version: 1.2.0.RELEASE\ntime:\n version: 1.2.0.RELEASE\n"); upgradeProperties.setConfigValues(upgradeConfig); UpgradeRequest upgradeRequest = new UpgradeRequest(); upgradeRequest.setUpgradeProperties(upgradeProperties); packageIdentifier = new PackageIdentifier(); String packageName = "log"; String packageVersion = "2.0.0"; packageIdentifier.setPackageName(packageName); packageIdentifier.setPackageVersion(packageVersion); upgradeRequest.setPackageIdentifier(packageIdentifier); logger.info("Upgrading to log 2.0.0 package"); Release upgradedRelease = upgrade(upgradeRequest); assertThat(upgradedRelease.getVersion()).isEqualTo(2); assertThat(upgradedRelease.getConfigValues().getRaw()).isEqualTo(upgradeRequest.getUpgradeProperties().getConfigValues().getRaw()); this.appDeployerDataRepository.findByReleaseNameAndReleaseVersionRequired(releaseName, 2); // Delete delete(releaseName); Release deletedRelease = releaseRepository.findByNameAndVersion(releaseName, 2); assertThat(deletedRelease.getInfo().getStatus().getStatusCode().equals(StatusCode.DELETED)); // Rollback logger.info("Rolling back the release " + release); Release rolledBackRelease = rollback(releaseName, 0); assertThat(rolledBackRelease.getManifest()).isEqualTo(release.getManifest()); assertThat(rolledBackRelease.getConfigValues().getRaw()).isEqualTo(release.getConfigValues().getRaw()); assertThat(rolledBackRelease.getInfo().getStatus().getStatusCode().equals(StatusCode.DEPLOYED)); deletedRelease = releaseRepository.findByNameAndVersion(releaseName, 2); assertThat(deletedRelease.getInfo().getStatus().getStatusCode().equals(StatusCode.DELETED)); delete(rolledBackRelease.getName()); } }