diff --git a/.gitlab-ci.yml b/.gitlab-ci.yml index 7b6bb91bf9cf87a3412e89cd79dadef2f8bddf3f..98109fce69469945dc35c2c9e808ca05fef4a109 100644 --- a/.gitlab-ci.yml +++ b/.gitlab-ci.yml @@ -53,6 +53,9 @@ variables: METASOLVER_CLI: "mvn --batch-mode -N -Dmaven.test.skip=$SKIP_TESTS -Ddocker.push=false -f meta_solver/pom.xml" MQ_ADAPTER_CLI: "mvn --batch-mode -N -Dmaven.test.skip=$SKIP_TESTS -Ddocker.push=false -f mq-http-adapter/pom.xml" GUI_BACKEND_CLI: "mvn --batch-mode -N -Dmaven.test.skip=$SKIP_TESTS -Ddocker.push=false -f gui-backend/pom.xml" + + FUNCTIONIZER_TESTING_TOOL_CLI: "mvn --batch-mode -N -Dmaven.test.skip=$SKIP_TESTS -Ddocker.push=false -f functionizer-testing-tool/pom.xml" + cache: paths: - maven_repo/ @@ -737,3 +740,18 @@ deploy:gui-backend: - echo "$K8S_SECRET_DOCKER_PASSWORD" | docker login $CI_REGISTRY -u $K8S_SECRET_DOCKER_USER --password-stdin - docker tag $LOCAL_REPO/melodic/gui-backend:unknown $CI_REGISTRY_IMAGE/gui-backend:$CI_COMMIT_BRANCH - docker push $CI_REGISTRY_IMAGE/gui-backend:$CI_COMMIT_BRANCH + +deploy:functionizer-testing-tool: + stage: deploy + only: + - master + - rc3.0 + - rc3.1 + image: $DOCKER_DIND_IMAGE + services: + - $DOCKER_DIND_SERVICE + script: + - $DOCKER_CLI $FUNCTIONIZER_TESTING_TOOL_CLI -Ddocker.imagePrefix=melodic/ clean install + - echo "$K8S_SECRET_DOCKER_PASSWORD" | docker login $CI_REGISTRY -u $K8S_SECRET_DOCKER_USER --password-stdin + - docker tag $LOCAL_REPO/melodic/functionizer-testing-tool:unknown $CI_REGISTRY_IMAGE/functionizer-testing-tool:$CI_COMMIT_BRANCH + - docker push $CI_REGISTRY_IMAGE/functionizer-testing-tool:$CI_COMMIT_BRANCH diff --git a/functionizer-testing-tool/pom.xml b/functionizer-testing-tool/pom.xml new file mode 100644 index 0000000000000000000000000000000000000000..36111ed6327842ce1d28ec281c086dfb5d10896d --- /dev/null +++ b/functionizer-testing-tool/pom.xml @@ -0,0 +1,187 @@ + + + + upperware + org.ow2.paasage + 3.1.0-SNAPSHOT + + 4.0.0 + + eu.functionizer + functionizer-testing-tool + Upperware - Functionizer testing tool + + + 1.8 + 3.1.0-SNAPSHOT + 3.1.0-SNAPSHOT + + functionizer-testing-tool + + + + + org.springframework + spring-beans + + + org.springframework + spring-context + + + org.springframework + spring-core + + + org.springframework + spring-web + + + org.springframework + spring-test + + + + org.springframework.boot + spring-boot + + + org.springframework.boot + spring-boot-autoconfigure + + + org.springframework.boot + spring-boot-starter-test + + + + org.slf4j + slf4j-api + 1.7.25 + + + org.yaml + snakeyaml + 1.23 + + + + org.ow2.paasage + melodic-commons + + + + org.ow2.paasage + jwt-commons + + + + com.amazonaws + aws-java-sdk-core + 1.11.327 + + + com.amazonaws + aws-java-sdk-lambda + 1.11.327 + + + + com.azure + azure-identity + 1.0.4 + + + + com.microsoft.azure + azure-mgmt-resources + 1.33.1 + + + + com.microsoft.azure + azure + 1.33.1 + + + + com.microsoft.azure.functions + azure-functions-java-library + 1.3.1 + + + + com.google.apis + google-api-services-cloudfunctions + v1-rev20200219-1.30.9 + + + + org.assertj + assertj-core + 3.13.2 + compile + + + + junit + junit + + + + org.junit.platform + junit-platform-launcher + + + org.junit.jupiter + junit-jupiter-engine + + + org.junit.jupiter + junit-jupiter-api + 5.5.2 + compile + + + org.junit.platform + junit-platform-reporting + 1.6.2 + + + io.github.cloudiator.client + java-rest + + + + + + ${project.artifactId} + + + org.springframework.boot + spring-boot-maven-plugin + + + + + com.spotify + docker-maven-plugin + + + org.codehaus.mojo + buildnumber-maven-plugin + + + org.codehaus.groovy.maven + gmaven-plugin + + + + + + + + diff --git a/functionizer-testing-tool/src/main/docker/Dockerfile b/functionizer-testing-tool/src/main/docker/Dockerfile new file mode 100644 index 0000000000000000000000000000000000000000..50b4981b4549f35646d32a41474833ad18ad700f --- /dev/null +++ b/functionizer-testing-tool/src/main/docker/Dockerfile @@ -0,0 +1,9 @@ +FROM java:8-alpine +VOLUME /tmp +ADD functionizer-testing-tool.jar functionizer-testing-tool.jar +RUN /bin/sh -c "apk add --no-cache bash" +RUN bash -c 'touch /functionizer-testing-tool.jar' + +COPY ./run.sh . +RUN bash -c 'chmod +rx run.sh' +ENTRYPOINT ["./run.sh"] diff --git a/functionizer-testing-tool/src/main/docker/run.sh b/functionizer-testing-tool/src/main/docker/run.sh new file mode 100644 index 0000000000000000000000000000000000000000..be98ed0704535b4f5d2a156b92cb466f26154c8c --- /dev/null +++ b/functionizer-testing-tool/src/main/docker/run.sh @@ -0,0 +1,7 @@ +#!/bin/sh + +/config/wait-for-cdo.sh && java \ +-Duser.timezone=Europe/Warsaw \ +-Djava.security.egd=file:/dev/./urandom \ +-agentlib:jdwp=transport=dt_socket,server=y,suspend=n,address=5005 \ +-jar functionizer-testing-tool.jar diff --git a/functionizer-testing-tool/src/main/java/eu/functionizer/functionizertestingtool/FunctionizerTestingToolApplication.java b/functionizer-testing-tool/src/main/java/eu/functionizer/functionizertestingtool/FunctionizerTestingToolApplication.java new file mode 100644 index 0000000000000000000000000000000000000000..7a528667365321cb23f50d4f1bb86a5ed1ac2044 --- /dev/null +++ b/functionizer-testing-tool/src/main/java/eu/functionizer/functionizertestingtool/FunctionizerTestingToolApplication.java @@ -0,0 +1,17 @@ +package eu.functionizer.functionizertestingtool; + +import eu.paasage.upperware.security.authapi.properties.MelodicSecurityProperties; +import eu.passage.upperware.commons.cloudiator.CloudiatorProperties; +import org.springframework.boot.SpringApplication; +import org.springframework.boot.autoconfigure.SpringBootApplication; +import org.springframework.boot.context.properties.EnableConfigurationProperties; + +@SpringBootApplication +@EnableConfigurationProperties({CloudiatorProperties.class, MelodicSecurityProperties.class}) +public class FunctionizerTestingToolApplication { + + public static void main(String[] args) { + SpringApplication.run(FunctionizerTestingToolApplication.class, args); + } + +} diff --git a/functionizer-testing-tool/src/main/java/eu/functionizer/functionizertestingtool/FunctionizerTestingToolController.java b/functionizer-testing-tool/src/main/java/eu/functionizer/functionizertestingtool/FunctionizerTestingToolController.java new file mode 100644 index 0000000000000000000000000000000000000000..24afed03152ad5f4ba06e90b1618fe7e60dfcf67 --- /dev/null +++ b/functionizer-testing-tool/src/main/java/eu/functionizer/functionizertestingtool/FunctionizerTestingToolController.java @@ -0,0 +1,37 @@ +package eu.functionizer.functionizertestingtool; + + +import eu.functionizer.functionizertestingtool.model.FunctionizerTestResult; +import eu.functionizer.functionizertestingtool.service.TestRunner; +import lombok.AllArgsConstructor; +import lombok.extern.slf4j.Slf4j; +import org.springframework.beans.factory.annotation.Autowired; +import org.springframework.http.HttpStatus; +import org.springframework.web.bind.annotation.*; + + +@RestController +@Slf4j +@AllArgsConstructor(onConstructor = @__(@Autowired)) +public class FunctionizerTestingToolController { + + private TestRunner testRunner; + + @RequestMapping("/") + @ResponseStatus(HttpStatus.OK) + public String index() { + return "Welcome to the Functionizer testing tool!"; + } + + @GetMapping(value = "/health") + @ResponseStatus(HttpStatus.OK) + public void health() { + } + + @PostMapping("/test") + @ResponseStatus(HttpStatus.OK) + public FunctionizerTestResult runTests() { + return testRunner.runTests(); + } + +} diff --git a/functionizer-testing-tool/src/main/java/eu/functionizer/functionizertestingtool/TestingToolContext.java b/functionizer-testing-tool/src/main/java/eu/functionizer/functionizertestingtool/TestingToolContext.java new file mode 100644 index 0000000000000000000000000000000000000000..66bc8d6afd0c70d86e78b67e844228f7bffd3e8e --- /dev/null +++ b/functionizer-testing-tool/src/main/java/eu/functionizer/functionizertestingtool/TestingToolContext.java @@ -0,0 +1,16 @@ +package eu.functionizer.functionizertestingtool; + +import eu.paasage.upperware.security.authapi.properties.MelodicSecurityProperties; +import eu.paasage.upperware.security.authapi.token.JWTService; +import eu.paasage.upperware.security.authapi.token.JWTServiceImpl; +import org.springframework.context.annotation.Bean; +import org.springframework.context.annotation.Configuration; + +@Configuration +public class TestingToolContext { + + @Bean + public JWTService jWTService(MelodicSecurityProperties melodicSecurityProperties) { + return new JWTServiceImpl(melodicSecurityProperties); + } +} diff --git a/functionizer-testing-tool/src/main/java/eu/functionizer/functionizertestingtool/WebSecurity.java b/functionizer-testing-tool/src/main/java/eu/functionizer/functionizertestingtool/WebSecurity.java new file mode 100644 index 0000000000000000000000000000000000000000..0b3966ff68d6b2727d7d0fc0d85b2a9d73c26e6a --- /dev/null +++ b/functionizer-testing-tool/src/main/java/eu/functionizer/functionizertestingtool/WebSecurity.java @@ -0,0 +1,45 @@ +package eu.functionizer.functionizertestingtool; + +import eu.paasage.upperware.security.authapi.JWTAuthorizationFilter; +import eu.paasage.upperware.security.authapi.token.JWTService; +import lombok.RequiredArgsConstructor; +import lombok.extern.slf4j.Slf4j; +import org.springframework.beans.factory.annotation.Autowired; +import org.springframework.beans.factory.annotation.Value; +import org.springframework.security.config.annotation.method.configuration.EnableGlobalMethodSecurity; +import org.springframework.security.config.annotation.web.builders.HttpSecurity; +import org.springframework.security.config.annotation.web.configuration.EnableWebSecurity; +import org.springframework.security.config.annotation.web.configuration.WebSecurityConfigurerAdapter; +import org.springframework.security.config.http.SessionCreationPolicy; + +@Slf4j +@EnableWebSecurity +@EnableGlobalMethodSecurity(prePostEnabled = true) +@RequiredArgsConstructor(onConstructor = @__(@Autowired)) +public class WebSecurity extends WebSecurityConfigurerAdapter { + + private final JWTService jwtService; + + @Value("${melodic.security.enabled:true}") + private boolean securityEnabled; + + @Override + protected void configure(HttpSecurity http) throws Exception { + + if (securityEnabled) { + log.info("Running WITH security"); + http.cors().and().csrf().disable().authorizeRequests() + .anyRequest().authenticated() + .and() + .addFilter(new JWTAuthorizationFilter(authenticationManager(), jwtService)) + // this disables session creation on Spring Security + .sessionManagement().sessionCreationPolicy(SessionCreationPolicy.STATELESS); + } else { + log.info("Running WITHOUT security"); + http.csrf().disable() + .authorizeRequests() + .antMatchers("/**").permitAll() + .anyRequest().authenticated(); + } + } +} diff --git a/functionizer-testing-tool/src/main/java/eu/functionizer/functionizertestingtool/model/CloudiatorData.java b/functionizer-testing-tool/src/main/java/eu/functionizer/functionizertestingtool/model/CloudiatorData.java new file mode 100644 index 0000000000000000000000000000000000000000..44990d100bafe39b21a13740a12bc4e647137ebd --- /dev/null +++ b/functionizer-testing-tool/src/main/java/eu/functionizer/functionizertestingtool/model/CloudiatorData.java @@ -0,0 +1,20 @@ +package eu.functionizer.functionizertestingtool.model; + +import io.github.cloudiator.rest.model.Function; +import io.github.cloudiator.rest.model.NodeCandidate; +import lombok.AllArgsConstructor; +import lombok.Getter; +import lombok.Setter; + +import java.util.Map; + +@AllArgsConstructor +@Getter +@Setter +public class CloudiatorData { + private Map deployedFunctions; + private Map functionDeployNames; + private Map nodeCandidates; + private Map userSecrets; + +} diff --git a/functionizer-testing-tool/src/main/java/eu/functionizer/functionizertestingtool/model/FunctionTestResult.java b/functionizer-testing-tool/src/main/java/eu/functionizer/functionizertestingtool/model/FunctionTestResult.java new file mode 100644 index 0000000000000000000000000000000000000000..6e83036b065c2b77336278997e3e58f4d077f809 --- /dev/null +++ b/functionizer-testing-tool/src/main/java/eu/functionizer/functionizertestingtool/model/FunctionTestResult.java @@ -0,0 +1,56 @@ +package eu.functionizer.functionizertestingtool.model; + +import lombok.Data; + +import java.util.ArrayList; +import java.util.List; + + +@Data +public class FunctionTestResult { + private String functionName; + private double duration; + private Long passed; + private Long failed; + private Long ignored; + private List testCaseResults; + private TestResultEnum overallResult; + private String failedAtStage; + private String message; + + public FunctionTestResult(String functionName) { + this.functionName = functionName; + this.testCaseResults = new ArrayList<>(); + this.passed = 0L; + this.failed = 0L; + this.ignored = 0L; + this.overallResult = TestResultEnum.SUCCESS; + } + + public void addTestCaseResult(TestCaseResult testCaseResult) { + this.testCaseResults.add(testCaseResult); + TestResultEnum result = testCaseResult.getResult(); + if (result == TestResultEnum.SUCCESS) { + this.passed += 1; + } else if (result == TestResultEnum.FAILURE){ + this.failed += 1; + } else if (result == TestResultEnum.IGNORED) { + this.ignored += 1; + } + updateOverallResult(); + } + + private void updateOverallResult() { + if (this.overallResult == TestResultEnum.PARTIAL_FAILURE + || this.overallResult == TestResultEnum.IGNORED) { + return; + } + if (this.failed == 0L && this.ignored == 0L) { + this.overallResult = TestResultEnum.SUCCESS; + } else if (this.passed == 0L) { + this.overallResult = TestResultEnum.FAILURE; + } else { + this.overallResult = TestResultEnum.PARTIAL_FAILURE; + } + } +} diff --git a/functionizer-testing-tool/src/main/java/eu/functionizer/functionizertestingtool/model/FunctionizerTestResult.java b/functionizer-testing-tool/src/main/java/eu/functionizer/functionizertestingtool/model/FunctionizerTestResult.java new file mode 100644 index 0000000000000000000000000000000000000000..8dbd9978d44a73af4dbdaac89043aec3d9a621b9 --- /dev/null +++ b/functionizer-testing-tool/src/main/java/eu/functionizer/functionizertestingtool/model/FunctionizerTestResult.java @@ -0,0 +1,23 @@ +package eu.functionizer.functionizertestingtool.model; + +import lombok.Data; + +import java.util.ArrayList; +import java.util.List; + +@Data +public class FunctionizerTestResult { + private List functionTestResults; + private TestResultEnum testsRunResult; + private String failedAtStage; + private String message; + private double duration; + + public FunctionizerTestResult() { + this.functionTestResults = new ArrayList<>(); + } + + public void addFunctionTestResult(FunctionTestResult functionTestResult) { + this.functionTestResults.add(functionTestResult); + } +} diff --git a/functionizer-testing-tool/src/main/java/eu/functionizer/functionizertestingtool/model/ReportEntryKey.java b/functionizer-testing-tool/src/main/java/eu/functionizer/functionizertestingtool/model/ReportEntryKey.java new file mode 100644 index 0000000000000000000000000000000000000000..71b8d99953fd67899130c12ac356fc4087985b1a --- /dev/null +++ b/functionizer-testing-tool/src/main/java/eu/functionizer/functionizertestingtool/model/ReportEntryKey.java @@ -0,0 +1,10 @@ +package eu.functionizer.functionizertestingtool.model; + +public class ReportEntryKey { + public final static String EVENT = "event"; + public final static String EXPECTED_OUTPUT = "expectedOutput"; + public final static String IGNORED = "ignored"; + public final static String ROOT = "root"; + public final static String STAGE = "stage"; + public final static String FAILURE_CAUSE = "failureCause"; +} diff --git a/functionizer-testing-tool/src/main/java/eu/functionizer/functionizertestingtool/model/TestCaseResult.java b/functionizer-testing-tool/src/main/java/eu/functionizer/functionizertestingtool/model/TestCaseResult.java new file mode 100644 index 0000000000000000000000000000000000000000..ef3c65b9e4b6c903d3675c74798d3caa215cf0d7 --- /dev/null +++ b/functionizer-testing-tool/src/main/java/eu/functionizer/functionizertestingtool/model/TestCaseResult.java @@ -0,0 +1,13 @@ +package eu.functionizer.functionizertestingtool.model; + +import lombok.Data; + +@Data +public class TestCaseResult { + private String event; + private String expectedOutput; + private String actualOutput; + private String message; + private TestResultEnum result; + private double duration; +} diff --git a/functionizer-testing-tool/src/main/java/eu/functionizer/functionizertestingtool/model/TestResultEnum.java b/functionizer-testing-tool/src/main/java/eu/functionizer/functionizertestingtool/model/TestResultEnum.java new file mode 100644 index 0000000000000000000000000000000000000000..4a2100edb579ce09a6953576cdb6ff9019788939 --- /dev/null +++ b/functionizer-testing-tool/src/main/java/eu/functionizer/functionizertestingtool/model/TestResultEnum.java @@ -0,0 +1,19 @@ +package eu.functionizer.functionizertestingtool.model; + +import org.junit.platform.engine.TestExecutionResult; + +public enum TestResultEnum { + SUCCESS, + PARTIAL_FAILURE, + IGNORED, + FAILURE; + + + public static TestResultEnum fromTestExecutionResult(TestExecutionResult result) { + if (result.getStatus() == TestExecutionResult.Status.SUCCESSFUL) { + return SUCCESS; + } else { + return FAILURE; + } + } +} diff --git a/functionizer-testing-tool/src/main/java/eu/functionizer/functionizertestingtool/service/TestRunner.java b/functionizer-testing-tool/src/main/java/eu/functionizer/functionizertestingtool/service/TestRunner.java new file mode 100644 index 0000000000000000000000000000000000000000..89a62839cc439f9f9c74e2d84c59754f730871bd --- /dev/null +++ b/functionizer-testing-tool/src/main/java/eu/functionizer/functionizertestingtool/service/TestRunner.java @@ -0,0 +1,53 @@ +package eu.functionizer.functionizertestingtool.service; + +import eu.functionizer.functionizertestingtool.model.FunctionizerTestResult; +import eu.functionizer.functionizertestingtool.service.test.FunctionizerReportData; +import eu.functionizer.functionizertestingtool.service.test.FunctionizerTestListener; +import eu.functionizer.functionizertestingtool.service.test.ServerlessFunctionTestFactory; + +import lombok.AllArgsConstructor; +import lombok.extern.slf4j.Slf4j; + +import org.junit.platform.launcher.Launcher; +import org.junit.platform.launcher.LauncherDiscoveryRequest; +import org.junit.platform.launcher.TestPlan; +import org.junit.platform.launcher.core.LauncherDiscoveryRequestBuilder; +import org.junit.platform.launcher.core.LauncherFactory; + +import org.springframework.beans.factory.annotation.Autowired; +import org.springframework.stereotype.Service; + +import static org.junit.platform.engine.discovery.DiscoverySelectors.selectClass; + + +@Service +@Slf4j +@AllArgsConstructor(onConstructor = @__(@Autowired)) +public class TestRunner { + + public FunctionizerTestResult runTests() { + + log.debug("Initiating Test Launcher"); + Launcher launcher = LauncherFactory.create(); + + log.debug("Collecting test classes"); + LauncherDiscoveryRequest request = LauncherDiscoveryRequestBuilder.request() + .selectors(selectClass(ServerlessFunctionTestFactory.class)) + .build(); + + TestPlan testPlan = launcher.discover(request); + + log.debug("Initiating Listener"); + + FunctionizerTestListener listener = new FunctionizerTestListener(); + + log.debug("Registering Listener"); + launcher.registerTestExecutionListeners(listener); + launcher.execute(request); + + log.info("Generating Report"); + FunctionizerReportData report = listener.getReport(); + log.info("Test run ended"); + return report.getTestResult(); + } +} diff --git a/functionizer-testing-tool/src/main/java/eu/functionizer/functionizertestingtool/service/provider/AWSLambdaService.java b/functionizer-testing-tool/src/main/java/eu/functionizer/functionizertestingtool/service/provider/AWSLambdaService.java new file mode 100644 index 0000000000000000000000000000000000000000..9b2d7aa30b6be5db7586d711397910d543c6c72f --- /dev/null +++ b/functionizer-testing-tool/src/main/java/eu/functionizer/functionizertestingtool/service/provider/AWSLambdaService.java @@ -0,0 +1,146 @@ +package eu.functionizer.functionizertestingtool.service.provider; + +import com.amazonaws.auth.AWSStaticCredentialsProvider; +import com.amazonaws.auth.BasicAWSCredentials; +import com.amazonaws.regions.Regions; +import com.amazonaws.services.lambda.AWSLambda; +import com.amazonaws.services.lambda.AWSLambdaClientBuilder; +import com.amazonaws.services.lambda.model.InvokeRequest; +import com.amazonaws.services.lambda.model.InvokeResult; +import eu.functionizer.functionizertestingtool.model.ReportEntryKey; +import eu.functionizer.functionizertestingtool.service.test.Stage; +import eu.passage.upperware.commons.model.testing.FunctionTestConfiguration; +import eu.passage.upperware.commons.model.testing.TestCase; +import lombok.extern.slf4j.Slf4j; +import org.junit.jupiter.api.DynamicNode; +import org.junit.jupiter.api.DynamicTest; + +import java.nio.charset.StandardCharsets; +import java.util.Arrays; +import java.util.List; +import java.util.Map; +import java.util.stream.Collectors; + +import static org.junit.Assert.assertEquals; +import static org.junit.Assert.fail; +import static org.junit.jupiter.api.DynamicTest.dynamicTest; + +@Slf4j +public class AWSLambdaService extends TestPreparationService { + + public static AWSLambda buildClient(String user, String secret, String location) { + BasicAWSCredentials credentials = new BasicAWSCredentials(user, secret); + + return AWSLambdaClientBuilder.standard() + .withCredentials(new AWSStaticCredentialsProvider(credentials)) + .withRegion(Regions.fromName(location)) + .build(); + } + + public static void prepareAWSLambdaTests( + List functionNode, + FunctionTestConfiguration configuration, + String stackId, + String deployedName, + String user, + String secret, + String location + ) { + AWSLambda awsLambdaClient; + + log.debug("Building AWS Lambda Function name"); + String awsFunctionName = String.join( + "-", + stackId, + deployedName + ); + + log.debug("Building AWS Lambda client"); + try { + awsLambdaClient = AWSLambdaService.buildClient(user, secret, location); + } catch (Exception e) { + failDynamicNode( + configuration.getFunctionName(), + Stage.BUILD_AWS_LAMBDA_CLIENT, + e.getMessage() + ); + ignoreTestCases(functionNode, configuration); + return; + } + + List awsTests = configuration.getTestCases() + .stream() + .map(testCase -> AWSLambdaService.createAWSLambdaTest( + awsLambdaClient, + configuration.getFunctionName(), + awsFunctionName, + testCase + )) + .collect(Collectors.toList()); + functionNode.addAll(awsTests); + } + + public static DynamicTest createAWSLambdaTest( + AWSLambda awsLambdaClient, + String functionName, + String awsLambdaFunctionName, + TestCase testCase + ) { + log.debug( + "Creating test case: event={}, expected output={}", + testCase.getEvent(), + testCase.getExpectedOutput() + ); + + String displayName = createTestCaseDisplayName( + functionName, + testCase.getEvent(), + testCase.getExpectedOutput() + ); + + Map reportEntry = createReportEntry(displayName); + reportEntry.put(ReportEntryKey.EVENT, testCase.getEvent()); + reportEntry.put(ReportEntryKey.EXPECTED_OUTPUT, testCase.getExpectedOutput()); + testReporter.publishEntry(reportEntry); + + return dynamicTest( + displayName, + () -> executeAWSLambdaTestCase( + awsLambdaClient, + awsLambdaFunctionName, + testCase.getEvent(), + testCase.getExpectedOutput() + ) + ); + } + + public static void executeAWSLambdaTestCase( + AWSLambda awsLambda, + String functionName, + String event, + String expectedOutput + ) { + InvokeRequest invokeRequest = new InvokeRequest() + .withFunctionName(functionName) + .withPayload(event); + + try { + InvokeResult result = awsLambda.invoke(invokeRequest); + String resultString = new String(result.getPayload().array(), StandardCharsets.UTF_8); + if (Arrays.asList(200, 202, 204).contains(result.getStatusCode())) { + assertEquals(expectedOutput, resultString); + } else { + fail(String.format( + "Received status code %s. Reason: %s", + result.getStatusCode(), + resultString + )); + } + } catch (Exception e) { + fail(String.format( + "An exception while fetching response from AWS Lambda Client occurred. Cause: %s", + e.getMessage() + )); + } + } +} diff --git a/functionizer-testing-tool/src/main/java/eu/functionizer/functionizertestingtool/service/provider/AzureFunctionsService.java b/functionizer-testing-tool/src/main/java/eu/functionizer/functionizertestingtool/service/provider/AzureFunctionsService.java new file mode 100644 index 0000000000000000000000000000000000000000..34453b19876af9223098d2f30aea4483ec4377fb --- /dev/null +++ b/functionizer-testing-tool/src/main/java/eu/functionizer/functionizertestingtool/service/provider/AzureFunctionsService.java @@ -0,0 +1,200 @@ +package eu.functionizer.functionizertestingtool.service.provider; + +import com.microsoft.azure.AzureEnvironment; +import com.microsoft.azure.credentials.ApplicationTokenCredentials; +import com.microsoft.azure.management.Azure; +import com.microsoft.azure.management.appservice.FunctionApp; +import eu.functionizer.functionizertestingtool.model.ReportEntryKey; +import eu.functionizer.functionizertestingtool.service.test.ServerlessFunctionTestFactory; +import eu.functionizer.functionizertestingtool.service.test.Stage; +import eu.passage.upperware.commons.model.testing.FunctionTestConfiguration; +import eu.passage.upperware.commons.model.testing.TestCase; +import io.github.cloudiator.rest.model.Function; +import lombok.extern.slf4j.Slf4j; +import org.junit.jupiter.api.DynamicNode; +import org.junit.jupiter.api.DynamicTest; +import org.junit.jupiter.api.TestReporter; +import org.springframework.http.HttpEntity; +import org.springframework.http.HttpHeaders; +import org.springframework.http.HttpStatus; +import org.springframework.http.MediaType; +import org.springframework.http.ResponseEntity; +import org.springframework.web.client.RestTemplate; + +import java.io.IOException; +import java.util.Collections; +import java.util.List; +import java.util.Map; +import java.util.stream.Collectors; + +import static org.junit.Assert.assertEquals; +import static org.junit.Assert.fail; +import static org.junit.jupiter.api.DynamicTest.dynamicTest; + +@Slf4j +public class AzureFunctionsService extends TestPreparationService { + private final static String ENDPOINT_STATIC = "azurewebsites.net/api"; + private final static String CLIENT_TENANT_DELIMITER = ":"; + + private static String buildFunctionUrl(String appName, String functionName) { + return String.format("http://%s.%s/%s", appName, ENDPOINT_STATIC, functionName); + } + + private static String buildResourceGroupName(String stackId) { + return stackId + "group"; + } + + private static Azure buildAzureClient(String user, String secret) throws IOException { + int colonIndex = user.lastIndexOf(CLIENT_TENANT_DELIMITER); + String clientId = user.substring(0, colonIndex); + String tenantId = user.substring(colonIndex + 1); + + ApplicationTokenCredentials credentials = new ApplicationTokenCredentials( + clientId, + tenantId, + secret, + AzureEnvironment.AZURE + ); + + return Azure.authenticate(credentials).withDefaultSubscription(); + } + + private static String getFunctionKey(Azure azureClient, String stackId) { + String resourceGroup = buildResourceGroupName(stackId); + + FunctionApp functionApp = azureClient + .appServices() + .functionApps() + .getByResourceGroup(resourceGroup, stackId); + + return functionApp.getMasterKey(); + } + + private static String invokeFunction(String url, String functionKey, String input) throws Exception { + + RestTemplate restTemplate = new RestTemplate(); + + HttpHeaders headers = new HttpHeaders(); + headers.setAccept(Collections.singletonList(MediaType.APPLICATION_JSON)); + headers.set("x-functions-key", functionKey); + + + HttpEntity entity = new HttpEntity<>(input, headers); + ResponseEntity response = restTemplate.postForEntity( + url, + entity, + String.class + ); + if (response.getStatusCode() == HttpStatus.OK) { + return response.getBody(); + } else { + throw new Exception("Not 200 response"); + } + } + + public static void prepareAzureTests( + List functionNode, + FunctionTestConfiguration configuration, + String stackId, + String user, + String secret + ) { + Azure azureClient; + String functionKey; + + Stage stage = Stage.BUILD_AZURE_CLIENT; + try { + log.debug("Building Azure client"); + azureClient = AzureFunctionsService.buildAzureClient(user, secret); + + log.debug("Fetching Function Key"); + stage = Stage.GET_AZURE_FUNCTION_KEY; + functionKey = AzureFunctionsService.getFunctionKey(azureClient, stackId); + } catch (Exception e) { + failDynamicNode( + configuration.getFunctionName(), + stage, + e.getMessage() + ); + ignoreTestCases(functionNode, configuration); + return; + } + + log.debug("Building Function URL"); + String azureFunctionEndpoint = AzureFunctionsService.buildFunctionUrl( + stackId, + configuration.getTriggerPath() + ); + log.info("Function URL = {}", azureFunctionEndpoint); + + List azureTests = configuration + .getTestCases() + .stream() + .map(testCase -> AzureFunctionsService.createAzureTest( + configuration.getFunctionName(), + azureFunctionEndpoint, + functionKey, + testCase, + testReporter + )) + .collect(Collectors.toList()); + + functionNode.addAll(azureTests); + } + + private static DynamicTest createAzureTest( + String functionName, + String azureFunctionEndpoint, + String functionKey, + TestCase testCase, + TestReporter testReporter + ) { + log.debug( + "Creating test case: event={}, expected output={}", + testCase.getEvent(), + testCase.getExpectedOutput() + ); + String displayName = createTestCaseDisplayName( + functionName, + testCase.getEvent(), + testCase.getExpectedOutput() + ); + + Map reportEntry = createReportEntry(displayName); + reportEntry.put(ReportEntryKey.EVENT, testCase.getEvent()); + reportEntry.put(ReportEntryKey.EXPECTED_OUTPUT, testCase.getExpectedOutput()); + testReporter.publishEntry(reportEntry); + + return dynamicTest( + displayName, + () -> executeAzureTestCase( + azureFunctionEndpoint, + functionKey, + testCase.getEvent(), + testCase.getExpectedOutput() + ) + ); + } + + private static void executeAzureTestCase( + String endpoint, + String functionKey, + String event, + String expectedOutput + ) { + Stage stage = Stage.TEST_EXECUTION; + try { + String response = AzureFunctionsService.invokeFunction( + endpoint, + functionKey, + event + ); + assertEquals(expectedOutput, response); + } catch (Exception e) { + fail(String.format( + "An exception while fetching response from AWS Lambda Client occurred. Cause: %s", + e.getMessage() + )); + } + } +} diff --git a/functionizer-testing-tool/src/main/java/eu/functionizer/functionizertestingtool/service/provider/TestPreparationService.java b/functionizer-testing-tool/src/main/java/eu/functionizer/functionizertestingtool/service/provider/TestPreparationService.java new file mode 100644 index 0000000000000000000000000000000000000000..6e7e26967acb159882bf66015e7dad6f3bc36a56 --- /dev/null +++ b/functionizer-testing-tool/src/main/java/eu/functionizer/functionizertestingtool/service/provider/TestPreparationService.java @@ -0,0 +1,69 @@ +package eu.functionizer.functionizertestingtool.service.provider; + +import eu.functionizer.functionizertestingtool.model.ReportEntryKey; +import eu.functionizer.functionizertestingtool.service.test.Stage; +import eu.passage.upperware.commons.model.testing.FunctionTestConfiguration; +import lombok.extern.slf4j.Slf4j; +import org.junit.jupiter.api.DynamicNode; +import org.junit.jupiter.api.TestReporter; + +import java.util.HashMap; +import java.util.List; +import java.util.Map; + +import static org.junit.Assert.fail; +import static org.junit.jupiter.api.DynamicTest.dynamicTest; + +@Slf4j +public class TestPreparationService { + + public static TestReporter testReporter; + + static String createTestCaseDisplayName( + String functionName, + String input, + String expectedOutput + ) { + return String.format( + "Function %s invoked with %s should return %s", + functionName, + input, + expectedOutput + ); + } + + public static Map createReportEntry(String displayName) { + Map reportEntry = new HashMap<>(); + reportEntry.put("displayName", displayName); + return reportEntry; + } + + public static void failDynamicNode(String displayName, Stage stage, String cause) { + log.info( + "Dynamic Node '{}' construction failed at the stage {}. Cause: {}", + displayName, + stage.getName(), + cause + ); + Map reportEntry = createReportEntry(displayName); + reportEntry.put(ReportEntryKey.STAGE, stage.getName()); + reportEntry.put(ReportEntryKey.FAILURE_CAUSE, cause); + testReporter.publishEntry(reportEntry); + } + + public static void ignoreTestCases(List functionNode, FunctionTestConfiguration configuration) { + configuration.getTestCases().forEach(testCase -> { + String testCaseDisplayName = createTestCaseDisplayName( + configuration.getFunctionName(), + testCase.getEvent(), + testCase.getExpectedOutput() + ); + Map reportEntry = createReportEntry(testCaseDisplayName); + reportEntry.put(ReportEntryKey.EVENT, testCase.getEvent()); + reportEntry.put(ReportEntryKey.EXPECTED_OUTPUT, testCase.getExpectedOutput()); + reportEntry.put(ReportEntryKey.IGNORED, "true"); + testReporter.publishEntry(reportEntry); + functionNode.add(dynamicTest(testCaseDisplayName, () -> fail("Test case ignored"))); + }); + } +} diff --git a/functionizer-testing-tool/src/main/java/eu/functionizer/functionizertestingtool/service/test/FunctionizerReportData.java b/functionizer-testing-tool/src/main/java/eu/functionizer/functionizertestingtool/service/test/FunctionizerReportData.java new file mode 100644 index 0000000000000000000000000000000000000000..471105000b852e6db4de7b9778a74258971ba265 --- /dev/null +++ b/functionizer-testing-tool/src/main/java/eu/functionizer/functionizertestingtool/service/test/FunctionizerReportData.java @@ -0,0 +1,217 @@ +package eu.functionizer.functionizertestingtool.service.test; + +import eu.functionizer.functionizertestingtool.model.*; +import org.junit.ComparisonFailure; +import org.junit.platform.commons.util.ExceptionUtils; +import org.junit.platform.engine.TestExecutionResult; +import org.junit.platform.engine.reporting.ReportEntry; +import org.junit.platform.launcher.TestIdentifier; +import org.junit.platform.launcher.TestPlan; + +import java.time.Clock; +import java.time.Duration; +import java.time.Instant; +import java.util.*; +import java.util.concurrent.ConcurrentHashMap; +import java.util.function.Predicate; + +import static eu.functionizer.functionizertestingtool.service.test.ServerlessFunctionTestFactory.ALL_TESTS_DISPLAY_NAME; +import static java.util.Collections.emptyMap; +import static org.junit.platform.engine.TestExecutionResult.Status.ABORTED; +import static org.junit.platform.engine.TestExecutionResult.Status.SUCCESSFUL; + +public class FunctionizerReportData { + private static final int MILLIS_PER_SECOND = 1000; + + public long timeFinished; + private List failures = new ArrayList<>(); + + private FunctionizerTestResult testResult; + + + private final Map finishedTests = new ConcurrentHashMap<>(); + private final Map skippedTests = new ConcurrentHashMap<>(); + private final Map startInstants = new ConcurrentHashMap<>(); + private final Map endInstants = new ConcurrentHashMap<>(); + + private TestIdentifier root; + private final Map> reportEntries = new ConcurrentHashMap<>(); + + private final TestPlan testPlan; + private final Clock clock; + + FunctionizerReportData(TestPlan testPlan, Clock clock) { + this.testPlan = testPlan; + this.clock = clock; + + } + + void markSkipped(TestIdentifier testIdentifier, String reason) { + this.skippedTests.put(testIdentifier, reason == null ? "" : reason); + } + + void markStarted(TestIdentifier testIdentifier) { + this.startInstants.put(testIdentifier, this.clock.instant()); + } + + void markFinished(TestIdentifier testIdentifier, TestExecutionResult result) { + this.endInstants.put(testIdentifier, this.clock.instant()); + if (result.getStatus() == ABORTED) { + String reason = result + .getThrowable() + .map(ExceptionUtils::readStackTrace) + .orElse(""); + this.skippedTests.put(testIdentifier, reason); + } + else { + this.finishedTests.put(testIdentifier, result); + } + } + + void addReportEntry(TestIdentifier testIdentifier, ReportEntry entry) { + // Note: we get the actual test display name from the report entry, + // because dynamic test report entries do not recognize their display names properly + + if (entry.getKeyValuePairs().containsKey(ReportEntryKey.ROOT)) { + this.root = testIdentifier; + } + + String actualTestDisplayName = entry.getKeyValuePairs().get("displayName"); + if (actualTestDisplayName != null) { + Map report = this.reportEntries.computeIfAbsent( + actualTestDisplayName, + key -> new HashMap<>() + ); + entry.getKeyValuePairs().forEach(report::put); + } + } + + void addFailure(TestIdentifier testIdentifier, Throwable throwable) { + failures.add(new Failure(testIdentifier.getDisplayName(), throwable.getMessage())); + } + + public static class Failure { + String displayName; + String cause; + + public Failure( + String displayName, + String cause + ) { + this.displayName = displayName; + this.cause = cause; + } + } + + TestExecutionResult getResult(TestIdentifier testIdentifier) { + if (this.finishedTests.containsKey(testIdentifier)) { + return this.finishedTests.get(testIdentifier); + } + Optional parent = this.testPlan.getParent(testIdentifier); + if (parent.isPresent()) { + Optional ancestor = findAncestor( + parent.get(), + this.finishedTests::containsKey + ); + if (ancestor.isPresent()) { + TestExecutionResult result = this.finishedTests.get(ancestor.get()); + if (result.getStatus() != SUCCESSFUL) { + return result; + } + } + } + + return null; + } + + double getDurationInSeconds(TestIdentifier testIdentifier) { + Instant startInstant = this.startInstants.getOrDefault(testIdentifier, Instant.EPOCH); + Instant endInstant = this.endInstants.getOrDefault(testIdentifier, startInstant); + return Duration.between(startInstant, endInstant).toMillis() / (double) MILLIS_PER_SECOND; + } + + private Optional findAncestor( + TestIdentifier testIdentifier, + Predicate predicate + ) { + Optional current = Optional.of(testIdentifier); + while (current.isPresent()) { + if (predicate.test(current.get())) { + return current; + } + current = this.testPlan.getParent(current.get()); + } + return Optional.empty(); + } + + public FunctionizerTestResult getTestResult() { + return this.testResult; + } + + public void prepareTestResult() { + this.testResult = new FunctionizerTestResult(); + this.testResult.setDuration(getDurationInSeconds(this.root)); + + Map testReportEntry = this.reportEntries.getOrDefault( + ALL_TESTS_DISPLAY_NAME, + emptyMap() + ); + if (testReportEntry.containsKey(ReportEntryKey.STAGE)) { + this.testResult.setMessage(testReportEntry.get(ReportEntryKey.FAILURE_CAUSE)); + this.testResult.setFailedAtStage(testReportEntry.get(ReportEntryKey.STAGE)); + this.testResult.setTestsRunResult(TestResultEnum.FAILURE); + return; + } + + Set functionNodes = testPlan.getChildren(this.root); + for (TestIdentifier functionNode : functionNodes) { + FunctionTestResult functionTestResult = new FunctionTestResult( + functionNode.getDisplayName() + ); + functionTestResult.setDuration(getDurationInSeconds(functionNode)); + Map functionReportEntry = this.reportEntries.getOrDefault( + functionNode.getDisplayName(), + emptyMap() + ); + if (functionReportEntry.containsKey(ReportEntryKey.STAGE)) { + functionTestResult.setFailedAtStage(functionReportEntry.get(ReportEntryKey.STAGE)); + functionTestResult.setMessage(functionReportEntry.get(ReportEntryKey.FAILURE_CAUSE)); + functionTestResult.setOverallResult(TestResultEnum.FAILURE); + } + Set testCases = testPlan.getChildren(functionNode); + + for(TestIdentifier testCase : testCases) { + TestCaseResult testCaseResult = new TestCaseResult(); + TestExecutionResult result = getResult(testCase); + testCaseResult.setDuration(getDurationInSeconds(testCase)); + testCaseResult.setResult(TestResultEnum.fromTestExecutionResult(result)); + + Map reportEntry = this.reportEntries.getOrDefault( + testCase.getDisplayName(), + emptyMap() + ); + if (reportEntry.containsKey(ReportEntryKey.IGNORED)) { + testCaseResult.setResult(TestResultEnum.IGNORED); + } + testCaseResult.setEvent(reportEntry.get(ReportEntryKey.EVENT)); + testCaseResult.setExpectedOutput(reportEntry.get(ReportEntryKey.EXPECTED_OUTPUT)); + testCaseResult.setActualOutput(reportEntry.get(ReportEntryKey.EXPECTED_OUTPUT)); + + result.getThrowable().ifPresent(error -> { + testCaseResult.setMessage(error.getMessage()); + if (error instanceof ComparisonFailure) { + ComparisonFailure comparisonFailure = (ComparisonFailure) error; + testCaseResult.setExpectedOutput(comparisonFailure.getExpected()); + testCaseResult.setActualOutput(comparisonFailure.getActual()); + } else { + testCaseResult.setActualOutput(null); + } + }); + functionTestResult.addTestCaseResult(testCaseResult); + } + + this.testResult.addFunctionTestResult(functionTestResult); + } + this.testResult.setTestsRunResult(TestResultEnum.SUCCESS); + } +} diff --git a/functionizer-testing-tool/src/main/java/eu/functionizer/functionizertestingtool/service/test/FunctionizerTestListener.java b/functionizer-testing-tool/src/main/java/eu/functionizer/functionizertestingtool/service/test/FunctionizerTestListener.java new file mode 100644 index 0000000000000000000000000000000000000000..a93bec0a50b9e4f3167bb58be77fa5c2be9cb95e --- /dev/null +++ b/functionizer-testing-tool/src/main/java/eu/functionizer/functionizertestingtool/service/test/FunctionizerTestListener.java @@ -0,0 +1,83 @@ +package eu.functionizer.functionizertestingtool.service.test; + +import org.junit.platform.commons.util.PreconditionViolationException; +import org.junit.platform.engine.TestExecutionResult; +import org.junit.platform.engine.reporting.ReportEntry; +import org.junit.platform.launcher.TestExecutionListener; +import org.junit.platform.launcher.TestIdentifier; +import org.junit.platform.launcher.TestPlan; + +import java.time.Clock; + +public class FunctionizerTestListener implements TestExecutionListener { + + private final Clock clock; + + private FunctionizerReportData reportData; + + public FunctionizerTestListener() { + this(Clock.systemDefaultZone()); + } + + private FunctionizerTestListener(Clock clock) { + this.clock = clock; + } + + public FunctionizerReportData getReport() { + return this.reportData; + } + + @Override + public void testPlanExecutionStarted(TestPlan testPlan) { + this.reportData = new FunctionizerReportData(testPlan, clock); + } + + @Override + public void testPlanExecutionFinished(TestPlan testPlan) { + this.reportData.timeFinished = System.currentTimeMillis(); + this.reportData.prepareTestResult(); + } + + @Override + public void dynamicTestRegistered(TestIdentifier testIdentifier) { + } + + @Override + public void executionSkipped(TestIdentifier testIdentifier, String reason) { + this.reportData.markSkipped(testIdentifier, reason); + } + + @Override + public void executionStarted(TestIdentifier testIdentifier) { + this.reportData.markStarted(testIdentifier); + } + + @Override + public void reportingEntryPublished(TestIdentifier testIdentifier, ReportEntry entry) { + this.reportData.addReportEntry(testIdentifier, entry); + } + + @Override + public void executionFinished(TestIdentifier testIdentifier, TestExecutionResult testExecutionResult) { + + switch (testExecutionResult.getStatus()) { + + case SUCCESSFUL: + + case ABORTED: { + break; + } + + case FAILED: { + testExecutionResult.getThrowable().ifPresent( + throwable -> this.reportData.addFailure(testIdentifier, throwable)); + break; + } + + default: + throw new PreconditionViolationException( + "Unsupported execution status:" + testExecutionResult.getStatus()); + } + this.reportData.markFinished(testIdentifier, testExecutionResult); + } +} diff --git a/functionizer-testing-tool/src/main/java/eu/functionizer/functionizertestingtool/service/test/ServerlessFunctionTestFactory.java b/functionizer-testing-tool/src/main/java/eu/functionizer/functionizertestingtool/service/test/ServerlessFunctionTestFactory.java new file mode 100644 index 0000000000000000000000000000000000000000..a84df2e836cf8f335789253a41386e89c3f29fff --- /dev/null +++ b/functionizer-testing-tool/src/main/java/eu/functionizer/functionizertestingtool/service/test/ServerlessFunctionTestFactory.java @@ -0,0 +1,410 @@ +package eu.functionizer.functionizertestingtool.service.test; + +import java.io.FileInputStream; +import java.io.IOException; + +import java.util.*; +import java.util.stream.Collectors; +import java.util.stream.Stream; + +import eu.functionizer.functionizertestingtool.model.CloudiatorData; +import eu.functionizer.functionizertestingtool.service.provider.TestPreparationService; +import eu.passage.upperware.commons.model.testing.FunctionTestConfiguration; +import eu.functionizer.functionizertestingtool.model.ReportEntryKey; +import eu.functionizer.functionizertestingtool.service.yaml.TestConfigurationLoader; +import eu.passage.upperware.commons.cloudiator.CloudiatorApi; +import eu.passage.upperware.commons.cloudiator.CloudiatorClientApi; +import eu.passage.upperware.commons.cloudiator.QueueInspector; +import eu.passage.upperware.commons.model.provider.CloudDefinition; +import eu.passage.upperware.commons.model.provider.Credential; +import eu.passage.upperware.commons.model.provider.Provider; +import eu.passage.upperware.commons.service.provider.ProviderIdCreatorService; +import eu.passage.upperware.commons.service.provider.ProviderService; +import eu.passage.upperware.commons.service.provider.ProviderValidationService; +import eu.passage.upperware.commons.service.store.SecureStoreService; +import eu.passage.upperware.commons.service.yaml.YamlDataService; +import eu.passage.upperware.commons.cloudiator.CloudiatorProperties; + +import io.github.cloudiator.rest.ApiClient; +import io.github.cloudiator.rest.ApiException; +import io.github.cloudiator.rest.api.*; + +import io.github.cloudiator.rest.model.*; + +import lombok.extern.slf4j.Slf4j; +import org.junit.jupiter.api.*; + +import static eu.functionizer.functionizertestingtool.service.provider.AWSLambdaService.prepareAWSLambdaTests; +import static eu.functionizer.functionizertestingtool.service.provider.AzureFunctionsService.prepareAzureTests; +import static eu.functionizer.functionizertestingtool.service.provider.TestPreparationService.*; + +@Slf4j +public class ServerlessFunctionTestFactory { + + private static final String CLOUDIATOR_CONFIG_FILE_PATH = System.getenv("MELODIC_CONFIG_DIR") + + "/eu.melodic.cloudiator-client.properties"; + + public static final String ALL_TESTS_DISPLAY_NAME = "All tests"; + + private static Map providerMap; + + private static CloudiatorApi cloudiatorApi; + private static MatchmakingApi matchmakingApi; + private static ProviderService providerService; + private static Stage stage = null; + + TestReporter testReporter; + + @BeforeAll + static void setUp() throws IOException { + + Properties properties = new Properties(); + properties.load(new FileInputStream(CLOUDIATOR_CONFIG_FILE_PATH)); + + CloudiatorProperties.Cloudiator cloudiator = new CloudiatorProperties.Cloudiator(); + cloudiator.setUrl(properties.getProperty("cloudiator.url")); + cloudiator.setApiKey(properties.getProperty("cloudiator.apiKey")); + cloudiator.setHttpReadTimeout( + Integer.parseInt(properties.getProperty("cloudiator.httpReadTimeout")) + ); + + CloudiatorProperties cloudiatorProperties = new CloudiatorProperties(); + cloudiatorProperties.setCloudiator(cloudiator); + + ApiClient apiClient = new ApiClient(); + apiClient.setBasePath(cloudiator.getUrl()); + apiClient.setApiKey(cloudiator.getApiKey()); + apiClient.setReadTimeout(cloudiator.getHttpReadTimeout()); + + QueueApi queueApi = new QueueApi(apiClient); + + cloudiatorApi = new CloudiatorClientApi( + new CloudApi(apiClient), + new SecurityApi(apiClient), + new NodeApi(apiClient), + new ProcessApi(apiClient), + queueApi, + new JobApi(apiClient), + new MonitoringApi(apiClient), + new QueueInspector(queueApi, cloudiatorProperties) + ); + + matchmakingApi = new MatchmakingApi(apiClient); + providerService = new ProviderService( + new ProviderIdCreatorService(), + new ProviderValidationService(), + cloudiatorApi, + new SecureStoreService(cloudiatorApi), + new YamlDataService() + ); + + providerMap = new HashMap<>(); + for (Provider provider : Provider.values()) { + providerMap.put(provider.value, provider); + } + } + + @BeforeEach + private void init(TestReporter testReporter) { + this.testReporter = testReporter; + } + + + @TestFactory + @DisplayName(ALL_TESTS_DISPLAY_NAME) + public Collection dynamicTestNodes() { + log.info("Test Factory started"); + stage = Stage.START; + TestPreparationService.testReporter = this.testReporter; + + Map entry = createReportEntry(ALL_TESTS_DISPLAY_NAME); + entry.put(ReportEntryKey.ROOT, "true"); + testReporter.publishEntry(entry); + + List dynamicNodes = new ArrayList<>(); + List functionTests; + + log.info("Loading test configuration"); + stage = Stage.LOAD_CONFIG; + try { + functionTests = TestConfigurationLoader + .loadTestConfiguration() + .getTests(); + } catch (Exception e) { + failDynamicNode(ALL_TESTS_DISPLAY_NAME, stage, e.getMessage()); + return dynamicNodes; + } + + CloudiatorData cloudiatorData; + + log.info("Fetching data from Cloudiator"); + stage = Stage.FETCH_CLOUDIATOR; + try { + cloudiatorData = fetchCloudiatorData(); + } catch (Exception e) { + testReporter.publishEntry("result", "fail"); + failDynamicNode(ALL_TESTS_DISPLAY_NAME, stage, e.getMessage()); + return dynamicNodes; + } + if (cloudiatorData == null) { + testReporter.publishEntry("result", "fail"); + failDynamicNode( + ALL_TESTS_DISPLAY_NAME, stage, + "There are no Functions on the deployed Function list received from Cloudiator." + ); + return dynamicNodes; + } + + log.info("Cloudiator data fetched successfully"); + + for (FunctionTestConfiguration functionTestConfiguration : functionTests) { + String functionConfigurationName = functionTestConfiguration.getFunctionName(); + + log.info("Gathering data on Function {}", functionConfigurationName); + stage = Stage.GATHER_FUNCTION_DATA; + + log.debug("Looking up Function in the deployed Function list"); + Function function = cloudiatorData + .getDeployedFunctions() + .get(functionConfigurationName.toLowerCase()); + + List functionNode; + + if (function == null) { + functionNode = new ArrayList<>(); + failDynamicNode( + functionConfigurationName, + stage, + "Not found in the deployed Function list" + ); + ignoreTestCases(functionNode, functionTestConfiguration); + } else { + + String functionDeployedName = cloudiatorData + .getFunctionDeployNames() + .get(functionConfigurationName); + + NodeCandidate nodeCandidate = cloudiatorData + .getNodeCandidates() + .get(function.getId()); + + log.debug("Collecting test cases"); + functionNode = prepareFunctionNode( + functionTestConfiguration, + nodeCandidate, + cloudiatorData.getUserSecrets(), + functionDeployedName, + function + ); + } + + dynamicNodes.add( + DynamicContainer.dynamicContainer( + functionConfigurationName, + functionNode + ) + ); + } + stage = Stage.END; + return dynamicNodes; + } + + private CloudiatorData fetchCloudiatorData( + ) throws ApiException { + + log.debug("Fetching Functions from Cloudiator"); + Map functions = getFunctionsMap(); + if (functions.size() == 0) { + return null; + } + + log.debug("Fetching Function Names from Cloudiator"); + Map functionDeployNames = getFunctionDeployNames(); + + log.debug("Fetching Node Candidates from Cloudiator"); + Map nodeCandidates = getFaasNodeCandidates(); + + log.debug("Fetching Cloud Provider Credentials"); + stage = Stage.FETCH_CREDENTIALS; + Map userSecrets = getCredentials(); + + return new CloudiatorData( + functions, + functionDeployNames, + nodeCandidates, + userSecrets + ); + } + + private Map getFunctionsMap() { + List functions = cloudiatorApi.getFunctionList(); + return functions + .stream() + .filter(function -> !Objects.requireNonNull(function.getStackId()).isEmpty()) + .collect(Collectors.toMap( + function -> Objects.requireNonNull(function.getStackId()).split("-")[0], + function -> function + )); + } + + private Map getFunctionDeployNames() { + Stream faasTasks = getFaasTasks(); + + log.debug("Extracting Function Names"); + return faasTasks + .collect(Collectors.toMap( + Task::getName, + ServerlessFunctionTestFactory::getTaskInterfaceFunctionName + )); + } + + private Stream getFaasTasks() { + log.debug("Fetching Cloudiator Jobs"); + List jobs = cloudiatorApi.getJobList(); + + log.debug("Extracting FaaS tasks from jobs"); + return jobs + .stream() + .map(Job::getTasks) + .filter(Objects::nonNull) + .flatMap(List::stream) + .filter(task -> Objects.nonNull(task) + && Objects.nonNull(task.getInterfaces()) + && Objects.requireNonNull(task.getInterfaces()) + .stream() + .findFirst() + .filter(anInterface -> anInterface instanceof FaasInterface) + .isPresent() + ); + } + + private static String getTaskInterfaceFunctionName(Task task) { + Optional taskInterface = Objects + .requireNonNull(task.getInterfaces()) + .stream() + .findFirst(); + if (taskInterface.isPresent()) { + FaasInterface faasInterface = (FaasInterface) taskInterface.get(); + return faasInterface.getFunctionName(); + } + return ""; + } + + private Map getFaasNodeCandidates() throws ApiException { + log.debug("Fetching FaaS Nodes"); + List faasNodes = cloudiatorApi.getFaasFromNodeList(); + + log.debug("Determining Node Candidates"); + List nodeCandidateIds = faasNodes + .stream() + .map(Node::getNodeCandidate) + .collect(Collectors.toList()); + + Map nodeCandidates = getNodeCandidatesByIds(nodeCandidateIds) + .stream() + .collect(Collectors.toMap(NodeCandidate::getId, nodeCandidate -> nodeCandidate)); + + return faasNodes + .stream() + .collect(Collectors.toMap( + Node::getOriginId, + node -> nodeCandidates.get(node.getNodeCandidate()) + )); + } + + private List getNodeCandidatesByIds(List ids) throws ApiException { + log.debug("Fetching Node Candidates"); + String constraint = String.format( + "nodes->forAll(n | Set{'%s'}->includes(n.id))", + String.join("','", ids) + ); + return matchmakingApi.findNodeCandidates( + Collections.singletonList(new OclRequirement() + .constraint(constraint) + .type("OclRequirement") + ) + ); + } + + private Map getCredentials() { + return providerService + .getCloudDefinitionsForAllProviders() + .stream() + .map(CloudDefinition::getCredential) + .collect(Collectors.toMap(Credential::getUser, Credential::getSecret)); + } + + private List prepareFunctionNode( + FunctionTestConfiguration functionTestConfiguration, + NodeCandidate nodeCandidate, + Map userSecrets, + String functionDeployedName, + Function function + ) { + log.info("Preparing Function Node"); + List functionNode = new ArrayList<>(); + + Cloud cloud = nodeCandidate.getCloud(); + if (cloud == null) { + log.info("Cloud not found. Omitting test cases"); + failDynamicNode( + functionTestConfiguration.getFunctionName(), + Stage.GATHER_FUNCTION_DATA,"Cloud not found" + ); + ignoreTestCases(functionNode, functionTestConfiguration); + return functionNode; + } + + log.debug("Retrieving Cloud credentials"); + String user = Objects.requireNonNull(nodeCandidate.getCloud()) + .getCredential() + .getUser(); + String secret = userSecrets.get(user); + if (user == null || secret == null) { + failDynamicNode + (functionTestConfiguration.getFunctionName(), + stage, + "Empty credentials." + ); + ignoreTestCases(functionNode, functionTestConfiguration); + return functionNode; + } + Provider provider = providerMap.get( + nodeCandidate.getCloud().getApi().getProviderName() + ); + + switch (provider) { + case AWS_EC2: + String location = Objects.requireNonNull(nodeCandidate.getLocation()).getProviderId(); + + prepareAWSLambdaTests( + functionNode, + functionTestConfiguration, + function.getStackId(), + functionDeployedName, + user, + secret, + location + ); + break; + + case AZURE: + prepareAzureTests( + functionNode, + functionTestConfiguration, + function.getStackId(), + user, + secret + ); + break; + + default: + log.info( + "Function {} has been deployed on unsupported Cloud. Omitting", + functionDeployedName + ); + ignoreTestCases(functionNode, functionTestConfiguration); + } + return functionNode; + } +} diff --git a/functionizer-testing-tool/src/main/java/eu/functionizer/functionizertestingtool/service/test/Stage.java b/functionizer-testing-tool/src/main/java/eu/functionizer/functionizertestingtool/service/test/Stage.java new file mode 100644 index 0000000000000000000000000000000000000000..25a12ea988e9e327816f956ba1387fbb667bc46a --- /dev/null +++ b/functionizer-testing-tool/src/main/java/eu/functionizer/functionizertestingtool/service/test/Stage.java @@ -0,0 +1,25 @@ +package eu.functionizer.functionizertestingtool.service.test; + +public enum Stage { + START("TEST FACTORY INITIALIZATION"), + LOAD_CONFIG("LOAD TEST CONFIG FILE"), + FETCH_CLOUDIATOR("FETCH CLOUDIATOR DATA"), + FETCH_CREDENTIALS("FETCH CLOUD CREDENTIALS"), + GATHER_FUNCTION_DATA("GATHER FUNCTION CLOUD DATA"), + BUILD_AZURE_CLIENT("BUILD AZURE CLIENT"), + BUILD_AWS_LAMBDA_CLIENT("BUILD AWS LAMBDA CLIENT"), + GET_AZURE_FUNCTION_KEY("GET AZURE FUNCTION KEY"), + TEST_EXECUTION("TEST CASE EXECUTION"), + AWS_INVOKE("INVOKE AWS LAMBDA FUNCTION"), + END("COLLECTING TESTS FINISH"); + + private final String name; + + Stage(String name) { + this.name = name; + } + + public String getName() { + return name; + } +} diff --git a/functionizer-testing-tool/src/main/java/eu/functionizer/functionizertestingtool/service/yaml/TestConfigurationLoader.java b/functionizer-testing-tool/src/main/java/eu/functionizer/functionizertestingtool/service/yaml/TestConfigurationLoader.java new file mode 100644 index 0000000000000000000000000000000000000000..fa6bfda4f2de44a859ac513c5a631a410e4a26ef --- /dev/null +++ b/functionizer-testing-tool/src/main/java/eu/functionizer/functionizertestingtool/service/yaml/TestConfigurationLoader.java @@ -0,0 +1,67 @@ +package eu.functionizer.functionizertestingtool.service.yaml; + + +import eu.passage.upperware.commons.model.testing.TestConfiguration; +import eu.passage.upperware.commons.MelodicConstants; +import lombok.extern.slf4j.Slf4j; +import org.springframework.http.HttpStatus; +import org.springframework.stereotype.Service; +import org.springframework.web.server.ResponseStatusException; +import org.yaml.snakeyaml.Yaml; +import org.yaml.snakeyaml.constructor.Constructor; +import org.yaml.snakeyaml.constructor.ConstructorException; + +import java.io.*; + +import static eu.passage.upperware.commons.service.testing.TestConfigurationValidationService.validate; + +@Service +@Slf4j +public class TestConfigurationLoader { + + private final static String CONFIG_FILE_PATH = MelodicConstants.TEST_CONFIG_FILE_DIR + "/tests.yml"; + + public static TestConfiguration loadTestConfiguration() throws Exception { + Yaml yaml = new Yaml(new Constructor(TestConfiguration.class)); + TestConfiguration configuration; + + try (FileInputStream fileInputStream = new FileInputStream(new File(CONFIG_FILE_PATH))) { + configuration = yaml.load(fileInputStream); + + } catch (FileNotFoundException e) { + String errorMessage = String.format( + "Test configuration file: %s is missing.", + CONFIG_FILE_PATH + ); + log.error(errorMessage, e); + throw new ResponseStatusException(HttpStatus.BAD_REQUEST, errorMessage); + } catch (IOException e) { + String errorMessage = String.format( + "Problem by reading file with test configuration: %s", + CONFIG_FILE_PATH + ); + log.error(errorMessage, e); + throw new ResponseStatusException(HttpStatus.BAD_REQUEST, errorMessage); + } catch (ConstructorException e) { + String errorMessage = String.format( + "The file has a bad format and could not be parsed: %s", e.getMessage() + ); + log.error(errorMessage); + throw new ResponseStatusException(HttpStatus.BAD_REQUEST, errorMessage); + } + validate(configuration); + return configuration; + } + + + private void saveTestConfiguration(TestConfiguration testConfiguration) { + Yaml yaml = new Yaml(); + FileWriter writer = null; + try { + writer = new FileWriter(CONFIG_FILE_PATH); + } catch (IOException e) { + log.error("Error by writing to file {} with test configuration: ", CONFIG_FILE_PATH, e); + } + yaml.dump(testConfiguration, writer); + } +} diff --git a/functionizer-testing-tool/src/main/resources/eu.functionizer.testingTool.properties b/functionizer-testing-tool/src/main/resources/eu.functionizer.testingTool.properties new file mode 100644 index 0000000000000000000000000000000000000000..bfea1b24a0dd1816c036a3aa0ea89496c41f5585 --- /dev/null +++ b/functionizer-testing-tool/src/main/resources/eu.functionizer.testingTool.properties @@ -0,0 +1,2 @@ +#### Logback-config +logging.config=file:${MELODIC_CONFIG_DIR}/logback-conf/logback-spring.xml diff --git a/gui-backend/pom.xml b/gui-backend/pom.xml index 4f9b10fb4494163568e674cd6f6af7fb8b7d5a43..ff85b6922ccd122d9a9a8a04fbcb974aaffa1158 100644 --- a/gui-backend/pom.xml +++ b/gui-backend/pom.xml @@ -61,13 +61,6 @@ 8.0.13 - - - org.yaml - snakeyaml - 1.21 - - org.passay passay diff --git a/gui-backend/src/main/java/eu/melodic/upperware/guibackend/ApplicationContext.java b/gui-backend/src/main/java/eu/melodic/upperware/guibackend/ApplicationContext.java index fba45c546bfbc4b9a84769be58a539f39f4c3bde..7b77406c2f579b4028d3610bca0d80e4154e177c 100644 --- a/gui-backend/src/main/java/eu/melodic/upperware/guibackend/ApplicationContext.java +++ b/gui-backend/src/main/java/eu/melodic/upperware/guibackend/ApplicationContext.java @@ -3,7 +3,15 @@ package eu.melodic.upperware.guibackend; import eu.paasage.upperware.security.authapi.properties.MelodicSecurityProperties; import eu.paasage.upperware.security.authapi.token.JWTService; import eu.paasage.upperware.security.authapi.token.JWTServiceImpl; +import eu.passage.upperware.commons.cloudiator.CloudiatorApi; +import eu.passage.upperware.commons.cloudiator.CloudiatorClientApi; import eu.passage.upperware.commons.cloudiator.CloudiatorProperties; +import eu.passage.upperware.commons.cloudiator.QueueInspector; +import eu.passage.upperware.commons.service.provider.ProviderIdCreatorService; +import eu.passage.upperware.commons.service.provider.ProviderService; +import eu.passage.upperware.commons.service.provider.ProviderValidationService; +import eu.passage.upperware.commons.service.store.SecureStoreService; +import eu.passage.upperware.commons.service.yaml.YamlDataService; import io.github.cloudiator.rest.ApiClient; import io.github.cloudiator.rest.api.*; import org.springframework.context.annotation.Bean; @@ -68,8 +76,68 @@ public class ApplicationContext { return new MonitoringApi(apiClient); } + @Bean + public CloudiatorApi cloudiatorApi( + CloudApi cloudApi, + SecurityApi securityApi, + NodeApi nodeApi, + ProcessApi processApi, + QueueApi queueApi, + JobApi jobApi, + MonitoringApi monitoringApi, + QueueInspector queueInspector + ) { + return new CloudiatorClientApi( + cloudApi, + securityApi, + nodeApi, + processApi, + queueApi, + jobApi, + monitoringApi, + queueInspector + ); + } + @Bean public JWTService jWTService(MelodicSecurityProperties melodicSecurityProperties) { return new JWTServiceImpl(melodicSecurityProperties); } + + @Bean + public ProviderIdCreatorService providerIdCreatorService() { + return new ProviderIdCreatorService(); + } + + @Bean + ProviderValidationService providerValidationService() { + return new ProviderValidationService(); + } + + @Bean + SecureStoreService secureStoreService(CloudiatorApi cloudiatorApi) { + return new SecureStoreService(cloudiatorApi); + } + + @Bean + public YamlDataService yamlDataService() { + return new YamlDataService(); + } + + @Bean + public ProviderService providerService( + ProviderIdCreatorService providerIdCreatorService, + ProviderValidationService providerValidationService, + CloudiatorApi cloudiatorApi, + SecureStoreService secureStoreService, + YamlDataService yamlDataService + ) { + return new ProviderService( + providerIdCreatorService, + providerValidationService, + cloudiatorApi, + secureStoreService, + yamlDataService + ); + } } diff --git a/gui-backend/src/main/java/eu/melodic/upperware/guibackend/communication/commons/ServiceName.java b/gui-backend/src/main/java/eu/melodic/upperware/guibackend/communication/commons/ServiceName.java index 3b182c4e5590c71ab2f6d077b42fc9733e4ad071..2c443f6a674f11529cf96d90f15a6fc084609bac 100644 --- a/gui-backend/src/main/java/eu/melodic/upperware/guibackend/communication/commons/ServiceName.java +++ b/gui-backend/src/main/java/eu/melodic/upperware/guibackend/communication/commons/ServiceName.java @@ -7,7 +7,8 @@ public enum ServiceName { CAMUNDA("Camunda"), JWT_SERVER("Authorization service"), ADAPTER("Adapter"), - METASOLVER("MetaSolver"); + METASOLVER("MetaSolver"), + FUNCTIONIZER_TESTING_TOOL("Functionizer testing tool"); public final String name; } diff --git a/gui-backend/src/main/java/eu/melodic/upperware/guibackend/communication/mule/deployment/CloudDefinitionRequest.java b/gui-backend/src/main/java/eu/melodic/upperware/guibackend/communication/mule/deployment/CloudDefinitionRequest.java index eb0d67d9bedb8564a00d14c496785a9fe45e5cec..756b1ab25d5ff04028a5f01f4d5a91acb1a8997f 100644 --- a/gui-backend/src/main/java/eu/melodic/upperware/guibackend/communication/mule/deployment/CloudDefinitionRequest.java +++ b/gui-backend/src/main/java/eu/melodic/upperware/guibackend/communication/mule/deployment/CloudDefinitionRequest.java @@ -1,6 +1,6 @@ package eu.melodic.upperware.guibackend.communication.mule.deployment; -import eu.melodic.upperware.guibackend.model.provider.CloudType; +import eu.passage.upperware.commons.model.provider.CloudType; import lombok.Builder; import lombok.Getter; diff --git a/gui-backend/src/main/java/eu/melodic/upperware/guibackend/communication/testingtool/FunctionizerTestingToolApi.java b/gui-backend/src/main/java/eu/melodic/upperware/guibackend/communication/testingtool/FunctionizerTestingToolApi.java new file mode 100644 index 0000000000000000000000000000000000000000..d2bd68cff2608db29c10751a628fd91ece03ebb2 --- /dev/null +++ b/gui-backend/src/main/java/eu/melodic/upperware/guibackend/communication/testingtool/FunctionizerTestingToolApi.java @@ -0,0 +1,5 @@ +package eu.melodic.upperware.guibackend.communication.testingtool; + +public interface FunctionizerTestingToolApi { + Object runTests(String token); +} diff --git a/gui-backend/src/main/java/eu/melodic/upperware/guibackend/communication/testingtool/FunctionizerTestingToolClientApi.java b/gui-backend/src/main/java/eu/melodic/upperware/guibackend/communication/testingtool/FunctionizerTestingToolClientApi.java new file mode 100644 index 0000000000000000000000000000000000000000..8d04ce47e74e5abe703028b991e257b48aea769b --- /dev/null +++ b/gui-backend/src/main/java/eu/melodic/upperware/guibackend/communication/testingtool/FunctionizerTestingToolClientApi.java @@ -0,0 +1,40 @@ +package eu.melodic.upperware.guibackend.communication.testingtool; + +import eu.melodic.upperware.guibackend.communication.commons.RestCommunicationService; +import eu.melodic.upperware.guibackend.communication.commons.ServiceName; +import eu.melodic.upperware.guibackend.properties.GuiBackendProperties; +import org.springframework.core.ParameterizedTypeReference; +import org.springframework.http.HttpEntity; +import org.springframework.http.HttpMethod; +import org.springframework.http.ResponseEntity; +import org.springframework.stereotype.Service; +import org.springframework.web.client.RestTemplate; + +@Service + +public class FunctionizerTestingToolClientApi extends RestCommunicationService implements FunctionizerTestingToolApi { + + private GuiBackendProperties guiBackendProperties; + + public FunctionizerTestingToolClientApi(RestTemplate restTemplate, GuiBackendProperties guiBackendProperties) { + super(restTemplate); + this.guiBackendProperties = guiBackendProperties; + } + + @Override + public Object runTests(String token) { + String requestUrl = guiBackendProperties.getFunctionizerTestingTool().getUrl() + "/test"; + ParameterizedTypeReference responseType = + new ParameterizedTypeReference() { + }; + HttpEntity request = createEmptyHttpEntityWithAuthorizationHeader(token); + ResponseEntity response = getResponse( + requestUrl, + responseType, + request, + ServiceName.FUNCTIONIZER_TESTING_TOOL.name, + HttpMethod.POST + ); + return response.getBody(); + } +} diff --git a/gui-backend/src/main/java/eu/melodic/upperware/guibackend/controller/application/ApplicationController.java b/gui-backend/src/main/java/eu/melodic/upperware/guibackend/controller/application/ApplicationController.java index 7b5a4b1c6355d281e99df666465b7c3d7eb0696a..7a3086f7cac66df7358f074fc6a84327fe7cd035 100644 --- a/gui-backend/src/main/java/eu/melodic/upperware/guibackend/controller/application/ApplicationController.java +++ b/gui-backend/src/main/java/eu/melodic/upperware/guibackend/controller/application/ApplicationController.java @@ -1,6 +1,6 @@ package eu.melodic.upperware.guibackend.controller.application; -import eu.melodic.upperware.guibackend.communication.cloudiator.CloudiatorApi; +import eu.passage.upperware.commons.cloudiator.CloudiatorApi; import io.github.cloudiator.rest.model.Function; import io.github.cloudiator.rest.model.Node; import lombok.AllArgsConstructor; diff --git a/gui-backend/src/main/java/eu/melodic/upperware/guibackend/controller/byon/ByonController.java b/gui-backend/src/main/java/eu/melodic/upperware/guibackend/controller/byon/ByonController.java index 9454cbdef3c1e1e773f7dd47877f26ee939a5599..2ba3df224bcb2864c3b83f3779c06cd4fdc8338e 100644 --- a/gui-backend/src/main/java/eu/melodic/upperware/guibackend/controller/byon/ByonController.java +++ b/gui-backend/src/main/java/eu/melodic/upperware/guibackend/controller/byon/ByonController.java @@ -1,8 +1,8 @@ package eu.melodic.upperware.guibackend.controller.byon; -import eu.melodic.upperware.guibackend.communication.cloudiator.CloudiatorApi; -import eu.melodic.upperware.guibackend.model.byon.ByonDefinition; -import eu.melodic.upperware.guibackend.model.byon.ByonEnums; +import eu.passage.upperware.commons.cloudiator.CloudiatorApi; +import eu.passage.upperware.commons.model.byon.ByonDefinition; +import eu.passage.upperware.commons.model.byon.ByonEnums; import eu.melodic.upperware.guibackend.service.byon.ByonService; import io.github.cloudiator.rest.model.ByonNode; import lombok.AllArgsConstructor; diff --git a/gui-backend/src/main/java/eu/melodic/upperware/guibackend/controller/deployment/DeploymentController.java b/gui-backend/src/main/java/eu/melodic/upperware/guibackend/controller/deployment/DeploymentController.java index 9a6a97cc242af02bcc734b91fdc31251174f235e..7d743a30a1916c6e3452debd8c7289bb3437d1c0 100644 --- a/gui-backend/src/main/java/eu/melodic/upperware/guibackend/controller/deployment/DeploymentController.java +++ b/gui-backend/src/main/java/eu/melodic/upperware/guibackend/controller/deployment/DeploymentController.java @@ -1,12 +1,12 @@ package eu.melodic.upperware.guibackend.controller.deployment; import eu.melodic.upperware.guibackend.controller.common.MelodicHeaders; -import eu.melodic.upperware.guibackend.controller.deployment.common.SecureVariable; +import eu.passage.upperware.commons.model.SecureVariable; import eu.melodic.upperware.guibackend.controller.deployment.request.DeploymentRequest; import eu.melodic.upperware.guibackend.controller.deployment.response.DeploymentResponse; import eu.melodic.upperware.guibackend.controller.deployment.response.UploadXmiResponse; import eu.melodic.upperware.guibackend.service.deployment.DeploymentService; -import eu.melodic.upperware.guibackend.service.secure.store.SecureStoreService; +import eu.passage.upperware.commons.service.store.SecureStoreService; import lombok.AllArgsConstructor; import lombok.extern.slf4j.Slf4j; import org.springframework.beans.factory.annotation.Autowired; diff --git a/gui-backend/src/main/java/eu/melodic/upperware/guibackend/controller/deployment/request/DeploymentRequest.java b/gui-backend/src/main/java/eu/melodic/upperware/guibackend/controller/deployment/request/DeploymentRequest.java index 9b4f4f8bfaca9d647829e7b843e6175dfb3fc3eb..b0e2173a26863b978b9ed92aa79bd268edf2c856 100644 --- a/gui-backend/src/main/java/eu/melodic/upperware/guibackend/controller/deployment/request/DeploymentRequest.java +++ b/gui-backend/src/main/java/eu/melodic/upperware/guibackend/controller/deployment/request/DeploymentRequest.java @@ -1,6 +1,6 @@ package eu.melodic.upperware.guibackend.controller.deployment.request; -import eu.melodic.upperware.guibackend.model.provider.CloudDefinition; +import eu.passage.upperware.commons.model.provider.CloudDefinition; import lombok.AllArgsConstructor; import lombok.Getter; import lombok.NoArgsConstructor; diff --git a/gui-backend/src/main/java/eu/melodic/upperware/guibackend/controller/deployment/response/UploadXmiResponse.java b/gui-backend/src/main/java/eu/melodic/upperware/guibackend/controller/deployment/response/UploadXmiResponse.java index c1f2c6ed5669990c78528b5b1e34620ca28b9a37..c470334ceb085b17caa56bc8b24f0c8562fcbfc1 100644 --- a/gui-backend/src/main/java/eu/melodic/upperware/guibackend/controller/deployment/response/UploadXmiResponse.java +++ b/gui-backend/src/main/java/eu/melodic/upperware/guibackend/controller/deployment/response/UploadXmiResponse.java @@ -1,6 +1,6 @@ package eu.melodic.upperware.guibackend.controller.deployment.response; -import eu.melodic.upperware.guibackend.controller.deployment.common.SecureVariable; +import eu.passage.upperware.commons.model.SecureVariable; import lombok.*; import org.springframework.http.HttpStatus; diff --git a/gui-backend/src/main/java/eu/melodic/upperware/guibackend/controller/process/ProcessController.java b/gui-backend/src/main/java/eu/melodic/upperware/guibackend/controller/process/ProcessController.java index 4e85c778ff2131353d59464d04e5ba615468ef05..0aea5c92abc9cef05eb699133e8c72c6e907c4cc 100644 --- a/gui-backend/src/main/java/eu/melodic/upperware/guibackend/controller/process/ProcessController.java +++ b/gui-backend/src/main/java/eu/melodic/upperware/guibackend/controller/process/ProcessController.java @@ -1,7 +1,7 @@ package eu.melodic.upperware.guibackend.controller.process; import eu.melodic.models.services.adapter.DifferenceResponse; -import eu.melodic.upperware.guibackend.communication.cloudiator.CloudiatorApi; +import eu.passage.upperware.commons.cloudiator.CloudiatorApi; import eu.melodic.upperware.guibackend.controller.process.response.CpModelResponse; import eu.melodic.upperware.guibackend.controller.process.response.CpSolutionResponse; import eu.melodic.upperware.guibackend.controller.process.response.ProcessInstanceResponse; diff --git a/gui-backend/src/main/java/eu/melodic/upperware/guibackend/controller/provider/ProviderController.java b/gui-backend/src/main/java/eu/melodic/upperware/guibackend/controller/provider/ProviderController.java index bc871e8ccde050c38b49843b6dce9e5752e16c00..e7d8bfade3a0671363471c307d2cd0bed508f8b6 100644 --- a/gui-backend/src/main/java/eu/melodic/upperware/guibackend/controller/provider/ProviderController.java +++ b/gui-backend/src/main/java/eu/melodic/upperware/guibackend/controller/provider/ProviderController.java @@ -1,8 +1,8 @@ package eu.melodic.upperware.guibackend.controller.provider; -import eu.melodic.upperware.guibackend.model.provider.CloudDefinition; -import eu.melodic.upperware.guibackend.model.provider.ProviderEnums; -import eu.melodic.upperware.guibackend.service.provider.ProviderService; +import eu.passage.upperware.commons.model.provider.CloudDefinition; +import eu.passage.upperware.commons.model.provider.ProviderEnums; +import eu.passage.upperware.commons.service.provider.ProviderService; import lombok.AllArgsConstructor; import lombok.extern.slf4j.Slf4j; import org.springframework.beans.factory.annotation.Autowired; diff --git a/gui-backend/src/main/java/eu/melodic/upperware/guibackend/controller/testing/TestingController.java b/gui-backend/src/main/java/eu/melodic/upperware/guibackend/controller/testing/TestingController.java new file mode 100644 index 0000000000000000000000000000000000000000..db47da5f2a2c15a80ad29746d7e4224c8acd4fca --- /dev/null +++ b/gui-backend/src/main/java/eu/melodic/upperware/guibackend/controller/testing/TestingController.java @@ -0,0 +1,40 @@ +package eu.melodic.upperware.guibackend.controller.testing; + +import eu.melodic.upperware.guibackend.communication.testingtool.FunctionizerTestingToolApi; +import eu.melodic.upperware.guibackend.controller.testing.response.UploadTestConfigResponse; +import eu.melodic.upperware.guibackend.service.testing.TestingService; +import lombok.AllArgsConstructor; +import lombok.extern.slf4j.Slf4j; +import org.springframework.http.HttpHeaders; +import org.springframework.http.HttpStatus; +import org.springframework.web.bind.annotation.*; +import org.springframework.web.multipart.MultipartFile; + +@RestController +@RequestMapping("/auth/test") +@Slf4j +@AllArgsConstructor +public class TestingController { + + TestingService testingService; + FunctionizerTestingToolApi functionizerTestApi; + + @PostMapping(value = "/upload", consumes = {"multipart/form-data"}) + @ResponseStatus(HttpStatus.CREATED) + public UploadTestConfigResponse uploadTestConfigFile(@RequestParam("file") MultipartFile file) { + String originalName = file.getResource().getFilename(); + log.info( + "POST request for upload test configuration file with name: {}", + originalName + ); + String fileName = testingService.uploadTestConfig(file); + log.info("File {} successfully uploaded.", originalName); + return testingService.createUploadTestConfigResponse(fileName); + } + + @PostMapping(value = "/run") + @ResponseStatus(HttpStatus.OK) + public Object runTests(@RequestHeader(value = HttpHeaders.AUTHORIZATION) String token) { + return functionizerTestApi.runTests(token); + } +} diff --git a/gui-backend/src/main/java/eu/melodic/upperware/guibackend/controller/testing/response/UploadTestConfigResponse.java b/gui-backend/src/main/java/eu/melodic/upperware/guibackend/controller/testing/response/UploadTestConfigResponse.java new file mode 100644 index 0000000000000000000000000000000000000000..d16aa9cf15f01489fcd536156be68a77a2907a7a --- /dev/null +++ b/gui-backend/src/main/java/eu/melodic/upperware/guibackend/controller/testing/response/UploadTestConfigResponse.java @@ -0,0 +1,12 @@ +package eu.melodic.upperware.guibackend.controller.testing.response; + +import lombok.*; + +@Getter +@Setter +@Builder +@NoArgsConstructor +@AllArgsConstructor +public class UploadTestConfigResponse { + private String testConfigFilePath; +} diff --git a/gui-backend/src/main/java/eu/melodic/upperware/guibackend/exception/controller/GuiExceptionHandler.java b/gui-backend/src/main/java/eu/melodic/upperware/guibackend/exception/controller/GuiExceptionHandler.java index 3fc356d97924b49687591050a1b307199bd7bd94..3ebe1847c967823781907631606a8a09c23736ee 100644 --- a/gui-backend/src/main/java/eu/melodic/upperware/guibackend/exception/controller/GuiExceptionHandler.java +++ b/gui-backend/src/main/java/eu/melodic/upperware/guibackend/exception/controller/GuiExceptionHandler.java @@ -1,7 +1,7 @@ package eu.melodic.upperware.guibackend.exception.controller; import eu.melodic.upperware.guibackend.exception.CamundaErrorVariableException; -import eu.melodic.upperware.guibackend.exception.SecureVariableNotFoundException; +import eu.passage.upperware.commons.exception.SecureVariableNotFoundException; import eu.melodic.upperware.guibackend.exception.response.CamundaVariableErrorResponse; import eu.melodic.upperware.guibackend.exception.response.MissingSecureVariableErrorResponse; import org.springframework.http.HttpStatus; diff --git a/gui-backend/src/main/java/eu/melodic/upperware/guibackend/model/provider/CloudType.java b/gui-backend/src/main/java/eu/melodic/upperware/guibackend/model/provider/CloudType.java deleted file mode 100644 index 0c94b5dc09845b0500796d0a24d17b9438fa2d2a..0000000000000000000000000000000000000000 --- a/gui-backend/src/main/java/eu/melodic/upperware/guibackend/model/provider/CloudType.java +++ /dev/null @@ -1,5 +0,0 @@ -package eu.melodic.upperware.guibackend.model.provider; - -public enum CloudType { - PRIVATE, PUBLIC -} diff --git a/gui-backend/src/main/java/eu/melodic/upperware/guibackend/properties/GuiBackendProperties.java b/gui-backend/src/main/java/eu/melodic/upperware/guibackend/properties/GuiBackendProperties.java index a127412cf2734229a916473ce3c1120f7c51a920..fc65991e8a752d608520cfd111ba8c41fa9fb847 100644 --- a/gui-backend/src/main/java/eu/melodic/upperware/guibackend/properties/GuiBackendProperties.java +++ b/gui-backend/src/main/java/eu/melodic/upperware/guibackend/properties/GuiBackendProperties.java @@ -43,6 +43,10 @@ public class GuiBackendProperties { @NotNull private ExternalService metaSolver; + @Valid + @NotNull + private ExternalService functionizerTestingTool; + @Getter @Setter public static class Mule extends ExternalService { diff --git a/gui-backend/src/main/java/eu/melodic/upperware/guibackend/service/byon/ByonIdCreatorService.java b/gui-backend/src/main/java/eu/melodic/upperware/guibackend/service/byon/ByonIdCreatorService.java index d20cde8e66f44e7ed051082d1d88bd04592c2bfb..b1e43d5e7bf10265851a1de083cd32c8de350d61 100644 --- a/gui-backend/src/main/java/eu/melodic/upperware/guibackend/service/byon/ByonIdCreatorService.java +++ b/gui-backend/src/main/java/eu/melodic/upperware/guibackend/service/byon/ByonIdCreatorService.java @@ -1,7 +1,7 @@ package eu.melodic.upperware.guibackend.service.byon; -import eu.melodic.upperware.guibackend.model.byon.ByonDefinition; -import eu.melodic.upperware.guibackend.model.byon.IpAddress; +import eu.passage.upperware.commons.model.byon.ByonDefinition; +import eu.passage.upperware.commons.model.byon.IpAddress; import lombok.AllArgsConstructor; import lombok.extern.slf4j.Slf4j; import org.springframework.beans.factory.annotation.Autowired; diff --git a/gui-backend/src/main/java/eu/melodic/upperware/guibackend/service/byon/ByonMapper.java b/gui-backend/src/main/java/eu/melodic/upperware/guibackend/service/byon/ByonMapper.java index a596bc5943e36e54bac3e12adc6d80c6e2c08171..d86a29b399cf70aa4763e41c9e0dce3ee867d12a 100644 --- a/gui-backend/src/main/java/eu/melodic/upperware/guibackend/service/byon/ByonMapper.java +++ b/gui-backend/src/main/java/eu/melodic/upperware/guibackend/service/byon/ByonMapper.java @@ -1,6 +1,6 @@ package eu.melodic.upperware.guibackend.service.byon; -import eu.melodic.upperware.guibackend.model.byon.ByonDefinition; +import eu.passage.upperware.commons.model.byon.ByonDefinition; import io.github.cloudiator.rest.model.IpAddress; import io.github.cloudiator.rest.model.NewNode; import io.github.cloudiator.rest.model.NodeProperties; @@ -20,7 +20,7 @@ public class ByonMapper { return newNode; } - private NodeProperties mapGuiNodePropertiesToCloudiatorNodeProperties(eu.melodic.upperware.guibackend.model.byon.NodeProperties guiNodeProperties) { + private NodeProperties mapGuiNodePropertiesToCloudiatorNodeProperties(eu.passage.upperware.commons.model.byon.NodeProperties guiNodeProperties) { NodeProperties resultNodeProperties = new NodeProperties(); resultNodeProperties.setProviderId(guiNodeProperties.getProviderId()); resultNodeProperties.setNumberOfCores(guiNodeProperties.getNumberOfCores()); @@ -31,7 +31,7 @@ public class ByonMapper { return resultNodeProperties; } - private List mapGuiIpAddressesToCloudiatorIpAddresses(List ipAddresses) { + private List mapGuiIpAddressesToCloudiatorIpAddresses(List ipAddresses) { return ipAddresses.stream() .map(ipAddress -> (IpAddress) ipAddress) .collect(Collectors.toList()); diff --git a/gui-backend/src/main/java/eu/melodic/upperware/guibackend/service/byon/ByonService.java b/gui-backend/src/main/java/eu/melodic/upperware/guibackend/service/byon/ByonService.java index f701a1509415095efea3c456549a158aad8abded..785072ec4fdc0de04769366891a0e5be034e6b24 100644 --- a/gui-backend/src/main/java/eu/melodic/upperware/guibackend/service/byon/ByonService.java +++ b/gui-backend/src/main/java/eu/melodic/upperware/guibackend/service/byon/ByonService.java @@ -1,12 +1,12 @@ package eu.melodic.upperware.guibackend.service.byon; -import eu.melodic.upperware.guibackend.communication.cloudiator.CloudiatorApi; +import eu.passage.upperware.commons.cloudiator.CloudiatorApi; import eu.melodic.upperware.guibackend.exception.ByonDefinitionNotFoundException; -import eu.melodic.upperware.guibackend.model.byon.ByonDefinition; -import eu.melodic.upperware.guibackend.model.byon.ByonEnums; -import eu.melodic.upperware.guibackend.model.byon.LoginCredential; -import eu.melodic.upperware.guibackend.service.secure.store.SecureStoreService; -import eu.melodic.upperware.guibackend.service.yaml.YamlDataService; +import eu.passage.upperware.commons.model.byon.ByonDefinition; +import eu.passage.upperware.commons.model.byon.ByonEnums; +import eu.passage.upperware.commons.model.byon.LoginCredential; +import eu.passage.upperware.commons.service.store.SecureStoreService; +import eu.passage.upperware.commons.service.yaml.YamlDataService; import io.github.cloudiator.rest.model.*; import lombok.AllArgsConstructor; import lombok.extern.slf4j.Slf4j; diff --git a/gui-backend/src/main/java/eu/melodic/upperware/guibackend/service/deployment/DeploymentMapper.java b/gui-backend/src/main/java/eu/melodic/upperware/guibackend/service/deployment/DeploymentMapper.java index 40d2c3ee3220434b7d7efb3bfb0672c8c1fbfb41..a3ed624ea281c1b8759b7865ae5319f5abe5f46c 100644 --- a/gui-backend/src/main/java/eu/melodic/upperware/guibackend/service/deployment/DeploymentMapper.java +++ b/gui-backend/src/main/java/eu/melodic/upperware/guibackend/service/deployment/DeploymentMapper.java @@ -8,7 +8,7 @@ import eu.melodic.upperware.guibackend.communication.mule.deployment.CloudConfig import eu.melodic.upperware.guibackend.communication.mule.deployment.CloudDefinitionRequest; import eu.melodic.upperware.guibackend.communication.mule.deployment.CredentialRequest; import eu.melodic.upperware.guibackend.controller.deployment.request.DeploymentRequest; -import eu.melodic.upperware.guibackend.model.provider.*; +import eu.passage.upperware.commons.model.provider.*; import org.apache.commons.lang3.RandomStringUtils; import org.apache.commons.lang3.StringUtils; import org.springframework.stereotype.Service; diff --git a/gui-backend/src/main/java/eu/melodic/upperware/guibackend/service/deployment/DeploymentService.java b/gui-backend/src/main/java/eu/melodic/upperware/guibackend/service/deployment/DeploymentService.java index 082d275ea53232721e2fa6368814282836ea05d7..5e1ccdc969b931472b38dbfbc53aab1d3e8a91f0 100644 --- a/gui-backend/src/main/java/eu/melodic/upperware/guibackend/service/deployment/DeploymentService.java +++ b/gui-backend/src/main/java/eu/melodic/upperware/guibackend/service/deployment/DeploymentService.java @@ -4,14 +4,14 @@ import eu.melodic.models.commons.Watermark; import eu.melodic.models.commons.WatermarkImpl; import eu.melodic.models.services.frontend.DeploymentProcessRequest; import eu.melodic.upperware.guibackend.communication.mule.MuleApi; -import eu.melodic.upperware.guibackend.controller.deployment.common.SecureVariable; +import eu.passage.upperware.commons.model.SecureVariable; import eu.melodic.upperware.guibackend.controller.deployment.request.DeploymentRequest; import eu.melodic.upperware.guibackend.controller.deployment.response.DeploymentResponse; import eu.melodic.upperware.guibackend.controller.deployment.response.UploadXmiResponse; import eu.melodic.upperware.guibackend.service.cdo.CdoService; import eu.melodic.upperware.guibackend.service.cdo.ModelNameGenerator; -import eu.melodic.upperware.guibackend.service.provider.ProviderService; -import eu.melodic.upperware.guibackend.service.secure.store.SecureStoreService; +import eu.passage.upperware.commons.service.provider.ProviderService; +import eu.passage.upperware.commons.service.store.SecureStoreService; import lombok.AllArgsConstructor; import lombok.extern.slf4j.Slf4j; import org.eclipse.net4j.connector.ConnectorException; diff --git a/gui-backend/src/main/java/eu/melodic/upperware/guibackend/service/testing/TestingService.java b/gui-backend/src/main/java/eu/melodic/upperware/guibackend/service/testing/TestingService.java new file mode 100644 index 0000000000000000000000000000000000000000..aa3e7a715ed763e2ee2be5b5fd050ada531838df --- /dev/null +++ b/gui-backend/src/main/java/eu/melodic/upperware/guibackend/service/testing/TestingService.java @@ -0,0 +1,102 @@ +package eu.melodic.upperware.guibackend.service.testing; + +import eu.melodic.upperware.guibackend.controller.testing.response.UploadTestConfigResponse; +import eu.passage.upperware.commons.MelodicConstants; +import eu.passage.upperware.commons.model.testing.FunctionTestConfiguration; +import eu.passage.upperware.commons.model.testing.TestConfiguration; +import eu.passage.upperware.commons.service.testing.TestConfigurationValidationService; +import lombok.AllArgsConstructor; +import lombok.extern.slf4j.Slf4j; +import org.eclipse.net4j.connector.ConnectorException; +import org.springframework.beans.factory.annotation.Autowired; +import org.springframework.http.HttpStatus; +import org.springframework.stereotype.Service; +import org.springframework.web.multipart.MultipartFile; +import org.springframework.web.server.ResponseStatusException; +import org.yaml.snakeyaml.Yaml; +import org.yaml.snakeyaml.constructor.Constructor; +import org.yaml.snakeyaml.constructor.ConstructorException; + +import java.io.*; + +import static eu.passage.upperware.commons.service.testing.TestConfigurationValidationService.checkFunctionNamesUniqueness; +import static eu.passage.upperware.commons.service.testing.TestConfigurationValidationService.checkTestCasesUniqueness; + + +@Service +@Slf4j +@AllArgsConstructor(onConstructor = @__(@Autowired)) +public class TestingService { + private final static String CONFIG_FILE_PATH = MelodicConstants.TEST_CONFIG_FILE_DIR + "/tests.yml"; + + public String uploadTestConfig(MultipartFile uploadFileRequest) { + try { + if (uploadFileRequest.getOriginalFilename() == null) { + throw new ResponseStatusException( + HttpStatus.BAD_REQUEST, + String.format("Problem by uploading your %s file. Please try again.", + uploadFileRequest.getResource().getFilename() + )); + } + File newFile = new File(CONFIG_FILE_PATH); + if (!newFile.exists()) { + newFile.getParentFile().mkdirs(); + } + validate(uploadFileRequest.getInputStream()); + + uploadFileRequest.transferTo(newFile); + log.info( + "File {} will be stored under path: {}", + uploadFileRequest.getResource().getFilename(), + CONFIG_FILE_PATH + ); + return CONFIG_FILE_PATH; + + } catch (IOException | IllegalStateException e) { + log.error("Error by uploading test configuration file:", e); + throw new ResponseStatusException( + HttpStatus.BAD_REQUEST, + String.format("Problem by uploading your %s file. Please try again.", + uploadFileRequest.getResource().getFilename() + )); + } catch (ConnectorException e) { + log.error("Error by uploading test configuration file:", e); + throw new ResponseStatusException( + HttpStatus.BAD_REQUEST, + String.format("Problem by uploading your %s file. CDO repository not working. Please try again.", + uploadFileRequest.getResource().getFilename() + )); + } + } + + public UploadTestConfigResponse createUploadTestConfigResponse(String fileName) { + return new UploadTestConfigResponse(fileName); + } + + private void validate(InputStream ymlFileInputStream) throws ResponseStatusException { + Yaml yaml = new Yaml(new Constructor(TestConfiguration.class)); + TestConfiguration configuration; + try { + configuration = yaml.load(ymlFileInputStream); + log.info("Checking uniqueness of function names"); + checkFunctionNamesUniqueness(configuration.getTests()); + + log.info("Checking uniqueness of test cases"); + for (FunctionTestConfiguration functionTestConfiguration : configuration.getTests()) { + checkTestCasesUniqueness( + functionTestConfiguration.getTestCases(), + functionTestConfiguration.getFunctionName() + ); + } + } catch (ConstructorException e) { + String errorMessage = "The file has a bad format and could not be parsed."; + log.error(errorMessage); + throw new ResponseStatusException(HttpStatus.BAD_REQUEST, errorMessage); + } catch ( + TestConfigurationValidationService.NotUniqueFunctionNameException + | TestConfigurationValidationService.NotUniqueTestCaseException e + ) { + throw new ResponseStatusException(HttpStatus.BAD_REQUEST, e.getMessage()); + } + } +} diff --git a/melodic-commons/pom.xml b/melodic-commons/pom.xml index 14b4207fcfeac8f015d10b2295ccadb3eca90f56..aa9322aae245d82b666d37ca4e2dd107f40438e2 100644 --- a/melodic-commons/pom.xml +++ b/melodic-commons/pom.xml @@ -33,12 +33,24 @@ 1.7.25 + + org.yaml + snakeyaml + 1.21 + + eu.melodic.cdo client repackaged + + io.github.cloudiator.client + java-rest + compile + + org.apache.commons commons-collections4 @@ -59,6 +71,20 @@ commons-lang3 + + javax.ws.rs + javax.ws.rs-api + 2.1 + compile + + + + org.codehaus.jackson + jackson-mapper-asl + 1.9.13 + compile + + diff --git a/melodic-commons/src/main/java/eu/passage/upperware/commons/MelodicConstants.java b/melodic-commons/src/main/java/eu/passage/upperware/commons/MelodicConstants.java index e17de0af93a257ba6803e4584ab3bc98370be872..8ff1e2319227430e8f3e4905765617cf08ad9c48 100644 --- a/melodic-commons/src/main/java/eu/passage/upperware/commons/MelodicConstants.java +++ b/melodic-commons/src/main/java/eu/passage/upperware/commons/MelodicConstants.java @@ -14,6 +14,7 @@ public class MelodicConstants { public static final String TOMCAT_TEM_DIR= ".."+File.separator+"temp"+File.separator; public static final String TOMCAT_GENERATION_DIR= TOMCAT_TEM_DIR+"paasage"+File.separator+"configurations"+File.separator; public static final String TOMCAT_ALT_GENERATION_TEMP_DIR= TOMCAT_ALT_TEM_DIR+"paasage"+File.separator+"configurations"+File.separator; + public static final String TEST_CONFIG_FILE_DIR = System.getenv("MELODIC_CONFIG_DIR") + File.separator + "tests"; private MelodicConstants() {} } diff --git a/gui-backend/src/main/java/eu/melodic/upperware/guibackend/communication/cloudiator/CloudiatorApi.java b/melodic-commons/src/main/java/eu/passage/upperware/commons/cloudiator/CloudiatorApi.java similarity index 93% rename from gui-backend/src/main/java/eu/melodic/upperware/guibackend/communication/cloudiator/CloudiatorApi.java rename to melodic-commons/src/main/java/eu/passage/upperware/commons/cloudiator/CloudiatorApi.java index 38771a21076a0d7396743b3e94ed374a739a964c..28ac65d24e75d2fcc7038f14dbf46ae7876865e9 100644 --- a/gui-backend/src/main/java/eu/melodic/upperware/guibackend/communication/cloudiator/CloudiatorApi.java +++ b/melodic-commons/src/main/java/eu/passage/upperware/commons/cloudiator/CloudiatorApi.java @@ -1,4 +1,4 @@ -package eu.melodic.upperware.guibackend.communication.cloudiator; +package eu.passage.upperware.commons.cloudiator; import io.github.cloudiator.rest.model.*; diff --git a/gui-backend/src/main/java/eu/melodic/upperware/guibackend/communication/cloudiator/CloudiatorClientApi.java b/melodic-commons/src/main/java/eu/passage/upperware/commons/cloudiator/CloudiatorClientApi.java similarity index 98% rename from gui-backend/src/main/java/eu/melodic/upperware/guibackend/communication/cloudiator/CloudiatorClientApi.java rename to melodic-commons/src/main/java/eu/passage/upperware/commons/cloudiator/CloudiatorClientApi.java index 30b2e4cbe32b8e1facf0282d598e1a0a9d3094a9..e4fda2b91fb10c03f463eb8cd2cbceae3576fe18 100644 --- a/gui-backend/src/main/java/eu/melodic/upperware/guibackend/communication/cloudiator/CloudiatorClientApi.java +++ b/melodic-commons/src/main/java/eu/passage/upperware/commons/cloudiator/CloudiatorClientApi.java @@ -1,6 +1,6 @@ -package eu.melodic.upperware.guibackend.communication.cloudiator; +package eu.passage.upperware.commons.cloudiator; -import eu.melodic.upperware.guibackend.exception.SecureVariableNotFoundException; +import eu.passage.upperware.commons.exception.SecureVariableNotFoundException; import io.github.cloudiator.rest.ApiException; import io.github.cloudiator.rest.api.*; import io.github.cloudiator.rest.model.*; diff --git a/gui-backend/src/main/java/eu/melodic/upperware/guibackend/communication/cloudiator/QueueInspector.java b/melodic-commons/src/main/java/eu/passage/upperware/commons/cloudiator/QueueInspector.java similarity index 97% rename from gui-backend/src/main/java/eu/melodic/upperware/guibackend/communication/cloudiator/QueueInspector.java rename to melodic-commons/src/main/java/eu/passage/upperware/commons/cloudiator/QueueInspector.java index aed5bd2cd9bb2892418e45fee4127a809c5c43c4..2fad9481d4abf65d94899e39ae0039582b64eb5f 100644 --- a/gui-backend/src/main/java/eu/melodic/upperware/guibackend/communication/cloudiator/QueueInspector.java +++ b/melodic-commons/src/main/java/eu/passage/upperware/commons/cloudiator/QueueInspector.java @@ -1,4 +1,4 @@ -package eu.melodic.upperware.guibackend.communication.cloudiator; +package eu.passage.upperware.commons.cloudiator; import eu.passage.upperware.commons.cloudiator.CloudiatorProperties; import io.github.cloudiator.rest.ApiException; diff --git a/gui-backend/src/main/java/eu/melodic/upperware/guibackend/exception/CloudDefinitionNotFoundException.java b/melodic-commons/src/main/java/eu/passage/upperware/commons/exception/CloudDefinitionNotFoundException.java similarity index 88% rename from gui-backend/src/main/java/eu/melodic/upperware/guibackend/exception/CloudDefinitionNotFoundException.java rename to melodic-commons/src/main/java/eu/passage/upperware/commons/exception/CloudDefinitionNotFoundException.java index 061cf8d04de0a9d5adf4c16d37f8247ce1ffda69..e5989c45eb1e2ffe76ec1f7633b9fad948da3270 100644 --- a/gui-backend/src/main/java/eu/melodic/upperware/guibackend/exception/CloudDefinitionNotFoundException.java +++ b/melodic-commons/src/main/java/eu/passage/upperware/commons/exception/CloudDefinitionNotFoundException.java @@ -1,4 +1,4 @@ -package eu.melodic.upperware.guibackend.exception; +package eu.passage.upperware.commons.exception; import org.springframework.http.HttpStatus; import org.springframework.web.bind.annotation.ResponseStatus; diff --git a/gui-backend/src/main/java/eu/melodic/upperware/guibackend/exception/SecureVariableNotFoundException.java b/melodic-commons/src/main/java/eu/passage/upperware/commons/exception/SecureVariableNotFoundException.java similarity index 88% rename from gui-backend/src/main/java/eu/melodic/upperware/guibackend/exception/SecureVariableNotFoundException.java rename to melodic-commons/src/main/java/eu/passage/upperware/commons/exception/SecureVariableNotFoundException.java index c8a0d0239bcc43c7fa8f2e2fc3cead30c16d1cc1..76b3b349eda400a85f5f9bba4ff9352e94cfab9e 100644 --- a/gui-backend/src/main/java/eu/melodic/upperware/guibackend/exception/SecureVariableNotFoundException.java +++ b/melodic-commons/src/main/java/eu/passage/upperware/commons/exception/SecureVariableNotFoundException.java @@ -1,4 +1,4 @@ -package eu.melodic.upperware.guibackend.exception; +package eu.passage.upperware.commons.exception; import lombok.Getter; import lombok.Setter; diff --git a/gui-backend/src/main/java/eu/melodic/upperware/guibackend/exception/ValidationException.java b/melodic-commons/src/main/java/eu/passage/upperware/commons/exception/ValidationException.java similarity index 85% rename from gui-backend/src/main/java/eu/melodic/upperware/guibackend/exception/ValidationException.java rename to melodic-commons/src/main/java/eu/passage/upperware/commons/exception/ValidationException.java index 2bd3c097156583614d27e870906255e87d224f22..b5ca3734a950d4538630483dad2ad21f28237c0e 100644 --- a/gui-backend/src/main/java/eu/melodic/upperware/guibackend/exception/ValidationException.java +++ b/melodic-commons/src/main/java/eu/passage/upperware/commons/exception/ValidationException.java @@ -1,4 +1,4 @@ -package eu.melodic.upperware.guibackend.exception; +package eu.passage.upperware.commons.exception; import org.springframework.http.HttpStatus; import org.springframework.web.bind.annotation.ResponseStatus; diff --git a/gui-backend/src/main/java/eu/melodic/upperware/guibackend/properties/GuiYamlData.java b/melodic-commons/src/main/java/eu/passage/upperware/commons/model/GuiYamlData.java similarity index 63% rename from gui-backend/src/main/java/eu/melodic/upperware/guibackend/properties/GuiYamlData.java rename to melodic-commons/src/main/java/eu/passage/upperware/commons/model/GuiYamlData.java index 59f9aedcec5d0e65bf0031ca50726fa4a7e0f436..04deefbcfc992feabae3d9fb59113585fbf4314c 100644 --- a/gui-backend/src/main/java/eu/melodic/upperware/guibackend/properties/GuiYamlData.java +++ b/melodic-commons/src/main/java/eu/passage/upperware/commons/model/GuiYamlData.java @@ -1,7 +1,7 @@ -package eu.melodic.upperware.guibackend.properties; +package eu.passage.upperware.commons.model; -import eu.melodic.upperware.guibackend.model.byon.ByonDefinition; -import eu.melodic.upperware.guibackend.model.provider.CloudDefinition; +import eu.passage.upperware.commons.model.byon.ByonDefinition; +import eu.passage.upperware.commons.model.provider.CloudDefinition; import lombok.AllArgsConstructor; import lombok.Getter; import lombok.NoArgsConstructor; diff --git a/gui-backend/src/main/java/eu/melodic/upperware/guibackend/controller/deployment/common/SecureVariable.java b/melodic-commons/src/main/java/eu/passage/upperware/commons/model/SecureVariable.java similarity index 78% rename from gui-backend/src/main/java/eu/melodic/upperware/guibackend/controller/deployment/common/SecureVariable.java rename to melodic-commons/src/main/java/eu/passage/upperware/commons/model/SecureVariable.java index 633d187641fd2fb51325e6c61f647233ea9d3a3e..7cfdbfe05b0f1618536a676d7e7f96fa42c7133c 100644 --- a/gui-backend/src/main/java/eu/melodic/upperware/guibackend/controller/deployment/common/SecureVariable.java +++ b/melodic-commons/src/main/java/eu/passage/upperware/commons/model/SecureVariable.java @@ -1,4 +1,4 @@ -package eu.melodic.upperware.guibackend.controller.deployment.common; +package eu.passage.upperware.commons.model; import lombok.AllArgsConstructor; import lombok.Getter; diff --git a/gui-backend/src/main/java/eu/melodic/upperware/guibackend/model/byon/ByonDefinition.java b/melodic-commons/src/main/java/eu/passage/upperware/commons/model/byon/ByonDefinition.java similarity index 88% rename from gui-backend/src/main/java/eu/melodic/upperware/guibackend/model/byon/ByonDefinition.java rename to melodic-commons/src/main/java/eu/passage/upperware/commons/model/byon/ByonDefinition.java index 63b32d2a2b5657409d2b0659dc08063169daf564..853785406fb734ec7b17cdde5a94363b107054a6 100644 --- a/gui-backend/src/main/java/eu/melodic/upperware/guibackend/model/byon/ByonDefinition.java +++ b/melodic-commons/src/main/java/eu/passage/upperware/commons/model/byon/ByonDefinition.java @@ -1,4 +1,4 @@ -package eu.melodic.upperware.guibackend.model.byon; +package eu.passage.upperware.commons.model.byon; import lombok.AllArgsConstructor; import lombok.Getter; diff --git a/gui-backend/src/main/java/eu/melodic/upperware/guibackend/model/byon/ByonEnums.java b/melodic-commons/src/main/java/eu/passage/upperware/commons/model/byon/ByonEnums.java similarity index 84% rename from gui-backend/src/main/java/eu/melodic/upperware/guibackend/model/byon/ByonEnums.java rename to melodic-commons/src/main/java/eu/passage/upperware/commons/model/byon/ByonEnums.java index ab06575d9ee47f116f4c9a84d1cc850b969a493f..be2212f75231ed375c422d71c84f4d0ad38cc25e 100644 --- a/gui-backend/src/main/java/eu/melodic/upperware/guibackend/model/byon/ByonEnums.java +++ b/melodic-commons/src/main/java/eu/passage/upperware/commons/model/byon/ByonEnums.java @@ -1,4 +1,4 @@ -package eu.melodic.upperware.guibackend.model.byon; +package eu.passage.upperware.commons.model.byon; import lombok.*; diff --git a/gui-backend/src/main/java/eu/melodic/upperware/guibackend/model/byon/GeoLocation.java b/melodic-commons/src/main/java/eu/passage/upperware/commons/model/byon/GeoLocation.java similarity index 83% rename from gui-backend/src/main/java/eu/melodic/upperware/guibackend/model/byon/GeoLocation.java rename to melodic-commons/src/main/java/eu/passage/upperware/commons/model/byon/GeoLocation.java index 7cc577584876e9ed2f12108cefcae8d59de42c60..dc56684810f6df94d84916d935012a3e50d80418 100644 --- a/gui-backend/src/main/java/eu/melodic/upperware/guibackend/model/byon/GeoLocation.java +++ b/melodic-commons/src/main/java/eu/passage/upperware/commons/model/byon/GeoLocation.java @@ -1,4 +1,4 @@ -package eu.melodic.upperware.guibackend.model.byon; +package eu.passage.upperware.commons.model.byon; import lombok.AllArgsConstructor; import lombok.Getter; diff --git a/gui-backend/src/main/java/eu/melodic/upperware/guibackend/model/byon/IpAddress.java b/melodic-commons/src/main/java/eu/passage/upperware/commons/model/byon/IpAddress.java similarity index 83% rename from gui-backend/src/main/java/eu/melodic/upperware/guibackend/model/byon/IpAddress.java rename to melodic-commons/src/main/java/eu/passage/upperware/commons/model/byon/IpAddress.java index 5eb56663c9d1f81f8f52107b6d7d9171f24311b7..58cf61ceffcb02443b84098d474a269826a2cc8a 100644 --- a/gui-backend/src/main/java/eu/melodic/upperware/guibackend/model/byon/IpAddress.java +++ b/melodic-commons/src/main/java/eu/passage/upperware/commons/model/byon/IpAddress.java @@ -1,4 +1,4 @@ -package eu.melodic.upperware.guibackend.model.byon; +package eu.passage.upperware.commons.model.byon; import lombok.AllArgsConstructor; diff --git a/gui-backend/src/main/java/eu/melodic/upperware/guibackend/model/byon/LoginCredential.java b/melodic-commons/src/main/java/eu/passage/upperware/commons/model/byon/LoginCredential.java similarity index 84% rename from gui-backend/src/main/java/eu/melodic/upperware/guibackend/model/byon/LoginCredential.java rename to melodic-commons/src/main/java/eu/passage/upperware/commons/model/byon/LoginCredential.java index 2da0f48a13cbc393c14758581beb2c8157f5d450..5fff7b327ae39f28cfbd6a93c4ade505dcfc4fb2 100644 --- a/gui-backend/src/main/java/eu/melodic/upperware/guibackend/model/byon/LoginCredential.java +++ b/melodic-commons/src/main/java/eu/passage/upperware/commons/model/byon/LoginCredential.java @@ -1,4 +1,4 @@ -package eu.melodic.upperware.guibackend.model.byon; +package eu.passage.upperware.commons.model.byon; import lombok.AllArgsConstructor; import lombok.Getter; diff --git a/gui-backend/src/main/java/eu/melodic/upperware/guibackend/model/byon/NodeProperties.java b/melodic-commons/src/main/java/eu/passage/upperware/commons/model/byon/NodeProperties.java similarity index 88% rename from gui-backend/src/main/java/eu/melodic/upperware/guibackend/model/byon/NodeProperties.java rename to melodic-commons/src/main/java/eu/passage/upperware/commons/model/byon/NodeProperties.java index a8d837200740dacc56cdf966670fd5e0af466fe2..6cb0dd5b0c4df3627434cea1ce5c13f13608c678 100644 --- a/gui-backend/src/main/java/eu/melodic/upperware/guibackend/model/byon/NodeProperties.java +++ b/melodic-commons/src/main/java/eu/passage/upperware/commons/model/byon/NodeProperties.java @@ -1,4 +1,4 @@ -package eu.melodic.upperware.guibackend.model.byon; +package eu.passage.upperware.commons.model.byon; import lombok.AllArgsConstructor; import lombok.Getter; diff --git a/gui-backend/src/main/java/eu/melodic/upperware/guibackend/model/byon/OperatingSystem.java b/melodic-commons/src/main/java/eu/passage/upperware/commons/model/byon/OperatingSystem.java similarity index 84% rename from gui-backend/src/main/java/eu/melodic/upperware/guibackend/model/byon/OperatingSystem.java rename to melodic-commons/src/main/java/eu/passage/upperware/commons/model/byon/OperatingSystem.java index c66d75d821c2e700abd98d96798b0db7cb139708..f14c32d56b10d4aef13bf7912b2ec777b78f519e 100644 --- a/gui-backend/src/main/java/eu/melodic/upperware/guibackend/model/byon/OperatingSystem.java +++ b/melodic-commons/src/main/java/eu/passage/upperware/commons/model/byon/OperatingSystem.java @@ -1,4 +1,4 @@ -package eu.melodic.upperware.guibackend.model.byon; +package eu.passage.upperware.commons.model.byon; import lombok.AllArgsConstructor; import lombok.Getter; diff --git a/gui-backend/src/main/java/eu/melodic/upperware/guibackend/model/provider/Api.java b/melodic-commons/src/main/java/eu/passage/upperware/commons/model/provider/Api.java similarity index 81% rename from gui-backend/src/main/java/eu/melodic/upperware/guibackend/model/provider/Api.java rename to melodic-commons/src/main/java/eu/passage/upperware/commons/model/provider/Api.java index 40699ee02d482f264a08c994594704c23643f0af..2e7e2d558f71f4dbe20641ee177f01a2f57517f5 100644 --- a/gui-backend/src/main/java/eu/melodic/upperware/guibackend/model/provider/Api.java +++ b/melodic-commons/src/main/java/eu/passage/upperware/commons/model/provider/Api.java @@ -1,4 +1,4 @@ -package eu.melodic.upperware.guibackend.model.provider; +package eu.passage.upperware.commons.model.provider; import lombok.AllArgsConstructor; import lombok.Getter; diff --git a/gui-backend/src/main/java/eu/melodic/upperware/guibackend/model/provider/CloudConfiguration.java b/melodic-commons/src/main/java/eu/passage/upperware/commons/model/provider/CloudConfiguration.java similarity index 81% rename from gui-backend/src/main/java/eu/melodic/upperware/guibackend/model/provider/CloudConfiguration.java rename to melodic-commons/src/main/java/eu/passage/upperware/commons/model/provider/CloudConfiguration.java index e8086c4c2a4945317c50d9e7db4977504550af6d..5af19758c436dcb544d611a0c96f19ef5ae3ea83 100644 --- a/gui-backend/src/main/java/eu/melodic/upperware/guibackend/model/provider/CloudConfiguration.java +++ b/melodic-commons/src/main/java/eu/passage/upperware/commons/model/provider/CloudConfiguration.java @@ -1,4 +1,4 @@ -package eu.melodic.upperware.guibackend.model.provider; +package eu.passage.upperware.commons.model.provider; import lombok.*; diff --git a/gui-backend/src/main/java/eu/melodic/upperware/guibackend/model/provider/CloudDefinition.java b/melodic-commons/src/main/java/eu/passage/upperware/commons/model/provider/CloudDefinition.java similarity index 84% rename from gui-backend/src/main/java/eu/melodic/upperware/guibackend/model/provider/CloudDefinition.java rename to melodic-commons/src/main/java/eu/passage/upperware/commons/model/provider/CloudDefinition.java index f85d88966bb3f346b94de3b35ca188035293270d..3df0545bffd89976f07ea403bf405e3924c2a963 100644 --- a/gui-backend/src/main/java/eu/melodic/upperware/guibackend/model/provider/CloudDefinition.java +++ b/melodic-commons/src/main/java/eu/passage/upperware/commons/model/provider/CloudDefinition.java @@ -1,4 +1,4 @@ -package eu.melodic.upperware.guibackend.model.provider; +package eu.passage.upperware.commons.model.provider; import lombok.*; diff --git a/melodic-commons/src/main/java/eu/passage/upperware/commons/model/provider/CloudType.java b/melodic-commons/src/main/java/eu/passage/upperware/commons/model/provider/CloudType.java new file mode 100644 index 0000000000000000000000000000000000000000..228692b046072379c8ecbf1b435f7b937f886265 --- /dev/null +++ b/melodic-commons/src/main/java/eu/passage/upperware/commons/model/provider/CloudType.java @@ -0,0 +1,5 @@ +package eu.passage.upperware.commons.model.provider; + +public enum CloudType { + PRIVATE, PUBLIC +} diff --git a/gui-backend/src/main/java/eu/melodic/upperware/guibackend/model/provider/Credential.java b/melodic-commons/src/main/java/eu/passage/upperware/commons/model/provider/Credential.java similarity index 82% rename from gui-backend/src/main/java/eu/melodic/upperware/guibackend/model/provider/Credential.java rename to melodic-commons/src/main/java/eu/passage/upperware/commons/model/provider/Credential.java index 2fc71011dcf1a8e2fdbfb8db439f63efcaa7d8b3..53fe5d876cc6ccc527b35ab32769e059cc994c50 100644 --- a/gui-backend/src/main/java/eu/melodic/upperware/guibackend/model/provider/Credential.java +++ b/melodic-commons/src/main/java/eu/passage/upperware/commons/model/provider/Credential.java @@ -1,4 +1,4 @@ -package eu.melodic.upperware.guibackend.model.provider; +package eu.passage.upperware.commons.model.provider; import lombok.AllArgsConstructor; import lombok.Getter; diff --git a/gui-backend/src/main/java/eu/melodic/upperware/guibackend/model/provider/ParentProperty.java b/melodic-commons/src/main/java/eu/passage/upperware/commons/model/provider/ParentProperty.java similarity index 80% rename from gui-backend/src/main/java/eu/melodic/upperware/guibackend/model/provider/ParentProperty.java rename to melodic-commons/src/main/java/eu/passage/upperware/commons/model/provider/ParentProperty.java index 10b63a53a5487f1aed01759216ddb248e01e9257..6004624659218da299a73b116240ab3178d66778 100644 --- a/gui-backend/src/main/java/eu/melodic/upperware/guibackend/model/provider/ParentProperty.java +++ b/melodic-commons/src/main/java/eu/passage/upperware/commons/model/provider/ParentProperty.java @@ -1,4 +1,4 @@ -package eu.melodic.upperware.guibackend.model.provider; +package eu.passage.upperware.commons.model.provider; import lombok.*; diff --git a/gui-backend/src/main/java/eu/melodic/upperware/guibackend/model/provider/Provider.java b/melodic-commons/src/main/java/eu/passage/upperware/commons/model/provider/Provider.java similarity index 82% rename from gui-backend/src/main/java/eu/melodic/upperware/guibackend/model/provider/Provider.java rename to melodic-commons/src/main/java/eu/passage/upperware/commons/model/provider/Provider.java index ef6d996ae90f9aec5eb12f781df800c0f4178146..0577de4c1062c21bc0b0ce001e5597d96d2064ce 100644 --- a/gui-backend/src/main/java/eu/melodic/upperware/guibackend/model/provider/Provider.java +++ b/melodic-commons/src/main/java/eu/passage/upperware/commons/model/provider/Provider.java @@ -1,4 +1,4 @@ -package eu.melodic.upperware.guibackend.model.provider; +package eu.passage.upperware.commons.model.provider; public enum Provider { AWS_EC2("aws-ec2"), diff --git a/gui-backend/src/main/java/eu/melodic/upperware/guibackend/model/provider/ProviderEnums.java b/melodic-commons/src/main/java/eu/passage/upperware/commons/model/provider/ProviderEnums.java similarity index 79% rename from gui-backend/src/main/java/eu/melodic/upperware/guibackend/model/provider/ProviderEnums.java rename to melodic-commons/src/main/java/eu/passage/upperware/commons/model/provider/ProviderEnums.java index eeb473155f4801be3245e1413a4b0e967b312fff..91f3f880445e3ad7cab501b4af37ec2cf2e0f449 100644 --- a/gui-backend/src/main/java/eu/melodic/upperware/guibackend/model/provider/ProviderEnums.java +++ b/melodic-commons/src/main/java/eu/passage/upperware/commons/model/provider/ProviderEnums.java @@ -1,4 +1,4 @@ -package eu.melodic.upperware.guibackend.model.provider; +package eu.passage.upperware.commons.model.provider; import lombok.*; diff --git a/gui-backend/src/main/java/eu/melodic/upperware/guibackend/model/provider/SingleProperty.java b/melodic-commons/src/main/java/eu/passage/upperware/commons/model/provider/SingleProperty.java similarity index 83% rename from gui-backend/src/main/java/eu/melodic/upperware/guibackend/model/provider/SingleProperty.java rename to melodic-commons/src/main/java/eu/passage/upperware/commons/model/provider/SingleProperty.java index fd8626bd47901c92870ff45631e6c881379d53f2..ca0f038a3a8b7a47934d1a47175b3edfe32ac78b 100644 --- a/gui-backend/src/main/java/eu/melodic/upperware/guibackend/model/provider/SingleProperty.java +++ b/melodic-commons/src/main/java/eu/passage/upperware/commons/model/provider/SingleProperty.java @@ -1,4 +1,4 @@ -package eu.melodic.upperware.guibackend.model.provider; +package eu.passage.upperware.commons.model.provider; import lombok.AllArgsConstructor; import lombok.Getter; diff --git a/melodic-commons/src/main/java/eu/passage/upperware/commons/model/testing/FunctionTestConfiguration.java b/melodic-commons/src/main/java/eu/passage/upperware/commons/model/testing/FunctionTestConfiguration.java new file mode 100644 index 0000000000000000000000000000000000000000..7ffa58719a9bb59e7de3a44be859dbb1c0252a49 --- /dev/null +++ b/melodic-commons/src/main/java/eu/passage/upperware/commons/model/testing/FunctionTestConfiguration.java @@ -0,0 +1,12 @@ +package eu.passage.upperware.commons.model.testing; + +import lombok.Data; + +import java.util.List; + +@Data +public class FunctionTestConfiguration { + private String functionName; + private String triggerPath; + private List testCases; +} diff --git a/melodic-commons/src/main/java/eu/passage/upperware/commons/model/testing/TestCase.java b/melodic-commons/src/main/java/eu/passage/upperware/commons/model/testing/TestCase.java new file mode 100644 index 0000000000000000000000000000000000000000..29b15255571692595ea68478a448935ba04afad3 --- /dev/null +++ b/melodic-commons/src/main/java/eu/passage/upperware/commons/model/testing/TestCase.java @@ -0,0 +1,11 @@ +package eu.passage.upperware.commons.model.testing; + +import lombok.Data; + +@Data +public class TestCase { + private String functionName; + private String event; + private String expectedOutput; + private String region; +} diff --git a/melodic-commons/src/main/java/eu/passage/upperware/commons/model/testing/TestConfiguration.java b/melodic-commons/src/main/java/eu/passage/upperware/commons/model/testing/TestConfiguration.java new file mode 100644 index 0000000000000000000000000000000000000000..4779c12a29c592eea6b3573492a345301bdce6c3 --- /dev/null +++ b/melodic-commons/src/main/java/eu/passage/upperware/commons/model/testing/TestConfiguration.java @@ -0,0 +1,10 @@ +package eu.passage.upperware.commons.model.testing; + +import lombok.Data; + +import java.util.List; + +@Data +public class TestConfiguration { + private List tests; +} diff --git a/gui-backend/src/main/java/eu/melodic/upperware/guibackend/service/provider/ProviderIdCreatorService.java b/melodic-commons/src/main/java/eu/passage/upperware/commons/service/provider/ProviderIdCreatorService.java similarity index 95% rename from gui-backend/src/main/java/eu/melodic/upperware/guibackend/service/provider/ProviderIdCreatorService.java rename to melodic-commons/src/main/java/eu/passage/upperware/commons/service/provider/ProviderIdCreatorService.java index 87d594171dba967c423b4626c59909525b5e43ac..a601ecefdb9f6d31f00190c22beea504ed1614b0 100644 --- a/gui-backend/src/main/java/eu/melodic/upperware/guibackend/service/provider/ProviderIdCreatorService.java +++ b/melodic-commons/src/main/java/eu/passage/upperware/commons/service/provider/ProviderIdCreatorService.java @@ -1,8 +1,8 @@ -package eu.melodic.upperware.guibackend.service.provider; +package eu.passage.upperware.commons.service.provider; -import eu.melodic.upperware.guibackend.model.provider.CloudDefinition; -import eu.melodic.upperware.guibackend.model.provider.ParentProperty; -import eu.melodic.upperware.guibackend.model.provider.SingleProperty; +import eu.passage.upperware.commons.model.provider.CloudDefinition; +import eu.passage.upperware.commons.model.provider.ParentProperty; +import eu.passage.upperware.commons.model.provider.SingleProperty; import lombok.AllArgsConstructor; import org.springframework.beans.factory.annotation.Autowired; import org.springframework.stereotype.Service; diff --git a/gui-backend/src/main/java/eu/melodic/upperware/guibackend/service/provider/ProviderService.java b/melodic-commons/src/main/java/eu/passage/upperware/commons/service/provider/ProviderService.java similarity index 93% rename from gui-backend/src/main/java/eu/melodic/upperware/guibackend/service/provider/ProviderService.java rename to melodic-commons/src/main/java/eu/passage/upperware/commons/service/provider/ProviderService.java index 6c7eadbfe573529e4d4f50537d1a3d9401ab6bf6..aff89ae43294c7ec8c1a632fdb1efe82a2d8a9ca 100644 --- a/gui-backend/src/main/java/eu/melodic/upperware/guibackend/service/provider/ProviderService.java +++ b/melodic-commons/src/main/java/eu/passage/upperware/commons/service/provider/ProviderService.java @@ -1,12 +1,12 @@ -package eu.melodic.upperware.guibackend.service.provider; - -import eu.melodic.upperware.guibackend.communication.cloudiator.CloudiatorApi; -import eu.melodic.upperware.guibackend.exception.CloudDefinitionNotFoundException; -import eu.melodic.upperware.guibackend.model.provider.CloudDefinition; -import eu.melodic.upperware.guibackend.model.provider.Provider; -import eu.melodic.upperware.guibackend.model.provider.ProviderEnums; -import eu.melodic.upperware.guibackend.service.secure.store.SecureStoreService; -import eu.melodic.upperware.guibackend.service.yaml.YamlDataService; +package eu.passage.upperware.commons.service.provider; + +import eu.passage.upperware.commons.cloudiator.CloudiatorApi; +import eu.passage.upperware.commons.exception.CloudDefinitionNotFoundException; +import eu.passage.upperware.commons.model.provider.CloudDefinition; +import eu.passage.upperware.commons.model.provider.Provider; +import eu.passage.upperware.commons.model.provider.ProviderEnums; +import eu.passage.upperware.commons.service.store.SecureStoreService; +import eu.passage.upperware.commons.service.yaml.YamlDataService; import io.github.cloudiator.rest.model.CloudType; import lombok.AllArgsConstructor; import lombok.extern.slf4j.Slf4j; diff --git a/gui-backend/src/main/java/eu/melodic/upperware/guibackend/service/provider/ProviderValidationService.java b/melodic-commons/src/main/java/eu/passage/upperware/commons/service/provider/ProviderValidationService.java similarity index 95% rename from gui-backend/src/main/java/eu/melodic/upperware/guibackend/service/provider/ProviderValidationService.java rename to melodic-commons/src/main/java/eu/passage/upperware/commons/service/provider/ProviderValidationService.java index 5c2f9204a9eae6e46f9cc3f9e21d876c884eb562..e188a6c4c1a3c80b80402398999d18459eadb588 100644 --- a/gui-backend/src/main/java/eu/melodic/upperware/guibackend/service/provider/ProviderValidationService.java +++ b/melodic-commons/src/main/java/eu/passage/upperware/commons/service/provider/ProviderValidationService.java @@ -1,7 +1,7 @@ -package eu.melodic.upperware.guibackend.service.provider; +package eu.passage.upperware.commons.service.provider; -import eu.melodic.upperware.guibackend.exception.ValidationException; -import eu.melodic.upperware.guibackend.model.provider.CloudDefinition; +import eu.passage.upperware.commons.exception.ValidationException; +import eu.passage.upperware.commons.model.provider.CloudDefinition; import org.springframework.stereotype.Service; import java.util.List; diff --git a/gui-backend/src/main/java/eu/melodic/upperware/guibackend/service/secure/store/SecureStoreService.java b/melodic-commons/src/main/java/eu/passage/upperware/commons/service/store/SecureStoreService.java similarity index 91% rename from gui-backend/src/main/java/eu/melodic/upperware/guibackend/service/secure/store/SecureStoreService.java rename to melodic-commons/src/main/java/eu/passage/upperware/commons/service/store/SecureStoreService.java index f37c34f6bba7460e23be6b7029c9b5a960b8b79f..6f3455cf7b1ceb76b96df1a12111510d70992db0 100644 --- a/gui-backend/src/main/java/eu/melodic/upperware/guibackend/service/secure/store/SecureStoreService.java +++ b/melodic-commons/src/main/java/eu/passage/upperware/commons/service/store/SecureStoreService.java @@ -1,11 +1,11 @@ -package eu.melodic.upperware.guibackend.service.secure.store; - -import eu.melodic.upperware.guibackend.communication.cloudiator.CloudiatorApi; -import eu.melodic.upperware.guibackend.controller.deployment.common.SecureVariable; -import eu.melodic.upperware.guibackend.exception.SecureVariableNotFoundException; -import eu.melodic.upperware.guibackend.exception.ValidationException; -import eu.melodic.upperware.guibackend.model.byon.LoginCredential; -import eu.melodic.upperware.guibackend.model.provider.CloudDefinition; +package eu.passage.upperware.commons.service.store; + +import eu.passage.upperware.commons.cloudiator.CloudiatorApi; +import eu.passage.upperware.commons.model.SecureVariable; +import eu.passage.upperware.commons.exception.SecureVariableNotFoundException; +import eu.passage.upperware.commons.exception.ValidationException; +import eu.passage.upperware.commons.model.byon.LoginCredential; +import eu.passage.upperware.commons.model.provider.CloudDefinition; import lombok.AllArgsConstructor; import lombok.extern.slf4j.Slf4j; import org.apache.commons.lang3.StringUtils; diff --git a/melodic-commons/src/main/java/eu/passage/upperware/commons/service/testing/TestConfigurationValidationService.java b/melodic-commons/src/main/java/eu/passage/upperware/commons/service/testing/TestConfigurationValidationService.java new file mode 100644 index 0000000000000000000000000000000000000000..e0b8bc0377c524b80ea4f37152fa11abfe46c786 --- /dev/null +++ b/melodic-commons/src/main/java/eu/passage/upperware/commons/service/testing/TestConfigurationValidationService.java @@ -0,0 +1,113 @@ +package eu.passage.upperware.commons.service.testing; + +import eu.passage.upperware.commons.model.testing.FunctionTestConfiguration; +import eu.passage.upperware.commons.model.testing.TestCase; +import eu.passage.upperware.commons.model.testing.TestConfiguration; +import lombok.extern.slf4j.Slf4j; +import org.apache.commons.lang3.tuple.Pair; + +import java.util.HashSet; +import java.util.List; +import java.util.Set; +import java.util.stream.Collectors; + +@Slf4j +public class TestConfigurationValidationService { + + public static class NotUniqueFunctionNameException extends Exception { + String functionName; + String message; + + NotUniqueFunctionNameException(String functionName) { + this.functionName = functionName; + this.message = String.format( + "Function name '%s' occurred in more than one test. Please adjust the config file", + functionName + ); + } + + @Override + public String getMessage() { + return this.message; + } + } + + public static class NotUniqueTestCaseException extends Exception { + String functionName; + String event; + String expectedOutput; + String message; + + NotUniqueTestCaseException(String functionName, String event, String expectedOutput) { + this.functionName = functionName; + this.event = event; + this.expectedOutput = expectedOutput; + this.message = String.format( + "The pair of event = '%s' and expected output = '%s'" + + " appears in more than one test case of function '%s'." + + " Please adjust the test cases to be unique", + this.event, + this.expectedOutput, + this.functionName + ); + } + @Override + public String getMessage() { + return this.message; + } + } + + public static void validate(TestConfiguration configuration) throws Exception { + log.info("Checking uniqueness of function names"); + checkFunctionNamesUniqueness(configuration.getTests()); + + log.info("Checking uniqueness of test cases"); + for (FunctionTestConfiguration functionTestConfiguration : configuration.getTests()){ + checkTestCasesUniqueness( + functionTestConfiguration.getTestCases(), + functionTestConfiguration.getFunctionName() + ); + } + } + + public static void checkFunctionNamesUniqueness( + List functionTestConfigurations + ) throws NotUniqueFunctionNameException { + List functionNames = functionTestConfigurations + .stream() + .map(FunctionTestConfiguration::getFunctionName) + .collect(Collectors.toList()); + Set uniqueNames = new HashSet<>(); + for (String name: functionNames) { + if (!uniqueNames.add(name)) { + log.error("Function name '{}' occurred in more than one test", name); + throw new NotUniqueFunctionNameException(name); + } + } + } + + public static void checkTestCasesUniqueness( + List testCases, String functionName + ) throws NotUniqueTestCaseException { + List> eventsExpectedOutputs = testCases + .stream() + .map(testCase -> Pair.of(testCase.getEvent(), testCase.getExpectedOutput())) + .collect(Collectors.toList()); + Set> uniquePairs = new HashSet<>(); + for (Pair eventExpectedOutput : eventsExpectedOutputs) { + if (!uniquePairs.add(eventExpectedOutput)) { + log.error( + "Function '{}' has more than one test case with event '{}' and expected output '{}'", + functionName, + eventExpectedOutput.getKey(), + eventExpectedOutput.getValue() + ); + throw new NotUniqueTestCaseException( + functionName, + eventExpectedOutput.getKey(), + eventExpectedOutput.getValue() + ); + } + } + } +} diff --git a/gui-backend/src/main/java/eu/melodic/upperware/guibackend/service/yaml/YamlDataService.java b/melodic-commons/src/main/java/eu/passage/upperware/commons/service/yaml/YamlDataService.java similarity index 90% rename from gui-backend/src/main/java/eu/melodic/upperware/guibackend/service/yaml/YamlDataService.java rename to melodic-commons/src/main/java/eu/passage/upperware/commons/service/yaml/YamlDataService.java index 643d15b617c711973e8e6ae5dd4e494a90afee72..3db6ea7831c2ac18f04d1208944b50b6129c9c66 100644 --- a/gui-backend/src/main/java/eu/melodic/upperware/guibackend/service/yaml/YamlDataService.java +++ b/melodic-commons/src/main/java/eu/passage/upperware/commons/service/yaml/YamlDataService.java @@ -1,8 +1,8 @@ -package eu.melodic.upperware.guibackend.service.yaml; +package eu.passage.upperware.commons.service.yaml; -import eu.melodic.upperware.guibackend.model.byon.ByonDefinition; -import eu.melodic.upperware.guibackend.model.provider.CloudDefinition; -import eu.melodic.upperware.guibackend.properties.GuiYamlData; +import eu.passage.upperware.commons.model.byon.ByonDefinition; +import eu.passage.upperware.commons.model.provider.CloudDefinition; +import eu.passage.upperware.commons.model.GuiYamlData; import lombok.extern.slf4j.Slf4j; import org.codehaus.jackson.map.ObjectMapper; import org.codehaus.jackson.type.TypeReference; diff --git a/gui-backend/src/main/resources/gui-data.yaml b/melodic-commons/src/main/resources/gui-data.yaml similarity index 95% rename from gui-backend/src/main/resources/gui-data.yaml rename to melodic-commons/src/main/resources/gui-data.yaml index a7f6a8007fded3940ac329b29521675516f69f38..17abda4c1fa034693ff79a37c2dc84dc062b6dd6 100644 --- a/gui-backend/src/main/resources/gui-data.yaml +++ b/melodic-commons/src/main/resources/gui-data.yaml @@ -1,4 +1,4 @@ -!!eu.melodic.upperware.guibackend.properties.GuiYamlData +!!eu.passage.upperware.commons.model.GuiYamlData byonDefinitions: cloudDefinitions: - api: {id: 1, providerName: openstack4j} diff --git a/pom.xml b/pom.xml index 3f8008846616eaf8eaf962a0344ee3568411f29b..9deff3ca3e4c8b5066e760b297cca254cca8b20b 100644 --- a/pom.xml +++ b/pom.xml @@ -42,6 +42,7 @@ mq-http-adapter penalty-calculator zpp-solver + functionizer-testing-tool