diff options
author | Dan Timoney <dtimoney@att.com> | 2019-08-09 21:49:29 +0000 |
---|---|---|
committer | Gerrit Code Review <gerrit@onap.org> | 2019-08-09 21:49:29 +0000 |
commit | 4001ac13397c082ee97c7ff440fa2ead5d50b421 (patch) | |
tree | 4ef46a3908b84acb9b46919856efef6657f785dd /ms | |
parent | 3c1781135b4029fdc657b2009259d3b8ddd8eebc (diff) | |
parent | 1e7e4a53684df04ba248c20d884ba907ca7c2870 (diff) |
Merge "Add declarative acceptance tests"
Diffstat (limited to 'ms')
16 files changed, 598 insertions, 76 deletions
diff --git a/ms/blueprintsprocessor/application/pom.xml b/ms/blueprintsprocessor/application/pom.xml index 4e487304c..a504ce3cc 100755 --- a/ms/blueprintsprocessor/application/pom.xml +++ b/ms/blueprintsprocessor/application/pom.xml @@ -113,6 +113,22 @@ <artifactId>reactor-test</artifactId> <scope>test</scope> </dependency> + <dependency> + <groupId>org.yaml</groupId> + <artifactId>snakeyaml</artifactId> + <scope>test</scope> + </dependency> + <dependency> + <groupId>com.nhaarman.mockitokotlin2</groupId> + <artifactId>mockito-kotlin</artifactId> + <version>2.1.0</version> + <scope>test</scope> + </dependency> + <dependency> + <groupId>ch.qos.logback</groupId> + <artifactId>logback-classic</artifactId> + <scope>test</scope> + </dependency> </dependencies> <build> <resources> diff --git a/ms/blueprintsprocessor/application/src/test/kotlin/org/onap/ccsdk/cds/blueprintsprocessor/BlueprintsAcceptanceTests.kt b/ms/blueprintsprocessor/application/src/test/kotlin/org/onap/ccsdk/cds/blueprintsprocessor/BlueprintsAcceptanceTests.kt new file mode 100644 index 000000000..0a57277ea --- /dev/null +++ b/ms/blueprintsprocessor/application/src/test/kotlin/org/onap/ccsdk/cds/blueprintsprocessor/BlueprintsAcceptanceTests.kt @@ -0,0 +1,280 @@ +/*- + * ============LICENSE_START======================================================= + * Copyright (C) 2019 Nordix Foundation. + * ================================================================================ + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + * + * SPDX-License-Identifier: Apache-2.0 + * ============LICENSE_END========================================================= + */ +package org.onap.ccsdk.cds.blueprintsprocessor + +import com.fasterxml.jackson.databind.ObjectMapper +import com.nhaarman.mockitokotlin2.any +import com.nhaarman.mockitokotlin2.argThat +import com.nhaarman.mockitokotlin2.atLeast +import com.nhaarman.mockitokotlin2.atLeastOnce +import com.nhaarman.mockitokotlin2.eq +import com.nhaarman.mockitokotlin2.mock +import com.nhaarman.mockitokotlin2.verify +import com.nhaarman.mockitokotlin2.verifyNoMoreInteractions +import com.nhaarman.mockitokotlin2.whenever +import org.junit.ClassRule +import org.junit.Rule +import org.junit.runner.RunWith +import org.junit.runners.Parameterized +import org.onap.ccsdk.cds.blueprintsprocessor.rest.RestLibConstants +import org.onap.ccsdk.cds.blueprintsprocessor.rest.service.BluePrintRestLibPropertyService +import org.onap.ccsdk.cds.blueprintsprocessor.rest.service.BlueprintWebClientService +import org.onap.ccsdk.cds.blueprintsprocessor.rest.service.BlueprintWebClientService.WebClientResponse +import org.onap.ccsdk.cds.controllerblueprints.core.utils.BluePrintArchiveUtils.Companion.compressToBytes +import org.skyscreamer.jsonassert.JSONAssert +import org.skyscreamer.jsonassert.JSONCompareMode +import org.slf4j.Logger +import org.slf4j.LoggerFactory +import org.springframework.beans.factory.annotation.Autowired +import org.springframework.boot.test.autoconfigure.web.reactive.AutoConfigureWebTestClient +import org.springframework.boot.test.context.SpringBootTest +import org.springframework.boot.test.mock.mockito.MockBean +import org.springframework.core.io.ByteArrayResource +import org.springframework.core.io.Resource +import org.springframework.http.MediaType +import org.springframework.test.context.ContextConfiguration +import org.springframework.test.context.TestPropertySource +import org.springframework.test.context.junit4.rules.SpringClassRule +import org.springframework.test.context.junit4.rules.SpringMethodRule +import org.springframework.test.web.reactive.server.WebTestClient +import org.yaml.snakeyaml.Yaml +import reactor.core.publisher.Mono +import java.io.File +import java.nio.file.Path +import java.nio.file.Paths +import kotlin.test.BeforeTest +import kotlin.test.Test + +@RunWith(Parameterized::class) +// Set blueprintsprocessor.httpPort=0 to trigger a random port selection +@SpringBootTest(webEnvironment = SpringBootTest.WebEnvironment.DEFINED_PORT) +@AutoConfigureWebTestClient(timeout = "PT10S") +@ContextConfiguration(initializers = [ + WorkingFoldersInitializer::class, + TestSecuritySettings.ServerContextInitializer::class +]) +@TestPropertySource(locations = ["classpath:application-test.properties"]) +@Suppress("UNCHECKED_CAST") +class BlueprintsAcceptanceTests(private val blueprintName: String, private val filename: String) { + + companion object { + const val UAT_BLUEPRINTS_BASE_DIR = "../../../components/model-catalog/blueprint-model/uat-blueprints" + const val EMBEDDED_UAT_FILE = "Tests/uat.yaml" + + @ClassRule + @JvmField + val springClassRule = SpringClassRule() + + val log: Logger = LoggerFactory.getLogger(BlueprintsAcceptanceTests::class.java) + + @Parameterized.Parameters(name = "{index} {0}") + @JvmStatic + fun filenames(): List<Array<String>> { + return File(UAT_BLUEPRINTS_BASE_DIR) + .listFiles { file -> file.isDirectory && File(file, EMBEDDED_UAT_FILE).isFile } + ?.map { file -> arrayOf(file.nameWithoutExtension, file.canonicalPath) } + ?: emptyList() + } + } + + @Rule + @JvmField + val springMethodRule = SpringMethodRule() + + @MockBean(name = RestLibConstants.SERVICE_BLUEPRINT_REST_LIB_PROPERTY) + lateinit var restClientFactory: BluePrintRestLibPropertyService + + @Autowired + // Bean is created programmatically by {@link WorkingFoldersInitializer#initialize(String)} + @Suppress("SpringJavaInjectionPointsAutowiringInspection") + lateinit var tempFolder: ExtendedTemporaryFolder + + @Autowired + lateinit var webTestClient: WebTestClient + + @Autowired + lateinit var mapper: ObjectMapper + + @BeforeTest + fun cleanupTemporaryFolder() { + tempFolder.deleteAllFiles() + } + + @Test + fun testBlueprint() { + val yaml: Map<String, *> = loadYaml(Paths.get(filename, EMBEDDED_UAT_FILE)) + + uploadBlueprint(blueprintName) + + // Configure mocked external services + val services = yaml["external-services"] as List<Map<String, *>>? ?: emptyList() + val expectationPerClient = services.map { service -> + val selector = service["selector"] as String + val expectations = (service["expectations"] as List<Map<String, *>>).map { + parseExpectation(it) + } + val mockClient = createRestClientMock(selector, expectations) + mockClient to expectations + }.toMap() + + // Run processes + for (process in (yaml["processes"] as List<Map<String, *>>)) { + val processName = process["name"] + log.info("Executing process '$processName'") + val request = mapper.writeValueAsString(process["request"]) + val expectedResponse = mapper.writeValueAsString(process["expectedResponse"]) + processBlueprint(request, expectedResponse) + } + + // Validate request payloads + for ((mockClient, expectations) in expectationPerClient) { + expectations.forEach { expectation -> + verify(mockClient, atLeastOnce()).exchangeResource( + eq(expectation.method), + eq(expectation.path), + argThat { assertJsonEqual(expectation.expectedRequestBody, this) }, + expectation.requestHeadersMatcher()) + } + // Don't mind the invocations to the overloaded exchangeResource(String, String, String) + verify(mockClient, atLeast(0)).exchangeResource(any(), any(), any()) + verifyNoMoreInteractions(mockClient) + } + } + + private fun createRestClientMock(selector: String, restExpectations: List<RestExpectation>): BlueprintWebClientService { + val restClient = mock<BlueprintWebClientService>(verboseLogging = true) + + // Delegates to overloaded exchangeResource(String, String, String, Map<String, String>) + whenever(restClient.exchangeResource(any(), any(), any())) + .thenAnswer { invocation -> + val method = invocation.arguments[0] as String + val path = invocation.arguments[1] as String + val request = invocation.arguments[2] as String + restClient.exchangeResource(method, path, request, emptyMap()) + } + for (expectation in restExpectations) { + whenever(restClient.exchangeResource( + eq(expectation.method), + eq(expectation.path), + any(), + any())) + .thenReturn(WebClientResponse(expectation.statusCode, expectation.responseBody)) + } + + whenever(restClientFactory.blueprintWebClientService(selector)) + .thenReturn(restClient) + return restClient + } + + private fun uploadBlueprint(blueprintName: String) { + val body = toMultiValueMap("file", getBlueprintAsResource(blueprintName)) + webTestClient + .post() + .uri("/api/v1/execution-service/upload") + .header("Authorization", TestSecuritySettings.clientAuthToken()) + .syncBody(body) + .exchange() + .expectStatus().isOk + } + + private fun processBlueprint(request: String, expectedResponse: String) { + webTestClient + .post() + .uri("/api/v1/execution-service/process") + .header("Authorization", TestSecuritySettings.clientAuthToken()) + .contentType(MediaType.APPLICATION_JSON_UTF8) + .body(Mono.just(request), String::class.java) + .exchange() + .expectStatus().isOk + .expectBody() + .json(expectedResponse) + } + + private fun getBlueprintAsResource(blueprintName: String): Resource { + val baseDir = Paths.get(UAT_BLUEPRINTS_BASE_DIR, blueprintName) + val zipBytes = compressToBytes(baseDir) + return object : ByteArrayResource(zipBytes) { + // Filename has to be returned in order to be able to post + override fun getFilename() = "$blueprintName.zip" + } + } + + private fun loadYaml(path: Path): Map<String, Any> { + return path.toFile().reader().use { reader -> + Yaml().load(reader) + } + } + + private fun assertJsonEqual(expected: Any, actual: String): Boolean { + if (actual != expected) { + // assertEquals throws an exception whenever match fails + JSONAssert.assertEquals(mapper.writeValueAsString(expected), actual, JSONCompareMode.LENIENT) + } + return true + } + + private fun parseExpectation(expectation: Map<String, *>): RestExpectation { + val request = expectation["request"] as Map<String, Any> + val method = request["method"] as String + val path = joinPath(request.getValue("path")) + val contentType = request["content-type"] as String? + val requestBody = request.getOrDefault("body", "") + + val response = expectation["response"] as Map<String, Any>? ?: emptyMap() + val status = response["status"] as Int? ?: 200 + val responseBody = when (val body = response["body"] ?: "") { + is String -> body + else -> mapper.writeValueAsString(body) + } + + return RestExpectation(method, path, contentType, requestBody, status, responseBody) + } + + /** + * Join a multilevel lists of strings. + * Example: joinPath(listOf("a", listOf("b", "c"), "d")) will result in "a/b/c/d". + */ + private fun joinPath(any: Any): String { + fun recursiveJoin(any: Any, sb: StringBuilder): StringBuilder { + when (any) { + is List<*> -> any.filterNotNull().forEach { recursiveJoin(it, sb) } + is String -> { + if (sb.isNotEmpty()) { + sb.append('/') + } + sb.append(any) + } + else -> throw IllegalArgumentException("Unsupported type: ${any.javaClass}") + } + return sb + } + + return recursiveJoin(any, StringBuilder()).toString() + } + + data class RestExpectation(val method: String, val path: String, val contentType: String?, + val expectedRequestBody: Any, + val statusCode: Int, val responseBody: String) { + + fun requestHeadersMatcher(): Map<String, String> { + return if (contentType != null) eq(mapOf("Content-Type" to contentType)) else any() + } + } +}
\ No newline at end of file diff --git a/ms/blueprintsprocessor/application/src/test/kotlin/org/onap/ccsdk/cds/blueprintsprocessor/CollectionUtils2.kt b/ms/blueprintsprocessor/application/src/test/kotlin/org/onap/ccsdk/cds/blueprintsprocessor/CollectionUtils2.kt new file mode 100644 index 000000000..63d64cae4 --- /dev/null +++ b/ms/blueprintsprocessor/application/src/test/kotlin/org/onap/ccsdk/cds/blueprintsprocessor/CollectionUtils2.kt @@ -0,0 +1,31 @@ +/*- + * ============LICENSE_START======================================================= + * Copyright (C) 2019 Nordix Foundation. + * ================================================================================ + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + * + * SPDX-License-Identifier: Apache-2.0 + * ============LICENSE_END========================================================= + */ +package org.onap.ccsdk.cds.blueprintsprocessor + +import org.springframework.util.CollectionUtils +import org.springframework.util.MultiValueMap + + +/** + * Convenient method to create a single-entry MultiValueMap. + */ +fun <K, V> toMultiValueMap(key: K, vararg values: V): MultiValueMap<K, V> { + return CollectionUtils.toMultiValueMap(mapOf(key to values.asList())) +} diff --git a/ms/blueprintsprocessor/application/src/test/kotlin/org/onap/ccsdk/cds/blueprintsprocessor/ExtendedTemporaryFolder.kt b/ms/blueprintsprocessor/application/src/test/kotlin/org/onap/ccsdk/cds/blueprintsprocessor/ExtendedTemporaryFolder.kt new file mode 100644 index 000000000..4576f2761 --- /dev/null +++ b/ms/blueprintsprocessor/application/src/test/kotlin/org/onap/ccsdk/cds/blueprintsprocessor/ExtendedTemporaryFolder.kt @@ -0,0 +1,40 @@ +package org.onap.ccsdk.cds.blueprintsprocessor + +import org.junit.rules.TemporaryFolder +import java.io.File +import java.io.IOException +import java.nio.file.FileVisitResult +import java.nio.file.Files +import java.nio.file.Path +import java.nio.file.SimpleFileVisitor +import java.nio.file.attribute.BasicFileAttributes +import javax.annotation.PreDestroy + +class ExtendedTemporaryFolder { + private val tempFolder = TemporaryFolder() + + init { + tempFolder.create() + } + + @PreDestroy + fun delete() = tempFolder.delete() + + /** + * A delegate to org.junit.rules.TemporaryFolder.TemporaryFolder.newFolder(String). + */ + fun newFolder(folder: String): File = tempFolder.newFolder(folder) + + /** + * Delete all files under the root temporary folder recursively. The folders are preserved. + */ + fun deleteAllFiles() { + Files.walkFileTree(tempFolder.root.toPath(), object : SimpleFileVisitor<Path>() { + @Throws(IOException::class) + override fun visitFile(file: Path?, attrs: BasicFileAttributes?): FileVisitResult { + file?.toFile()?.delete() + return FileVisitResult.CONTINUE + } + }) + } +} diff --git a/ms/blueprintsprocessor/application/src/test/kotlin/org/onap/ccsdk/cds/blueprintsprocessor/TestSecuritySettings.kt b/ms/blueprintsprocessor/application/src/test/kotlin/org/onap/ccsdk/cds/blueprintsprocessor/TestSecuritySettings.kt new file mode 100644 index 000000000..f7ab2554c --- /dev/null +++ b/ms/blueprintsprocessor/application/src/test/kotlin/org/onap/ccsdk/cds/blueprintsprocessor/TestSecuritySettings.kt @@ -0,0 +1,45 @@ +/*- + * ============LICENSE_START======================================================= + * Copyright (C) 2019 Nordix Foundation. + * ================================================================================ + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + * + * SPDX-License-Identifier: Apache-2.0 + * ============LICENSE_END========================================================= + */ +package org.onap.ccsdk.cds.blueprintsprocessor + +import org.springframework.context.ApplicationContextInitializer +import org.springframework.context.ConfigurableApplicationContext +import org.springframework.test.context.support.TestPropertySourceUtils +import org.springframework.util.Base64Utils +import java.nio.charset.StandardCharsets + +class TestSecuritySettings { + companion object { + private const val authUsername = "walter.white" + private const val authPassword = "Heisenberg" + + fun clientAuthToken() = + "Basic " + Base64Utils.encodeToString("$authUsername:$authPassword".toByteArray(StandardCharsets.UTF_8)) + } + + class ServerContextInitializer : ApplicationContextInitializer<ConfigurableApplicationContext> { + override fun initialize(context: ConfigurableApplicationContext) { + TestPropertySourceUtils.addInlinedPropertiesToEnvironment(context, + "security.user.name=$authUsername", + "security.user.password={noop}$authPassword" + ) + } + } +} diff --git a/ms/blueprintsprocessor/application/src/test/kotlin/org/onap/ccsdk/cds/blueprintsprocessor/WorkingFoldersInitializer.kt b/ms/blueprintsprocessor/application/src/test/kotlin/org/onap/ccsdk/cds/blueprintsprocessor/WorkingFoldersInitializer.kt new file mode 100644 index 000000000..37615cb1a --- /dev/null +++ b/ms/blueprintsprocessor/application/src/test/kotlin/org/onap/ccsdk/cds/blueprintsprocessor/WorkingFoldersInitializer.kt @@ -0,0 +1,48 @@ +/*- + * ============LICENSE_START======================================================= + * Copyright (C) 2019 Nordix Foundation. + * ================================================================================ + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + * + * SPDX-License-Identifier: Apache-2.0 + * ============LICENSE_END========================================================= + */ +package org.onap.ccsdk.cds.blueprintsprocessor + +import org.springframework.beans.factory.support.BeanDefinitionBuilder +import org.springframework.beans.factory.support.BeanDefinitionRegistry +import org.springframework.context.ApplicationContextInitializer +import org.springframework.context.ConfigurableApplicationContext +import org.springframework.stereotype.Component +import org.springframework.test.context.support.TestPropertySourceUtils + +@Component +class WorkingFoldersInitializer : ApplicationContextInitializer<ConfigurableApplicationContext> { + + override fun initialize(context: ConfigurableApplicationContext) { + val tempFolder = ExtendedTemporaryFolder() + val properties = listOf("Deploy", "Archive", "Working") + .map { "blueprintsprocessor.blueprint${it}Path=${tempFolder.newFolder(it)}" } + .toTypedArray() + TestPropertySourceUtils.addInlinedPropertiesToEnvironment(context, *properties) + // Expose tempFolder as a bean so it can be accessed via DI + registerSingleton(context, "tempFolder", ExtendedTemporaryFolder::class.java, tempFolder) + } + + @Suppress("SameParameterValue") + private fun <T> registerSingleton(context: ConfigurableApplicationContext, + beanName: String, beanClass: Class<T>, instance: T) { + val builder = BeanDefinitionBuilder.genericBeanDefinition(beanClass) { instance } + (context.beanFactory as BeanDefinitionRegistry).registerBeanDefinition(beanName, builder.beanDefinition) + } +} diff --git a/ms/blueprintsprocessor/application/src/test/resources/application-test.properties b/ms/blueprintsprocessor/application/src/test/resources/application-test.properties new file mode 100644 index 000000000..b8b80f2dd --- /dev/null +++ b/ms/blueprintsprocessor/application/src/test/resources/application-test.properties @@ -0,0 +1,50 @@ +# +# Copyright © 2019 Nordix Foundation. +# +# Licensed under the Apache License, Version 2.0 (the "License"); +# you may not use this file except in compliance with the License. +# You may obtain a copy of the License at +# +# http://www.apache.org/licenses/LICENSE-2.0 +# +# Unless required by applicable law or agreed to in writing, software +# distributed under the License is distributed on an "AS IS" BASIS, +# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +# See the License for the specific language governing permissions and +# limitations under the License. +# + +spring.http.log-request-details=true + +blueprintsprocessor.httpPort=0 +blueprintsprocessor.grpcEnable=true +blueprintsprocessor.grpcPort=0 + +blueprintsprocessor.db.primary.url=jdbc:h2:mem:testdb;MODE=MySQL;DB_CLOSE_DELAY=-1 +blueprintsprocessor.db.primary.username=sa +blueprintsprocessor.db.primary.password= +blueprintsprocessor.db.primary.driverClassName=org.h2.Driver +blueprintsprocessor.db.primary.hibernateHbm2ddlAuto=create-drop +blueprintsprocessor.db.primary.hibernateDDLAuto=update +blueprintsprocessor.db.primary.hibernateNamingStrategy=org.hibernate.cfg.ImprovedNamingStrategy +blueprintsprocessor.db.primary.hibernateDialect=org.hibernate.dialect.H2Dialect + +# The properties bellow are set programmatically +#blueprintsprocessor.blueprintDeployPath= +#blueprintsprocessor.blueprintArchivePath= +#blueprintsprocessor.blueprintWorkingPath= +#security.user.name= +#security.user.password= + +# Python executor +blueprints.processor.functions.python.executor.executionPath=../../../components/scripts/python/ccsdk_blueprints +blueprints.processor.functions.python.executor.modulePaths=\ + ../../../components/scripts/python/ccsdk_blueprints,\ + ../../../components/scripts/python/ccsdk_netconf,\ + ../../../components/scripts/python/ccsdk_restconf + +# Executor Options +blueprintsprocessor.cliExecutor.enabled=true +blueprintprocessor.netconfExecutor.enabled=true + +blueprintsprocessor.restconfEnabled=true
\ No newline at end of file diff --git a/ms/blueprintsprocessor/application/src/test/resources/logback-test.xml b/ms/blueprintsprocessor/application/src/test/resources/logback-test.xml index 16e7d3d1b..eaa51c0a3 100644 --- a/ms/blueprintsprocessor/application/src/test/resources/logback-test.xml +++ b/ms/blueprintsprocessor/application/src/test/resources/logback-test.xml @@ -1,5 +1,6 @@ <!--
~ Copyright © 2017-2018 AT&T Intellectual Property.
+ ~ Modifications Copyright (C) 2019 Nordix Foundation.
~
~ Licensed under the Apache License, Version 2.0 (the "License");
~ you may not use this file except in compliance with the License.
@@ -16,19 +17,24 @@ <configuration>
<appender name="STDOUT" class="ch.qos.logback.core.ConsoleAppender">
- <!-- encoders are assigned the type
- ch.qos.logback.classic.encoder.PatternLayoutEncoder by default -->
<encoder>
- <pattern>%d{HH:mm:ss.SSS} %-5level [%thread] %logger{50} - %msg%n</pattern>
+ <pattern>%d{HH:mm:ss.SSS} %-5level %-40.40logger{39} : %msg%n</pattern>
</encoder>
</appender>
+ <logger name="org.springframework.web.HttpLogging" level="trace"/>
+ <logger name="org.springframework.web.reactive.function.client.ExchangeFunctions" level="trace"/>
- <logger name="org.springframework" level="warn"/>
- <logger name="org.hibernate" level="info"/>
- <logger name="org.onap.ccsdk.cds.blueprintsprocessor" level="info"/>
+ <!-- Helpful to optimize Spring Context caching to speed-up tests
+ and prevent resorting to @DirtiesContext as much as possible -->
+ <logger name="org.springframework.test.context.cache" level="debug"/>
- <root level="warn">
+ <!-- Please refer to https://thoughts-on-java.org/hibernate-logging-guide/
+ for a lengthy discussion on good Hibernate logging practices -->
+ <logger name="org.hibernate.SQL" level="debug"/>
+ <logger name="org.hibernate.type.descriptor.sql" level="trace"/>
+
+ <root level="info">
<appender-ref ref="STDOUT"/>
</root>
diff --git a/ms/blueprintsprocessor/modules/commons/rest-lib/src/main/kotlin/org/onap/ccsdk/cds/blueprintsprocessor/rest/service/BluePrintRestLibPropertyService.kt b/ms/blueprintsprocessor/modules/commons/rest-lib/src/main/kotlin/org/onap/ccsdk/cds/blueprintsprocessor/rest/service/BluePrintRestLibPropertyService.kt index da4d9933f..4f6865764 100644 --- a/ms/blueprintsprocessor/modules/commons/rest-lib/src/main/kotlin/org/onap/ccsdk/cds/blueprintsprocessor/rest/service/BluePrintRestLibPropertyService.kt +++ b/ms/blueprintsprocessor/modules/commons/rest-lib/src/main/kotlin/org/onap/ccsdk/cds/blueprintsprocessor/rest/service/BluePrintRestLibPropertyService.kt @@ -37,13 +37,13 @@ import org.springframework.stereotype.Service open class BluePrintRestLibPropertyService(private var bluePrintProperties: BluePrintProperties) { - fun blueprintWebClientService(jsonNode: JsonNode): + open fun blueprintWebClientService(jsonNode: JsonNode): BlueprintWebClientService { val restClientProperties = restClientProperties(jsonNode) return blueprintWebClientService(restClientProperties) } - fun blueprintWebClientService(selector: String): BlueprintWebClientService { + open fun blueprintWebClientService(selector: String): BlueprintWebClientService { val prefix = "blueprintsprocessor.restclient.$selector" val restClientProperties = restClientProperties(prefix) return blueprintWebClientService(restClientProperties) diff --git a/ms/blueprintsprocessor/parent/pom.xml b/ms/blueprintsprocessor/parent/pom.xml index 5bc4d3645..ec8bfd816 100755 --- a/ms/blueprintsprocessor/parent/pom.xml +++ b/ms/blueprintsprocessor/parent/pom.xml @@ -122,11 +122,6 @@ <version>2.6</version> </dependency> <dependency> - <groupId>org.apache.commons</groupId> - <artifactId>commons-compress</artifactId> - <version>1.15</version> - </dependency> - <dependency> <groupId>org.apache.velocity</groupId> <artifactId>velocity</artifactId> <version>${velocity.version}</version> @@ -531,10 +526,6 @@ <artifactId>commons-io</artifactId> </dependency> <dependency> - <groupId>org.apache.commons</groupId> - <artifactId>commons-compress</artifactId> - </dependency> - <dependency> <groupId>com.jayway.jsonpath</groupId> <artifactId>json-path</artifactId> <exclusions> diff --git a/ms/controllerblueprints/modules/blueprint-core/src/main/kotlin/org/onap/ccsdk/cds/controllerblueprints/core/FileExtensionFunctions.kt b/ms/controllerblueprints/modules/blueprint-core/src/main/kotlin/org/onap/ccsdk/cds/controllerblueprints/core/FileExtensionFunctions.kt index 18091e630..518e9b236 100644 --- a/ms/controllerblueprints/modules/blueprint-core/src/main/kotlin/org/onap/ccsdk/cds/controllerblueprints/core/FileExtensionFunctions.kt +++ b/ms/controllerblueprints/modules/blueprint-core/src/main/kotlin/org/onap/ccsdk/cds/controllerblueprints/core/FileExtensionFunctions.kt @@ -52,7 +52,7 @@ fun File.compress(targetZipFileName: String): File { * Compress the current Dir to the target zip file and return the target zip file */ fun File.compress(targetZipFile: File): File { - BluePrintArchiveUtils.compress(this, targetZipFile, true) + BluePrintArchiveUtils.compress(this, targetZipFile) return targetZipFile } diff --git a/ms/controllerblueprints/modules/blueprint-core/src/main/kotlin/org/onap/ccsdk/cds/controllerblueprints/core/utils/BluePrintArchiveUtils.kt b/ms/controllerblueprints/modules/blueprint-core/src/main/kotlin/org/onap/ccsdk/cds/controllerblueprints/core/utils/BluePrintArchiveUtils.kt index 8517be843..2f082db9c 100755 --- a/ms/controllerblueprints/modules/blueprint-core/src/main/kotlin/org/onap/ccsdk/cds/controllerblueprints/core/utils/BluePrintArchiveUtils.kt +++ b/ms/controllerblueprints/modules/blueprint-core/src/main/kotlin/org/onap/ccsdk/cds/controllerblueprints/core/utils/BluePrintArchiveUtils.kt @@ -1,6 +1,7 @@ /* * Copyright © 2017-2018 AT&T Intellectual Property. * Modifications Copyright © 2019 Bell Canada. + * Modifications Copyright © 2019 Nordix Foundation. * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. @@ -17,17 +18,26 @@ package org.onap.ccsdk.cds.controllerblueprints.core.utils -import org.apache.commons.compress.archivers.zip.ZipArchiveEntry -import org.apache.commons.compress.archivers.zip.ZipArchiveOutputStream -import org.apache.commons.io.IOUtils +import com.google.common.base.Predicates import org.onap.ccsdk.cds.controllerblueprints.core.BluePrintProcessorException import org.slf4j.LoggerFactory import java.io.BufferedInputStream +import java.io.ByteArrayOutputStream import java.io.File -import java.io.FileInputStream +import java.io.FileOutputStream import java.io.IOException +import java.io.OutputStream import java.nio.charset.Charset +import java.nio.file.FileVisitResult +import java.nio.file.Files +import java.nio.file.Path +import java.nio.file.SimpleFileVisitor +import java.nio.file.attribute.BasicFileAttributes +import java.util.function.Predicate +import java.util.zip.Deflater +import java.util.zip.ZipEntry import java.util.zip.ZipFile +import java.util.zip.ZipOutputStream class BluePrintArchiveUtils { @@ -39,17 +49,17 @@ class BluePrintArchiveUtils { * * @param source the base directory * @param destination the output filename - * @param absolute store absolute filepath (from directory) or only filename * @return True if OK */ - fun compress(source: File, destination: File, absolute: Boolean): Boolean { + fun compress(source: File, destination: File): Boolean { try { if(!destination.parentFile.exists()) { destination.parentFile.mkdirs() } destination.createNewFile() - ZipArchiveOutputStream(destination).use { - recurseFiles(source, source, it, absolute) + val ignoreZipFiles = Predicate<Path> { path -> !path.endsWith(".zip") && !path.endsWith(".ZIP") } + FileOutputStream(destination).use { out -> + compressFolder(source.toPath(), out, pathFilter = ignoreZipFiles) } } catch (e: Exception) { log.error("Fail to compress folder($source) to path(${destination.path})", e) @@ -59,40 +69,61 @@ class BluePrintArchiveUtils { } /** - * Recursive traversal to add files - * - * @param root - * @param file - * @param zaos - * @param absolute - * @throws IOException + * In-memory compress an entire folder. */ - @Throws(IOException::class) - private fun recurseFiles(root: File, file: File, zaos: ZipArchiveOutputStream, - absolute: Boolean) { - if (file.isDirectory) { - // recursive call - val files = file.listFiles() - for (fileChild in files!!) { - recurseFiles(root, fileChild, zaos, absolute) - } - } else if (!file.name.endsWith(".zip") && !file.name.endsWith(".ZIP")) { - val filename = if (absolute) { - file.absolutePath.substring(root.absolutePath.length) - } else { - file.name - } - val zae = ZipArchiveEntry(filename) - zae.size = file.length() - zaos.putArchiveEntry(zae) - FileInputStream(file).use { - IOUtils.copy(it, zaos) - it.close() - } - zaos.closeArchiveEntry() - } + fun compressToBytes(baseDir: Path, compressionLevel: Int = Deflater.NO_COMPRESSION): ByteArray { + return compressFolder(baseDir, ByteArrayOutputStream(), compressionLevel = compressionLevel) + .toByteArray() } + /** + * Compress an entire folder. + * + * @param baseDir path of base folder to be packaged. + * @param output the output stream + * @param pathFilter filter to ignore files based on its path. + * @param compressionLevel the wanted compression level. + * @param fixedModificationTime to force every entry to have this modification time. + * Useful for reproducible operations, like tests, for example. + */ + private fun <T> compressFolder(baseDir: Path, output: T, + pathFilter: Predicate<Path> = Predicates.alwaysTrue(), + compressionLevel: Int = Deflater.DEFAULT_COMPRESSION, + fixedModificationTime: Long? = null): T + where T : OutputStream { + ZipOutputStream(output) + .apply { setLevel(compressionLevel) } + .use { zos -> + Files.walkFileTree(baseDir, object : SimpleFileVisitor<Path>() { + @Throws(IOException::class) + override fun visitFile(file: Path, attrs: BasicFileAttributes): FileVisitResult { + if (pathFilter.test(file)) { + val zipEntry = ZipEntry(baseDir.relativize(file).toString()) + fixedModificationTime?.let { + zipEntry.time = it + } + zipEntry.time = 0; + zos.putNextEntry(zipEntry) + Files.copy(file, zos) + zos.closeEntry() + } + return FileVisitResult.CONTINUE + } + + @Throws(IOException::class) + override fun preVisitDirectory(dir: Path, attrs: BasicFileAttributes): FileVisitResult { + val zipEntry = ZipEntry(baseDir.relativize(dir).toString() + "/") + fixedModificationTime?.let { + zipEntry.time = it + } + zos.putNextEntry(zipEntry) + zos.closeEntry() + return FileVisitResult.CONTINUE + } + }) + } + return output + } fun deCompress(zipFile: File, targetPath: String): File { val zip = ZipFile(zipFile, Charset.defaultCharset()) diff --git a/ms/controllerblueprints/modules/db-resources/src/main/kotlin/org/onap/ccsdk/cds/controllerblueprints/db/resources/BlueprintCatalogServiceImpl.kt b/ms/controllerblueprints/modules/db-resources/src/main/kotlin/org/onap/ccsdk/cds/controllerblueprints/db/resources/BlueprintCatalogServiceImpl.kt index 9780bbd31..b3436a991 100644 --- a/ms/controllerblueprints/modules/db-resources/src/main/kotlin/org/onap/ccsdk/cds/controllerblueprints/db/resources/BlueprintCatalogServiceImpl.kt +++ b/ms/controllerblueprints/modules/db-resources/src/main/kotlin/org/onap/ccsdk/cds/controllerblueprints/db/resources/BlueprintCatalogServiceImpl.kt @@ -46,7 +46,7 @@ abstract class BlueprintCatalogServiceImpl( workingDir = blueprintFile.absolutePath archiveFile = normalizedFile(bluePrintPathConfiguration.blueprintArchivePath, processingId, "cba.zip") - if (!BluePrintArchiveUtils.compress(blueprintFile, archiveFile, true)) { + if (!BluePrintArchiveUtils.compress(blueprintFile, archiveFile)) { throw BluePrintException("Fail to compress blueprint") } } else { diff --git a/ms/controllerblueprints/modules/service/src/main/kotlin/org/onap/ccsdk/cds/controllerblueprints/service/utils/BluePrintEnhancerUtils.kt b/ms/controllerblueprints/modules/service/src/main/kotlin/org/onap/ccsdk/cds/controllerblueprints/service/utils/BluePrintEnhancerUtils.kt index d4753e194..a0f8ca9c5 100644 --- a/ms/controllerblueprints/modules/service/src/main/kotlin/org/onap/ccsdk/cds/controllerblueprints/service/utils/BluePrintEnhancerUtils.kt +++ b/ms/controllerblueprints/modules/service/src/main/kotlin/org/onap/ccsdk/cds/controllerblueprints/service/utils/BluePrintEnhancerUtils.kt @@ -109,7 +109,7 @@ class BluePrintEnhancerUtils { suspend fun compressToFilePart(enhanceDir: String, archiveDir: String): ResponseEntity<Resource> { val compressedFile = normalizedFile(archiveDir, "enhanced-cba.zip") - BluePrintArchiveUtils.compress(Paths.get(enhanceDir).toFile(), compressedFile, true) + BluePrintArchiveUtils.compress(Paths.get(enhanceDir).toFile(), compressedFile) return prepareResourceEntity(compressedFile.name, compressedFile.readBytes()) } diff --git a/ms/controllerblueprints/modules/service/src/test/kotlin/org/onap/ccsdk/cds/controllerblueprints/service/enhancer/BluePrintEnhancerServiceImplTest.kt b/ms/controllerblueprints/modules/service/src/test/kotlin/org/onap/ccsdk/cds/controllerblueprints/service/enhancer/BluePrintEnhancerServiceImplTest.kt index 1f872c2da..d09479b6c 100644 --- a/ms/controllerblueprints/modules/service/src/test/kotlin/org/onap/ccsdk/cds/controllerblueprints/service/enhancer/BluePrintEnhancerServiceImplTest.kt +++ b/ms/controllerblueprints/modules/service/src/test/kotlin/org/onap/ccsdk/cds/controllerblueprints/service/enhancer/BluePrintEnhancerServiceImplTest.kt @@ -66,7 +66,6 @@ class BluePrintEnhancerServiceImplTest { testBaseConfigEnhancementAndValidation() testVFWEnhancementAndValidation() testGoldenEnhancementAndValidation() - testCapabilityRestconfEnhancementAndValidation() testRemoteScriptsEnhancementAndValidation() testCapabilityCliEnhancementAndValidation() } @@ -87,12 +86,6 @@ class BluePrintEnhancerServiceImplTest { testComponentInvokeEnhancementAndValidation(basePath, "golden-enhance") } - fun testCapabilityRestconfEnhancementAndValidation() { - val basePath = "./../../../../components/model-catalog/blueprint-model/test-blueprint/capability_restconf" - testComponentInvokeEnhancementAndValidation(basePath, "capability_restconf-enhance") - - } - fun testRemoteScriptsEnhancementAndValidation() { val basePath = "./../../../../components/model-catalog/blueprint-model/test-blueprint/remote_scripts" testComponentInvokeEnhancementAndValidation(basePath, "remote_scripts-enhance") diff --git a/ms/controllerblueprints/parent/pom.xml b/ms/controllerblueprints/parent/pom.xml index a16783f75..2e9dcb243 100644 --- a/ms/controllerblueprints/parent/pom.xml +++ b/ms/controllerblueprints/parent/pom.xml @@ -94,11 +94,6 @@ <version>2.6</version> </dependency> <dependency> - <groupId>org.apache.commons</groupId> - <artifactId>commons-compress</artifactId> - <version>1.15</version> - </dependency> - <dependency> <groupId>org.apache.velocity</groupId> <artifactId>velocity</artifactId> <version>${velocity.version}</version> @@ -294,10 +289,6 @@ <artifactId>commons-io</artifactId> </dependency> <dependency> - <groupId>org.apache.commons</groupId> - <artifactId>commons-compress</artifactId> - </dependency> - <dependency> <groupId>com.jayway.jsonpath</groupId> <artifactId>json-path</artifactId> <exclusions> |