diff --git a/NOTICE b/NOTICE index 077703a2e2e012e4bee28dd7fb7a75dfd9de1fdf..93fafe44cbccd99420d824e0eb4417513ac1d43a 100644 --- a/NOTICE +++ b/NOTICE @@ -1,4 +1,4 @@ -Copyright 2023 GÉANT Association +Copyright 2024 GÉANT Association Contributions to this work were made on behalf of the GÉANT project, a project that has received funding from the European Union’s Horizon 2020 research and innovation programme under Grant Agreement No. 731122 (GN4-2) diff --git a/build.gradle b/build.gradle index eb6c9ba6988c509f39cfa337f1f3b8e862b6349b..dc58eebd2155b8a580675b5ba04f494d819dba5e 100644 --- a/build.gradle +++ b/build.gradle @@ -13,7 +13,7 @@ repositories { mavenCentral() } -version = '1.7.-SNAPSHOT' +version = '1.6.2-SNAPSHOT' group = 'net.geant.nmaas' java { diff --git a/build_and_publish.sh b/build_and_publish.sh deleted file mode 100644 index 712a57ed64dd627c7685e8d1e55e282ea0d72fb3..0000000000000000000000000000000000000000 --- a/build_and_publish.sh +++ /dev/null @@ -1,7 +0,0 @@ -#!/bin/bash - -TAG=1.7.0 -PACKAGE=nmaas-platform -REPOSITORY=artifactory.geant.net/nmaas-docker-local -sudo docker build --rm -t $REPOSITORY/$PACKAGE:$TAG -f ./Dockerfile .. -sudo docker push $REPOSITORY/$PACKAGE:$TAG diff --git a/docker/nmaas-platform.properties.template b/docker/nmaas-platform.properties.template index 70e00fe0432d363b0489fe7cac226a6ab10aea6e..b2417a17e202706d86b26e86f2d1e6a19b6144d2 100644 --- a/docker/nmaas-platform.properties.template +++ b/docker/nmaas-platform.properties.template @@ -141,8 +141,7 @@ janitor.port=${JANITOR_PORT} # -------------------- # # GitLab configuration # # -------------------- # -gitlab.address=${GITLAB_ADDRESS} -gitlab.port=${GITLAB_PORT} +gitlab.apiUrl=${GITLAB_API_URL} gitlab.token=${GITLAB_TOKEN} # ------------------------ # diff --git a/src/integrationTest/resources/application.properties b/src/integrationTest/resources/application.properties index f97e69412aab321e14b36f930f78ccb9105ef1c5..cba386b644c32db8498fd14e6eff98ffbb8db03f 100644 --- a/src/integrationTest/resources/application.properties +++ b/src/integrationTest/resources/application.properties @@ -118,8 +118,7 @@ janitor.port=5000 # -------------------- # # GitLab configuration # # -------------------- # -gitlab.address=nmaas-gitlab-unicorn -gitlab.port=8080 +gitlab.apiUrl=http://nmaas-gitlab-unicorn:8080 gitlab.token=test_gitlab_token # ------------------------ # diff --git a/src/main/java/net/geant/nmaas/externalservices/gitlab/GitLabManager.java b/src/main/java/net/geant/nmaas/externalservices/gitlab/GitLabManager.java index f763eff5655241556b5973edf1795d09d1cb082d..a833ba2ef14981fdaeb4967eceda955fecccd8a5 100644 --- a/src/main/java/net/geant/nmaas/externalservices/gitlab/GitLabManager.java +++ b/src/main/java/net/geant/nmaas/externalservices/gitlab/GitLabManager.java @@ -22,21 +22,16 @@ import static com.google.common.base.Preconditions.checkArgument; @Log4j2 public class GitLabManager { - @Value("${gitlab.address}") - private String gitLabAddress; + private static final String GITLAB_API_NAMESPACE = "/api/v4"; - @Value("${gitlab.port}") - private Integer gitLabPort; + @Value("${gitlab.apiUrl}") + private String gitLabApiUrl; @Value("${gitlab.token}") private String gitLabToken; - public String getGitlabServer() { - return this.gitLabAddress; - } - - public int getGitlabPort() { - return this.gitLabPort; + public String getGitLabApiUrl() { + return this.gitLabApiUrl; } public GroupApi groups() { @@ -59,19 +54,20 @@ public class GitLabManager { return new GitLabApi(GitLabApi.ApiVersion.V4, getApiUrl(), this.gitLabToken); } - private String getApiUrl(){ - return String.format("http://%s:%d", this.gitLabAddress, this.gitLabPort); + String getApiUrl() { + return gitLabApiUrl.endsWith(GITLAB_API_NAMESPACE) + ? gitLabApiUrl.substring(0, gitLabApiUrl.length() - GITLAB_API_NAMESPACE.length()) + : gitLabApiUrl; } public void validateGitLabInstance() { - checkArgument(this.gitLabAddress != null && !this.gitLabAddress.isEmpty(), "GitLab address is null or empty"); - checkArgument(this.gitLabPort != null, "GitLab port is null"); + checkArgument(this.gitLabApiUrl != null && !this.gitLabApiUrl.isEmpty(), "GitLab api URL is null or empty"); checkArgument(this.gitLabToken != null && !this.gitLabToken.isEmpty(), "GitLab token is null or empty"); try { api().getVersion(); log.trace("GitLab instance is running"); - } catch (GitLabApiException e){ - throw new GitLabInvalidConfigurationException("GitLab instance is not running -> " + e.getMessage()); + } catch (GitLabApiException e) { + throw new GitLabInvalidConfigurationException("GitLab instance doesn't respond -> " + e.getMessage()); } } diff --git a/src/main/java/net/geant/nmaas/nmservice/configuration/gitlab/GitLabConfigHandler.java b/src/main/java/net/geant/nmaas/nmservice/configuration/gitlab/GitLabConfigHandler.java index 1ae22d4c96ef0bd1975b28de4655bbcd1fa319ff..646309ed57fd872851bf36787ae1380beea50bc7 100644 --- a/src/main/java/net/geant/nmaas/nmservice/configuration/gitlab/GitLabConfigHandler.java +++ b/src/main/java/net/geant/nmaas/nmservice/configuration/gitlab/GitLabConfigHandler.java @@ -201,8 +201,8 @@ public class GitLabConfigHandler implements GitConfigHandler { String getHttpUrlToRepo(Integer gitLabProjectId) throws GitLabApiException { String[] urlFromGitlabApiParts = gitLabManager.projects().getProject(gitLabProjectId).getHttpUrlToRepo().split("//"); String[] urlParts = urlFromGitlabApiParts[1].split("/"); - urlParts[0] = gitLabManager.getGitlabServer() + ":" + gitLabManager.getGitlabPort(); - return urlFromGitlabApiParts[0] + "//" + String.join("/", urlParts); + urlParts[0] = gitLabManager.getGitLabApiUrl(); + return String.join("/", urlParts); } @Override @@ -261,8 +261,8 @@ public class GitLabConfigHandler implements GitConfigHandler { * * @param deploymentId unique identifier of service deployment * @param configIds list of identifiers of configuration files that should be loaded from database and uploaded to the git repository - * @throws InvalidDeploymentIdException if a service for given deployment identifier could not be found in database - * @throws ConfigFileNotFoundException if any of the configuration files for which an identifier is given could not be found in database + * @throws InvalidDeploymentIdException if a service for given deployment identifier could not be found in the database + * @throws ConfigFileNotFoundException if any of the configuration files for which an identifier is given could not be found in the database * @throws FileTransferException if any error occurs during communication with the git repository API */ @Override diff --git a/src/main/java/net/geant/nmaas/portal/api/market/ApplicationController.java b/src/main/java/net/geant/nmaas/portal/api/market/ApplicationController.java index 38ab004726d458a1a5dac6551cab35c433a6bb0f..b5c106c37295a6a8a86314799fa46509977a444a 100644 --- a/src/main/java/net/geant/nmaas/portal/api/market/ApplicationController.java +++ b/src/main/java/net/geant/nmaas/portal/api/market/ApplicationController.java @@ -9,6 +9,8 @@ import lombok.extern.log4j.Log4j2; import net.geant.nmaas.notifications.MailAttributes; import net.geant.nmaas.notifications.NotificationEvent; import net.geant.nmaas.notifications.templates.MailType; +import net.geant.nmaas.orchestration.AppLifecycleManager; +import net.geant.nmaas.portal.api.domain.AppInstanceState; import net.geant.nmaas.portal.api.domain.AppRateView; import net.geant.nmaas.portal.api.domain.ApplicationBaseView; import net.geant.nmaas.portal.api.domain.ApplicationStateChangeRequest; @@ -26,6 +28,7 @@ import net.geant.nmaas.portal.persistent.entity.ApplicationVersion; import net.geant.nmaas.portal.persistent.entity.Role; import net.geant.nmaas.portal.persistent.entity.User; import net.geant.nmaas.portal.persistent.repositories.RatingRepository; +import net.geant.nmaas.portal.service.ApplicationInstanceService; import net.geant.nmaas.portal.service.impl.ApplicationServiceImpl; import org.springframework.context.ApplicationEventPublisher; import org.springframework.http.HttpStatus; @@ -68,10 +71,25 @@ public class ApplicationController extends AppBaseController { private ApplicationView application; } + @AllArgsConstructor + @NoArgsConstructor + @Getter + @Setter + public static class ApplicationDTOVersionList { + private ApplicationBaseView applicationBase; + private List<ApplicationView> applications; + } + private final ApplicationEventPublisher eventPublisher; private final RatingRepository ratingRepository; + private final ApplicationInstanceService applicationInstanceService; + + private final AppLifecycleManager appLifecycleManager; + + private final AppInstanceController appInstanceController; + /* * Application Base Part */ @@ -134,8 +152,7 @@ public class ApplicationController extends AppBaseController { @PreAuthorize("hasRole('ROLE_SYSTEM_ADMIN') || hasRole('ROLE_TOOL_MANAGER')") @Transactional public void updateApplicationBaseOwner(@PathVariable Long id, @PathVariable String owner, Principal principal) { - // only system admin and owner can update application base - log.info("Upate owner for application {} to {}", id, owner); + log.info("Updating owner of application {} to {}", id, owner); this.applicationBaseOwnerCheck(id, principal); appBaseService.updateOwner(id, owner); } @@ -147,13 +164,12 @@ public class ApplicationController extends AppBaseController { ApplicationBase base = appBaseService.getBaseApp(id); // only system admin and owner can update application base this.applicationBaseOwnerCheck(base.getName(), principal); - ApplicationState state = ApplicationState.DELETED; for (ApplicationVersion appVersion : base.getVersions()) { Application app = getApp(appVersion.getAppVersionId()); - applicationService.changeApplicationState(app, state); - appVersion.setState(state); + if (app.getState() != ApplicationState.DELETED) { + throw new ProcessingException("Can't delete " + base.getName() + " application base since version " + app.getVersion() + " is not deleted"); + } } - appBaseService.deleteAppBase(base); } @@ -198,13 +214,26 @@ public class ApplicationController extends AppBaseController { ); } - @GetMapping(value="/versions/{id}") + @GetMapping(value = "/base/allversions/{id}") + @Transactional + public ApplicationDTOVersionList getApplicationDTOWithAllVersions(@PathVariable Long id) { + ApplicationBase base = appBaseService.getBaseApp(id); + List<Application> versionList = this.applicationService.findAll().stream() + .filter(app -> app.getName().equalsIgnoreCase(base.getName())) + .collect(Collectors.toList()); + return new ApplicationDTOVersionList( + modelMapper.map(base, ApplicationBaseView.class), + versionList.stream().map(app->modelMapper.map(app, ApplicationView.class)).collect(Collectors.toList()) + ); + } + + @GetMapping(value = "/versions/{id}") @Transactional public Set<ApplicationVersion> getApplicationVersion(@PathVariable Long id) { return this.getVersions(id); } - @GetMapping(value="/version/{id}") + @GetMapping(value = "/version/{id}") @Transactional public ApplicationView getApplication(@PathVariable Long id) { Application app = getApp(id); @@ -268,7 +297,7 @@ public class ApplicationController extends AppBaseController { this.applicationBaseOwnerCheck(view.getName(), principal); // check if id exists - if(view.getId() == null) { + if (view.getId() == null) { log.error("ID is not present in Application update"); throw new ProcessingException("Cannot update application without id"); } @@ -277,12 +306,12 @@ public class ApplicationController extends AppBaseController { Optional<Application> optId = applicationService.findApplication(view.getId()); Optional<Application> optNameVersion = applicationService.findApplication(view.getName(), view.getVersion()); - if(optId.isEmpty() || optNameVersion.isEmpty()) { + if (optId.isEmpty() || optNameVersion.isEmpty()) { log.error("Requested application does not exist"); throw new MissingElementException("Application does not exist"); } - if(!optId.get().equals(optNameVersion.get())) { + if (!optId.get().equals(optNameVersion.get())) { log.error("Retrieved different applications using id and name&version, update aborted"); throw new ProcessingException("You cannot change application name, version and id"); } @@ -318,8 +347,17 @@ public class ApplicationController extends AppBaseController { @PatchMapping(value = "/state/{id}") @PreAuthorize("hasRole('ROLE_SYSTEM_ADMIN')") @Transactional - public void changeApplicationState(@PathVariable long id, @RequestBody ApplicationStateChangeRequest stateChangeRequest) { + public void changeApplicationState(@PathVariable long id, @RequestBody ApplicationStateChangeRequest stateChangeRequest, Principal principal) { Application app = getApp(id); + if (stateChangeRequest.getState().equals(ApplicationState.DELETED)) { + long numberOfRunningInstances = applicationInstanceService.findAllByApplication(app).stream() + .map(ai -> appInstanceController.getState(ai.getId(), principal)) + .filter(s -> !List.of(AppInstanceState.DONE, AppInstanceState.FAILURE, AppInstanceState.REMOVED).contains(s.getState())) + .count(); + if (numberOfRunningInstances > 0) { + throw new ProcessingException("Can not set state to Disabled. There is still " + numberOfRunningInstances + " running instances of this version."); + } + } applicationService.changeApplicationState(app, stateChangeRequest.getState()); appBaseService.updateApplicationVersionState(app.getName(), app.getVersion(), stateChangeRequest.getState()); this.sendMails(app, stateChangeRequest); diff --git a/src/main/java/net/geant/nmaas/portal/service/impl/ApplicationBaseServiceImpl.java b/src/main/java/net/geant/nmaas/portal/service/impl/ApplicationBaseServiceImpl.java index 99c33f6a9c286eeb589489bf7496d6efd0bdb500..b14732966429676d91f30800fff6e5ccf3706c51 100644 --- a/src/main/java/net/geant/nmaas/portal/service/impl/ApplicationBaseServiceImpl.java +++ b/src/main/java/net/geant/nmaas/portal/service/impl/ApplicationBaseServiceImpl.java @@ -97,7 +97,7 @@ public class ApplicationBaseServiceImpl implements ApplicationBaseService { .findAny() .ifPresent(appVersion -> appVersion.setState(state)); appBase.validate(); - appBaseRepository.save(appBase); + appBaseRepository.save(appBase); if (state.equals(ApplicationState.ACTIVE)) { eventPublisher.publishEvent(new ApplicationActivatedEvent(this, name, version)); } diff --git a/src/main/resources/application.properties b/src/main/resources/application.properties index e9ab18a79304d4c48d0f6c4046eea9c2747dda0a..5d22cdb9d5f50a31bac206f0a4887fe490ff621f 100644 --- a/src/main/resources/application.properties +++ b/src/main/resources/application.properties @@ -140,6 +140,7 @@ helm.enableTls=${HELM_ENABLETLS:false} # possible values for Helm version are v2 and v3 (if none is provided v3 is used by default) helm.version=${HELM_VERSION:v2} + # --------------------- # # Janitor configuration # # --------------------- # @@ -149,8 +150,8 @@ janitor.port=${JANITOR_PORT:5000} # -------------------- # # GitLab configuration # # -------------------- # -gitlab.address=${GITLAB_ADDRESS:nmaas-gitlab-unicorn} -gitlab.port=${GITLAB_PORT:8080} + +gitlab.apiUrl=${GITLAB_ADDRESS:http://nmaas-gitlab-unicorn:8080} gitlab.token=${GITLAB_TOKEN:test_gitlab_token} # ------------------------ # diff --git a/src/main/resources/changelog.json b/src/main/resources/changelog.json index 9c765bbec6f247898fdfdb8565a9040a7d700ba8..a49d6c2c5732ce99a47ad89198c6e977b6749155 100644 --- a/src/main/resources/changelog.json +++ b/src/main/resources/changelog.json @@ -1,5 +1,16 @@ { "versions" : [ + { + "verNo" : "1.6.2", + "date" : "(2024/07/xx)", + "topic" : [ + { + "title" : "Fixed issue with displaying user details view", + "tags" : "[Bugfix]", + "description" : "Fix was applied to used password handling component affecting few views." + } + ] + }, { "verNo" : "1.6.1", "date" : "(2024/05/22)", diff --git a/src/test/java/net/geant/nmaas/externalservices/gitlab/GitLabManagerTest.java b/src/test/java/net/geant/nmaas/externalservices/gitlab/GitLabManagerTest.java index 87508027859755e52512538a480d677a114e970b..f280101b031cee1aac45bdebd2b3f9b64f654b36 100644 --- a/src/test/java/net/geant/nmaas/externalservices/gitlab/GitLabManagerTest.java +++ b/src/test/java/net/geant/nmaas/externalservices/gitlab/GitLabManagerTest.java @@ -3,31 +3,37 @@ package net.geant.nmaas.externalservices.gitlab; import net.geant.nmaas.externalservices.gitlab.exceptions.GitLabInvalidConfigurationException; import org.junit.jupiter.api.Test; +import static org.assertj.core.api.Assertions.assertThat; +import static org.junit.jupiter.api.Assertions.assertEquals; import static org.junit.jupiter.api.Assertions.assertThrows; import static org.junit.jupiter.api.Assertions.assertTrue; public class GitLabManagerTest { - private GitLabManager manager; + private final GitLabManager manager = new GitLabManager(); @Test void shouldValidateGitLabInstance() { - manager = new GitLabManager(); Exception thrown; - thrown = assertThrows(IllegalArgumentException.class, () -> manager.validateGitLabInstance()); - assertTrue(thrown.getMessage().contains("GitLab address is null or empty")); + thrown = assertThrows(IllegalArgumentException.class, manager::validateGitLabInstance); + assertTrue(thrown.getMessage().contains("GitLab api URL is null or empty")); - manager.setGitLabAddress("localhost"); - thrown = assertThrows(IllegalArgumentException.class, () -> manager.validateGitLabInstance()); - assertTrue(thrown.getMessage().contains("GitLab port is null")); - - manager.setGitLabPort(8080); - thrown = assertThrows(IllegalArgumentException.class, () -> manager.validateGitLabInstance()); - assertTrue(thrown.getMessage().contains("GitLab token is null or empty")); + manager.setGitLabApiUrl("http://localhost:8080"); + thrown = assertThrows(IllegalArgumentException.class, manager::validateGitLabInstance); + assertThat(thrown.getMessage()).contains("GitLab token is null or empty"); manager.setGitLabToken("token"); - assertThrows(GitLabInvalidConfigurationException.class, () -> manager.validateGitLabInstance()); + assertThrows(GitLabInvalidConfigurationException.class, manager::validateGitLabInstance); + } + + @Test + void shouldPrepareApiBaseUrl() { + manager.setGitLabApiUrl("http://localhost:8080"); + assertEquals("http://localhost:8080", manager.getApiUrl()); + + manager.setGitLabApiUrl("http://localhost:8080/api/v4"); + assertEquals("http://localhost:8080", manager.getApiUrl()); } } diff --git a/src/test/java/net/geant/nmaas/nmservice/configuration/gitlab/GitLabConfigHandlerTest.java b/src/test/java/net/geant/nmaas/nmservice/configuration/gitlab/GitLabConfigHandlerTest.java index 13e2796521e34dbba0988c556cb9724b7a25de45..aaa8d4661a966fad4e81c43cf9ca4874a87e4ebb 100644 --- a/src/test/java/net/geant/nmaas/nmservice/configuration/gitlab/GitLabConfigHandlerTest.java +++ b/src/test/java/net/geant/nmaas/nmservice/configuration/gitlab/GitLabConfigHandlerTest.java @@ -47,8 +47,7 @@ public class GitLabConfigHandlerTest { Project project = mock(Project.class); when(projectApi.getProject(anyInt())).thenReturn(project); when(project.getHttpUrlToRepo()).thenReturn("http://example.gitlab.com/group/project.git"); - when(gitLabManager.getGitlabServer()).thenReturn("test-server"); - when(gitLabManager.getGitlabPort()).thenReturn(80); + when(gitLabManager.getGitLabApiUrl()).thenReturn("http://test-server:80"); when(gitLabManager.projects()).thenReturn(projectApi); String result = handler.getHttpUrlToRepo(1); diff --git a/src/test/resources/application.properties b/src/test/resources/application.properties index 0ba2e0bc557f21f092dac9c74f826626c5265095..e5c378cb9c4a0d2a3293faa2b8a10c7ade3944ba 100644 --- a/src/test/resources/application.properties +++ b/src/test/resources/application.properties @@ -131,8 +131,7 @@ janitor.port=5000 # -------------------- # # GitLab configuration # # -------------------- # -gitlab.address=nmaas-gitlab-unicorn -gitlab.port=8080 +gitlab.apiUrl=http://nmaas-gitlab-unicorn:8080 gitlab.token=test_gitlab_token # ------------------------ # diff --git a/src/test/shell/data/i18n/de.json b/src/test/shell/data/i18n/de.json index 6d0b19d12f24876d4953b45d8279058ef58ae479..6399c7727a09b2d9554b529523c7dfaec018b137 100644 --- a/src/test/shell/data/i18n/de.json +++ b/src/test/shell/data/i18n/de.json @@ -735,6 +735,7 @@ "VIEW_BUTTON": "Anzeigen", "EDIT_BUTTON": "Bearbeiten", "DELETE_BUTTON": "Remove", + "SELECT_VERSION" : "Select version to be base in creator", "CONFIRM_REMOVAL" : { "HEADER": "Confirm application removal", "DESCRIPTION": "Do you want to completely remove this application from the catalogue along with all its versions?", diff --git a/src/test/shell/data/i18n/en.json b/src/test/shell/data/i18n/en.json index db0f617fa2cfdbc459e50a754b156396ed3f132e..a3e1a365ce6e285a2f56fcf79f1e32b483c34841 100644 --- a/src/test/shell/data/i18n/en.json +++ b/src/test/shell/data/i18n/en.json @@ -736,6 +736,7 @@ "VIEW_BUTTON": "View", "EDIT_BUTTON": "Edit", "DELETE_BUTTON": "Remove", + "SELECT_VERSION" : "Select version to be base in creator", "CONFIRM_REMOVAL" : { "HEADER": "Confirm application removal", "DESCRIPTION": "Do you want to completely remove this application from the catalogue along with all its versions?", diff --git a/src/test/shell/data/i18n/fr.json b/src/test/shell/data/i18n/fr.json index 2efa5331df0fdb4e9f60f1591a2014d056e0370c..8a1ade28d1092890f07ca38e5467d8fafd0f23c2 100644 --- a/src/test/shell/data/i18n/fr.json +++ b/src/test/shell/data/i18n/fr.json @@ -734,6 +734,7 @@ "VIEW_BUTTON": "View", "EDIT_BUTTON": "Edit", "DELETE_BUTTON": "Remove", + "SELECT_VERSION" : "Select version to be base in creator", "CONFIRM_REMOVAL" : { "HEADER": "Confirm application removal", "DESCRIPTION": "Do you want to completely remove this application from the catalogue along with all its versions?", diff --git a/src/test/shell/data/i18n/pl.json b/src/test/shell/data/i18n/pl.json index 992805e424c5f38f174d0e0624d17e969b5c652b..19e904699840b18e74475e72252277d1205594c0 100644 --- a/src/test/shell/data/i18n/pl.json +++ b/src/test/shell/data/i18n/pl.json @@ -735,6 +735,7 @@ "VIEW_BUTTON": "Pokaż", "EDIT_BUTTON": "Edytuj", "DELETE_BUTTON": "Usuń", + "SELECT_VERSION" : "Wybierz wersję aplikacji, która ma być podstawą w kreatorze", "CONFIRM_REMOVAL" : { "HEADER": "Potwierdź usunięcie aplikacji", "DESCRIPTION": "Czy na pewno chcesz całkowicie usunąć tą aplikację z katalogu razem ze wszystkimi wersjami?",