diff --git a/ahoy-components/pom.xml b/ahoy-components/pom.xml index c967e6f3..9115a69a 100644 --- a/ahoy-components/pom.xml +++ b/ahoy-components/pom.xml @@ -22,7 +22,7 @@ za.co.lsd.ahoy ahoy - 0.14.0 + 0.15.0 ahoy-components diff --git a/ahoy-components/src/main/java/za/co/lsd/ahoy/server/ReleaseController.java b/ahoy-components/src/main/java/za/co/lsd/ahoy/server/ReleaseController.java index 0e827191..0a477839 100644 --- a/ahoy-components/src/main/java/za/co/lsd/ahoy/server/ReleaseController.java +++ b/ahoy-components/src/main/java/za/co/lsd/ahoy/server/ReleaseController.java @@ -114,10 +114,11 @@ public ResponseEntity copyApplicationVersionEnvConfig(@PathVariable Long r return new ResponseEntity<>(null, new HttpHeaders(), HttpStatus.OK); } - @PostMapping("/releases/{releaseId}/duplicate") - public ResponseEntity duplicate(@PathVariable Long releaseId, @RequestBody DuplicateOptions duplicateOptions) { + @PostMapping("/releases/duplicate/{sourceReleaseId}/{destReleaseId}") + public ResponseEntity duplicate(@PathVariable Long sourceReleaseId, @PathVariable Long destReleaseId, + @RequestBody DuplicateOptions duplicateOptions) { - Release duplicatedRelease = releaseService.duplicate(releaseId, duplicateOptions); + Release duplicatedRelease = releaseService.duplicate(sourceReleaseId, destReleaseId, duplicateOptions); return new ResponseEntity<>(duplicatedRelease, new HttpHeaders(), HttpStatus.OK); } diff --git a/ahoy-components/src/main/java/za/co/lsd/ahoy/server/ReleaseService.java b/ahoy-components/src/main/java/za/co/lsd/ahoy/server/ReleaseService.java index 07be22db..ec970d64 100644 --- a/ahoy-components/src/main/java/za/co/lsd/ahoy/server/ReleaseService.java +++ b/ahoy-components/src/main/java/za/co/lsd/ahoy/server/ReleaseService.java @@ -228,16 +228,17 @@ public ReleaseVersion upgrade(Long releaseVersionId, UpgradeOptions upgradeOptio } @Transactional - public Release duplicate(Long releaseId, DuplicateOptions duplicateOptions) { - Release sourceRelease = releaseRepository.findById(releaseId) - .orElseThrow(() -> new ResourceNotFoundException("Could not find source release: " + releaseId)); + public Release duplicate(Long sourceReleaseId, Long destReleaseId, DuplicateOptions duplicateOptions) { + Release sourceRelease = releaseRepository.findById(sourceReleaseId) + .orElseThrow(() -> new ResourceNotFoundException("Could not find source release: " + sourceReleaseId)); - Release duplicatedRelease = releaseRepository.save(new Release(duplicateOptions.getReleaseName())); - log.debug("Duplicated release: {} for source release: {}", duplicatedRelease, sourceRelease); + Release destRelease = releaseRepository.findById(destReleaseId) + .orElseThrow(() -> new ResourceNotFoundException("Could not find destination release: " + sourceReleaseId)); + log.debug("Duplicating release: {} to release: {} with options: {}", sourceRelease, destRelease, duplicateOptions); for (ReleaseVersion sourceReleaseVersion : sourceRelease.getReleaseVersions()) { ReleaseVersion duplicatedReleaseVersion = new ReleaseVersion(sourceReleaseVersion.getVersion()); - duplicatedRelease.addReleaseVersion(duplicatedReleaseVersion); + destRelease.addReleaseVersion(duplicatedReleaseVersion); duplicatedReleaseVersion.setApplicationVersions(new ArrayList<>(sourceReleaseVersion.getApplicationVersions())); duplicatedReleaseVersion = releaseVersionRepository.save(duplicatedReleaseVersion); log.debug("Duplicated release version: {} for source release version: {}", duplicatedReleaseVersion, sourceReleaseVersion); @@ -245,12 +246,12 @@ public Release duplicate(Long releaseId, DuplicateOptions duplicateOptions) { if (duplicateOptions.isAddToSameEnvironments()) { for (EnvironmentRelease sourceEnvRelease : sourceRelease.getEnvironmentReleases()) { - EnvironmentRelease duplicatedEnvRelease = new EnvironmentRelease(sourceEnvRelease.getEnvironment(), duplicatedRelease); + EnvironmentRelease duplicatedEnvRelease = new EnvironmentRelease(sourceEnvRelease.getEnvironment(), destRelease); duplicatedEnvRelease = environmentReleaseRepository.save(duplicatedEnvRelease); log.debug("Duplicated environment release: {} for source environment release: {}", duplicatedEnvRelease.getId(), sourceEnvRelease.getId()); if (duplicateOptions.isCopyEnvironmentConfig()) { - for (ReleaseVersion destReleaseVersion : duplicatedRelease.getReleaseVersions()) { + for (ReleaseVersion destReleaseVersion : destRelease.getReleaseVersions()) { ReleaseVersion sourceReleaseVersion = sourceRelease.getReleaseVersions().stream() .filter(rv -> rv.getVersion().equals(destReleaseVersion.getVersion())) .findFirst() @@ -265,7 +266,7 @@ public Release duplicate(Long releaseId, DuplicateOptions duplicateOptions) { } } - return duplicatedRelease; + return destRelease; } @Transactional @@ -347,21 +348,30 @@ public Flux getLogs(EnvironmentReleaseId environmentReleaseId, /** * Copies environment config from one release version to another for the same environment release. */ - private void copyEnvironmentConfig(EnvironmentRelease environmentRelease, ReleaseVersion sourceReleaseVersion, ReleaseVersion destReleaseVersion) { - this.copyEnvironmentConfig(environmentRelease, sourceReleaseVersion, environmentRelease, destReleaseVersion); + public void copyEnvironmentConfig(EnvironmentRelease environmentRelease, ReleaseVersion sourceReleaseVersion, ReleaseVersion destReleaseVersion) { + copyEnvironmentConfig(environmentRelease, sourceReleaseVersion, environmentRelease, destReleaseVersion); + } + + /** + * Copies environment config from one environment release to another for all its release versions. Usually used when duplicating and environment with all its releases. + */ + public void copyEnvironmentConfig(EnvironmentRelease sourceEnvironmentRelease, EnvironmentRelease destEnvironmentRelease) { + for (ReleaseVersion releaseVersion : sourceEnvironmentRelease.getRelease().getReleaseVersions()) { + copyEnvironmentConfig(sourceEnvironmentRelease, destEnvironmentRelease, releaseVersion); + } } /** * Copies environment config from one environment release to another for the same version. */ - private void copyEnvironmentConfig(EnvironmentRelease sourceEnvironmentRelease, EnvironmentRelease destEnvironmentRelease, ReleaseVersion releaseVersion) { - this.copyEnvironmentConfig(sourceEnvironmentRelease, releaseVersion, destEnvironmentRelease, releaseVersion); + public void copyEnvironmentConfig(EnvironmentRelease sourceEnvironmentRelease, EnvironmentRelease destEnvironmentRelease, ReleaseVersion releaseVersion) { + copyEnvironmentConfig(sourceEnvironmentRelease, releaseVersion, destEnvironmentRelease, releaseVersion); } /** * Copies environment config from one environment release version to another environment release version. */ - private void copyEnvironmentConfig(EnvironmentRelease sourceEnvironmentRelease, ReleaseVersion sourceReleaseVersion, EnvironmentRelease destEnvironmentRelease, ReleaseVersion destReleaseVersion) { + public void copyEnvironmentConfig(EnvironmentRelease sourceEnvironmentRelease, ReleaseVersion sourceReleaseVersion, EnvironmentRelease destEnvironmentRelease, ReleaseVersion destReleaseVersion) { for (ApplicationVersion applicationVersion : destReleaseVersion.getApplicationVersions()) { copyEnvironmentConfig(sourceEnvironmentRelease, sourceReleaseVersion, applicationVersion, destEnvironmentRelease, destReleaseVersion, applicationVersion); } @@ -370,7 +380,7 @@ private void copyEnvironmentConfig(EnvironmentRelease sourceEnvironmentRelease, /** * Copies environment config from one application version to another for the same release version and environment. */ - private void copyEnvironmentConfig(EnvironmentRelease environmentRelease, ReleaseVersion releaseVersion, ApplicationVersion sourceApplicationVersion, ApplicationVersion destApplicationVersion) { + public void copyEnvironmentConfig(EnvironmentRelease environmentRelease, ReleaseVersion releaseVersion, ApplicationVersion sourceApplicationVersion, ApplicationVersion destApplicationVersion) { copyEnvironmentConfig(environmentRelease, releaseVersion, sourceApplicationVersion, environmentRelease, releaseVersion, destApplicationVersion); } diff --git a/ahoy-components/src/main/java/za/co/lsd/ahoy/server/applications/ApplicationEnvironmentConfig.java b/ahoy-components/src/main/java/za/co/lsd/ahoy/server/applications/ApplicationEnvironmentConfig.java index a27ecc65..1a885a32 100644 --- a/ahoy-components/src/main/java/za/co/lsd/ahoy/server/applications/ApplicationEnvironmentConfig.java +++ b/ahoy-components/src/main/java/za/co/lsd/ahoy/server/applications/ApplicationEnvironmentConfig.java @@ -60,7 +60,7 @@ public boolean hasReplicas() { } public boolean routeEnabled() { - return spec != null && spec.getRouteEnabled() != null && spec.getRouteEnabled(); + return spec != null && spec.isRouteEnabled(); } public boolean hasRoute() { @@ -68,7 +68,7 @@ public boolean hasRoute() { } public boolean environmentVariablesEnabled() { - return spec != null && spec.getEnvironmentVariablesEnabled() != null && spec.getEnvironmentVariablesEnabled(); + return spec != null && spec.isEnvironmentVariablesEnabled(); } public boolean hasEnvironmentVariables() { @@ -76,7 +76,7 @@ public boolean hasEnvironmentVariables() { } public boolean configEnabled() { - return spec != null && spec.getConfigFilesEnabled() != null && spec.getConfigFilesEnabled(); + return spec != null && spec.isConfigFilesEnabled(); } public boolean hasConfigs() { @@ -84,7 +84,7 @@ public boolean hasConfigs() { } public boolean volumesEnabled() { - return spec != null && spec.getVolumesEnabled() != null && spec.getVolumesEnabled(); + return spec != null && spec.isVolumesEnabled(); } public boolean hasVolumes() { @@ -92,7 +92,7 @@ public boolean hasVolumes() { } public boolean secretsEnabled() { - return spec != null && spec.getSecretsEnabled() != null && spec.getSecretsEnabled(); + return spec != null && spec.isSecretsEnabled(); } public boolean hasSecrets() { @@ -100,7 +100,7 @@ public boolean hasSecrets() { } public boolean resourcesEnabled() { - return spec != null && spec.getResourcesEnabled() != null && spec.getResourcesEnabled(); + return spec != null && spec.isResourcesEnabled(); } public boolean hasResources() { @@ -108,7 +108,7 @@ public boolean hasResources() { } public ApplicationEnvironmentSpec summarySpec() { - return ApplicationEnvironmentSpec.newSummarySpec(spec.getRouteEnabled(), spec.getRouteHostname()); + return ApplicationEnvironmentSpec.newSummarySpec(spec.isRouteEnabled(), spec.getRouteHostname()); } @Override diff --git a/ahoy-components/src/main/java/za/co/lsd/ahoy/server/applications/ApplicationEnvironmentSpec.java b/ahoy-components/src/main/java/za/co/lsd/ahoy/server/applications/ApplicationEnvironmentSpec.java index b17755e9..3903abd2 100644 --- a/ahoy-components/src/main/java/za/co/lsd/ahoy/server/applications/ApplicationEnvironmentSpec.java +++ b/ahoy-components/src/main/java/za/co/lsd/ahoy/server/applications/ApplicationEnvironmentSpec.java @@ -26,25 +26,25 @@ public class ApplicationEnvironmentSpec { private Integer replicas; - private Boolean routeEnabled; + private boolean routeEnabled; private String routeHostname; private Integer routeTargetPort; private boolean tls; private String tlsSecretName; - private Boolean environmentVariablesEnabled; + private boolean environmentVariablesEnabled; private List environmentVariables; - private Boolean configFilesEnabled; + private boolean configFilesEnabled; private List configFiles; - private Boolean volumesEnabled; + private boolean volumesEnabled; private List volumes; - private Boolean secretsEnabled; + private boolean secretsEnabled; private List secrets; - private Boolean resourcesEnabled; + private boolean resourcesEnabled; private ApplicationResources resources; public static ApplicationEnvironmentSpec newSummarySpec(Boolean routeEnabled, String routeHostname) { diff --git a/ahoy-components/src/main/java/za/co/lsd/ahoy/server/applications/ApplicationSpec.java b/ahoy-components/src/main/java/za/co/lsd/ahoy/server/applications/ApplicationSpec.java index 23fd8e52..a680a292 100644 --- a/ahoy-components/src/main/java/za/co/lsd/ahoy/server/applications/ApplicationSpec.java +++ b/ahoy-components/src/main/java/za/co/lsd/ahoy/server/applications/ApplicationSpec.java @@ -31,14 +31,14 @@ public class ApplicationSpec extends ContainerSpec { private String dockerRegistryName; - private Boolean configFilesEnabled; + private boolean configFilesEnabled; private String configPath; private List configFiles; - private Boolean volumesEnabled; + private boolean volumesEnabled; private List volumes; - private Boolean secretsEnabled; + private boolean secretsEnabled; private List secrets; private List containers = new ArrayList<>(); @@ -49,26 +49,14 @@ public ApplicationSpec(String name, String image, String dockerRegistryName) { this.dockerRegistryName = dockerRegistryName; } - public boolean configEnabled() { - return configFilesEnabled != null && configFilesEnabled; - } - public boolean hasConfigs() { return configFiles != null && configFiles.size() > 0; } - public boolean volumesEnabled() { - return volumesEnabled != null && volumesEnabled; - } - public boolean hasVolumes() { return volumes != null && volumes.size() > 0; } - public boolean secretsEnabled() { - return secretsEnabled != null && secretsEnabled; - } - public boolean hasSecrets() { return secrets != null && secrets.size() > 0; } diff --git a/ahoy-components/src/main/java/za/co/lsd/ahoy/server/docker/DockerRegistry.java b/ahoy-components/src/main/java/za/co/lsd/ahoy/server/docker/DockerRegistry.java index 2cd5ffb3..4dad1e6a 100644 --- a/ahoy-components/src/main/java/za/co/lsd/ahoy/server/docker/DockerRegistry.java +++ b/ahoy-components/src/main/java/za/co/lsd/ahoy/server/docker/DockerRegistry.java @@ -30,7 +30,7 @@ public class DockerRegistry { @ToString.Exclude private String password; @ToString.Exclude - private Boolean secure; + private boolean secure; public DockerRegistry(String name, String server, String username, String password) { this.name = name; diff --git a/ahoy-components/src/main/java/za/co/lsd/ahoy/server/environments/DuplicateOptions.java b/ahoy-components/src/main/java/za/co/lsd/ahoy/server/environments/DuplicateOptions.java new file mode 100644 index 00000000..949ab5ed --- /dev/null +++ b/ahoy-components/src/main/java/za/co/lsd/ahoy/server/environments/DuplicateOptions.java @@ -0,0 +1,28 @@ +/* + * Copyright 2022 LSD Information Technology (Pty) Ltd + * + * 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 + * + * http://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 za.co.lsd.ahoy.server.environments; + +import lombok.AllArgsConstructor; +import lombok.Data; +import lombok.NoArgsConstructor; + +@Data +@NoArgsConstructor +@AllArgsConstructor +public class DuplicateOptions { + private boolean copyEnvironmentConfig; +} diff --git a/ahoy-components/src/main/java/za/co/lsd/ahoy/server/environments/Environment.java b/ahoy-components/src/main/java/za/co/lsd/ahoy/server/environments/Environment.java index b344eadd..9b9c39c9 100644 --- a/ahoy-components/src/main/java/za/co/lsd/ahoy/server/environments/Environment.java +++ b/ahoy-components/src/main/java/za/co/lsd/ahoy/server/environments/Environment.java @@ -24,6 +24,7 @@ import org.hibernate.Hibernate; import za.co.lsd.ahoy.server.cluster.Cluster; import za.co.lsd.ahoy.server.environmentrelease.EnvironmentRelease; +import za.co.lsd.ahoy.server.releases.ReleaseHistory; import javax.persistence.*; import javax.validation.constraints.NotNull; @@ -61,6 +62,12 @@ public class Environment implements Serializable { @ToString.Exclude private List environmentReleases = new ArrayList<>(); + @OneToMany(mappedBy = "environment", cascade = CascadeType.REMOVE, orphanRemoval = true) + @OrderBy("id") + @JsonIgnore + @ToString.Exclude + private List releaseHistories = new ArrayList<>(); + public Environment(@NotNull String name) { this.name = name; } diff --git a/ahoy-components/src/main/java/za/co/lsd/ahoy/server/environments/EnvironmentService.java b/ahoy-components/src/main/java/za/co/lsd/ahoy/server/environments/EnvironmentService.java index 26d7fbd5..25da9c34 100644 --- a/ahoy-components/src/main/java/za/co/lsd/ahoy/server/environments/EnvironmentService.java +++ b/ahoy-components/src/main/java/za/co/lsd/ahoy/server/environments/EnvironmentService.java @@ -69,20 +69,24 @@ public Environment destroy(Environment environment) { } @Transactional - public Environment duplicate(Long sourceEnvironmentId, Long destEnvironmentId) { + public Environment duplicate(Long sourceEnvironmentId, Long destEnvironmentId, DuplicateOptions duplicateOptions) { Environment sourceEnvironment = environmentRepository.findById(sourceEnvironmentId) .orElseThrow(() -> new ResourceNotFoundException("Could not find source environment: " + sourceEnvironmentId)); Environment destEnvironment = environmentRepository.findById(destEnvironmentId) .orElseThrow(() -> new ResourceNotFoundException("Could not find destination environment: " + destEnvironmentId)); - log.info("Duplicating environment {} to environment {}", sourceEnvironment, destEnvironment); + log.info("Duplicating environment {} to environment {} with options {}", sourceEnvironment, destEnvironment, duplicateOptions); List sourceEnvironmentReleases = sourceEnvironment.getEnvironmentReleases(); for (EnvironmentRelease sourceEnvironmentRelease : sourceEnvironmentReleases) { EnvironmentRelease newEnvironmentRelease = new EnvironmentRelease(destEnvironment, sourceEnvironmentRelease.getRelease()); environmentReleaseRepository.save(newEnvironmentRelease); + + if (duplicateOptions.isCopyEnvironmentConfig()) { + releaseService.copyEnvironmentConfig(sourceEnvironmentRelease, newEnvironmentRelease); + } } return destEnvironment; } diff --git a/ahoy-components/src/main/java/za/co/lsd/ahoy/server/environments/EnvironmentsController.java b/ahoy-components/src/main/java/za/co/lsd/ahoy/server/environments/EnvironmentsController.java index 81821470..88202abb 100644 --- a/ahoy-components/src/main/java/za/co/lsd/ahoy/server/environments/EnvironmentsController.java +++ b/ahoy-components/src/main/java/za/co/lsd/ahoy/server/environments/EnvironmentsController.java @@ -45,15 +45,17 @@ public ResponseEntity destroy(@PathVariable Long environmentId) { } @PostMapping("/duplicate/{sourceEnvironmentId}/{destEnvironmentId}") - public ResponseEntity duplicate(@PathVariable Long sourceEnvironmentId, @PathVariable Long destEnvironmentId) { + public ResponseEntity duplicate(@PathVariable Long sourceEnvironmentId, @PathVariable Long destEnvironmentId, + @RequestBody DuplicateOptions duplicateOptions) { - Environment destEnvironment = environmentService.duplicate(sourceEnvironmentId, destEnvironmentId); + Environment destEnvironment = environmentService.duplicate(sourceEnvironmentId, destEnvironmentId, duplicateOptions); return new ResponseEntity<>(destEnvironment, new HttpHeaders(), HttpStatus.OK); } @PostMapping("/{environmentId}/move") - public ResponseEntity move(@PathVariable Long environmentId, @RequestBody MoveOptions moveOptions) { + public ResponseEntity move(@PathVariable Long environmentId, + @RequestBody MoveOptions moveOptions) { Environment destEnvironment = environmentService.move(environmentId, moveOptions); diff --git a/ahoy-components/src/main/java/za/co/lsd/ahoy/server/helm/TemplateWriter.java b/ahoy-components/src/main/java/za/co/lsd/ahoy/server/helm/TemplateWriter.java index baaab285..b4949d3a 100644 --- a/ahoy-components/src/main/java/za/co/lsd/ahoy/server/helm/TemplateWriter.java +++ b/ahoy-components/src/main/java/za/co/lsd/ahoy/server/helm/TemplateWriter.java @@ -74,23 +74,23 @@ public void writeTemplates(EnvironmentRelease environmentRelease, ReleaseVersion ApplicationSpec applicationSpec = applicationVersion.getSpec(); - if ((applicationSpec.configEnabled() && applicationSpec.hasConfigs()) || + if ((applicationSpec.isConfigFilesEnabled() && applicationSpec.hasConfigs()) || applicationEnvironmentConfig.map(e -> e.configEnabled() && e.hasConfigs()).orElse(false)) { addTemplate(application, "configmap", templatesPath, trackedTemplates); } - if ((applicationSpec.volumesEnabled() && applicationSpec.hasVolumes()) || + if ((applicationSpec.isVolumesEnabled() && applicationSpec.hasVolumes()) || applicationEnvironmentConfig.map(e -> e.volumesEnabled() && e.hasVolumes()).orElse(false)) { addTemplate(application, "pvc", templatesPath, trackedTemplates); } - if ((applicationSpec.secretsEnabled() && applicationSpec.hasSecrets()) || + if ((applicationSpec.isSecretsEnabled() && applicationSpec.hasSecrets()) || applicationEnvironmentConfig.map(e -> e.secretsEnabled() && e.hasSecrets()).orElse(false)) { addTemplate(application, "secret-generic", templatesPath, trackedTemplates); } Optional dockerRegistry = dockerRegistryProvider.dockerRegistryFor(applicationSpec.getDockerRegistryName()); - if (dockerRegistry.isPresent() && dockerRegistry.get().getSecure()) { + if (dockerRegistry.isPresent() && dockerRegistry.get().isSecure()) { addTemplate(application, "secret-dockerconfig", templatesPath, trackedTemplates); } diff --git a/ahoy-components/src/main/java/za/co/lsd/ahoy/server/helm/values/ValuesBuilder.java b/ahoy-components/src/main/java/za/co/lsd/ahoy/server/helm/values/ValuesBuilder.java index a6c12da6..73314ddf 100644 --- a/ahoy-components/src/main/java/za/co/lsd/ahoy/server/helm/values/ValuesBuilder.java +++ b/ahoy-components/src/main/java/za/co/lsd/ahoy/server/helm/values/ValuesBuilder.java @@ -42,6 +42,9 @@ @Component @Slf4j public class ValuesBuilder { + private static final String SECRET_TYPE_GENERIC = "Opaque"; + private static final String SECRET_TYPE_TLS = "kubernetes.io/tls"; + private final DockerRegistryProvider dockerRegistryProvider; private final ApplicationEnvironmentConfigProvider environmentConfigProvider; private final DockerConfigSealedSecretProducer dockerConfigSealedSecretProducer; @@ -115,7 +118,7 @@ private ApplicationValues buildApplicationValues(EnvironmentRelease environmentR buildEnvironmentVariables(containerValuesBuilder, containerSpec, environmentConfig); buildResources(containerValuesBuilder, containerSpec, environmentConfig); - switch(containerSpec.getType()) { + switch (containerSpec.getType()) { case Container: buildHealthChecks(containerValuesBuilder, containerSpec); containerValues.put(containerSpec.getName(), containerValuesBuilder.build()); @@ -133,7 +136,7 @@ private ApplicationValues buildApplicationValues(EnvironmentRelease environmentR private void buildDockerRegistry(ApplicationValues.ApplicationValuesBuilder builder, ApplicationSpec applicationSpec) throws IOException { Optional dockerRegistry = dockerRegistryProvider.dockerRegistryFor(applicationSpec.getDockerRegistryName()); - if (dockerRegistry.isPresent() && dockerRegistry.get().getSecure()) { + if (dockerRegistry.isPresent() && dockerRegistry.get().isSecure()) { builder.dockerConfigJson(dockerConfigSealedSecretProducer.produce(dockerRegistry.get())); } } @@ -185,7 +188,7 @@ private void buildConfigFiles(ApplicationValues.ApplicationValuesBuilder builder Map configFiles = new LinkedHashMap<>(); - if (applicationSpec.configEnabled() && applicationSpec.hasConfigs()) { + if (applicationSpec.isConfigFilesEnabled() && applicationSpec.hasConfigs()) { for (ApplicationConfigFile applicationConfigFile : applicationSpec.getConfigFiles()) { configFiles.put(configName(applicationConfigFile), new ApplicationConfigFileValues(applicationConfigFile)); } @@ -200,7 +203,7 @@ private void buildConfigFiles(ApplicationValues.ApplicationValuesBuilder builder } builder - .configFilesEnabled(applicationSpec.configEnabled() || (environmentConfig != null && environmentConfig.configEnabled())) + .configFilesEnabled(applicationSpec.isConfigFilesEnabled() || (environmentConfig != null && environmentConfig.configEnabled())) .configPath(applicationSpec.getConfigPath()) .configFiles(configFiles) .configFileHashes(hashes(configFiles)); @@ -210,7 +213,7 @@ private void buildVolumes(ApplicationValues.ApplicationValuesBuilder builder, Ap Map volumes = new LinkedHashMap<>(); - if (applicationSpec.volumesEnabled() && applicationSpec.hasVolumes()) { + if (applicationSpec.isVolumesEnabled() && applicationSpec.hasVolumes()) { for (ApplicationVolume applicationVolume : applicationSpec.getVolumes()) { volumes.put(applicationVolume.getName(), new ApplicationVolumeValues(applicationVolume)); } @@ -227,7 +230,7 @@ private void buildVolumes(ApplicationValues.ApplicationValuesBuilder builder, Ap } builder - .volumesEnabled(applicationSpec.volumesEnabled() || (environmentConfig != null && environmentConfig.volumesEnabled())) + .volumesEnabled(applicationSpec.isVolumesEnabled() || (environmentConfig != null && environmentConfig.volumesEnabled())) .volumes(volumes); } @@ -235,7 +238,7 @@ private void buildSecrets(ApplicationValues.ApplicationValuesBuilder builder, Ap Map secrets = new LinkedHashMap<>(); - if (applicationSpec.secretsEnabled() && applicationSpec.hasSecrets()) { + if (applicationSpec.isSecretsEnabled() && applicationSpec.hasSecrets()) { for (ApplicationSecret applicationSecret : applicationSpec.getSecrets()) { Map encryptedData = secretDataSealedSecretProducer.produce(applicationSecret); secrets.put(applicationSecret.getName(), new ApplicationSecretValues(applicationSecret.getName(), secretType(applicationSecret), encryptedData)); @@ -254,7 +257,7 @@ private void buildSecrets(ApplicationValues.ApplicationValuesBuilder builder, Ap } builder - .secretsEnabled(applicationSpec.secretsEnabled() || (environmentConfig != null && environmentConfig.secretsEnabled())) + .secretsEnabled(applicationSpec.isSecretsEnabled() || (environmentConfig != null && environmentConfig.secretsEnabled())) .secrets(secrets); } @@ -322,16 +325,15 @@ private String hashes(Map configFiles) thro private String configName(ApplicationConfigFile configFile) { return "application-config-file-" + Hashing.crc32() - .hashString(configFile.getName(), StandardCharsets.UTF_8) - .toString(); + .hashString(configFile.getName(), StandardCharsets.UTF_8); } private String secretType(ApplicationSecret applicationSecret) { switch (applicationSecret.getType()) { case Generic: - return "Opague"; + return SECRET_TYPE_GENERIC; case Tls: - return "kubernetes.io/tls"; + return SECRET_TYPE_TLS; default: throw new IllegalStateException("Unhandled secret type"); } diff --git a/ahoy-components/src/main/java/za/co/lsd/ahoy/server/releases/DuplicateOptions.java b/ahoy-components/src/main/java/za/co/lsd/ahoy/server/releases/DuplicateOptions.java index 72c34f5d..ffa08f54 100644 --- a/ahoy-components/src/main/java/za/co/lsd/ahoy/server/releases/DuplicateOptions.java +++ b/ahoy-components/src/main/java/za/co/lsd/ahoy/server/releases/DuplicateOptions.java @@ -24,7 +24,6 @@ @NoArgsConstructor @AllArgsConstructor public class DuplicateOptions { - private String releaseName; private boolean addToSameEnvironments; private boolean copyEnvironmentConfig; } diff --git a/ahoy-components/src/test/java/za/co/lsd/ahoy/server/ReleaseControllerTest.java b/ahoy-components/src/test/java/za/co/lsd/ahoy/server/ReleaseControllerTest.java index eb2b759e..920c5458 100644 --- a/ahoy-components/src/test/java/za/co/lsd/ahoy/server/ReleaseControllerTest.java +++ b/ahoy-components/src/test/java/za/co/lsd/ahoy/server/ReleaseControllerTest.java @@ -327,13 +327,13 @@ void copyApplicationVersionEnvConfigAsDeveloper() throws Exception { @WithMockUser(authorities = {Scope.ahoy, Role.releasemanager}) void duplicate() throws Exception { // given - DuplicateOptions duplicateOptions = new DuplicateOptions("release-1-copy", false, false); + DuplicateOptions duplicateOptions = new DuplicateOptions(false, false); Release duplicatedRelease = new Release(2L, "release-1-copy"); - when(releaseService.duplicate(eq(1L), eq(duplicateOptions))).thenReturn(duplicatedRelease); + when(releaseService.duplicate(eq(1L), eq(2L), eq(duplicateOptions))).thenReturn(duplicatedRelease); // when - mvc.perform(post("/api/releases/1/duplicate") + mvc.perform(post("/api/releases/duplicate/1/2") .contentType(MediaType.APPLICATION_JSON) .content(json(duplicateOptions))) .andDo(print()) @@ -342,17 +342,17 @@ void duplicate() throws Exception { .andExpect(jsonPath("$.name").value("release-1-copy")); // then - verify(releaseService, times(1)).duplicate(eq(1L), eq(duplicateOptions)); + verify(releaseService, times(1)).duplicate(eq(1L), eq(2L), eq(duplicateOptions)); } @Test @WithMockUser(authorities = {Scope.ahoy, Role.developer}) void duplicateAsDeveloper() throws Exception { // given - DuplicateOptions duplicateOptions = new DuplicateOptions("release-1-copy", false, false); + DuplicateOptions duplicateOptions = new DuplicateOptions(false, false); // when - mvc.perform(post("/api/releases/1/duplicate") + mvc.perform(post("/api/releases/duplicate/1/2") .contentType(MediaType.APPLICATION_JSON) .content(json(duplicateOptions))) .andDo(print()) diff --git a/ahoy-components/src/test/java/za/co/lsd/ahoy/server/ReleaseServiceIntegrationTest.java b/ahoy-components/src/test/java/za/co/lsd/ahoy/server/ReleaseServiceIntegrationTest.java index 17359911..6ee103ca 100644 --- a/ahoy-components/src/test/java/za/co/lsd/ahoy/server/ReleaseServiceIntegrationTest.java +++ b/ahoy-components/src/test/java/za/co/lsd/ahoy/server/ReleaseServiceIntegrationTest.java @@ -47,6 +47,7 @@ import za.co.lsd.ahoy.server.environmentrelease.EnvironmentReleaseRepository; import za.co.lsd.ahoy.server.environments.Environment; import za.co.lsd.ahoy.server.environments.EnvironmentRepository; +import za.co.lsd.ahoy.server.environments.EnvironmentService; import za.co.lsd.ahoy.server.git.GitSettings; import za.co.lsd.ahoy.server.git.LocalRepo; import za.co.lsd.ahoy.server.releases.*; @@ -93,6 +94,8 @@ class ReleaseServiceIntegrationTest { @Autowired private ReleaseService releaseService; @Autowired + private EnvironmentService environmentService; + @Autowired private SettingsProvider settingsProvider; @Autowired private SettingsService settingsService; @@ -569,4 +572,70 @@ void promote() { assertEquals(ReleaseHistoryAction.PROMOTE, releaseHistory.getAction()); assertEquals(ReleaseHistoryStatus.SUCCESS, releaseHistory.getStatus()); } + + /** + * Tests that we can successfully delete an environment after it has been deployed. + * This should undeploy the currently deployed release as well as cascade delete all related entities. + */ + @Test + @WithMockUser(authorities = {Scope.ahoy, Role.admin, Role.user}) + @DirtiesContext(methodMode = DirtiesContext.MethodMode.AFTER_METHOD) + void deleteEnvironmentWithDeployedRelease() throws Exception { + // given + Cluster cluster = clusterRepository.findById(1L).orElseThrow(); + Environment environment = new Environment("dev"); + cluster.addEnvironment(environment); + environment = environmentRepository.save(environment); + Release release = releaseRepository.save(new Release("release1")); + EnvironmentRelease environmentRelease = new EnvironmentRelease(environment, release); + environmentRelease = environmentReleaseRepository.save(environmentRelease); + + Application application = applicationRepository.save(new Application("app1")); + ApplicationVersion applicationVersion = applicationVersionRepository.save(new ApplicationVersion("1.0.0", application)); + + ReleaseVersion releaseVersion = new ReleaseVersion("1.0.0"); + release.addReleaseVersion(releaseVersion); + releaseVersion.setApplicationVersions(Collections.singletonList(applicationVersion)); + releaseVersion = releaseVersionRepository.save(releaseVersion); + + DeployOptions deployOptions = new DeployOptions(releaseVersion.getId(), "This is a test commit message"); + + String argoApplicationName = "minikube-dev-release1"; + String argoUid = UUID.randomUUID().toString(); + when(argoClient.getApplication(eq(argoApplicationName))) + .thenReturn( + Optional.empty(), + Optional.of(ArgoApplication.builder() + .metadata(ArgoMetadata.builder() + .name(argoApplicationName) + .uid(argoUid) + .build()).build())); + when(argoClient.createApplication(any())).thenAnswer(invocationOnMock -> { + ArgoApplication argoApplication = invocationOnMock.getArgument(0); + argoApplication.getMetadata().setUid(argoUid); + return argoApplication; + }); + releaseService.deploy(environmentRelease.getId(), deployOptions).get(); + + // when + environmentService.destroy(environment.getId()); + + // then + // verify external collaborators + verify(clusterManager, times(1)).createNamespace("release1-dev"); + verify(argoClient, times(1)).upsertRepository(); + verify(argoClient, times(1)).createRepositoryCertificates(); + verify(argoClient, times(2)).getApplication(eq(argoApplicationName)); + verify(argoClient, times(1)).createApplication(any(ArgoApplication.class)); + verify(argoClient, times(1)).deleteApplication(argoApplicationName); + verifyNoMoreInteractions(clusterManager, argoClient); + + // verify environment release + Optional retrievedEnvironmentRelease = environmentReleaseRepository.findById(environmentRelease.getId()); + assertTrue(retrievedEnvironmentRelease.isEmpty()); + + // verify release history + List releaseHistories = StreamSupport.stream(releaseHistoryRepository.findAll().spliterator(), false).collect(Collectors.toList()); + assertEquals(0, releaseHistories.size()); + } } diff --git a/ahoy-components/src/test/java/za/co/lsd/ahoy/server/ReleaseServiceTest.java b/ahoy-components/src/test/java/za/co/lsd/ahoy/server/ReleaseServiceTest.java index 5e16dd23..5ddb447e 100644 --- a/ahoy-components/src/test/java/za/co/lsd/ahoy/server/ReleaseServiceTest.java +++ b/ahoy-components/src/test/java/za/co/lsd/ahoy/server/ReleaseServiceTest.java @@ -331,11 +331,14 @@ public void duplicate() { Application application = new Application("app1"); ApplicationVersion applicationVersion = new ApplicationVersion("1.0.0", application); - Release release = new Release(1L, "release1"); + Release sourceRelease = new Release(1L, "release1"); ReleaseVersion releaseVersion = new ReleaseVersion(1L, "1.0.0"); - release.addReleaseVersion(releaseVersion); + sourceRelease.addReleaseVersion(releaseVersion); releaseVersion.setApplicationVersions(Collections.singletonList(applicationVersion)); - when(releaseRepository.findById(1L)).thenReturn(Optional.of(release)); + Release destRelease = new Release(2L, "release1-copy"); + + when(releaseRepository.findById(1L)).thenReturn(Optional.of(sourceRelease)); + when(releaseRepository.findById(2L)).thenReturn(Optional.of(destRelease)); when(releaseRepository.save(any(Release.class))).thenAnswer(i -> { Release r = (Release) i.getArguments()[0]; @@ -349,10 +352,11 @@ public void duplicate() { }); // when - DuplicateOptions duplicateOptions = new DuplicateOptions("release1-copy", false, false); - Release duplicatedRelease = releaseService.duplicate(release.getId(), duplicateOptions); + DuplicateOptions duplicateOptions = new DuplicateOptions(false, false); + Release duplicatedRelease = releaseService.duplicate(sourceRelease.getId(), destRelease.getId(), duplicateOptions); // then + assertNotNull(duplicatedRelease, "We should have returned the duplicated release"); assertEquals(2L, duplicatedRelease.getId(), "Id incorrect"); assertEquals("release1-copy", duplicatedRelease.getName(), "Name incorrect"); assertEquals(1, duplicatedRelease.getReleaseVersions().size(), "Versions incorrect"); @@ -363,10 +367,8 @@ public void duplicate() { assertTrue(duplicatedReleaseVersion.getReleaseHistories().isEmpty(), "Duplicated release version history incorrect"); assertEquals(releaseVersion.getApplicationVersions(), duplicatedReleaseVersion.getApplicationVersions(), "Duplicated released version doesn't have the application versions from the upgraded version"); - ArgumentCaptor releaseCaptor = ArgumentCaptor.forClass(Release.class); - verify(releaseRepository, times(1)).save(releaseCaptor.capture()); - Release savedRelease = releaseCaptor.getValue(); - assertSame(savedRelease, duplicatedRelease, "Saved release should be the same as the duplicated release"); + verify(releaseRepository, times(1)).findById(sourceRelease.getId()); + verify(releaseRepository, times(1)).findById(destRelease.getId()); ArgumentCaptor releaseVersionCaptor = ArgumentCaptor.forClass(ReleaseVersion.class); verify(releaseVersionRepository, times(1)).save(releaseVersionCaptor.capture()); @@ -387,14 +389,16 @@ public void duplicateWithEnvironment() { Application application = new Application("app1"); ApplicationVersion applicationVersion = new ApplicationVersion("1.0.0", application); - Release release = new Release(1L, "release1"); + Release sourceRelease = new Release(1L, "release1"); ReleaseVersion releaseVersion = new ReleaseVersion(1L, "1.0.0"); - release.addReleaseVersion(releaseVersion); + sourceRelease.addReleaseVersion(releaseVersion); releaseVersion.setApplicationVersions(Collections.singletonList(applicationVersion)); + Release destRelease = new Release(2L, "release1-copy"); - new EnvironmentRelease(environment, release); + new EnvironmentRelease(environment, sourceRelease); - when(releaseRepository.findById(1L)).thenReturn(Optional.of(release)); + when(releaseRepository.findById(1L)).thenReturn(Optional.of(sourceRelease)); + when(releaseRepository.findById(2L)).thenReturn(Optional.of(destRelease)); when(releaseRepository.save(any(Release.class))).thenAnswer(i -> { Release r = (Release) i.getArguments()[0]; @@ -414,10 +418,11 @@ public void duplicateWithEnvironment() { }); // when - DuplicateOptions duplicateOptions = new DuplicateOptions("release1-copy", true, true); - Release duplicatedRelease = releaseService.duplicate(release.getId(), duplicateOptions); + DuplicateOptions duplicateOptions = new DuplicateOptions(true, true); + Release duplicatedRelease = releaseService.duplicate(sourceRelease.getId(), destRelease.getId(), duplicateOptions); // then + assertNotNull(duplicatedRelease, "We should have returned the duplicated release"); assertEquals(2L, duplicatedRelease.getId(), "Id incorrect"); assertEquals("release1-copy", duplicatedRelease.getName(), "Name incorrect"); assertEquals(1, duplicatedRelease.getReleaseVersions().size(), "Versions incorrect"); @@ -432,10 +437,8 @@ public void duplicateWithEnvironment() { assertEquals(1L, duplicatedEnvironmentRelease.getEnvironment().getId(), "Duplicated environment release env incorrect"); assertEquals(2L, duplicatedEnvironmentRelease.getRelease().getId(), "Duplicated environment release release incorrect"); - ArgumentCaptor releaseCaptor = ArgumentCaptor.forClass(Release.class); - verify(releaseRepository, times(1)).save(releaseCaptor.capture()); - Release savedRelease = releaseCaptor.getValue(); - assertSame(savedRelease, duplicatedRelease, "Saved release should be the same as the duplicated release"); + verify(releaseRepository, times(1)).findById(sourceRelease.getId()); + verify(releaseRepository, times(1)).findById(destRelease.getId()); ArgumentCaptor releaseVersionCaptor = ArgumentCaptor.forClass(ReleaseVersion.class); verify(releaseVersionRepository, times(1)).save(releaseVersionCaptor.capture()); @@ -460,14 +463,16 @@ public void duplicateWithEnvironmentAndEnvConfig() { Application application = new Application("app1"); ApplicationVersion applicationVersion = new ApplicationVersion("1.0.0", application); - Release release = new Release(1L, "release1"); + Release sourceRelease = new Release(1L, "release1"); ReleaseVersion releaseVersion = new ReleaseVersion(1L, "1.0.0"); - release.addReleaseVersion(releaseVersion); + sourceRelease.addReleaseVersion(releaseVersion); releaseVersion.setApplicationVersions(Collections.singletonList(applicationVersion)); + Release destRelease = new Release(2L, "release1-copy"); - EnvironmentRelease environmentRelease = new EnvironmentRelease(environment, release); + EnvironmentRelease environmentRelease = new EnvironmentRelease(environment, sourceRelease); - when(releaseRepository.findById(1L)).thenReturn(Optional.of(release)); + when(releaseRepository.findById(1L)).thenReturn(Optional.of(sourceRelease)); + when(releaseRepository.findById(2L)).thenReturn(Optional.of(destRelease)); when(releaseRepository.save(any(Release.class))).thenAnswer(i -> { Release r = (Release) i.getArguments()[0]; @@ -494,10 +499,11 @@ public void duplicateWithEnvironmentAndEnvConfig() { when(environmentConfigProvider.environmentConfigFor(environmentRelease, releaseVersion, applicationVersion)).thenReturn(Optional.of(environmentConfig)); // when - DuplicateOptions duplicateOptions = new DuplicateOptions("release1-copy", true, true); - Release duplicatedRelease = releaseService.duplicate(release.getId(), duplicateOptions); + DuplicateOptions duplicateOptions = new DuplicateOptions(true, true); + Release duplicatedRelease = releaseService.duplicate(sourceRelease.getId(), destRelease.getId(), duplicateOptions); // then + assertNotNull(duplicatedRelease, "We should have returned the duplicated release"); assertEquals(2L, duplicatedRelease.getId(), "Id incorrect"); assertEquals("release1-copy", duplicatedRelease.getName(), "Name incorrect"); assertEquals(1, duplicatedRelease.getReleaseVersions().size(), "Versions incorrect"); @@ -512,10 +518,8 @@ public void duplicateWithEnvironmentAndEnvConfig() { assertEquals(1L, duplicatedEnvironmentRelease.getEnvironment().getId(), "Duplicated environment release env incorrect"); assertEquals(2L, duplicatedEnvironmentRelease.getRelease().getId(), "Duplicated environment release release incorrect"); - ArgumentCaptor releaseCaptor = ArgumentCaptor.forClass(Release.class); - verify(releaseRepository, times(1)).save(releaseCaptor.capture()); - Release savedRelease = releaseCaptor.getValue(); - assertSame(savedRelease, duplicatedRelease, "Saved release should be the same as the duplicated release"); + verify(releaseRepository, times(1)).findById(sourceRelease.getId()); + verify(releaseRepository, times(1)).findById(destRelease.getId()); ArgumentCaptor releaseVersionCaptor = ArgumentCaptor.forClass(ReleaseVersion.class); verify(releaseVersionRepository, times(1)).save(releaseVersionCaptor.capture()); diff --git a/ahoy-components/src/test/java/za/co/lsd/ahoy/server/environments/EnvironmentServiceTest.java b/ahoy-components/src/test/java/za/co/lsd/ahoy/server/environments/EnvironmentServiceTest.java index 696b295e..0f9a4cb3 100644 --- a/ahoy-components/src/test/java/za/co/lsd/ahoy/server/environments/EnvironmentServiceTest.java +++ b/ahoy-components/src/test/java/za/co/lsd/ahoy/server/environments/EnvironmentServiceTest.java @@ -33,6 +33,7 @@ import za.co.lsd.ahoy.server.cluster.ClusterRepository; import za.co.lsd.ahoy.server.cluster.ClusterType; import za.co.lsd.ahoy.server.environmentrelease.EnvironmentRelease; +import za.co.lsd.ahoy.server.environmentrelease.EnvironmentReleaseRepository; import za.co.lsd.ahoy.server.releases.Release; import za.co.lsd.ahoy.server.releases.ReleaseVersion; @@ -54,6 +55,8 @@ class EnvironmentServiceTest { private ClusterRepository clusterRepository; @MockBean private ReleaseService releaseService; + @MockBean + private EnvironmentReleaseRepository environmentReleaseRepository; @Autowired private EnvironmentService environmentService; @@ -66,6 +69,64 @@ void updateOrderIndex() { verify(environmentRepository, times(1)).updateOrderIndex(eq(1L), eq(500.0)); } + @Test + void duplicate() { + // given + Cluster cluster = new Cluster(1L, "test-cluster", "https://kubernetes1.default.svc", ClusterType.KUBERNETES); + + Environment sourceEnvironment = new Environment(1L, "dev"); + Environment destEnvironment = new Environment(2L, "qa"); + cluster.addEnvironment(sourceEnvironment); + + Release release = new Release(1L, "release1"); + EnvironmentRelease environmentRelease = new EnvironmentRelease(sourceEnvironment, release); + environmentRelease.setCurrentReleaseVersion(null); + + when(environmentRepository.findById(1L)).thenReturn(Optional.of(sourceEnvironment)); + when(environmentRepository.findById(2L)).thenReturn(Optional.of(destEnvironment)); + + // when + environmentService.duplicate(sourceEnvironment.getId(), destEnvironment.getId(), new DuplicateOptions(false)); + + // then + verify(environmentRepository, times(1)).findById(sourceEnvironment.getId()); + verify(environmentRepository, times(1)).findById(destEnvironment.getId()); + EnvironmentRelease expectedEnvironmentRelease = new EnvironmentRelease(destEnvironment, release); + verify(environmentReleaseRepository, times(1)).save(eq(expectedEnvironmentRelease)); + + verifyNoMoreInteractions(environmentRepository, environmentReleaseRepository, releaseService); + } + + @Test + void duplicateWithEnvironmentConfig() { + // given + Cluster cluster = new Cluster(1L, "test-cluster", "https://kubernetes1.default.svc", ClusterType.KUBERNETES); + + Environment sourceEnvironment = new Environment(1L, "dev"); + Environment destEnvironment = new Environment(2L, "qa"); + cluster.addEnvironment(sourceEnvironment); + + Release release = new Release(1L, "release1"); + EnvironmentRelease environmentRelease = new EnvironmentRelease(sourceEnvironment, release); + environmentRelease.setCurrentReleaseVersion(null); + + when(environmentRepository.findById(1L)).thenReturn(Optional.of(sourceEnvironment)); + when(environmentRepository.findById(2L)).thenReturn(Optional.of(destEnvironment)); + + // when + DuplicateOptions duplicateOptionsWithCopyEnvConfig = new DuplicateOptions(true); + environmentService.duplicate(sourceEnvironment.getId(), destEnvironment.getId(), duplicateOptionsWithCopyEnvConfig); + + // then + verify(environmentRepository, times(1)).findById(sourceEnvironment.getId()); + verify(environmentRepository, times(1)).findById(destEnvironment.getId()); + EnvironmentRelease expectedEnvironmentRelease = new EnvironmentRelease(destEnvironment, release); + verify(environmentReleaseRepository, times(1)).save(eq(expectedEnvironmentRelease)); + verify(releaseService, times(1)).copyEnvironmentConfig(same(environmentRelease), eq(expectedEnvironmentRelease)); + + verifyNoMoreInteractions(environmentRepository, environmentReleaseRepository, releaseService); + } + @Test void moveNoPreviousDeployments() { // given diff --git a/ahoy-components/src/test/java/za/co/lsd/ahoy/server/helm/ChartGeneratorTest.java b/ahoy-components/src/test/java/za/co/lsd/ahoy/server/helm/ChartGeneratorTest.java index 0f7c6ff8..6d8e1e44 100644 --- a/ahoy-components/src/test/java/za/co/lsd/ahoy/server/helm/ChartGeneratorTest.java +++ b/ahoy-components/src/test/java/za/co/lsd/ahoy/server/helm/ChartGeneratorTest.java @@ -484,9 +484,9 @@ public void generateFull() throws Exception { volumes.put("my-env-secret-volume", new ApplicationVolumeValues("my-env-secret-volume", "/opt/env-secret-vol", "my-env-secret")); Map secrets = new LinkedHashMap<>(); - secrets.put("my-secret", new ApplicationSecretValues("my-secret", "Opague", Collections.singletonMap("secret-key", "secret-value"))); + secrets.put("my-secret", new ApplicationSecretValues("my-secret", "Opaque", Collections.singletonMap("secret-key", "secret-value"))); secrets.put("my-tls-secret", new ApplicationSecretValues("my-tls-secret", "kubernetes.io/tls", Collections.singletonMap("cert", "my-cert"))); - secrets.put("my-env-secret", new ApplicationSecretValues("my-env-secret", "Opague", Collections.singletonMap("env-secret-key", "env-secret-value"))); + secrets.put("my-env-secret", new ApplicationSecretValues("my-env-secret", "Opaque", Collections.singletonMap("env-secret-key", "env-secret-value"))); ApplicationValues expectedApplicationValues = ApplicationValues.builder() .name("app1") @@ -704,9 +704,9 @@ public void generateFullMultiContainer() throws Exception { volumes.put("my-env-secret-volume", new ApplicationVolumeValues("my-env-secret-volume", "/opt/env-secret-vol", "my-env-secret")); Map secrets = new LinkedHashMap<>(); - secrets.put("my-secret", new ApplicationSecretValues("my-secret", "Opague", Collections.singletonMap("secret-key", "secret-value"))); + secrets.put("my-secret", new ApplicationSecretValues("my-secret", "Opaque", Collections.singletonMap("secret-key", "secret-value"))); secrets.put("my-tls-secret", new ApplicationSecretValues("my-tls-secret", "kubernetes.io/tls", Collections.singletonMap("cert", "my-cert"))); - secrets.put("my-env-secret", new ApplicationSecretValues("my-env-secret", "Opague", Collections.singletonMap("env-secret-key", "env-secret-value"))); + secrets.put("my-env-secret", new ApplicationSecretValues("my-env-secret", "Opaque", Collections.singletonMap("env-secret-key", "env-secret-value"))); Map containers = new LinkedHashMap<>(); containers.put("default", ContainerValues.builder() @@ -946,9 +946,9 @@ public void generateFullInitContainer() throws Exception { volumes.put("my-env-secret-volume", new ApplicationVolumeValues("my-env-secret-volume", "/opt/env-secret-vol", "my-env-secret")); Map secrets = new LinkedHashMap<>(); - secrets.put("my-secret", new ApplicationSecretValues("my-secret", "Opague", Collections.singletonMap("secret-key", "secret-value"))); + secrets.put("my-secret", new ApplicationSecretValues("my-secret", "Opaque", Collections.singletonMap("secret-key", "secret-value"))); secrets.put("my-tls-secret", new ApplicationSecretValues("my-tls-secret", "kubernetes.io/tls", Collections.singletonMap("cert", "my-cert"))); - secrets.put("my-env-secret", new ApplicationSecretValues("my-env-secret", "Opague", Collections.singletonMap("env-secret-key", "env-secret-value"))); + secrets.put("my-env-secret", new ApplicationSecretValues("my-env-secret", "Opaque", Collections.singletonMap("env-secret-key", "env-secret-value"))); ApplicationValues expectedApplicationValues = ApplicationValues.builder() .name("app1") @@ -1125,7 +1125,7 @@ public void generateEnvConfigOnly() throws Exception { volumes.put("my-env-secret-volume", new ApplicationVolumeValues("my-env-secret-volume", "/opt/env-secret-vol", "my-env-secret")); Map secrets = new LinkedHashMap<>(); - secrets.put("my-env-secret", new ApplicationSecretValues("my-env-secret", "Opague", Collections.singletonMap("env-secret-key", "env-secret-value"))); + secrets.put("my-env-secret", new ApplicationSecretValues("my-env-secret", "Opaque", Collections.singletonMap("env-secret-key", "env-secret-value"))); secrets.put("my-tls-secret", new ApplicationSecretValues("my-tls-secret", "kubernetes.io/tls", Collections.singletonMap("cert", "my-cert"))); ApplicationValues expectedApplicationValues = ApplicationValues.builder() diff --git a/ahoy-server/pom.xml b/ahoy-server/pom.xml index e6556ce8..523e521e 100644 --- a/ahoy-server/pom.xml +++ b/ahoy-server/pom.xml @@ -22,7 +22,7 @@ za.co.lsd.ahoy ahoy - 0.14.0 + 0.15.0 ahoy-server diff --git a/ahoy-ui/package-lock.json b/ahoy-ui/package-lock.json index a97f3642..66900421 100644 --- a/ahoy-ui/package-lock.json +++ b/ahoy-ui/package-lock.json @@ -1,12 +1,12 @@ { "name": "ahoy-ui", - "version": "0.14.0", + "version": "0.15.0", "lockfileVersion": 2, "requires": true, "packages": { "": { "name": "ahoy-ui", - "version": "0.14.0", + "version": "0.15.0", "dependencies": { "@angular/animations": "^13.2.4", "@angular/cdk": "^13.2.4", @@ -23,9 +23,9 @@ "event-source-polyfill": "^1.0.25", "jwt-decode": "^3.1.2", "net": "^1.0.2", - "primeflex": "3.1.2", + "primeflex": "3.2.1", "primeicons": "5.0.0", - "primeng": "13.0.4", + "primeng": "13.4.1", "rxjs": "^7.5.2", "tslib": "^2.0.0", "zone.js": "~0.11.4" @@ -10764,9 +10764,9 @@ } }, "node_modules/primeflex": { - "version": "3.1.2", - "resolved": "https://registry.npmjs.org/primeflex/-/primeflex-3.1.2.tgz", - "integrity": "sha512-q63sFNk3KPHSoM4EEEgo0MFTJSQhz4PZpJoQnVa8bcju/N7D8koloF9fniXV8UBlKFQzh0DLr5iFZ4c6FFXO4g==" + "version": "3.2.1", + "resolved": "https://registry.npmjs.org/primeflex/-/primeflex-3.2.1.tgz", + "integrity": "sha512-sGDJ4mh2fG19xa1yc4IPGSQ8MUMyu5nU+tYnl27AFuFHhX8XwEA7fWCtWyBbclclVhXtre+Kf9WobWPnfxWAEQ==" }, "node_modules/primeicons": { "version": "5.0.0", @@ -10774,9 +10774,9 @@ "integrity": "sha512-heygWF0X5HFI1otlZE62pp6ye7sZ8om78J9au2BRkg8O7Y8AHTZ9qKMRzchZUHLe8zUAvdi6hZzzm9XxgwIExw==" }, "node_modules/primeng": { - "version": "13.0.4", - "resolved": "https://registry.npmjs.org/primeng/-/primeng-13.0.4.tgz", - "integrity": "sha512-hX6TcZ5UwCzA3cfl9csk/N9l0R9mek/QR/BWDQI2zH9Jtc5W1x9FUXnZPj4pUlCDvYL5NW21Yu7d9fDhIe20qQ==", + "version": "13.4.1", + "resolved": "https://registry.npmjs.org/primeng/-/primeng-13.4.1.tgz", + "integrity": "sha512-Dp72Yt7ORwoVYXr5r4oNTacQHUyLAdTuvXXNg9ZNjjxCdIfYZsVjTFBHBVzgEo9BpPy+/5twYy1JP/eXXDNPHg==", "dependencies": { "tslib": "^2.3.0" }, @@ -21769,9 +21769,9 @@ "dev": true }, "primeflex": { - "version": "3.1.2", - "resolved": "https://registry.npmjs.org/primeflex/-/primeflex-3.1.2.tgz", - "integrity": "sha512-q63sFNk3KPHSoM4EEEgo0MFTJSQhz4PZpJoQnVa8bcju/N7D8koloF9fniXV8UBlKFQzh0DLr5iFZ4c6FFXO4g==" + "version": "3.2.1", + "resolved": "https://registry.npmjs.org/primeflex/-/primeflex-3.2.1.tgz", + "integrity": "sha512-sGDJ4mh2fG19xa1yc4IPGSQ8MUMyu5nU+tYnl27AFuFHhX8XwEA7fWCtWyBbclclVhXtre+Kf9WobWPnfxWAEQ==" }, "primeicons": { "version": "5.0.0", @@ -21779,9 +21779,9 @@ "integrity": "sha512-heygWF0X5HFI1otlZE62pp6ye7sZ8om78J9au2BRkg8O7Y8AHTZ9qKMRzchZUHLe8zUAvdi6hZzzm9XxgwIExw==" }, "primeng": { - "version": "13.0.4", - "resolved": "https://registry.npmjs.org/primeng/-/primeng-13.0.4.tgz", - "integrity": "sha512-hX6TcZ5UwCzA3cfl9csk/N9l0R9mek/QR/BWDQI2zH9Jtc5W1x9FUXnZPj4pUlCDvYL5NW21Yu7d9fDhIe20qQ==", + "version": "13.4.1", + "resolved": "https://registry.npmjs.org/primeng/-/primeng-13.4.1.tgz", + "integrity": "sha512-Dp72Yt7ORwoVYXr5r4oNTacQHUyLAdTuvXXNg9ZNjjxCdIfYZsVjTFBHBVzgEo9BpPy+/5twYy1JP/eXXDNPHg==", "requires": { "tslib": "^2.3.0" } diff --git a/ahoy-ui/package.json b/ahoy-ui/package.json index 8ac15a79..2bbbbc9a 100644 --- a/ahoy-ui/package.json +++ b/ahoy-ui/package.json @@ -1,6 +1,6 @@ { "name": "ahoy-ui", - "version": "0.14.0", + "version": "0.15.0", "scripts": { "ng": "ng", "start": "ng serve", @@ -29,9 +29,9 @@ "event-source-polyfill": "^1.0.25", "jwt-decode": "^3.1.2", "net": "^1.0.2", - "primeflex": "3.1.2", + "primeflex": "3.2.1", "primeicons": "5.0.0", - "primeng": "13.0.4", + "primeng": "13.4.1", "rxjs": "^7.5.2", "tslib": "^2.0.0", "zone.js": "~0.11.4" diff --git a/ahoy-ui/pom.xml b/ahoy-ui/pom.xml index 2f8c488c..e9340770 100644 --- a/ahoy-ui/pom.xml +++ b/ahoy-ui/pom.xml @@ -22,7 +22,7 @@ za.co.lsd.ahoy ahoy - 0.14.0 + 0.15.0 ahoy-ui pom diff --git a/ahoy-ui/projects/ahoy-app/src/assets/layout/css/layout-dark.css b/ahoy-ui/projects/ahoy-app/src/assets/layout/css/layout-dark.css index 430a929f..9d82bd81 100644 --- a/ahoy-ui/projects/ahoy-app/src/assets/layout/css/layout-dark.css +++ b/ahoy-ui/projects/ahoy-app/src/assets/layout/css/layout-dark.css @@ -4326,6 +4326,109 @@ body a { z-index: 999; } +.layout-topbar .notifications .layout-topbar-action-panel img { + width: 32px; + height: 32px; +} +.layout-topbar .notifications .layout-topbar-action-panel .layout-topbar-action-item { + border-bottom: 1px solid var(--divider-color); +} +.layout-topbar .notifications .layout-topbar-action-panel .layout-topbar-action-item:last-child { + border-bottom: 0 none; +} +.layout-topbar .app .layout-topbar-action-panel a { + padding: 0.5rem 0; +} +.layout-topbar .app .layout-topbar-action-panel a i { + width: 42px; + height: 42px; + border: 1px solid transparent; + border-radius: 50%; + margin: 0.5rem 0; + display: flex; + align-items: center; + justify-content: center; +} + +.layout-rightmenu .header { + padding-bottom: 0.5rem; + margin-bottom: 0.714rem; + border-bottom: 1px solid var(--divider-color); +} +.layout-rightmenu .online-members img { + width: 32px; +} +.layout-rightmenu .online-members b { + color: var(--primary-color); +} +.layout-rightmenu .latest-activity i { + border: 1px solid transparent; + border-radius: 50px; + background-color: var(--surface-d); +} +.layout-rightmenu .next-events ul { + margin: 0; + list-style-type: none; + padding: 0; +} +.layout-rightmenu .next-events ul > li { + padding: 0.875rem 0.5rem; +} + +.layout-help-page .questions.p-accordion p-accordiontab .p-accordion-tab { + margin-top: 1rem; +} +.layout-help-page .questions.p-accordion p-accordiontab:first-child .p-accordion-tab { + margin-top: 0; +} + +.layout-invoice-page .p-invoice-datatable-responsive .p-datatable-tbody > tr > td .p-column-title { + display: none; +} +@media screen and (max-width: 40rem) { + .layout-invoice-page .p-invoice-datatable-responsive .p-datatable-thead > tr > th, +.layout-invoice-page .p-invoice-datatable-responsive .p-datatable-tfoot > tr > td { + display: none !important; + } + .layout-invoice-page .p-invoice-datatable-responsive .p-datatable-tbody > tr > td { + text-align: left; + display: block; + width: 100%; + float: left; + clear: left; + border: 0 none; + } + .layout-invoice-page .p-invoice-datatable-responsive .p-datatable-tbody > tr > td .p-column-title { + padding: 0.4rem; + min-width: 30%; + display: inline-block; + margin: -0.4em 1em -0.4em -0.4rem; + font-weight: bold; + } + .layout-invoice-page .p-invoice-datatable-responsive .p-datatable-tbody > tr > td:last-child { + border-bottom: 1px solid var(--surface-d); + } +} + +@media (min-width: 992px) { + .layout-menu-slim .layout-inline-menu .layout-inline-menu-action { + justify-content: center; + } + .layout-menu-slim .layout-inline-menu .layout-inline-menu-action > span, +.layout-menu-slim .layout-inline-menu .layout-inline-menu-action > i { + display: none !important; + } + .layout-menu-slim .layout-inline-menu .layout-inline-menu-action-panel .layout-inline-menu-action-item > a { + justify-content: center; + } + .layout-menu-slim .layout-inline-menu .layout-inline-menu-action-panel .layout-inline-menu-action-item > a > i { + font-size: 1.5rem; + margin-right: 0 !important; + } + .layout-menu-slim .layout-inline-menu .layout-inline-menu-action-panel .layout-inline-menu-action-item > a > span { + display: none; + } +} .layout-footer { background-color: #1e1e1e; } diff --git a/ahoy-ui/projects/ahoy-app/src/assets/layout/css/layout-light.css b/ahoy-ui/projects/ahoy-app/src/assets/layout/css/layout-light.css index 893a60c0..d0f37c6a 100644 --- a/ahoy-ui/projects/ahoy-app/src/assets/layout/css/layout-light.css +++ b/ahoy-ui/projects/ahoy-app/src/assets/layout/css/layout-light.css @@ -4326,6 +4326,109 @@ body a { z-index: 999; } +.layout-topbar .notifications .layout-topbar-action-panel img { + width: 32px; + height: 32px; +} +.layout-topbar .notifications .layout-topbar-action-panel .layout-topbar-action-item { + border-bottom: 1px solid var(--divider-color); +} +.layout-topbar .notifications .layout-topbar-action-panel .layout-topbar-action-item:last-child { + border-bottom: 0 none; +} +.layout-topbar .app .layout-topbar-action-panel a { + padding: 0.5rem 0; +} +.layout-topbar .app .layout-topbar-action-panel a i { + width: 42px; + height: 42px; + border: 1px solid transparent; + border-radius: 50%; + margin: 0.5rem 0; + display: flex; + align-items: center; + justify-content: center; +} + +.layout-rightmenu .header { + padding-bottom: 0.5rem; + margin-bottom: 0.714rem; + border-bottom: 1px solid var(--divider-color); +} +.layout-rightmenu .online-members img { + width: 32px; +} +.layout-rightmenu .online-members b { + color: var(--primary-color); +} +.layout-rightmenu .latest-activity i { + border: 1px solid transparent; + border-radius: 50px; + background-color: var(--surface-d); +} +.layout-rightmenu .next-events ul { + margin: 0; + list-style-type: none; + padding: 0; +} +.layout-rightmenu .next-events ul > li { + padding: 0.875rem 0.5rem; +} + +.layout-help-page .questions.p-accordion p-accordiontab .p-accordion-tab { + margin-top: 1rem; +} +.layout-help-page .questions.p-accordion p-accordiontab:first-child .p-accordion-tab { + margin-top: 0; +} + +.layout-invoice-page .p-invoice-datatable-responsive .p-datatable-tbody > tr > td .p-column-title { + display: none; +} +@media screen and (max-width: 40rem) { + .layout-invoice-page .p-invoice-datatable-responsive .p-datatable-thead > tr > th, +.layout-invoice-page .p-invoice-datatable-responsive .p-datatable-tfoot > tr > td { + display: none !important; + } + .layout-invoice-page .p-invoice-datatable-responsive .p-datatable-tbody > tr > td { + text-align: left; + display: block; + width: 100%; + float: left; + clear: left; + border: 0 none; + } + .layout-invoice-page .p-invoice-datatable-responsive .p-datatable-tbody > tr > td .p-column-title { + padding: 0.4rem; + min-width: 30%; + display: inline-block; + margin: -0.4em 1em -0.4em -0.4rem; + font-weight: bold; + } + .layout-invoice-page .p-invoice-datatable-responsive .p-datatable-tbody > tr > td:last-child { + border-bottom: 1px solid var(--surface-d); + } +} + +@media (min-width: 992px) { + .layout-menu-slim .layout-inline-menu .layout-inline-menu-action { + justify-content: center; + } + .layout-menu-slim .layout-inline-menu .layout-inline-menu-action > span, +.layout-menu-slim .layout-inline-menu .layout-inline-menu-action > i { + display: none !important; + } + .layout-menu-slim .layout-inline-menu .layout-inline-menu-action-panel .layout-inline-menu-action-item > a { + justify-content: center; + } + .layout-menu-slim .layout-inline-menu .layout-inline-menu-action-panel .layout-inline-menu-action-item > a > i { + font-size: 1.5rem; + margin-right: 0 !important; + } + .layout-menu-slim .layout-inline-menu .layout-inline-menu-action-panel .layout-inline-menu-action-item > a > span { + display: none; + } +} .layout-footer { background-color: #ffffff; } diff --git a/ahoy-ui/projects/ahoy-app/src/assets/sass/layout/_config.scss b/ahoy-ui/projects/ahoy-app/src/assets/sass/layout/_config.scss index 8671d550..5ada20b1 100644 --- a/ahoy-ui/projects/ahoy-app/src/assets/sass/layout/_config.scss +++ b/ahoy-ui/projects/ahoy-app/src/assets/sass/layout/_config.scss @@ -75,8 +75,9 @@ } .layout-config-button.p-button { + z-index: 1001; position: fixed; - top: 35%; + top: 50%; right: 0; width: auto; border-top-right-radius: 0; diff --git a/ahoy-ui/projects/ahoy-app/src/assets/sass/layout/_main.scss b/ahoy-ui/projects/ahoy-app/src/assets/sass/layout/_main.scss index 290df080..18afc6ce 100644 --- a/ahoy-ui/projects/ahoy-app/src/assets/sass/layout/_main.scss +++ b/ahoy-ui/projects/ahoy-app/src/assets/sass/layout/_main.scss @@ -73,3 +73,164 @@ body { color: $textSecondaryColor; z-index: 999; } + +.layout-topbar { + .notifications { + .layout-topbar-action-panel { + img { + width: 32px; + height: 32px; + } + + .layout-topbar-action-item { + border-bottom: 1px solid var(--divider-color); + + &:last-child { + border-bottom: 0 none; + } + } + } + } + + .app { + .layout-topbar-action-panel { + a { + padding: .5rem 0; + + i { + width: 42px; + height: 42px; + border: 1px solid transparent; + border-radius: 50%; + margin: .5rem 0; + display: flex; + align-items: center; + justify-content: center; + } + } + } + } +} + +.layout-rightmenu { + .header { + padding-bottom: .5rem; + margin-bottom: .714rem; + border-bottom: 1px solid var(--divider-color); + } + + .online-members { + img { + width: 32px; + } + + b { + color: var(--primary-color) + } + } + + .latest-activity { + i { + border: 1px solid transparent; + border-radius: 50px; + background-color: var(--surface-d); + } + } + + .next-events { + ul { + margin: 0; + list-style-type: none; + padding: 0; + + > li { + padding: .875rem .5rem; + } + } + } +} + +.layout-help-page { + .questions { + &.p-accordion p-accordiontab { + .p-accordion-tab { + margin-top: 1rem; + } + + &:first-child { + .p-accordion-tab { + margin-top: 0; + } + } + } + } +} + +.layout-invoice-page { + .p-invoice-datatable-responsive .p-datatable-tbody > tr > td .p-column-title { + display: none; + } + + @media screen and (max-width: 40rem) { + .p-invoice-datatable-responsive { + .p-datatable-thead > tr > th, + .p-datatable-tfoot > tr > td { + display: none !important; + } + + .p-datatable-tbody > tr > td { + text-align: left; + display: block; + width: 100%; + float: left; + clear: left; + border: 0 none; + + .p-column-title { + padding: .4rem; + min-width: 30%; + display: inline-block; + margin: -.4em 1em -.4em -.4rem; + font-weight: bold; + } + + &:last-child { + border-bottom: 1px solid var(--surface-d); + } + } + } + } +} + +@media (min-width: 992px) { + .layout-menu-slim { + .layout-inline-menu { + .layout-inline-menu-action { + justify-content: center; + + > span, + > i { + display: none !important; + } + } + + .layout-inline-menu-action-panel { + .layout-inline-menu-action-item { + > a { + justify-content: center; + + > i { + font-size: 1.5rem; + margin-right: 0 !important; + } + + > span { + display: none; + } + } + } + } + } + } +} + diff --git a/ahoy-ui/projects/ahoy-app/src/assets/sass/theme/designer/_mixins.scss b/ahoy-ui/projects/ahoy-app/src/assets/sass/theme/designer/_mixins.scss index f341bf71..3da63abb 100644 --- a/ahoy-ui/projects/ahoy-app/src/assets/sass/theme/designer/_mixins.scss +++ b/ahoy-ui/projects/ahoy-app/src/assets/sass/theme/designer/_mixins.scss @@ -194,3 +194,12 @@ @function shade($color, $percentage) { @return mix(#000, $color, $percentage); } + +@mixin button-states { + // - - diff --git a/ahoy-ui/projects/ahoy-components/src/lib/releases/duplicate-dialog/duplicate-dialog.component.ts b/ahoy-ui/projects/ahoy-components/src/lib/releases/duplicate-dialog/duplicate-dialog.component.ts deleted file mode 100644 index e14357e6..00000000 --- a/ahoy-ui/projects/ahoy-components/src/lib/releases/duplicate-dialog/duplicate-dialog.component.ts +++ /dev/null @@ -1,48 +0,0 @@ -/* - * Copyright 2022 LSD Information Technology (Pty) Ltd - * - * 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 - * - * http://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. - */ - -import {Component, OnInit} from '@angular/core'; -import {DynamicDialogConfig, DynamicDialogRef} from 'primeng/dynamicdialog'; -import {DuplicateOptions, Release} from '../release'; -import {ReleaseService} from '../release.service'; - -@Component({ - selector: 'app-duplicate-dialog', - templateUrl: './duplicate-dialog.component.html', - styleUrls: ['./duplicate-dialog.component.scss'] -}) -export class DuplicateDialogComponent implements OnInit { - releasesForValidation: Release[]; - selectedRelease: Release; - duplicateOptions = new DuplicateOptions(); - - constructor(public ref: DynamicDialogRef, - public config: DynamicDialogConfig, - private releaseService: ReleaseService) { - const data = config.data; - this.selectedRelease = data.selectedRelease; - this.duplicateOptions.releaseName = this.selectedRelease.name + '-copy'; - } - - ngOnInit(): void { - this.releaseService.getAll() - .subscribe(releases => this.releasesForValidation = releases); - } - - close(result: any) { - this.ref.close(result); - } -} diff --git a/ahoy-ui/projects/ahoy-components/src/lib/releases/release-detail/release-detail.component.html b/ahoy-ui/projects/ahoy-components/src/lib/releases/release-detail/release-detail.component.html index c1c50a50..5c15d9dc 100644 --- a/ahoy-ui/projects/ahoy-components/src/lib/releases/release-detail/release-detail.component.html +++ b/ahoy-ui/projects/ahoy-components/src/lib/releases/release-detail/release-detail.component.html @@ -21,8 +21,9 @@
-
{{editMode ? "Edit" : "New"}} release
+
{{editMode ? "Edit" : "New"}} release
New release in {{environment.name}}
+
Duplicate release {{sourceRelease.name}}
@@ -54,7 +55,7 @@
New release in {{environment.name}}
-
+
New release in {{environment.name}}
+
+
+ + +
+
+ + +
+
+
diff --git a/ahoy-ui/projects/ahoy-components/src/lib/releases/release-detail/release-detail.component.ts b/ahoy-ui/projects/ahoy-components/src/lib/releases/release-detail/release-detail.component.ts index d0c667c6..f1b70687 100644 --- a/ahoy-ui/projects/ahoy-components/src/lib/releases/release-detail/release-detail.component.ts +++ b/ahoy-ui/projects/ahoy-components/src/lib/releases/release-detail/release-detail.component.ts @@ -25,8 +25,8 @@ import {EnvironmentRelease, EnvironmentReleaseId} from '../../environment-releas import {EnvironmentReleaseService} from '../../environment-release/environment-release.service'; import {Environment} from '../../environments/environment'; import {EnvironmentService} from '../../environments/environment.service'; -import {Release, ReleaseVersion} from '../../releases/release'; -import {ReleaseService} from '../../releases/release.service'; +import {DuplicateOptions, Release, ReleaseVersion} from '../release'; +import {ReleaseService} from '../release.service'; import {TaskEvent} from '../../taskevents/task-events'; import {LoggerService} from '../../util/logger.service'; @@ -41,6 +41,8 @@ export class ReleaseDetailComponent implements OnInit { releasesForValidation: Release[]; editMode = false; environment: Environment; + sourceRelease: Release; + duplicateOptions: DuplicateOptions; constructor( private log: LoggerService, @@ -56,12 +58,12 @@ export class ReleaseDetailComponent implements OnInit { ngOnInit() { const releaseId = this.route.snapshot.paramMap.get('releaseId'); - const environmentId = +this.route.snapshot.queryParamMap.get('environmentId'); if (releaseId === 'new') { this.release = new Release(); this.releaseVersion = new ReleaseVersion(); + const environmentId = +this.route.snapshot.queryParamMap.get('environmentId'); if (environmentId) { this.environmentService.get(environmentId) .subscribe(env => { @@ -72,6 +74,16 @@ export class ReleaseDetailComponent implements OnInit { this.setBreadcrumb(); } + const sourceReleaseId = +this.route.snapshot.queryParamMap.get('sourceReleaseId'); + if (sourceReleaseId) { + this.releaseService.get(sourceReleaseId) + .subscribe((rel) => { + this.duplicateOptions = new DuplicateOptions(); + this.sourceRelease = rel; + this.setBreadcrumb(); + }); + } + } else { this.editMode = true; this.getRelease(+releaseId); @@ -96,6 +108,12 @@ export class ReleaseDetailComponent implements OnInit { {label: this.release.name}, {label: 'edit'} ]); + } else if (this.sourceRelease) { + this.breadcrumbService.setItems([ + {label: 'releases', routerLink: '/releases'}, + {label: this.sourceRelease.name}, + {label: 'duplicate'} + ]); } else if (this.environment) { this.breadcrumbService.setItems([ {label: this.environment.name, routerLink: '/environments'}, @@ -112,15 +130,22 @@ export class ReleaseDetailComponent implements OnInit { save() { this.releaseService.save(this.release) .pipe( + mergeMap(release => { + if (this.sourceRelease) { + // we're duplicating a source release + return this.releaseService.duplicate(this.sourceRelease, release, this.duplicateOptions); + } + return of(release); + }), mergeMap(release => { this.release = release; - if (!this.editMode) { + if (!this.editMode && !this.sourceRelease) { this.releaseVersion.release = this.releaseService.link(release.id); return this.releaseService.saveVersion(this.releaseVersion); } return of(release); }), - mergeMap(releaseVersion => { + mergeMap(release => { if (this.environment) { const environmentRelease = new EnvironmentRelease(); environmentRelease.id = new EnvironmentReleaseId(); @@ -129,7 +154,7 @@ export class ReleaseDetailComponent implements OnInit { return this.environmentReleaseService.save(environmentRelease); } - return of(releaseVersion); + return of(release); }) ) .subscribe(() => this.location.back()); diff --git a/ahoy-ui/projects/ahoy-components/src/lib/releases/release.service.ts b/ahoy-ui/projects/ahoy-components/src/lib/releases/release.service.ts index 9eef6d3b..4d04c41c 100644 --- a/ahoy-ui/projects/ahoy-components/src/lib/releases/release.service.ts +++ b/ahoy-ui/projects/ahoy-components/src/lib/releases/release.service.ts @@ -166,10 +166,10 @@ export class ReleaseService { ); } - duplicate(release: Release, duplicateOptions: DuplicateOptions): Observable { - this.log.debug(`duplicating release: ${release.name} with options`, duplicateOptions); + duplicate(sourceRelease: Release, destRelease: Release, duplicateOptions: DuplicateOptions): Observable { + this.log.debug(`duplicating release: ${sourceRelease.name} with options`, duplicateOptions); - const url = `/api/releases/${release.id}/duplicate`; + const url = `/api/releases/duplicate/${sourceRelease.id}/${destRelease.id}`; return this.restClient.post(url, duplicateOptions, true).pipe( tap((duplicatedRelease) => diff --git a/ahoy-ui/projects/ahoy-components/src/lib/releases/release.ts b/ahoy-ui/projects/ahoy-components/src/lib/releases/release.ts index d39f837f..4b30a5e7 100644 --- a/ahoy-ui/projects/ahoy-components/src/lib/releases/release.ts +++ b/ahoy-ui/projects/ahoy-components/src/lib/releases/release.ts @@ -48,7 +48,6 @@ export class UpgradeAppOptions { } export class DuplicateOptions { - releaseName: string; addToSameEnvironments = true; copyEnvironmentConfig = false; } diff --git a/ahoy-ui/projects/ahoy-components/src/lib/releases/releases.component.html b/ahoy-ui/projects/ahoy-components/src/lib/releases/releases.component.html index 9cfd2260..4e3683d8 100644 --- a/ahoy-ui/projects/ahoy-components/src/lib/releases/releases.component.html +++ b/ahoy-ui/projects/ahoy-components/src/lib/releases/releases.component.html @@ -88,8 +88,8 @@
Releases
pTooltip="Edit release" *appUserRole="[Role.admin, Role.releasemanager, Role.developer]"> diff --git a/ahoy-ui/projects/ahoy-components/src/lib/releases/releases.component.ts b/ahoy-ui/projects/ahoy-components/src/lib/releases/releases.component.ts index 0e98df46..73ab4557 100644 --- a/ahoy-ui/projects/ahoy-components/src/lib/releases/releases.component.ts +++ b/ahoy-ui/projects/ahoy-components/src/lib/releases/releases.component.ts @@ -28,8 +28,7 @@ import {TaskEvent} from '../taskevents/task-events'; import {Role} from '../util/auth'; import {LoggerService} from '../util/logger.service'; import {AddToEnvironmentDialogComponent} from './add-to-environment-dialog/add-to-environment-dialog.component'; -import {DuplicateDialogComponent} from './duplicate-dialog/duplicate-dialog.component'; -import {DuplicateOptions, Release} from './release'; +import {Release} from './release'; import {ReleaseService} from './release.service'; @Component({ @@ -87,20 +86,6 @@ export class ReleasesComponent implements OnInit { ).subscribe(() => this.getReleases()); } - duplicate(event: Event, release: Release) { - const dialogConfig = new DynamicDialogConfig(); - dialogConfig.header = `Duplicate ${(release.name)}`; - dialogConfig.data = {selectedRelease: release}; - - const dialogRef = this.dialogService.open(DuplicateDialogComponent, dialogConfig); - dialogRef.onClose.pipe( - filter((result) => result !== undefined), // cancelled - mergeMap((duplicateOptions: DuplicateOptions) => { - return this.releaseService.duplicate(release, duplicateOptions); - }) - ).subscribe(() => this.getReleases()); - } - deleteRelease(event, release: Release) { // TODO nested subscribes this.confirmationService.confirm({ diff --git a/pom.xml b/pom.xml index ddbd3b27..84753b9a 100644 --- a/pom.xml +++ b/pom.xml @@ -27,7 +27,7 @@ za.co.lsd.ahoy ahoy - 0.14.0 + 0.15.0 pom ahoy