From ff8df16778818804d31b844b0df7b2f9a16f2cba Mon Sep 17 00:00:00 2001 From: Lukasz Rajewski Date: Fri, 4 Sep 2020 18:16:45 +0200 Subject: Add k8s-upload-profile templating and packaging Issue-ID: CCSDK-2631 Signed-off-by: Lukasz Rajewski Change-Id: Id520338ffc2f43821d114b037467fbcc2f042b66 --- .../profile/upload/K8sProfileUploadComponent.kt | 212 ++++++++++++++++++--- 1 file changed, 186 insertions(+), 26 deletions(-) (limited to 'ms/blueprintsprocessor/functions/k8s-profile-upload/src/main/kotlin/org') diff --git a/ms/blueprintsprocessor/functions/k8s-profile-upload/src/main/kotlin/org/onap/ccsdk/cds/blueprintsprocessor/functions/k8s/profile/upload/K8sProfileUploadComponent.kt b/ms/blueprintsprocessor/functions/k8s-profile-upload/src/main/kotlin/org/onap/ccsdk/cds/blueprintsprocessor/functions/k8s/profile/upload/K8sProfileUploadComponent.kt index aa726a5e9..0ae76ea7e 100644 --- a/ms/blueprintsprocessor/functions/k8s-profile-upload/src/main/kotlin/org/onap/ccsdk/cds/blueprintsprocessor/functions/k8s/profile/upload/K8sProfileUploadComponent.kt +++ b/ms/blueprintsprocessor/functions/k8s-profile-upload/src/main/kotlin/org/onap/ccsdk/cds/blueprintsprocessor/functions/k8s/profile/upload/K8sProfileUploadComponent.kt @@ -19,26 +19,40 @@ package org.onap.ccsdk.cds.blueprintsprocessor.functions.k8s.profile.upload +import com.fasterxml.jackson.databind.JsonNode +import com.fasterxml.jackson.databind.node.ObjectNode +import org.apache.commons.io.FileUtils +import org.onap.ccsdk.cds.blueprintsprocessor.core.BluePrintPropertiesService +import org.onap.ccsdk.cds.blueprintsprocessor.core.api.data.ExecutionServiceInput +import org.onap.ccsdk.cds.blueprintsprocessor.functions.resource.resolution.ResourceResolutionConstants +import org.onap.ccsdk.cds.blueprintsprocessor.functions.resource.resolution.ResourceResolutionService import org.onap.ccsdk.cds.blueprintsprocessor.services.execution.AbstractComponentFunction +import org.onap.ccsdk.cds.controllerblueprints.core.BluePrintConstants +import org.onap.ccsdk.cds.controllerblueprints.core.BluePrintProcessorException +import org.onap.ccsdk.cds.controllerblueprints.core.asJsonNode +import org.onap.ccsdk.cds.controllerblueprints.core.data.ArtifactDefinition +import org.onap.ccsdk.cds.controllerblueprints.core.returnNullIfMissing +import org.onap.ccsdk.cds.controllerblueprints.core.service.BluePrintVelocityTemplateService +import org.onap.ccsdk.cds.controllerblueprints.core.utils.ArchiveType +import org.onap.ccsdk.cds.controllerblueprints.core.utils.BluePrintArchiveUtils +import org.onap.ccsdk.cds.controllerblueprints.core.utils.JacksonUtils +import org.slf4j.LoggerFactory import org.springframework.beans.factory.config.ConfigurableBeanFactory import org.springframework.context.annotation.Scope import org.springframework.stereotype.Component -import java.util.ArrayList -import java.nio.file.Paths +import org.yaml.snakeyaml.Yaml import java.io.File -import com.fasterxml.jackson.databind.JsonNode -import org.onap.ccsdk.cds.controllerblueprints.core.asJsonNode -import org.onap.ccsdk.cds.blueprintsprocessor.core.api.data.ExecutionServiceInput -import org.onap.ccsdk.cds.blueprintsprocessor.core.BluePrintPropertiesService -import org.slf4j.LoggerFactory +import java.nio.file.Files import java.nio.file.Path -import org.onap.ccsdk.cds.controllerblueprints.core.returnNullIfMissing -import com.fasterxml.jackson.databind.node.ObjectNode -import org.onap.ccsdk.cds.controllerblueprints.core.utils.JacksonUtils +import java.nio.file.Paths +import kotlin.collections.ArrayList @Component("component-k8s-profile-upload") @Scope(value = ConfigurableBeanFactory.SCOPE_PROTOTYPE) -open class K8sProfileUploadComponent(private var bluePrintPropertiesService: BluePrintPropertiesService) : +open class K8sProfileUploadComponent( + private var bluePrintPropertiesService: BluePrintPropertiesService, + private val resourceResolutionService: ResourceResolutionService +) : AbstractComponentFunction() { @@ -98,9 +112,9 @@ open class K8sProfileUploadComponent(private var bluePrintPropertiesService: Blu } // For clarity we pull out the required fields - val profileName = prefixInputParamsMap[INPUT_K8S_PROFILE_NAME]?.returnNullIfMissing()?.textValue() - val definitionName = prefixInputParamsMap[INPUT_K8S_DEFINITION_NAME]?.returnNullIfMissing()?.textValue() - val definitionVersion = prefixInputParamsMap[INPUT_K8S_DEFINITION_VERSION]?.returnNullIfMissing()?.textValue() + val profileName: String? = prefixInputParamsMap[INPUT_K8S_PROFILE_NAME]?.returnNullIfMissing()?.asText() + val definitionName: String? = prefixInputParamsMap[INPUT_K8S_DEFINITION_NAME]?.returnNullIfMissing()?.asText() + val definitionVersion: String? = prefixInputParamsMap[INPUT_K8S_DEFINITION_VERSION]?.returnNullIfMissing()?.asText() val k8sProfileUploadConfiguration = K8sProfileUploadConfiguration(bluePrintPropertiesService) @@ -124,14 +138,26 @@ open class K8sProfileUploadComponent(private var bluePrintPropertiesService: Blu } else { log.info("Uploading K8s Profile..") outputPrefixStatuses.put(prefix, OUTPUT_ERROR) - + val profileNamespace: String? = prefixInputParamsMap[INPUT_K8S_PROFILE_NAMESPACE]?.returnNullIfMissing()?.asText() + var profileSource: String? = prefixInputParamsMap[INPUT_K8S_PROFILE_SOURCE]?.returnNullIfMissing()?.asText() + if (profileNamespace == null) + throw BluePrintProcessorException("Profile $profileName namespace is missing") + if (profileSource == null) { + profileSource = profileName + log.info("Profile name used instead of profile source") + } + val bluePrintContext = bluePrintRuntimeService.bluePrintContext() + val artifact: ArtifactDefinition = bluePrintContext.nodeTemplateArtifact(nodeTemplateName, profileSource) + if (artifact.type != BluePrintConstants.MODEL_TYPE_ARTIFACT_K8S_PROFILE) + throw BluePrintProcessorException("Unexpected profile artifact type for profile source " + + "$profileSource. Expecting: $artifact.type") var profile = K8sProfile() profile.profileName = profileName profile.rbName = definitionName profile.rbVersion = definitionVersion - profile.namespace = prefixInputParamsMap[INPUT_K8S_PROFILE_NAMESPACE]?.textValue() + profile.namespace = profileNamespace + val profileFilePath: Path = prepareProfileFile(profileName, profileSource, artifact.file) api.createProfile(profile) - val profileFilePath: Path = prepareProfileFile(profileName) api.uploadProfileContent(profile, profileFilePath) log.info("K8s Profile Upload Completed") @@ -156,16 +182,150 @@ open class K8sProfileUploadComponent(private var bluePrintPropertiesService: Blu return result } - fun prepareProfileFile(k8sRbProfileName: String): Path { + private suspend fun prepareProfileFile(k8sRbProfileName: String, ks8ProfileSource: String, ks8ProfileLocation: String): Path { val bluePrintContext = bluePrintRuntimeService.bluePrintContext() val bluePrintBasePath: String = bluePrintContext.rootPath - return Paths.get( - bluePrintBasePath.plus(File.separator) - .plus("Templates") - .plus(File.separator) - .plus("k8s-profiles") - .plus(File.separator) - .plus("$k8sRbProfileName.tar.gz") - ) + val profileSourceFileFolderPath: String = bluePrintBasePath.plus(File.separator) + .plus(ks8ProfileLocation) + val profileFilePathTarGz: String = profileSourceFileFolderPath.plus(".tar.gz") + val profileFilePathTgz: String = profileSourceFileFolderPath.plus(".tgz") + + if (Paths.get(profileFilePathTarGz).toFile().exists()) + return Paths.get(profileFilePathTarGz) + else if (Paths.get(profileFilePathTgz).toFile().exists()) + return Paths.get(profileFilePathTgz) + else if (Paths.get(profileSourceFileFolderPath).toFile().exists()) { + log.info("Profile building started from source $ks8ProfileSource") + val properties: MutableMap = mutableMapOf() + properties[ResourceResolutionConstants.RESOURCE_RESOLUTION_INPUT_STORE_RESULT] = false + properties[ResourceResolutionConstants.RESOURCE_RESOLUTION_INPUT_RESOLUTION_KEY] = "" + properties[ResourceResolutionConstants.RESOURCE_RESOLUTION_INPUT_RESOURCE_ID] = "" + properties[ResourceResolutionConstants.RESOURCE_RESOLUTION_INPUT_RESOURCE_TYPE] = "" + properties[ResourceResolutionConstants.RESOURCE_RESOLUTION_INPUT_OCCURRENCE] = 1 + properties[ResourceResolutionConstants.RESOURCE_RESOLUTION_INPUT_RESOLUTION_SUMMARY] = false + val resolutionResult: Pair = resourceResolutionService.resolveResources( + bluePrintRuntimeService, + nodeTemplateName, + ks8ProfileSource, + properties) + val tempMainPath: File = createTempDir("k8s-profile-", "") + val tempProfilePath: File = createTempDir("content-", "", tempMainPath) + + try { + val manifestFiles: ArrayList? = readManifestFiles(Paths.get(profileSourceFileFolderPath).toFile(), + tempProfilePath) + if (manifestFiles != null) { + templateLocation(Paths.get(profileSourceFileFolderPath).toFile(), resolutionResult.second, + tempProfilePath, manifestFiles) + } else + throw BluePrintProcessorException("Manifest file is missing") + // Preparation of the final profile content + val finalProfileFilePath = Paths.get(tempMainPath.toString().plus(File.separator).plus( + "$k8sRbProfileName.tar.gz")) + if (!BluePrintArchiveUtils.compress(tempProfilePath, finalProfileFilePath.toFile(), + ArchiveType.TarGz)) { + throw BluePrintProcessorException("Profile compression has failed") + } + FileUtils.deleteDirectory(tempProfilePath) + + return finalProfileFilePath + } catch (t: Throwable) { + FileUtils.deleteDirectory(tempMainPath) + throw t + } + } else + throw BluePrintProcessorException("Profile source $ks8ProfileSource is missing in CBA folder") + } + + private fun readManifestFiles(profileSource: File, destinationFolder: File): ArrayList? { + val directoryListing: Array? = profileSource.listFiles() + var result: ArrayList? = null + if (directoryListing != null) { + for (child in directoryListing) { + if (!child.isDirectory && child.name.toLowerCase() == "manifest.yaml") { + child.bufferedReader().use { inr -> + val manifestYaml = Yaml() + val manifestObject: Map = manifestYaml.load(inr) + val typeObject: MutableMap? = manifestObject["type"] as MutableMap? + if (typeObject != null) { + result = ArrayList() + val valuesObject = typeObject["values"] + if (valuesObject != null) { + result!!.add(File(destinationFolder.toString().plus(File.separator).plus(valuesObject))) + result!!.add(File(destinationFolder.toString().plus(File.separator).plus(child.name))) + } + (typeObject["configresource"] as ArrayList<*>?)?.forEach { item -> + val fileInfo: Map = item as Map + val filePath = fileInfo["filepath"] + val chartPath = fileInfo["chartpath"] + if (filePath == null || chartPath == null) + log.error("One configresource in manifest was skipped because of the wrong format") + else { + result!!.add(File(destinationFolder.toString().plus(File.separator).plus(filePath))) + } + } + } + } + break + } + } + } + return result + } + + private fun templateLocation( + location: File, + params: JsonNode, + destinationFolder: File, + manifestFiles: ArrayList + ) { + val directoryListing: Array? = location.listFiles() + if (directoryListing != null) { + for (child in directoryListing) { + var newDestinationFolder = destinationFolder.toPath() + if (child.isDirectory) + newDestinationFolder = Paths.get(destinationFolder.toString().plus(File.separator).plus(child.name)) + + templateLocation(child, params, newDestinationFolder.toFile(), manifestFiles) + } + } else if (!location.isDirectory) { + if (location.extension.toLowerCase() == "vtl") { + templateFile(location, params, destinationFolder, manifestFiles) + } else { + val finalFilePath = Paths.get(destinationFolder.path.plus(File.separator) + .plus(location.name)).toFile() + if (isFileInTheManifestFiles(finalFilePath, manifestFiles)) { + if (!destinationFolder.exists()) + Files.createDirectories(destinationFolder.toPath()) + FileUtils.copyFile(location, finalFilePath) + } + } + } + } + + private fun isFileInTheManifestFiles(file: File, manifestFiles: ArrayList): Boolean { + manifestFiles.forEach { fileFromManifest -> + if (fileFromManifest.toString().toLowerCase() == file.toString().toLowerCase()) + return true + } + return false + } + + private fun templateFile( + templatedFile: File, + params: JsonNode, + destinationFolder: File, + manifestFiles: ArrayList + ) { + val finalFile = File(destinationFolder.path.plus(File.separator) + .plus(templatedFile.nameWithoutExtension)) + if (!isFileInTheManifestFiles(finalFile, manifestFiles)) + return + val fileContent = templatedFile.bufferedReader().readText() + val finalFileContent = BluePrintVelocityTemplateService.generateContent(fileContent, + params, true) + if (!destinationFolder.exists()) + Files.createDirectories(destinationFolder.toPath()) + finalFile.bufferedWriter().use { out -> out.write(finalFileContent) } } } -- cgit 1.2.3-korg